2 * @license AngularJS v1.4.8
3 * (c) 2010-2015 Google, Inc. http://angularjs.org
6 (function(window, document, undefined) {'use strict';
11 * This object provides a utility for producing rich Error messages within
12 * Angular. It can be called as follows:
14 * var exampleMinErr = minErr('example');
15 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
17 * The above creates an instance of minErr in the example namespace. The
18 * resulting error will have a namespaced error code of example.one. The
19 * resulting error will replace {0} with the value of foo, and {1} with the
20 * value of bar. The object is not restricted in the number of arguments it can
23 * If fewer arguments are specified than necessary for interpolation, the extra
24 * interpolation markers will be preserved in the final string.
26 * Since data will be parsed statically during a build step, some restrictions
27 * are applied with respect to how minErr instances are created and called.
28 * Instances should have names of the form namespaceMinErr for a minErr created
29 * using minErr('namespace') . Error codes, namespaces and template strings
30 * should all be static strings, not variables or general expressions.
32 * @param {string} module The namespace to use for the new minErr instance.
33 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34 * error from returned function, for cases when a particular type of error is useful.
35 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
38 function minErr(module, ErrorConstructor) {
39 ErrorConstructor = ErrorConstructor || Error;
43 var templateArgs = arguments,
44 code = templateArgs[0],
45 message = '[' + (module ? module + ':' : '') + code + '] ',
46 template = templateArgs[1],
49 message += template.replace(/\{\d+\}/g, function(match) {
50 var index = +match.slice(1, -1),
51 shiftedIndex = index + SKIP_INDEXES;
53 if (shiftedIndex < templateArgs.length) {
54 return toDebugString(templateArgs[shiftedIndex]);
60 message += '\nhttp://errors.angularjs.org/1.4.8/' +
61 (module ? module + '/' : '') + code;
63 for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
64 message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
65 encodeURIComponent(toDebugString(templateArgs[i]));
68 return new ErrorConstructor(message);
72 /* We need to tell jshint what variables are being exported */
73 /* global angular: true,
84 REGEX_STRING_REGEXP: true,
85 VALIDITY_STATE_PROPERTY: true,
89 manualLowercase: true,
90 manualUppercase: true,
123 escapeForRegexp: true,
136 toJsonReplacer: true,
139 convertTimezoneToLocal: true,
140 timezoneToOffset: true,
142 tryDecodeURIComponent: true,
145 encodeUriSegment: true,
146 encodeUriQuery: true,
149 getTestability: true,
154 assertNotHasOwnProperty: true,
157 hasOwnProperty: true,
160 NODE_TYPE_ELEMENT: true,
161 NODE_TYPE_ATTRIBUTE: true,
162 NODE_TYPE_TEXT: true,
163 NODE_TYPE_COMMENT: true,
164 NODE_TYPE_DOCUMENT: true,
165 NODE_TYPE_DOCUMENT_FRAGMENT: true,
168 ////////////////////////////////////
177 * The ng module is loaded by default when an AngularJS application is started. The module itself
178 * contains the essential components for an AngularJS application to function. The table below
179 * lists a high level breakdown of each of the services/factories, filters, directives and testing
180 * components available within this core module.
182 * <div doc-module-components="ng"></div>
185 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
187 // The name of a form control's ValidityState property.
188 // This is used so that it's possible for internal tests to create mock ValidityStates.
189 var VALIDITY_STATE_PROPERTY = 'validity';
193 * @name angular.lowercase
197 * @description Converts the specified string to lowercase.
198 * @param {string} string String to be converted to lowercase.
199 * @returns {string} Lowercased string.
201 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
202 var hasOwnProperty = Object.prototype.hasOwnProperty;
206 * @name angular.uppercase
210 * @description Converts the specified string to uppercase.
211 * @param {string} string String to be converted to uppercase.
212 * @returns {string} Uppercased string.
214 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
217 var manualLowercase = function(s) {
218 /* jshint bitwise: false */
220 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
223 var manualUppercase = function(s) {
224 /* jshint bitwise: false */
226 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
231 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
232 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
233 // with correct but slower alternatives.
234 if ('i' !== 'I'.toLowerCase()) {
235 lowercase = manualLowercase;
236 uppercase = manualUppercase;
241 msie, // holds major version number for IE, or NaN if UA is not IE.
242 jqLite, // delay binding since jQuery could be loaded after us.
243 jQuery, // delay binding
247 toString = Object.prototype.toString,
248 getPrototypeOf = Object.getPrototypeOf,
249 ngMinErr = minErr('ng'),
252 angular = window.angular || (window.angular = {}),
257 * documentMode is an IE-only property
258 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
260 msie = document.documentMode;
266 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
269 function isArrayLike(obj) {
271 // `null`, `undefined` and `window` are not array-like
272 if (obj == null || isWindow(obj)) return false;
274 // arrays, strings and jQuery/jqLite objects are array like
275 // * jqLite is either the jQuery or jqLite constructor function
276 // * we have to check the existance of jqLite first as this method is called
277 // via the forEach method when constructing the jqLite object in the first place
278 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
280 // Support: iOS 8.2 (not reproducible in simulator)
281 // "length" in obj used to prevent JIT error (gh-11508)
282 var length = "length" in Object(obj) && obj.length;
284 // NodeList objects (with `item` method) and
285 // other objects with suitable length characteristics are array-like
286 return isNumber(length) &&
287 (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
292 * @name angular.forEach
297 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
298 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
299 * is the value of an object property or an array element, `key` is the object property key or
300 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
302 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
303 * using the `hasOwnProperty` method.
306 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
307 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
308 * return the value provided.
311 var values = {name: 'misko', gender: 'male'};
313 angular.forEach(values, function(value, key) {
314 this.push(key + ': ' + value);
316 expect(log).toEqual(['name: misko', 'gender: male']);
319 * @param {Object|Array} obj Object to iterate over.
320 * @param {Function} iterator Iterator function.
321 * @param {Object=} context Object to become context (`this`) for the iterator function.
322 * @returns {Object|Array} Reference to `obj`.
325 function forEach(obj, iterator, context) {
328 if (isFunction(obj)) {
330 // Need to check if hasOwnProperty exists,
331 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
332 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
333 iterator.call(context, obj[key], key, obj);
336 } else if (isArray(obj) || isArrayLike(obj)) {
337 var isPrimitive = typeof obj !== 'object';
338 for (key = 0, length = obj.length; key < length; key++) {
339 if (isPrimitive || key in obj) {
340 iterator.call(context, obj[key], key, obj);
343 } else if (obj.forEach && obj.forEach !== forEach) {
344 obj.forEach(iterator, context, obj);
345 } else if (isBlankObject(obj)) {
346 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
348 iterator.call(context, obj[key], key, obj);
350 } else if (typeof obj.hasOwnProperty === 'function') {
351 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
353 if (obj.hasOwnProperty(key)) {
354 iterator.call(context, obj[key], key, obj);
358 // Slow path for objects which do not have a method `hasOwnProperty`
360 if (hasOwnProperty.call(obj, key)) {
361 iterator.call(context, obj[key], key, obj);
369 function forEachSorted(obj, iterator, context) {
370 var keys = Object.keys(obj).sort();
371 for (var i = 0; i < keys.length; i++) {
372 iterator.call(context, obj[keys[i]], keys[i]);
379 * when using forEach the params are value, key, but it is often useful to have key, value.
380 * @param {function(string, *)} iteratorFn
381 * @returns {function(*, string)}
383 function reverseParams(iteratorFn) {
384 return function(value, key) { iteratorFn(key, value); };
388 * A consistent way of creating unique IDs in angular.
390 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
391 * we hit number precision issues in JavaScript.
393 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
395 * @returns {number} an unique alpha-numeric string
403 * Set or clear the hashkey for an object.
405 * @param h the hashkey (!truthy to delete the hashkey)
407 function setHashKey(obj, h) {
411 delete obj.$$hashKey;
416 function baseExtend(dst, objs, deep) {
417 var h = dst.$$hashKey;
419 for (var i = 0, ii = objs.length; i < ii; ++i) {
421 if (!isObject(obj) && !isFunction(obj)) continue;
422 var keys = Object.keys(obj);
423 for (var j = 0, jj = keys.length; j < jj; j++) {
427 if (deep && isObject(src)) {
429 dst[key] = new Date(src.valueOf());
430 } else if (isRegExp(src)) {
431 dst[key] = new RegExp(src);
432 } else if (src.nodeName) {
433 dst[key] = src.cloneNode(true);
434 } else if (isElement(src)) {
435 dst[key] = src.clone();
437 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
438 baseExtend(dst[key], [src], true);
452 * @name angular.extend
457 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
458 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
459 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
461 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
462 * {@link angular.merge} for this.
464 * @param {Object} dst Destination object.
465 * @param {...Object} src Source object(s).
466 * @returns {Object} Reference to `dst`.
468 function extend(dst) {
469 return baseExtend(dst, slice.call(arguments, 1), false);
475 * @name angular.merge
480 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
481 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
482 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
484 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
485 * objects, performing a deep copy.
487 * @param {Object} dst Destination object.
488 * @param {...Object} src Source object(s).
489 * @returns {Object} Reference to `dst`.
491 function merge(dst) {
492 return baseExtend(dst, slice.call(arguments, 1), true);
497 function toInt(str) {
498 return parseInt(str, 10);
502 function inherit(parent, extra) {
503 return extend(Object.create(parent), extra);
513 * A function that performs no operations. This function can be useful when writing code in the
516 function foo(callback) {
517 var result = calculateResult();
518 (callback || angular.noop)(result);
528 * @name angular.identity
533 * A function that returns its first argument. This function is useful when writing code in the
537 function transformer(transformationFn, value) {
538 return (transformationFn || angular.identity)(value);
541 * @param {*} value to be returned.
542 * @returns {*} the value passed in.
544 function identity($) {return $;}
545 identity.$inject = [];
548 function valueFn(value) {return function() {return value;};}
550 function hasCustomToString(obj) {
551 return isFunction(obj.toString) && obj.toString !== toString;
557 * @name angular.isUndefined
562 * Determines if a reference is undefined.
564 * @param {*} value Reference to check.
565 * @returns {boolean} True if `value` is undefined.
567 function isUndefined(value) {return typeof value === 'undefined';}
572 * @name angular.isDefined
577 * Determines if a reference is defined.
579 * @param {*} value Reference to check.
580 * @returns {boolean} True if `value` is defined.
582 function isDefined(value) {return typeof value !== 'undefined';}
587 * @name angular.isObject
592 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
593 * considered to be objects. Note that JavaScript arrays are objects.
595 * @param {*} value Reference to check.
596 * @returns {boolean} True if `value` is an `Object` but not `null`.
598 function isObject(value) {
599 // http://jsperf.com/isobject4
600 return value !== null && typeof value === 'object';
605 * Determine if a value is an object with a null prototype
607 * @returns {boolean} True if `value` is an `Object` with a null prototype
609 function isBlankObject(value) {
610 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
616 * @name angular.isString
621 * Determines if a reference is a `String`.
623 * @param {*} value Reference to check.
624 * @returns {boolean} True if `value` is a `String`.
626 function isString(value) {return typeof value === 'string';}
631 * @name angular.isNumber
636 * Determines if a reference is a `Number`.
638 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
640 * If you wish to exclude these then you can use the native
641 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
644 * @param {*} value Reference to check.
645 * @returns {boolean} True if `value` is a `Number`.
647 function isNumber(value) {return typeof value === 'number';}
652 * @name angular.isDate
657 * Determines if a value is a date.
659 * @param {*} value Reference to check.
660 * @returns {boolean} True if `value` is a `Date`.
662 function isDate(value) {
663 return toString.call(value) === '[object Date]';
669 * @name angular.isArray
674 * Determines if a reference is an `Array`.
676 * @param {*} value Reference to check.
677 * @returns {boolean} True if `value` is an `Array`.
679 var isArray = Array.isArray;
683 * @name angular.isFunction
688 * Determines if a reference is a `Function`.
690 * @param {*} value Reference to check.
691 * @returns {boolean} True if `value` is a `Function`.
693 function isFunction(value) {return typeof value === 'function';}
697 * Determines if a value is a regular expression object.
700 * @param {*} value Reference to check.
701 * @returns {boolean} True if `value` is a `RegExp`.
703 function isRegExp(value) {
704 return toString.call(value) === '[object RegExp]';
709 * Checks if `obj` is a window object.
712 * @param {*} obj Object to check
713 * @returns {boolean} True if `obj` is a window obj.
715 function isWindow(obj) {
716 return obj && obj.window === obj;
720 function isScope(obj) {
721 return obj && obj.$evalAsync && obj.$watch;
725 function isFile(obj) {
726 return toString.call(obj) === '[object File]';
730 function isFormData(obj) {
731 return toString.call(obj) === '[object FormData]';
735 function isBlob(obj) {
736 return toString.call(obj) === '[object Blob]';
740 function isBoolean(value) {
741 return typeof value === 'boolean';
745 function isPromiseLike(obj) {
746 return obj && isFunction(obj.then);
750 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
751 function isTypedArray(value) {
752 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
756 var trim = function(value) {
757 return isString(value) ? value.trim() : value;
761 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
762 // Prereq: s is a string.
763 var escapeForRegexp = function(s) {
764 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
765 replace(/\x08/g, '\\x08');
771 * @name angular.isElement
776 * Determines if a reference is a DOM element (or wrapped jQuery element).
778 * @param {*} value Reference to check.
779 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
781 function isElement(node) {
783 (node.nodeName // we are a direct element
784 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
788 * @param str 'key1,key2,...'
789 * @returns {object} in the form of {key1:true, key2:true, ...}
791 function makeMap(str) {
792 var obj = {}, items = str.split(","), i;
793 for (i = 0; i < items.length; i++) {
794 obj[items[i]] = true;
800 function nodeName_(element) {
801 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
804 function includes(array, obj) {
805 return Array.prototype.indexOf.call(array, obj) != -1;
808 function arrayRemove(array, value) {
809 var index = array.indexOf(value);
811 array.splice(index, 1);
823 * Creates a deep copy of `source`, which should be an object or an array.
825 * * If no destination is supplied, a copy of the object or array is created.
826 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
827 * are deleted and then all elements/properties from the source are copied to it.
828 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
829 * * If `source` is identical to 'destination' an exception will be thrown.
831 * @param {*} source The source that will be used to make a copy.
832 * Can be any type, including primitives, `null`, and `undefined`.
833 * @param {(Object|Array)=} destination Destination into which the source is copied. If
834 * provided, must be of the same type as `source`.
835 * @returns {*} The copy or updated `destination`, if `destination` was specified.
838 <example module="copyExample">
839 <file name="index.html">
840 <div ng-controller="ExampleController">
841 <form novalidate class="simple-form">
842 Name: <input type="text" ng-model="user.name" /><br />
843 E-mail: <input type="email" ng-model="user.email" /><br />
844 Gender: <input type="radio" ng-model="user.gender" value="male" />male
845 <input type="radio" ng-model="user.gender" value="female" />female<br />
846 <button ng-click="reset()">RESET</button>
847 <button ng-click="update(user)">SAVE</button>
849 <pre>form = {{user | json}}</pre>
850 <pre>master = {{master | json}}</pre>
854 angular.module('copyExample', [])
855 .controller('ExampleController', ['$scope', function($scope) {
858 $scope.update = function(user) {
859 // Example with 1 argument
860 $scope.master= angular.copy(user);
863 $scope.reset = function() {
864 // Example with 2 arguments
865 angular.copy($scope.master, $scope.user);
874 function copy(source, destination) {
875 var stackSource = [];
879 if (isTypedArray(destination)) {
880 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
882 if (source === destination) {
883 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
886 // Empty the destination object
887 if (isArray(destination)) {
888 destination.length = 0;
890 forEach(destination, function(value, key) {
891 if (key !== '$$hashKey') {
892 delete destination[key];
897 stackSource.push(source);
898 stackDest.push(destination);
899 return copyRecurse(source, destination);
902 return copyElement(source);
904 function copyRecurse(source, destination) {
905 var h = destination.$$hashKey;
907 if (isArray(source)) {
908 for (var i = 0, ii = source.length; i < ii; i++) {
909 destination.push(copyElement(source[i]));
911 } else if (isBlankObject(source)) {
912 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
913 for (key in source) {
914 destination[key] = copyElement(source[key]);
916 } else if (source && typeof source.hasOwnProperty === 'function') {
917 // Slow path, which must rely on hasOwnProperty
918 for (key in source) {
919 if (source.hasOwnProperty(key)) {
920 destination[key] = copyElement(source[key]);
924 // Slowest path --- hasOwnProperty can't be called as a method
925 for (key in source) {
926 if (hasOwnProperty.call(source, key)) {
927 destination[key] = copyElement(source[key]);
931 setHashKey(destination, h);
935 function copyElement(source) {
937 if (!isObject(source)) {
941 // Already copied values
942 var index = stackSource.indexOf(source);
944 return stackDest[index];
947 if (isWindow(source) || isScope(source)) {
948 throw ngMinErr('cpws',
949 "Can't copy! Making copies of Window or Scope instances is not supported.");
952 var needsRecurse = false;
955 if (isArray(source)) {
958 } else if (isTypedArray(source)) {
959 destination = new source.constructor(source);
960 } else if (isDate(source)) {
961 destination = new Date(source.getTime());
962 } else if (isRegExp(source)) {
963 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
964 destination.lastIndex = source.lastIndex;
965 } else if (isFunction(source.cloneNode)) {
966 destination = source.cloneNode(true);
968 destination = Object.create(getPrototypeOf(source));
972 stackSource.push(source);
973 stackDest.push(destination);
976 ? copyRecurse(source, destination)
982 * Creates a shallow copy of an object, an array or a primitive.
984 * Assumes that there are no proto properties for objects.
986 function shallowCopy(src, dst) {
990 for (var i = 0, ii = src.length; i < ii; i++) {
993 } else if (isObject(src)) {
996 for (var key in src) {
997 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1009 * @name angular.equals
1014 * Determines if two objects or two values are equivalent. Supports value types, regular
1015 * expressions, arrays and objects.
1017 * Two objects or values are considered equivalent if at least one of the following is true:
1019 * * Both objects or values pass `===` comparison.
1020 * * Both objects or values are of the same type and all of their properties are equal by
1021 * comparing them with `angular.equals`.
1022 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1023 * * Both values represent the same regular expression (In JavaScript,
1024 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1025 * representation matches).
1027 * During a property comparison, properties of `function` type and properties with names
1028 * that begin with `$` are ignored.
1030 * Scope and DOMWindow objects are being compared only by identify (`===`).
1032 * @param {*} o1 Object or value to compare.
1033 * @param {*} o2 Object or value to compare.
1034 * @returns {boolean} True if arguments are equal.
1036 function equals(o1, o2) {
1037 if (o1 === o2) return true;
1038 if (o1 === null || o2 === null) return false;
1039 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1040 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1042 if (t1 == 'object') {
1044 if (!isArray(o2)) return false;
1045 if ((length = o1.length) == o2.length) {
1046 for (key = 0; key < length; key++) {
1047 if (!equals(o1[key], o2[key])) return false;
1051 } else if (isDate(o1)) {
1052 if (!isDate(o2)) return false;
1053 return equals(o1.getTime(), o2.getTime());
1054 } else if (isRegExp(o1)) {
1055 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1057 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1058 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1059 keySet = createMap();
1061 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1062 if (!equals(o1[key], o2[key])) return false;
1066 if (!(key in keySet) &&
1067 key.charAt(0) !== '$' &&
1068 isDefined(o2[key]) &&
1069 !isFunction(o2[key])) return false;
1078 var csp = function() {
1079 if (!isDefined(csp.rules)) {
1082 var ngCspElement = (document.querySelector('[ng-csp]') ||
1083 document.querySelector('[data-ng-csp]'));
1086 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1087 ngCspElement.getAttribute('data-ng-csp');
1089 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1090 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1094 noUnsafeEval: noUnsafeEval(),
1095 noInlineStyle: false
1102 function noUnsafeEval() {
1104 /* jshint -W031, -W054 */
1106 /* jshint +W031, +W054 */
1120 * @param {string=} ngJq the name of the library available under `window`
1121 * to be used for angular.element
1123 * Use this directive to force the angular.element library. This should be
1124 * used to force either jqLite by leaving ng-jq blank or setting the name of
1125 * the jquery variable under window (eg. jQuery).
1127 * Since angular looks for this directive when it is loaded (doesn't wait for the
1128 * DOMContentLoaded event), it must be placed on an element that comes before the script
1129 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1133 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1142 * This example shows how to use a jQuery based library of a different name.
1143 * The library name must be available at the top most 'window'.
1146 <html ng-app ng-jq="jQueryLib">
1152 var jq = function() {
1153 if (isDefined(jq.name_)) return jq.name_;
1155 var i, ii = ngAttrPrefixes.length, prefix, name;
1156 for (i = 0; i < ii; ++i) {
1157 prefix = ngAttrPrefixes[i];
1158 if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1159 name = el.getAttribute(prefix + 'jq');
1164 return (jq.name_ = name);
1167 function concat(array1, array2, index) {
1168 return array1.concat(slice.call(array2, index));
1171 function sliceArgs(args, startIndex) {
1172 return slice.call(args, startIndex || 0);
1179 * @name angular.bind
1184 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1185 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1186 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1187 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1189 * @param {Object} self Context which `fn` should be evaluated in.
1190 * @param {function()} fn Function to be bound.
1191 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1192 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1195 function bind(self, fn) {
1196 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1197 if (isFunction(fn) && !(fn instanceof RegExp)) {
1198 return curryArgs.length
1200 return arguments.length
1201 ? fn.apply(self, concat(curryArgs, arguments, 0))
1202 : fn.apply(self, curryArgs);
1205 return arguments.length
1206 ? fn.apply(self, arguments)
1210 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1216 function toJsonReplacer(key, value) {
1219 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1221 } else if (isWindow(value)) {
1223 } else if (value && document === value) {
1225 } else if (isScope(value)) {
1235 * @name angular.toJson
1240 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1241 * stripped since angular uses this notation internally.
1243 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1244 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1245 * If set to an integer, the JSON output will contain that many spaces per indentation.
1246 * @returns {string|undefined} JSON-ified string representing `obj`.
1248 function toJson(obj, pretty) {
1249 if (typeof obj === 'undefined') return undefined;
1250 if (!isNumber(pretty)) {
1251 pretty = pretty ? 2 : null;
1253 return JSON.stringify(obj, toJsonReplacer, pretty);
1259 * @name angular.fromJson
1264 * Deserializes a JSON string.
1266 * @param {string} json JSON string to deserialize.
1267 * @returns {Object|Array|string|number} Deserialized JSON string.
1269 function fromJson(json) {
1270 return isString(json)
1276 function timezoneToOffset(timezone, fallback) {
1277 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1278 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1282 function addDateMinutes(date, minutes) {
1283 date = new Date(date.getTime());
1284 date.setMinutes(date.getMinutes() + minutes);
1289 function convertTimezoneToLocal(date, timezone, reverse) {
1290 reverse = reverse ? -1 : 1;
1291 var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1292 return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1297 * @returns {string} Returns the string representation of the element.
1299 function startingTag(element) {
1300 element = jqLite(element).clone();
1302 // turns out IE does not let you set .html() on elements which
1303 // are not allowed to have children. So we just ignore it.
1306 var elemHtml = jqLite('<div>').append(element).html();
1308 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1310 match(/^(<[^>]+>)/)[1].
1311 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1313 return lowercase(elemHtml);
1319 /////////////////////////////////////////////////
1322 * Tries to decode the URI component without throwing an exception.
1325 * @param str value potential URI component to check.
1326 * @returns {boolean} True if `value` can be decoded
1327 * with the decodeURIComponent function.
1329 function tryDecodeURIComponent(value) {
1331 return decodeURIComponent(value);
1333 // Ignore any invalid uri component
1339 * Parses an escaped url query string into key-value pairs.
1340 * @returns {Object.<string,boolean|Array>}
1342 function parseKeyValue(/**string*/keyValue) {
1344 forEach((keyValue || "").split('&'), function(keyValue) {
1345 var splitPoint, key, val;
1347 key = keyValue = keyValue.replace(/\+/g,'%20');
1348 splitPoint = keyValue.indexOf('=');
1349 if (splitPoint !== -1) {
1350 key = keyValue.substring(0, splitPoint);
1351 val = keyValue.substring(splitPoint + 1);
1353 key = tryDecodeURIComponent(key);
1354 if (isDefined(key)) {
1355 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1356 if (!hasOwnProperty.call(obj, key)) {
1358 } else if (isArray(obj[key])) {
1361 obj[key] = [obj[key],val];
1369 function toKeyValue(obj) {
1371 forEach(obj, function(value, key) {
1372 if (isArray(value)) {
1373 forEach(value, function(arrayValue) {
1374 parts.push(encodeUriQuery(key, true) +
1375 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1378 parts.push(encodeUriQuery(key, true) +
1379 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1382 return parts.length ? parts.join('&') : '';
1387 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1388 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1391 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1392 * pct-encoded = "%" HEXDIG HEXDIG
1393 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1394 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1395 * / "*" / "+" / "," / ";" / "="
1397 function encodeUriSegment(val) {
1398 return encodeUriQuery(val, true).
1399 replace(/%26/gi, '&').
1400 replace(/%3D/gi, '=').
1401 replace(/%2B/gi, '+');
1406 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1407 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1408 * encoded per http://tools.ietf.org/html/rfc3986:
1409 * query = *( pchar / "/" / "?" )
1410 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1411 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1412 * pct-encoded = "%" HEXDIG HEXDIG
1413 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1414 * / "*" / "+" / "," / ";" / "="
1416 function encodeUriQuery(val, pctEncodeSpaces) {
1417 return encodeURIComponent(val).
1418 replace(/%40/gi, '@').
1419 replace(/%3A/gi, ':').
1420 replace(/%24/g, '$').
1421 replace(/%2C/gi, ',').
1422 replace(/%3B/gi, ';').
1423 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1426 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1428 function getNgAttribute(element, ngAttr) {
1429 var attr, i, ii = ngAttrPrefixes.length;
1430 for (i = 0; i < ii; ++i) {
1431 attr = ngAttrPrefixes[i] + ngAttr;
1432 if (isString(attr = element.getAttribute(attr))) {
1445 * @param {angular.Module} ngApp an optional application
1446 * {@link angular.module module} name to load.
1447 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1448 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1449 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1450 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1451 * tracking down the root of these bugs.
1455 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1456 * designates the **root element** of the application and is typically placed near the root element
1457 * of the page - e.g. on the `<body>` or `<html>` tags.
1459 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1460 * found in the document will be used to define the root element to auto-bootstrap as an
1461 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1462 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1464 * You can specify an **AngularJS module** to be used as the root module for the application. This
1465 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1466 * should contain the application code needed or have dependencies on other modules that will
1467 * contain the code. See {@link angular.module} for more information.
1469 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1470 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1471 * would not be resolved to `3`.
1473 * `ngApp` is the easiest, and most common way to bootstrap an application.
1475 <example module="ngAppDemo">
1476 <file name="index.html">
1477 <div ng-controller="ngAppDemoController">
1478 I can add: {{a}} + {{b}} = {{ a+b }}
1481 <file name="script.js">
1482 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1489 * Using `ngStrictDi`, you would see something like this:
1491 <example ng-app-included="true">
1492 <file name="index.html">
1493 <div ng-app="ngAppStrictDemo" ng-strict-di>
1494 <div ng-controller="GoodController1">
1495 I can add: {{a}} + {{b}} = {{ a+b }}
1497 <p>This renders because the controller does not fail to
1498 instantiate, by using explicit annotation style (see
1499 script.js for details)
1503 <div ng-controller="GoodController2">
1504 Name: <input ng-model="name"><br />
1507 <p>This renders because the controller does not fail to
1508 instantiate, by using explicit annotation style
1509 (see script.js for details)
1513 <div ng-controller="BadController">
1514 I can add: {{a}} + {{b}} = {{ a+b }}
1516 <p>The controller could not be instantiated, due to relying
1517 on automatic function annotations (which are disabled in
1518 strict mode). As such, the content of this section is not
1519 interpolated, and there should be an error in your web console.
1524 <file name="script.js">
1525 angular.module('ngAppStrictDemo', [])
1526 // BadController will fail to instantiate, due to relying on automatic function annotation,
1527 // rather than an explicit annotation
1528 .controller('BadController', function($scope) {
1532 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1533 // due to using explicit annotations using the array style and $inject property, respectively.
1534 .controller('GoodController1', ['$scope', function($scope) {
1538 .controller('GoodController2', GoodController2);
1539 function GoodController2($scope) {
1540 $scope.name = "World";
1542 GoodController2.$inject = ['$scope'];
1544 <file name="style.css">
1545 div[ng-controller] {
1547 -webkit-border-radius: 4px;
1552 div[ng-controller^=Good] {
1553 border-color: #d6e9c6;
1554 background-color: #dff0d8;
1557 div[ng-controller^=Bad] {
1558 border-color: #ebccd1;
1559 background-color: #f2dede;
1566 function angularInit(element, bootstrap) {
1571 // The element `element` has priority over any other element
1572 forEach(ngAttrPrefixes, function(prefix) {
1573 var name = prefix + 'app';
1575 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1576 appElement = element;
1577 module = element.getAttribute(name);
1580 forEach(ngAttrPrefixes, function(prefix) {
1581 var name = prefix + 'app';
1584 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1585 appElement = candidate;
1586 module = candidate.getAttribute(name);
1590 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1591 bootstrap(appElement, module ? [module] : [], config);
1597 * @name angular.bootstrap
1600 * Use this function to manually start up angular application.
1602 * See: {@link guide/bootstrap Bootstrap}
1604 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1605 * They must use {@link ng.directive:ngApp ngApp}.
1607 * Angular will detect if it has been loaded into the browser more than once and only allow the
1608 * first loaded script to be bootstrapped and will report a warning to the browser console for
1609 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1610 * multiple instances of Angular try to work on the DOM.
1616 * <div ng-controller="WelcomeController">
1620 * <script src="angular.js"></script>
1622 * var app = angular.module('demo', [])
1623 * .controller('WelcomeController', function($scope) {
1624 * $scope.greeting = 'Welcome!';
1626 * angular.bootstrap(document, ['demo']);
1632 * @param {DOMElement} element DOM element which is the root of angular application.
1633 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1634 * Each item in the array should be the name of a predefined module or a (DI annotated)
1635 * function that will be invoked by the injector as a `config` block.
1636 * See: {@link angular.module modules}
1637 * @param {Object=} config an object for defining configuration options for the application. The
1638 * following keys are supported:
1640 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1641 * assist in finding bugs which break minified code. Defaults to `false`.
1643 * @returns {auto.$injector} Returns the newly created injector for this app.
1645 function bootstrap(element, modules, config) {
1646 if (!isObject(config)) config = {};
1647 var defaultConfig = {
1650 config = extend(defaultConfig, config);
1651 var doBootstrap = function() {
1652 element = jqLite(element);
1654 if (element.injector()) {
1655 var tag = (element[0] === document) ? 'document' : startingTag(element);
1656 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1659 "App Already Bootstrapped with this Element '{0}'",
1660 tag.replace(/</,'<').replace(/>/,'>'));
1663 modules = modules || [];
1664 modules.unshift(['$provide', function($provide) {
1665 $provide.value('$rootElement', element);
1668 if (config.debugInfoEnabled) {
1669 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1670 modules.push(['$compileProvider', function($compileProvider) {
1671 $compileProvider.debugInfoEnabled(true);
1675 modules.unshift('ng');
1676 var injector = createInjector(modules, config.strictDi);
1677 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1678 function bootstrapApply(scope, element, compile, injector) {
1679 scope.$apply(function() {
1680 element.data('$injector', injector);
1681 compile(element)(scope);
1688 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1689 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1691 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1692 config.debugInfoEnabled = true;
1693 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1696 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1697 return doBootstrap();
1700 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1701 angular.resumeBootstrap = function(extraModules) {
1702 forEach(extraModules, function(module) {
1703 modules.push(module);
1705 return doBootstrap();
1708 if (isFunction(angular.resumeDeferredBootstrap)) {
1709 angular.resumeDeferredBootstrap();
1715 * @name angular.reloadWithDebugInfo
1718 * Use this function to reload the current application with debug information turned on.
1719 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1721 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1723 function reloadWithDebugInfo() {
1724 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1725 window.location.reload();
1729 * @name angular.getTestability
1732 * Get the testability service for the instance of Angular on the given
1734 * @param {DOMElement} element DOM element which is the root of angular application.
1736 function getTestability(rootElement) {
1737 var injector = angular.element(rootElement).injector();
1739 throw ngMinErr('test',
1740 'no injector found for element argument to getTestability');
1742 return injector.get('$$testability');
1745 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1746 function snake_case(name, separator) {
1747 separator = separator || '_';
1748 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1749 return (pos ? separator : '') + letter.toLowerCase();
1753 var bindJQueryFired = false;
1754 var skipDestroyOnNextJQueryCleanData;
1755 function bindJQuery() {
1756 var originalCleanData;
1758 if (bindJQueryFired) {
1762 // bind to jQuery if present;
1764 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1765 !jqName ? undefined : // use jqLite
1766 window[jqName]; // use jQuery specified by `ngJq`
1768 // Use jQuery if it exists with proper functionality, otherwise default to us.
1769 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1770 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1771 // versions. It will not work for sure with jQuery <1.7, though.
1772 if (jQuery && jQuery.fn.on) {
1775 scope: JQLitePrototype.scope,
1776 isolateScope: JQLitePrototype.isolateScope,
1777 controller: JQLitePrototype.controller,
1778 injector: JQLitePrototype.injector,
1779 inheritedData: JQLitePrototype.inheritedData
1782 // All nodes removed from the DOM via various jQuery APIs like .remove()
1783 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1784 // the $destroy event on all removed nodes.
1785 originalCleanData = jQuery.cleanData;
1786 jQuery.cleanData = function(elems) {
1788 if (!skipDestroyOnNextJQueryCleanData) {
1789 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1790 events = jQuery._data(elem, "events");
1791 if (events && events.$destroy) {
1792 jQuery(elem).triggerHandler('$destroy');
1796 skipDestroyOnNextJQueryCleanData = false;
1798 originalCleanData(elems);
1804 angular.element = jqLite;
1806 // Prevent double-proxying.
1807 bindJQueryFired = true;
1811 * throw error if the argument is falsy.
1813 function assertArg(arg, name, reason) {
1815 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1820 function assertArgFn(arg, name, acceptArrayAnnotation) {
1821 if (acceptArrayAnnotation && isArray(arg)) {
1822 arg = arg[arg.length - 1];
1825 assertArg(isFunction(arg), name, 'not a function, got ' +
1826 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1831 * throw error if the name given is hasOwnProperty
1832 * @param {String} name the name to test
1833 * @param {String} context the context in which the name is used, such as module or directive
1835 function assertNotHasOwnProperty(name, context) {
1836 if (name === 'hasOwnProperty') {
1837 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1842 * Return the value accessible from the object by path. Any undefined traversals are ignored
1843 * @param {Object} obj starting object
1844 * @param {String} path path to traverse
1845 * @param {boolean} [bindFnToScope=true]
1846 * @returns {Object} value as accessible by path
1848 //TODO(misko): this function needs to be removed
1849 function getter(obj, path, bindFnToScope) {
1850 if (!path) return obj;
1851 var keys = path.split('.');
1853 var lastInstance = obj;
1854 var len = keys.length;
1856 for (var i = 0; i < len; i++) {
1859 obj = (lastInstance = obj)[key];
1862 if (!bindFnToScope && isFunction(obj)) {
1863 return bind(lastInstance, obj);
1869 * Return the DOM siblings between the first and last node in the given array.
1870 * @param {Array} array like object
1871 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1873 function getBlockNodes(nodes) {
1874 // TODO(perf): update `nodes` instead of creating a new object?
1875 var node = nodes[0];
1876 var endNode = nodes[nodes.length - 1];
1879 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1880 if (blockNodes || nodes[i] !== node) {
1882 blockNodes = jqLite(slice.call(nodes, 0, i));
1884 blockNodes.push(node);
1888 return blockNodes || nodes;
1893 * Creates a new object without a prototype. This object is useful for lookup without having to
1894 * guard against prototypically inherited properties via hasOwnProperty.
1896 * Related micro-benchmarks:
1897 * - http://jsperf.com/object-create2
1898 * - http://jsperf.com/proto-map-lookup/2
1899 * - http://jsperf.com/for-in-vs-object-keys2
1903 function createMap() {
1904 return Object.create(null);
1907 var NODE_TYPE_ELEMENT = 1;
1908 var NODE_TYPE_ATTRIBUTE = 2;
1909 var NODE_TYPE_TEXT = 3;
1910 var NODE_TYPE_COMMENT = 8;
1911 var NODE_TYPE_DOCUMENT = 9;
1912 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1916 * @name angular.Module
1920 * Interface for configuring angular {@link angular.module modules}.
1923 function setupModuleLoader(window) {
1925 var $injectorMinErr = minErr('$injector');
1926 var ngMinErr = minErr('ng');
1928 function ensure(obj, name, factory) {
1929 return obj[name] || (obj[name] = factory());
1932 var angular = ensure(window, 'angular', Object);
1934 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1935 angular.$$minErr = angular.$$minErr || minErr;
1937 return ensure(angular, 'module', function() {
1938 /** @type {Object.<string, angular.Module>} */
1943 * @name angular.module
1947 * The `angular.module` is a global place for creating, registering and retrieving Angular
1949 * All modules (angular core or 3rd party) that should be available to an application must be
1950 * registered using this mechanism.
1952 * Passing one argument retrieves an existing {@link angular.Module},
1953 * whereas passing more than one argument creates a new {@link angular.Module}
1958 * A module is a collection of services, directives, controllers, filters, and configuration information.
1959 * `angular.module` is used to configure the {@link auto.$injector $injector}.
1962 * // Create a new module
1963 * var myModule = angular.module('myModule', []);
1965 * // register a new service
1966 * myModule.value('appName', 'MyCoolApp');
1968 * // configure existing services inside initialization blocks.
1969 * myModule.config(['$locationProvider', function($locationProvider) {
1970 * // Configure existing providers
1971 * $locationProvider.hashPrefix('!');
1975 * Then you can create an injector and load your modules like this:
1978 * var injector = angular.injector(['ng', 'myModule'])
1981 * However it's more likely that you'll just use
1982 * {@link ng.directive:ngApp ngApp} or
1983 * {@link angular.bootstrap} to simplify this process for you.
1985 * @param {!string} name The name of the module to create or retrieve.
1986 * @param {!Array.<string>=} requires If specified then new module is being created. If
1987 * unspecified then the module is being retrieved for further configuration.
1988 * @param {Function=} configFn Optional configuration function for the module. Same as
1989 * {@link angular.Module#config Module#config()}.
1990 * @returns {module} new module with the {@link angular.Module} api.
1992 return function module(name, requires, configFn) {
1993 var assertNotHasOwnProperty = function(name, context) {
1994 if (name === 'hasOwnProperty') {
1995 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1999 assertNotHasOwnProperty(name, 'module');
2000 if (requires && modules.hasOwnProperty(name)) {
2001 modules[name] = null;
2003 return ensure(modules, name, function() {
2005 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2006 "the module name or forgot to load it. If registering a module ensure that you " +
2007 "specify the dependencies as the second argument.", name);
2010 /** @type {!Array.<Array.<*>>} */
2011 var invokeQueue = [];
2013 /** @type {!Array.<Function>} */
2014 var configBlocks = [];
2016 /** @type {!Array.<Function>} */
2019 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2021 /** @type {angular.Module} */
2022 var moduleInstance = {
2024 _invokeQueue: invokeQueue,
2025 _configBlocks: configBlocks,
2026 _runBlocks: runBlocks,
2030 * @name angular.Module#requires
2034 * Holds the list of modules which the injector will load before the current module is
2041 * @name angular.Module#name
2045 * Name of the module.
2052 * @name angular.Module#provider
2054 * @param {string} name service name
2055 * @param {Function} providerType Construction function for creating new instance of the
2058 * See {@link auto.$provide#provider $provide.provider()}.
2060 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2064 * @name angular.Module#factory
2066 * @param {string} name service name
2067 * @param {Function} providerFunction Function for creating new instance of the service.
2069 * See {@link auto.$provide#factory $provide.factory()}.
2071 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2075 * @name angular.Module#service
2077 * @param {string} name service name
2078 * @param {Function} constructor A constructor function that will be instantiated.
2080 * See {@link auto.$provide#service $provide.service()}.
2082 service: invokeLaterAndSetModuleName('$provide', 'service'),
2086 * @name angular.Module#value
2088 * @param {string} name service name
2089 * @param {*} object Service instance object.
2091 * See {@link auto.$provide#value $provide.value()}.
2093 value: invokeLater('$provide', 'value'),
2097 * @name angular.Module#constant
2099 * @param {string} name constant name
2100 * @param {*} object Constant value.
2102 * Because the constants are fixed, they get applied before other provide methods.
2103 * See {@link auto.$provide#constant $provide.constant()}.
2105 constant: invokeLater('$provide', 'constant', 'unshift'),
2109 * @name angular.Module#decorator
2111 * @param {string} The name of the service to decorate.
2112 * @param {Function} This function will be invoked when the service needs to be
2113 * instantiated and should return the decorated service instance.
2115 * See {@link auto.$provide#decorator $provide.decorator()}.
2117 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2121 * @name angular.Module#animation
2123 * @param {string} name animation name
2124 * @param {Function} animationFactory Factory function for creating new instance of an
2128 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2131 * Defines an animation hook that can be later used with
2132 * {@link $animate $animate} service and directives that use this service.
2135 * module.animation('.animation-name', function($inject1, $inject2) {
2137 * eventName : function(element, done) {
2138 * //code to run the animation
2139 * //once complete, then run done()
2140 * return function cancellationFunction(element) {
2141 * //code to cancel the animation
2148 * See {@link ng.$animateProvider#register $animateProvider.register()} and
2149 * {@link ngAnimate ngAnimate module} for more information.
2151 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2155 * @name angular.Module#filter
2157 * @param {string} name Filter name - this must be a valid angular expression identifier
2158 * @param {Function} filterFactory Factory function for creating new instance of filter.
2160 * See {@link ng.$filterProvider#register $filterProvider.register()}.
2162 * <div class="alert alert-warning">
2163 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2164 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2165 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2166 * (`myapp_subsection_filterx`).
2169 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2173 * @name angular.Module#controller
2175 * @param {string|Object} name Controller name, or an object map of controllers where the
2176 * keys are the names and the values are the constructors.
2177 * @param {Function} constructor Controller constructor function.
2179 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2181 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2185 * @name angular.Module#directive
2187 * @param {string|Object} name Directive name, or an object map of directives where the
2188 * keys are the names and the values are the factories.
2189 * @param {Function} directiveFactory Factory function for creating new instance of
2192 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2194 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2198 * @name angular.Module#config
2200 * @param {Function} configFn Execute this function on module load. Useful for service
2203 * Use this method to register work which needs to be performed on module loading.
2204 * For more about how to configure services, see
2205 * {@link providers#provider-recipe Provider Recipe}.
2211 * @name angular.Module#run
2213 * @param {Function} initializationFn Execute this function after injector creation.
2214 * Useful for application initialization.
2216 * Use this method to register work which should be performed when the injector is done
2217 * loading all modules.
2219 run: function(block) {
2220 runBlocks.push(block);
2229 return moduleInstance;
2232 * @param {string} provider
2233 * @param {string} method
2234 * @param {String=} insertMethod
2235 * @returns {angular.Module}
2237 function invokeLater(provider, method, insertMethod, queue) {
2238 if (!queue) queue = invokeQueue;
2240 queue[insertMethod || 'push']([provider, method, arguments]);
2241 return moduleInstance;
2246 * @param {string} provider
2247 * @param {string} method
2248 * @returns {angular.Module}
2250 function invokeLaterAndSetModuleName(provider, method) {
2251 return function(recipeName, factoryFunction) {
2252 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2253 invokeQueue.push([provider, method, arguments]);
2254 return moduleInstance;
2263 /* global: toDebugString: true */
2265 function serializeObject(obj) {
2268 return JSON.stringify(obj, function(key, val) {
2269 val = toJsonReplacer(key, val);
2270 if (isObject(val)) {
2272 if (seen.indexOf(val) >= 0) return '...';
2280 function toDebugString(obj) {
2281 if (typeof obj === 'function') {
2282 return obj.toString().replace(/ \{[\s\S]*$/, '');
2283 } else if (isUndefined(obj)) {
2285 } else if (typeof obj !== 'string') {
2286 return serializeObject(obj);
2291 /* global angularModule: true,
2296 htmlAnchorDirective,
2305 ngBindHtmlDirective,
2306 ngBindTemplateDirective,
2308 ngClassEvenDirective,
2309 ngClassOddDirective,
2311 ngControllerDirective,
2316 ngIncludeFillContentDirective,
2318 ngNonBindableDirective,
2319 ngPluralizeDirective,
2324 ngSwitchWhenDirective,
2325 ngSwitchDefaultDirective,
2327 ngTranscludeDirective,
2340 ngModelOptionsDirective,
2341 ngAttributeAliasDirectives,
2344 $AnchorScrollProvider,
2346 $CoreAnimateCssProvider,
2347 $$CoreAnimateQueueProvider,
2348 $$CoreAnimateRunnerProvider,
2350 $CacheFactoryProvider,
2351 $ControllerProvider,
2353 $ExceptionHandlerProvider,
2355 $$ForceReflowProvider,
2356 $InterpolateProvider,
2360 $HttpParamSerializerProvider,
2361 $HttpParamSerializerJQLikeProvider,
2362 $HttpBackendProvider,
2363 $xhrFactoryProvider,
2370 $$SanitizeUriProvider,
2372 $SceDelegateProvider,
2374 $TemplateCacheProvider,
2375 $TemplateRequestProvider,
2376 $$TestabilityProvider,
2381 $$CookieReaderProvider
2387 * @name angular.version
2390 * An object that contains information about the current AngularJS version.
2392 * This object has the following properties:
2394 * - `full` – `{string}` – Full version string, such as "0.9.18".
2395 * - `major` – `{number}` – Major version number, such as "0".
2396 * - `minor` – `{number}` – Minor version number, such as "9".
2397 * - `dot` – `{number}` – Dot version number, such as "18".
2398 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2401 full: '1.4.8', // all of these placeholder strings will be replaced by grunt's
2402 major: 1, // package task
2405 codeName: 'ice-manipulation'
2409 function publishExternalAPI(angular) {
2411 'bootstrap': bootstrap,
2418 'injector': createInjector,
2422 'fromJson': fromJson,
2423 'identity': identity,
2424 'isUndefined': isUndefined,
2425 'isDefined': isDefined,
2426 'isString': isString,
2427 'isFunction': isFunction,
2428 'isObject': isObject,
2429 'isNumber': isNumber,
2430 'isElement': isElement,
2434 'lowercase': lowercase,
2435 'uppercase': uppercase,
2436 'callbacks': {counter: 0},
2437 'getTestability': getTestability,
2440 'reloadWithDebugInfo': reloadWithDebugInfo
2443 angularModule = setupModuleLoader(window);
2445 angularModule('ng', ['ngLocale'], ['$provide',
2446 function ngModule($provide) {
2447 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2449 $$sanitizeUri: $$SanitizeUriProvider
2451 $provide.provider('$compile', $CompileProvider).
2453 a: htmlAnchorDirective,
2454 input: inputDirective,
2455 textarea: inputDirective,
2456 form: formDirective,
2457 script: scriptDirective,
2458 select: selectDirective,
2459 style: styleDirective,
2460 option: optionDirective,
2461 ngBind: ngBindDirective,
2462 ngBindHtml: ngBindHtmlDirective,
2463 ngBindTemplate: ngBindTemplateDirective,
2464 ngClass: ngClassDirective,
2465 ngClassEven: ngClassEvenDirective,
2466 ngClassOdd: ngClassOddDirective,
2467 ngCloak: ngCloakDirective,
2468 ngController: ngControllerDirective,
2469 ngForm: ngFormDirective,
2470 ngHide: ngHideDirective,
2471 ngIf: ngIfDirective,
2472 ngInclude: ngIncludeDirective,
2473 ngInit: ngInitDirective,
2474 ngNonBindable: ngNonBindableDirective,
2475 ngPluralize: ngPluralizeDirective,
2476 ngRepeat: ngRepeatDirective,
2477 ngShow: ngShowDirective,
2478 ngStyle: ngStyleDirective,
2479 ngSwitch: ngSwitchDirective,
2480 ngSwitchWhen: ngSwitchWhenDirective,
2481 ngSwitchDefault: ngSwitchDefaultDirective,
2482 ngOptions: ngOptionsDirective,
2483 ngTransclude: ngTranscludeDirective,
2484 ngModel: ngModelDirective,
2485 ngList: ngListDirective,
2486 ngChange: ngChangeDirective,
2487 pattern: patternDirective,
2488 ngPattern: patternDirective,
2489 required: requiredDirective,
2490 ngRequired: requiredDirective,
2491 minlength: minlengthDirective,
2492 ngMinlength: minlengthDirective,
2493 maxlength: maxlengthDirective,
2494 ngMaxlength: maxlengthDirective,
2495 ngValue: ngValueDirective,
2496 ngModelOptions: ngModelOptionsDirective
2499 ngInclude: ngIncludeFillContentDirective
2501 directive(ngAttributeAliasDirectives).
2502 directive(ngEventDirectives);
2504 $anchorScroll: $AnchorScrollProvider,
2505 $animate: $AnimateProvider,
2506 $animateCss: $CoreAnimateCssProvider,
2507 $$animateQueue: $$CoreAnimateQueueProvider,
2508 $$AnimateRunner: $$CoreAnimateRunnerProvider,
2509 $browser: $BrowserProvider,
2510 $cacheFactory: $CacheFactoryProvider,
2511 $controller: $ControllerProvider,
2512 $document: $DocumentProvider,
2513 $exceptionHandler: $ExceptionHandlerProvider,
2514 $filter: $FilterProvider,
2515 $$forceReflow: $$ForceReflowProvider,
2516 $interpolate: $InterpolateProvider,
2517 $interval: $IntervalProvider,
2518 $http: $HttpProvider,
2519 $httpParamSerializer: $HttpParamSerializerProvider,
2520 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2521 $httpBackend: $HttpBackendProvider,
2522 $xhrFactory: $xhrFactoryProvider,
2523 $location: $LocationProvider,
2525 $parse: $ParseProvider,
2526 $rootScope: $RootScopeProvider,
2530 $sceDelegate: $SceDelegateProvider,
2531 $sniffer: $SnifferProvider,
2532 $templateCache: $TemplateCacheProvider,
2533 $templateRequest: $TemplateRequestProvider,
2534 $$testability: $$TestabilityProvider,
2535 $timeout: $TimeoutProvider,
2536 $window: $WindowProvider,
2537 $$rAF: $$RAFProvider,
2538 $$jqLite: $$jqLiteProvider,
2539 $$HashMap: $$HashMapProvider,
2540 $$cookieReader: $$CookieReaderProvider
2546 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2547 * Any commits to this file should be reviewed with security in mind. *
2548 * Changes to this file can potentially create security vulnerabilities. *
2549 * An approval from 2 Core members with history of modifying *
2550 * this file is required. *
2552 * Does the change somehow allow for arbitrary javascript to be executed? *
2553 * Or allows for someone to change the prototype of built-in objects? *
2554 * Or gives undesired access to variables likes document or window? *
2555 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2557 /* global JQLitePrototype: true,
2558 addEventListenerFn: true,
2559 removeEventListenerFn: true,
2564 //////////////////////////////////
2566 //////////////////////////////////
2570 * @name angular.element
2575 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2577 * If jQuery is available, `angular.element` is an alias for the
2578 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2579 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2581 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2582 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2583 * commonly needed functionality with the goal of having a very small footprint.</div>
2585 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2587 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2588 * jqLite; they are never raw DOM references.</div>
2590 * ## Angular's jqLite
2591 * jqLite provides only the following jQuery methods:
2593 * - [`addClass()`](http://api.jquery.com/addClass/)
2594 * - [`after()`](http://api.jquery.com/after/)
2595 * - [`append()`](http://api.jquery.com/append/)
2596 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2597 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2598 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2599 * - [`clone()`](http://api.jquery.com/clone/)
2600 * - [`contents()`](http://api.jquery.com/contents/)
2601 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
2602 * - [`data()`](http://api.jquery.com/data/)
2603 * - [`detach()`](http://api.jquery.com/detach/)
2604 * - [`empty()`](http://api.jquery.com/empty/)
2605 * - [`eq()`](http://api.jquery.com/eq/)
2606 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2607 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2608 * - [`html()`](http://api.jquery.com/html/)
2609 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2610 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2611 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2612 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2613 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2614 * - [`prepend()`](http://api.jquery.com/prepend/)
2615 * - [`prop()`](http://api.jquery.com/prop/)
2616 * - [`ready()`](http://api.jquery.com/ready/)
2617 * - [`remove()`](http://api.jquery.com/remove/)
2618 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2619 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2620 * - [`removeData()`](http://api.jquery.com/removeData/)
2621 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2622 * - [`text()`](http://api.jquery.com/text/)
2623 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2624 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2625 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2626 * - [`val()`](http://api.jquery.com/val/)
2627 * - [`wrap()`](http://api.jquery.com/wrap/)
2629 * ## jQuery/jqLite Extras
2630 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2633 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2634 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2635 * element before it is removed.
2638 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2639 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2640 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2642 * - `injector()` - retrieves the injector of the current element or its parent.
2643 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2644 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2646 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2647 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2648 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2649 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2650 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2651 * parent element is reached.
2653 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2654 * @returns {Object} jQuery object.
2657 JQLite.expando = 'ng339';
2659 var jqCache = JQLite.cache = {},
2661 addEventListenerFn = function(element, type, fn) {
2662 element.addEventListener(type, fn, false);
2664 removeEventListenerFn = function(element, type, fn) {
2665 element.removeEventListener(type, fn, false);
2669 * !!! This is an undocumented "private" function !!!
2671 JQLite._data = function(node) {
2672 //jQuery always returns an object on cache miss
2673 return this.cache[node[this.expando]] || {};
2676 function jqNextId() { return ++jqId; }
2679 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2680 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2681 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2682 var jqLiteMinErr = minErr('jqLite');
2685 * Converts snake_case to camelCase.
2686 * Also there is special case for Moz prefix starting with upper case letter.
2687 * @param name Name to normalize
2689 function camelCase(name) {
2691 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2692 return offset ? letter.toUpperCase() : letter;
2694 replace(MOZ_HACK_REGEXP, 'Moz$1');
2697 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2698 var HTML_REGEXP = /<|&#?\w+;/;
2699 var TAG_NAME_REGEXP = /<([\w:-]+)/;
2700 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2703 'option': [1, '<select multiple="multiple">', '</select>'],
2705 'thead': [1, '<table>', '</table>'],
2706 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2707 'tr': [2, '<table><tbody>', '</tbody></table>'],
2708 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2709 '_default': [0, "", ""]
2712 wrapMap.optgroup = wrapMap.option;
2713 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2714 wrapMap.th = wrapMap.td;
2717 function jqLiteIsTextNode(html) {
2718 return !HTML_REGEXP.test(html);
2721 function jqLiteAcceptsData(node) {
2722 // The window object can accept data but has no nodeType
2723 // Otherwise we are only interested in elements (1) and documents (9)
2724 var nodeType = node.nodeType;
2725 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2728 function jqLiteHasData(node) {
2729 for (var key in jqCache[node.ng339]) {
2735 function jqLiteBuildFragment(html, context) {
2737 fragment = context.createDocumentFragment(),
2740 if (jqLiteIsTextNode(html)) {
2741 // Convert non-html into a text node
2742 nodes.push(context.createTextNode(html));
2744 // Convert html into DOM nodes
2745 tmp = tmp || fragment.appendChild(context.createElement("div"));
2746 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2747 wrap = wrapMap[tag] || wrapMap._default;
2748 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2750 // Descend through wrappers to the right content
2753 tmp = tmp.lastChild;
2756 nodes = concat(nodes, tmp.childNodes);
2758 tmp = fragment.firstChild;
2759 tmp.textContent = "";
2762 // Remove wrapper from fragment
2763 fragment.textContent = "";
2764 fragment.innerHTML = ""; // Clear inner HTML
2765 forEach(nodes, function(node) {
2766 fragment.appendChild(node);
2772 function jqLiteParseHTML(html, context) {
2773 context = context || document;
2776 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2777 return [context.createElement(parsed[1])];
2780 if ((parsed = jqLiteBuildFragment(html, context))) {
2781 return parsed.childNodes;
2788 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2789 var jqLiteContains = Node.prototype.contains || function(arg) {
2790 // jshint bitwise: false
2791 return !!(this.compareDocumentPosition(arg) & 16);
2792 // jshint bitwise: true
2795 /////////////////////////////////////////////
2796 function JQLite(element) {
2797 if (element instanceof JQLite) {
2803 if (isString(element)) {
2804 element = trim(element);
2807 if (!(this instanceof JQLite)) {
2808 if (argIsString && element.charAt(0) != '<') {
2809 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2811 return new JQLite(element);
2815 jqLiteAddNodes(this, jqLiteParseHTML(element));
2817 jqLiteAddNodes(this, element);
2821 function jqLiteClone(element) {
2822 return element.cloneNode(true);
2825 function jqLiteDealoc(element, onlyDescendants) {
2826 if (!onlyDescendants) jqLiteRemoveData(element);
2828 if (element.querySelectorAll) {
2829 var descendants = element.querySelectorAll('*');
2830 for (var i = 0, l = descendants.length; i < l; i++) {
2831 jqLiteRemoveData(descendants[i]);
2836 function jqLiteOff(element, type, fn, unsupported) {
2837 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2839 var expandoStore = jqLiteExpandoStore(element);
2840 var events = expandoStore && expandoStore.events;
2841 var handle = expandoStore && expandoStore.handle;
2843 if (!handle) return; //no listeners registered
2846 for (type in events) {
2847 if (type !== '$destroy') {
2848 removeEventListenerFn(element, type, handle);
2850 delete events[type];
2854 var removeHandler = function(type) {
2855 var listenerFns = events[type];
2856 if (isDefined(fn)) {
2857 arrayRemove(listenerFns || [], fn);
2859 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2860 removeEventListenerFn(element, type, handle);
2861 delete events[type];
2865 forEach(type.split(' '), function(type) {
2866 removeHandler(type);
2867 if (MOUSE_EVENT_MAP[type]) {
2868 removeHandler(MOUSE_EVENT_MAP[type]);
2874 function jqLiteRemoveData(element, name) {
2875 var expandoId = element.ng339;
2876 var expandoStore = expandoId && jqCache[expandoId];
2880 delete expandoStore.data[name];
2884 if (expandoStore.handle) {
2885 if (expandoStore.events.$destroy) {
2886 expandoStore.handle({}, '$destroy');
2890 delete jqCache[expandoId];
2891 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2896 function jqLiteExpandoStore(element, createIfNecessary) {
2897 var expandoId = element.ng339,
2898 expandoStore = expandoId && jqCache[expandoId];
2900 if (createIfNecessary && !expandoStore) {
2901 element.ng339 = expandoId = jqNextId();
2902 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2905 return expandoStore;
2909 function jqLiteData(element, key, value) {
2910 if (jqLiteAcceptsData(element)) {
2912 var isSimpleSetter = isDefined(value);
2913 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2914 var massGetter = !key;
2915 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2916 var data = expandoStore && expandoStore.data;
2918 if (isSimpleSetter) { // data('key', value)
2921 if (massGetter) { // data()
2924 if (isSimpleGetter) { // data('key')
2925 // don't force creation of expandoStore if it doesn't exist yet
2926 return data && data[key];
2927 } else { // mass-setter: data({key1: val1, key2: val2})
2935 function jqLiteHasClass(element, selector) {
2936 if (!element.getAttribute) return false;
2937 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2938 indexOf(" " + selector + " ") > -1);
2941 function jqLiteRemoveClass(element, cssClasses) {
2942 if (cssClasses && element.setAttribute) {
2943 forEach(cssClasses.split(' '), function(cssClass) {
2944 element.setAttribute('class', trim(
2945 (" " + (element.getAttribute('class') || '') + " ")
2946 .replace(/[\n\t]/g, " ")
2947 .replace(" " + trim(cssClass) + " ", " "))
2953 function jqLiteAddClass(element, cssClasses) {
2954 if (cssClasses && element.setAttribute) {
2955 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2956 .replace(/[\n\t]/g, " ");
2958 forEach(cssClasses.split(' '), function(cssClass) {
2959 cssClass = trim(cssClass);
2960 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2961 existingClasses += cssClass + ' ';
2965 element.setAttribute('class', trim(existingClasses));
2970 function jqLiteAddNodes(root, elements) {
2971 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2975 // if a Node (the most common case)
2976 if (elements.nodeType) {
2977 root[root.length++] = elements;
2979 var length = elements.length;
2981 // if an Array or NodeList and not a Window
2982 if (typeof length === 'number' && elements.window !== elements) {
2984 for (var i = 0; i < length; i++) {
2985 root[root.length++] = elements[i];
2989 root[root.length++] = elements;
2996 function jqLiteController(element, name) {
2997 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3000 function jqLiteInheritedData(element, name, value) {
3001 // if element is the document object work with the html element instead
3002 // this makes $(document).scope() possible
3003 if (element.nodeType == NODE_TYPE_DOCUMENT) {
3004 element = element.documentElement;
3006 var names = isArray(name) ? name : [name];
3009 for (var i = 0, ii = names.length; i < ii; i++) {
3010 if (isDefined(value = jqLite.data(element, names[i]))) return value;
3013 // If dealing with a document fragment node with a host element, and no parent, use the host
3014 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3015 // to lookup parent controllers.
3016 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3020 function jqLiteEmpty(element) {
3021 jqLiteDealoc(element, true);
3022 while (element.firstChild) {
3023 element.removeChild(element.firstChild);
3027 function jqLiteRemove(element, keepData) {
3028 if (!keepData) jqLiteDealoc(element);
3029 var parent = element.parentNode;
3030 if (parent) parent.removeChild(element);
3034 function jqLiteDocumentLoaded(action, win) {
3035 win = win || window;
3036 if (win.document.readyState === 'complete') {
3037 // Force the action to be run async for consistent behaviour
3038 // from the action's point of view
3039 // i.e. it will definitely not be in a $apply
3040 win.setTimeout(action);
3042 // No need to unbind this handler as load is only ever called once
3043 jqLite(win).on('load', action);
3047 //////////////////////////////////////////
3048 // Functions which are declared directly.
3049 //////////////////////////////////////////
3050 var JQLitePrototype = JQLite.prototype = {
3051 ready: function(fn) {
3054 function trigger() {
3060 // check if document is already loaded
3061 if (document.readyState === 'complete') {
3062 setTimeout(trigger);
3064 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3065 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3067 JQLite(window).on('load', trigger); // fallback to window.onload for others
3071 toString: function() {
3073 forEach(this, function(e) { value.push('' + e);});
3074 return '[' + value.join(', ') + ']';
3077 eq: function(index) {
3078 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3087 //////////////////////////////////////////
3088 // Functions iterating getter/setters.
3089 // these functions return self on setter and
3091 //////////////////////////////////////////
3092 var BOOLEAN_ATTR = {};
3093 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3094 BOOLEAN_ATTR[lowercase(value)] = value;
3096 var BOOLEAN_ELEMENTS = {};
3097 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3098 BOOLEAN_ELEMENTS[value] = true;
3100 var ALIASED_ATTR = {
3101 'ngMinlength': 'minlength',
3102 'ngMaxlength': 'maxlength',
3105 'ngPattern': 'pattern'
3108 function getBooleanAttrName(element, name) {
3109 // check dom last since we will most likely fail on name
3110 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3112 // booleanAttr is here twice to minimize DOM access
3113 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3116 function getAliasedAttrName(name) {
3117 return ALIASED_ATTR[name];
3122 removeData: jqLiteRemoveData,
3123 hasData: jqLiteHasData
3124 }, function(fn, name) {
3130 inheritedData: jqLiteInheritedData,
3132 scope: function(element) {
3133 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3134 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3137 isolateScope: function(element) {
3138 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3139 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3142 controller: jqLiteController,
3144 injector: function(element) {
3145 return jqLiteInheritedData(element, '$injector');
3148 removeAttr: function(element, name) {
3149 element.removeAttribute(name);
3152 hasClass: jqLiteHasClass,
3154 css: function(element, name, value) {
3155 name = camelCase(name);
3157 if (isDefined(value)) {
3158 element.style[name] = value;
3160 return element.style[name];
3164 attr: function(element, name, value) {
3165 var nodeType = element.nodeType;
3166 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3169 var lowercasedName = lowercase(name);
3170 if (BOOLEAN_ATTR[lowercasedName]) {
3171 if (isDefined(value)) {
3173 element[name] = true;
3174 element.setAttribute(name, lowercasedName);
3176 element[name] = false;
3177 element.removeAttribute(lowercasedName);
3180 return (element[name] ||
3181 (element.attributes.getNamedItem(name) || noop).specified)
3185 } else if (isDefined(value)) {
3186 element.setAttribute(name, value);
3187 } else if (element.getAttribute) {
3188 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3189 // some elements (e.g. Document) don't have get attribute, so return undefined
3190 var ret = element.getAttribute(name, 2);
3191 // normalize non-existing attributes to undefined (as jQuery)
3192 return ret === null ? undefined : ret;
3196 prop: function(element, name, value) {
3197 if (isDefined(value)) {
3198 element[name] = value;
3200 return element[name];
3208 function getText(element, value) {
3209 if (isUndefined(value)) {
3210 var nodeType = element.nodeType;
3211 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3213 element.textContent = value;
3217 val: function(element, value) {
3218 if (isUndefined(value)) {
3219 if (element.multiple && nodeName_(element) === 'select') {
3221 forEach(element.options, function(option) {
3222 if (option.selected) {
3223 result.push(option.value || option.text);
3226 return result.length === 0 ? null : result;
3228 return element.value;
3230 element.value = value;
3233 html: function(element, value) {
3234 if (isUndefined(value)) {
3235 return element.innerHTML;
3237 jqLiteDealoc(element, true);
3238 element.innerHTML = value;
3242 }, function(fn, name) {
3244 * Properties: writes return selection, reads return first value
3246 JQLite.prototype[name] = function(arg1, arg2) {
3248 var nodeCount = this.length;
3250 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3251 // in a way that survives minification.
3252 // jqLiteEmpty takes no arguments but is a setter.
3253 if (fn !== jqLiteEmpty &&
3254 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3255 if (isObject(arg1)) {
3257 // we are a write, but the object properties are the key/values
3258 for (i = 0; i < nodeCount; i++) {
3259 if (fn === jqLiteData) {
3260 // data() takes the whole object in jQuery
3264 fn(this[i], key, arg1[key]);
3268 // return self for chaining
3271 // we are a read, so read the first child.
3272 // TODO: do we still need this?
3274 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3275 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3276 for (var j = 0; j < jj; j++) {
3277 var nodeValue = fn(this[j], arg1, arg2);
3278 value = value ? value + nodeValue : nodeValue;
3283 // we are a write, so apply to all children
3284 for (i = 0; i < nodeCount; i++) {
3285 fn(this[i], arg1, arg2);
3287 // return self for chaining
3293 function createEventHandler(element, events) {
3294 var eventHandler = function(event, type) {
3295 // jQuery specific api
3296 event.isDefaultPrevented = function() {
3297 return event.defaultPrevented;
3300 var eventFns = events[type || event.type];
3301 var eventFnsLength = eventFns ? eventFns.length : 0;
3303 if (!eventFnsLength) return;
3305 if (isUndefined(event.immediatePropagationStopped)) {
3306 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3307 event.stopImmediatePropagation = function() {
3308 event.immediatePropagationStopped = true;
3310 if (event.stopPropagation) {
3311 event.stopPropagation();
3314 if (originalStopImmediatePropagation) {
3315 originalStopImmediatePropagation.call(event);
3320 event.isImmediatePropagationStopped = function() {
3321 return event.immediatePropagationStopped === true;
3324 // Some events have special handlers that wrap the real handler
3325 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3327 // Copy event handlers in case event handlers array is modified during execution.
3328 if ((eventFnsLength > 1)) {
3329 eventFns = shallowCopy(eventFns);
3332 for (var i = 0; i < eventFnsLength; i++) {
3333 if (!event.isImmediatePropagationStopped()) {
3334 handlerWrapper(element, event, eventFns[i]);
3339 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3340 // events on `element`
3341 eventHandler.elem = element;
3342 return eventHandler;
3345 function defaultHandlerWrapper(element, event, handler) {
3346 handler.call(element, event);
3349 function specialMouseHandlerWrapper(target, event, handler) {
3350 // Refer to jQuery's implementation of mouseenter & mouseleave
3351 // Read about mouseenter and mouseleave:
3352 // http://www.quirksmode.org/js/events_mouse.html#link8
3353 var related = event.relatedTarget;
3354 // For mousenter/leave call the handler if related is outside the target.
3355 // NB: No relatedTarget if the mouse left/entered the browser window
3356 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3357 handler.call(target, event);
3361 //////////////////////////////////////////
3362 // Functions iterating traversal.
3363 // These functions chain results into a single
3365 //////////////////////////////////////////
3367 removeData: jqLiteRemoveData,
3369 on: function jqLiteOn(element, type, fn, unsupported) {
3370 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3372 // Do not add event handlers to non-elements because they will not be cleaned up.
3373 if (!jqLiteAcceptsData(element)) {
3377 var expandoStore = jqLiteExpandoStore(element, true);
3378 var events = expandoStore.events;
3379 var handle = expandoStore.handle;
3382 handle = expandoStore.handle = createEventHandler(element, events);
3385 // http://jsperf.com/string-indexof-vs-split
3386 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3387 var i = types.length;
3389 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3390 var eventFns = events[type];
3393 eventFns = events[type] = [];
3394 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3395 if (type !== '$destroy' && !noEventListener) {
3396 addEventListenerFn(element, type, handle);
3405 if (MOUSE_EVENT_MAP[type]) {
3406 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3407 addHandler(type, undefined, true);
3416 one: function(element, type, fn) {
3417 element = jqLite(element);
3419 //add the listener twice so that when it is called
3420 //you can remove the original function and still be
3421 //able to call element.off(ev, fn) normally
3422 element.on(type, function onFn() {
3423 element.off(type, fn);
3424 element.off(type, onFn);
3426 element.on(type, fn);
3429 replaceWith: function(element, replaceNode) {
3430 var index, parent = element.parentNode;
3431 jqLiteDealoc(element);
3432 forEach(new JQLite(replaceNode), function(node) {
3434 parent.insertBefore(node, index.nextSibling);
3436 parent.replaceChild(node, element);
3442 children: function(element) {
3444 forEach(element.childNodes, function(element) {
3445 if (element.nodeType === NODE_TYPE_ELEMENT) {
3446 children.push(element);
3452 contents: function(element) {
3453 return element.contentDocument || element.childNodes || [];
3456 append: function(element, node) {
3457 var nodeType = element.nodeType;
3458 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3460 node = new JQLite(node);
3462 for (var i = 0, ii = node.length; i < ii; i++) {
3463 var child = node[i];
3464 element.appendChild(child);
3468 prepend: function(element, node) {
3469 if (element.nodeType === NODE_TYPE_ELEMENT) {
3470 var index = element.firstChild;
3471 forEach(new JQLite(node), function(child) {
3472 element.insertBefore(child, index);
3477 wrap: function(element, wrapNode) {
3478 wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3479 var parent = element.parentNode;
3481 parent.replaceChild(wrapNode, element);
3483 wrapNode.appendChild(element);
3486 remove: jqLiteRemove,
3488 detach: function(element) {
3489 jqLiteRemove(element, true);
3492 after: function(element, newElement) {
3493 var index = element, parent = element.parentNode;
3494 newElement = new JQLite(newElement);
3496 for (var i = 0, ii = newElement.length; i < ii; i++) {
3497 var node = newElement[i];
3498 parent.insertBefore(node, index.nextSibling);
3503 addClass: jqLiteAddClass,
3504 removeClass: jqLiteRemoveClass,
3506 toggleClass: function(element, selector, condition) {
3508 forEach(selector.split(' '), function(className) {
3509 var classCondition = condition;
3510 if (isUndefined(classCondition)) {
3511 classCondition = !jqLiteHasClass(element, className);
3513 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3518 parent: function(element) {
3519 var parent = element.parentNode;
3520 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3523 next: function(element) {
3524 return element.nextElementSibling;
3527 find: function(element, selector) {
3528 if (element.getElementsByTagName) {
3529 return element.getElementsByTagName(selector);
3537 triggerHandler: function(element, event, extraParameters) {
3539 var dummyEvent, eventFnsCopy, handlerArgs;
3540 var eventName = event.type || event;
3541 var expandoStore = jqLiteExpandoStore(element);
3542 var events = expandoStore && expandoStore.events;
3543 var eventFns = events && events[eventName];
3546 // Create a dummy event to pass to the handlers
3548 preventDefault: function() { this.defaultPrevented = true; },
3549 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3550 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3551 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3552 stopPropagation: noop,
3557 // If a custom event was provided then extend our dummy event with it
3559 dummyEvent = extend(dummyEvent, event);
3562 // Copy event handlers in case event handlers array is modified during execution.
3563 eventFnsCopy = shallowCopy(eventFns);
3564 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3566 forEach(eventFnsCopy, function(fn) {
3567 if (!dummyEvent.isImmediatePropagationStopped()) {
3568 fn.apply(element, handlerArgs);
3573 }, function(fn, name) {
3575 * chaining functions
3577 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3580 for (var i = 0, ii = this.length; i < ii; i++) {
3581 if (isUndefined(value)) {
3582 value = fn(this[i], arg1, arg2, arg3);
3583 if (isDefined(value)) {
3584 // any function which returns a value needs to be wrapped
3585 value = jqLite(value);
3588 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3591 return isDefined(value) ? value : this;
3594 // bind legacy bind/unbind to on/off
3595 JQLite.prototype.bind = JQLite.prototype.on;
3596 JQLite.prototype.unbind = JQLite.prototype.off;
3600 // Provider for private $$jqLite service
3601 function $$jqLiteProvider() {
3602 this.$get = function $$jqLite() {
3603 return extend(JQLite, {
3604 hasClass: function(node, classes) {
3605 if (node.attr) node = node[0];
3606 return jqLiteHasClass(node, classes);
3608 addClass: function(node, classes) {
3609 if (node.attr) node = node[0];
3610 return jqLiteAddClass(node, classes);
3612 removeClass: function(node, classes) {
3613 if (node.attr) node = node[0];
3614 return jqLiteRemoveClass(node, classes);
3621 * Computes a hash of an 'obj'.
3624 * number is number as string
3625 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3626 * that is also assigned to the $$hashKey property of the object.
3629 * @returns {string} hash string such that the same input will have the same hash string.
3630 * The resulting string key is in 'type:hashKey' format.
3632 function hashKey(obj, nextUidFn) {
3633 var key = obj && obj.$$hashKey;
3636 if (typeof key === 'function') {
3637 key = obj.$$hashKey();
3642 var objType = typeof obj;
3643 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3644 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3646 key = objType + ':' + obj;
3653 * HashMap which can use objects as keys
3655 function HashMap(array, isolatedUid) {
3658 this.nextUid = function() {
3662 forEach(array, this.put, this);
3664 HashMap.prototype = {
3666 * Store key value pair
3667 * @param key key to store can be any type
3668 * @param value value to store can be any type
3670 put: function(key, value) {
3671 this[hashKey(key, this.nextUid)] = value;
3676 * @returns {Object} the value for the key
3678 get: function(key) {
3679 return this[hashKey(key, this.nextUid)];
3683 * Remove the key/value pair
3686 remove: function(key) {
3687 var value = this[key = hashKey(key, this.nextUid)];
3693 var $$HashMapProvider = [function() {
3694 this.$get = [function() {
3702 * @name angular.injector
3706 * Creates an injector object that can be used for retrieving services as well as for
3707 * dependency injection (see {@link guide/di dependency injection}).
3709 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3710 * {@link angular.module}. The `ng` module must be explicitly added.
3711 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3712 * disallows argument name annotation inference.
3713 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3718 * // create an injector
3719 * var $injector = angular.injector(['ng']);
3721 * // use the injector to kick off your application
3722 * // use the type inference to auto inject arguments, or use implicit injection
3723 * $injector.invoke(function($rootScope, $compile, $document) {
3724 * $compile($document)($rootScope);
3725 * $rootScope.$digest();
3729 * Sometimes you want to get access to the injector of a currently running Angular app
3730 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3731 * application has been bootstrapped. You can do this using the extra `injector()` added
3732 * to JQuery/jqLite elements. See {@link angular.element}.
3734 * *This is fairly rare but could be the case if a third party library is injecting the
3737 * In the following example a new block of HTML containing a `ng-controller`
3738 * directive is added to the end of the document body by JQuery. We then compile and link
3739 * it into the current AngularJS scope.
3742 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3743 * $(document.body).append($div);
3745 * angular.element(document).injector().invoke(function($compile) {
3746 * var scope = angular.element($div).scope();
3747 * $compile($div)(scope);
3758 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3761 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3762 var FN_ARG_SPLIT = /,/;
3763 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3764 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3765 var $injectorMinErr = minErr('$injector');
3767 function anonFn(fn) {
3768 // For anonymous functions, showing at the very least the function signature can help in
3770 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3771 args = fnText.match(FN_ARGS);
3773 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3778 function annotate(fn, strictDi, name) {
3784 if (typeof fn === 'function') {
3785 if (!($inject = fn.$inject)) {
3789 if (!isString(name) || !name) {
3790 name = fn.name || anonFn(fn);
3792 throw $injectorMinErr('strictdi',
3793 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3795 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3796 argDecl = fnText.match(FN_ARGS);
3797 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3798 arg.replace(FN_ARG, function(all, underscore, name) {
3803 fn.$inject = $inject;
3805 } else if (isArray(fn)) {
3806 last = fn.length - 1;
3807 assertArgFn(fn[last], 'fn');
3808 $inject = fn.slice(0, last);
3810 assertArgFn(fn, 'fn', true);
3815 ///////////////////////////////////////
3823 * `$injector` is used to retrieve object instances as defined by
3824 * {@link auto.$provide provider}, instantiate types, invoke methods,
3827 * The following always holds true:
3830 * var $injector = angular.injector();
3831 * expect($injector.get('$injector')).toBe($injector);
3832 * expect($injector.invoke(function($injector) {
3834 * })).toBe($injector);
3837 * # Injection Function Annotation
3839 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3840 * following are all valid ways of annotating function with injection arguments and are equivalent.
3843 * // inferred (only works if code not minified/obfuscated)
3844 * $injector.invoke(function(serviceA){});
3847 * function explicit(serviceA) {};
3848 * explicit.$inject = ['serviceA'];
3849 * $injector.invoke(explicit);
3852 * $injector.invoke(['serviceA', function(serviceA){}]);
3857 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3858 * can then be parsed and the function arguments can be extracted. This method of discovering
3859 * annotations is disallowed when the injector is in strict mode.
3860 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3863 * ## `$inject` Annotation
3864 * By adding an `$inject` property onto a function the injection parameters can be specified.
3867 * As an array of injection names, where the last item in the array is the function to call.
3872 * @name $injector#get
3875 * Return an instance of the service.
3877 * @param {string} name The name of the instance to retrieve.
3878 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3879 * @return {*} The instance.
3884 * @name $injector#invoke
3887 * Invoke the method and supply the method arguments from the `$injector`.
3889 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3890 * injected according to the {@link guide/di $inject Annotation} rules.
3891 * @param {Object=} self The `this` for the invoked method.
3892 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3893 * object first, before the `$injector` is consulted.
3894 * @returns {*} the value returned by the invoked `fn` function.
3899 * @name $injector#has
3902 * Allows the user to query if the particular service exists.
3904 * @param {string} name Name of the service to query.
3905 * @returns {boolean} `true` if injector has given service.
3910 * @name $injector#instantiate
3912 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3913 * operator, and supplies all of the arguments to the constructor function as specified by the
3914 * constructor annotation.
3916 * @param {Function} Type Annotated constructor function.
3917 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3918 * object first, before the `$injector` is consulted.
3919 * @returns {Object} new instance of `Type`.
3924 * @name $injector#annotate
3927 * Returns an array of service names which the function is requesting for injection. This API is
3928 * used by the injector to determine which services need to be injected into the function when the
3929 * function is invoked. There are three ways in which the function can be annotated with the needed
3934 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3935 * by converting the function into a string using `toString()` method and extracting the argument
3939 * function MyController($scope, $route) {
3944 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3947 * You can disallow this method by using strict injection mode.
3949 * This method does not work with code minification / obfuscation. For this reason the following
3950 * annotation strategies are supported.
3952 * # The `$inject` property
3954 * If a function has an `$inject` property and its value is an array of strings, then the strings
3955 * represent names of services to be injected into the function.
3958 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
3961 * // Define function dependencies
3962 * MyController['$inject'] = ['$scope', '$route'];
3965 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3968 * # The array notation
3970 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3971 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3972 * a way that survives minification is a better choice:
3975 * // We wish to write this (not minification / obfuscation safe)
3976 * injector.invoke(function($compile, $rootScope) {
3980 * // We are forced to write break inlining
3981 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3984 * tmpFn.$inject = ['$compile', '$rootScope'];
3985 * injector.invoke(tmpFn);
3987 * // To better support inline function the inline annotation is supported
3988 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3993 * expect(injector.annotate(
3994 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3995 * ).toEqual(['$compile', '$rootScope']);
3998 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3999 * be retrieved as described above.
4001 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4003 * @returns {Array.<string>} The names of the services which the function requires.
4015 * The {@link auto.$provide $provide} service has a number of methods for registering components
4016 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4017 * {@link angular.Module}.
4019 * An Angular **service** is a singleton object created by a **service factory**. These **service
4020 * factories** are functions which, in turn, are created by a **service provider**.
4021 * The **service providers** are constructor functions. When instantiated they must contain a
4022 * property called `$get`, which holds the **service factory** function.
4024 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4025 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4026 * function to get the instance of the **service**.
4028 * Often services have no configuration options and there is no need to add methods to the service
4029 * provider. The provider will be no more than a constructor function with a `$get` property. For
4030 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4031 * services without specifying a provider.
4033 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4034 * {@link auto.$injector $injector}
4035 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4036 * providers and services.
4037 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4038 * services, not providers.
4039 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4040 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
4041 * given factory function.
4042 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4043 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4044 * a new object using the given constructor function.
4046 * See the individual methods for more information and examples.
4051 * @name $provide#provider
4054 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4055 * are constructor functions, whose instances are responsible for "providing" a factory for a
4058 * Service provider names start with the name of the service they provide followed by `Provider`.
4059 * For example, the {@link ng.$log $log} service has a provider called
4060 * {@link ng.$logProvider $logProvider}.
4062 * Service provider objects can have additional methods which allow configuration of the provider
4063 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4064 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4065 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4066 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4069 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4071 * @param {(Object|function())} provider If the provider is:
4073 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4074 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4075 * - `Constructor`: a new instance of the provider will be created using
4076 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4078 * @returns {Object} registered provider instance
4082 * The following example shows how to create a simple event tracking service and register it using
4083 * {@link auto.$provide#provider $provide.provider()}.
4086 * // Define the eventTracker provider
4087 * function EventTrackerProvider() {
4088 * var trackingUrl = '/track';
4090 * // A provider method for configuring where the tracked events should been saved
4091 * this.setTrackingUrl = function(url) {
4092 * trackingUrl = url;
4095 * // The service factory function
4096 * this.$get = ['$http', function($http) {
4097 * var trackedEvents = {};
4099 * // Call this to track an event
4100 * event: function(event) {
4101 * var count = trackedEvents[event] || 0;
4103 * trackedEvents[event] = count;
4106 * // Call this to save the tracked events to the trackingUrl
4107 * save: function() {
4108 * $http.post(trackingUrl, trackedEvents);
4114 * describe('eventTracker', function() {
4117 * beforeEach(module(function($provide) {
4118 * // Register the eventTracker provider
4119 * $provide.provider('eventTracker', EventTrackerProvider);
4122 * beforeEach(module(function(eventTrackerProvider) {
4123 * // Configure eventTracker provider
4124 * eventTrackerProvider.setTrackingUrl('/custom-track');
4127 * it('tracks events', inject(function(eventTracker) {
4128 * expect(eventTracker.event('login')).toEqual(1);
4129 * expect(eventTracker.event('login')).toEqual(2);
4132 * it('saves to the tracking url', inject(function(eventTracker, $http) {
4133 * postSpy = spyOn($http, 'post');
4134 * eventTracker.event('login');
4135 * eventTracker.save();
4136 * expect(postSpy).toHaveBeenCalled();
4137 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4138 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4139 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4147 * @name $provide#factory
4150 * Register a **service factory**, which will be called to return the service instance.
4151 * This is short for registering a service where its provider consists of only a `$get` property,
4152 * which is the given service factory function.
4153 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4154 * configure your service in a provider.
4156 * @param {string} name The name of the instance.
4157 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4158 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4159 * @returns {Object} registered provider instance
4162 * Here is an example of registering a service
4164 * $provide.factory('ping', ['$http', function($http) {
4165 * return function ping() {
4166 * return $http.send('/ping');
4170 * You would then inject and use this service like this:
4172 * someModule.controller('Ctrl', ['ping', function(ping) {
4181 * @name $provide#service
4184 * Register a **service constructor**, which will be invoked with `new` to create the service
4186 * This is short for registering a service where its provider's `$get` property is the service
4187 * constructor function that will be used to instantiate the service instance.
4189 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4192 * @param {string} name The name of the instance.
4193 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4194 * that will be instantiated.
4195 * @returns {Object} registered provider instance
4198 * Here is an example of registering a service using
4199 * {@link auto.$provide#service $provide.service(class)}.
4201 * var Ping = function($http) {
4202 * this.$http = $http;
4205 * Ping.$inject = ['$http'];
4207 * Ping.prototype.send = function() {
4208 * return this.$http.get('/ping');
4210 * $provide.service('ping', Ping);
4212 * You would then inject and use this service like this:
4214 * someModule.controller('Ctrl', ['ping', function(ping) {
4223 * @name $provide#value
4226 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4227 * number, an array, an object or a function. This is short for registering a service where its
4228 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4231 * Value services are similar to constant services, except that they cannot be injected into a
4232 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4234 * {@link auto.$provide#decorator decorator}.
4236 * @param {string} name The name of the instance.
4237 * @param {*} value The value.
4238 * @returns {Object} registered provider instance
4241 * Here are some examples of creating value services.
4243 * $provide.value('ADMIN_USER', 'admin');
4245 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4247 * $provide.value('halfOf', function(value) {
4256 * @name $provide#constant
4259 * Register a **constant service**, such as a string, a number, an array, an object or a function,
4260 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4261 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4262 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4264 * @param {string} name The name of the constant.
4265 * @param {*} value The constant value.
4266 * @returns {Object} registered instance
4269 * Here a some examples of creating constants:
4271 * $provide.constant('SHARD_HEIGHT', 306);
4273 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4275 * $provide.constant('double', function(value) {
4284 * @name $provide#decorator
4287 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4288 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4289 * service. The object returned by the decorator may be the original service, or a new service
4290 * object which replaces or wraps and delegates to the original service.
4292 * @param {string} name The name of the service to decorate.
4293 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4294 * instantiated and should return the decorated service instance. The function is called using
4295 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4296 * Local injection arguments:
4298 * * `$delegate` - The original service instance, which can be monkey patched, configured,
4299 * decorated or delegated to.
4302 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4303 * calls to {@link ng.$log#error $log.warn()}.
4305 * $provide.decorator('$log', ['$delegate', function($delegate) {
4306 * $delegate.warn = $delegate.error;
4313 function createInjector(modulesToLoad, strictDi) {
4314 strictDi = (strictDi === true);
4315 var INSTANTIATING = {},
4316 providerSuffix = 'Provider',
4318 loadedModules = new HashMap([], true),
4321 provider: supportObject(provider),
4322 factory: supportObject(factory),
4323 service: supportObject(service),
4324 value: supportObject(value),
4325 constant: supportObject(constant),
4326 decorator: decorator
4329 providerInjector = (providerCache.$injector =
4330 createInternalInjector(providerCache, function(serviceName, caller) {
4331 if (angular.isString(caller)) {
4334 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4337 instanceInjector = (instanceCache.$injector =
4338 createInternalInjector(instanceCache, function(serviceName, caller) {
4339 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4340 return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4344 forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4346 return instanceInjector;
4348 ////////////////////////////////////
4350 ////////////////////////////////////
4352 function supportObject(delegate) {
4353 return function(key, value) {
4354 if (isObject(key)) {
4355 forEach(key, reverseParams(delegate));
4357 return delegate(key, value);
4362 function provider(name, provider_) {
4363 assertNotHasOwnProperty(name, 'service');
4364 if (isFunction(provider_) || isArray(provider_)) {
4365 provider_ = providerInjector.instantiate(provider_);
4367 if (!provider_.$get) {
4368 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4370 return providerCache[name + providerSuffix] = provider_;
4373 function enforceReturnValue(name, factory) {
4374 return function enforcedReturnValue() {
4375 var result = instanceInjector.invoke(factory, this);
4376 if (isUndefined(result)) {
4377 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4383 function factory(name, factoryFn, enforce) {
4384 return provider(name, {
4385 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4389 function service(name, constructor) {
4390 return factory(name, ['$injector', function($injector) {
4391 return $injector.instantiate(constructor);
4395 function value(name, val) { return factory(name, valueFn(val), false); }
4397 function constant(name, value) {
4398 assertNotHasOwnProperty(name, 'constant');
4399 providerCache[name] = value;
4400 instanceCache[name] = value;
4403 function decorator(serviceName, decorFn) {
4404 var origProvider = providerInjector.get(serviceName + providerSuffix),
4405 orig$get = origProvider.$get;
4407 origProvider.$get = function() {
4408 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4409 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4413 ////////////////////////////////////
4415 ////////////////////////////////////
4416 function loadModules(modulesToLoad) {
4417 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4418 var runBlocks = [], moduleFn;
4419 forEach(modulesToLoad, function(module) {
4420 if (loadedModules.get(module)) return;
4421 loadedModules.put(module, true);
4423 function runInvokeQueue(queue) {
4425 for (i = 0, ii = queue.length; i < ii; i++) {
4426 var invokeArgs = queue[i],
4427 provider = providerInjector.get(invokeArgs[0]);
4429 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4434 if (isString(module)) {
4435 moduleFn = angularModule(module);
4436 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4437 runInvokeQueue(moduleFn._invokeQueue);
4438 runInvokeQueue(moduleFn._configBlocks);
4439 } else if (isFunction(module)) {
4440 runBlocks.push(providerInjector.invoke(module));
4441 } else if (isArray(module)) {
4442 runBlocks.push(providerInjector.invoke(module));
4444 assertArgFn(module, 'module');
4447 if (isArray(module)) {
4448 module = module[module.length - 1];
4450 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4451 // Safari & FF's stack traces don't contain error.message content
4452 // unlike those of Chrome and IE
4453 // So if stack doesn't contain message, we create a new string that contains both.
4454 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4456 e = e.message + '\n' + e.stack;
4458 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4459 module, e.stack || e.message || e);
4465 ////////////////////////////////////
4466 // internal Injector
4467 ////////////////////////////////////
4469 function createInternalInjector(cache, factory) {
4471 function getService(serviceName, caller) {
4472 if (cache.hasOwnProperty(serviceName)) {
4473 if (cache[serviceName] === INSTANTIATING) {
4474 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4475 serviceName + ' <- ' + path.join(' <- '));
4477 return cache[serviceName];
4480 path.unshift(serviceName);
4481 cache[serviceName] = INSTANTIATING;
4482 return cache[serviceName] = factory(serviceName, caller);
4484 if (cache[serviceName] === INSTANTIATING) {
4485 delete cache[serviceName];
4494 function invoke(fn, self, locals, serviceName) {
4495 if (typeof locals === 'string') {
4496 serviceName = locals;
4501 $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4505 for (i = 0, length = $inject.length; i < length; i++) {
4507 if (typeof key !== 'string') {
4508 throw $injectorMinErr('itkn',
4509 'Incorrect injection token! Expected service name as string, got {0}', key);
4512 locals && locals.hasOwnProperty(key)
4514 : getService(key, serviceName)
4521 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4523 return fn.apply(self, args);
4526 function instantiate(Type, locals, serviceName) {
4527 // Check if Type is annotated and use just the given function at n-1 as parameter
4528 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4529 // Object creation: http://jsperf.com/create-constructor/2
4530 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4531 var returnedValue = invoke(Type, instance, locals, serviceName);
4533 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4538 instantiate: instantiate,
4540 annotate: createInjector.$$annotate,
4541 has: function(name) {
4542 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4548 createInjector.$$annotate = annotate;
4552 * @name $anchorScrollProvider
4555 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4556 * {@link ng.$location#hash $location.hash()} changes.
4558 function $AnchorScrollProvider() {
4560 var autoScrollingEnabled = true;
4564 * @name $anchorScrollProvider#disableAutoScrolling
4567 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4568 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4569 * Use this method to disable automatic scrolling.
4571 * If automatic scrolling is disabled, one must explicitly call
4572 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4575 this.disableAutoScrolling = function() {
4576 autoScrollingEnabled = false;
4581 * @name $anchorScroll
4584 * @requires $location
4585 * @requires $rootScope
4588 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4589 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4591 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4593 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4594 * match any anchor whenever it changes. This can be disabled by calling
4595 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4597 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4598 * vertical scroll-offset (either fixed or dynamic).
4600 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4601 * {@link ng.$location#hash $location.hash()} will be used.
4603 * @property {(number|function|jqLite)} yOffset
4604 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4605 * positioned elements at the top of the page, such as navbars, headers etc.
4607 * `yOffset` can be specified in various ways:
4608 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4609 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4610 * a number representing the offset (in pixels).<br /><br />
4611 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4612 * the top of the page to the element's bottom will be used as offset.<br />
4613 * **Note**: The element will be taken into account only as long as its `position` is set to
4614 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4615 * their height and/or positioning according to the viewport's size.
4618 * <div class="alert alert-warning">
4619 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4620 * not some child element.
4624 <example module="anchorScrollExample">
4625 <file name="index.html">
4626 <div id="scrollArea" ng-controller="ScrollController">
4627 <a ng-click="gotoBottom()">Go to bottom</a>
4628 <a id="bottom"></a> You're at the bottom!
4631 <file name="script.js">
4632 angular.module('anchorScrollExample', [])
4633 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4634 function ($scope, $location, $anchorScroll) {
4635 $scope.gotoBottom = function() {
4636 // set the location.hash to the id of
4637 // the element you wish to scroll to.
4638 $location.hash('bottom');
4640 // call $anchorScroll()
4645 <file name="style.css">
4659 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4660 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4663 <example module="anchorScrollOffsetExample">
4664 <file name="index.html">
4665 <div class="fixed-header" ng-controller="headerCtrl">
4666 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4670 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4674 <file name="script.js">
4675 angular.module('anchorScrollOffsetExample', [])
4676 .run(['$anchorScroll', function($anchorScroll) {
4677 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4679 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4680 function ($anchorScroll, $location, $scope) {
4681 $scope.gotoAnchor = function(x) {
4682 var newHash = 'anchor' + x;
4683 if ($location.hash() !== newHash) {
4684 // set the $location.hash to `newHash` and
4685 // $anchorScroll will automatically scroll to it
4686 $location.hash('anchor' + x);
4688 // call $anchorScroll() explicitly,
4689 // since $location.hash hasn't changed
4696 <file name="style.css">
4702 border: 2px dashed DarkOrchid;
4703 padding: 10px 10px 200px 10px;
4707 background-color: rgba(0, 0, 0, 0.2);
4710 top: 0; left: 0; right: 0;
4714 display: inline-block;
4720 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4721 var document = $window.document;
4723 // Helper function to get first anchor from a NodeList
4724 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4725 // and working in all supported browsers.)
4726 function getFirstAnchor(list) {
4728 Array.prototype.some.call(list, function(element) {
4729 if (nodeName_(element) === 'a') {
4737 function getYOffset() {
4739 var offset = scroll.yOffset;
4741 if (isFunction(offset)) {
4743 } else if (isElement(offset)) {
4744 var elem = offset[0];
4745 var style = $window.getComputedStyle(elem);
4746 if (style.position !== 'fixed') {
4749 offset = elem.getBoundingClientRect().bottom;
4751 } else if (!isNumber(offset)) {
4758 function scrollTo(elem) {
4760 elem.scrollIntoView();
4762 var offset = getYOffset();
4765 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4766 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4767 // top of the viewport.
4769 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4770 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4771 // way down the page.
4773 // This is often the case for elements near the bottom of the page.
4775 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4776 // the top of the element and the offset, which is enough to align the top of `elem` at the
4777 // desired position.
4778 var elemTop = elem.getBoundingClientRect().top;
4779 $window.scrollBy(0, elemTop - offset);
4782 $window.scrollTo(0, 0);
4786 function scroll(hash) {
4787 hash = isString(hash) ? hash : $location.hash();
4790 // empty hash, scroll to the top of the page
4791 if (!hash) scrollTo(null);
4793 // element with given id
4794 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4796 // first anchor with given name :-D
4797 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4799 // no element and hash == 'top', scroll to the top of the page
4800 else if (hash === 'top') scrollTo(null);
4803 // does not scroll when user clicks on anchor link that is currently on
4804 // (no url change, no $location.hash() change), browser native does scroll
4805 if (autoScrollingEnabled) {
4806 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4807 function autoScrollWatchAction(newVal, oldVal) {
4808 // skip the initial scroll if $location.hash is empty
4809 if (newVal === oldVal && newVal === '') return;
4811 jqLiteDocumentLoaded(function() {
4812 $rootScope.$evalAsync(scroll);
4821 var $animateMinErr = minErr('$animate');
4822 var ELEMENT_NODE = 1;
4823 var NG_ANIMATE_CLASSNAME = 'ng-animate';
4825 function mergeClasses(a,b) {
4826 if (!a && !b) return '';
4829 if (isArray(a)) a = a.join(' ');
4830 if (isArray(b)) b = b.join(' ');
4834 function extractElementNode(element) {
4835 for (var i = 0; i < element.length; i++) {
4836 var elm = element[i];
4837 if (elm.nodeType === ELEMENT_NODE) {
4843 function splitClasses(classes) {
4844 if (isString(classes)) {
4845 classes = classes.split(' ');
4848 // Use createMap() to prevent class assumptions involving property names in
4850 var obj = createMap();
4851 forEach(classes, function(klass) {
4852 // sometimes the split leaves empty string values
4853 // incase extra spaces were applied to the options
4861 // if any other type of options value besides an Object value is
4862 // passed into the $animate.method() animation then this helper code
4863 // will be run which will ignore it. While this patch is not the
4864 // greatest solution to this, a lot of existing plugins depend on
4865 // $animate to either call the callback (< 1.2) or return a promise
4866 // that can be changed. This helper function ensures that the options
4867 // are wiped clean incase a callback function is provided.
4868 function prepareAnimateOptions(options) {
4869 return isObject(options)
4874 var $$CoreAnimateRunnerProvider = function() {
4875 this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4876 function AnimateRunner() {}
4877 AnimateRunner.all = noop;
4878 AnimateRunner.chain = noop;
4879 AnimateRunner.prototype = {
4885 then: function(pass, fail) {
4886 return $q(function(resolve) {
4890 }).then(pass, fail);
4893 return AnimateRunner;
4897 // this is prefixed with Core since it conflicts with
4898 // the animateQueueProvider defined in ngAnimate/animateQueue.js
4899 var $$CoreAnimateQueueProvider = function() {
4900 var postDigestQueue = new HashMap();
4901 var postDigestElements = [];
4903 this.$get = ['$$AnimateRunner', '$rootScope',
4904 function($$AnimateRunner, $rootScope) {
4911 push: function(element, event, options, domOperation) {
4912 domOperation && domOperation();
4914 options = options || {};
4915 options.from && element.css(options.from);
4916 options.to && element.css(options.to);
4918 if (options.addClass || options.removeClass) {
4919 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4922 return new $$AnimateRunner(); // jshint ignore:line
4927 function updateData(data, classes, value) {
4928 var changed = false;
4930 classes = isString(classes) ? classes.split(' ') :
4931 isArray(classes) ? classes : [];
4932 forEach(classes, function(className) {
4935 data[className] = value;
4942 function handleCSSClassChanges() {
4943 forEach(postDigestElements, function(element) {
4944 var data = postDigestQueue.get(element);
4946 var existing = splitClasses(element.attr('class'));
4949 forEach(data, function(status, className) {
4950 var hasClass = !!existing[className];
4951 if (status !== hasClass) {
4953 toAdd += (toAdd.length ? ' ' : '') + className;
4955 toRemove += (toRemove.length ? ' ' : '') + className;
4960 forEach(element, function(elm) {
4961 toAdd && jqLiteAddClass(elm, toAdd);
4962 toRemove && jqLiteRemoveClass(elm, toRemove);
4964 postDigestQueue.remove(element);
4967 postDigestElements.length = 0;
4971 function addRemoveClassesPostDigest(element, add, remove) {
4972 var data = postDigestQueue.get(element) || {};
4974 var classesAdded = updateData(data, add, true);
4975 var classesRemoved = updateData(data, remove, false);
4977 if (classesAdded || classesRemoved) {
4979 postDigestQueue.put(element, data);
4980 postDigestElements.push(element);
4982 if (postDigestElements.length === 1) {
4983 $rootScope.$$postDigest(handleCSSClassChanges);
4992 * @name $animateProvider
4995 * Default implementation of $animate that doesn't perform any animations, instead just
4996 * synchronously performs DOM updates and resolves the returned runner promise.
4998 * In order to enable animations the `ngAnimate` module has to be loaded.
5000 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5002 var $AnimateProvider = ['$provide', function($provide) {
5003 var provider = this;
5005 this.$$registeredAnimations = Object.create(null);
5009 * @name $animateProvider#register
5012 * Registers a new injectable animation factory function. The factory function produces the
5013 * animation object which contains callback functions for each event that is expected to be
5016 * * `eventFn`: `function(element, ... , doneFunction, options)`
5017 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
5018 * on the type of animation additional arguments will be injected into the animation function. The
5019 * list below explains the function signatures for the different animation methods:
5021 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5022 * - addClass: function(element, addedClasses, doneFunction, options)
5023 * - removeClass: function(element, removedClasses, doneFunction, options)
5024 * - enter, leave, move: function(element, doneFunction, options)
5025 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
5027 * Make sure to trigger the `doneFunction` once the animation is fully complete.
5031 * //enter, leave, move signature
5032 * eventFn : function(element, done, options) {
5033 * //code to run the animation
5034 * //once complete, then run done()
5035 * return function endFunction(wasCancelled) {
5036 * //code to cancel the animation
5042 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5043 * @param {Function} factory The factory function that will be executed to return the animation
5046 this.register = function(name, factory) {
5047 if (name && name.charAt(0) !== '.') {
5048 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5051 var key = name + '-animation';
5052 provider.$$registeredAnimations[name.substr(1)] = key;
5053 $provide.factory(key, factory);
5058 * @name $animateProvider#classNameFilter
5061 * Sets and/or returns the CSS class regular expression that is checked when performing
5062 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5063 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5064 * When setting the `classNameFilter` value, animations will only be performed on elements
5065 * that successfully match the filter expression. This in turn can boost performance
5066 * for low-powered devices as well as applications containing a lot of structural operations.
5067 * @param {RegExp=} expression The className expression which will be checked against all animations
5068 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5070 this.classNameFilter = function(expression) {
5071 if (arguments.length === 1) {
5072 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5073 if (this.$$classNameFilter) {
5074 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5075 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5076 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5081 return this.$$classNameFilter;
5084 this.$get = ['$$animateQueue', function($$animateQueue) {
5085 function domInsert(element, parentElement, afterElement) {
5086 // if for some reason the previous element was removed
5087 // from the dom sometime before this code runs then let's
5088 // just stick to using the parent element as the anchor
5090 var afterNode = extractElementNode(afterElement);
5091 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5092 afterElement = null;
5095 afterElement ? afterElement.after(element) : parentElement.prepend(element);
5101 * @description The $animate service exposes a series of DOM utility methods that provide support
5102 * for animation hooks. The default behavior is the application of DOM operations, however,
5103 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5104 * to ensure that animation runs with the triggered DOM operation.
5106 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5107 * included and only when it is active then the animation hooks that `$animate` triggers will be
5108 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5109 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5110 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5112 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5114 * To learn more about enabling animation support, click here to visit the
5115 * {@link ngAnimate ngAnimate module page}.
5118 // we don't call it directly since non-existant arguments may
5119 // be interpreted as null within the sub enabled function
5126 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5127 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5128 * is fired with the following params:
5131 * $animate.on('enter', container,
5132 * function callback(element, phase) {
5133 * // cool we detected an enter animation within the container
5138 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5139 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5140 * as well as among its children
5141 * @param {Function} callback the callback function that will be fired when the listener is triggered
5143 * The arguments present in the callback function are:
5144 * * `element` - The captured DOM element that the animation was fired on.
5145 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5147 on: $$animateQueue.on,
5152 * @name $animate#off
5154 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5155 * can be used in three different ways depending on the arguments:
5158 * // remove all the animation event listeners listening for `enter`
5159 * $animate.off('enter');
5161 * // remove all the animation event listeners listening for `enter` on the given element and its children
5162 * $animate.off('enter', container);
5164 * // remove the event listener function provided by `listenerFn` that is set
5165 * // to listen for `enter` on the given `element` as well as its children
5166 * $animate.off('enter', container, callback);
5169 * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5170 * @param {DOMElement=} container the container element the event listener was placed on
5171 * @param {Function=} callback the callback function that was registered as the listener
5173 off: $$animateQueue.off,
5177 * @name $animate#pin
5179 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5180 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5181 * element despite being outside the realm of the application or within another application. Say for example if the application
5182 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5183 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5184 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5186 * Note that this feature is only active when the `ngAnimate` module is used.
5188 * @param {DOMElement} element the external element that will be pinned
5189 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5191 pin: $$animateQueue.pin,
5196 * @name $animate#enabled
5198 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5199 * function can be called in four ways:
5202 * // returns true or false
5203 * $animate.enabled();
5205 * // changes the enabled state for all animations
5206 * $animate.enabled(false);
5207 * $animate.enabled(true);
5209 * // returns true or false if animations are enabled for an element
5210 * $animate.enabled(element);
5212 * // changes the enabled state for an element and its children
5213 * $animate.enabled(element, true);
5214 * $animate.enabled(element, false);
5217 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5218 * @param {boolean=} enabled whether or not the animations will be enabled for the element
5220 * @return {boolean} whether or not animations are enabled
5222 enabled: $$animateQueue.enabled,
5226 * @name $animate#cancel
5228 * @description Cancels the provided animation.
5230 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5232 cancel: function(runner) {
5233 runner.end && runner.end();
5239 * @name $animate#enter
5241 * @description Inserts the element into the DOM either after the `after` element (if provided) or
5242 * as the first child within the `parent` element and then triggers an animation.
5243 * A promise is returned that will be resolved during the next digest once the animation
5246 * @param {DOMElement} element the element which will be inserted into the DOM
5247 * @param {DOMElement} parent the parent element which will append the element as
5248 * a child (so long as the after element is not present)
5249 * @param {DOMElement=} after the sibling element after which the element will be appended
5250 * @param {object=} options an optional collection of options/styles that will be applied to the element
5252 * @return {Promise} the animation callback promise
5254 enter: function(element, parent, after, options) {
5255 parent = parent && jqLite(parent);
5256 after = after && jqLite(after);
5257 parent = parent || after.parent();
5258 domInsert(element, parent, after);
5259 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5265 * @name $animate#move
5267 * @description Inserts (moves) the element into its new position in the DOM either after
5268 * the `after` element (if provided) or as the first child within the `parent` element
5269 * and then triggers an animation. A promise is returned that will be resolved
5270 * during the next digest once the animation has completed.
5272 * @param {DOMElement} element the element which will be moved into the new DOM position
5273 * @param {DOMElement} parent the parent element which will append the element as
5274 * a child (so long as the after element is not present)
5275 * @param {DOMElement=} after the sibling element after which the element will be appended
5276 * @param {object=} options an optional collection of options/styles that will be applied to the element
5278 * @return {Promise} the animation callback promise
5280 move: function(element, parent, after, options) {
5281 parent = parent && jqLite(parent);
5282 after = after && jqLite(after);
5283 parent = parent || after.parent();
5284 domInsert(element, parent, after);
5285 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5290 * @name $animate#leave
5292 * @description Triggers an animation and then removes the element from the DOM.
5293 * When the function is called a promise is returned that will be resolved during the next
5294 * digest once the animation has completed.
5296 * @param {DOMElement} element the element which will be removed from the DOM
5297 * @param {object=} options an optional collection of options/styles that will be applied to the element
5299 * @return {Promise} the animation callback promise
5301 leave: function(element, options) {
5302 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5309 * @name $animate#addClass
5312 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5313 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
5314 * animation if element already contains the CSS class or if the class is removed at a later step.
5315 * Note that class-based animations are treated differently compared to structural animations
5316 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5317 * depending if CSS or JavaScript animations are used.
5319 * @param {DOMElement} element the element which the CSS classes will be applied to
5320 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5321 * @param {object=} options an optional collection of options/styles that will be applied to the element
5323 * @return {Promise} the animation callback promise
5325 addClass: function(element, className, options) {
5326 options = prepareAnimateOptions(options);
5327 options.addClass = mergeClasses(options.addclass, className);
5328 return $$animateQueue.push(element, 'addClass', options);
5333 * @name $animate#removeClass
5336 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5337 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5338 * animation if element does not contain the CSS class or if the class is added at a later step.
5339 * Note that class-based animations are treated differently compared to structural animations
5340 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5341 * depending if CSS or JavaScript animations are used.
5343 * @param {DOMElement} element the element which the CSS classes will be applied to
5344 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5345 * @param {object=} options an optional collection of options/styles that will be applied to the element
5347 * @return {Promise} the animation callback promise
5349 removeClass: function(element, className, options) {
5350 options = prepareAnimateOptions(options);
5351 options.removeClass = mergeClasses(options.removeClass, className);
5352 return $$animateQueue.push(element, 'removeClass', options);
5357 * @name $animate#setClass
5360 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5361 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5362 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5363 * passed. Note that class-based animations are treated differently compared to structural animations
5364 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5365 * depending if CSS or JavaScript animations are used.
5367 * @param {DOMElement} element the element which the CSS classes will be applied to
5368 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5369 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5370 * @param {object=} options an optional collection of options/styles that will be applied to the element
5372 * @return {Promise} the animation callback promise
5374 setClass: function(element, add, remove, options) {
5375 options = prepareAnimateOptions(options);
5376 options.addClass = mergeClasses(options.addClass, add);
5377 options.removeClass = mergeClasses(options.removeClass, remove);
5378 return $$animateQueue.push(element, 'setClass', options);
5383 * @name $animate#animate
5386 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5387 * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5388 * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5389 * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5390 * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5392 * @param {DOMElement} element the element which the CSS styles will be applied to
5393 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5394 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5395 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5396 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5397 * (Note that if no animation is detected then this value will not be appplied to the element.)
5398 * @param {object=} options an optional collection of options/styles that will be applied to the element
5400 * @return {Promise} the animation callback promise
5402 animate: function(element, from, to, className, options) {
5403 options = prepareAnimateOptions(options);
5404 options.from = options.from ? extend(options.from, from) : from;
5405 options.to = options.to ? extend(options.to, to) : to;
5407 className = className || 'ng-inline-animate';
5408 options.tempClasses = mergeClasses(options.tempClasses, className);
5409 return $$animateQueue.push(element, 'animate', options);
5421 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5422 * then the `$animateCss` service will actually perform animations.
5424 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5426 var $CoreAnimateCssProvider = function() {
5427 this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5429 var RAFPromise = function() {};
5430 RAFPromise.prototype = {
5431 done: function(cancel) {
5432 this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5437 cancel: function() {
5440 getPromise: function() {
5442 this.defer = $q.defer();
5444 return this.defer.promise;
5446 then: function(f1,f2) {
5447 return this.getPromise().then(f1,f2);
5449 'catch': function(f1) {
5450 return this.getPromise()['catch'](f1);
5452 'finally': function(f1) {
5453 return this.getPromise()['finally'](f1);
5457 return function(element, options) {
5458 // there is no point in applying the styles since
5459 // there is no animation that goes on at all in
5460 // this version of $animateCss.
5461 if (options.cleanupStyles) {
5462 options.from = options.to = null;
5466 element.css(options.from);
5467 options.from = null;
5470 var closed, runner = new RAFPromise();
5488 if (options.addClass) {
5489 element.addClass(options.addClass);
5490 options.addClass = null;
5492 if (options.removeClass) {
5493 element.removeClass(options.removeClass);
5494 options.removeClass = null;
5497 element.css(options.to);
5505 /* global stripHash: true */
5508 * ! This is a private undocumented service !
5513 * This object has two goals:
5515 * - hide all the global state in the browser caused by the window object
5516 * - abstract away all the browser specific features and inconsistencies
5518 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5519 * service, which can be used for convenient testing of the application without the interaction with
5520 * the real browser apis.
5523 * @param {object} window The global window object.
5524 * @param {object} document jQuery wrapped document.
5525 * @param {object} $log window.console or an object with the same interface.
5526 * @param {object} $sniffer $sniffer service
5528 function Browser(window, document, $log, $sniffer) {
5530 rawDocument = document[0],
5531 location = window.location,
5532 history = window.history,
5533 setTimeout = window.setTimeout,
5534 clearTimeout = window.clearTimeout,
5535 pendingDeferIds = {};
5537 self.isMock = false;
5539 var outstandingRequestCount = 0;
5540 var outstandingRequestCallbacks = [];
5542 // TODO(vojta): remove this temporary api
5543 self.$$completeOutstandingRequest = completeOutstandingRequest;
5544 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5547 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5548 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5550 function completeOutstandingRequest(fn) {
5552 fn.apply(null, sliceArgs(arguments, 1));
5554 outstandingRequestCount--;
5555 if (outstandingRequestCount === 0) {
5556 while (outstandingRequestCallbacks.length) {
5558 outstandingRequestCallbacks.pop()();
5567 function getHash(url) {
5568 var index = url.indexOf('#');
5569 return index === -1 ? '' : url.substr(index);
5574 * Note: this method is used only by scenario runner
5575 * TODO(vojta): prefix this method with $$ ?
5576 * @param {function()} callback Function that will be called when no outstanding request
5578 self.notifyWhenNoOutstandingRequests = function(callback) {
5579 if (outstandingRequestCount === 0) {
5582 outstandingRequestCallbacks.push(callback);
5586 //////////////////////////////////////////////////////////////
5588 //////////////////////////////////////////////////////////////
5590 var cachedState, lastHistoryState,
5591 lastBrowserUrl = location.href,
5592 baseElement = document.find('base'),
5593 pendingLocation = null;
5596 lastHistoryState = cachedState;
5599 * @name $browser#url
5603 * Without any argument, this method just returns current value of location.href.
5606 * With at least one argument, this method sets url to new value.
5607 * If html5 history api supported, pushState/replaceState is used, otherwise
5608 * location.href/location.replace is used.
5609 * Returns its own instance to allow chaining
5611 * NOTE: this api is intended for use only by the $location service. Please use the
5612 * {@link ng.$location $location service} to change url.
5614 * @param {string} url New url (when used as setter)
5615 * @param {boolean=} replace Should new url replace current history record?
5616 * @param {object=} state object to use with pushState/replaceState
5618 self.url = function(url, replace, state) {
5619 // In modern browsers `history.state` is `null` by default; treating it separately
5620 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5621 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5622 if (isUndefined(state)) {
5626 // Android Browser BFCache causes location, history reference to become stale.
5627 if (location !== window.location) location = window.location;
5628 if (history !== window.history) history = window.history;
5632 var sameState = lastHistoryState === state;
5634 // Don't change anything if previous and current URLs and states match. This also prevents
5635 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5636 // See https://github.com/angular/angular.js/commit/ffb2701
5637 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5640 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5641 lastBrowserUrl = url;
5642 lastHistoryState = state;
5643 // Don't use history API if only the hash changed
5644 // due to a bug in IE10/IE11 which leads
5645 // to not firing a `hashchange` nor `popstate` event
5646 // in some cases (see #9143).
5647 if ($sniffer.history && (!sameBase || !sameState)) {
5648 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5650 // Do the assignment again so that those two variables are referentially identical.
5651 lastHistoryState = cachedState;
5653 if (!sameBase || pendingLocation) {
5654 pendingLocation = url;
5657 location.replace(url);
5658 } else if (!sameBase) {
5659 location.href = url;
5661 location.hash = getHash(url);
5663 if (location.href !== url) {
5664 pendingLocation = url;
5670 // - pendingLocation is needed as browsers don't allow to read out
5671 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5672 // https://openradar.appspot.com/22186109).
5673 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5674 return pendingLocation || location.href.replace(/%27/g,"'");
5679 * @name $browser#state
5682 * This method is a getter.
5684 * Return history.state or null if history.state is undefined.
5686 * @returns {object} state
5688 self.state = function() {
5692 var urlChangeListeners = [],
5693 urlChangeInit = false;
5695 function cacheStateAndFireUrlChange() {
5696 pendingLocation = null;
5701 function getCurrentState() {
5703 return history.state;
5705 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5709 // This variable should be used *only* inside the cacheState function.
5710 var lastCachedState = null;
5711 function cacheState() {
5712 // This should be the only place in $browser where `history.state` is read.
5713 cachedState = getCurrentState();
5714 cachedState = isUndefined(cachedState) ? null : cachedState;
5716 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5717 if (equals(cachedState, lastCachedState)) {
5718 cachedState = lastCachedState;
5720 lastCachedState = cachedState;
5723 function fireUrlChange() {
5724 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5728 lastBrowserUrl = self.url();
5729 lastHistoryState = cachedState;
5730 forEach(urlChangeListeners, function(listener) {
5731 listener(self.url(), cachedState);
5736 * @name $browser#onUrlChange
5739 * Register callback function that will be called, when url changes.
5741 * It's only called when the url is changed from outside of angular:
5742 * - user types different url into address bar
5743 * - user clicks on history (forward/back) button
5744 * - user clicks on a link
5746 * It's not called when url is changed by $browser.url() method
5748 * The listener gets called with new url as parameter.
5750 * NOTE: this api is intended for use only by the $location service. Please use the
5751 * {@link ng.$location $location service} to monitor url changes in angular apps.
5753 * @param {function(string)} listener Listener function to be called when url changes.
5754 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5756 self.onUrlChange = function(callback) {
5757 // TODO(vojta): refactor to use node's syntax for events
5758 if (!urlChangeInit) {
5759 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5760 // don't fire popstate when user change the address bar and don't fire hashchange when url
5761 // changed by push/replaceState
5763 // html5 history api - popstate event
5764 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5766 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5768 urlChangeInit = true;
5771 urlChangeListeners.push(callback);
5777 * Remove popstate and hashchange handler from window.
5779 * NOTE: this api is intended for use only by $rootScope.
5781 self.$$applicationDestroyed = function() {
5782 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5786 * Checks whether the url has changed outside of Angular.
5787 * Needs to be exported to be able to check for changes that have been done in sync,
5788 * as hashchange/popstate events fire in async.
5790 self.$$checkUrlChange = fireUrlChange;
5792 //////////////////////////////////////////////////////////////
5794 //////////////////////////////////////////////////////////////
5797 * @name $browser#baseHref
5800 * Returns current <base href>
5801 * (always relative - without domain)
5803 * @returns {string} The current base href
5805 self.baseHref = function() {
5806 var href = baseElement.attr('href');
5807 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5811 * @name $browser#defer
5812 * @param {function()} fn A function, who's execution should be deferred.
5813 * @param {number=} [delay=0] of milliseconds to defer the function execution.
5814 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5817 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5819 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5820 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5821 * via `$browser.defer.flush()`.
5824 self.defer = function(fn, delay) {
5826 outstandingRequestCount++;
5827 timeoutId = setTimeout(function() {
5828 delete pendingDeferIds[timeoutId];
5829 completeOutstandingRequest(fn);
5831 pendingDeferIds[timeoutId] = true;
5837 * @name $browser#defer.cancel
5840 * Cancels a deferred task identified with `deferId`.
5842 * @param {*} deferId Token returned by the `$browser.defer` function.
5843 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5846 self.defer.cancel = function(deferId) {
5847 if (pendingDeferIds[deferId]) {
5848 delete pendingDeferIds[deferId];
5849 clearTimeout(deferId);
5850 completeOutstandingRequest(noop);
5858 function $BrowserProvider() {
5859 this.$get = ['$window', '$log', '$sniffer', '$document',
5860 function($window, $log, $sniffer, $document) {
5861 return new Browser($window, $document, $log, $sniffer);
5867 * @name $cacheFactory
5870 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5875 * var cache = $cacheFactory('cacheId');
5876 * expect($cacheFactory.get('cacheId')).toBe(cache);
5877 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5879 * cache.put("key", "value");
5880 * cache.put("another key", "another value");
5882 * // We've specified no options on creation
5883 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5888 * @param {string} cacheId Name or id of the newly created cache.
5889 * @param {object=} options Options object that specifies the cache behavior. Properties:
5891 * - `{number=}` `capacity` — turns the cache into LRU cache.
5893 * @returns {object} Newly created cache object with the following set of methods:
5895 * - `{object}` `info()` — Returns id, size, and options of cache.
5896 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5898 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5899 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5900 * - `{void}` `removeAll()` — Removes all cached values.
5901 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5904 <example module="cacheExampleApp">
5905 <file name="index.html">
5906 <div ng-controller="CacheController">
5907 <input ng-model="newCacheKey" placeholder="Key">
5908 <input ng-model="newCacheValue" placeholder="Value">
5909 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5911 <p ng-if="keys.length">Cached Values</p>
5912 <div ng-repeat="key in keys">
5913 <span ng-bind="key"></span>
5915 <b ng-bind="cache.get(key)"></b>
5919 <div ng-repeat="(key, value) in cache.info()">
5920 <span ng-bind="key"></span>
5922 <b ng-bind="value"></b>
5926 <file name="script.js">
5927 angular.module('cacheExampleApp', []).
5928 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5930 $scope.cache = $cacheFactory('cacheId');
5931 $scope.put = function(key, value) {
5932 if (angular.isUndefined($scope.cache.get(key))) {
5933 $scope.keys.push(key);
5935 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
5939 <file name="style.css">
5946 function $CacheFactoryProvider() {
5948 this.$get = function() {
5951 function cacheFactory(cacheId, options) {
5952 if (cacheId in caches) {
5953 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5957 stats = extend({}, options, {id: cacheId}),
5959 capacity = (options && options.capacity) || Number.MAX_VALUE,
5960 lruHash = createMap(),
5966 * @name $cacheFactory.Cache
5969 * A cache object used to store and retrieve data, primarily used by
5970 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5971 * templates and other data.
5974 * angular.module('superCache')
5975 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5976 * return $cacheFactory('super-cache');
5983 * it('should behave like a cache', inject(function(superCache) {
5984 * superCache.put('key', 'value');
5985 * superCache.put('another key', 'another value');
5987 * expect(superCache.info()).toEqual({
5988 * id: 'super-cache',
5992 * superCache.remove('another key');
5993 * expect(superCache.get('another key')).toBeUndefined();
5995 * superCache.removeAll();
5996 * expect(superCache.info()).toEqual({
5997 * id: 'super-cache',
6003 return caches[cacheId] = {
6007 * @name $cacheFactory.Cache#put
6011 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6012 * retrieved later, and incrementing the size of the cache if the key was not already
6013 * present in the cache. If behaving like an LRU cache, it will also remove stale
6014 * entries from the set.
6016 * It will not insert undefined values into the cache.
6018 * @param {string} key the key under which the cached data is stored.
6019 * @param {*} value the value to store alongside the key. If it is undefined, the key
6020 * will not be stored.
6021 * @returns {*} the value stored.
6023 put: function(key, value) {
6024 if (isUndefined(value)) return;
6025 if (capacity < Number.MAX_VALUE) {
6026 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6031 if (!(key in data)) size++;
6034 if (size > capacity) {
6035 this.remove(staleEnd.key);
6043 * @name $cacheFactory.Cache#get
6047 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6049 * @param {string} key the key of the data to be retrieved
6050 * @returns {*} the value stored.
6052 get: function(key) {
6053 if (capacity < Number.MAX_VALUE) {
6054 var lruEntry = lruHash[key];
6056 if (!lruEntry) return;
6067 * @name $cacheFactory.Cache#remove
6071 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6073 * @param {string} key the key of the entry to be removed
6075 remove: function(key) {
6076 if (capacity < Number.MAX_VALUE) {
6077 var lruEntry = lruHash[key];
6079 if (!lruEntry) return;
6081 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6082 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6083 link(lruEntry.n,lruEntry.p);
6085 delete lruHash[key];
6088 if (!(key in data)) return;
6097 * @name $cacheFactory.Cache#removeAll
6101 * Clears the cache object of any entries.
6103 removeAll: function() {
6106 lruHash = createMap();
6107 freshEnd = staleEnd = null;
6113 * @name $cacheFactory.Cache#destroy
6117 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6118 * removing it from the {@link $cacheFactory $cacheFactory} set.
6120 destroy: function() {
6124 delete caches[cacheId];
6130 * @name $cacheFactory.Cache#info
6134 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6136 * @returns {object} an object with the following properties:
6138 * <li>**id**: the id of the cache instance</li>
6139 * <li>**size**: the number of entries kept in the cache instance</li>
6140 * <li>**...**: any additional properties from the options object when creating the
6145 return extend({}, stats, {size: size});
6151 * makes the `entry` the freshEnd of the LRU linked list
6153 function refresh(entry) {
6154 if (entry != freshEnd) {
6157 } else if (staleEnd == entry) {
6161 link(entry.n, entry.p);
6162 link(entry, freshEnd);
6170 * bidirectionally links two entries of the LRU linked list
6172 function link(nextEntry, prevEntry) {
6173 if (nextEntry != prevEntry) {
6174 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6175 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6183 * @name $cacheFactory#info
6186 * Get information about all the caches that have been created
6188 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6190 cacheFactory.info = function() {
6192 forEach(caches, function(cache, cacheId) {
6193 info[cacheId] = cache.info();
6201 * @name $cacheFactory#get
6204 * Get access to a cache object by the `cacheId` used when it was created.
6206 * @param {string} cacheId Name or id of a cache to access.
6207 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6209 cacheFactory.get = function(cacheId) {
6210 return caches[cacheId];
6214 return cacheFactory;
6220 * @name $templateCache
6223 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6224 * can load templates directly into the cache in a `script` tag, or by consuming the
6225 * `$templateCache` service directly.
6227 * Adding via the `script` tag:
6230 * <script type="text/ng-template" id="templateId.html">
6231 * <p>This is the content of the template</p>
6235 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6236 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6237 * element with ng-app attribute), otherwise the template will be ignored.
6239 * Adding via the `$templateCache` service:
6242 * var myApp = angular.module('myApp', []);
6243 * myApp.run(function($templateCache) {
6244 * $templateCache.put('templateId.html', 'This is the content of the template');
6248 * To retrieve the template later, simply use it in your HTML:
6250 * <div ng-include=" 'templateId.html' "></div>
6253 * or get it via Javascript:
6255 * $templateCache.get('templateId.html')
6258 * See {@link ng.$cacheFactory $cacheFactory}.
6261 function $TemplateCacheProvider() {
6262 this.$get = ['$cacheFactory', function($cacheFactory) {
6263 return $cacheFactory('templates');
6267 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6268 * Any commits to this file should be reviewed with security in mind. *
6269 * Changes to this file can potentially create security vulnerabilities. *
6270 * An approval from 2 Core members with history of modifying *
6271 * this file is required. *
6273 * Does the change somehow allow for arbitrary javascript to be executed? *
6274 * Or allows for someone to change the prototype of built-in objects? *
6275 * Or gives undesired access to variables likes document or window? *
6276 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6278 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6280 * DOM-related variables:
6282 * - "node" - DOM Node
6283 * - "element" - DOM Element or Node
6284 * - "$node" or "$element" - jqLite-wrapped node or element
6287 * Compiler related stuff:
6289 * - "linkFn" - linking fn of a single directive
6290 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6291 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
6292 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6302 * Compiles an HTML string or DOM into a template and produces a template function, which
6303 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6305 * The compilation is a process of walking the DOM tree and matching DOM elements to
6306 * {@link ng.$compileProvider#directive directives}.
6308 * <div class="alert alert-warning">
6309 * **Note:** This document is an in-depth reference of all directive options.
6310 * For a gentle introduction to directives with examples of common use cases,
6311 * see the {@link guide/directive directive guide}.
6314 * ## Comprehensive Directive API
6316 * There are many different options for a directive.
6318 * The difference resides in the return value of the factory function.
6319 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6320 * or just the `postLink` function (all other properties will have the default values).
6322 * <div class="alert alert-success">
6323 * **Best Practice:** It's recommended to use the "directive definition object" form.
6326 * Here's an example directive declared with a Directive Definition Object:
6329 * var myModule = angular.module(...);
6331 * myModule.directive('directiveName', function factory(injectables) {
6332 * var directiveDefinitionObject = {
6334 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6336 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6337 * transclude: false,
6339 * templateNamespace: 'html',
6341 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6342 * controllerAs: 'stringIdentifier',
6343 * bindToController: false,
6344 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6345 * compile: function compile(tElement, tAttrs, transclude) {
6347 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6348 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
6351 * // return function postLink( ... ) { ... }
6355 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6356 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
6359 * // link: function postLink( ... ) { ... }
6361 * return directiveDefinitionObject;
6365 * <div class="alert alert-warning">
6366 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6369 * Therefore the above can be simplified as:
6372 * var myModule = angular.module(...);
6374 * myModule.directive('directiveName', function factory(injectables) {
6375 * var directiveDefinitionObject = {
6376 * link: function postLink(scope, iElement, iAttrs) { ... }
6378 * return directiveDefinitionObject;
6380 * // return function postLink(scope, iElement, iAttrs) { ... }
6386 * ### Directive Definition Object
6388 * The directive definition object provides instructions to the {@link ng.$compile
6389 * compiler}. The attributes are:
6391 * #### `multiElement`
6392 * When this property is set to true, the HTML compiler will collect DOM nodes between
6393 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6394 * together as the directive elements. It is recommended that this feature be used on directives
6395 * which are not strictly behavioural (such as {@link ngClick}), and which
6396 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6399 * When there are multiple directives defined on a single DOM element, sometimes it
6400 * is necessary to specify the order in which the directives are applied. The `priority` is used
6401 * to sort the directives before their `compile` functions get called. Priority is defined as a
6402 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6403 * are also run in priority order, but post-link functions are run in reverse order. The order
6404 * of directives with the same priority is undefined. The default priority is `0`.
6407 * If set to true then the current `priority` will be the last set of directives
6408 * which will execute (any directives at the current priority will still execute
6409 * as the order of execution on same `priority` is undefined). Note that expressions
6410 * and other directives used in the directive's template will also be excluded from execution.
6413 * The scope property can be `true`, an object or a falsy value:
6415 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6417 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6418 * the directive's element. If multiple directives on the same element request a new scope,
6419 * only one new scope is created. The new scope rule does not apply for the root of the template
6420 * since the root of the template always gets a new scope.
6422 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6423 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6424 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6425 * data in the parent scope.
6427 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6428 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6429 * the object hash map to the name of the property on the isolate scope; the values define how the property
6430 * is bound to the parent scope, via matching attributes on the directive's element:
6432 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6433 * always a string since DOM attributes are strings. If no `attr` name is specified then the
6434 * attribute name is assumed to be the same as the local name.
6435 * Given `<widget my-attr="hello {{name}}">` and widget definition
6436 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6437 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6438 * `localName` property on the widget scope. The `name` is read from the parent scope (not
6441 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6442 * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6443 * name is specified then the attribute name is assumed to be the same as the local name.
6444 * Given `<widget my-attr="parentModel">` and widget definition of
6445 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6446 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6447 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6448 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6449 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6450 * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6451 * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6453 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6454 * If no `attr` name is specified then the attribute name is assumed to be the same as the
6455 * local name. Given `<widget my-attr="count = count + value">` and widget definition of
6456 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6457 * a function wrapper for the `count = count + value` expression. Often it's desirable to
6458 * pass data from the isolated scope via an expression to the parent scope, this can be
6459 * done by passing a map of local variable names and values into the expression wrapper fn.
6460 * For example, if the expression is `increment(amount)` then we can specify the amount value
6461 * by calling the `localFn` as `localFn({amount: 22})`.
6463 * In general it's possible to apply more than one directive to one element, but there might be limitations
6464 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6465 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6467 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6468 * * **child scope** + **no scope** => Both directives will share one single child scope
6469 * * **child scope** + **child scope** => Both directives will share one single child scope
6470 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
6471 * its parent's scope
6472 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6473 * be applied to the same element.
6474 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
6475 * cannot be applied to the same element.
6478 * #### `bindToController`
6479 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6480 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6481 * is instantiated, the initial values of the isolate scope bindings are already available.
6484 * Controller constructor function. The controller is instantiated before the
6485 * pre-linking phase and can be accessed by other directives (see
6486 * `require` attribute). This allows the directives to communicate with each other and augment
6487 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6489 * * `$scope` - Current scope associated with the element
6490 * * `$element` - Current element
6491 * * `$attrs` - Current attributes object for the element
6492 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6493 * `function([scope], cloneLinkingFn, futureParentElement)`.
6494 * * `scope`: optional argument to override the scope.
6495 * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6496 * * `futureParentElement`:
6497 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6498 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6499 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6500 * and when the `cloneLinkinFn` is passed,
6501 * as those elements need to created and cloned in a special way when they are defined outside their
6502 * usual containers (e.g. like `<svg>`).
6503 * * See also the `directive.templateNamespace` property.
6507 * Require another directive and inject its controller as the fourth argument to the linking function. The
6508 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6509 * injected argument will be an array in corresponding order. If no such directive can be
6510 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6511 * is specified, in which case error checking is skipped). The name can be prefixed with:
6513 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6514 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6515 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6516 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6517 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6518 * `null` to the `link` fn if not found.
6519 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6520 * `null` to the `link` fn if not found.
6523 * #### `controllerAs`
6524 * Identifier name for a reference to the controller in the directive's scope.
6525 * This allows the controller to be referenced from the directive template. This is especially
6526 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6527 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6528 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6532 * String of subset of `EACM` which restricts the directive to a specific directive
6533 * declaration style. If omitted, the defaults (elements and attributes) are used.
6535 * * `E` - Element name (default): `<my-directive></my-directive>`
6536 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6537 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6538 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6541 * #### `templateNamespace`
6542 * String representing the document type used by the markup in the template.
6543 * AngularJS needs this information as those elements need to be created and cloned
6544 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6546 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6547 * top-level elements such as `<svg>` or `<math>`.
6548 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6549 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6551 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6554 * HTML markup that may:
6555 * * Replace the contents of the directive's element (default).
6556 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6557 * * Wrap the contents of the directive's element (if `transclude` is true).
6561 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6562 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6563 * function api below) and returns a string value.
6566 * #### `templateUrl`
6567 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6569 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6570 * for later when the template has been resolved. In the meantime it will continue to compile and link
6571 * sibling and parent elements as though this element had not contained any directives.
6573 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6574 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6575 * case when only one deeply nested directive has `templateUrl`.
6577 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6579 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6580 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6581 * a string value representing the url. In either case, the template URL is passed through {@link
6582 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6585 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6586 * specify what the template should replace. Defaults to `false`.
6588 * * `true` - the template will replace the directive's element.
6589 * * `false` - the template will replace the contents of the directive's element.
6591 * The replacement process migrates all of the attributes / classes from the old element to the new
6592 * one. See the {@link guide/directive#template-expanding-directive
6593 * Directives Guide} for an example.
6595 * There are very few scenarios where element replacement is required for the application function,
6596 * the main one being reusable custom components that are used within SVG contexts
6597 * (because SVG doesn't work with custom elements in the DOM tree).
6600 * Extract the contents of the element where the directive appears and make it available to the directive.
6601 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6602 * {@link $compile#transclusion Transclusion} section below.
6604 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6605 * directive's element or the entire element:
6607 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6608 * * `'element'` - transclude the whole of the directive's element including any directives on this
6609 * element that defined at a lower priority than this directive. When used, the `template`
6610 * property is ignored.
6616 * function compile(tElement, tAttrs, transclude) { ... }
6619 * The compile function deals with transforming the template DOM. Since most directives do not do
6620 * template transformation, it is not used often. The compile function takes the following arguments:
6622 * * `tElement` - template element - The element where the directive has been declared. It is
6623 * safe to do template transformation on the element and child elements only.
6625 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6626 * between all directive compile functions.
6628 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6630 * <div class="alert alert-warning">
6631 * **Note:** The template instance and the link instance may be different objects if the template has
6632 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6633 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6634 * should be done in a linking function rather than in a compile function.
6637 * <div class="alert alert-warning">
6638 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6639 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6640 * stack overflow errors.
6642 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6643 * a directive's template instead of relying on automatic template compilation via `template` or
6644 * `templateUrl` declaration or manual compilation inside the compile function.
6647 * <div class="alert alert-danger">
6648 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6649 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
6650 * to the link function instead.
6653 * A compile function can have a return value which can be either a function or an object.
6655 * * returning a (post-link) function - is equivalent to registering the linking function via the
6656 * `link` property of the config object when the compile function is empty.
6658 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6659 * control when a linking function should be called during the linking phase. See info about
6660 * pre-linking and post-linking functions below.
6664 * This property is used only if the `compile` property is not defined.
6667 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6670 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6671 * executed after the template has been cloned. This is where most of the directive logic will be
6674 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6675 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6677 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
6678 * manipulate the children of the element only in `postLink` function since the children have
6679 * already been linked.
6681 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6682 * between all directive linking functions.
6684 * * `controller` - the directive's required controller instance(s) - Instances are shared
6685 * among all directives, which allows the directives to use the controllers as a communication
6686 * channel. The exact value depends on the directive's `require` property:
6687 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6688 * * `string`: the controller instance
6689 * * `array`: array of controller instances
6691 * If a required controller cannot be found, and it is optional, the instance is `null`,
6692 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6694 * Note that you can also require the directive's own controller - it will be made available like
6695 * any other controller.
6697 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6698 * This is the same as the `$transclude`
6699 * parameter of directive controllers, see there for details.
6700 * `function([scope], cloneLinkingFn, futureParentElement)`.
6702 * #### Pre-linking function
6704 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6705 * compiler linking function will fail to locate the correct elements for linking.
6707 * #### Post-linking function
6709 * Executed after the child elements are linked.
6711 * Note that child elements that contain `templateUrl` directives will not have been compiled
6712 * and linked since they are waiting for their template to load asynchronously and their own
6713 * compilation and linking has been suspended until that occurs.
6715 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6716 * for their async templates to be resolved.
6721 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6722 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6723 * scope from where they were taken.
6725 * Transclusion is used (often with {@link ngTransclude}) to insert the
6726 * original contents of a directive's element into a specified place in the template of the directive.
6727 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6728 * content has access to the properties on the scope from which it was taken, even if the directive
6729 * has isolated scope.
6730 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6732 * This makes it possible for the widget to have private state for its template, while the transcluded
6733 * content has access to its originating scope.
6735 * <div class="alert alert-warning">
6736 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6737 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6738 * Testing Transclusion Directives}.
6741 * #### Transclusion Functions
6743 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6744 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6745 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6747 * <div class="alert alert-info">
6748 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6749 * ngTransclude will deal with it for us.
6752 * If you want to manually control the insertion and removal of the transcluded content in your directive
6753 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6754 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6756 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6757 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6758 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6760 * <div class="alert alert-info">
6761 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6762 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6765 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6766 * attach function**:
6769 * var transcludedContent, transclusionScope;
6771 * $transclude(function(clone, scope) {
6772 * element.append(clone);
6773 * transcludedContent = clone;
6774 * transclusionScope = scope;
6778 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6779 * associated transclusion scope:
6782 * transcludedContent.remove();
6783 * transclusionScope.$destroy();
6786 * <div class="alert alert-info">
6787 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6788 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6789 * then you are also responsible for calling `$destroy` on the transclusion scope.
6792 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6793 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6794 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6797 * #### Transclusion Scopes
6799 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6800 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6801 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6804 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6810 * <div transclusion>
6816 * The `$parent` scope hierarchy will look like this:
6824 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6835 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6836 * `link()` or `compile()` functions. It has a variety of uses.
6838 * accessing *Normalized attribute names:*
6839 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6840 * the attributes object allows for normalized access to
6843 * * *Directive inter-communication:* All directives share the same instance of the attributes
6844 * object which allows the directives to use the attributes object as inter directive
6847 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6848 * allowing other directives to read the interpolated value.
6850 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6851 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6852 * the only way to easily get the actual value because during the linking phase the interpolation
6853 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
6856 * function linkingFn(scope, elm, attrs, ctrl) {
6857 * // get the attribute value
6858 * console.log(attrs.ngModel);
6860 * // change the attribute
6861 * attrs.$set('ngModel', 'new value');
6863 * // observe changes to interpolated attribute
6864 * attrs.$observe('ngModel', function(value) {
6865 * console.log('ngModel has changed value to ' + value);
6872 * <div class="alert alert-warning">
6873 * **Note**: Typically directives are registered with `module.directive`. The example below is
6874 * to illustrate how `$compile` works.
6877 <example module="compileExample">
6878 <file name="index.html">
6880 angular.module('compileExample', [], function($compileProvider) {
6881 // configure new 'compile' directive by passing a directive
6882 // factory function. The factory function injects the '$compile'
6883 $compileProvider.directive('compile', function($compile) {
6884 // directive factory creates a link function
6885 return function(scope, element, attrs) {
6888 // watch the 'compile' expression for changes
6889 return scope.$eval(attrs.compile);
6892 // when the 'compile' expression changes
6893 // assign it into the current DOM
6894 element.html(value);
6896 // compile the new DOM and link it to the current
6898 // NOTE: we only compile .childNodes so that
6899 // we don't get into infinite loop compiling ourselves
6900 $compile(element.contents())(scope);
6906 .controller('GreeterController', ['$scope', function($scope) {
6907 $scope.name = 'Angular';
6908 $scope.html = 'Hello {{name}}';
6911 <div ng-controller="GreeterController">
6912 <input ng-model="name"> <br/>
6913 <textarea ng-model="html"></textarea> <br/>
6914 <div compile="html"></div>
6917 <file name="protractor.js" type="protractor">
6918 it('should auto compile', function() {
6919 var textarea = $('textarea');
6920 var output = $('div[compile]');
6921 // The initial state reads 'Hello Angular'.
6922 expect(output.getText()).toBe('Hello Angular');
6924 textarea.sendKeys('{{name}}!');
6925 expect(output.getText()).toBe('Angular!');
6932 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6933 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6935 * <div class="alert alert-danger">
6936 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6937 * e.g. will not use the right outer scope. Please pass the transclude function as a
6938 * `parentBoundTranscludeFn` to the link function instead.
6941 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6942 * root element(s), not their children)
6943 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6944 * (a DOM element/tree) to a scope. Where:
6946 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6947 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6948 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
6949 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6950 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
6952 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
6953 * * `scope` - is the current scope with which the linking function is working with.
6955 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
6956 * keys may be used to control linking behavior:
6958 * * `parentBoundTranscludeFn` - the transclude function made available to
6959 * directives; if given, it will be passed through to the link functions of
6960 * directives found in `element` during compilation.
6961 * * `transcludeControllers` - an object hash with keys that map controller names
6962 * to controller instances; if given, it will make the controllers
6963 * available to directives.
6964 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6965 * the cloned elements; only needed for transcludes that are allowed to contain non html
6966 * elements (e.g. SVG elements). See also the directive.controller property.
6968 * Calling the linking function returns the element of the template. It is either the original
6969 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6971 * After linking the view is not updated until after a call to $digest which typically is done by
6972 * Angular automatically.
6974 * If you need access to the bound view, there are two ways to do it:
6976 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6977 * before you send them to the compiler and keep this reference around.
6979 * var element = $compile('<p>{{total}}</p>')(scope);
6982 * - if on the other hand, you need the element to be cloned, the view reference from the original
6983 * example would not point to the clone, but rather to the original template that was cloned. In
6984 * this case, you can access the clone via the cloneAttachFn:
6986 * var templateElement = angular.element('<p>{{total}}</p>'),
6989 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6990 * //attach the clone to DOM document at the right place
6993 * //now we have reference to the cloned DOM via `clonedElement`
6997 * For information on how the compiler works, see the
6998 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7001 var $compileMinErr = minErr('$compile');
7005 * @name $compileProvider
7009 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7010 function $CompileProvider($provide, $$sanitizeUriProvider) {
7011 var hasDirectives = {},
7012 Suffix = 'Directive',
7013 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7014 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7015 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7016 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7018 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7019 // The assumption is that future DOM event attribute names will begin with
7020 // 'on' and be composed of only English letters.
7021 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7023 function parseIsolateBindings(scope, directiveName, isController) {
7024 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7028 forEach(scope, function(definition, scopeName) {
7029 var match = definition.match(LOCAL_REGEXP);
7032 throw $compileMinErr('iscp',
7033 "Invalid {3} for directive '{0}'." +
7034 " Definition: {... {1}: '{2}' ...}",
7035 directiveName, scopeName, definition,
7036 (isController ? "controller bindings definition" :
7037 "isolate scope definition"));
7040 bindings[scopeName] = {
7042 collection: match[2] === '*',
7043 optional: match[3] === '?',
7044 attrName: match[4] || scopeName
7051 function parseDirectiveBindings(directive, directiveName) {
7054 bindToController: null
7056 if (isObject(directive.scope)) {
7057 if (directive.bindToController === true) {
7058 bindings.bindToController = parseIsolateBindings(directive.scope,
7059 directiveName, true);
7060 bindings.isolateScope = {};
7062 bindings.isolateScope = parseIsolateBindings(directive.scope,
7063 directiveName, false);
7066 if (isObject(directive.bindToController)) {
7067 bindings.bindToController =
7068 parseIsolateBindings(directive.bindToController, directiveName, true);
7070 if (isObject(bindings.bindToController)) {
7071 var controller = directive.controller;
7072 var controllerAs = directive.controllerAs;
7074 // There is no controller, there may or may not be a controllerAs property
7075 throw $compileMinErr('noctrl',
7076 "Cannot bind to controller without directive '{0}'s controller.",
7078 } else if (!identifierForController(controller, controllerAs)) {
7079 // There is a controller, but no identifier or controllerAs property
7080 throw $compileMinErr('noident',
7081 "Cannot bind to controller without identifier for directive '{0}'.",
7088 function assertValidDirectiveName(name) {
7089 var letter = name.charAt(0);
7090 if (!letter || letter !== lowercase(letter)) {
7091 throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7093 if (name !== name.trim()) {
7094 throw $compileMinErr('baddir',
7095 "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7102 * @name $compileProvider#directive
7106 * Register a new directive with the compiler.
7108 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7109 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7110 * names and the values are the factories.
7111 * @param {Function|Array} directiveFactory An injectable directive factory function. See
7112 * {@link guide/directive} for more info.
7113 * @returns {ng.$compileProvider} Self for chaining.
7115 this.directive = function registerDirective(name, directiveFactory) {
7116 assertNotHasOwnProperty(name, 'directive');
7117 if (isString(name)) {
7118 assertValidDirectiveName(name);
7119 assertArg(directiveFactory, 'directiveFactory');
7120 if (!hasDirectives.hasOwnProperty(name)) {
7121 hasDirectives[name] = [];
7122 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7123 function($injector, $exceptionHandler) {
7124 var directives = [];
7125 forEach(hasDirectives[name], function(directiveFactory, index) {
7127 var directive = $injector.invoke(directiveFactory);
7128 if (isFunction(directive)) {
7129 directive = { compile: valueFn(directive) };
7130 } else if (!directive.compile && directive.link) {
7131 directive.compile = valueFn(directive.link);
7133 directive.priority = directive.priority || 0;
7134 directive.index = index;
7135 directive.name = directive.name || name;
7136 directive.require = directive.require || (directive.controller && directive.name);
7137 directive.restrict = directive.restrict || 'EA';
7138 var bindings = directive.$$bindings =
7139 parseDirectiveBindings(directive, directive.name);
7140 if (isObject(bindings.isolateScope)) {
7141 directive.$$isolateBindings = bindings.isolateScope;
7143 directive.$$moduleName = directiveFactory.$$moduleName;
7144 directives.push(directive);
7146 $exceptionHandler(e);
7152 hasDirectives[name].push(directiveFactory);
7154 forEach(name, reverseParams(registerDirective));
7162 * @name $compileProvider#aHrefSanitizationWhitelist
7166 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7167 * urls during a[href] sanitization.
7169 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7171 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7172 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7173 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7174 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7176 * @param {RegExp=} regexp New regexp to whitelist urls with.
7177 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7178 * chaining otherwise.
7180 this.aHrefSanitizationWhitelist = function(regexp) {
7181 if (isDefined(regexp)) {
7182 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7185 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7192 * @name $compileProvider#imgSrcSanitizationWhitelist
7196 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7197 * urls during img[src] sanitization.
7199 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7201 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7202 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7203 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7204 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7206 * @param {RegExp=} regexp New regexp to whitelist urls with.
7207 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7208 * chaining otherwise.
7210 this.imgSrcSanitizationWhitelist = function(regexp) {
7211 if (isDefined(regexp)) {
7212 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7215 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7221 * @name $compileProvider#debugInfoEnabled
7223 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7224 * current debugInfoEnabled state
7225 * @returns {*} current value if used as getter or itself (chaining) if used as setter
7230 * Call this method to enable/disable various debug runtime information in the compiler such as adding
7231 * binding information and a reference to the current scope on to DOM elements.
7232 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7233 * * `ng-binding` CSS class
7234 * * `$binding` data property containing an array of the binding expressions
7236 * You may want to disable this in production for a significant performance boost. See
7237 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7239 * The default value is true.
7241 var debugInfoEnabled = true;
7242 this.debugInfoEnabled = function(enabled) {
7243 if (isDefined(enabled)) {
7244 debugInfoEnabled = enabled;
7247 return debugInfoEnabled;
7251 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7252 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7253 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
7254 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
7256 var Attributes = function(element, attributesToCopy) {
7257 if (attributesToCopy) {
7258 var keys = Object.keys(attributesToCopy);
7261 for (i = 0, l = keys.length; i < l; i++) {
7263 this[key] = attributesToCopy[key];
7269 this.$$element = element;
7272 Attributes.prototype = {
7275 * @name $compile.directive.Attributes#$normalize
7279 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7280 * `data-`) to its normalized, camelCase form.
7282 * Also there is special case for Moz prefix starting with upper case letter.
7284 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7286 * @param {string} name Name to normalize
7288 $normalize: directiveNormalize,
7293 * @name $compile.directive.Attributes#$addClass
7297 * Adds the CSS class value specified by the classVal parameter to the element. If animations
7298 * are enabled then an animation will be triggered for the class addition.
7300 * @param {string} classVal The className value that will be added to the element
7302 $addClass: function(classVal) {
7303 if (classVal && classVal.length > 0) {
7304 $animate.addClass(this.$$element, classVal);
7310 * @name $compile.directive.Attributes#$removeClass
7314 * Removes the CSS class value specified by the classVal parameter from the element. If
7315 * animations are enabled then an animation will be triggered for the class removal.
7317 * @param {string} classVal The className value that will be removed from the element
7319 $removeClass: function(classVal) {
7320 if (classVal && classVal.length > 0) {
7321 $animate.removeClass(this.$$element, classVal);
7327 * @name $compile.directive.Attributes#$updateClass
7331 * Adds and removes the appropriate CSS class values to the element based on the difference
7332 * between the new and old CSS class values (specified as newClasses and oldClasses).
7334 * @param {string} newClasses The current CSS className value
7335 * @param {string} oldClasses The former CSS className value
7337 $updateClass: function(newClasses, oldClasses) {
7338 var toAdd = tokenDifference(newClasses, oldClasses);
7339 if (toAdd && toAdd.length) {
7340 $animate.addClass(this.$$element, toAdd);
7343 var toRemove = tokenDifference(oldClasses, newClasses);
7344 if (toRemove && toRemove.length) {
7345 $animate.removeClass(this.$$element, toRemove);
7350 * Set a normalized attribute on the element in a way such that all directives
7351 * can share the attribute. This function properly handles boolean attributes.
7352 * @param {string} key Normalized key. (ie ngAttribute)
7353 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7354 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7356 * @param {string=} attrName Optional none normalized name. Defaults to key.
7358 $set: function(key, value, writeAttr, attrName) {
7359 // TODO: decide whether or not to throw an error if "class"
7360 //is set through this function since it may cause $updateClass to
7363 var node = this.$$element[0],
7364 booleanKey = getBooleanAttrName(node, key),
7365 aliasedKey = getAliasedAttrName(key),
7370 this.$$element.prop(key, value);
7371 attrName = booleanKey;
7372 } else if (aliasedKey) {
7373 this[aliasedKey] = value;
7374 observer = aliasedKey;
7379 // translate normalized key to actual key
7381 this.$attr[key] = attrName;
7383 attrName = this.$attr[key];
7385 this.$attr[key] = attrName = snake_case(key, '-');
7389 nodeName = nodeName_(this.$$element);
7391 if ((nodeName === 'a' && key === 'href') ||
7392 (nodeName === 'img' && key === 'src')) {
7393 // sanitize a[href] and img[src] values
7394 this[key] = value = $$sanitizeUri(value, key === 'src');
7395 } else if (nodeName === 'img' && key === 'srcset') {
7396 // sanitize img[srcset] values
7399 // first check if there are spaces because it's not the same pattern
7400 var trimmedSrcset = trim(value);
7401 // ( 999x ,| 999w ,| ,|, )
7402 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7403 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7405 // split srcset into tuple of uri and descriptor except for the last item
7406 var rawUris = trimmedSrcset.split(pattern);
7409 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7410 for (var i = 0; i < nbrUrisWith2parts; i++) {
7411 var innerIdx = i * 2;
7413 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7414 // add the descriptor
7415 result += (" " + trim(rawUris[innerIdx + 1]));
7418 // split the last item into uri and descriptor
7419 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7421 // sanitize the last uri
7422 result += $$sanitizeUri(trim(lastTuple[0]), true);
7424 // and add the last descriptor if any
7425 if (lastTuple.length === 2) {
7426 result += (" " + trim(lastTuple[1]));
7428 this[key] = value = result;
7431 if (writeAttr !== false) {
7432 if (value === null || isUndefined(value)) {
7433 this.$$element.removeAttr(attrName);
7435 this.$$element.attr(attrName, value);
7440 var $$observers = this.$$observers;
7441 $$observers && forEach($$observers[observer], function(fn) {
7445 $exceptionHandler(e);
7453 * @name $compile.directive.Attributes#$observe
7457 * Observes an interpolated attribute.
7459 * The observer function will be invoked once during the next `$digest` following
7460 * compilation. The observer is then invoked whenever the interpolated value
7463 * @param {string} key Normalized key. (ie ngAttribute) .
7464 * @param {function(interpolatedValue)} fn Function that will be called whenever
7465 the interpolated value of the attribute changes.
7466 * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7467 * @returns {function()} Returns a deregistration function for this observer.
7469 $observe: function(key, fn) {
7471 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7472 listeners = ($$observers[key] || ($$observers[key] = []));
7475 $rootScope.$evalAsync(function() {
7476 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7477 // no one registered attribute interpolation function, so lets call it manually
7483 arrayRemove(listeners, fn);
7489 function safeAddClass($element, className) {
7491 $element.addClass(className);
7493 // ignore, since it means that we are trying to set class on
7494 // SVG element, where class name is read-only.
7499 var startSymbol = $interpolate.startSymbol(),
7500 endSymbol = $interpolate.endSymbol(),
7501 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
7503 : function denormalizeTemplate(template) {
7504 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7506 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7507 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7509 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7510 var bindings = $element.data('$binding') || [];
7512 if (isArray(binding)) {
7513 bindings = bindings.concat(binding);
7515 bindings.push(binding);
7518 $element.data('$binding', bindings);
7521 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7522 safeAddClass($element, 'ng-binding');
7525 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7526 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7527 $element.data(dataName, scope);
7530 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7531 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7536 //================================
7538 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7539 previousCompileContext) {
7540 if (!($compileNodes instanceof jqLite)) {
7541 // jquery always rewraps, whereas we need to preserve the original selector so that we can
7543 $compileNodes = jqLite($compileNodes);
7545 // We can not compile top level text elements since text nodes can be merged and we will
7546 // not be able to attach scope data to them, so we will wrap them in <span>
7547 forEach($compileNodes, function(node, index) {
7548 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7549 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7552 var compositeLinkFn =
7553 compileNodes($compileNodes, transcludeFn, $compileNodes,
7554 maxPriority, ignoreDirective, previousCompileContext);
7555 compile.$$addScopeClass($compileNodes);
7556 var namespace = null;
7557 return function publicLinkFn(scope, cloneConnectFn, options) {
7558 assertArg(scope, 'scope');
7560 if (previousCompileContext && previousCompileContext.needsNewScope) {
7561 // A parent directive did a replace and a directive on this element asked
7562 // for transclusion, which caused us to lose a layer of element on which
7563 // we could hold the new transclusion scope, so we will create it manually
7565 scope = scope.$parent.$new();
7568 options = options || {};
7569 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7570 transcludeControllers = options.transcludeControllers,
7571 futureParentElement = options.futureParentElement;
7573 // When `parentBoundTranscludeFn` is passed, it is a
7574 // `controllersBoundTransclude` function (it was previously passed
7575 // as `transclude` to directive.link) so we must unwrap it to get
7576 // its `boundTranscludeFn`
7577 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7578 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7582 namespace = detectNamespaceForChildElements(futureParentElement);
7585 if (namespace !== 'html') {
7586 // When using a directive with replace:true and templateUrl the $compileNodes
7587 // (or a child element inside of them)
7588 // might change, so we need to recreate the namespace adapted compileNodes
7589 // for call to the link function.
7590 // Note: This will already clone the nodes...
7592 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7594 } else if (cloneConnectFn) {
7595 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7596 // and sometimes changes the structure of the DOM.
7597 $linkNode = JQLitePrototype.clone.call($compileNodes);
7599 $linkNode = $compileNodes;
7602 if (transcludeControllers) {
7603 for (var controllerName in transcludeControllers) {
7604 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7608 compile.$$addScopeInfo($linkNode, scope);
7610 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7611 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7616 function detectNamespaceForChildElements(parentElement) {
7617 // TODO: Make this detect MathML as well...
7618 var node = parentElement && parentElement[0];
7622 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7627 * Compile function matches each node in nodeList against the directives. Once all directives
7628 * for a particular node are collected their compile functions are executed. The compile
7629 * functions return values - the linking functions - are combined into a composite linking
7630 * function, which is the a linking function for the node.
7632 * @param {NodeList} nodeList an array of nodes or NodeList to compile
7633 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7634 * scope argument is auto-generated to the new child of the transcluded parent scope.
7635 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7636 * the rootElement must be set the jqLite collection of the compile root. This is
7637 * needed so that the jqLite collection items can be replaced with widgets.
7638 * @param {number=} maxPriority Max directive priority.
7639 * @returns {Function} A composite linking function of all of the matched directives or null.
7641 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7642 previousCompileContext) {
7644 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7646 for (var i = 0; i < nodeList.length; i++) {
7647 attrs = new Attributes();
7649 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7650 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7653 nodeLinkFn = (directives.length)
7654 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7655 null, [], [], previousCompileContext)
7658 if (nodeLinkFn && nodeLinkFn.scope) {
7659 compile.$$addScopeClass(attrs.$$element);
7662 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7663 !(childNodes = nodeList[i].childNodes) ||
7666 : compileNodes(childNodes,
7668 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7669 && nodeLinkFn.transclude) : transcludeFn);
7671 if (nodeLinkFn || childLinkFn) {
7672 linkFns.push(i, nodeLinkFn, childLinkFn);
7674 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7677 //use the previous context only for the first element in the virtual group
7678 previousCompileContext = null;
7681 // return a linking function if we have found anything, null otherwise
7682 return linkFnFound ? compositeLinkFn : null;
7684 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7685 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7689 if (nodeLinkFnFound) {
7690 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7691 // offsets don't get screwed up
7692 var nodeListLength = nodeList.length;
7693 stableNodeList = new Array(nodeListLength);
7695 // create a sparse array by only copying the elements which have a linkFn
7696 for (i = 0; i < linkFns.length; i+=3) {
7698 stableNodeList[idx] = nodeList[idx];
7701 stableNodeList = nodeList;
7704 for (i = 0, ii = linkFns.length; i < ii;) {
7705 node = stableNodeList[linkFns[i++]];
7706 nodeLinkFn = linkFns[i++];
7707 childLinkFn = linkFns[i++];
7710 if (nodeLinkFn.scope) {
7711 childScope = scope.$new();
7712 compile.$$addScopeInfo(jqLite(node), childScope);
7717 if (nodeLinkFn.transcludeOnThisElement) {
7718 childBoundTranscludeFn = createBoundTranscludeFn(
7719 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7721 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7722 childBoundTranscludeFn = parentBoundTranscludeFn;
7724 } else if (!parentBoundTranscludeFn && transcludeFn) {
7725 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7728 childBoundTranscludeFn = null;
7731 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7733 } else if (childLinkFn) {
7734 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7740 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7742 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7744 if (!transcludedScope) {
7745 transcludedScope = scope.$new(false, containingScope);
7746 transcludedScope.$$transcluded = true;
7749 return transcludeFn(transcludedScope, cloneFn, {
7750 parentBoundTranscludeFn: previousBoundTranscludeFn,
7751 transcludeControllers: controllers,
7752 futureParentElement: futureParentElement
7756 return boundTranscludeFn;
7760 * Looks for directives on the given node and adds them to the directive collection which is
7763 * @param node Node to search.
7764 * @param directives An array to which the directives are added to. This array is sorted before
7765 * the function returns.
7766 * @param attrs The shared attrs object which is used to populate the normalized attributes.
7767 * @param {number=} maxPriority Max directive priority.
7769 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7770 var nodeType = node.nodeType,
7771 attrsMap = attrs.$attr,
7776 case NODE_TYPE_ELEMENT: /* Element */
7777 // use the node name: <directive>
7778 addDirective(directives,
7779 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7781 // iterate over the attributes
7782 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7783 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7784 var attrStartName = false;
7785 var attrEndName = false;
7789 value = trim(attr.value);
7791 // support ngAttr attribute binding
7792 ngAttrName = directiveNormalize(name);
7793 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7794 name = name.replace(PREFIX_REGEXP, '')
7795 .substr(8).replace(/_(.)/g, function(match, letter) {
7796 return letter.toUpperCase();
7800 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7801 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
7802 attrStartName = name;
7803 attrEndName = name.substr(0, name.length - 5) + 'end';
7804 name = name.substr(0, name.length - 6);
7807 nName = directiveNormalize(name.toLowerCase());
7808 attrsMap[nName] = name;
7809 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7810 attrs[nName] = value;
7811 if (getBooleanAttrName(node, nName)) {
7812 attrs[nName] = true; // presence means true
7815 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7816 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7820 // use class as directive
7821 className = node.className;
7822 if (isObject(className)) {
7823 // Maybe SVGAnimatedString
7824 className = className.animVal;
7826 if (isString(className) && className !== '') {
7827 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7828 nName = directiveNormalize(match[2]);
7829 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7830 attrs[nName] = trim(match[3]);
7832 className = className.substr(match.index + match[0].length);
7836 case NODE_TYPE_TEXT: /* Text Node */
7838 // Workaround for #11781
7839 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7840 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7841 node.parentNode.removeChild(node.nextSibling);
7844 addTextInterpolateDirective(directives, node.nodeValue);
7846 case NODE_TYPE_COMMENT: /* Comment */
7848 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7850 nName = directiveNormalize(match[1]);
7851 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7852 attrs[nName] = trim(match[2]);
7856 // turns out that under some circumstances IE9 throws errors when one attempts to read
7857 // comment's node value.
7858 // Just ignore it and continue. (Can't seem to reproduce in test case.)
7863 directives.sort(byPriority);
7868 * Given a node with an directive-start it collects all of the siblings until it finds
7875 function groupScan(node, attrStart, attrEnd) {
7878 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7881 throw $compileMinErr('uterdir',
7882 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7883 attrStart, attrEnd);
7885 if (node.nodeType == NODE_TYPE_ELEMENT) {
7886 if (node.hasAttribute(attrStart)) depth++;
7887 if (node.hasAttribute(attrEnd)) depth--;
7890 node = node.nextSibling;
7891 } while (depth > 0);
7896 return jqLite(nodes);
7900 * Wrapper for linking function which converts normal linking function into a grouped
7905 * @returns {Function}
7907 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7908 return function(scope, element, attrs, controllers, transcludeFn) {
7909 element = groupScan(element[0], attrStart, attrEnd);
7910 return linkFn(scope, element, attrs, controllers, transcludeFn);
7915 * Once the directives have been collected, their compile functions are executed. This method
7916 * is responsible for inlining directive templates as well as terminating the application
7917 * of the directives if the terminal directive has been reached.
7919 * @param {Array} directives Array of collected directives to execute their compile function.
7920 * this needs to be pre-sorted by priority order.
7921 * @param {Node} compileNode The raw DOM node to apply the compile functions to
7922 * @param {Object} templateAttrs The shared attribute function
7923 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7924 * scope argument is auto-generated to the new
7925 * child of the transcluded parent scope.
7926 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7927 * argument has the root jqLite array so that we can replace nodes
7929 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7930 * compiling the transclusion.
7931 * @param {Array.<Function>} preLinkFns
7932 * @param {Array.<Function>} postLinkFns
7933 * @param {Object} previousCompileContext Context used for previous compilation of the current
7935 * @returns {Function} linkFn
7937 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7938 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7939 previousCompileContext) {
7940 previousCompileContext = previousCompileContext || {};
7942 var terminalPriority = -Number.MAX_VALUE,
7943 newScopeDirective = previousCompileContext.newScopeDirective,
7944 controllerDirectives = previousCompileContext.controllerDirectives,
7945 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7946 templateDirective = previousCompileContext.templateDirective,
7947 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7948 hasTranscludeDirective = false,
7949 hasTemplate = false,
7950 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7951 $compileNode = templateAttrs.$$element = jqLite(compileNode),
7955 replaceDirective = originalReplaceDirective,
7956 childTranscludeFn = transcludeFn,
7960 // executes all directives on the current element
7961 for (var i = 0, ii = directives.length; i < ii; i++) {
7962 directive = directives[i];
7963 var attrStart = directive.$$start;
7964 var attrEnd = directive.$$end;
7966 // collect multiblock sections
7968 $compileNode = groupScan(compileNode, attrStart, attrEnd);
7970 $template = undefined;
7972 if (terminalPriority > directive.priority) {
7973 break; // prevent further processing of directives
7976 if (directiveValue = directive.scope) {
7978 // skip the check for directives with async templates, we'll check the derived sync
7979 // directive when the template arrives
7980 if (!directive.templateUrl) {
7981 if (isObject(directiveValue)) {
7982 // This directive is trying to add an isolated scope.
7983 // Check that there is no scope of any kind already
7984 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7985 directive, $compileNode);
7986 newIsolateScopeDirective = directive;
7988 // This directive is trying to add a child scope.
7989 // Check that there is no isolated scope already
7990 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7995 newScopeDirective = newScopeDirective || directive;
7998 directiveName = directive.name;
8000 if (!directive.templateUrl && directive.controller) {
8001 directiveValue = directive.controller;
8002 controllerDirectives = controllerDirectives || createMap();
8003 assertNoDuplicate("'" + directiveName + "' controller",
8004 controllerDirectives[directiveName], directive, $compileNode);
8005 controllerDirectives[directiveName] = directive;
8008 if (directiveValue = directive.transclude) {
8009 hasTranscludeDirective = true;
8011 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8012 // This option should only be used by directives that know how to safely handle element transclusion,
8013 // where the transcluded nodes are added or replaced after linking.
8014 if (!directive.$$tlb) {
8015 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8016 nonTlbTranscludeDirective = directive;
8019 if (directiveValue == 'element') {
8020 hasElementTranscludeDirective = true;
8021 terminalPriority = directive.priority;
8022 $template = $compileNode;
8023 $compileNode = templateAttrs.$$element =
8024 jqLite(document.createComment(' ' + directiveName + ': ' +
8025 templateAttrs[directiveName] + ' '));
8026 compileNode = $compileNode[0];
8027 replaceWith(jqCollection, sliceArgs($template), compileNode);
8029 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8030 replaceDirective && replaceDirective.name, {
8032 // - controllerDirectives - otherwise we'll create duplicates controllers
8033 // - newIsolateScopeDirective or templateDirective - combining templates with
8034 // element transclusion doesn't make sense.
8036 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8037 // on the same element more than once.
8038 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8041 $template = jqLite(jqLiteClone(compileNode)).contents();
8042 $compileNode.empty(); // clear contents
8043 childTranscludeFn = compile($template, transcludeFn, undefined,
8044 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8048 if (directive.template) {
8050 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8051 templateDirective = directive;
8053 directiveValue = (isFunction(directive.template))
8054 ? directive.template($compileNode, templateAttrs)
8055 : directive.template;
8057 directiveValue = denormalizeTemplate(directiveValue);
8059 if (directive.replace) {
8060 replaceDirective = directive;
8061 if (jqLiteIsTextNode(directiveValue)) {
8064 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8066 compileNode = $template[0];
8068 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8069 throw $compileMinErr('tplrt',
8070 "Template for directive '{0}' must have exactly one root element. {1}",
8074 replaceWith(jqCollection, $compileNode, compileNode);
8076 var newTemplateAttrs = {$attr: {}};
8078 // combine directives from the original node and from the template:
8079 // - take the array of directives for this element
8080 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8081 // - collect directives from the template and sort them by priority
8082 // - combine directives as: processed + template + unprocessed
8083 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8084 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8086 if (newIsolateScopeDirective || newScopeDirective) {
8087 // The original directive caused the current element to be replaced but this element
8088 // also needs to have a new scope, so we need to tell the template directives
8089 // that they would need to get their scope from further up, if they require transclusion
8090 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8092 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8093 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8095 ii = directives.length;
8097 $compileNode.html(directiveValue);
8101 if (directive.templateUrl) {
8103 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8104 templateDirective = directive;
8106 if (directive.replace) {
8107 replaceDirective = directive;
8110 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8111 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8112 controllerDirectives: controllerDirectives,
8113 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8114 newIsolateScopeDirective: newIsolateScopeDirective,
8115 templateDirective: templateDirective,
8116 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8118 ii = directives.length;
8119 } else if (directive.compile) {
8121 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8122 if (isFunction(linkFn)) {
8123 addLinkFns(null, linkFn, attrStart, attrEnd);
8124 } else if (linkFn) {
8125 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8128 $exceptionHandler(e, startingTag($compileNode));
8132 if (directive.terminal) {
8133 nodeLinkFn.terminal = true;
8134 terminalPriority = Math.max(terminalPriority, directive.priority);
8139 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8140 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8141 nodeLinkFn.templateOnThisElement = hasTemplate;
8142 nodeLinkFn.transclude = childTranscludeFn;
8144 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8146 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8149 ////////////////////
8151 function addLinkFns(pre, post, attrStart, attrEnd) {
8153 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8154 pre.require = directive.require;
8155 pre.directiveName = directiveName;
8156 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8157 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8159 preLinkFns.push(pre);
8162 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8163 post.require = directive.require;
8164 post.directiveName = directiveName;
8165 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8166 post = cloneAndAnnotateFn(post, {isolateScope: true});
8168 postLinkFns.push(post);
8173 function getControllers(directiveName, require, $element, elementControllers) {
8176 if (isString(require)) {
8177 var match = require.match(REQUIRE_PREFIX_REGEXP);
8178 var name = require.substring(match[0].length);
8179 var inheritType = match[1] || match[3];
8180 var optional = match[2] === '?';
8182 //If only parents then start at the parent element
8183 if (inheritType === '^^') {
8184 $element = $element.parent();
8185 //Otherwise attempt getting the controller from elementControllers in case
8186 //the element is transcluded (and has no data) and to avoid .data if possible
8188 value = elementControllers && elementControllers[name];
8189 value = value && value.instance;
8193 var dataName = '$' + name + 'Controller';
8194 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8197 if (!value && !optional) {
8198 throw $compileMinErr('ctreq',
8199 "Controller '{0}', required by directive '{1}', can't be found!",
8200 name, directiveName);
8202 } else if (isArray(require)) {
8204 for (var i = 0, ii = require.length; i < ii; i++) {
8205 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8209 return value || null;
8212 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8213 var elementControllers = createMap();
8214 for (var controllerKey in controllerDirectives) {
8215 var directive = controllerDirectives[controllerKey];
8217 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8220 $transclude: transcludeFn
8223 var controller = directive.controller;
8224 if (controller == '@') {
8225 controller = attrs[directive.name];
8228 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8230 // For directives with element transclusion the element is a comment,
8231 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8232 // clean up (http://bugs.jquery.com/ticket/8335).
8233 // Instead, we save the controllers for the element in a local hash and attach to .data
8234 // later, once we have the actual element.
8235 elementControllers[directive.name] = controllerInstance;
8236 if (!hasElementTranscludeDirective) {
8237 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8240 return elementControllers;
8243 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8244 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8245 attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8247 if (compileNode === linkNode) {
8248 attrs = templateAttrs;
8249 $element = templateAttrs.$$element;
8251 $element = jqLite(linkNode);
8252 attrs = new Attributes($element, templateAttrs);
8255 controllerScope = scope;
8256 if (newIsolateScopeDirective) {
8257 isolateScope = scope.$new(true);
8258 } else if (newScopeDirective) {
8259 controllerScope = scope.$parent;
8262 if (boundTranscludeFn) {
8263 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8264 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8265 transcludeFn = controllersBoundTransclude;
8266 transcludeFn.$$boundTransclude = boundTranscludeFn;
8269 if (controllerDirectives) {
8270 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8273 if (newIsolateScopeDirective) {
8274 // Initialize isolate scope bindings for new isolate scope directive.
8275 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8276 templateDirective === newIsolateScopeDirective.$$originalDirective)));
8277 compile.$$addScopeClass($element, true);
8278 isolateScope.$$isolateBindings =
8279 newIsolateScopeDirective.$$isolateBindings;
8280 removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8281 isolateScope.$$isolateBindings,
8282 newIsolateScopeDirective);
8283 if (removeScopeBindingWatches) {
8284 isolateScope.$on('$destroy', removeScopeBindingWatches);
8288 // Initialize bindToController bindings
8289 for (var name in elementControllers) {
8290 var controllerDirective = controllerDirectives[name];
8291 var controller = elementControllers[name];
8292 var bindings = controllerDirective.$$bindings.bindToController;
8294 if (controller.identifier && bindings) {
8295 removeControllerBindingWatches =
8296 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8299 var controllerResult = controller();
8300 if (controllerResult !== controller.instance) {
8301 // If the controller constructor has a return value, overwrite the instance
8302 // from setupControllers
8303 controller.instance = controllerResult;
8304 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8305 removeControllerBindingWatches && removeControllerBindingWatches();
8306 removeControllerBindingWatches =
8307 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8312 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8313 linkFn = preLinkFns[i];
8314 invokeLinkFn(linkFn,
8315 linkFn.isolateScope ? isolateScope : scope,
8318 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8324 // We only pass the isolate scope, if the isolate directive has a template,
8325 // otherwise the child elements do not belong to the isolate directive.
8326 var scopeToChild = scope;
8327 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8328 scopeToChild = isolateScope;
8330 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8333 for (i = postLinkFns.length - 1; i >= 0; i--) {
8334 linkFn = postLinkFns[i];
8335 invokeLinkFn(linkFn,
8336 linkFn.isolateScope ? isolateScope : scope,
8339 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8344 // This is the function that is injected as `$transclude`.
8345 // Note: all arguments are optional!
8346 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8347 var transcludeControllers;
8349 // No scope passed in:
8350 if (!isScope(scope)) {
8351 futureParentElement = cloneAttachFn;
8352 cloneAttachFn = scope;
8356 if (hasElementTranscludeDirective) {
8357 transcludeControllers = elementControllers;
8359 if (!futureParentElement) {
8360 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8362 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8367 // Depending upon the context in which a directive finds itself it might need to have a new isolated
8368 // or child scope created. For instance:
8369 // * if the directive has been pulled into a template because another directive with a higher priority
8370 // asked for element transclusion
8371 // * if the directive itself asks for transclusion but it is at the root of a template and the original
8372 // element was replaced. See https://github.com/angular/angular.js/issues/12936
8373 function markDirectiveScope(directives, isolateScope, newScope) {
8374 for (var j = 0, jj = directives.length; j < jj; j++) {
8375 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8380 * looks up the directive and decorates it with exception handling and proper parameters. We
8381 * call this the boundDirective.
8383 * @param {string} name name of the directive to look up.
8384 * @param {string} location The directive must be found in specific format.
8385 * String containing any of theses characters:
8387 * * `E`: element name
8391 * @returns {boolean} true if directive was added.
8393 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8395 if (name === ignoreDirective) return null;
8397 if (hasDirectives.hasOwnProperty(name)) {
8398 for (var directive, directives = $injector.get(name + Suffix),
8399 i = 0, ii = directives.length; i < ii; i++) {
8401 directive = directives[i];
8402 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8403 directive.restrict.indexOf(location) != -1) {
8404 if (startAttrName) {
8405 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8407 tDirectives.push(directive);
8410 } catch (e) { $exceptionHandler(e); }
8418 * looks up the directive and returns true if it is a multi-element directive,
8419 * and therefore requires DOM nodes between -start and -end markers to be grouped
8422 * @param {string} name name of the directive to look up.
8423 * @returns true if directive was registered as multi-element.
8425 function directiveIsMultiElement(name) {
8426 if (hasDirectives.hasOwnProperty(name)) {
8427 for (var directive, directives = $injector.get(name + Suffix),
8428 i = 0, ii = directives.length; i < ii; i++) {
8429 directive = directives[i];
8430 if (directive.multiElement) {
8439 * When the element is replaced with HTML template then the new attributes
8440 * on the template need to be merged with the existing attributes in the DOM.
8441 * The desired effect is to have both of the attributes present.
8443 * @param {object} dst destination attributes (original DOM)
8444 * @param {object} src source attributes (from the directive template)
8446 function mergeTemplateAttributes(dst, src) {
8447 var srcAttr = src.$attr,
8448 dstAttr = dst.$attr,
8449 $element = dst.$$element;
8451 // reapply the old attributes to the new element
8452 forEach(dst, function(value, key) {
8453 if (key.charAt(0) != '$') {
8454 if (src[key] && src[key] !== value) {
8455 value += (key === 'style' ? ';' : ' ') + src[key];
8457 dst.$set(key, value, true, srcAttr[key]);
8461 // copy the new attributes on the old attrs object
8462 forEach(src, function(value, key) {
8463 if (key == 'class') {
8464 safeAddClass($element, value);
8465 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8466 } else if (key == 'style') {
8467 $element.attr('style', $element.attr('style') + ';' + value);
8468 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8469 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8470 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8471 // have an attribute like "has-own-property" or "data-has-own-property", etc.
8472 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8474 dstAttr[key] = srcAttr[key];
8480 function compileTemplateUrl(directives, $compileNode, tAttrs,
8481 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8483 afterTemplateNodeLinkFn,
8484 afterTemplateChildLinkFn,
8485 beforeTemplateCompileNode = $compileNode[0],
8486 origAsyncDirective = directives.shift(),
8487 derivedSyncDirective = inherit(origAsyncDirective, {
8488 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8490 templateUrl = (isFunction(origAsyncDirective.templateUrl))
8491 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8492 : origAsyncDirective.templateUrl,
8493 templateNamespace = origAsyncDirective.templateNamespace;
8495 $compileNode.empty();
8497 $templateRequest(templateUrl)
8498 .then(function(content) {
8499 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8501 content = denormalizeTemplate(content);
8503 if (origAsyncDirective.replace) {
8504 if (jqLiteIsTextNode(content)) {
8507 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8509 compileNode = $template[0];
8511 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8512 throw $compileMinErr('tplrt',
8513 "Template for directive '{0}' must have exactly one root element. {1}",
8514 origAsyncDirective.name, templateUrl);
8517 tempTemplateAttrs = {$attr: {}};
8518 replaceWith($rootElement, $compileNode, compileNode);
8519 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8521 if (isObject(origAsyncDirective.scope)) {
8522 // the original directive that caused the template to be loaded async required
8524 markDirectiveScope(templateDirectives, true);
8526 directives = templateDirectives.concat(directives);
8527 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8529 compileNode = beforeTemplateCompileNode;
8530 $compileNode.html(content);
8533 directives.unshift(derivedSyncDirective);
8535 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8536 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8537 previousCompileContext);
8538 forEach($rootElement, function(node, i) {
8539 if (node == compileNode) {
8540 $rootElement[i] = $compileNode[0];
8543 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8545 while (linkQueue.length) {
8546 var scope = linkQueue.shift(),
8547 beforeTemplateLinkNode = linkQueue.shift(),
8548 linkRootElement = linkQueue.shift(),
8549 boundTranscludeFn = linkQueue.shift(),
8550 linkNode = $compileNode[0];
8552 if (scope.$$destroyed) continue;
8554 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8555 var oldClasses = beforeTemplateLinkNode.className;
8557 if (!(previousCompileContext.hasElementTranscludeDirective &&
8558 origAsyncDirective.replace)) {
8559 // it was cloned therefore we have to clone as well.
8560 linkNode = jqLiteClone(compileNode);
8562 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8564 // Copy in CSS classes from original node
8565 safeAddClass(jqLite(linkNode), oldClasses);
8567 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8568 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8570 childBoundTranscludeFn = boundTranscludeFn;
8572 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8573 childBoundTranscludeFn);
8578 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8579 var childBoundTranscludeFn = boundTranscludeFn;
8580 if (scope.$$destroyed) return;
8582 linkQueue.push(scope,
8585 childBoundTranscludeFn);
8587 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8588 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8590 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8597 * Sorting function for bound directives.
8599 function byPriority(a, b) {
8600 var diff = b.priority - a.priority;
8601 if (diff !== 0) return diff;
8602 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8603 return a.index - b.index;
8606 function assertNoDuplicate(what, previousDirective, directive, element) {
8608 function wrapModuleNameIfDefined(moduleName) {
8610 (' (module: ' + moduleName + ')') :
8614 if (previousDirective) {
8615 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8616 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8617 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8622 function addTextInterpolateDirective(directives, text) {
8623 var interpolateFn = $interpolate(text, true);
8624 if (interpolateFn) {
8627 compile: function textInterpolateCompileFn(templateNode) {
8628 var templateNodeParent = templateNode.parent(),
8629 hasCompileParent = !!templateNodeParent.length;
8631 // When transcluding a template that has bindings in the root
8632 // we don't have a parent and thus need to add the class during linking fn.
8633 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8635 return function textInterpolateLinkFn(scope, node) {
8636 var parent = node.parent();
8637 if (!hasCompileParent) compile.$$addBindingClass(parent);
8638 compile.$$addBindingInfo(parent, interpolateFn.expressions);
8639 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8640 node[0].nodeValue = value;
8649 function wrapTemplate(type, template) {
8650 type = lowercase(type || 'html');
8654 var wrapper = document.createElement('div');
8655 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8656 return wrapper.childNodes[0].childNodes;
8663 function getTrustedContext(node, attrNormalizedName) {
8664 if (attrNormalizedName == "srcdoc") {
8667 var tag = nodeName_(node);
8668 // maction[xlink:href] can source SVG. It's not limited to <maction>.
8669 if (attrNormalizedName == "xlinkHref" ||
8670 (tag == "form" && attrNormalizedName == "action") ||
8671 (tag != "img" && (attrNormalizedName == "src" ||
8672 attrNormalizedName == "ngSrc"))) {
8673 return $sce.RESOURCE_URL;
8678 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8679 var trustedContext = getTrustedContext(node, name);
8680 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8682 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8684 // no interpolation found -> ignore
8685 if (!interpolateFn) return;
8688 if (name === "multiple" && nodeName_(node) === "select") {
8689 throw $compileMinErr("selmulti",
8690 "Binding to the 'multiple' attribute is not supported. Element: {0}",
8696 compile: function() {
8698 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8699 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8701 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8702 throw $compileMinErr('nodomevents',
8703 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
8704 "ng- versions (such as ng-click instead of onclick) instead.");
8707 // If the attribute has changed since last $interpolate()ed
8708 var newValue = attr[name];
8709 if (newValue !== value) {
8710 // we need to interpolate again since the attribute value has been updated
8711 // (e.g. by another directive's compile function)
8712 // ensure unset/empty values make interpolateFn falsy
8713 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8717 // if attribute was updated so that there is no interpolation going on we don't want to
8718 // register any observers
8719 if (!interpolateFn) return;
8721 // initialize attr object so that it's ready in case we need the value for isolate
8722 // scope initialization, otherwise the value would not be available from isolate
8723 // directive's linking fn during linking phase
8724 attr[name] = interpolateFn(scope);
8726 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8727 (attr.$$observers && attr.$$observers[name].$$scope || scope).
8728 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8729 //special case for class attribute addition + removal
8730 //so that class changes can tap into the animation
8731 //hooks provided by the $animate service. Be sure to
8732 //skip animations when the first digest occurs (when
8733 //both the new and the old values are the same) since
8734 //the CSS classes are the non-interpolated values
8735 if (name === 'class' && newValue != oldValue) {
8736 attr.$updateClass(newValue, oldValue);
8738 attr.$set(name, newValue);
8749 * This is a special jqLite.replaceWith, which can replace items which
8750 * have no parents, provided that the containing jqLite collection is provided.
8752 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8753 * in the root of the tree.
8754 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8755 * the shell, but replace its DOM node reference.
8756 * @param {Node} newNode The new DOM node.
8758 function replaceWith($rootElement, elementsToRemove, newNode) {
8759 var firstElementToRemove = elementsToRemove[0],
8760 removeCount = elementsToRemove.length,
8761 parent = firstElementToRemove.parentNode,
8765 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8766 if ($rootElement[i] == firstElementToRemove) {
8767 $rootElement[i++] = newNode;
8768 for (var j = i, j2 = j + removeCount - 1,
8769 jj = $rootElement.length;
8770 j < jj; j++, j2++) {
8772 $rootElement[j] = $rootElement[j2];
8774 delete $rootElement[j];
8777 $rootElement.length -= removeCount - 1;
8779 // If the replaced element is also the jQuery .context then replace it
8780 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8781 // http://api.jquery.com/context/
8782 if ($rootElement.context === firstElementToRemove) {
8783 $rootElement.context = newNode;
8791 parent.replaceChild(newNode, firstElementToRemove);
8794 // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8795 var fragment = document.createDocumentFragment();
8796 fragment.appendChild(firstElementToRemove);
8798 if (jqLite.hasData(firstElementToRemove)) {
8799 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8800 // data here because there's no public interface in jQuery to do that and copying over
8801 // event listeners (which is the main use of private data) wouldn't work anyway.
8802 jqLite.data(newNode, jqLite.data(firstElementToRemove));
8804 // Remove data of the replaced element. We cannot just call .remove()
8805 // on the element it since that would deallocate scope that is needed
8806 // for the new node. Instead, remove the data "manually".
8808 delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8810 // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8811 // the replaced element. The cleanData version monkey-patched by Angular would cause
8812 // the scope to be trashed and we do need the very same scope to work with the new
8813 // element. However, we cannot just cache the non-patched version and use it here as
8814 // that would break if another library patches the method after Angular does (one
8815 // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8816 // skipped this one time.
8817 skipDestroyOnNextJQueryCleanData = true;
8818 jQuery.cleanData([firstElementToRemove]);
8822 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8823 var element = elementsToRemove[k];
8824 jqLite(element).remove(); // must do this way to clean up expando
8825 fragment.appendChild(element);
8826 delete elementsToRemove[k];
8829 elementsToRemove[0] = newNode;
8830 elementsToRemove.length = 1;
8834 function cloneAndAnnotateFn(fn, annotation) {
8835 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8839 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8841 linkFn(scope, $element, attrs, controllers, transcludeFn);
8843 $exceptionHandler(e, startingTag($element));
8848 // Set up $watches for isolate scope and controller bindings. This process
8849 // only occurs for isolate scopes and new scopes with controllerAs.
8850 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
8851 var removeWatchCollection = [];
8852 forEach(bindings, function(definition, scopeName) {
8853 var attrName = definition.attrName,
8854 optional = definition.optional,
8855 mode = definition.mode, // @, =, or &
8857 parentGet, parentSet, compare;
8862 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
8863 destination[scopeName] = attrs[attrName] = void 0;
8865 attrs.$observe(attrName, function(value) {
8866 if (isString(value)) {
8867 destination[scopeName] = value;
8870 attrs.$$observers[attrName].$$scope = scope;
8871 if (isString(attrs[attrName])) {
8872 // If the attribute has been provided then we trigger an interpolation to ensure
8873 // the value is there for use in the link fn
8874 destination[scopeName] = $interpolate(attrs[attrName])(scope);
8879 if (!hasOwnProperty.call(attrs, attrName)) {
8880 if (optional) break;
8881 attrs[attrName] = void 0;
8883 if (optional && !attrs[attrName]) break;
8885 parentGet = $parse(attrs[attrName]);
8886 if (parentGet.literal) {
8889 compare = function(a, b) { return a === b || (a !== a && b !== b); };
8891 parentSet = parentGet.assign || function() {
8892 // reset the change, or we will throw this exception on every $digest
8893 lastValue = destination[scopeName] = parentGet(scope);
8894 throw $compileMinErr('nonassign',
8895 "Expression '{0}' used with directive '{1}' is non-assignable!",
8896 attrs[attrName], directive.name);
8898 lastValue = destination[scopeName] = parentGet(scope);
8899 var parentValueWatch = function parentValueWatch(parentValue) {
8900 if (!compare(parentValue, destination[scopeName])) {
8901 // we are out of sync and need to copy
8902 if (!compare(parentValue, lastValue)) {
8903 // parent changed and it has precedence
8904 destination[scopeName] = parentValue;
8906 // if the parent can be assigned then do so
8907 parentSet(scope, parentValue = destination[scopeName]);
8910 return lastValue = parentValue;
8912 parentValueWatch.$stateful = true;
8914 if (definition.collection) {
8915 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8917 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8919 removeWatchCollection.push(removeWatch);
8923 // Don't assign Object.prototype method to scope
8924 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
8926 // Don't assign noop to destination if expression is not valid
8927 if (parentGet === noop && optional) break;
8929 destination[scopeName] = function(locals) {
8930 return parentGet(scope, locals);
8936 return removeWatchCollection.length && function removeWatches() {
8937 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
8938 removeWatchCollection[i]();
8945 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8947 * Converts all accepted directives format into proper directive name.
8948 * @param name Name to normalize
8950 function directiveNormalize(name) {
8951 return camelCase(name.replace(PREFIX_REGEXP, ''));
8956 * @name $compile.directive.Attributes
8959 * A shared object between directive compile / linking functions which contains normalized DOM
8960 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8961 * needed since all of these are treated as equivalent in Angular:
8964 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8970 * @name $compile.directive.Attributes#$attr
8973 * A map of DOM element attribute names to the normalized name. This is
8974 * needed to do reverse lookup from normalized name back to actual name.
8980 * @name $compile.directive.Attributes#$set
8984 * Set DOM element attribute value.
8987 * @param {string} name Normalized element attribute name of the property to modify. The name is
8988 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8989 * property to the original name.
8990 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8996 * Closure compiler type information
8999 function nodesetLinkingFn(
9000 /* angular.Scope */ scope,
9001 /* NodeList */ nodeList,
9002 /* Element */ rootElement,
9003 /* function(Function) */ boundTranscludeFn
9006 function directiveLinkingFn(
9007 /* nodesetLinkingFn */ nodesetLinkingFn,
9008 /* angular.Scope */ scope,
9010 /* Element */ rootElement,
9011 /* function(Function) */ boundTranscludeFn
9014 function tokenDifference(str1, str2) {
9016 tokens1 = str1.split(/\s+/),
9017 tokens2 = str2.split(/\s+/);
9020 for (var i = 0; i < tokens1.length; i++) {
9021 var token = tokens1[i];
9022 for (var j = 0; j < tokens2.length; j++) {
9023 if (token == tokens2[j]) continue outer;
9025 values += (values.length > 0 ? ' ' : '') + token;
9030 function removeComments(jqNodes) {
9031 jqNodes = jqLite(jqNodes);
9032 var i = jqNodes.length;
9039 var node = jqNodes[i];
9040 if (node.nodeType === NODE_TYPE_COMMENT) {
9041 splice.call(jqNodes, i, 1);
9047 var $controllerMinErr = minErr('$controller');
9050 var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9051 function identifierForController(controller, ident) {
9052 if (ident && isString(ident)) return ident;
9053 if (isString(controller)) {
9054 var match = CNTRL_REG.exec(controller);
9055 if (match) return match[3];
9062 * @name $controllerProvider
9064 * The {@link ng.$controller $controller service} is used by Angular to create new
9067 * This provider allows controller registration via the
9068 * {@link ng.$controllerProvider#register register} method.
9070 function $ControllerProvider() {
9071 var controllers = {},
9076 * @name $controllerProvider#register
9077 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9078 * the names and the values are the constructors.
9079 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9080 * annotations in the array notation).
9082 this.register = function(name, constructor) {
9083 assertNotHasOwnProperty(name, 'controller');
9084 if (isObject(name)) {
9085 extend(controllers, name);
9087 controllers[name] = constructor;
9093 * @name $controllerProvider#allowGlobals
9094 * @description If called, allows `$controller` to find controller constructors on `window`
9096 this.allowGlobals = function() {
9101 this.$get = ['$injector', '$window', function($injector, $window) {
9106 * @requires $injector
9108 * @param {Function|string} constructor If called with a function then it's considered to be the
9109 * controller constructor function. Otherwise it's considered to be a string which is used
9110 * to retrieve the controller constructor using the following steps:
9112 * * check if a controller with given name is registered via `$controllerProvider`
9113 * * check if evaluating the string on the current scope returns a constructor
9114 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9115 * `window` object (not recommended)
9117 * The string can use the `controller as property` syntax, where the controller instance is published
9118 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9119 * to work correctly.
9121 * @param {Object} locals Injection locals for Controller.
9122 * @return {Object} Instance of given controller.
9125 * `$controller` service is responsible for instantiating controllers.
9127 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9128 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9130 return function(expression, locals, later, ident) {
9132 // param `later` --- indicates that the controller's constructor is invoked at a later time.
9133 // If true, $controller will allocate the object with the correct
9134 // prototype chain, but will not invoke the controller until a returned
9135 // callback is invoked.
9136 // param `ident` --- An optional label which overrides the label parsed from the controller
9137 // expression, if any.
9138 var instance, match, constructor, identifier;
9139 later = later === true;
9140 if (ident && isString(ident)) {
9144 if (isString(expression)) {
9145 match = expression.match(CNTRL_REG);
9147 throw $controllerMinErr('ctrlfmt',
9148 "Badly formed controller string '{0}'. " +
9149 "Must match `__name__ as __id__` or `__name__`.", expression);
9151 constructor = match[1],
9152 identifier = identifier || match[3];
9153 expression = controllers.hasOwnProperty(constructor)
9154 ? controllers[constructor]
9155 : getter(locals.$scope, constructor, true) ||
9156 (globals ? getter($window, constructor, true) : undefined);
9158 assertArgFn(expression, constructor, true);
9162 // Instantiate controller later:
9163 // This machinery is used to create an instance of the object before calling the
9164 // controller's constructor itself.
9166 // This allows properties to be added to the controller before the constructor is
9167 // invoked. Primarily, this is used for isolate scope bindings in $compile.
9169 // This feature is not intended for use by applications, and is thus not documented
9171 // Object creation: http://jsperf.com/create-constructor/2
9172 var controllerPrototype = (isArray(expression) ?
9173 expression[expression.length - 1] : expression).prototype;
9174 instance = Object.create(controllerPrototype || null);
9177 addIdentifier(locals, identifier, instance, constructor || expression.name);
9181 return instantiate = extend(function() {
9182 var result = $injector.invoke(expression, instance, locals, constructor);
9183 if (result !== instance && (isObject(result) || isFunction(result))) {
9186 // If result changed, re-assign controllerAs value to scope.
9187 addIdentifier(locals, identifier, instance, constructor || expression.name);
9193 identifier: identifier
9197 instance = $injector.instantiate(expression, locals, constructor);
9200 addIdentifier(locals, identifier, instance, constructor || expression.name);
9206 function addIdentifier(locals, identifier, instance, name) {
9207 if (!(locals && isObject(locals.$scope))) {
9208 throw minErr('$controller')('noscp',
9209 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9213 locals.$scope[identifier] = instance;
9224 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9227 <example module="documentExample">
9228 <file name="index.html">
9229 <div ng-controller="ExampleController">
9230 <p>$document title: <b ng-bind="title"></b></p>
9231 <p>window.document title: <b ng-bind="windowTitle"></b></p>
9234 <file name="script.js">
9235 angular.module('documentExample', [])
9236 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9237 $scope.title = $document[0].title;
9238 $scope.windowTitle = angular.element(window.document)[0].title;
9243 function $DocumentProvider() {
9244 this.$get = ['$window', function(window) {
9245 return jqLite(window.document);
9251 * @name $exceptionHandler
9255 * Any uncaught exception in angular expressions is delegated to this service.
9256 * The default implementation simply delegates to `$log.error` which logs it into
9257 * the browser console.
9259 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9260 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9265 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9266 * return function(exception, cause) {
9267 * exception.message += ' (caused by "' + cause + '")';
9273 * This example will override the normal action of `$exceptionHandler`, to make angular
9274 * exceptions fail hard when they happen, instead of just logging to the console.
9277 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9278 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9279 * (unless executed during a digest).
9281 * If you wish, you can manually delegate exceptions, e.g.
9282 * `try { ... } catch(e) { $exceptionHandler(e); }`
9284 * @param {Error} exception Exception associated with the error.
9285 * @param {string=} cause optional information about the context in which
9286 * the error was thrown.
9289 function $ExceptionHandlerProvider() {
9290 this.$get = ['$log', function($log) {
9291 return function(exception, cause) {
9292 $log.error.apply($log, arguments);
9297 var $$ForceReflowProvider = function() {
9298 this.$get = ['$document', function($document) {
9299 return function(domNode) {
9300 //the line below will force the browser to perform a repaint so
9301 //that all the animated elements within the animation frame will
9302 //be properly updated and drawn on screen. This is required to
9303 //ensure that the preparation animation is properly flushed so that
9304 //the active state picks up from there. DO NOT REMOVE THIS LINE.
9305 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9306 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9307 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9309 if (!domNode.nodeType && domNode instanceof jqLite) {
9310 domNode = domNode[0];
9313 domNode = $document[0].body;
9315 return domNode.offsetWidth + 1;
9320 var APPLICATION_JSON = 'application/json';
9321 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9322 var JSON_START = /^\[|^\{(?!\{)/;
9327 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9328 var $httpMinErr = minErr('$http');
9329 var $httpMinErrLegacyFn = function(method) {
9331 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9335 function serializeValue(v) {
9337 return isDate(v) ? v.toISOString() : toJson(v);
9343 function $HttpParamSerializerProvider() {
9346 * @name $httpParamSerializer
9349 * Default {@link $http `$http`} params serializer that converts objects to strings
9350 * according to the following rules:
9352 * * `{'foo': 'bar'}` results in `foo=bar`
9353 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9354 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9355 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9357 * Note that serializer will sort the request parameters alphabetically.
9360 this.$get = function() {
9361 return function ngParamSerializer(params) {
9362 if (!params) return '';
9364 forEachSorted(params, function(value, key) {
9365 if (value === null || isUndefined(value)) return;
9366 if (isArray(value)) {
9367 forEach(value, function(v, k) {
9368 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
9371 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9375 return parts.join('&');
9380 function $HttpParamSerializerJQLikeProvider() {
9383 * @name $httpParamSerializerJQLike
9386 * Alternative {@link $http `$http`} params serializer that follows
9387 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9388 * The serializer will also sort the params alphabetically.
9390 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9397 * paramSerializer: '$httpParamSerializerJQLike'
9401 * It is also possible to set it as the default `paramSerializer` in the
9402 * {@link $httpProvider#defaults `$httpProvider`}.
9404 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9405 * form data for submission:
9408 * .controller(function($http, $httpParamSerializerJQLike) {
9414 * data: $httpParamSerializerJQLike(myData),
9416 * 'Content-Type': 'application/x-www-form-urlencoded'
9424 this.$get = function() {
9425 return function jQueryLikeParamSerializer(params) {
9426 if (!params) return '';
9428 serialize(params, '', true);
9429 return parts.join('&');
9431 function serialize(toSerialize, prefix, topLevel) {
9432 if (toSerialize === null || isUndefined(toSerialize)) return;
9433 if (isArray(toSerialize)) {
9434 forEach(toSerialize, function(value, index) {
9435 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9437 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9438 forEachSorted(toSerialize, function(value, key) {
9439 serialize(value, prefix +
9440 (topLevel ? '' : '[') +
9442 (topLevel ? '' : ']'));
9445 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9452 function defaultHttpResponseTransform(data, headers) {
9453 if (isString(data)) {
9454 // Strip json vulnerability protection prefix and trim whitespace
9455 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9458 var contentType = headers('Content-Type');
9459 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9460 data = fromJson(tempData);
9468 function isJsonLike(str) {
9469 var jsonStart = str.match(JSON_START);
9470 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9474 * Parse headers into key value object
9476 * @param {string} headers Raw headers as a string
9477 * @returns {Object} Parsed headers as key value object
9479 function parseHeaders(headers) {
9480 var parsed = createMap(), i;
9482 function fillInParsed(key, val) {
9484 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9488 if (isString(headers)) {
9489 forEach(headers.split('\n'), function(line) {
9490 i = line.indexOf(':');
9491 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9493 } else if (isObject(headers)) {
9494 forEach(headers, function(headerVal, headerKey) {
9495 fillInParsed(lowercase(headerKey), trim(headerVal));
9504 * Returns a function that provides access to parsed headers.
9506 * Headers are lazy parsed when first requested.
9509 * @param {(string|Object)} headers Headers to provide access to.
9510 * @returns {function(string=)} Returns a getter function which if called with:
9512 * - if called with single an argument returns a single header value or null
9513 * - if called with no arguments returns an object containing all headers.
9515 function headersGetter(headers) {
9518 return function(name) {
9519 if (!headersObj) headersObj = parseHeaders(headers);
9522 var value = headersObj[lowercase(name)];
9523 if (value === void 0) {
9535 * Chain all given functions
9537 * This function is used for both request and response transforming
9539 * @param {*} data Data to transform.
9540 * @param {function(string=)} headers HTTP headers getter fn.
9541 * @param {number} status HTTP status code of the response.
9542 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9543 * @returns {*} Transformed data.
9545 function transformData(data, headers, status, fns) {
9546 if (isFunction(fns)) {
9547 return fns(data, headers, status);
9550 forEach(fns, function(fn) {
9551 data = fn(data, headers, status);
9558 function isSuccess(status) {
9559 return 200 <= status && status < 300;
9565 * @name $httpProvider
9567 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9569 function $HttpProvider() {
9572 * @name $httpProvider#defaults
9575 * Object containing default values for all {@link ng.$http $http} requests.
9577 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9578 * that will provide the cache for all requests who set their `cache` property to `true`.
9579 * If you set the `defaults.cache = false` then only requests that specify their own custom
9580 * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9582 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9583 * Defaults value is `'XSRF-TOKEN'`.
9585 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9586 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9588 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9589 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9590 * setting default headers.
9591 * - **`defaults.headers.common`**
9592 * - **`defaults.headers.post`**
9593 * - **`defaults.headers.put`**
9594 * - **`defaults.headers.patch`**
9597 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9598 * used to the prepare string representation of request parameters (specified as an object).
9599 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9600 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9603 var defaults = this.defaults = {
9604 // transform incoming response data
9605 transformResponse: [defaultHttpResponseTransform],
9607 // transform outgoing request data
9608 transformRequest: [function(d) {
9609 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9615 'Accept': 'application/json, text/plain, */*'
9617 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9618 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9619 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9622 xsrfCookieName: 'XSRF-TOKEN',
9623 xsrfHeaderName: 'X-XSRF-TOKEN',
9625 paramSerializer: '$httpParamSerializer'
9628 var useApplyAsync = false;
9631 * @name $httpProvider#useApplyAsync
9634 * Configure $http service to combine processing of multiple http responses received at around
9635 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9636 * significant performance improvement for bigger applications that make many HTTP requests
9637 * concurrently (common during application bootstrap).
9639 * Defaults to false. If no value is specified, returns the current configured value.
9641 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9642 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9643 * to load and share the same digest cycle.
9645 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9646 * otherwise, returns the current configured value.
9648 this.useApplyAsync = function(value) {
9649 if (isDefined(value)) {
9650 useApplyAsync = !!value;
9653 return useApplyAsync;
9656 var useLegacyPromise = true;
9659 * @name $httpProvider#useLegacyPromiseExtensions
9662 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9663 * This should be used to make sure that applications work without these methods.
9665 * Defaults to true. If no value is specified, returns the current configured value.
9667 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9669 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9670 * otherwise, returns the current configured value.
9672 this.useLegacyPromiseExtensions = function(value) {
9673 if (isDefined(value)) {
9674 useLegacyPromise = !!value;
9677 return useLegacyPromise;
9682 * @name $httpProvider#interceptors
9685 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9686 * pre-processing of request or postprocessing of responses.
9688 * These service factories are ordered by request, i.e. they are applied in the same order as the
9689 * array, on request, but reverse order, on response.
9691 * {@link ng.$http#interceptors Interceptors detailed info}
9693 var interceptorFactories = this.interceptors = [];
9695 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9696 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9698 var defaultCache = $cacheFactory('$http');
9701 * Make sure that default param serializer is exposed as a function
9703 defaults.paramSerializer = isString(defaults.paramSerializer) ?
9704 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9707 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9708 * The reversal is needed so that we can build up the interception chain around the
9711 var reversedInterceptors = [];
9713 forEach(interceptorFactories, function(interceptorFactory) {
9714 reversedInterceptors.unshift(isString(interceptorFactory)
9715 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9722 * @requires ng.$httpBackend
9723 * @requires $cacheFactory
9724 * @requires $rootScope
9726 * @requires $injector
9729 * The `$http` service is a core Angular service that facilitates communication with the remote
9730 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9731 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9733 * For unit testing applications that use `$http` service, see
9734 * {@link ngMock.$httpBackend $httpBackend mock}.
9736 * For a higher level of abstraction, please check out the {@link ngResource.$resource
9737 * $resource} service.
9739 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9740 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9741 * it is important to familiarize yourself with these APIs and the guarantees they provide.
9745 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9746 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
9749 * // Simple GET request example:
9753 * }).then(function successCallback(response) {
9754 * // this callback will be called asynchronously
9755 * // when the response is available
9756 * }, function errorCallback(response) {
9757 * // called asynchronously if an error occurs
9758 * // or server returns response with an error status.
9762 * The response object has these properties:
9764 * - **data** – `{string|Object}` – The response body transformed with the transform
9766 * - **status** – `{number}` – HTTP status code of the response.
9767 * - **headers** – `{function([headerName])}` – Header getter function.
9768 * - **config** – `{Object}` – The configuration object that was used to generate the request.
9769 * - **statusText** – `{string}` – HTTP status text of the response.
9771 * A response status code between 200 and 299 is considered a success status and
9772 * will result in the success callback being called. Note that if the response is a redirect,
9773 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9774 * called for such responses.
9777 * ## Shortcut methods
9779 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9780 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9784 * $http.get('/someUrl', config).then(successCallback, errorCallback);
9785 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9788 * Complete list of shortcut methods:
9790 * - {@link ng.$http#get $http.get}
9791 * - {@link ng.$http#head $http.head}
9792 * - {@link ng.$http#post $http.post}
9793 * - {@link ng.$http#put $http.put}
9794 * - {@link ng.$http#delete $http.delete}
9795 * - {@link ng.$http#jsonp $http.jsonp}
9796 * - {@link ng.$http#patch $http.patch}
9799 * ## Writing Unit Tests that use $http
9800 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9801 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9802 * request using trained responses.
9805 * $httpBackend.expectGET(...);
9807 * $httpBackend.flush();
9810 * ## Deprecation Notice
9811 * <div class="alert alert-danger">
9812 * The `$http` legacy promise methods `success` and `error` have been deprecated.
9813 * Use the standard `then` method instead.
9814 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9815 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9818 * ## Setting HTTP Headers
9820 * The $http service will automatically add certain HTTP headers to all requests. These defaults
9821 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9822 * object, which currently contains this default configuration:
9824 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9825 * - `Accept: application/json, text/plain, * / *`
9826 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9827 * - `Content-Type: application/json`
9828 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9829 * - `Content-Type: application/json`
9831 * To add or overwrite these defaults, simply add or remove a property from these configuration
9832 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9833 * with the lowercased HTTP method name as the key, e.g.
9834 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9836 * The defaults can also be set at runtime via the `$http.defaults` object in the same
9837 * fashion. For example:
9840 * module.run(function($http) {
9841 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9845 * In addition, you can supply a `headers` property in the config object passed when
9846 * calling `$http(config)`, which overrides the defaults without changing them globally.
9848 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9849 * Use the `headers` property, setting the desired header to `undefined`. For example:
9854 * url: 'http://example.com',
9856 * 'Content-Type': undefined
9858 * data: { test: 'test' }
9861 * $http(req).then(function(){...}, function(){...});
9864 * ## Transforming Requests and Responses
9866 * Both requests and responses can be transformed using transformation functions: `transformRequest`
9867 * and `transformResponse`. These properties can be a single function that returns
9868 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9869 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9871 * ### Default Transformations
9873 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9874 * `defaults.transformResponse` properties. If a request does not provide its own transformations
9875 * then these will be applied.
9877 * You can augment or replace the default transformations by modifying these properties by adding to or
9878 * replacing the array.
9880 * Angular provides the following default transformations:
9882 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9884 * - If the `data` property of the request configuration object contains an object, serialize it
9887 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9889 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
9890 * - If JSON response is detected, deserialize it using a JSON parser.
9893 * ### Overriding the Default Transformations Per Request
9895 * If you wish override the request/response transformations only for a single request then provide
9896 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9899 * Note that if you provide these properties on the config object the default transformations will be
9900 * overwritten. If you wish to augment the default transformations then you must include them in your
9901 * local transformation array.
9903 * The following code demonstrates adding a new response transformation to be run after the default response
9904 * transformations have been run.
9907 * function appendTransform(defaults, transform) {
9909 * // We can't guarantee that the default transformation is an array
9910 * defaults = angular.isArray(defaults) ? defaults : [defaults];
9912 * // Append the new transformation to the defaults
9913 * return defaults.concat(transform);
9919 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9920 * return doTransform(value);
9928 * To enable caching, set the request configuration `cache` property to `true` (to use default
9929 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
9930 * When the cache is enabled, `$http` stores the response from the server in the specified
9931 * cache. The next time the same request is made, the response is served from the cache without
9932 * sending a request to the server.
9934 * Note that even if the response is served from cache, delivery of the data is asynchronous in
9935 * the same way that real requests are.
9937 * If there are multiple GET requests for the same URL that should be cached using the same
9938 * cache, but the cache is not populated yet, only one request to the server will be made and
9939 * the remaining requests will be fulfilled using the response from the first request.
9941 * You can change the default cache to a new object (built with
9942 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
9943 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
9944 * their `cache` property to `true` will now use this cache object.
9946 * If you set the default cache to `false` then only requests that specify their own custom
9947 * cache object will be cached.
9951 * Before you start creating interceptors, be sure to understand the
9952 * {@link ng.$q $q and deferred/promise APIs}.
9954 * For purposes of global error handling, authentication, or any kind of synchronous or
9955 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
9956 * able to intercept requests before they are handed to the server and
9957 * responses before they are handed over to the application code that
9958 * initiated these requests. The interceptors leverage the {@link ng.$q
9959 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
9961 * The interceptors are service factories that are registered with the `$httpProvider` by
9962 * adding them to the `$httpProvider.interceptors` array. The factory is called and
9963 * injected with dependencies (if specified) and returns the interceptor.
9965 * There are two kinds of interceptors (and two kinds of rejection interceptors):
9967 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
9968 * modify the `config` object or create a new one. The function needs to return the `config`
9969 * object directly, or a promise containing the `config` or a new `config` object.
9970 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
9971 * resolved with a rejection.
9972 * * `response`: interceptors get called with http `response` object. The function is free to
9973 * modify the `response` object or create a new one. The function needs to return the `response`
9974 * object directly, or as a promise containing the `response` or a new `response` object.
9975 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
9976 * resolved with a rejection.
9980 * // register the interceptor as a service
9981 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
9983 * // optional method
9984 * 'request': function(config) {
9985 * // do something on success
9989 * // optional method
9990 * 'requestError': function(rejection) {
9991 * // do something on error
9992 * if (canRecover(rejection)) {
9993 * return responseOrNewPromise
9995 * return $q.reject(rejection);
10000 * // optional method
10001 * 'response': function(response) {
10002 * // do something on success
10006 * // optional method
10007 * 'responseError': function(rejection) {
10008 * // do something on error
10009 * if (canRecover(rejection)) {
10010 * return responseOrNewPromise
10012 * return $q.reject(rejection);
10017 * $httpProvider.interceptors.push('myHttpInterceptor');
10020 * // alternatively, register the interceptor via an anonymous factory
10021 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10023 * 'request': function(config) {
10027 * 'response': function(response) {
10034 * ## Security Considerations
10036 * When designing web applications, consider security threats from:
10038 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10039 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10041 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10042 * pre-configured with strategies that address these issues, but for this to work backend server
10043 * cooperation is required.
10045 * ### JSON Vulnerability Protection
10047 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10048 * allows third party website to turn your JSON resource URL into
10049 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10050 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10051 * Angular will automatically strip the prefix before processing it as JSON.
10053 * For example if your server needs to return:
10058 * which is vulnerable to attack, your server can return:
10064 * Angular will strip the prefix, before processing the JSON.
10067 * ### Cross Site Request Forgery (XSRF) Protection
10069 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10070 * an unauthorized site can gain your user's private data. Angular provides a mechanism
10071 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10072 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10073 * JavaScript that runs on your domain could read the cookie, your server can be assured that
10074 * the XHR came from JavaScript running on your domain. The header will not be set for
10075 * cross-domain requests.
10077 * To take advantage of this, your server needs to set a token in a JavaScript readable session
10078 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10079 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10080 * that only JavaScript running on your domain could have sent the request. The token must be
10081 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10082 * making up its own tokens). We recommend that the token is a digest of your site's
10083 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
10084 * for added security.
10086 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10087 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10088 * or the per-request config object.
10090 * In order to prevent collisions in environments where multiple Angular apps share the
10091 * same domain or subdomain, we recommend that each application uses unique cookie name.
10093 * @param {object} config Object describing the request to be made and how it should be
10094 * processed. The object has following properties:
10096 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10097 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10098 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10099 * with the `paramSerializer` and appended as GET parameters.
10100 * - **data** – `{string|Object}` – Data to be sent as the request message data.
10101 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
10102 * HTTP headers to send to the server. If the return value of a function is null, the
10103 * header will not be sent. Functions accept a config object as an argument.
10104 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10105 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10106 * - **transformRequest** –
10107 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10108 * transform function or an array of such functions. The transform function takes the http
10109 * request body and headers and returns its transformed (typically serialized) version.
10110 * See {@link ng.$http#overriding-the-default-transformations-per-request
10111 * Overriding the Default Transformations}
10112 * - **transformResponse** –
10113 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10114 * transform function or an array of such functions. The transform function takes the http
10115 * response body, headers and status and returns its transformed (typically deserialized) version.
10116 * See {@link ng.$http#overriding-the-default-transformations-per-request
10117 * Overriding the Default TransformationjqLiks}
10118 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10119 * prepare the string representation of request parameters (specified as an object).
10120 * If specified as string, it is interpreted as function registered with the
10121 * {@link $injector $injector}, which means you can create your own serializer
10122 * by registering it as a {@link auto.$provide#service service}.
10123 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10124 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10125 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10126 * GET request, otherwise if a cache instance built with
10127 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10129 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10130 * that should abort the request when resolved.
10131 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10132 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10133 * for more information.
10134 * - **responseType** - `{string}` - see
10135 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10137 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10138 * when the request succeeds or fails.
10141 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10142 * requests. This is primarily meant to be used for debugging purposes.
10146 <example module="httpExample">
10147 <file name="index.html">
10148 <div ng-controller="FetchController">
10149 <select ng-model="method" aria-label="Request method">
10150 <option>GET</option>
10151 <option>JSONP</option>
10153 <input type="text" ng-model="url" size="80" aria-label="URL" />
10154 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10155 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10156 <button id="samplejsonpbtn"
10157 ng-click="updateModel('JSONP',
10158 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10161 <button id="invalidjsonpbtn"
10162 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10165 <pre>http status code: {{status}}</pre>
10166 <pre>http response data: {{data}}</pre>
10169 <file name="script.js">
10170 angular.module('httpExample', [])
10171 .controller('FetchController', ['$scope', '$http', '$templateCache',
10172 function($scope, $http, $templateCache) {
10173 $scope.method = 'GET';
10174 $scope.url = 'http-hello.html';
10176 $scope.fetch = function() {
10177 $scope.code = null;
10178 $scope.response = null;
10180 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10181 then(function(response) {
10182 $scope.status = response.status;
10183 $scope.data = response.data;
10184 }, function(response) {
10185 $scope.data = response.data || "Request failed";
10186 $scope.status = response.status;
10190 $scope.updateModel = function(method, url) {
10191 $scope.method = method;
10196 <file name="http-hello.html">
10199 <file name="protractor.js" type="protractor">
10200 var status = element(by.binding('status'));
10201 var data = element(by.binding('data'));
10202 var fetchBtn = element(by.id('fetchbtn'));
10203 var sampleGetBtn = element(by.id('samplegetbtn'));
10204 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10205 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10207 it('should make an xhr GET request', function() {
10208 sampleGetBtn.click();
10210 expect(status.getText()).toMatch('200');
10211 expect(data.getText()).toMatch(/Hello, \$http!/);
10214 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10215 // it('should make a JSONP request to angularjs.org', function() {
10216 // sampleJsonpBtn.click();
10217 // fetchBtn.click();
10218 // expect(status.getText()).toMatch('200');
10219 // expect(data.getText()).toMatch(/Super Hero!/);
10222 it('should make JSONP request to invalid URL and invoke the error handler',
10224 invalidJsonpBtn.click();
10226 expect(status.getText()).toMatch('0');
10227 expect(data.getText()).toMatch('Request failed');
10232 function $http(requestConfig) {
10234 if (!angular.isObject(requestConfig)) {
10235 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
10238 var config = extend({
10240 transformRequest: defaults.transformRequest,
10241 transformResponse: defaults.transformResponse,
10242 paramSerializer: defaults.paramSerializer
10245 config.headers = mergeHeaders(requestConfig);
10246 config.method = uppercase(config.method);
10247 config.paramSerializer = isString(config.paramSerializer) ?
10248 $injector.get(config.paramSerializer) : config.paramSerializer;
10250 var serverRequest = function(config) {
10251 var headers = config.headers;
10252 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10254 // strip content-type if data is undefined
10255 if (isUndefined(reqData)) {
10256 forEach(headers, function(value, header) {
10257 if (lowercase(header) === 'content-type') {
10258 delete headers[header];
10263 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10264 config.withCredentials = defaults.withCredentials;
10268 return sendReq(config, reqData).then(transformResponse, transformResponse);
10271 var chain = [serverRequest, undefined];
10272 var promise = $q.when(config);
10274 // apply interceptors
10275 forEach(reversedInterceptors, function(interceptor) {
10276 if (interceptor.request || interceptor.requestError) {
10277 chain.unshift(interceptor.request, interceptor.requestError);
10279 if (interceptor.response || interceptor.responseError) {
10280 chain.push(interceptor.response, interceptor.responseError);
10284 while (chain.length) {
10285 var thenFn = chain.shift();
10286 var rejectFn = chain.shift();
10288 promise = promise.then(thenFn, rejectFn);
10291 if (useLegacyPromise) {
10292 promise.success = function(fn) {
10293 assertArgFn(fn, 'fn');
10295 promise.then(function(response) {
10296 fn(response.data, response.status, response.headers, config);
10301 promise.error = function(fn) {
10302 assertArgFn(fn, 'fn');
10304 promise.then(null, function(response) {
10305 fn(response.data, response.status, response.headers, config);
10310 promise.success = $httpMinErrLegacyFn('success');
10311 promise.error = $httpMinErrLegacyFn('error');
10316 function transformResponse(response) {
10317 // make a copy since the response must be cacheable
10318 var resp = extend({}, response);
10319 resp.data = transformData(response.data, response.headers, response.status,
10320 config.transformResponse);
10321 return (isSuccess(response.status))
10326 function executeHeaderFns(headers, config) {
10327 var headerContent, processedHeaders = {};
10329 forEach(headers, function(headerFn, header) {
10330 if (isFunction(headerFn)) {
10331 headerContent = headerFn(config);
10332 if (headerContent != null) {
10333 processedHeaders[header] = headerContent;
10336 processedHeaders[header] = headerFn;
10340 return processedHeaders;
10343 function mergeHeaders(config) {
10344 var defHeaders = defaults.headers,
10345 reqHeaders = extend({}, config.headers),
10346 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10348 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10350 // using for-in instead of forEach to avoid unecessary iteration after header has been found
10351 defaultHeadersIteration:
10352 for (defHeaderName in defHeaders) {
10353 lowercaseDefHeaderName = lowercase(defHeaderName);
10355 for (reqHeaderName in reqHeaders) {
10356 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10357 continue defaultHeadersIteration;
10361 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10364 // execute if header value is a function for merged headers
10365 return executeHeaderFns(reqHeaders, shallowCopy(config));
10369 $http.pendingRequests = [];
10376 * Shortcut method to perform `GET` request.
10378 * @param {string} url Relative or absolute URL specifying the destination of the request
10379 * @param {Object=} config Optional configuration object
10380 * @returns {HttpPromise} Future object
10385 * @name $http#delete
10388 * Shortcut method to perform `DELETE` request.
10390 * @param {string} url Relative or absolute URL specifying the destination of the request
10391 * @param {Object=} config Optional configuration object
10392 * @returns {HttpPromise} Future object
10400 * Shortcut method to perform `HEAD` request.
10402 * @param {string} url Relative or absolute URL specifying the destination of the request
10403 * @param {Object=} config Optional configuration object
10404 * @returns {HttpPromise} Future object
10409 * @name $http#jsonp
10412 * Shortcut method to perform `JSONP` request.
10414 * @param {string} url Relative or absolute URL specifying the destination of the request.
10415 * The name of the callback should be the string `JSON_CALLBACK`.
10416 * @param {Object=} config Optional configuration object
10417 * @returns {HttpPromise} Future object
10419 createShortMethods('get', 'delete', 'head', 'jsonp');
10426 * Shortcut method to perform `POST` request.
10428 * @param {string} url Relative or absolute URL specifying the destination of the request
10429 * @param {*} data Request content
10430 * @param {Object=} config Optional configuration object
10431 * @returns {HttpPromise} Future object
10439 * Shortcut method to perform `PUT` request.
10441 * @param {string} url Relative or absolute URL specifying the destination of the request
10442 * @param {*} data Request content
10443 * @param {Object=} config Optional configuration object
10444 * @returns {HttpPromise} Future object
10449 * @name $http#patch
10452 * Shortcut method to perform `PATCH` request.
10454 * @param {string} url Relative or absolute URL specifying the destination of the request
10455 * @param {*} data Request content
10456 * @param {Object=} config Optional configuration object
10457 * @returns {HttpPromise} Future object
10459 createShortMethodsWithData('post', 'put', 'patch');
10463 * @name $http#defaults
10466 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10467 * default headers, withCredentials as well as request and response transformations.
10469 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10471 $http.defaults = defaults;
10477 function createShortMethods(names) {
10478 forEach(arguments, function(name) {
10479 $http[name] = function(url, config) {
10480 return $http(extend({}, config || {}, {
10489 function createShortMethodsWithData(name) {
10490 forEach(arguments, function(name) {
10491 $http[name] = function(url, data, config) {
10492 return $http(extend({}, config || {}, {
10503 * Makes the request.
10505 * !!! ACCESSES CLOSURE VARS:
10506 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10508 function sendReq(config, reqData) {
10509 var deferred = $q.defer(),
10510 promise = deferred.promise,
10513 reqHeaders = config.headers,
10514 url = buildUrl(config.url, config.paramSerializer(config.params));
10516 $http.pendingRequests.push(config);
10517 promise.then(removePendingReq, removePendingReq);
10520 if ((config.cache || defaults.cache) && config.cache !== false &&
10521 (config.method === 'GET' || config.method === 'JSONP')) {
10522 cache = isObject(config.cache) ? config.cache
10523 : isObject(defaults.cache) ? defaults.cache
10528 cachedResp = cache.get(url);
10529 if (isDefined(cachedResp)) {
10530 if (isPromiseLike(cachedResp)) {
10531 // cached request has already been sent, but there is no response yet
10532 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10534 // serving from cache
10535 if (isArray(cachedResp)) {
10536 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10538 resolvePromise(cachedResp, 200, {}, 'OK');
10542 // put the promise for the non-transformed response into cache as a placeholder
10543 cache.put(url, promise);
10548 // if we won't have the response in cache, set the xsrf headers and
10549 // send the request to the backend
10550 if (isUndefined(cachedResp)) {
10551 var xsrfValue = urlIsSameOrigin(config.url)
10552 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10555 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10558 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10559 config.withCredentials, config.responseType);
10566 * Callback registered to $httpBackend():
10567 * - caches the response if desired
10568 * - resolves the raw $http promise
10571 function done(status, response, headersString, statusText) {
10573 if (isSuccess(status)) {
10574 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10576 // remove promise from the cache
10581 function resolveHttpPromise() {
10582 resolvePromise(response, status, headersString, statusText);
10585 if (useApplyAsync) {
10586 $rootScope.$applyAsync(resolveHttpPromise);
10588 resolveHttpPromise();
10589 if (!$rootScope.$$phase) $rootScope.$apply();
10595 * Resolves the raw $http promise.
10597 function resolvePromise(response, status, headers, statusText) {
10598 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10599 status = status >= -1 ? status : 0;
10601 (isSuccess(status) ? deferred.resolve : deferred.reject)({
10604 headers: headersGetter(headers),
10606 statusText: statusText
10610 function resolvePromiseWithResult(result) {
10611 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10614 function removePendingReq() {
10615 var idx = $http.pendingRequests.indexOf(config);
10616 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10621 function buildUrl(url, serializedParams) {
10622 if (serializedParams.length > 0) {
10623 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10632 * @name $xhrFactory
10635 * Factory function used to create XMLHttpRequest objects.
10637 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10640 * angular.module('myApp', [])
10641 * .factory('$xhrFactory', function() {
10642 * return function createXhr(method, url) {
10643 * return new window.XMLHttpRequest({mozSystem: true});
10648 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10649 * @param {string} url URL of the request.
10651 function $xhrFactoryProvider() {
10652 this.$get = function() {
10653 return function createXhr() {
10654 return new window.XMLHttpRequest();
10661 * @name $httpBackend
10662 * @requires $window
10663 * @requires $document
10664 * @requires $xhrFactory
10667 * HTTP backend used by the {@link ng.$http service} that delegates to
10668 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10670 * You should never need to use this service directly, instead use the higher-level abstractions:
10671 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10673 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10674 * $httpBackend} which can be trained with responses.
10676 function $HttpBackendProvider() {
10677 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10678 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10682 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10683 // TODO(vojta): fix the signature
10684 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10685 $browser.$$incOutstandingRequestCount();
10686 url = url || $browser.url();
10688 if (lowercase(method) == 'jsonp') {
10689 var callbackId = '_' + (callbacks.counter++).toString(36);
10690 callbacks[callbackId] = function(data) {
10691 callbacks[callbackId].data = data;
10692 callbacks[callbackId].called = true;
10695 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10696 callbackId, function(status, text) {
10697 completeRequest(callback, status, callbacks[callbackId].data, "", text);
10698 callbacks[callbackId] = noop;
10702 var xhr = createXhr(method, url);
10704 xhr.open(method, url, true);
10705 forEach(headers, function(value, key) {
10706 if (isDefined(value)) {
10707 xhr.setRequestHeader(key, value);
10711 xhr.onload = function requestLoaded() {
10712 var statusText = xhr.statusText || '';
10714 // responseText is the old-school way of retrieving response (supported by IE9)
10715 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10716 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10718 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10719 var status = xhr.status === 1223 ? 204 : xhr.status;
10721 // fix status code when it is 0 (0 status is undocumented).
10722 // Occurs when accessing file resources or on Android 4.1 stock browser
10723 // while retrieving files from application cache.
10724 if (status === 0) {
10725 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10728 completeRequest(callback,
10731 xhr.getAllResponseHeaders(),
10735 var requestError = function() {
10736 // The response is always empty
10737 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10738 completeRequest(callback, -1, null, null, '');
10741 xhr.onerror = requestError;
10742 xhr.onabort = requestError;
10744 if (withCredentials) {
10745 xhr.withCredentials = true;
10748 if (responseType) {
10750 xhr.responseType = responseType;
10752 // WebKit added support for the json responseType value on 09/03/2013
10753 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10754 // known to throw when setting the value "json" as the response type. Other older
10755 // browsers implementing the responseType
10757 // The json response type can be ignored if not supported, because JSON payloads are
10758 // parsed on the client-side regardless.
10759 if (responseType !== 'json') {
10765 xhr.send(isUndefined(post) ? null : post);
10769 var timeoutId = $browserDefer(timeoutRequest, timeout);
10770 } else if (isPromiseLike(timeout)) {
10771 timeout.then(timeoutRequest);
10775 function timeoutRequest() {
10776 jsonpDone && jsonpDone();
10777 xhr && xhr.abort();
10780 function completeRequest(callback, status, response, headersString, statusText) {
10781 // cancel timeout and subsequent timeout promise resolution
10782 if (isDefined(timeoutId)) {
10783 $browserDefer.cancel(timeoutId);
10785 jsonpDone = xhr = null;
10787 callback(status, response, headersString, statusText);
10788 $browser.$$completeOutstandingRequest(noop);
10792 function jsonpReq(url, callbackId, done) {
10793 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10794 // - fetches local scripts via XHR and evals them
10795 // - adds and immediately removes script elements from the document
10796 var script = rawDocument.createElement('script'), callback = null;
10797 script.type = "text/javascript";
10799 script.async = true;
10801 callback = function(event) {
10802 removeEventListenerFn(script, "load", callback);
10803 removeEventListenerFn(script, "error", callback);
10804 rawDocument.body.removeChild(script);
10807 var text = "unknown";
10810 if (event.type === "load" && !callbacks[callbackId].called) {
10811 event = { type: "error" };
10814 status = event.type === "error" ? 404 : 200;
10818 done(status, text);
10822 addEventListenerFn(script, "load", callback);
10823 addEventListenerFn(script, "error", callback);
10824 rawDocument.body.appendChild(script);
10829 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10830 $interpolateMinErr.throwNoconcat = function(text) {
10831 throw $interpolateMinErr('noconcat',
10832 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10833 "interpolations that concatenate multiple expressions when a trusted value is " +
10834 "required. See http://docs.angularjs.org/api/ng.$sce", text);
10837 $interpolateMinErr.interr = function(text, err) {
10838 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10843 * @name $interpolateProvider
10847 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10850 <example module="customInterpolationApp">
10851 <file name="index.html">
10853 var customInterpolationApp = angular.module('customInterpolationApp', []);
10855 customInterpolationApp.config(function($interpolateProvider) {
10856 $interpolateProvider.startSymbol('//');
10857 $interpolateProvider.endSymbol('//');
10861 customInterpolationApp.controller('DemoController', function() {
10862 this.label = "This binding is brought you by // interpolation symbols.";
10865 <div ng-app="App" ng-controller="DemoController as demo">
10869 <file name="protractor.js" type="protractor">
10870 it('should interpolate binding with custom symbols', function() {
10871 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10876 function $InterpolateProvider() {
10877 var startSymbol = '{{';
10878 var endSymbol = '}}';
10882 * @name $interpolateProvider#startSymbol
10884 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10886 * @param {string=} value new value to set the starting symbol to.
10887 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10889 this.startSymbol = function(value) {
10891 startSymbol = value;
10894 return startSymbol;
10900 * @name $interpolateProvider#endSymbol
10902 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10904 * @param {string=} value new value to set the ending symbol to.
10905 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10907 this.endSymbol = function(value) {
10917 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10918 var startSymbolLength = startSymbol.length,
10919 endSymbolLength = endSymbol.length,
10920 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10921 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10923 function escape(ch) {
10924 return '\\\\\\' + ch;
10927 function unescapeText(text) {
10928 return text.replace(escapedStartRegexp, startSymbol).
10929 replace(escapedEndRegexp, endSymbol);
10932 function stringify(value) {
10933 if (value == null) { // null || undefined
10936 switch (typeof value) {
10940 value = '' + value;
10943 value = toJson(value);
10951 * @name $interpolate
10959 * Compiles a string with markup into an interpolation function. This service is used by the
10960 * HTML {@link ng.$compile $compile} service for data binding. See
10961 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
10962 * interpolation markup.
10966 * var $interpolate = ...; // injected
10967 * var exp = $interpolate('Hello {{name | uppercase}}!');
10968 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
10971 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
10972 * `true`, the interpolation function will return `undefined` unless all embedded expressions
10973 * evaluate to a value other than `undefined`.
10976 * var $interpolate = ...; // injected
10977 * var context = {greeting: 'Hello', name: undefined };
10979 * // default "forgiving" mode
10980 * var exp = $interpolate('{{greeting}} {{name}}!');
10981 * expect(exp(context)).toEqual('Hello !');
10983 * // "allOrNothing" mode
10984 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
10985 * expect(exp(context)).toBeUndefined();
10986 * context.name = 'Angular';
10987 * expect(exp(context)).toEqual('Hello Angular!');
10990 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
10992 * ####Escaped Interpolation
10993 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
10994 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
10995 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
10998 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
10999 * degree, while also enabling code examples to work without relying on the
11000 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11002 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11003 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
11004 * interpolation start/end markers with their escaped counterparts.**
11006 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11007 * output when the $interpolate service processes the text. So, for HTML elements interpolated
11008 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11009 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11010 * this is typically useful only when user-data is used in rendering a template from the server, or
11011 * when otherwise untrusted data is used by a directive.
11014 * <file name="index.html">
11015 * <div ng-init="username='A user'">
11016 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11018 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
11019 * application, but fails to accomplish their task, because the server has correctly
11020 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11022 * <p>Instead, the result of the attempted script injection is visible, and can be removed
11023 * from the database by an administrator.</p>
11028 * @param {string} text The text with markup to interpolate.
11029 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11030 * embedded expression in order to return an interpolation function. Strings with no
11031 * embedded expression will return null for the interpolation function.
11032 * @param {string=} trustedContext when provided, the returned function passes the interpolated
11033 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11034 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
11035 * provides Strict Contextual Escaping for details.
11036 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11037 * unless all embedded expressions evaluate to a value other than `undefined`.
11038 * @returns {function(context)} an interpolation function which is used to compute the
11039 * interpolated string. The function has these parameters:
11041 * - `context`: evaluation context for all expressions embedded in the interpolated text
11043 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11044 allOrNothing = !!allOrNothing;
11050 textLength = text.length,
11053 expressionPositions = [];
11055 while (index < textLength) {
11056 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11057 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11058 if (index !== startIndex) {
11059 concat.push(unescapeText(text.substring(index, startIndex)));
11061 exp = text.substring(startIndex + startSymbolLength, endIndex);
11062 expressions.push(exp);
11063 parseFns.push($parse(exp, parseStringifyInterceptor));
11064 index = endIndex + endSymbolLength;
11065 expressionPositions.push(concat.length);
11068 // we did not find an interpolation, so we have to add the remainder to the separators array
11069 if (index !== textLength) {
11070 concat.push(unescapeText(text.substring(index)));
11076 // Concatenating expressions makes it hard to reason about whether some combination of
11077 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
11078 // single expression be used for iframe[src], object[src], etc., we ensure that the value
11079 // that's used is assigned or constructed by some JS code somewhere that is more testable or
11080 // make it obvious that you bound the value to some user controlled value. This helps reduce
11081 // the load when auditing for XSS issues.
11082 if (trustedContext && concat.length > 1) {
11083 $interpolateMinErr.throwNoconcat(text);
11086 if (!mustHaveExpression || expressions.length) {
11087 var compute = function(values) {
11088 for (var i = 0, ii = expressions.length; i < ii; i++) {
11089 if (allOrNothing && isUndefined(values[i])) return;
11090 concat[expressionPositions[i]] = values[i];
11092 return concat.join('');
11095 var getValue = function(value) {
11096 return trustedContext ?
11097 $sce.getTrusted(trustedContext, value) :
11098 $sce.valueOf(value);
11101 return extend(function interpolationFn(context) {
11103 var ii = expressions.length;
11104 var values = new Array(ii);
11107 for (; i < ii; i++) {
11108 values[i] = parseFns[i](context);
11111 return compute(values);
11113 $exceptionHandler($interpolateMinErr.interr(text, err));
11117 // all of these properties are undocumented for now
11118 exp: text, //just for compatibility with regular watchers created via $watch
11119 expressions: expressions,
11120 $$watchDelegate: function(scope, listener) {
11122 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11123 var currValue = compute(values);
11124 if (isFunction(listener)) {
11125 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11127 lastValue = currValue;
11133 function parseStringifyInterceptor(value) {
11135 value = getValue(value);
11136 return allOrNothing && !isDefined(value) ? value : stringify(value);
11138 $exceptionHandler($interpolateMinErr.interr(text, err));
11146 * @name $interpolate#startSymbol
11148 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11150 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11153 * @returns {string} start symbol.
11155 $interpolate.startSymbol = function() {
11156 return startSymbol;
11162 * @name $interpolate#endSymbol
11164 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11166 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11169 * @returns {string} end symbol.
11171 $interpolate.endSymbol = function() {
11175 return $interpolate;
11179 function $IntervalProvider() {
11180 this.$get = ['$rootScope', '$window', '$q', '$$q',
11181 function($rootScope, $window, $q, $$q) {
11182 var intervals = {};
11190 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11193 * The return value of registering an interval function is a promise. This promise will be
11194 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11195 * run indefinitely if `count` is not defined. The value of the notification will be the
11196 * number of iterations that have run.
11197 * To cancel an interval, call `$interval.cancel(promise)`.
11199 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11200 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11203 * <div class="alert alert-warning">
11204 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11205 * with them. In particular they are not automatically destroyed when a controller's scope or a
11206 * directive's element are destroyed.
11207 * You should take this into consideration and make sure to always cancel the interval at the
11208 * appropriate moment. See the example below for more details on how and when to do this.
11211 * @param {function()} fn A function that should be called repeatedly.
11212 * @param {number} delay Number of milliseconds between each function call.
11213 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11215 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11216 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11217 * @param {...*=} Pass additional parameters to the executed function.
11218 * @returns {promise} A promise which will be notified on each iteration.
11221 * <example module="intervalExample">
11222 * <file name="index.html">
11224 * angular.module('intervalExample', [])
11225 * .controller('ExampleController', ['$scope', '$interval',
11226 * function($scope, $interval) {
11227 * $scope.format = 'M/d/yy h:mm:ss a';
11228 * $scope.blood_1 = 100;
11229 * $scope.blood_2 = 120;
11232 * $scope.fight = function() {
11233 * // Don't start a new fight if we are already fighting
11234 * if ( angular.isDefined(stop) ) return;
11236 * stop = $interval(function() {
11237 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11238 * $scope.blood_1 = $scope.blood_1 - 3;
11239 * $scope.blood_2 = $scope.blood_2 - 4;
11241 * $scope.stopFight();
11246 * $scope.stopFight = function() {
11247 * if (angular.isDefined(stop)) {
11248 * $interval.cancel(stop);
11249 * stop = undefined;
11253 * $scope.resetFight = function() {
11254 * $scope.blood_1 = 100;
11255 * $scope.blood_2 = 120;
11258 * $scope.$on('$destroy', function() {
11259 * // Make sure that the interval is destroyed too
11260 * $scope.stopFight();
11263 * // Register the 'myCurrentTime' directive factory method.
11264 * // We inject $interval and dateFilter service since the factory method is DI.
11265 * .directive('myCurrentTime', ['$interval', 'dateFilter',
11266 * function($interval, dateFilter) {
11267 * // return the directive link function. (compile function not needed)
11268 * return function(scope, element, attrs) {
11269 * var format, // date format
11270 * stopTime; // so that we can cancel the time updates
11272 * // used to update the UI
11273 * function updateTime() {
11274 * element.text(dateFilter(new Date(), format));
11277 * // watch the expression, and update the UI on change.
11278 * scope.$watch(attrs.myCurrentTime, function(value) {
11283 * stopTime = $interval(updateTime, 1000);
11285 * // listen on DOM destroy (removal) event, and cancel the next UI update
11286 * // to prevent updating time after the DOM element was removed.
11287 * element.on('$destroy', function() {
11288 * $interval.cancel(stopTime);
11295 * <div ng-controller="ExampleController">
11296 * <label>Date format: <input ng-model="format"></label> <hr/>
11297 * Current time is: <span my-current-time="format"></span>
11299 * Blood 1 : <font color='red'>{{blood_1}}</font>
11300 * Blood 2 : <font color='red'>{{blood_2}}</font>
11301 * <button type="button" data-ng-click="fight()">Fight</button>
11302 * <button type="button" data-ng-click="stopFight()">StopFight</button>
11303 * <button type="button" data-ng-click="resetFight()">resetFight</button>
11310 function interval(fn, delay, count, invokeApply) {
11311 var hasParams = arguments.length > 4,
11312 args = hasParams ? sliceArgs(arguments, 4) : [],
11313 setInterval = $window.setInterval,
11314 clearInterval = $window.clearInterval,
11316 skipApply = (isDefined(invokeApply) && !invokeApply),
11317 deferred = (skipApply ? $$q : $q).defer(),
11318 promise = deferred.promise;
11320 count = isDefined(count) ? count : 0;
11322 promise.then(null, null, (!hasParams) ? fn : function() {
11323 fn.apply(null, args);
11326 promise.$$intervalId = setInterval(function tick() {
11327 deferred.notify(iteration++);
11329 if (count > 0 && iteration >= count) {
11330 deferred.resolve(iteration);
11331 clearInterval(promise.$$intervalId);
11332 delete intervals[promise.$$intervalId];
11335 if (!skipApply) $rootScope.$apply();
11339 intervals[promise.$$intervalId] = deferred;
11347 * @name $interval#cancel
11350 * Cancels a task associated with the `promise`.
11352 * @param {Promise=} promise returned by the `$interval` function.
11353 * @returns {boolean} Returns `true` if the task was successfully canceled.
11355 interval.cancel = function(promise) {
11356 if (promise && promise.$$intervalId in intervals) {
11357 intervals[promise.$$intervalId].reject('canceled');
11358 $window.clearInterval(promise.$$intervalId);
11359 delete intervals[promise.$$intervalId];
11374 * $locale service provides localization rules for various Angular components. As of right now the
11375 * only public api is:
11377 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11380 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11381 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11382 var $locationMinErr = minErr('$location');
11386 * Encode path using encodeUriSegment, ignoring forward slashes
11388 * @param {string} path Path to encode
11389 * @returns {string}
11391 function encodePath(path) {
11392 var segments = path.split('/'),
11393 i = segments.length;
11396 segments[i] = encodeUriSegment(segments[i]);
11399 return segments.join('/');
11402 function parseAbsoluteUrl(absoluteUrl, locationObj) {
11403 var parsedUrl = urlResolve(absoluteUrl);
11405 locationObj.$$protocol = parsedUrl.protocol;
11406 locationObj.$$host = parsedUrl.hostname;
11407 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11411 function parseAppUrl(relativeUrl, locationObj) {
11412 var prefixed = (relativeUrl.charAt(0) !== '/');
11414 relativeUrl = '/' + relativeUrl;
11416 var match = urlResolve(relativeUrl);
11417 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11418 match.pathname.substring(1) : match.pathname);
11419 locationObj.$$search = parseKeyValue(match.search);
11420 locationObj.$$hash = decodeURIComponent(match.hash);
11422 // make sure path starts with '/';
11423 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11424 locationObj.$$path = '/' + locationObj.$$path;
11431 * @param {string} begin
11432 * @param {string} whole
11433 * @returns {string} returns text from whole after begin or undefined if it does not begin with
11436 function beginsWith(begin, whole) {
11437 if (whole.indexOf(begin) === 0) {
11438 return whole.substr(begin.length);
11443 function stripHash(url) {
11444 var index = url.indexOf('#');
11445 return index == -1 ? url : url.substr(0, index);
11448 function trimEmptyHash(url) {
11449 return url.replace(/(#.+)|#$/, '$1');
11453 function stripFile(url) {
11454 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11457 /* return the server only (scheme://host:port) */
11458 function serverBase(url) {
11459 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11464 * LocationHtml5Url represents an url
11465 * This object is exposed as $location service when HTML5 mode is enabled and supported
11468 * @param {string} appBase application base URL
11469 * @param {string} appBaseNoFile application base URL stripped of any filename
11470 * @param {string} basePrefix url path prefix
11472 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11473 this.$$html5 = true;
11474 basePrefix = basePrefix || '';
11475 parseAbsoluteUrl(appBase, this);
11479 * Parse given html5 (regular) url string into properties
11480 * @param {string} url HTML5 url
11483 this.$$parse = function(url) {
11484 var pathUrl = beginsWith(appBaseNoFile, url);
11485 if (!isString(pathUrl)) {
11486 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11490 parseAppUrl(pathUrl, this);
11492 if (!this.$$path) {
11500 * Compose url and update `absUrl` property
11503 this.$$compose = function() {
11504 var search = toKeyValue(this.$$search),
11505 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11507 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11508 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11511 this.$$parseLinkUrl = function(url, relHref) {
11512 if (relHref && relHref[0] === '#') {
11513 // special case for links to hash fragments:
11514 // keep the old url and only replace the hash fragment
11515 this.hash(relHref.slice(1));
11518 var appUrl, prevAppUrl;
11521 if (isDefined(appUrl = beginsWith(appBase, url))) {
11522 prevAppUrl = appUrl;
11523 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11524 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11526 rewrittenUrl = appBase + prevAppUrl;
11528 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11529 rewrittenUrl = appBaseNoFile + appUrl;
11530 } else if (appBaseNoFile == url + '/') {
11531 rewrittenUrl = appBaseNoFile;
11533 if (rewrittenUrl) {
11534 this.$$parse(rewrittenUrl);
11536 return !!rewrittenUrl;
11542 * LocationHashbangUrl represents url
11543 * This object is exposed as $location service when developer doesn't opt into html5 mode.
11544 * It also serves as the base class for html5 mode fallback on legacy browsers.
11547 * @param {string} appBase application base URL
11548 * @param {string} appBaseNoFile application base URL stripped of any filename
11549 * @param {string} hashPrefix hashbang prefix
11551 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11553 parseAbsoluteUrl(appBase, this);
11557 * Parse given hashbang url into properties
11558 * @param {string} url Hashbang url
11561 this.$$parse = function(url) {
11562 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11563 var withoutHashUrl;
11565 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11567 // The rest of the url starts with a hash so we have
11568 // got either a hashbang path or a plain hash fragment
11569 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11570 if (isUndefined(withoutHashUrl)) {
11571 // There was no hashbang prefix so we just have a hash fragment
11572 withoutHashUrl = withoutBaseUrl;
11576 // There was no hashbang path nor hash fragment:
11577 // If we are in HTML5 mode we use what is left as the path;
11578 // Otherwise we ignore what is left
11579 if (this.$$html5) {
11580 withoutHashUrl = withoutBaseUrl;
11582 withoutHashUrl = '';
11583 if (isUndefined(withoutBaseUrl)) {
11590 parseAppUrl(withoutHashUrl, this);
11592 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11597 * In Windows, on an anchor node on documents loaded from
11598 * the filesystem, the browser will return a pathname
11599 * prefixed with the drive name ('/C:/path') when a
11600 * pathname without a drive is set:
11601 * * a.setAttribute('href', '/foo')
11602 * * a.pathname === '/C:/foo' //true
11604 * Inside of Angular, we're always using pathnames that
11605 * do not include drive names for routing.
11607 function removeWindowsDriveName(path, url, base) {
11609 Matches paths for file protocol on windows,
11610 such as /C:/foo/bar, and captures only /foo/bar.
11612 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11614 var firstPathSegmentMatch;
11616 //Get the relative path from the input URL.
11617 if (url.indexOf(base) === 0) {
11618 url = url.replace(base, '');
11621 // The input URL intentionally contains a first path segment that ends with a colon.
11622 if (windowsFilePathExp.exec(url)) {
11626 firstPathSegmentMatch = windowsFilePathExp.exec(path);
11627 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11632 * Compose hashbang url and update `absUrl` property
11635 this.$$compose = function() {
11636 var search = toKeyValue(this.$$search),
11637 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11639 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11640 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11643 this.$$parseLinkUrl = function(url, relHref) {
11644 if (stripHash(appBase) == stripHash(url)) {
11654 * LocationHashbangUrl represents url
11655 * This object is exposed as $location service when html5 history api is enabled but the browser
11656 * does not support it.
11659 * @param {string} appBase application base URL
11660 * @param {string} appBaseNoFile application base URL stripped of any filename
11661 * @param {string} hashPrefix hashbang prefix
11663 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11664 this.$$html5 = true;
11665 LocationHashbangUrl.apply(this, arguments);
11667 this.$$parseLinkUrl = function(url, relHref) {
11668 if (relHref && relHref[0] === '#') {
11669 // special case for links to hash fragments:
11670 // keep the old url and only replace the hash fragment
11671 this.hash(relHref.slice(1));
11678 if (appBase == stripHash(url)) {
11679 rewrittenUrl = url;
11680 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11681 rewrittenUrl = appBase + hashPrefix + appUrl;
11682 } else if (appBaseNoFile === url + '/') {
11683 rewrittenUrl = appBaseNoFile;
11685 if (rewrittenUrl) {
11686 this.$$parse(rewrittenUrl);
11688 return !!rewrittenUrl;
11691 this.$$compose = function() {
11692 var search = toKeyValue(this.$$search),
11693 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11695 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11696 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11697 this.$$absUrl = appBase + hashPrefix + this.$$url;
11703 var locationPrototype = {
11706 * Are we in html5 mode?
11712 * Has any change been replacing?
11719 * @name $location#absUrl
11722 * This method is getter only.
11724 * Return full url representation with all segments encoded according to rules specified in
11725 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11729 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11730 * var absUrl = $location.absUrl();
11731 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11734 * @return {string} full url
11736 absUrl: locationGetter('$$absUrl'),
11740 * @name $location#url
11743 * This method is getter / setter.
11745 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11747 * Change path, search and hash, when called with parameter and return `$location`.
11751 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11752 * var url = $location.url();
11753 * // => "/some/path?foo=bar&baz=xoxo"
11756 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11757 * @return {string} url
11759 url: function(url) {
11760 if (isUndefined(url)) {
11764 var match = PATH_MATCH.exec(url);
11765 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11766 if (match[2] || match[1] || url === '') this.search(match[3] || '');
11767 this.hash(match[5] || '');
11774 * @name $location#protocol
11777 * This method is getter only.
11779 * Return protocol of current url.
11783 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11784 * var protocol = $location.protocol();
11788 * @return {string} protocol of current url
11790 protocol: locationGetter('$$protocol'),
11794 * @name $location#host
11797 * This method is getter only.
11799 * Return host of current url.
11801 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11805 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11806 * var host = $location.host();
11807 * // => "example.com"
11809 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
11810 * host = $location.host();
11811 * // => "example.com"
11812 * host = location.host;
11813 * // => "example.com:8080"
11816 * @return {string} host of current url.
11818 host: locationGetter('$$host'),
11822 * @name $location#port
11825 * This method is getter only.
11827 * Return port of current url.
11831 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11832 * var port = $location.port();
11836 * @return {Number} port
11838 port: locationGetter('$$port'),
11842 * @name $location#path
11845 * This method is getter / setter.
11847 * Return path of current url when called without any parameter.
11849 * Change path when called with parameter and return `$location`.
11851 * Note: Path should always begin with forward slash (/), this method will add the forward slash
11852 * if it is missing.
11856 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11857 * var path = $location.path();
11858 * // => "/some/path"
11861 * @param {(string|number)=} path New path
11862 * @return {string} path
11864 path: locationGetterSetter('$$path', function(path) {
11865 path = path !== null ? path.toString() : '';
11866 return path.charAt(0) == '/' ? path : '/' + path;
11871 * @name $location#search
11874 * This method is getter / setter.
11876 * Return search part (as object) of current url when called without any parameter.
11878 * Change search part when called with parameter and return `$location`.
11882 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11883 * var searchObject = $location.search();
11884 * // => {foo: 'bar', baz: 'xoxo'}
11886 * // set foo to 'yipee'
11887 * $location.search('foo', 'yipee');
11888 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11891 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11894 * When called with a single argument the method acts as a setter, setting the `search` component
11895 * of `$location` to the specified value.
11897 * If the argument is a hash object containing an array of values, these values will be encoded
11898 * as duplicate search parameters in the url.
11900 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11901 * will override only a single search property.
11903 * If `paramValue` is an array, it will override the property of the `search` component of
11904 * `$location` specified via the first argument.
11906 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11908 * If `paramValue` is `true`, the property specified via the first argument will be added with no
11909 * value nor trailing equal sign.
11911 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11912 * one or more arguments returns `$location` object itself.
11914 search: function(search, paramValue) {
11915 switch (arguments.length) {
11917 return this.$$search;
11919 if (isString(search) || isNumber(search)) {
11920 search = search.toString();
11921 this.$$search = parseKeyValue(search);
11922 } else if (isObject(search)) {
11923 search = copy(search, {});
11924 // remove object undefined or null properties
11925 forEach(search, function(value, key) {
11926 if (value == null) delete search[key];
11929 this.$$search = search;
11931 throw $locationMinErr('isrcharg',
11932 'The first argument of the `$location#search()` call must be a string or an object.');
11936 if (isUndefined(paramValue) || paramValue === null) {
11937 delete this.$$search[search];
11939 this.$$search[search] = paramValue;
11949 * @name $location#hash
11952 * This method is getter / setter.
11954 * Returns the hash fragment when called without any parameters.
11956 * Changes the hash fragment when called with a parameter and returns `$location`.
11960 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
11961 * var hash = $location.hash();
11962 * // => "hashValue"
11965 * @param {(string|number)=} hash New hash fragment
11966 * @return {string} hash
11968 hash: locationGetterSetter('$$hash', function(hash) {
11969 return hash !== null ? hash.toString() : '';
11974 * @name $location#replace
11977 * If called, all changes to $location during the current `$digest` will replace the current history
11978 * record, instead of adding a new one.
11980 replace: function() {
11981 this.$$replace = true;
11986 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
11987 Location.prototype = Object.create(locationPrototype);
11991 * @name $location#state
11994 * This method is getter / setter.
11996 * Return the history state object when called without any parameter.
11998 * Change the history state object when called with one parameter and return `$location`.
11999 * The state object is later passed to `pushState` or `replaceState`.
12001 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12002 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12003 * older browsers (like IE9 or Android < 4.0), don't use this method.
12005 * @param {object=} state State object for pushState or replaceState
12006 * @return {object} state
12008 Location.prototype.state = function(state) {
12009 if (!arguments.length) {
12010 return this.$$state;
12013 if (Location !== LocationHtml5Url || !this.$$html5) {
12014 throw $locationMinErr('nostate', 'History API state support is available only ' +
12015 'in HTML5 mode and only in browsers supporting HTML5 History API');
12017 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12018 // but we're changing the $$state reference to $browser.state() during the $digest
12019 // so the modification window is narrow.
12020 this.$$state = isUndefined(state) ? null : state;
12027 function locationGetter(property) {
12028 return function() {
12029 return this[property];
12034 function locationGetterSetter(property, preprocess) {
12035 return function(value) {
12036 if (isUndefined(value)) {
12037 return this[property];
12040 this[property] = preprocess(value);
12052 * @requires $rootElement
12055 * The $location service parses the URL in the browser address bar (based on the
12056 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12057 * available to your application. Changes to the URL in the address bar are reflected into
12058 * $location service and changes to $location are reflected into the browser address bar.
12060 * **The $location service:**
12062 * - Exposes the current URL in the browser address bar, so you can
12063 * - Watch and observe the URL.
12064 * - Change the URL.
12065 * - Synchronizes the URL with the browser when the user
12066 * - Changes the address bar.
12067 * - Clicks the back or forward button (or clicks a History link).
12068 * - Clicks on a link.
12069 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12071 * For more information see {@link guide/$location Developer Guide: Using $location}
12076 * @name $locationProvider
12078 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12080 function $LocationProvider() {
12081 var hashPrefix = '',
12090 * @name $locationProvider#hashPrefix
12092 * @param {string=} prefix Prefix for hash part (containing path and search)
12093 * @returns {*} current value if used as getter or itself (chaining) if used as setter
12095 this.hashPrefix = function(prefix) {
12096 if (isDefined(prefix)) {
12097 hashPrefix = prefix;
12106 * @name $locationProvider#html5Mode
12108 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12109 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12111 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12112 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12113 * support `pushState`.
12114 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12115 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12116 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
12117 * See the {@link guide/$location $location guide for more information}
12118 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12119 * enables/disables url rewriting for relative links.
12121 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12123 this.html5Mode = function(mode) {
12124 if (isBoolean(mode)) {
12125 html5Mode.enabled = mode;
12127 } else if (isObject(mode)) {
12129 if (isBoolean(mode.enabled)) {
12130 html5Mode.enabled = mode.enabled;
12133 if (isBoolean(mode.requireBase)) {
12134 html5Mode.requireBase = mode.requireBase;
12137 if (isBoolean(mode.rewriteLinks)) {
12138 html5Mode.rewriteLinks = mode.rewriteLinks;
12149 * @name $location#$locationChangeStart
12150 * @eventType broadcast on root scope
12152 * Broadcasted before a URL will change.
12154 * This change can be prevented by calling
12155 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12156 * details about event object. Upon successful change
12157 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12159 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12160 * the browser supports the HTML5 History API.
12162 * @param {Object} angularEvent Synthetic event object.
12163 * @param {string} newUrl New URL
12164 * @param {string=} oldUrl URL that was before it was changed.
12165 * @param {string=} newState New history state object
12166 * @param {string=} oldState History state object that was before it was changed.
12171 * @name $location#$locationChangeSuccess
12172 * @eventType broadcast on root scope
12174 * Broadcasted after a URL was changed.
12176 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12177 * the browser supports the HTML5 History API.
12179 * @param {Object} angularEvent Synthetic event object.
12180 * @param {string} newUrl New URL
12181 * @param {string=} oldUrl URL that was before it was changed.
12182 * @param {string=} newState New history state object
12183 * @param {string=} oldState History state object that was before it was changed.
12186 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12187 function($rootScope, $browser, $sniffer, $rootElement, $window) {
12190 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12191 initialUrl = $browser.url(),
12194 if (html5Mode.enabled) {
12195 if (!baseHref && html5Mode.requireBase) {
12196 throw $locationMinErr('nobase',
12197 "$location in HTML5 mode requires a <base> tag to be present!");
12199 appBase = serverBase(initialUrl) + (baseHref || '/');
12200 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12202 appBase = stripHash(initialUrl);
12203 LocationMode = LocationHashbangUrl;
12205 var appBaseNoFile = stripFile(appBase);
12207 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12208 $location.$$parseLinkUrl(initialUrl, initialUrl);
12210 $location.$$state = $browser.state();
12212 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12214 function setBrowserUrlWithFallback(url, replace, state) {
12215 var oldUrl = $location.url();
12216 var oldState = $location.$$state;
12218 $browser.url(url, replace, state);
12220 // Make sure $location.state() returns referentially identical (not just deeply equal)
12221 // state object; this makes possible quick checking if the state changed in the digest
12222 // loop. Checking deep equality would be too expensive.
12223 $location.$$state = $browser.state();
12225 // Restore old values if pushState fails
12226 $location.url(oldUrl);
12227 $location.$$state = oldState;
12233 $rootElement.on('click', function(event) {
12234 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12235 // currently we open nice url link and redirect then
12237 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12239 var elm = jqLite(event.target);
12241 // traverse the DOM up to find first A tag
12242 while (nodeName_(elm[0]) !== 'a') {
12243 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12244 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12247 var absHref = elm.prop('href');
12248 // get the actual href attribute - see
12249 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12250 var relHref = elm.attr('href') || elm.attr('xlink:href');
12252 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12253 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12255 absHref = urlResolve(absHref.animVal).href;
12258 // Ignore when url is started with javascript: or mailto:
12259 if (IGNORE_URI_REGEXP.test(absHref)) return;
12261 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12262 if ($location.$$parseLinkUrl(absHref, relHref)) {
12263 // We do a preventDefault for all urls that are part of the angular application,
12264 // in html5mode and also without, so that we are able to abort navigation without
12265 // getting double entries in the location history.
12266 event.preventDefault();
12267 // update location manually
12268 if ($location.absUrl() != $browser.url()) {
12269 $rootScope.$apply();
12270 // hack to work around FF6 bug 684208 when scenario runner clicks on links
12271 $window.angular['ff-684208-preventDefault'] = true;
12278 // rewrite hashbang url <> html5 url
12279 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12280 $browser.url($location.absUrl(), true);
12283 var initializing = true;
12285 // update $location when $browser url changes
12286 $browser.onUrlChange(function(newUrl, newState) {
12288 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12289 // If we are navigating outside of the app then force a reload
12290 $window.location.href = newUrl;
12294 $rootScope.$evalAsync(function() {
12295 var oldUrl = $location.absUrl();
12296 var oldState = $location.$$state;
12297 var defaultPrevented;
12298 newUrl = trimEmptyHash(newUrl);
12299 $location.$$parse(newUrl);
12300 $location.$$state = newState;
12302 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12303 newState, oldState).defaultPrevented;
12305 // if the location was changed by a `$locationChangeStart` handler then stop
12306 // processing this location change
12307 if ($location.absUrl() !== newUrl) return;
12309 if (defaultPrevented) {
12310 $location.$$parse(oldUrl);
12311 $location.$$state = oldState;
12312 setBrowserUrlWithFallback(oldUrl, false, oldState);
12314 initializing = false;
12315 afterLocationChange(oldUrl, oldState);
12318 if (!$rootScope.$$phase) $rootScope.$digest();
12322 $rootScope.$watch(function $locationWatch() {
12323 var oldUrl = trimEmptyHash($browser.url());
12324 var newUrl = trimEmptyHash($location.absUrl());
12325 var oldState = $browser.state();
12326 var currentReplace = $location.$$replace;
12327 var urlOrStateChanged = oldUrl !== newUrl ||
12328 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12330 if (initializing || urlOrStateChanged) {
12331 initializing = false;
12333 $rootScope.$evalAsync(function() {
12334 var newUrl = $location.absUrl();
12335 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12336 $location.$$state, oldState).defaultPrevented;
12338 // if the location was changed by a `$locationChangeStart` handler then stop
12339 // processing this location change
12340 if ($location.absUrl() !== newUrl) return;
12342 if (defaultPrevented) {
12343 $location.$$parse(oldUrl);
12344 $location.$$state = oldState;
12346 if (urlOrStateChanged) {
12347 setBrowserUrlWithFallback(newUrl, currentReplace,
12348 oldState === $location.$$state ? null : $location.$$state);
12350 afterLocationChange(oldUrl, oldState);
12355 $location.$$replace = false;
12357 // we don't need to return anything because $evalAsync will make the digest loop dirty when
12358 // there is a change
12363 function afterLocationChange(oldUrl, oldState) {
12364 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12365 $location.$$state, oldState);
12373 * @requires $window
12376 * Simple service for logging. Default implementation safely writes the message
12377 * into the browser's console (if present).
12379 * The main purpose of this service is to simplify debugging and troubleshooting.
12381 * The default is to log `debug` messages. You can use
12382 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12385 <example module="logExample">
12386 <file name="script.js">
12387 angular.module('logExample', [])
12388 .controller('LogController', ['$scope', '$log', function($scope, $log) {
12389 $scope.$log = $log;
12390 $scope.message = 'Hello World!';
12393 <file name="index.html">
12394 <div ng-controller="LogController">
12395 <p>Reload this page with open console, enter text and hit the log button...</p>
12397 <input type="text" ng-model="message" /></label>
12398 <button ng-click="$log.log(message)">log</button>
12399 <button ng-click="$log.warn(message)">warn</button>
12400 <button ng-click="$log.info(message)">info</button>
12401 <button ng-click="$log.error(message)">error</button>
12402 <button ng-click="$log.debug(message)">debug</button>
12410 * @name $logProvider
12412 * Use the `$logProvider` to configure how the application logs messages
12414 function $LogProvider() {
12420 * @name $logProvider#debugEnabled
12422 * @param {boolean=} flag enable or disable debug level messages
12423 * @returns {*} current value if used as getter or itself (chaining) if used as setter
12425 this.debugEnabled = function(flag) {
12426 if (isDefined(flag)) {
12434 this.$get = ['$window', function($window) {
12441 * Write a log message
12443 log: consoleLog('log'),
12450 * Write an information message
12452 info: consoleLog('info'),
12459 * Write a warning message
12461 warn: consoleLog('warn'),
12468 * Write an error message
12470 error: consoleLog('error'),
12477 * Write a debug message
12479 debug: (function() {
12480 var fn = consoleLog('debug');
12482 return function() {
12484 fn.apply(self, arguments);
12490 function formatError(arg) {
12491 if (arg instanceof Error) {
12493 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12494 ? 'Error: ' + arg.message + '\n' + arg.stack
12496 } else if (arg.sourceURL) {
12497 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12503 function consoleLog(type) {
12504 var console = $window.console || {},
12505 logFn = console[type] || console.log || noop,
12508 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12509 // The reason behind this is that console.log has type "object" in IE8...
12511 hasApply = !!logFn.apply;
12515 return function() {
12517 forEach(arguments, function(arg) {
12518 args.push(formatError(arg));
12520 return logFn.apply(console, args);
12524 // we are IE which either doesn't have window.console => this is noop and we do nothing,
12525 // or we are IE where console.log doesn't have apply so we log at least first 2 args
12526 return function(arg1, arg2) {
12527 logFn(arg1, arg2 == null ? '' : arg2);
12533 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12534 * Any commits to this file should be reviewed with security in mind. *
12535 * Changes to this file can potentially create security vulnerabilities. *
12536 * An approval from 2 Core members with history of modifying *
12537 * this file is required. *
12539 * Does the change somehow allow for arbitrary javascript to be executed? *
12540 * Or allows for someone to change the prototype of built-in objects? *
12541 * Or gives undesired access to variables likes document or window? *
12542 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12544 var $parseMinErr = minErr('$parse');
12546 // Sandboxing Angular Expressions
12547 // ------------------------------
12548 // Angular expressions are generally considered safe because these expressions only have direct
12549 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12550 // obtaining a reference to native JS functions such as the Function constructor.
12552 // As an example, consider the following Angular expression:
12554 // {}.toString.constructor('alert("evil JS code")')
12556 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12557 // against the expression language, but not to prevent exploits that were enabled by exposing
12558 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12559 // practice and therefore we are not even trying to protect against interaction with an object
12560 // explicitly exposed in this way.
12562 // In general, it is not possible to access a Window object from an angular expression unless a
12563 // window or some DOM object that has a reference to window is published onto a Scope.
12564 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12567 // See https://docs.angularjs.org/guide/security
12570 function ensureSafeMemberName(name, fullExpression) {
12571 if (name === "__defineGetter__" || name === "__defineSetter__"
12572 || name === "__lookupGetter__" || name === "__lookupSetter__"
12573 || name === "__proto__") {
12574 throw $parseMinErr('isecfld',
12575 'Attempting to access a disallowed field in Angular expressions! '
12576 + 'Expression: {0}', fullExpression);
12581 function getStringValue(name, fullExpression) {
12582 // From the JavaScript docs:
12583 // Property names must be strings. This means that non-string objects cannot be used
12584 // as keys in an object. Any non-string object, including a number, is typecasted
12585 // into a string via the toString method.
12587 // So, to ensure that we are checking the same `name` that JavaScript would use,
12588 // we cast it to a string, if possible.
12589 // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12590 // this is, this will handle objects that misbehave.
12592 if (!isString(name)) {
12593 throw $parseMinErr('iseccst',
12594 'Cannot convert object to primitive value! '
12595 + 'Expression: {0}', fullExpression);
12600 function ensureSafeObject(obj, fullExpression) {
12601 // nifty check if obj is Function that is fast and works across iframes and other contexts
12603 if (obj.constructor === obj) {
12604 throw $parseMinErr('isecfn',
12605 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12607 } else if (// isWindow(obj)
12608 obj.window === obj) {
12609 throw $parseMinErr('isecwindow',
12610 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12612 } else if (// isElement(obj)
12613 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12614 throw $parseMinErr('isecdom',
12615 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12617 } else if (// block Object so that we can't get hold of dangerous Object.* methods
12619 throw $parseMinErr('isecobj',
12620 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12627 var CALL = Function.prototype.call;
12628 var APPLY = Function.prototype.apply;
12629 var BIND = Function.prototype.bind;
12631 function ensureSafeFunction(obj, fullExpression) {
12633 if (obj.constructor === obj) {
12634 throw $parseMinErr('isecfn',
12635 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12637 } else if (obj === CALL || obj === APPLY || obj === BIND) {
12638 throw $parseMinErr('isecff',
12639 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12645 function ensureSafeAssignContext(obj, fullExpression) {
12647 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12648 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12649 throw $parseMinErr('isecaf',
12650 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12655 var OPERATORS = createMap();
12656 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12657 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12660 /////////////////////////////////////////
12666 var Lexer = function(options) {
12667 this.options = options;
12670 Lexer.prototype = {
12671 constructor: Lexer,
12673 lex: function(text) {
12678 while (this.index < this.text.length) {
12679 var ch = this.text.charAt(this.index);
12680 if (ch === '"' || ch === "'") {
12681 this.readString(ch);
12682 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12684 } else if (this.isIdent(ch)) {
12686 } else if (this.is(ch, '(){}[].,;:?')) {
12687 this.tokens.push({index: this.index, text: ch});
12689 } else if (this.isWhitespace(ch)) {
12692 var ch2 = ch + this.peek();
12693 var ch3 = ch2 + this.peek(2);
12694 var op1 = OPERATORS[ch];
12695 var op2 = OPERATORS[ch2];
12696 var op3 = OPERATORS[ch3];
12697 if (op1 || op2 || op3) {
12698 var token = op3 ? ch3 : (op2 ? ch2 : ch);
12699 this.tokens.push({index: this.index, text: token, operator: true});
12700 this.index += token.length;
12702 this.throwError('Unexpected next character ', this.index, this.index + 1);
12706 return this.tokens;
12709 is: function(ch, chars) {
12710 return chars.indexOf(ch) !== -1;
12713 peek: function(i) {
12715 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12718 isNumber: function(ch) {
12719 return ('0' <= ch && ch <= '9') && typeof ch === "string";
12722 isWhitespace: function(ch) {
12723 // IE treats non-breaking space as \u00A0
12724 return (ch === ' ' || ch === '\r' || ch === '\t' ||
12725 ch === '\n' || ch === '\v' || ch === '\u00A0');
12728 isIdent: function(ch) {
12729 return ('a' <= ch && ch <= 'z' ||
12730 'A' <= ch && ch <= 'Z' ||
12731 '_' === ch || ch === '$');
12734 isExpOperator: function(ch) {
12735 return (ch === '-' || ch === '+' || this.isNumber(ch));
12738 throwError: function(error, start, end) {
12739 end = end || this.index;
12740 var colStr = (isDefined(start)
12741 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12743 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12744 error, colStr, this.text);
12747 readNumber: function() {
12749 var start = this.index;
12750 while (this.index < this.text.length) {
12751 var ch = lowercase(this.text.charAt(this.index));
12752 if (ch == '.' || this.isNumber(ch)) {
12755 var peekCh = this.peek();
12756 if (ch == 'e' && this.isExpOperator(peekCh)) {
12758 } else if (this.isExpOperator(ch) &&
12759 peekCh && this.isNumber(peekCh) &&
12760 number.charAt(number.length - 1) == 'e') {
12762 } else if (this.isExpOperator(ch) &&
12763 (!peekCh || !this.isNumber(peekCh)) &&
12764 number.charAt(number.length - 1) == 'e') {
12765 this.throwError('Invalid exponent');
12776 value: Number(number)
12780 readIdent: function() {
12781 var start = this.index;
12782 while (this.index < this.text.length) {
12783 var ch = this.text.charAt(this.index);
12784 if (!(this.isIdent(ch) || this.isNumber(ch))) {
12791 text: this.text.slice(start, this.index),
12796 readString: function(quote) {
12797 var start = this.index;
12800 var rawString = quote;
12801 var escape = false;
12802 while (this.index < this.text.length) {
12803 var ch = this.text.charAt(this.index);
12807 var hex = this.text.substring(this.index + 1, this.index + 5);
12808 if (!hex.match(/[\da-f]{4}/i)) {
12809 this.throwError('Invalid unicode escape [\\u' + hex + ']');
12812 string += String.fromCharCode(parseInt(hex, 16));
12814 var rep = ESCAPE[ch];
12815 string = string + (rep || ch);
12818 } else if (ch === '\\') {
12820 } else if (ch === quote) {
12834 this.throwError('Unterminated quote', start);
12838 var AST = function(lexer, options) {
12839 this.lexer = lexer;
12840 this.options = options;
12843 AST.Program = 'Program';
12844 AST.ExpressionStatement = 'ExpressionStatement';
12845 AST.AssignmentExpression = 'AssignmentExpression';
12846 AST.ConditionalExpression = 'ConditionalExpression';
12847 AST.LogicalExpression = 'LogicalExpression';
12848 AST.BinaryExpression = 'BinaryExpression';
12849 AST.UnaryExpression = 'UnaryExpression';
12850 AST.CallExpression = 'CallExpression';
12851 AST.MemberExpression = 'MemberExpression';
12852 AST.Identifier = 'Identifier';
12853 AST.Literal = 'Literal';
12854 AST.ArrayExpression = 'ArrayExpression';
12855 AST.Property = 'Property';
12856 AST.ObjectExpression = 'ObjectExpression';
12857 AST.ThisExpression = 'ThisExpression';
12859 // Internal use only
12860 AST.NGValueParameter = 'NGValueParameter';
12863 ast: function(text) {
12865 this.tokens = this.lexer.lex(text);
12867 var value = this.program();
12869 if (this.tokens.length !== 0) {
12870 this.throwError('is an unexpected token', this.tokens[0]);
12876 program: function() {
12879 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12880 body.push(this.expressionStatement());
12881 if (!this.expect(';')) {
12882 return { type: AST.Program, body: body};
12887 expressionStatement: function() {
12888 return { type: AST.ExpressionStatement, expression: this.filterChain() };
12891 filterChain: function() {
12892 var left = this.expression();
12894 while ((token = this.expect('|'))) {
12895 left = this.filter(left);
12900 expression: function() {
12901 return this.assignment();
12904 assignment: function() {
12905 var result = this.ternary();
12906 if (this.expect('=')) {
12907 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12912 ternary: function() {
12913 var test = this.logicalOR();
12916 if (this.expect('?')) {
12917 alternate = this.expression();
12918 if (this.consume(':')) {
12919 consequent = this.expression();
12920 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
12926 logicalOR: function() {
12927 var left = this.logicalAND();
12928 while (this.expect('||')) {
12929 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
12934 logicalAND: function() {
12935 var left = this.equality();
12936 while (this.expect('&&')) {
12937 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
12942 equality: function() {
12943 var left = this.relational();
12945 while ((token = this.expect('==','!=','===','!=='))) {
12946 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
12951 relational: function() {
12952 var left = this.additive();
12954 while ((token = this.expect('<', '>', '<=', '>='))) {
12955 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
12960 additive: function() {
12961 var left = this.multiplicative();
12963 while ((token = this.expect('+','-'))) {
12964 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
12969 multiplicative: function() {
12970 var left = this.unary();
12972 while ((token = this.expect('*','/','%'))) {
12973 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
12978 unary: function() {
12980 if ((token = this.expect('+', '-', '!'))) {
12981 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
12983 return this.primary();
12987 primary: function() {
12989 if (this.expect('(')) {
12990 primary = this.filterChain();
12992 } else if (this.expect('[')) {
12993 primary = this.arrayDeclaration();
12994 } else if (this.expect('{')) {
12995 primary = this.object();
12996 } else if (this.constants.hasOwnProperty(this.peek().text)) {
12997 primary = copy(this.constants[this.consume().text]);
12998 } else if (this.peek().identifier) {
12999 primary = this.identifier();
13000 } else if (this.peek().constant) {
13001 primary = this.constant();
13003 this.throwError('not a primary expression', this.peek());
13007 while ((next = this.expect('(', '[', '.'))) {
13008 if (next.text === '(') {
13009 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13011 } else if (next.text === '[') {
13012 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13014 } else if (next.text === '.') {
13015 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13017 this.throwError('IMPOSSIBLE');
13023 filter: function(baseExpression) {
13024 var args = [baseExpression];
13025 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13027 while (this.expect(':')) {
13028 args.push(this.expression());
13034 parseArguments: function() {
13036 if (this.peekToken().text !== ')') {
13038 args.push(this.expression());
13039 } while (this.expect(','));
13044 identifier: function() {
13045 var token = this.consume();
13046 if (!token.identifier) {
13047 this.throwError('is not a valid identifier', token);
13049 return { type: AST.Identifier, name: token.text };
13052 constant: function() {
13053 // TODO check that it is a constant
13054 return { type: AST.Literal, value: this.consume().value };
13057 arrayDeclaration: function() {
13059 if (this.peekToken().text !== ']') {
13061 if (this.peek(']')) {
13062 // Support trailing commas per ES5.1.
13065 elements.push(this.expression());
13066 } while (this.expect(','));
13070 return { type: AST.ArrayExpression, elements: elements };
13073 object: function() {
13074 var properties = [], property;
13075 if (this.peekToken().text !== '}') {
13077 if (this.peek('}')) {
13078 // Support trailing commas per ES5.1.
13081 property = {type: AST.Property, kind: 'init'};
13082 if (this.peek().constant) {
13083 property.key = this.constant();
13084 } else if (this.peek().identifier) {
13085 property.key = this.identifier();
13087 this.throwError("invalid key", this.peek());
13090 property.value = this.expression();
13091 properties.push(property);
13092 } while (this.expect(','));
13096 return {type: AST.ObjectExpression, properties: properties };
13099 throwError: function(msg, token) {
13100 throw $parseMinErr('syntax',
13101 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13102 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13105 consume: function(e1) {
13106 if (this.tokens.length === 0) {
13107 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13110 var token = this.expect(e1);
13112 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13117 peekToken: function() {
13118 if (this.tokens.length === 0) {
13119 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13121 return this.tokens[0];
13124 peek: function(e1, e2, e3, e4) {
13125 return this.peekAhead(0, e1, e2, e3, e4);
13128 peekAhead: function(i, e1, e2, e3, e4) {
13129 if (this.tokens.length > i) {
13130 var token = this.tokens[i];
13131 var t = token.text;
13132 if (t === e1 || t === e2 || t === e3 || t === e4 ||
13133 (!e1 && !e2 && !e3 && !e4)) {
13140 expect: function(e1, e2, e3, e4) {
13141 var token = this.peek(e1, e2, e3, e4);
13143 this.tokens.shift();
13150 /* `undefined` is not a constant, it is an identifier,
13151 * but using it as an identifier is not supported
13154 'true': { type: AST.Literal, value: true },
13155 'false': { type: AST.Literal, value: false },
13156 'null': { type: AST.Literal, value: null },
13157 'undefined': {type: AST.Literal, value: undefined },
13158 'this': {type: AST.ThisExpression }
13162 function ifDefined(v, d) {
13163 return typeof v !== 'undefined' ? v : d;
13166 function plusFn(l, r) {
13167 if (typeof l === 'undefined') return r;
13168 if (typeof r === 'undefined') return l;
13172 function isStateless($filter, filterName) {
13173 var fn = $filter(filterName);
13174 return !fn.$stateful;
13177 function findConstantAndWatchExpressions(ast, $filter) {
13180 switch (ast.type) {
13182 allConstants = true;
13183 forEach(ast.body, function(expr) {
13184 findConstantAndWatchExpressions(expr.expression, $filter);
13185 allConstants = allConstants && expr.expression.constant;
13187 ast.constant = allConstants;
13190 ast.constant = true;
13193 case AST.UnaryExpression:
13194 findConstantAndWatchExpressions(ast.argument, $filter);
13195 ast.constant = ast.argument.constant;
13196 ast.toWatch = ast.argument.toWatch;
13198 case AST.BinaryExpression:
13199 findConstantAndWatchExpressions(ast.left, $filter);
13200 findConstantAndWatchExpressions(ast.right, $filter);
13201 ast.constant = ast.left.constant && ast.right.constant;
13202 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13204 case AST.LogicalExpression:
13205 findConstantAndWatchExpressions(ast.left, $filter);
13206 findConstantAndWatchExpressions(ast.right, $filter);
13207 ast.constant = ast.left.constant && ast.right.constant;
13208 ast.toWatch = ast.constant ? [] : [ast];
13210 case AST.ConditionalExpression:
13211 findConstantAndWatchExpressions(ast.test, $filter);
13212 findConstantAndWatchExpressions(ast.alternate, $filter);
13213 findConstantAndWatchExpressions(ast.consequent, $filter);
13214 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13215 ast.toWatch = ast.constant ? [] : [ast];
13217 case AST.Identifier:
13218 ast.constant = false;
13219 ast.toWatch = [ast];
13221 case AST.MemberExpression:
13222 findConstantAndWatchExpressions(ast.object, $filter);
13223 if (ast.computed) {
13224 findConstantAndWatchExpressions(ast.property, $filter);
13226 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13227 ast.toWatch = [ast];
13229 case AST.CallExpression:
13230 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13232 forEach(ast.arguments, function(expr) {
13233 findConstantAndWatchExpressions(expr, $filter);
13234 allConstants = allConstants && expr.constant;
13235 if (!expr.constant) {
13236 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13239 ast.constant = allConstants;
13240 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13242 case AST.AssignmentExpression:
13243 findConstantAndWatchExpressions(ast.left, $filter);
13244 findConstantAndWatchExpressions(ast.right, $filter);
13245 ast.constant = ast.left.constant && ast.right.constant;
13246 ast.toWatch = [ast];
13248 case AST.ArrayExpression:
13249 allConstants = true;
13251 forEach(ast.elements, function(expr) {
13252 findConstantAndWatchExpressions(expr, $filter);
13253 allConstants = allConstants && expr.constant;
13254 if (!expr.constant) {
13255 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13258 ast.constant = allConstants;
13259 ast.toWatch = argsToWatch;
13261 case AST.ObjectExpression:
13262 allConstants = true;
13264 forEach(ast.properties, function(property) {
13265 findConstantAndWatchExpressions(property.value, $filter);
13266 allConstants = allConstants && property.value.constant;
13267 if (!property.value.constant) {
13268 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13271 ast.constant = allConstants;
13272 ast.toWatch = argsToWatch;
13274 case AST.ThisExpression:
13275 ast.constant = false;
13281 function getInputs(body) {
13282 if (body.length != 1) return;
13283 var lastExpression = body[0].expression;
13284 var candidate = lastExpression.toWatch;
13285 if (candidate.length !== 1) return candidate;
13286 return candidate[0] !== lastExpression ? candidate : undefined;
13289 function isAssignable(ast) {
13290 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13293 function assignableAST(ast) {
13294 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13295 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13299 function isLiteral(ast) {
13300 return ast.body.length === 0 ||
13301 ast.body.length === 1 && (
13302 ast.body[0].expression.type === AST.Literal ||
13303 ast.body[0].expression.type === AST.ArrayExpression ||
13304 ast.body[0].expression.type === AST.ObjectExpression);
13307 function isConstant(ast) {
13308 return ast.constant;
13311 function ASTCompiler(astBuilder, $filter) {
13312 this.astBuilder = astBuilder;
13313 this.$filter = $filter;
13316 ASTCompiler.prototype = {
13317 compile: function(expression, expensiveChecks) {
13319 var ast = this.astBuilder.ast(expression);
13323 expensiveChecks: expensiveChecks,
13324 fn: {vars: [], body: [], own: {}},
13325 assign: {vars: [], body: [], own: {}},
13328 findConstantAndWatchExpressions(ast, self.$filter);
13331 this.stage = 'assign';
13332 if ((assignable = assignableAST(ast))) {
13333 this.state.computing = 'assign';
13334 var result = this.nextId();
13335 this.recurse(assignable, result);
13336 this.return_(result);
13337 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13339 var toWatch = getInputs(ast.body);
13340 self.stage = 'inputs';
13341 forEach(toWatch, function(watch, key) {
13342 var fnKey = 'fn' + key;
13343 self.state[fnKey] = {vars: [], body: [], own: {}};
13344 self.state.computing = fnKey;
13345 var intoId = self.nextId();
13346 self.recurse(watch, intoId);
13347 self.return_(intoId);
13348 self.state.inputs.push(fnKey);
13349 watch.watchId = key;
13351 this.state.computing = 'fn';
13352 this.stage = 'main';
13355 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13356 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13357 '"' + this.USE + ' ' + this.STRICT + '";\n' +
13358 this.filterPrefix() +
13359 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13365 var fn = (new Function('$filter',
13366 'ensureSafeMemberName',
13367 'ensureSafeObject',
13368 'ensureSafeFunction',
13370 'ensureSafeAssignContext',
13376 ensureSafeMemberName,
13378 ensureSafeFunction,
13380 ensureSafeAssignContext,
13385 this.state = this.stage = undefined;
13386 fn.literal = isLiteral(ast);
13387 fn.constant = isConstant(ast);
13395 watchFns: function() {
13397 var fns = this.state.inputs;
13399 forEach(fns, function(name) {
13400 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13403 result.push('fn.inputs=[' + fns.join(',') + '];');
13405 return result.join('');
13408 generateFunction: function(name, params) {
13409 return 'function(' + params + '){' +
13410 this.varsPrefix(name) +
13415 filterPrefix: function() {
13418 forEach(this.state.filters, function(id, filter) {
13419 parts.push(id + '=$filter(' + self.escape(filter) + ')');
13421 if (parts.length) return 'var ' + parts.join(',') + ';';
13425 varsPrefix: function(section) {
13426 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13429 body: function(section) {
13430 return this.state[section].body.join('');
13433 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13434 var left, right, self = this, args, expression;
13435 recursionFn = recursionFn || noop;
13436 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13437 intoId = intoId || this.nextId();
13439 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13440 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13444 switch (ast.type) {
13446 forEach(ast.body, function(expression, pos) {
13447 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13448 if (pos !== ast.body.length - 1) {
13449 self.current().body.push(right, ';');
13451 self.return_(right);
13456 expression = this.escape(ast.value);
13457 this.assign(intoId, expression);
13458 recursionFn(expression);
13460 case AST.UnaryExpression:
13461 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13462 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13463 this.assign(intoId, expression);
13464 recursionFn(expression);
13466 case AST.BinaryExpression:
13467 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13468 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13469 if (ast.operator === '+') {
13470 expression = this.plus(left, right);
13471 } else if (ast.operator === '-') {
13472 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13474 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13476 this.assign(intoId, expression);
13477 recursionFn(expression);
13479 case AST.LogicalExpression:
13480 intoId = intoId || this.nextId();
13481 self.recurse(ast.left, intoId);
13482 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13483 recursionFn(intoId);
13485 case AST.ConditionalExpression:
13486 intoId = intoId || this.nextId();
13487 self.recurse(ast.test, intoId);
13488 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13489 recursionFn(intoId);
13491 case AST.Identifier:
13492 intoId = intoId || this.nextId();
13494 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13495 nameId.computed = false;
13496 nameId.name = ast.name;
13498 ensureSafeMemberName(ast.name);
13499 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13501 self.if_(self.stage === 'inputs' || 's', function() {
13502 if (create && create !== 1) {
13504 self.not(self.nonComputedMember('s', ast.name)),
13505 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13507 self.assign(intoId, self.nonComputedMember('s', ast.name));
13509 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13511 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13512 self.addEnsureSafeObject(intoId);
13514 recursionFn(intoId);
13516 case AST.MemberExpression:
13517 left = nameId && (nameId.context = this.nextId()) || this.nextId();
13518 intoId = intoId || this.nextId();
13519 self.recurse(ast.object, left, undefined, function() {
13520 self.if_(self.notNull(left), function() {
13521 if (ast.computed) {
13522 right = self.nextId();
13523 self.recurse(ast.property, right);
13524 self.getStringValue(right);
13525 self.addEnsureSafeMemberName(right);
13526 if (create && create !== 1) {
13527 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13529 expression = self.ensureSafeObject(self.computedMember(left, right));
13530 self.assign(intoId, expression);
13532 nameId.computed = true;
13533 nameId.name = right;
13536 ensureSafeMemberName(ast.property.name);
13537 if (create && create !== 1) {
13538 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13540 expression = self.nonComputedMember(left, ast.property.name);
13541 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13542 expression = self.ensureSafeObject(expression);
13544 self.assign(intoId, expression);
13546 nameId.computed = false;
13547 nameId.name = ast.property.name;
13551 self.assign(intoId, 'undefined');
13553 recursionFn(intoId);
13556 case AST.CallExpression:
13557 intoId = intoId || this.nextId();
13559 right = self.filter(ast.callee.name);
13561 forEach(ast.arguments, function(expr) {
13562 var argument = self.nextId();
13563 self.recurse(expr, argument);
13564 args.push(argument);
13566 expression = right + '(' + args.join(',') + ')';
13567 self.assign(intoId, expression);
13568 recursionFn(intoId);
13570 right = self.nextId();
13573 self.recurse(ast.callee, right, left, function() {
13574 self.if_(self.notNull(right), function() {
13575 self.addEnsureSafeFunction(right);
13576 forEach(ast.arguments, function(expr) {
13577 self.recurse(expr, self.nextId(), undefined, function(argument) {
13578 args.push(self.ensureSafeObject(argument));
13582 if (!self.state.expensiveChecks) {
13583 self.addEnsureSafeObject(left.context);
13585 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13587 expression = right + '(' + args.join(',') + ')';
13589 expression = self.ensureSafeObject(expression);
13590 self.assign(intoId, expression);
13592 self.assign(intoId, 'undefined');
13594 recursionFn(intoId);
13598 case AST.AssignmentExpression:
13599 right = this.nextId();
13601 if (!isAssignable(ast.left)) {
13602 throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13604 this.recurse(ast.left, undefined, left, function() {
13605 self.if_(self.notNull(left.context), function() {
13606 self.recurse(ast.right, right);
13607 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13608 self.addEnsureSafeAssignContext(left.context);
13609 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13610 self.assign(intoId, expression);
13611 recursionFn(intoId || expression);
13615 case AST.ArrayExpression:
13617 forEach(ast.elements, function(expr) {
13618 self.recurse(expr, self.nextId(), undefined, function(argument) {
13619 args.push(argument);
13622 expression = '[' + args.join(',') + ']';
13623 this.assign(intoId, expression);
13624 recursionFn(expression);
13626 case AST.ObjectExpression:
13628 forEach(ast.properties, function(property) {
13629 self.recurse(property.value, self.nextId(), undefined, function(expr) {
13630 args.push(self.escape(
13631 property.key.type === AST.Identifier ? property.key.name :
13632 ('' + property.key.value)) +
13636 expression = '{' + args.join(',') + '}';
13637 this.assign(intoId, expression);
13638 recursionFn(expression);
13640 case AST.ThisExpression:
13641 this.assign(intoId, 's');
13644 case AST.NGValueParameter:
13645 this.assign(intoId, 'v');
13651 getHasOwnProperty: function(element, property) {
13652 var key = element + '.' + property;
13653 var own = this.current().own;
13654 if (!own.hasOwnProperty(key)) {
13655 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13660 assign: function(id, value) {
13662 this.current().body.push(id, '=', value, ';');
13666 filter: function(filterName) {
13667 if (!this.state.filters.hasOwnProperty(filterName)) {
13668 this.state.filters[filterName] = this.nextId(true);
13670 return this.state.filters[filterName];
13673 ifDefined: function(id, defaultValue) {
13674 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13677 plus: function(left, right) {
13678 return 'plus(' + left + ',' + right + ')';
13681 return_: function(id) {
13682 this.current().body.push('return ', id, ';');
13685 if_: function(test, alternate, consequent) {
13686 if (test === true) {
13689 var body = this.current().body;
13690 body.push('if(', test, '){');
13694 body.push('else{');
13701 not: function(expression) {
13702 return '!(' + expression + ')';
13705 notNull: function(expression) {
13706 return expression + '!=null';
13709 nonComputedMember: function(left, right) {
13710 return left + '.' + right;
13713 computedMember: function(left, right) {
13714 return left + '[' + right + ']';
13717 member: function(left, right, computed) {
13718 if (computed) return this.computedMember(left, right);
13719 return this.nonComputedMember(left, right);
13722 addEnsureSafeObject: function(item) {
13723 this.current().body.push(this.ensureSafeObject(item), ';');
13726 addEnsureSafeMemberName: function(item) {
13727 this.current().body.push(this.ensureSafeMemberName(item), ';');
13730 addEnsureSafeFunction: function(item) {
13731 this.current().body.push(this.ensureSafeFunction(item), ';');
13734 addEnsureSafeAssignContext: function(item) {
13735 this.current().body.push(this.ensureSafeAssignContext(item), ';');
13738 ensureSafeObject: function(item) {
13739 return 'ensureSafeObject(' + item + ',text)';
13742 ensureSafeMemberName: function(item) {
13743 return 'ensureSafeMemberName(' + item + ',text)';
13746 ensureSafeFunction: function(item) {
13747 return 'ensureSafeFunction(' + item + ',text)';
13750 getStringValue: function(item) {
13751 this.assign(item, 'getStringValue(' + item + ',text)');
13754 ensureSafeAssignContext: function(item) {
13755 return 'ensureSafeAssignContext(' + item + ',text)';
13758 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13760 return function() {
13761 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13765 lazyAssign: function(id, value) {
13767 return function() {
13768 self.assign(id, value);
13772 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13774 stringEscapeFn: function(c) {
13775 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13778 escape: function(value) {
13779 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13780 if (isNumber(value)) return value.toString();
13781 if (value === true) return 'true';
13782 if (value === false) return 'false';
13783 if (value === null) return 'null';
13784 if (typeof value === 'undefined') return 'undefined';
13786 throw $parseMinErr('esc', 'IMPOSSIBLE');
13789 nextId: function(skip, init) {
13790 var id = 'v' + (this.state.nextId++);
13792 this.current().vars.push(id + (init ? '=' + init : ''));
13797 current: function() {
13798 return this.state[this.state.computing];
13803 function ASTInterpreter(astBuilder, $filter) {
13804 this.astBuilder = astBuilder;
13805 this.$filter = $filter;
13808 ASTInterpreter.prototype = {
13809 compile: function(expression, expensiveChecks) {
13811 var ast = this.astBuilder.ast(expression);
13812 this.expression = expression;
13813 this.expensiveChecks = expensiveChecks;
13814 findConstantAndWatchExpressions(ast, self.$filter);
13817 if ((assignable = assignableAST(ast))) {
13818 assign = this.recurse(assignable);
13820 var toWatch = getInputs(ast.body);
13824 forEach(toWatch, function(watch, key) {
13825 var input = self.recurse(watch);
13826 watch.input = input;
13827 inputs.push(input);
13828 watch.watchId = key;
13831 var expressions = [];
13832 forEach(ast.body, function(expression) {
13833 expressions.push(self.recurse(expression.expression));
13835 var fn = ast.body.length === 0 ? function() {} :
13836 ast.body.length === 1 ? expressions[0] :
13837 function(scope, locals) {
13839 forEach(expressions, function(exp) {
13840 lastValue = exp(scope, locals);
13845 fn.assign = function(scope, value, locals) {
13846 return assign(scope, locals, value);
13850 fn.inputs = inputs;
13852 fn.literal = isLiteral(ast);
13853 fn.constant = isConstant(ast);
13857 recurse: function(ast, context, create) {
13858 var left, right, self = this, args, expression;
13860 return this.inputs(ast.input, ast.watchId);
13862 switch (ast.type) {
13864 return this.value(ast.value, context);
13865 case AST.UnaryExpression:
13866 right = this.recurse(ast.argument);
13867 return this['unary' + ast.operator](right, context);
13868 case AST.BinaryExpression:
13869 left = this.recurse(ast.left);
13870 right = this.recurse(ast.right);
13871 return this['binary' + ast.operator](left, right, context);
13872 case AST.LogicalExpression:
13873 left = this.recurse(ast.left);
13874 right = this.recurse(ast.right);
13875 return this['binary' + ast.operator](left, right, context);
13876 case AST.ConditionalExpression:
13877 return this['ternary?:'](
13878 this.recurse(ast.test),
13879 this.recurse(ast.alternate),
13880 this.recurse(ast.consequent),
13883 case AST.Identifier:
13884 ensureSafeMemberName(ast.name, self.expression);
13885 return self.identifier(ast.name,
13886 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13887 context, create, self.expression);
13888 case AST.MemberExpression:
13889 left = this.recurse(ast.object, false, !!create);
13890 if (!ast.computed) {
13891 ensureSafeMemberName(ast.property.name, self.expression);
13892 right = ast.property.name;
13894 if (ast.computed) right = this.recurse(ast.property);
13895 return ast.computed ?
13896 this.computedMember(left, right, context, create, self.expression) :
13897 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13898 case AST.CallExpression:
13900 forEach(ast.arguments, function(expr) {
13901 args.push(self.recurse(expr));
13903 if (ast.filter) right = this.$filter(ast.callee.name);
13904 if (!ast.filter) right = this.recurse(ast.callee, true);
13905 return ast.filter ?
13906 function(scope, locals, assign, inputs) {
13908 for (var i = 0; i < args.length; ++i) {
13909 values.push(args[i](scope, locals, assign, inputs));
13911 var value = right.apply(undefined, values, inputs);
13912 return context ? {context: undefined, name: undefined, value: value} : value;
13914 function(scope, locals, assign, inputs) {
13915 var rhs = right(scope, locals, assign, inputs);
13917 if (rhs.value != null) {
13918 ensureSafeObject(rhs.context, self.expression);
13919 ensureSafeFunction(rhs.value, self.expression);
13921 for (var i = 0; i < args.length; ++i) {
13922 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
13924 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
13926 return context ? {value: value} : value;
13928 case AST.AssignmentExpression:
13929 left = this.recurse(ast.left, true, 1);
13930 right = this.recurse(ast.right);
13931 return function(scope, locals, assign, inputs) {
13932 var lhs = left(scope, locals, assign, inputs);
13933 var rhs = right(scope, locals, assign, inputs);
13934 ensureSafeObject(lhs.value, self.expression);
13935 ensureSafeAssignContext(lhs.context);
13936 lhs.context[lhs.name] = rhs;
13937 return context ? {value: rhs} : rhs;
13939 case AST.ArrayExpression:
13941 forEach(ast.elements, function(expr) {
13942 args.push(self.recurse(expr));
13944 return function(scope, locals, assign, inputs) {
13946 for (var i = 0; i < args.length; ++i) {
13947 value.push(args[i](scope, locals, assign, inputs));
13949 return context ? {value: value} : value;
13951 case AST.ObjectExpression:
13953 forEach(ast.properties, function(property) {
13954 args.push({key: property.key.type === AST.Identifier ?
13955 property.key.name :
13956 ('' + property.key.value),
13957 value: self.recurse(property.value)
13960 return function(scope, locals, assign, inputs) {
13962 for (var i = 0; i < args.length; ++i) {
13963 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
13965 return context ? {value: value} : value;
13967 case AST.ThisExpression:
13968 return function(scope) {
13969 return context ? {value: scope} : scope;
13971 case AST.NGValueParameter:
13972 return function(scope, locals, assign, inputs) {
13973 return context ? {value: assign} : assign;
13978 'unary+': function(argument, context) {
13979 return function(scope, locals, assign, inputs) {
13980 var arg = argument(scope, locals, assign, inputs);
13981 if (isDefined(arg)) {
13986 return context ? {value: arg} : arg;
13989 'unary-': function(argument, context) {
13990 return function(scope, locals, assign, inputs) {
13991 var arg = argument(scope, locals, assign, inputs);
13992 if (isDefined(arg)) {
13997 return context ? {value: arg} : arg;
14000 'unary!': function(argument, context) {
14001 return function(scope, locals, assign, inputs) {
14002 var arg = !argument(scope, locals, assign, inputs);
14003 return context ? {value: arg} : arg;
14006 'binary+': function(left, right, context) {
14007 return function(scope, locals, assign, inputs) {
14008 var lhs = left(scope, locals, assign, inputs);
14009 var rhs = right(scope, locals, assign, inputs);
14010 var arg = plusFn(lhs, rhs);
14011 return context ? {value: arg} : arg;
14014 'binary-': function(left, right, context) {
14015 return function(scope, locals, assign, inputs) {
14016 var lhs = left(scope, locals, assign, inputs);
14017 var rhs = right(scope, locals, assign, inputs);
14018 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14019 return context ? {value: arg} : arg;
14022 'binary*': function(left, right, context) {
14023 return function(scope, locals, assign, inputs) {
14024 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14025 return context ? {value: arg} : arg;
14028 'binary/': function(left, right, context) {
14029 return function(scope, locals, assign, inputs) {
14030 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14031 return context ? {value: arg} : arg;
14034 'binary%': function(left, right, context) {
14035 return function(scope, locals, assign, inputs) {
14036 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14037 return context ? {value: arg} : arg;
14040 'binary===': function(left, right, context) {
14041 return function(scope, locals, assign, inputs) {
14042 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14043 return context ? {value: arg} : arg;
14046 'binary!==': function(left, right, context) {
14047 return function(scope, locals, assign, inputs) {
14048 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14049 return context ? {value: arg} : arg;
14052 'binary==': function(left, right, context) {
14053 return function(scope, locals, assign, inputs) {
14054 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14055 return context ? {value: arg} : arg;
14058 'binary!=': function(left, right, context) {
14059 return function(scope, locals, assign, inputs) {
14060 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14061 return context ? {value: arg} : arg;
14064 'binary<': function(left, right, context) {
14065 return function(scope, locals, assign, inputs) {
14066 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14067 return context ? {value: arg} : arg;
14070 'binary>': function(left, right, context) {
14071 return function(scope, locals, assign, inputs) {
14072 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14073 return context ? {value: arg} : arg;
14076 'binary<=': function(left, right, context) {
14077 return function(scope, locals, assign, inputs) {
14078 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14079 return context ? {value: arg} : arg;
14082 'binary>=': function(left, right, context) {
14083 return function(scope, locals, assign, inputs) {
14084 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14085 return context ? {value: arg} : arg;
14088 'binary&&': function(left, right, context) {
14089 return function(scope, locals, assign, inputs) {
14090 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14091 return context ? {value: arg} : arg;
14094 'binary||': function(left, right, context) {
14095 return function(scope, locals, assign, inputs) {
14096 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14097 return context ? {value: arg} : arg;
14100 'ternary?:': function(test, alternate, consequent, context) {
14101 return function(scope, locals, assign, inputs) {
14102 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14103 return context ? {value: arg} : arg;
14106 value: function(value, context) {
14107 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14109 identifier: function(name, expensiveChecks, context, create, expression) {
14110 return function(scope, locals, assign, inputs) {
14111 var base = locals && (name in locals) ? locals : scope;
14112 if (create && create !== 1 && base && !(base[name])) {
14115 var value = base ? base[name] : undefined;
14116 if (expensiveChecks) {
14117 ensureSafeObject(value, expression);
14120 return {context: base, name: name, value: value};
14126 computedMember: function(left, right, context, create, expression) {
14127 return function(scope, locals, assign, inputs) {
14128 var lhs = left(scope, locals, assign, inputs);
14132 rhs = right(scope, locals, assign, inputs);
14133 rhs = getStringValue(rhs);
14134 ensureSafeMemberName(rhs, expression);
14135 if (create && create !== 1 && lhs && !(lhs[rhs])) {
14139 ensureSafeObject(value, expression);
14142 return {context: lhs, name: rhs, value: value};
14148 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14149 return function(scope, locals, assign, inputs) {
14150 var lhs = left(scope, locals, assign, inputs);
14151 if (create && create !== 1 && lhs && !(lhs[right])) {
14154 var value = lhs != null ? lhs[right] : undefined;
14155 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14156 ensureSafeObject(value, expression);
14159 return {context: lhs, name: right, value: value};
14165 inputs: function(input, watchId) {
14166 return function(scope, value, locals, inputs) {
14167 if (inputs) return inputs[watchId];
14168 return input(scope, value, locals);
14176 var Parser = function(lexer, $filter, options) {
14177 this.lexer = lexer;
14178 this.$filter = $filter;
14179 this.options = options;
14180 this.ast = new AST(this.lexer);
14181 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14182 new ASTCompiler(this.ast, $filter);
14185 Parser.prototype = {
14186 constructor: Parser,
14188 parse: function(text) {
14189 return this.astCompiler.compile(text, this.options.expensiveChecks);
14193 var getterFnCacheDefault = createMap();
14194 var getterFnCacheExpensive = createMap();
14196 function isPossiblyDangerousMemberName(name) {
14197 return name == 'constructor';
14200 var objectValueOf = Object.prototype.valueOf;
14202 function getValueOf(value) {
14203 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14206 ///////////////////////////////////
14215 * Converts Angular {@link guide/expression expression} into a function.
14218 * var getter = $parse('user.name');
14219 * var setter = getter.assign;
14220 * var context = {user:{name:'angular'}};
14221 * var locals = {user:{name:'local'}};
14223 * expect(getter(context)).toEqual('angular');
14224 * setter(context, 'newValue');
14225 * expect(context.user.name).toEqual('newValue');
14226 * expect(getter(context, locals)).toEqual('local');
14230 * @param {string} expression String expression to compile.
14231 * @returns {function(context, locals)} a function which represents the compiled expression:
14233 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14234 * are evaluated against (typically a scope object).
14235 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14238 * The returned function also has the following properties:
14239 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14241 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14242 * constant literals.
14243 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14244 * set to a function to change its value on the given context.
14251 * @name $parseProvider
14254 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14257 function $ParseProvider() {
14258 var cacheDefault = createMap();
14259 var cacheExpensive = createMap();
14261 this.$get = ['$filter', function($filter) {
14262 var noUnsafeEval = csp().noUnsafeEval;
14263 var $parseOptions = {
14265 expensiveChecks: false
14267 $parseOptionsExpensive = {
14269 expensiveChecks: true
14272 return function $parse(exp, interceptorFn, expensiveChecks) {
14273 var parsedExpression, oneTime, cacheKey;
14275 switch (typeof exp) {
14280 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14281 parsedExpression = cache[cacheKey];
14283 if (!parsedExpression) {
14284 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14286 exp = exp.substring(2);
14288 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14289 var lexer = new Lexer(parseOptions);
14290 var parser = new Parser(lexer, $filter, parseOptions);
14291 parsedExpression = parser.parse(exp);
14292 if (parsedExpression.constant) {
14293 parsedExpression.$$watchDelegate = constantWatchDelegate;
14294 } else if (oneTime) {
14295 parsedExpression.$$watchDelegate = parsedExpression.literal ?
14296 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14297 } else if (parsedExpression.inputs) {
14298 parsedExpression.$$watchDelegate = inputsWatchDelegate;
14300 cache[cacheKey] = parsedExpression;
14302 return addInterceptor(parsedExpression, interceptorFn);
14305 return addInterceptor(exp, interceptorFn);
14312 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14314 if (newValue == null || oldValueOfValue == null) { // null/undefined
14315 return newValue === oldValueOfValue;
14318 if (typeof newValue === 'object') {
14320 // attempt to convert the value to a primitive type
14321 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14322 // be cheaply dirty-checked
14323 newValue = getValueOf(newValue);
14325 if (typeof newValue === 'object') {
14326 // objects/arrays are not supported - deep-watching them would be too expensive
14330 // fall-through to the primitive equality check
14334 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14337 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14338 var inputExpressions = parsedExpression.inputs;
14341 if (inputExpressions.length === 1) {
14342 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14343 inputExpressions = inputExpressions[0];
14344 return scope.$watch(function expressionInputWatch(scope) {
14345 var newInputValue = inputExpressions(scope);
14346 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14347 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14348 oldInputValueOf = newInputValue && getValueOf(newInputValue);
14351 }, listener, objectEquality, prettyPrintExpression);
14354 var oldInputValueOfValues = [];
14355 var oldInputValues = [];
14356 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14357 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14358 oldInputValues[i] = null;
14361 return scope.$watch(function expressionInputsWatch(scope) {
14362 var changed = false;
14364 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14365 var newInputValue = inputExpressions[i](scope);
14366 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14367 oldInputValues[i] = newInputValue;
14368 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14373 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14377 }, listener, objectEquality, prettyPrintExpression);
14380 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14381 var unwatch, lastValue;
14382 return unwatch = scope.$watch(function oneTimeWatch(scope) {
14383 return parsedExpression(scope);
14384 }, function oneTimeListener(value, old, scope) {
14386 if (isFunction(listener)) {
14387 listener.apply(this, arguments);
14389 if (isDefined(value)) {
14390 scope.$$postDigest(function() {
14391 if (isDefined(lastValue)) {
14396 }, objectEquality);
14399 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14400 var unwatch, lastValue;
14401 return unwatch = scope.$watch(function oneTimeWatch(scope) {
14402 return parsedExpression(scope);
14403 }, function oneTimeListener(value, old, scope) {
14405 if (isFunction(listener)) {
14406 listener.call(this, value, old, scope);
14408 if (isAllDefined(value)) {
14409 scope.$$postDigest(function() {
14410 if (isAllDefined(lastValue)) unwatch();
14413 }, objectEquality);
14415 function isAllDefined(value) {
14416 var allDefined = true;
14417 forEach(value, function(val) {
14418 if (!isDefined(val)) allDefined = false;
14424 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14426 return unwatch = scope.$watch(function constantWatch(scope) {
14427 return parsedExpression(scope);
14428 }, function constantListener(value, old, scope) {
14429 if (isFunction(listener)) {
14430 listener.apply(this, arguments);
14433 }, objectEquality);
14436 function addInterceptor(parsedExpression, interceptorFn) {
14437 if (!interceptorFn) return parsedExpression;
14438 var watchDelegate = parsedExpression.$$watchDelegate;
14439 var useInputs = false;
14442 watchDelegate !== oneTimeLiteralWatchDelegate &&
14443 watchDelegate !== oneTimeWatchDelegate;
14445 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14446 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14447 return interceptorFn(value, scope, locals);
14448 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14449 var value = parsedExpression(scope, locals, assign, inputs);
14450 var result = interceptorFn(value, scope, locals);
14451 // we only return the interceptor's result if the
14452 // initial value is defined (for bind-once)
14453 return isDefined(value) ? result : value;
14456 // Propagate $$watchDelegates other then inputsWatchDelegate
14457 if (parsedExpression.$$watchDelegate &&
14458 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14459 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14460 } else if (!interceptorFn.$stateful) {
14461 // If there is an interceptor, but no watchDelegate then treat the interceptor like
14462 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14463 fn.$$watchDelegate = inputsWatchDelegate;
14464 useInputs = !parsedExpression.inputs;
14465 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14476 * @requires $rootScope
14479 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14480 * when they are done processing.
14482 * This is an implementation of promises/deferred objects inspired by
14483 * [Kris Kowal's Q](https://github.com/kriskowal/q).
14485 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14486 * implementations, and the other which resembles ES6 promises to some degree.
14490 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14491 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14492 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14494 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14497 * It can be used like so:
14500 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14501 * // are available in the current lexical scope (they could have been injected or passed in).
14503 * function asyncGreet(name) {
14504 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
14505 * return $q(function(resolve, reject) {
14506 * setTimeout(function() {
14507 * if (okToGreet(name)) {
14508 * resolve('Hello, ' + name + '!');
14510 * reject('Greeting ' + name + ' is not allowed.');
14516 * var promise = asyncGreet('Robin Hood');
14517 * promise.then(function(greeting) {
14518 * alert('Success: ' + greeting);
14519 * }, function(reason) {
14520 * alert('Failed: ' + reason);
14524 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14526 * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14528 * However, the more traditional CommonJS-style usage is still available, and documented below.
14530 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14531 * interface for interacting with an object that represents the result of an action that is
14532 * performed asynchronously, and may or may not be finished at any given point in time.
14534 * From the perspective of dealing with error handling, deferred and promise APIs are to
14535 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14538 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14539 * // are available in the current lexical scope (they could have been injected or passed in).
14541 * function asyncGreet(name) {
14542 * var deferred = $q.defer();
14544 * setTimeout(function() {
14545 * deferred.notify('About to greet ' + name + '.');
14547 * if (okToGreet(name)) {
14548 * deferred.resolve('Hello, ' + name + '!');
14550 * deferred.reject('Greeting ' + name + ' is not allowed.');
14554 * return deferred.promise;
14557 * var promise = asyncGreet('Robin Hood');
14558 * promise.then(function(greeting) {
14559 * alert('Success: ' + greeting);
14560 * }, function(reason) {
14561 * alert('Failed: ' + reason);
14562 * }, function(update) {
14563 * alert('Got notification: ' + update);
14567 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14568 * comes in the way of guarantees that promise and deferred APIs make, see
14569 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14571 * Additionally the promise api allows for composition that is very hard to do with the
14572 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14573 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14574 * section on serial or parallel joining of promises.
14576 * # The Deferred API
14578 * A new instance of deferred is constructed by calling `$q.defer()`.
14580 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14581 * that can be used for signaling the successful or unsuccessful completion, as well as the status
14586 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14587 * constructed via `$q.reject`, the promise will be rejected instead.
14588 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14589 * resolving it with a rejection constructed via `$q.reject`.
14590 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14591 * multiple times before the promise is either resolved or rejected.
14595 * - promise – `{Promise}` – promise object associated with this deferred.
14598 * # The Promise API
14600 * A new promise instance is created when a deferred instance is created and can be retrieved by
14601 * calling `deferred.promise`.
14603 * The purpose of the promise object is to allow for interested parties to get access to the result
14604 * of the deferred task when it completes.
14608 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14609 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14610 * as soon as the result is available. The callbacks are called with a single argument: the result
14611 * or rejection reason. Additionally, the notify callback may be called zero or more times to
14612 * provide a progress indication, before the promise is resolved or rejected.
14614 * This method *returns a new promise* which is resolved or rejected via the return value of the
14615 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14616 * with the value which is resolved in that promise using
14617 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14618 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14619 * resolved or rejected from the notifyCallback method.
14621 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14623 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14624 * but to do so without modifying the final value. This is useful to release resources or do some
14625 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14626 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14627 * more information.
14629 * # Chaining promises
14631 * Because calling the `then` method of a promise returns a new derived promise, it is easily
14632 * possible to create a chain of promises:
14635 * promiseB = promiseA.then(function(result) {
14636 * return result + 1;
14639 * // promiseB will be resolved immediately after promiseA is resolved and its value
14640 * // will be the result of promiseA incremented by 1
14643 * It is possible to create chains of any length and since a promise can be resolved with another
14644 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14645 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14646 * $http's response interceptors.
14649 * # Differences between Kris Kowal's Q and $q
14651 * There are two main differences:
14653 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14654 * mechanism in angular, which means faster propagation of resolution or rejection into your
14655 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
14656 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14657 * all the important functionality needed for common async tasks.
14662 * it('should simulate promise', inject(function($q, $rootScope) {
14663 * var deferred = $q.defer();
14664 * var promise = deferred.promise;
14665 * var resolvedValue;
14667 * promise.then(function(value) { resolvedValue = value; });
14668 * expect(resolvedValue).toBeUndefined();
14670 * // Simulate resolving of promise
14671 * deferred.resolve(123);
14672 * // Note that the 'then' function does not get called synchronously.
14673 * // This is because we want the promise API to always be async, whether or not
14674 * // it got called synchronously or asynchronously.
14675 * expect(resolvedValue).toBeUndefined();
14677 * // Propagate promise resolution to 'then' functions using $apply().
14678 * $rootScope.$apply();
14679 * expect(resolvedValue).toEqual(123);
14683 * @param {function(function, function)} resolver Function which is responsible for resolving or
14684 * rejecting the newly created promise. The first parameter is a function which resolves the
14685 * promise, the second parameter is a function which rejects the promise.
14687 * @returns {Promise} The newly created promise.
14689 function $QProvider() {
14691 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14692 return qFactory(function(callback) {
14693 $rootScope.$evalAsync(callback);
14694 }, $exceptionHandler);
14698 function $$QProvider() {
14699 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14700 return qFactory(function(callback) {
14701 $browser.defer(callback);
14702 }, $exceptionHandler);
14707 * Constructs a promise manager.
14709 * @param {function(function)} nextTick Function for executing functions in the next turn.
14710 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14711 * debugging purposes.
14712 * @returns {object} Promise manager.
14714 function qFactory(nextTick, exceptionHandler) {
14715 var $qMinErr = minErr('$q', TypeError);
14716 function callOnce(self, resolveFn, rejectFn) {
14717 var called = false;
14718 function wrap(fn) {
14719 return function(value) {
14720 if (called) return;
14722 fn.call(self, value);
14726 return [wrap(resolveFn), wrap(rejectFn)];
14731 * @name ng.$q#defer
14735 * Creates a `Deferred` object which represents a task which will finish in the future.
14737 * @returns {Deferred} Returns a new instance of deferred.
14739 var defer = function() {
14740 return new Deferred();
14743 function Promise() {
14744 this.$$state = { status: 0 };
14747 extend(Promise.prototype, {
14748 then: function(onFulfilled, onRejected, progressBack) {
14749 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14752 var result = new Deferred();
14754 this.$$state.pending = this.$$state.pending || [];
14755 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14756 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14758 return result.promise;
14761 "catch": function(callback) {
14762 return this.then(null, callback);
14765 "finally": function(callback, progressBack) {
14766 return this.then(function(value) {
14767 return handleCallback(value, true, callback);
14768 }, function(error) {
14769 return handleCallback(error, false, callback);
14774 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14775 function simpleBind(context, fn) {
14776 return function(value) {
14777 fn.call(context, value);
14781 function processQueue(state) {
14782 var fn, deferred, pending;
14784 pending = state.pending;
14785 state.processScheduled = false;
14786 state.pending = undefined;
14787 for (var i = 0, ii = pending.length; i < ii; ++i) {
14788 deferred = pending[i][0];
14789 fn = pending[i][state.status];
14791 if (isFunction(fn)) {
14792 deferred.resolve(fn(state.value));
14793 } else if (state.status === 1) {
14794 deferred.resolve(state.value);
14796 deferred.reject(state.value);
14799 deferred.reject(e);
14800 exceptionHandler(e);
14805 function scheduleProcessQueue(state) {
14806 if (state.processScheduled || !state.pending) return;
14807 state.processScheduled = true;
14808 nextTick(function() { processQueue(state); });
14811 function Deferred() {
14812 this.promise = new Promise();
14813 //Necessary to support unbound execution :/
14814 this.resolve = simpleBind(this, this.resolve);
14815 this.reject = simpleBind(this, this.reject);
14816 this.notify = simpleBind(this, this.notify);
14819 extend(Deferred.prototype, {
14820 resolve: function(val) {
14821 if (this.promise.$$state.status) return;
14822 if (val === this.promise) {
14823 this.$$reject($qMinErr(
14825 "Expected promise to be resolved with value other than itself '{0}'",
14828 this.$$resolve(val);
14833 $$resolve: function(val) {
14836 fns = callOnce(this, this.$$resolve, this.$$reject);
14838 if ((isObject(val) || isFunction(val))) then = val && val.then;
14839 if (isFunction(then)) {
14840 this.promise.$$state.status = -1;
14841 then.call(val, fns[0], fns[1], this.notify);
14843 this.promise.$$state.value = val;
14844 this.promise.$$state.status = 1;
14845 scheduleProcessQueue(this.promise.$$state);
14849 exceptionHandler(e);
14853 reject: function(reason) {
14854 if (this.promise.$$state.status) return;
14855 this.$$reject(reason);
14858 $$reject: function(reason) {
14859 this.promise.$$state.value = reason;
14860 this.promise.$$state.status = 2;
14861 scheduleProcessQueue(this.promise.$$state);
14864 notify: function(progress) {
14865 var callbacks = this.promise.$$state.pending;
14867 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14868 nextTick(function() {
14869 var callback, result;
14870 for (var i = 0, ii = callbacks.length; i < ii; i++) {
14871 result = callbacks[i][0];
14872 callback = callbacks[i][3];
14874 result.notify(isFunction(callback) ? callback(progress) : progress);
14876 exceptionHandler(e);
14890 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14891 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14892 * a promise chain, you don't need to worry about it.
14894 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14895 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14896 * a promise error callback and you want to forward the error to the promise derived from the
14897 * current promise, you have to "rethrow" the error by returning a rejection constructed via
14901 * promiseB = promiseA.then(function(result) {
14902 * // success: do something and resolve promiseB
14903 * // with the old or a new result
14905 * }, function(reason) {
14906 * // error: handle the error if possible and
14907 * // resolve promiseB with newPromiseOrValue,
14908 * // otherwise forward the rejection to promiseB
14909 * if (canHandle(reason)) {
14910 * // handle the error and recover
14911 * return newPromiseOrValue;
14913 * return $q.reject(reason);
14917 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
14918 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
14920 var reject = function(reason) {
14921 var result = new Deferred();
14922 result.reject(reason);
14923 return result.promise;
14926 var makePromise = function makePromise(value, resolved) {
14927 var result = new Deferred();
14929 result.resolve(value);
14931 result.reject(value);
14933 return result.promise;
14936 var handleCallback = function handleCallback(value, isResolved, callback) {
14937 var callbackOutput = null;
14939 if (isFunction(callback)) callbackOutput = callback();
14941 return makePromise(e, false);
14943 if (isPromiseLike(callbackOutput)) {
14944 return callbackOutput.then(function() {
14945 return makePromise(value, isResolved);
14946 }, function(error) {
14947 return makePromise(error, false);
14950 return makePromise(value, isResolved);
14960 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
14961 * This is useful when you are dealing with an object that might or might not be a promise, or if
14962 * the promise comes from a source that can't be trusted.
14964 * @param {*} value Value or a promise
14965 * @param {Function=} successCallback
14966 * @param {Function=} errorCallback
14967 * @param {Function=} progressCallback
14968 * @returns {Promise} Returns a promise of the passed value or promise
14972 var when = function(value, callback, errback, progressBack) {
14973 var result = new Deferred();
14974 result.resolve(value);
14975 return result.promise.then(callback, errback, progressBack);
14984 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
14986 * @param {*} value Value or a promise
14987 * @param {Function=} successCallback
14988 * @param {Function=} errorCallback
14989 * @param {Function=} progressCallback
14990 * @returns {Promise} Returns a promise of the passed value or promise
14992 var resolve = when;
15000 * Combines multiple promises into a single promise that is resolved when all of the input
15001 * promises are resolved.
15003 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15004 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15005 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
15006 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
15007 * with the same rejection value.
15010 function all(promises) {
15011 var deferred = new Deferred(),
15013 results = isArray(promises) ? [] : {};
15015 forEach(promises, function(promise, key) {
15017 when(promise).then(function(value) {
15018 if (results.hasOwnProperty(key)) return;
15019 results[key] = value;
15020 if (!(--counter)) deferred.resolve(results);
15021 }, function(reason) {
15022 if (results.hasOwnProperty(key)) return;
15023 deferred.reject(reason);
15027 if (counter === 0) {
15028 deferred.resolve(results);
15031 return deferred.promise;
15034 var $Q = function Q(resolver) {
15035 if (!isFunction(resolver)) {
15036 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15039 if (!(this instanceof Q)) {
15040 // More useful when $Q is the Promise itself.
15041 return new Q(resolver);
15044 var deferred = new Deferred();
15046 function resolveFn(value) {
15047 deferred.resolve(value);
15050 function rejectFn(reason) {
15051 deferred.reject(reason);
15054 resolver(resolveFn, rejectFn);
15056 return deferred.promise;
15060 $Q.reject = reject;
15062 $Q.resolve = resolve;
15068 function $$RAFProvider() { //rAF
15069 this.$get = ['$window', '$timeout', function($window, $timeout) {
15070 var requestAnimationFrame = $window.requestAnimationFrame ||
15071 $window.webkitRequestAnimationFrame;
15073 var cancelAnimationFrame = $window.cancelAnimationFrame ||
15074 $window.webkitCancelAnimationFrame ||
15075 $window.webkitCancelRequestAnimationFrame;
15077 var rafSupported = !!requestAnimationFrame;
15078 var raf = rafSupported
15080 var id = requestAnimationFrame(fn);
15081 return function() {
15082 cancelAnimationFrame(id);
15086 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15087 return function() {
15088 $timeout.cancel(timer);
15092 raf.supported = rafSupported;
15101 * The design decisions behind the scope are heavily favored for speed and memory consumption.
15103 * The typical use of scope is to watch the expressions, which most of the time return the same
15104 * value as last time so we optimize the operation.
15106 * Closures construction is expensive in terms of speed as well as memory:
15107 * - No closures, instead use prototypical inheritance for API
15108 * - Internal state needs to be stored on scope directly, which means that private state is
15109 * exposed as $$____ properties
15111 * Loop operations are optimized by using while(count--) { ... }
15112 * - This means that in order to keep the same order of execution as addition we have to add
15113 * items to the array at the beginning (unshift) instead of at the end (push)
15115 * Child scopes are created and removed often
15116 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15118 * There are fewer watches than observers. This is why you don't want the observer to be implemented
15119 * in the same way as watch. Watch requires return of the initialization function which is expensive
15126 * @name $rootScopeProvider
15129 * Provider for the $rootScope service.
15134 * @name $rootScopeProvider#digestTtl
15137 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15138 * assuming that the model is unstable.
15140 * The current default is 10 iterations.
15142 * In complex applications it's possible that the dependencies between `$watch`s will result in
15143 * several digest iterations. However if an application needs more than the default 10 digest
15144 * iterations for its model to stabilize then you should investigate what is causing the model to
15145 * continuously change during the digest.
15147 * Increasing the TTL could have performance implications, so you should not change it without
15148 * proper justification.
15150 * @param {number} limit The number of digest iterations.
15159 * Every application has a single root {@link ng.$rootScope.Scope scope}.
15160 * All other scopes are descendant scopes of the root scope. Scopes provide separation
15161 * between the model and the view, via a mechanism for watching the model for changes.
15162 * They also provide event emission/broadcast and subscription facility. See the
15163 * {@link guide/scope developer guide on scopes}.
15165 function $RootScopeProvider() {
15167 var $rootScopeMinErr = minErr('$rootScope');
15168 var lastDirtyWatch = null;
15169 var applyAsyncId = null;
15171 this.digestTtl = function(value) {
15172 if (arguments.length) {
15178 function createChildScopeClass(parent) {
15179 function ChildScope() {
15180 this.$$watchers = this.$$nextSibling =
15181 this.$$childHead = this.$$childTail = null;
15182 this.$$listeners = {};
15183 this.$$listenerCount = {};
15184 this.$$watchersCount = 0;
15185 this.$id = nextUid();
15186 this.$$ChildScope = null;
15188 ChildScope.prototype = parent;
15192 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15193 function($injector, $exceptionHandler, $parse, $browser) {
15195 function destroyChildScope($event) {
15196 $event.currentScope.$$destroyed = true;
15199 function cleanUpScope($scope) {
15202 // There is a memory leak in IE9 if all child scopes are not disconnected
15203 // completely when a scope is destroyed. So this code will recurse up through
15204 // all this scopes children
15206 // See issue https://github.com/angular/angular.js/issues/10706
15207 $scope.$$childHead && cleanUpScope($scope.$$childHead);
15208 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15211 // The code below works around IE9 and V8's memory leaks
15214 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15215 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15216 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15218 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15219 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15224 * @name $rootScope.Scope
15227 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15228 * {@link auto.$injector $injector}. Child scopes are created using the
15229 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15230 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15231 * an in-depth introduction and usage examples.
15235 * A scope can inherit from a parent scope, as in this example:
15237 var parent = $rootScope;
15238 var child = parent.$new();
15240 parent.salutation = "Hello";
15241 expect(child.salutation).toEqual('Hello');
15243 child.salutation = "Welcome";
15244 expect(child.salutation).toEqual('Welcome');
15245 expect(parent.salutation).toEqual('Hello');
15248 * When interacting with `Scope` in tests, additional helper methods are available on the
15249 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15253 * @param {Object.<string, function()>=} providers Map of service factory which need to be
15254 * provided for the current scope. Defaults to {@link ng}.
15255 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15256 * append/override services provided by `providers`. This is handy
15257 * when unit-testing and having the need to override a default
15259 * @returns {Object} Newly created scope.
15263 this.$id = nextUid();
15264 this.$$phase = this.$parent = this.$$watchers =
15265 this.$$nextSibling = this.$$prevSibling =
15266 this.$$childHead = this.$$childTail = null;
15268 this.$$destroyed = false;
15269 this.$$listeners = {};
15270 this.$$listenerCount = {};
15271 this.$$watchersCount = 0;
15272 this.$$isolateBindings = null;
15277 * @name $rootScope.Scope#$id
15280 * Unique scope ID (monotonically increasing) useful for debugging.
15285 * @name $rootScope.Scope#$parent
15288 * Reference to the parent scope.
15293 * @name $rootScope.Scope#$root
15296 * Reference to the root scope.
15299 Scope.prototype = {
15300 constructor: Scope,
15303 * @name $rootScope.Scope#$new
15307 * Creates a new child {@link ng.$rootScope.Scope scope}.
15309 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15310 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15312 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15313 * desired for the scope and its child scopes to be permanently detached from the parent and
15314 * thus stop participating in model change detection and listener notification by invoking.
15316 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15317 * parent scope. The scope is isolated, as it can not see parent scope properties.
15318 * When creating widgets, it is useful for the widget to not accidentally read parent
15321 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15322 * of the newly created scope. Defaults to `this` scope if not provided.
15323 * This is used when creating a transclude scope to correctly place it
15324 * in the scope hierarchy while maintaining the correct prototypical
15327 * @returns {Object} The newly created child scope.
15330 $new: function(isolate, parent) {
15333 parent = parent || this;
15336 child = new Scope();
15337 child.$root = this.$root;
15339 // Only create a child scope class if somebody asks for one,
15340 // but cache it to allow the VM to optimize lookups.
15341 if (!this.$$ChildScope) {
15342 this.$$ChildScope = createChildScopeClass(this);
15344 child = new this.$$ChildScope();
15346 child.$parent = parent;
15347 child.$$prevSibling = parent.$$childTail;
15348 if (parent.$$childHead) {
15349 parent.$$childTail.$$nextSibling = child;
15350 parent.$$childTail = child;
15352 parent.$$childHead = parent.$$childTail = child;
15355 // When the new scope is not isolated or we inherit from `this`, and
15356 // the parent scope is destroyed, the property `$$destroyed` is inherited
15357 // prototypically. In all other cases, this property needs to be set
15358 // when the parent scope is destroyed.
15359 // The listener needs to be added after the parent is set
15360 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15367 * @name $rootScope.Scope#$watch
15371 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15373 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15374 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
15375 * its value when executed multiple times with the same input because it may be executed multiple
15376 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15377 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15378 * - The `listener` is called only when the value from the current `watchExpression` and the
15379 * previous call to `watchExpression` are not equal (with the exception of the initial run,
15380 * see below). Inequality is determined according to reference inequality,
15381 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15382 * via the `!==` Javascript operator, unless `objectEquality == true`
15384 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15385 * according to the {@link angular.equals} function. To save the value of the object for
15386 * later comparison, the {@link angular.copy} function is used. This therefore means that
15387 * watching complex objects will have adverse memory and performance implications.
15388 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15389 * This is achieved by rerunning the watchers until no changes are detected. The rerun
15390 * iteration limit is 10 to prevent an infinite loop deadlock.
15393 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15394 * you can register a `watchExpression` function with no `listener`. (Be prepared for
15395 * multiple calls to your `watchExpression` because it will execute multiple times in a
15396 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15398 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15399 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15400 * watcher. In rare cases, this is undesirable because the listener is called when the result
15401 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15402 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15403 * listener was called due to initialization.
15409 // let's assume that scope was dependency injected as the $rootScope
15410 var scope = $rootScope;
15411 scope.name = 'misko';
15414 expect(scope.counter).toEqual(0);
15415 scope.$watch('name', function(newValue, oldValue) {
15416 scope.counter = scope.counter + 1;
15418 expect(scope.counter).toEqual(0);
15421 // the listener is always called during the first $digest loop after it was registered
15422 expect(scope.counter).toEqual(1);
15425 // but now it will not be called unless the value changes
15426 expect(scope.counter).toEqual(1);
15428 scope.name = 'adam';
15430 expect(scope.counter).toEqual(2);
15434 // Using a function as a watchExpression
15436 scope.foodCounter = 0;
15437 expect(scope.foodCounter).toEqual(0);
15439 // This function returns the value being watched. It is called for each turn of the $digest loop
15440 function() { return food; },
15441 // This is the change listener, called when the value returned from the above function changes
15442 function(newValue, oldValue) {
15443 if ( newValue !== oldValue ) {
15444 // Only increment the counter if the value changed
15445 scope.foodCounter = scope.foodCounter + 1;
15449 // No digest has been run so the counter will be zero
15450 expect(scope.foodCounter).toEqual(0);
15452 // Run the digest but since food has not changed count will still be zero
15454 expect(scope.foodCounter).toEqual(0);
15456 // Update food and run digest. Now the counter will increment
15457 food = 'cheeseburger';
15459 expect(scope.foodCounter).toEqual(1);
15465 * @param {(function()|string)} watchExpression Expression that is evaluated on each
15466 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15467 * a call to the `listener`.
15469 * - `string`: Evaluated as {@link guide/expression expression}
15470 * - `function(scope)`: called with current `scope` as a parameter.
15471 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15472 * of `watchExpression` changes.
15474 * - `newVal` contains the current value of the `watchExpression`
15475 * - `oldVal` contains the previous value of the `watchExpression`
15476 * - `scope` refers to the current scope
15477 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15478 * comparing for reference equality.
15479 * @returns {function()} Returns a deregistration function for this listener.
15481 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15482 var get = $parse(watchExp);
15484 if (get.$$watchDelegate) {
15485 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15488 array = scope.$$watchers,
15491 last: initWatchVal,
15493 exp: prettyPrintExpression || watchExp,
15494 eq: !!objectEquality
15497 lastDirtyWatch = null;
15499 if (!isFunction(listener)) {
15504 array = scope.$$watchers = [];
15506 // we use unshift since we use a while loop in $digest for speed.
15507 // the while loop reads in reverse order.
15508 array.unshift(watcher);
15509 incrementWatchersCount(this, 1);
15511 return function deregisterWatch() {
15512 if (arrayRemove(array, watcher) >= 0) {
15513 incrementWatchersCount(scope, -1);
15515 lastDirtyWatch = null;
15521 * @name $rootScope.Scope#$watchGroup
15525 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15526 * If any one expression in the collection changes the `listener` is executed.
15528 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15529 * call to $digest() to see if any items changes.
15530 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15532 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15533 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15535 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15536 * expression in `watchExpressions` changes
15537 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15538 * those of `watchExpression`
15539 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15540 * those of `watchExpression`
15541 * The `scope` refers to the current scope.
15542 * @returns {function()} Returns a de-registration function for all listeners.
15544 $watchGroup: function(watchExpressions, listener) {
15545 var oldValues = new Array(watchExpressions.length);
15546 var newValues = new Array(watchExpressions.length);
15547 var deregisterFns = [];
15549 var changeReactionScheduled = false;
15550 var firstRun = true;
15552 if (!watchExpressions.length) {
15553 // No expressions means we call the listener ASAP
15554 var shouldCall = true;
15555 self.$evalAsync(function() {
15556 if (shouldCall) listener(newValues, newValues, self);
15558 return function deregisterWatchGroup() {
15559 shouldCall = false;
15563 if (watchExpressions.length === 1) {
15564 // Special case size of one
15565 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15566 newValues[0] = value;
15567 oldValues[0] = oldValue;
15568 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15572 forEach(watchExpressions, function(expr, i) {
15573 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15574 newValues[i] = value;
15575 oldValues[i] = oldValue;
15576 if (!changeReactionScheduled) {
15577 changeReactionScheduled = true;
15578 self.$evalAsync(watchGroupAction);
15581 deregisterFns.push(unwatchFn);
15584 function watchGroupAction() {
15585 changeReactionScheduled = false;
15589 listener(newValues, newValues, self);
15591 listener(newValues, oldValues, self);
15595 return function deregisterWatchGroup() {
15596 while (deregisterFns.length) {
15597 deregisterFns.shift()();
15605 * @name $rootScope.Scope#$watchCollection
15609 * Shallow watches the properties of an object and fires whenever any of the properties change
15610 * (for arrays, this implies watching the array items; for object maps, this implies watching
15611 * the properties). If a change is detected, the `listener` callback is fired.
15613 * - The `obj` collection is observed via standard $watch operation and is examined on every
15614 * call to $digest() to see if any items have been added, removed, or moved.
15615 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15616 * adding, removing, and moving items belonging to an object or array.
15621 $scope.names = ['igor', 'matias', 'misko', 'james'];
15622 $scope.dataCount = 4;
15624 $scope.$watchCollection('names', function(newNames, oldNames) {
15625 $scope.dataCount = newNames.length;
15628 expect($scope.dataCount).toEqual(4);
15631 //still at 4 ... no changes
15632 expect($scope.dataCount).toEqual(4);
15634 $scope.names.pop();
15637 //now there's been a change
15638 expect($scope.dataCount).toEqual(3);
15642 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15643 * expression value should evaluate to an object or an array which is observed on each
15644 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15645 * collection will trigger a call to the `listener`.
15647 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15648 * when a change is detected.
15649 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
15650 * - The `oldCollection` object is a copy of the former collection data.
15651 * Due to performance considerations, the`oldCollection` value is computed only if the
15652 * `listener` function declares two or more arguments.
15653 * - The `scope` argument refers to the current scope.
15655 * @returns {function()} Returns a de-registration function for this listener. When the
15656 * de-registration function is executed, the internal watch operation is terminated.
15658 $watchCollection: function(obj, listener) {
15659 $watchCollectionInterceptor.$stateful = true;
15662 // the current value, updated on each dirty-check run
15664 // a shallow copy of the newValue from the last dirty-check run,
15665 // updated to match newValue during dirty-check run
15667 // a shallow copy of the newValue from when the last change happened
15669 // only track veryOldValue if the listener is asking for it
15670 var trackVeryOldValue = (listener.length > 1);
15671 var changeDetected = 0;
15672 var changeDetector = $parse(obj, $watchCollectionInterceptor);
15673 var internalArray = [];
15674 var internalObject = {};
15675 var initRun = true;
15678 function $watchCollectionInterceptor(_value) {
15680 var newLength, key, bothNaN, newItem, oldItem;
15682 // If the new value is undefined, then return undefined as the watch may be a one-time watch
15683 if (isUndefined(newValue)) return;
15685 if (!isObject(newValue)) { // if primitive
15686 if (oldValue !== newValue) {
15687 oldValue = newValue;
15690 } else if (isArrayLike(newValue)) {
15691 if (oldValue !== internalArray) {
15692 // we are transitioning from something which was not an array into array.
15693 oldValue = internalArray;
15694 oldLength = oldValue.length = 0;
15698 newLength = newValue.length;
15700 if (oldLength !== newLength) {
15701 // if lengths do not match we need to trigger change notification
15703 oldValue.length = oldLength = newLength;
15705 // copy the items to oldValue and look for changes.
15706 for (var i = 0; i < newLength; i++) {
15707 oldItem = oldValue[i];
15708 newItem = newValue[i];
15710 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15711 if (!bothNaN && (oldItem !== newItem)) {
15713 oldValue[i] = newItem;
15717 if (oldValue !== internalObject) {
15718 // we are transitioning from something which was not an object into object.
15719 oldValue = internalObject = {};
15723 // copy the items to oldValue and look for changes.
15725 for (key in newValue) {
15726 if (hasOwnProperty.call(newValue, key)) {
15728 newItem = newValue[key];
15729 oldItem = oldValue[key];
15731 if (key in oldValue) {
15732 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15733 if (!bothNaN && (oldItem !== newItem)) {
15735 oldValue[key] = newItem;
15739 oldValue[key] = newItem;
15744 if (oldLength > newLength) {
15745 // we used to have more keys, need to find them and destroy them.
15747 for (key in oldValue) {
15748 if (!hasOwnProperty.call(newValue, key)) {
15750 delete oldValue[key];
15755 return changeDetected;
15758 function $watchCollectionAction() {
15761 listener(newValue, newValue, self);
15763 listener(newValue, veryOldValue, self);
15766 // make a copy for the next time a collection is changed
15767 if (trackVeryOldValue) {
15768 if (!isObject(newValue)) {
15770 veryOldValue = newValue;
15771 } else if (isArrayLike(newValue)) {
15772 veryOldValue = new Array(newValue.length);
15773 for (var i = 0; i < newValue.length; i++) {
15774 veryOldValue[i] = newValue[i];
15776 } else { // if object
15778 for (var key in newValue) {
15779 if (hasOwnProperty.call(newValue, key)) {
15780 veryOldValue[key] = newValue[key];
15787 return this.$watch(changeDetector, $watchCollectionAction);
15792 * @name $rootScope.Scope#$digest
15796 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15797 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15798 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15799 * until no more listeners are firing. This means that it is possible to get into an infinite
15800 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15801 * iterations exceeds 10.
15803 * Usually, you don't call `$digest()` directly in
15804 * {@link ng.directive:ngController controllers} or in
15805 * {@link ng.$compileProvider#directive directives}.
15806 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15807 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15809 * If you want to be notified whenever `$digest()` is called,
15810 * you can register a `watchExpression` function with
15811 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15813 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15818 scope.name = 'misko';
15821 expect(scope.counter).toEqual(0);
15822 scope.$watch('name', function(newValue, oldValue) {
15823 scope.counter = scope.counter + 1;
15825 expect(scope.counter).toEqual(0);
15828 // the listener is always called during the first $digest loop after it was registered
15829 expect(scope.counter).toEqual(1);
15832 // but now it will not be called unless the value changes
15833 expect(scope.counter).toEqual(1);
15835 scope.name = 'adam';
15837 expect(scope.counter).toEqual(2);
15841 $digest: function() {
15842 var watch, value, last,
15846 next, current, target = this,
15848 logIdx, logMsg, asyncTask;
15850 beginPhase('$digest');
15851 // Check for changes to browser url that happened in sync before the call to $digest
15852 $browser.$$checkUrlChange();
15854 if (this === $rootScope && applyAsyncId !== null) {
15855 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15856 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15857 $browser.defer.cancel(applyAsyncId);
15861 lastDirtyWatch = null;
15863 do { // "while dirty" loop
15867 while (asyncQueue.length) {
15869 asyncTask = asyncQueue.shift();
15870 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15872 $exceptionHandler(e);
15874 lastDirtyWatch = null;
15877 traverseScopesLoop:
15878 do { // "traverse the scopes" loop
15879 if ((watchers = current.$$watchers)) {
15880 // process our watches
15881 length = watchers.length;
15884 watch = watchers[length];
15885 // Most common watches are on primitives, in which case we can short
15886 // circuit it with === operator, only when === fails do we use .equals
15888 if ((value = watch.get(current)) !== (last = watch.last) &&
15890 ? equals(value, last)
15891 : (typeof value === 'number' && typeof last === 'number'
15892 && isNaN(value) && isNaN(last)))) {
15894 lastDirtyWatch = watch;
15895 watch.last = watch.eq ? copy(value, null) : value;
15896 watch.fn(value, ((last === initWatchVal) ? value : last), current);
15899 if (!watchLog[logIdx]) watchLog[logIdx] = [];
15900 watchLog[logIdx].push({
15901 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15906 } else if (watch === lastDirtyWatch) {
15907 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15908 // have already been tested.
15910 break traverseScopesLoop;
15914 $exceptionHandler(e);
15919 // Insanity Warning: scope depth-first traversal
15920 // yes, this code is a bit crazy, but it works and we have tests to prove it!
15921 // this piece should be kept in sync with the traversal in $broadcast
15922 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
15923 (current !== target && current.$$nextSibling)))) {
15924 while (current !== target && !(next = current.$$nextSibling)) {
15925 current = current.$parent;
15928 } while ((current = next));
15930 // `break traverseScopesLoop;` takes us to here
15932 if ((dirty || asyncQueue.length) && !(ttl--)) {
15934 throw $rootScopeMinErr('infdig',
15935 '{0} $digest() iterations reached. Aborting!\n' +
15936 'Watchers fired in the last 5 iterations: {1}',
15940 } while (dirty || asyncQueue.length);
15944 while (postDigestQueue.length) {
15946 postDigestQueue.shift()();
15948 $exceptionHandler(e);
15956 * @name $rootScope.Scope#$destroy
15957 * @eventType broadcast on scope being destroyed
15960 * Broadcasted when a scope and its children are being destroyed.
15962 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
15963 * clean up DOM bindings before an element is removed from the DOM.
15968 * @name $rootScope.Scope#$destroy
15972 * Removes the current scope (and all of its children) from the parent scope. Removal implies
15973 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
15974 * propagate to the current scope and its children. Removal also implies that the current
15975 * scope is eligible for garbage collection.
15977 * The `$destroy()` is usually used by directives such as
15978 * {@link ng.directive:ngRepeat ngRepeat} for managing the
15979 * unrolling of the loop.
15981 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
15982 * Application code can register a `$destroy` event handler that will give it a chance to
15983 * perform any necessary cleanup.
15985 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
15986 * clean up DOM bindings before an element is removed from the DOM.
15988 $destroy: function() {
15989 // We can't destroy a scope that has been already destroyed.
15990 if (this.$$destroyed) return;
15991 var parent = this.$parent;
15993 this.$broadcast('$destroy');
15994 this.$$destroyed = true;
15996 if (this === $rootScope) {
15997 //Remove handlers attached to window when $rootScope is removed
15998 $browser.$$applicationDestroyed();
16001 incrementWatchersCount(this, -this.$$watchersCount);
16002 for (var eventName in this.$$listenerCount) {
16003 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16006 // sever all the references to parent scopes (after this cleanup, the current scope should
16007 // not be retained by any of our references and should be eligible for garbage collection)
16008 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16009 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16010 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16011 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16013 // Disable listeners, watchers and apply/digest methods
16014 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16015 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16016 this.$$listeners = {};
16018 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16019 this.$$nextSibling = null;
16020 cleanUpScope(this);
16025 * @name $rootScope.Scope#$eval
16029 * Executes the `expression` on the current scope and returns the result. Any exceptions in
16030 * the expression are propagated (uncaught). This is useful when evaluating Angular
16035 var scope = ng.$rootScope.Scope();
16039 expect(scope.$eval('a+b')).toEqual(3);
16040 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16043 * @param {(string|function())=} expression An angular expression to be executed.
16045 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16046 * - `function(scope)`: execute the function with the current `scope` parameter.
16048 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16049 * @returns {*} The result of evaluating the expression.
16051 $eval: function(expr, locals) {
16052 return $parse(expr)(this, locals);
16057 * @name $rootScope.Scope#$evalAsync
16061 * Executes the expression on the current scope at a later point in time.
16063 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16066 * - it will execute after the function that scheduled the evaluation (preferably before DOM
16068 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16069 * `expression` execution.
16071 * Any exceptions from the execution of the expression are forwarded to the
16072 * {@link ng.$exceptionHandler $exceptionHandler} service.
16074 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16075 * will be scheduled. However, it is encouraged to always call code that changes the model
16076 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16078 * @param {(string|function())=} expression An angular expression to be executed.
16080 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16081 * - `function(scope)`: execute the function with the current `scope` parameter.
16083 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16085 $evalAsync: function(expr, locals) {
16086 // if we are outside of an $digest loop and this is the first time we are scheduling async
16087 // task also schedule async auto-flush
16088 if (!$rootScope.$$phase && !asyncQueue.length) {
16089 $browser.defer(function() {
16090 if (asyncQueue.length) {
16091 $rootScope.$digest();
16096 asyncQueue.push({scope: this, expression: expr, locals: locals});
16099 $$postDigest: function(fn) {
16100 postDigestQueue.push(fn);
16105 * @name $rootScope.Scope#$apply
16109 * `$apply()` is used to execute an expression in angular from outside of the angular
16110 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16111 * Because we are calling into the angular framework we need to perform proper scope life
16112 * cycle of {@link ng.$exceptionHandler exception handling},
16113 * {@link ng.$rootScope.Scope#$digest executing watches}.
16117 * # Pseudo-Code of `$apply()`
16119 function $apply(expr) {
16121 return $eval(expr);
16123 $exceptionHandler(e);
16131 * Scope's `$apply()` method transitions through the following stages:
16133 * 1. The {@link guide/expression expression} is executed using the
16134 * {@link ng.$rootScope.Scope#$eval $eval()} method.
16135 * 2. Any exceptions from the execution of the expression are forwarded to the
16136 * {@link ng.$exceptionHandler $exceptionHandler} service.
16137 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16138 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16141 * @param {(string|function())=} exp An angular expression to be executed.
16143 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16144 * - `function(scope)`: execute the function with current `scope` parameter.
16146 * @returns {*} The result of evaluating the expression.
16148 $apply: function(expr) {
16150 beginPhase('$apply');
16152 return this.$eval(expr);
16157 $exceptionHandler(e);
16160 $rootScope.$digest();
16162 $exceptionHandler(e);
16170 * @name $rootScope.Scope#$applyAsync
16174 * Schedule the invocation of $apply to occur at a later time. The actual time difference
16175 * varies across browsers, but is typically around ~10 milliseconds.
16177 * This can be used to queue up multiple expressions which need to be evaluated in the same
16180 * @param {(string|function())=} exp An angular expression to be executed.
16182 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16183 * - `function(scope)`: execute the function with current `scope` parameter.
16185 $applyAsync: function(expr) {
16187 expr && applyAsyncQueue.push($applyAsyncExpression);
16188 scheduleApplyAsync();
16190 function $applyAsyncExpression() {
16197 * @name $rootScope.Scope#$on
16201 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16202 * discussion of event life cycle.
16204 * The event listener function format is: `function(event, args...)`. The `event` object
16205 * passed into the listener has the following attributes:
16207 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16209 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16210 * event propagates through the scope hierarchy, this property is set to null.
16211 * - `name` - `{string}`: name of the event.
16212 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16213 * further event propagation (available only for events that were `$emit`-ed).
16214 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16216 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16218 * @param {string} name Event name to listen on.
16219 * @param {function(event, ...args)} listener Function to call when the event is emitted.
16220 * @returns {function()} Returns a deregistration function for this listener.
16222 $on: function(name, listener) {
16223 var namedListeners = this.$$listeners[name];
16224 if (!namedListeners) {
16225 this.$$listeners[name] = namedListeners = [];
16227 namedListeners.push(listener);
16229 var current = this;
16231 if (!current.$$listenerCount[name]) {
16232 current.$$listenerCount[name] = 0;
16234 current.$$listenerCount[name]++;
16235 } while ((current = current.$parent));
16238 return function() {
16239 var indexOfListener = namedListeners.indexOf(listener);
16240 if (indexOfListener !== -1) {
16241 namedListeners[indexOfListener] = null;
16242 decrementListenerCount(self, 1, name);
16250 * @name $rootScope.Scope#$emit
16254 * Dispatches an event `name` upwards through the scope hierarchy notifying the
16255 * registered {@link ng.$rootScope.Scope#$on} listeners.
16257 * The event life cycle starts at the scope on which `$emit` was called. All
16258 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16259 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16260 * registered listeners along the way. The event will stop propagating if one of the listeners
16263 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16264 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16266 * @param {string} name Event name to emit.
16267 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16268 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16270 $emit: function(name, args) {
16274 stopPropagation = false,
16277 targetScope: scope,
16278 stopPropagation: function() {stopPropagation = true;},
16279 preventDefault: function() {
16280 event.defaultPrevented = true;
16282 defaultPrevented: false
16284 listenerArgs = concat([event], arguments, 1),
16288 namedListeners = scope.$$listeners[name] || empty;
16289 event.currentScope = scope;
16290 for (i = 0, length = namedListeners.length; i < length; i++) {
16292 // if listeners were deregistered, defragment the array
16293 if (!namedListeners[i]) {
16294 namedListeners.splice(i, 1);
16300 //allow all listeners attached to the current scope to run
16301 namedListeners[i].apply(null, listenerArgs);
16303 $exceptionHandler(e);
16306 //if any listener on the current scope stops propagation, prevent bubbling
16307 if (stopPropagation) {
16308 event.currentScope = null;
16312 scope = scope.$parent;
16315 event.currentScope = null;
16323 * @name $rootScope.Scope#$broadcast
16327 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16328 * registered {@link ng.$rootScope.Scope#$on} listeners.
16330 * The event life cycle starts at the scope on which `$broadcast` was called. All
16331 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16332 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16333 * scope and calls all registered listeners along the way. The event cannot be canceled.
16335 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16336 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16338 * @param {string} name Event name to broadcast.
16339 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16340 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16342 $broadcast: function(name, args) {
16348 targetScope: target,
16349 preventDefault: function() {
16350 event.defaultPrevented = true;
16352 defaultPrevented: false
16355 if (!target.$$listenerCount[name]) return event;
16357 var listenerArgs = concat([event], arguments, 1),
16358 listeners, i, length;
16360 //down while you can, then up and next sibling or up and next sibling until back at root
16361 while ((current = next)) {
16362 event.currentScope = current;
16363 listeners = current.$$listeners[name] || [];
16364 for (i = 0, length = listeners.length; i < length; i++) {
16365 // if listeners were deregistered, defragment the array
16366 if (!listeners[i]) {
16367 listeners.splice(i, 1);
16374 listeners[i].apply(null, listenerArgs);
16376 $exceptionHandler(e);
16380 // Insanity Warning: scope depth-first traversal
16381 // yes, this code is a bit crazy, but it works and we have tests to prove it!
16382 // this piece should be kept in sync with the traversal in $digest
16383 // (though it differs due to having the extra check for $$listenerCount)
16384 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16385 (current !== target && current.$$nextSibling)))) {
16386 while (current !== target && !(next = current.$$nextSibling)) {
16387 current = current.$parent;
16392 event.currentScope = null;
16397 var $rootScope = new Scope();
16399 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16400 var asyncQueue = $rootScope.$$asyncQueue = [];
16401 var postDigestQueue = $rootScope.$$postDigestQueue = [];
16402 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16407 function beginPhase(phase) {
16408 if ($rootScope.$$phase) {
16409 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16412 $rootScope.$$phase = phase;
16415 function clearPhase() {
16416 $rootScope.$$phase = null;
16419 function incrementWatchersCount(current, count) {
16421 current.$$watchersCount += count;
16422 } while ((current = current.$parent));
16425 function decrementListenerCount(current, count, name) {
16427 current.$$listenerCount[name] -= count;
16429 if (current.$$listenerCount[name] === 0) {
16430 delete current.$$listenerCount[name];
16432 } while ((current = current.$parent));
16436 * function used as an initial value for watchers.
16437 * because it's unique we can easily tell it apart from other values
16439 function initWatchVal() {}
16441 function flushApplyAsync() {
16442 while (applyAsyncQueue.length) {
16444 applyAsyncQueue.shift()();
16446 $exceptionHandler(e);
16449 applyAsyncId = null;
16452 function scheduleApplyAsync() {
16453 if (applyAsyncId === null) {
16454 applyAsyncId = $browser.defer(function() {
16455 $rootScope.$apply(flushApplyAsync);
16464 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16466 function $$SanitizeUriProvider() {
16467 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16468 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16472 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16473 * urls during a[href] sanitization.
16475 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16477 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16478 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16479 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16480 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16482 * @param {RegExp=} regexp New regexp to whitelist urls with.
16483 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16484 * chaining otherwise.
16486 this.aHrefSanitizationWhitelist = function(regexp) {
16487 if (isDefined(regexp)) {
16488 aHrefSanitizationWhitelist = regexp;
16491 return aHrefSanitizationWhitelist;
16497 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16498 * urls during img[src] sanitization.
16500 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16502 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16503 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16504 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16505 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16507 * @param {RegExp=} regexp New regexp to whitelist urls with.
16508 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16509 * chaining otherwise.
16511 this.imgSrcSanitizationWhitelist = function(regexp) {
16512 if (isDefined(regexp)) {
16513 imgSrcSanitizationWhitelist = regexp;
16516 return imgSrcSanitizationWhitelist;
16519 this.$get = function() {
16520 return function sanitizeUri(uri, isImage) {
16521 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16523 normalizedVal = urlResolve(uri).href;
16524 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16525 return 'unsafe:' + normalizedVal;
16532 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16533 * Any commits to this file should be reviewed with security in mind. *
16534 * Changes to this file can potentially create security vulnerabilities. *
16535 * An approval from 2 Core members with history of modifying *
16536 * this file is required. *
16538 * Does the change somehow allow for arbitrary javascript to be executed? *
16539 * Or allows for someone to change the prototype of built-in objects? *
16540 * Or gives undesired access to variables likes document or window? *
16541 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16543 var $sceMinErr = minErr('$sce');
16545 var SCE_CONTEXTS = {
16549 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16550 // url. (e.g. ng-include, script src, templateUrl)
16551 RESOURCE_URL: 'resourceUrl',
16555 // Helper functions follow.
16557 function adjustMatcher(matcher) {
16558 if (matcher === 'self') {
16560 } else if (isString(matcher)) {
16561 // Strings match exactly except for 2 wildcards - '*' and '**'.
16562 // '*' matches any character except those from the set ':/.?&'.
16563 // '**' matches any character (like .* in a RegExp).
16564 // More than 2 *'s raises an error as it's ill defined.
16565 if (matcher.indexOf('***') > -1) {
16566 throw $sceMinErr('iwcard',
16567 'Illegal sequence *** in string matcher. String: {0}', matcher);
16569 matcher = escapeForRegexp(matcher).
16570 replace('\\*\\*', '.*').
16571 replace('\\*', '[^:/.?&;]*');
16572 return new RegExp('^' + matcher + '$');
16573 } else if (isRegExp(matcher)) {
16574 // The only other type of matcher allowed is a Regexp.
16575 // Match entire URL / disallow partial matches.
16576 // Flags are reset (i.e. no global, ignoreCase or multiline)
16577 return new RegExp('^' + matcher.source + '$');
16579 throw $sceMinErr('imatcher',
16580 'Matchers may only be "self", string patterns or RegExp objects');
16585 function adjustMatchers(matchers) {
16586 var adjustedMatchers = [];
16587 if (isDefined(matchers)) {
16588 forEach(matchers, function(matcher) {
16589 adjustedMatchers.push(adjustMatcher(matcher));
16592 return adjustedMatchers;
16598 * @name $sceDelegate
16603 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16604 * Contextual Escaping (SCE)} services to AngularJS.
16606 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16607 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
16608 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16609 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16610 * work because `$sce` delegates to `$sceDelegate` for these operations.
16612 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16614 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
16615 * can override it completely to change the behavior of `$sce`, the common case would
16616 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16617 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16618 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16619 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16620 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16625 * @name $sceDelegateProvider
16628 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16629 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
16630 * that the URLs used for sourcing Angular templates are safe. Refer {@link
16631 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16632 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16634 * For the general details about this service in Angular, read the main page for {@link ng.$sce
16635 * Strict Contextual Escaping (SCE)}.
16637 * **Example**: Consider the following case. <a name="example"></a>
16639 * - your app is hosted at url `http://myapp.example.com/`
16640 * - but some of your templates are hosted on other domains you control such as
16641 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
16642 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16644 * Here is what a secure configuration for this scenario might look like:
16647 * angular.module('myApp', []).config(function($sceDelegateProvider) {
16648 * $sceDelegateProvider.resourceUrlWhitelist([
16649 * // Allow same origin resource loads.
16651 * // Allow loading from our assets domain. Notice the difference between * and **.
16652 * 'http://srv*.assets.example.com/**'
16655 * // The blacklist overrides the whitelist so the open redirect here is blocked.
16656 * $sceDelegateProvider.resourceUrlBlacklist([
16657 * 'http://myapp.example.com/clickThru**'
16663 function $SceDelegateProvider() {
16664 this.SCE_CONTEXTS = SCE_CONTEXTS;
16666 // Resource URLs can also be trusted by policy.
16667 var resourceUrlWhitelist = ['self'],
16668 resourceUrlBlacklist = [];
16672 * @name $sceDelegateProvider#resourceUrlWhitelist
16675 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16676 * provided. This must be an array or null. A snapshot of this array is used so further
16677 * changes to the array are ignored.
16679 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16680 * allowed in this array.
16682 * Note: **an empty whitelist array will block all URLs**!
16684 * @return {Array} the currently set whitelist array.
16686 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16687 * same origin resource requests.
16690 * Sets/Gets the whitelist of trusted resource URLs.
16692 this.resourceUrlWhitelist = function(value) {
16693 if (arguments.length) {
16694 resourceUrlWhitelist = adjustMatchers(value);
16696 return resourceUrlWhitelist;
16701 * @name $sceDelegateProvider#resourceUrlBlacklist
16704 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16705 * provided. This must be an array or null. A snapshot of this array is used so further
16706 * changes to the array are ignored.
16708 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16709 * allowed in this array.
16711 * The typical usage for the blacklist is to **block
16712 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16713 * these would otherwise be trusted but actually return content from the redirected domain.
16715 * Finally, **the blacklist overrides the whitelist** and has the final say.
16717 * @return {Array} the currently set blacklist array.
16719 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16720 * is no blacklist.)
16723 * Sets/Gets the blacklist of trusted resource URLs.
16726 this.resourceUrlBlacklist = function(value) {
16727 if (arguments.length) {
16728 resourceUrlBlacklist = adjustMatchers(value);
16730 return resourceUrlBlacklist;
16733 this.$get = ['$injector', function($injector) {
16735 var htmlSanitizer = function htmlSanitizer(html) {
16736 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16739 if ($injector.has('$sanitize')) {
16740 htmlSanitizer = $injector.get('$sanitize');
16744 function matchUrl(matcher, parsedUrl) {
16745 if (matcher === 'self') {
16746 return urlIsSameOrigin(parsedUrl);
16748 // definitely a regex. See adjustMatchers()
16749 return !!matcher.exec(parsedUrl.href);
16753 function isResourceUrlAllowedByPolicy(url) {
16754 var parsedUrl = urlResolve(url.toString());
16755 var i, n, allowed = false;
16756 // Ensure that at least one item from the whitelist allows this url.
16757 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16758 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16764 // Ensure that no item from the blacklist blocked this url.
16765 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16766 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16775 function generateHolderType(Base) {
16776 var holderType = function TrustedValueHolderType(trustedValue) {
16777 this.$$unwrapTrustedValue = function() {
16778 return trustedValue;
16782 holderType.prototype = new Base();
16784 holderType.prototype.valueOf = function sceValueOf() {
16785 return this.$$unwrapTrustedValue();
16787 holderType.prototype.toString = function sceToString() {
16788 return this.$$unwrapTrustedValue().toString();
16793 var trustedValueHolderBase = generateHolderType(),
16796 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16797 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16798 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16799 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16800 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16804 * @name $sceDelegate#trustAs
16807 * Returns an object that is trusted by angular for use in specified strict
16808 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16809 * attribute interpolation, any dom event binding attribute interpolation
16810 * such as for onclick, etc.) that uses the provided value.
16811 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16813 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
16814 * resourceUrl, html, js and css.
16815 * @param {*} value The value that that should be considered trusted/safe.
16816 * @returns {*} A value that can be used to stand in for the provided `value` in places
16817 * where Angular expects a $sce.trustAs() return value.
16819 function trustAs(type, trustedValue) {
16820 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16821 if (!Constructor) {
16822 throw $sceMinErr('icontext',
16823 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16824 type, trustedValue);
16826 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16827 return trustedValue;
16829 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
16830 // mutable objects, we ensure here that the value passed in is actually a string.
16831 if (typeof trustedValue !== 'string') {
16832 throw $sceMinErr('itype',
16833 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16836 return new Constructor(trustedValue);
16841 * @name $sceDelegate#valueOf
16844 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16845 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16846 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16848 * If the passed parameter is not a value that had been returned by {@link
16849 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16851 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16852 * call or anything else.
16853 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16854 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
16855 * `value` unchanged.
16857 function valueOf(maybeTrusted) {
16858 if (maybeTrusted instanceof trustedValueHolderBase) {
16859 return maybeTrusted.$$unwrapTrustedValue();
16861 return maybeTrusted;
16867 * @name $sceDelegate#getTrusted
16870 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16871 * returns the originally supplied value if the queried context type is a supertype of the
16872 * created type. If this condition isn't satisfied, throws an exception.
16874 * @param {string} type The kind of context in which this value is to be used.
16875 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16876 * `$sceDelegate.trustAs`} call.
16877 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16878 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
16880 function getTrusted(type, maybeTrusted) {
16881 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16882 return maybeTrusted;
16884 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16885 if (constructor && maybeTrusted instanceof constructor) {
16886 return maybeTrusted.$$unwrapTrustedValue();
16888 // If we get here, then we may only take one of two actions.
16889 // 1. sanitize the value for the requested type, or
16890 // 2. throw an exception.
16891 if (type === SCE_CONTEXTS.RESOURCE_URL) {
16892 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16893 return maybeTrusted;
16895 throw $sceMinErr('insecurl',
16896 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
16897 maybeTrusted.toString());
16899 } else if (type === SCE_CONTEXTS.HTML) {
16900 return htmlSanitizer(maybeTrusted);
16902 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16905 return { trustAs: trustAs,
16906 getTrusted: getTrusted,
16907 valueOf: valueOf };
16914 * @name $sceProvider
16917 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
16918 * - enable/disable Strict Contextual Escaping (SCE) in a module
16919 * - override the default implementation with a custom delegate
16921 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
16924 /* jshint maxlen: false*/
16933 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
16935 * # Strict Contextual Escaping
16937 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
16938 * contexts to result in a value that is marked as safe to use for that context. One example of
16939 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
16940 * to these contexts as privileged or SCE contexts.
16942 * As of version 1.2, Angular ships with SCE enabled by default.
16944 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
16945 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
16946 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
16947 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
16948 * to the top of your HTML document.
16950 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
16951 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
16953 * Here's an example of a binding in a privileged context:
16956 * <input ng-model="userHtml" aria-label="User input">
16957 * <div ng-bind-html="userHtml"></div>
16960 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
16961 * disabled, this application allows the user to render arbitrary HTML into the DIV.
16962 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
16963 * bindings. (HTML is just one example of a context where rendering user controlled input creates
16964 * security vulnerabilities.)
16966 * For the case of HTML, you might use a library, either on the client side, or on the server side,
16967 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
16969 * How would you ensure that every place that used these types of bindings was bound to a value that
16970 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
16971 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
16972 * properties/fields and forgot to update the binding to the sanitized value?
16974 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
16975 * determine that something explicitly says it's safe to use a value for binding in that
16976 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
16977 * for those values that you can easily tell are safe - because they were received from your server,
16978 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
16979 * allowing only the files in a specific directory to do this. Ensuring that the internal API
16980 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
16982 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
16983 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
16984 * obtain values that will be accepted by SCE / privileged contexts.
16987 * ## How does it work?
16989 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
16990 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
16991 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
16992 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
16994 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
16995 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
16999 * var ngBindHtmlDirective = ['$sce', function($sce) {
17000 * return function(scope, element, attr) {
17001 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17002 * element.html(value || '');
17008 * ## Impact on loading templates
17010 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17011 * `templateUrl`'s specified by {@link guide/directive directives}.
17013 * By default, Angular only loads templates from the same domain and protocol as the application
17014 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
17015 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
17016 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17017 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17021 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17022 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17023 * policy apply in addition to this and may further restrict whether the template is successfully
17024 * loaded. This means that without the right CORS policy, loading templates from a different domain
17025 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
17028 * ## This feels like too much overhead
17030 * It's important to remember that SCE only applies to interpolation expressions.
17032 * If your expressions are constant literals, they're automatically trusted and you don't need to
17033 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17034 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17036 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17037 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
17039 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17040 * templates in `ng-include` from your application's domain without having to even know about SCE.
17041 * It blocks loading templates from other domains or loading templates over http from an https
17042 * served document. You can change these by setting your own custom {@link
17043 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17044 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17046 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
17047 * application that's secure and can be audited to verify that with much more ease than bolting
17048 * security onto an application later.
17050 * <a name="contexts"></a>
17051 * ## What trusted context types are supported?
17053 * | Context | Notes |
17054 * |---------------------|----------------|
17055 * | `$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. |
17056 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
17057 * | `$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. |
17058 * | `$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. |
17059 * | `$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. |
17061 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17063 * Each element in these arrays must be one of the following:
17066 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
17067 * domain** as the application document using the **same protocol**.
17068 * - **String** (except the special value `'self'`)
17069 * - The string is matched against the full *normalized / absolute URL* of the resource
17070 * being tested (substring matches are not good enough.)
17071 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
17072 * match themselves.
17073 * - `*`: matches zero or more occurrences of any character other than one of the following 6
17074 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
17076 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
17077 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
17078 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17079 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
17080 * http://foo.example.com/templates/**).
17081 * - **RegExp** (*see caveat below*)
17082 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
17083 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
17084 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17085 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
17086 * small number of cases. A `.` character in the regex used when matching the scheme or a
17087 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
17088 * is highly recommended to use the string patterns and only fall back to regular expressions
17089 * as a last resort.
17090 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
17091 * matched against the **entire** *normalized / absolute URL* of the resource being tested
17092 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
17093 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17094 * - If you are generating your JavaScript from some other templating engine (not
17095 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17096 * remember to escape your regular expression (and be aware that you might need more than
17097 * one level of escaping depending on your templating engine and the way you interpolated
17098 * the value.) Do make use of your platform's escaping mechanism as it might be good
17099 * enough before coding your own. E.g. Ruby has
17100 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17101 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17102 * Javascript lacks a similar built in function for escaping. Take a look at Google
17103 * Closure library's [goog.string.regExpEscape(s)](
17104 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17106 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17108 * ## Show me an example using SCE.
17110 * <example module="mySceApp" deps="angular-sanitize.js">
17111 * <file name="index.html">
17112 * <div ng-controller="AppController as myCtrl">
17113 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17114 * <b>User comments</b><br>
17115 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17116 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
17118 * <div class="well">
17119 * <div ng-repeat="userComment in myCtrl.userComments">
17120 * <b>{{userComment.name}}</b>:
17121 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17128 * <file name="script.js">
17129 * angular.module('mySceApp', ['ngSanitize'])
17130 * .controller('AppController', ['$http', '$templateCache', '$sce',
17131 * function($http, $templateCache, $sce) {
17133 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17134 * self.userComments = userComments;
17136 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
17137 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
17138 * 'sanitization."">Hover over this text.</span>');
17142 * <file name="test_data.json">
17144 * { "name": "Alice",
17146 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17149 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
17154 * <file name="protractor.js" type="protractor">
17155 * describe('SCE doc demo', function() {
17156 * it('should sanitize untrusted values', function() {
17157 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17158 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
17161 * it('should NOT sanitize explicitly trusted values', function() {
17162 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17163 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
17164 * 'sanitization."">Hover over this text.</span>');
17172 * ## Can I disable SCE completely?
17174 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
17175 * for little coding overhead. It will be much harder to take an SCE disabled application and
17176 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
17177 * for cases where you have a lot of existing code that was written before SCE was introduced and
17178 * you're migrating them a module at a time.
17180 * That said, here's how you can completely disable SCE:
17183 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17184 * // Completely disable SCE. For demonstration purposes only!
17185 * // Do not use in new projects.
17186 * $sceProvider.enabled(false);
17191 /* jshint maxlen: 100 */
17193 function $SceProvider() {
17194 var enabled = true;
17198 * @name $sceProvider#enabled
17201 * @param {boolean=} value If provided, then enables/disables SCE.
17202 * @return {boolean} true if SCE is enabled, false otherwise.
17205 * Enables/disables SCE and returns the current value.
17207 this.enabled = function(value) {
17208 if (arguments.length) {
17215 /* Design notes on the default implementation for SCE.
17217 * The API contract for the SCE delegate
17218 * -------------------------------------
17219 * The SCE delegate object must provide the following 3 methods:
17221 * - trustAs(contextEnum, value)
17222 * This method is used to tell the SCE service that the provided value is OK to use in the
17223 * contexts specified by contextEnum. It must return an object that will be accepted by
17224 * getTrusted() for a compatible contextEnum and return this value.
17227 * For values that were not produced by trustAs(), return them as is. For values that were
17228 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
17229 * trustAs is wrapping the given values into some type, this operation unwraps it when given
17232 * - getTrusted(contextEnum, value)
17233 * This function should return the a value that is safe to use in the context specified by
17234 * contextEnum or throw and exception otherwise.
17236 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17237 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
17238 * instance, an implementation could maintain a registry of all trusted objects by context. In
17239 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
17240 * return the same object passed in if it was found in the registry under a compatible context or
17241 * throw an exception otherwise. An implementation might only wrap values some of the time based
17242 * on some criteria. getTrusted() might return a value and not throw an exception for special
17243 * constants or objects even if not wrapped. All such implementations fulfill this contract.
17246 * A note on the inheritance model for SCE contexts
17247 * ------------------------------------------------
17248 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
17249 * is purely an implementation details.
17251 * The contract is simply this:
17253 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17254 * will also succeed.
17256 * Inheritance happens to capture this in a natural way. In some future, we
17257 * may not use inheritance anymore. That is OK because no code outside of
17258 * sce.js and sceSpecs.js would need to be aware of this detail.
17261 this.$get = ['$parse', '$sceDelegate', function(
17262 $parse, $sceDelegate) {
17263 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
17264 // the "expression(javascript expression)" syntax which is insecure.
17265 if (enabled && msie < 8) {
17266 throw $sceMinErr('iequirks',
17267 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17268 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17269 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
17272 var sce = shallowCopy(SCE_CONTEXTS);
17276 * @name $sce#isEnabled
17279 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
17280 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17283 * Returns a boolean indicating if SCE is enabled.
17285 sce.isEnabled = function() {
17288 sce.trustAs = $sceDelegate.trustAs;
17289 sce.getTrusted = $sceDelegate.getTrusted;
17290 sce.valueOf = $sceDelegate.valueOf;
17293 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17294 sce.valueOf = identity;
17299 * @name $sce#parseAs
17302 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
17303 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
17304 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17307 * @param {string} type The kind of SCE context in which this result will be used.
17308 * @param {string} expression String expression to compile.
17309 * @returns {function(context, locals)} a function which represents the compiled expression:
17311 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17312 * are evaluated against (typically a scope object).
17313 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17316 sce.parseAs = function sceParseAs(type, expr) {
17317 var parsed = $parse(expr);
17318 if (parsed.literal && parsed.constant) {
17321 return $parse(expr, function(value) {
17322 return sce.getTrusted(type, value);
17329 * @name $sce#trustAs
17332 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
17333 * returns an object that is trusted by angular for use in specified strict contextual
17334 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17335 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
17336 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
17339 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
17340 * resourceUrl, html, js and css.
17341 * @param {*} value The value that that should be considered trusted/safe.
17342 * @returns {*} A value that can be used to stand in for the provided `value` in places
17343 * where Angular expects a $sce.trustAs() return value.
17348 * @name $sce#trustAsHtml
17351 * Shorthand method. `$sce.trustAsHtml(value)` →
17352 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17354 * @param {*} value The value to trustAs.
17355 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17356 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
17357 * only accept expressions that are either literal constants or are the
17358 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17363 * @name $sce#trustAsUrl
17366 * Shorthand method. `$sce.trustAsUrl(value)` →
17367 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17369 * @param {*} value The value to trustAs.
17370 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17371 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
17372 * only accept expressions that are either literal constants or are the
17373 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17378 * @name $sce#trustAsResourceUrl
17381 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
17382 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17384 * @param {*} value The value to trustAs.
17385 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17386 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
17387 * only accept expressions that are either literal constants or are the return
17388 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
17393 * @name $sce#trustAsJs
17396 * Shorthand method. `$sce.trustAsJs(value)` →
17397 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17399 * @param {*} value The value to trustAs.
17400 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17401 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
17402 * only accept expressions that are either literal constants or are the
17403 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17408 * @name $sce#getTrusted
17411 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
17412 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17413 * originally supplied value if the queried context type is a supertype of the created type.
17414 * If this condition isn't satisfied, throws an exception.
17416 * @param {string} type The kind of context in which this value is to be used.
17417 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17419 * @returns {*} The value the was originally provided to
17420 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17421 * Otherwise, throws an exception.
17426 * @name $sce#getTrustedHtml
17429 * Shorthand method. `$sce.getTrustedHtml(value)` →
17430 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17432 * @param {*} value The value to pass to `$sce.getTrusted`.
17433 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17438 * @name $sce#getTrustedCss
17441 * Shorthand method. `$sce.getTrustedCss(value)` →
17442 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17444 * @param {*} value The value to pass to `$sce.getTrusted`.
17445 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17450 * @name $sce#getTrustedUrl
17453 * Shorthand method. `$sce.getTrustedUrl(value)` →
17454 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17456 * @param {*} value The value to pass to `$sce.getTrusted`.
17457 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17462 * @name $sce#getTrustedResourceUrl
17465 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
17466 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17468 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17469 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17474 * @name $sce#getTrustedJs
17477 * Shorthand method. `$sce.getTrustedJs(value)` →
17478 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17480 * @param {*} value The value to pass to `$sce.getTrusted`.
17481 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17486 * @name $sce#parseAsHtml
17489 * Shorthand method. `$sce.parseAsHtml(expression string)` →
17490 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17492 * @param {string} expression String expression to compile.
17493 * @returns {function(context, locals)} a function which represents the compiled expression:
17495 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17496 * are evaluated against (typically a scope object).
17497 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17503 * @name $sce#parseAsCss
17506 * Shorthand method. `$sce.parseAsCss(value)` →
17507 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17509 * @param {string} expression String expression to compile.
17510 * @returns {function(context, locals)} a function which represents the compiled expression:
17512 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17513 * are evaluated against (typically a scope object).
17514 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17520 * @name $sce#parseAsUrl
17523 * Shorthand method. `$sce.parseAsUrl(value)` →
17524 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17526 * @param {string} expression String expression to compile.
17527 * @returns {function(context, locals)} a function which represents the compiled expression:
17529 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17530 * are evaluated against (typically a scope object).
17531 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17537 * @name $sce#parseAsResourceUrl
17540 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
17541 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17543 * @param {string} expression String expression to compile.
17544 * @returns {function(context, locals)} a function which represents the compiled expression:
17546 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17547 * are evaluated against (typically a scope object).
17548 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17554 * @name $sce#parseAsJs
17557 * Shorthand method. `$sce.parseAsJs(value)` →
17558 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17560 * @param {string} expression String expression to compile.
17561 * @returns {function(context, locals)} a function which represents the compiled expression:
17563 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17564 * are evaluated against (typically a scope object).
17565 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17569 // Shorthand delegations.
17570 var parse = sce.parseAs,
17571 getTrusted = sce.getTrusted,
17572 trustAs = sce.trustAs;
17574 forEach(SCE_CONTEXTS, function(enumValue, name) {
17575 var lName = lowercase(name);
17576 sce[camelCase("parse_as_" + lName)] = function(expr) {
17577 return parse(enumValue, expr);
17579 sce[camelCase("get_trusted_" + lName)] = function(value) {
17580 return getTrusted(enumValue, value);
17582 sce[camelCase("trust_as_" + lName)] = function(value) {
17583 return trustAs(enumValue, value);
17592 * !!! This is an undocumented "private" service !!!
17595 * @requires $window
17596 * @requires $document
17598 * @property {boolean} history Does the browser support html5 history api ?
17599 * @property {boolean} transitions Does the browser support CSS transition events ?
17600 * @property {boolean} animations Does the browser support CSS animation events ?
17603 * This is very simple implementation of testing browser's features.
17605 function $SnifferProvider() {
17606 this.$get = ['$window', '$document', function($window, $document) {
17607 var eventSupport = {},
17609 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17610 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17611 document = $document[0] || {},
17613 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17614 bodyStyle = document.body && document.body.style,
17615 transitions = false,
17616 animations = false,
17620 for (var prop in bodyStyle) {
17621 if (match = vendorRegex.exec(prop)) {
17622 vendorPrefix = match[0];
17623 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17628 if (!vendorPrefix) {
17629 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17632 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17633 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17635 if (android && (!transitions || !animations)) {
17636 transitions = isString(bodyStyle.webkitTransition);
17637 animations = isString(bodyStyle.webkitAnimation);
17643 // Android has history.pushState, but it does not update location correctly
17644 // so let's not use the history API at all.
17645 // http://code.google.com/p/android/issues/detail?id=17471
17646 // https://github.com/angular/angular.js/issues/904
17648 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17649 // so let's not use the history API also
17650 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17652 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17654 hasEvent: function(event) {
17655 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17656 // it. In particular the event is not fired when backspace or delete key are pressed or
17657 // when cut operation is performed.
17658 // IE10+ implements 'input' event but it erroneously fires under various situations,
17659 // e.g. when placeholder changes, or a form is focused.
17660 if (event === 'input' && msie <= 11) return false;
17662 if (isUndefined(eventSupport[event])) {
17663 var divElm = document.createElement('div');
17664 eventSupport[event] = 'on' + event in divElm;
17667 return eventSupport[event];
17670 vendorPrefix: vendorPrefix,
17671 transitions: transitions,
17672 animations: animations,
17678 var $compileMinErr = minErr('$compile');
17682 * @name $templateRequest
17685 * The `$templateRequest` service runs security checks then downloads the provided template using
17686 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17687 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17688 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17689 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17690 * when `tpl` is of type string and `$templateCache` has the matching entry.
17692 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17693 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17695 * @return {Promise} a promise for the HTTP response data of the given URL.
17697 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17699 function $TemplateRequestProvider() {
17700 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17701 function handleRequestFn(tpl, ignoreRequestError) {
17702 handleRequestFn.totalPendingRequests++;
17704 // We consider the template cache holds only trusted templates, so
17705 // there's no need to go through whitelisting again for keys that already
17706 // are included in there. This also makes Angular accept any script
17707 // directive, no matter its name. However, we still need to unwrap trusted
17709 if (!isString(tpl) || !$templateCache.get(tpl)) {
17710 tpl = $sce.getTrustedResourceUrl(tpl);
17713 var transformResponse = $http.defaults && $http.defaults.transformResponse;
17715 if (isArray(transformResponse)) {
17716 transformResponse = transformResponse.filter(function(transformer) {
17717 return transformer !== defaultHttpResponseTransform;
17719 } else if (transformResponse === defaultHttpResponseTransform) {
17720 transformResponse = null;
17723 var httpOptions = {
17724 cache: $templateCache,
17725 transformResponse: transformResponse
17728 return $http.get(tpl, httpOptions)
17729 ['finally'](function() {
17730 handleRequestFn.totalPendingRequests--;
17732 .then(function(response) {
17733 $templateCache.put(tpl, response.data);
17734 return response.data;
17737 function handleError(resp) {
17738 if (!ignoreRequestError) {
17739 throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17740 tpl, resp.status, resp.statusText);
17742 return $q.reject(resp);
17746 handleRequestFn.totalPendingRequests = 0;
17748 return handleRequestFn;
17752 function $$TestabilityProvider() {
17753 this.$get = ['$rootScope', '$browser', '$location',
17754 function($rootScope, $browser, $location) {
17757 * @name $testability
17760 * The private $$testability service provides a collection of methods for use when debugging
17761 * or by automated test and debugging tools.
17763 var testability = {};
17766 * @name $$testability#findBindings
17769 * Returns an array of elements that are bound (via ng-bind or {{}})
17770 * to expressions matching the input.
17772 * @param {Element} element The element root to search from.
17773 * @param {string} expression The binding expression to match.
17774 * @param {boolean} opt_exactMatch If true, only returns exact matches
17775 * for the expression. Filters and whitespace are ignored.
17777 testability.findBindings = function(element, expression, opt_exactMatch) {
17778 var bindings = element.getElementsByClassName('ng-binding');
17780 forEach(bindings, function(binding) {
17781 var dataBinding = angular.element(binding).data('$binding');
17783 forEach(dataBinding, function(bindingName) {
17784 if (opt_exactMatch) {
17785 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17786 if (matcher.test(bindingName)) {
17787 matches.push(binding);
17790 if (bindingName.indexOf(expression) != -1) {
17791 matches.push(binding);
17801 * @name $$testability#findModels
17804 * Returns an array of elements that are two-way found via ng-model to
17805 * expressions matching the input.
17807 * @param {Element} element The element root to search from.
17808 * @param {string} expression The model expression to match.
17809 * @param {boolean} opt_exactMatch If true, only returns exact matches
17810 * for the expression.
17812 testability.findModels = function(element, expression, opt_exactMatch) {
17813 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17814 for (var p = 0; p < prefixes.length; ++p) {
17815 var attributeEquals = opt_exactMatch ? '=' : '*=';
17816 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17817 var elements = element.querySelectorAll(selector);
17818 if (elements.length) {
17825 * @name $$testability#getLocation
17828 * Shortcut for getting the location in a browser agnostic way. Returns
17829 * the path, search, and hash. (e.g. /path?a=b#hash)
17831 testability.getLocation = function() {
17832 return $location.url();
17836 * @name $$testability#setLocation
17839 * Shortcut for navigating to a location without doing a full page reload.
17841 * @param {string} url The location url (path, search and hash,
17842 * e.g. /path?a=b#hash) to go to.
17844 testability.setLocation = function(url) {
17845 if (url !== $location.url()) {
17846 $location.url(url);
17847 $rootScope.$digest();
17852 * @name $$testability#whenStable
17855 * Calls the callback when $timeout and $http requests are completed.
17857 * @param {function} callback
17859 testability.whenStable = function(callback) {
17860 $browser.notifyWhenNoOutstandingRequests(callback);
17863 return testability;
17867 function $TimeoutProvider() {
17868 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17869 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
17871 var deferreds = {};
17879 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17880 * block and delegates any exceptions to
17881 * {@link ng.$exceptionHandler $exceptionHandler} service.
17883 * The return value of calling `$timeout` is a promise, which will be resolved when
17884 * the delay has passed and the timeout function, if provided, is executed.
17886 * To cancel a timeout request, call `$timeout.cancel(promise)`.
17888 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17889 * synchronously flush the queue of deferred functions.
17891 * If you only want a promise that will be resolved after some specified delay
17892 * then you can call `$timeout` without the `fn` function.
17894 * @param {function()=} fn A function, whose execution should be delayed.
17895 * @param {number=} [delay=0] Delay in milliseconds.
17896 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17897 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17898 * @param {...*=} Pass additional parameters to the executed function.
17899 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17900 * promise will be resolved with is the return value of the `fn` function.
17903 function timeout(fn, delay, invokeApply) {
17904 if (!isFunction(fn)) {
17905 invokeApply = delay;
17910 var args = sliceArgs(arguments, 3),
17911 skipApply = (isDefined(invokeApply) && !invokeApply),
17912 deferred = (skipApply ? $$q : $q).defer(),
17913 promise = deferred.promise,
17916 timeoutId = $browser.defer(function() {
17918 deferred.resolve(fn.apply(null, args));
17920 deferred.reject(e);
17921 $exceptionHandler(e);
17924 delete deferreds[promise.$$timeoutId];
17927 if (!skipApply) $rootScope.$apply();
17930 promise.$$timeoutId = timeoutId;
17931 deferreds[timeoutId] = deferred;
17939 * @name $timeout#cancel
17942 * Cancels a task associated with the `promise`. As a result of this, the promise will be
17943 * resolved with a rejection.
17945 * @param {Promise=} promise Promise returned by the `$timeout` function.
17946 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
17949 timeout.cancel = function(promise) {
17950 if (promise && promise.$$timeoutId in deferreds) {
17951 deferreds[promise.$$timeoutId].reject('canceled');
17952 delete deferreds[promise.$$timeoutId];
17953 return $browser.defer.cancel(promise.$$timeoutId);
17962 // NOTE: The usage of window and document instead of $window and $document here is
17963 // deliberate. This service depends on the specific behavior of anchor nodes created by the
17964 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
17965 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
17966 // doesn't know about mocked locations and resolves URLs to the real document - which is
17967 // exactly the behavior needed here. There is little value is mocking these out for this
17969 var urlParsingNode = document.createElement("a");
17970 var originUrl = urlResolve(window.location.href);
17975 * Implementation Notes for non-IE browsers
17976 * ----------------------------------------
17977 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
17978 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
17979 * URL will be resolved into an absolute URL in the context of the application document.
17980 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
17981 * properties are all populated to reflect the normalized URL. This approach has wide
17982 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
17983 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
17985 * Implementation Notes for IE
17986 * ---------------------------
17987 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
17988 * browsers. However, the parsed components will not be set if the URL assigned did not specify
17989 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
17990 * work around that by performing the parsing in a 2nd step by taking a previously normalized
17991 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
17992 * properties such as protocol, hostname, port, etc.
17995 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
17996 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
17997 * http://url.spec.whatwg.org/#urlutils
17998 * https://github.com/angular/angular.js/pull/2902
17999 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18002 * @param {string} url The URL to be parsed.
18003 * @description Normalizes and parses a URL.
18004 * @returns {object} Returns the normalized URL as a dictionary.
18006 * | member name | Description |
18007 * |---------------|----------------|
18008 * | href | A normalized version of the provided URL if it was not an absolute URL |
18009 * | protocol | The protocol including the trailing colon |
18010 * | host | The host and port (if the port is non-default) of the normalizedUrl |
18011 * | search | The search params, minus the question mark |
18012 * | hash | The hash string, minus the hash symbol
18013 * | hostname | The hostname
18014 * | port | The port, without ":"
18015 * | pathname | The pathname, beginning with "/"
18018 function urlResolve(url) {
18022 // Normalize before parse. Refer Implementation Notes on why this is
18023 // done in two steps on IE.
18024 urlParsingNode.setAttribute("href", href);
18025 href = urlParsingNode.href;
18028 urlParsingNode.setAttribute('href', href);
18030 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18032 href: urlParsingNode.href,
18033 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18034 host: urlParsingNode.host,
18035 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18036 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18037 hostname: urlParsingNode.hostname,
18038 port: urlParsingNode.port,
18039 pathname: (urlParsingNode.pathname.charAt(0) === '/')
18040 ? urlParsingNode.pathname
18041 : '/' + urlParsingNode.pathname
18046 * Parse a request URL and determine whether this is a same-origin request as the application document.
18048 * @param {string|object} requestUrl The url of the request as a string that will be resolved
18049 * or a parsed URL object.
18050 * @returns {boolean} Whether the request is for the same origin as the application document.
18052 function urlIsSameOrigin(requestUrl) {
18053 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18054 return (parsed.protocol === originUrl.protocol &&
18055 parsed.host === originUrl.host);
18063 * A reference to the browser's `window` object. While `window`
18064 * is globally available in JavaScript, it causes testability problems, because
18065 * it is a global variable. In angular we always refer to it through the
18066 * `$window` service, so it may be overridden, removed or mocked for testing.
18068 * Expressions, like the one defined for the `ngClick` directive in the example
18069 * below, are evaluated with respect to the current scope. Therefore, there is
18070 * no risk of inadvertently coding in a dependency on a global value in such an
18074 <example module="windowExample">
18075 <file name="index.html">
18077 angular.module('windowExample', [])
18078 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18079 $scope.greeting = 'Hello, World!';
18080 $scope.doGreeting = function(greeting) {
18081 $window.alert(greeting);
18085 <div ng-controller="ExampleController">
18086 <input type="text" ng-model="greeting" aria-label="greeting" />
18087 <button ng-click="doGreeting(greeting)">ALERT</button>
18090 <file name="protractor.js" type="protractor">
18091 it('should display the greeting in the input box', function() {
18092 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18093 // If we click the button it will block the test runner
18094 // element(':button').click();
18099 function $WindowProvider() {
18100 this.$get = valueFn(window);
18104 * @name $$cookieReader
18105 * @requires $document
18108 * This is a private service for reading cookies used by $http and ngCookies
18110 * @return {Object} a key/value map of the current cookies
18112 function $$CookieReader($document) {
18113 var rawDocument = $document[0] || {};
18114 var lastCookies = {};
18115 var lastCookieString = '';
18117 function safeDecodeURIComponent(str) {
18119 return decodeURIComponent(str);
18125 return function() {
18126 var cookieArray, cookie, i, index, name;
18127 var currentCookieString = rawDocument.cookie || '';
18129 if (currentCookieString !== lastCookieString) {
18130 lastCookieString = currentCookieString;
18131 cookieArray = lastCookieString.split('; ');
18134 for (i = 0; i < cookieArray.length; i++) {
18135 cookie = cookieArray[i];
18136 index = cookie.indexOf('=');
18137 if (index > 0) { //ignore nameless cookies
18138 name = safeDecodeURIComponent(cookie.substring(0, index));
18139 // the first value that is seen for a cookie is the most
18140 // specific one. values for the same cookie name that
18141 // follow are for less specific paths.
18142 if (isUndefined(lastCookies[name])) {
18143 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18148 return lastCookies;
18152 $$CookieReader.$inject = ['$document'];
18154 function $$CookieReaderProvider() {
18155 this.$get = $$CookieReader;
18158 /* global currencyFilter: true,
18160 filterFilter: true,
18162 limitToFilter: true,
18163 lowercaseFilter: true,
18164 numberFilter: true,
18165 orderByFilter: true,
18166 uppercaseFilter: true,
18171 * @name $filterProvider
18174 * Filters are just functions which transform input to an output. However filters need to be
18175 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18176 * annotated with dependencies and is responsible for creating a filter function.
18178 * <div class="alert alert-warning">
18179 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18180 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18181 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18182 * (`myapp_subsection_filterx`).
18186 * // Filter registration
18187 * function MyModule($provide, $filterProvider) {
18188 * // create a service to demonstrate injection (not always needed)
18189 * $provide.value('greet', function(name){
18190 * return 'Hello ' + name + '!';
18193 * // register a filter factory which uses the
18194 * // greet service to demonstrate DI.
18195 * $filterProvider.register('greet', function(greet){
18196 * // return the filter function which uses the greet service
18197 * // to generate salutation
18198 * return function(text) {
18199 * // filters need to be forgiving so check input validity
18200 * return text && greet(text) || text;
18206 * The filter function is registered with the `$injector` under the filter name suffix with
18210 * it('should be the same instance', inject(
18211 * function($filterProvider) {
18212 * $filterProvider.register('reverse', function(){
18216 * function($filter, reverseFilter) {
18217 * expect($filter('reverse')).toBe(reverseFilter);
18222 * For more information about how angular filters work, and how to create your own filters, see
18223 * {@link guide/filter Filters} in the Angular Developer Guide.
18231 * Filters are used for formatting data displayed to the user.
18233 * The general syntax in templates is as follows:
18235 * {{ expression [| filter_name[:parameter_value] ... ] }}
18237 * @param {String} name Name of the filter function to retrieve
18238 * @return {Function} the filter function
18240 <example name="$filter" module="filterExample">
18241 <file name="index.html">
18242 <div ng-controller="MainCtrl">
18243 <h3>{{ originalText }}</h3>
18244 <h3>{{ filteredText }}</h3>
18248 <file name="script.js">
18249 angular.module('filterExample', [])
18250 .controller('MainCtrl', function($scope, $filter) {
18251 $scope.originalText = 'hello';
18252 $scope.filteredText = $filter('uppercase')($scope.originalText);
18257 $FilterProvider.$inject = ['$provide'];
18258 function $FilterProvider($provide) {
18259 var suffix = 'Filter';
18263 * @name $filterProvider#register
18264 * @param {string|Object} name Name of the filter function, or an object map of filters where
18265 * the keys are the filter names and the values are the filter factories.
18267 * <div class="alert alert-warning">
18268 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18269 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18270 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18271 * (`myapp_subsection_filterx`).
18273 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18274 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18275 * of the registered filter instances.
18277 function register(name, factory) {
18278 if (isObject(name)) {
18280 forEach(name, function(filter, key) {
18281 filters[key] = register(key, filter);
18285 return $provide.factory(name + suffix, factory);
18288 this.register = register;
18290 this.$get = ['$injector', function($injector) {
18291 return function(name) {
18292 return $injector.get(name + suffix);
18296 ////////////////////////////////////////
18299 currencyFilter: false,
18301 filterFilter: false,
18303 limitToFilter: false,
18304 lowercaseFilter: false,
18305 numberFilter: false,
18306 orderByFilter: false,
18307 uppercaseFilter: false,
18310 register('currency', currencyFilter);
18311 register('date', dateFilter);
18312 register('filter', filterFilter);
18313 register('json', jsonFilter);
18314 register('limitTo', limitToFilter);
18315 register('lowercase', lowercaseFilter);
18316 register('number', numberFilter);
18317 register('orderBy', orderByFilter);
18318 register('uppercase', uppercaseFilter);
18327 * Selects a subset of items from `array` and returns it as a new array.
18329 * @param {Array} array The source array.
18330 * @param {string|Object|function()} expression The predicate to be used for selecting items from
18335 * - `string`: The string is used for matching against the contents of the `array`. All strings or
18336 * objects with string properties in `array` that match this string will be returned. This also
18337 * applies to nested object properties.
18338 * The predicate can be negated by prefixing the string with `!`.
18340 * - `Object`: A pattern object can be used to filter specific properties on objects contained
18341 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18342 * which have property `name` containing "M" and property `phone` containing "1". A special
18343 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18344 * property of the object or its nested object properties. That's equivalent to the simple
18345 * substring match with a `string` as described above. The predicate can be negated by prefixing
18346 * the string with `!`.
18347 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
18348 * not containing "M".
18350 * Note that a named property will match properties on the same level only, while the special
18351 * `$` property will match properties on the same level or deeper. E.g. an array item like
18352 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18353 * **will** be matched by `{$: 'John'}`.
18355 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18356 * The function is called for each element of the array, with the element, its index, and
18357 * the entire array itself as arguments.
18359 * The final result is an array of those elements that the predicate returned true for.
18361 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18362 * determining if the expected value (from the filter expression) and actual value (from
18363 * the object in the array) should be considered a match.
18367 * - `function(actual, expected)`:
18368 * The function will be given the object value and the predicate value to compare and
18369 * should return true if both values should be considered equal.
18371 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18372 * This is essentially strict comparison of expected and actual.
18374 * - `false|undefined`: A short hand for a function which will look for a substring match in case
18377 * Primitive values are converted to strings. Objects are not compared against primitives,
18378 * unless they have a custom `toString` method (e.g. `Date` objects).
18382 <file name="index.html">
18383 <div ng-init="friends = [{name:'John', phone:'555-1276'},
18384 {name:'Mary', phone:'800-BIG-MARY'},
18385 {name:'Mike', phone:'555-4321'},
18386 {name:'Adam', phone:'555-5678'},
18387 {name:'Julie', phone:'555-8765'},
18388 {name:'Juliette', phone:'555-5678'}]"></div>
18390 <label>Search: <input ng-model="searchText"></label>
18391 <table id="searchTextResults">
18392 <tr><th>Name</th><th>Phone</th></tr>
18393 <tr ng-repeat="friend in friends | filter:searchText">
18394 <td>{{friend.name}}</td>
18395 <td>{{friend.phone}}</td>
18399 <label>Any: <input ng-model="search.$"></label> <br>
18400 <label>Name only <input ng-model="search.name"></label><br>
18401 <label>Phone only <input ng-model="search.phone"></label><br>
18402 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18403 <table id="searchObjResults">
18404 <tr><th>Name</th><th>Phone</th></tr>
18405 <tr ng-repeat="friendObj in friends | filter:search:strict">
18406 <td>{{friendObj.name}}</td>
18407 <td>{{friendObj.phone}}</td>
18411 <file name="protractor.js" type="protractor">
18412 var expectFriendNames = function(expectedNames, key) {
18413 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18414 arr.forEach(function(wd, i) {
18415 expect(wd.getText()).toMatch(expectedNames[i]);
18420 it('should search across all fields when filtering with a string', function() {
18421 var searchText = element(by.model('searchText'));
18422 searchText.clear();
18423 searchText.sendKeys('m');
18424 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18426 searchText.clear();
18427 searchText.sendKeys('76');
18428 expectFriendNames(['John', 'Julie'], 'friend');
18431 it('should search in specific fields when filtering with a predicate object', function() {
18432 var searchAny = element(by.model('search.$'));
18434 searchAny.sendKeys('i');
18435 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18437 it('should use a equal comparison when comparator is true', function() {
18438 var searchName = element(by.model('search.name'));
18439 var strict = element(by.model('strict'));
18440 searchName.clear();
18441 searchName.sendKeys('Julie');
18443 expectFriendNames(['Julie'], 'friendObj');
18448 function filterFilter() {
18449 return function(array, expression, comparator) {
18450 if (!isArrayLike(array)) {
18451 if (array == null) {
18454 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18458 var expressionType = getTypeForFilter(expression);
18460 var matchAgainstAnyProp;
18462 switch (expressionType) {
18464 predicateFn = expression;
18470 matchAgainstAnyProp = true;
18474 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18480 return Array.prototype.filter.call(array, predicateFn);
18484 // Helper functions for `filterFilter`
18485 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18486 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18489 if (comparator === true) {
18490 comparator = equals;
18491 } else if (!isFunction(comparator)) {
18492 comparator = function(actual, expected) {
18493 if (isUndefined(actual)) {
18494 // No substring matching against `undefined`
18497 if ((actual === null) || (expected === null)) {
18498 // No substring matching against `null`; only match against `null`
18499 return actual === expected;
18501 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18502 // Should not compare primitives against objects, unless they have custom `toString` method
18506 actual = lowercase('' + actual);
18507 expected = lowercase('' + expected);
18508 return actual.indexOf(expected) !== -1;
18512 predicateFn = function(item) {
18513 if (shouldMatchPrimitives && !isObject(item)) {
18514 return deepCompare(item, expression.$, comparator, false);
18516 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18519 return predicateFn;
18522 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18523 var actualType = getTypeForFilter(actual);
18524 var expectedType = getTypeForFilter(expected);
18526 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18527 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18528 } else if (isArray(actual)) {
18529 // In case `actual` is an array, consider it a match
18530 // if ANY of it's items matches `expected`
18531 return actual.some(function(item) {
18532 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18536 switch (actualType) {
18539 if (matchAgainstAnyProp) {
18540 for (key in actual) {
18541 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18545 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18546 } else if (expectedType === 'object') {
18547 for (key in expected) {
18548 var expectedVal = expected[key];
18549 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18553 var matchAnyProperty = key === '$';
18554 var actualVal = matchAnyProperty ? actual : actual[key];
18555 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18561 return comparator(actual, expected);
18567 return comparator(actual, expected);
18571 // Used for easily differentiating between `null` and actual `object`
18572 function getTypeForFilter(val) {
18573 return (val === null) ? 'null' : typeof val;
18582 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18583 * symbol for current locale is used.
18585 * @param {number} amount Input to filter.
18586 * @param {string=} symbol Currency symbol or identifier to be displayed.
18587 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18588 * @returns {string} Formatted number.
18592 <example module="currencyExample">
18593 <file name="index.html">
18595 angular.module('currencyExample', [])
18596 .controller('ExampleController', ['$scope', function($scope) {
18597 $scope.amount = 1234.56;
18600 <div ng-controller="ExampleController">
18601 <input type="number" ng-model="amount" aria-label="amount"> <br>
18602 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18603 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18604 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18607 <file name="protractor.js" type="protractor">
18608 it('should init with 1234.56', function() {
18609 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18610 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18611 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18613 it('should update', function() {
18614 if (browser.params.browser == 'safari') {
18615 // Safari does not understand the minus key. See
18616 // https://github.com/angular/protractor/issues/481
18619 element(by.model('amount')).clear();
18620 element(by.model('amount')).sendKeys('-1234');
18621 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18622 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18623 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18628 currencyFilter.$inject = ['$locale'];
18629 function currencyFilter($locale) {
18630 var formats = $locale.NUMBER_FORMATS;
18631 return function(amount, currencySymbol, fractionSize) {
18632 if (isUndefined(currencySymbol)) {
18633 currencySymbol = formats.CURRENCY_SYM;
18636 if (isUndefined(fractionSize)) {
18637 fractionSize = formats.PATTERNS[1].maxFrac;
18640 // if null or undefined pass it through
18641 return (amount == null)
18643 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18644 replace(/\u00A4/g, currencySymbol);
18654 * Formats a number as text.
18656 * If the input is null or undefined, it will just be returned.
18657 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18658 * If the input is not a number an empty string is returned.
18661 * @param {number|string} number Number to format.
18662 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18663 * If this is not provided then the fraction size is computed from the current locale's number
18664 * formatting pattern. In the case of the default locale, it will be 3.
18665 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18668 <example module="numberFilterExample">
18669 <file name="index.html">
18671 angular.module('numberFilterExample', [])
18672 .controller('ExampleController', ['$scope', function($scope) {
18673 $scope.val = 1234.56789;
18676 <div ng-controller="ExampleController">
18677 <label>Enter number: <input ng-model='val'></label><br>
18678 Default formatting: <span id='number-default'>{{val | number}}</span><br>
18679 No fractions: <span>{{val | number:0}}</span><br>
18680 Negative number: <span>{{-val | number:4}}</span>
18683 <file name="protractor.js" type="protractor">
18684 it('should format numbers', function() {
18685 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18686 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18687 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18690 it('should update', function() {
18691 element(by.model('val')).clear();
18692 element(by.model('val')).sendKeys('3374.333');
18693 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18694 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18695 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18702 numberFilter.$inject = ['$locale'];
18703 function numberFilter($locale) {
18704 var formats = $locale.NUMBER_FORMATS;
18705 return function(number, fractionSize) {
18707 // if null or undefined pass it through
18708 return (number == null)
18710 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18715 var DECIMAL_SEP = '.';
18716 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18717 if (isObject(number)) return '';
18719 var isNegative = number < 0;
18720 number = Math.abs(number);
18722 var isInfinity = number === Infinity;
18723 if (!isInfinity && !isFinite(number)) return '';
18725 var numStr = number + '',
18727 hasExponent = false,
18730 if (isInfinity) formatedText = '\u221e';
18732 if (!isInfinity && numStr.indexOf('e') !== -1) {
18733 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18734 if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18737 formatedText = numStr;
18738 hasExponent = true;
18742 if (!isInfinity && !hasExponent) {
18743 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18745 // determine fractionSize if it is not specified
18746 if (isUndefined(fractionSize)) {
18747 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18750 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18752 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18753 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18755 var fraction = ('' + number).split(DECIMAL_SEP);
18756 var whole = fraction[0];
18757 fraction = fraction[1] || '';
18760 lgroup = pattern.lgSize,
18761 group = pattern.gSize;
18763 if (whole.length >= (lgroup + group)) {
18764 pos = whole.length - lgroup;
18765 for (i = 0; i < pos; i++) {
18766 if ((pos - i) % group === 0 && i !== 0) {
18767 formatedText += groupSep;
18769 formatedText += whole.charAt(i);
18773 for (i = pos; i < whole.length; i++) {
18774 if ((whole.length - i) % lgroup === 0 && i !== 0) {
18775 formatedText += groupSep;
18777 formatedText += whole.charAt(i);
18780 // format fraction part.
18781 while (fraction.length < fractionSize) {
18785 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18787 if (fractionSize > 0 && number < 1) {
18788 formatedText = number.toFixed(fractionSize);
18789 number = parseFloat(formatedText);
18790 formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18794 if (number === 0) {
18795 isNegative = false;
18798 parts.push(isNegative ? pattern.negPre : pattern.posPre,
18800 isNegative ? pattern.negSuf : pattern.posSuf);
18801 return parts.join('');
18804 function padNumber(num, digits, trim) {
18811 while (num.length < digits) num = '0' + num;
18813 num = num.substr(num.length - digits);
18819 function dateGetter(name, size, offset, trim) {
18820 offset = offset || 0;
18821 return function(date) {
18822 var value = date['get' + name]();
18823 if (offset > 0 || value > -offset) {
18826 if (value === 0 && offset == -12) value = 12;
18827 return padNumber(value, size, trim);
18831 function dateStrGetter(name, shortForm) {
18832 return function(date, formats) {
18833 var value = date['get' + name]();
18834 var get = uppercase(shortForm ? ('SHORT' + name) : name);
18836 return formats[get][value];
18840 function timeZoneGetter(date, formats, offset) {
18841 var zone = -1 * offset;
18842 var paddedZone = (zone >= 0) ? "+" : "";
18844 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18845 padNumber(Math.abs(zone % 60), 2);
18850 function getFirstThursdayOfYear(year) {
18851 // 0 = index of January
18852 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18853 // 4 = index of Thursday (+1 to account for 1st = 5)
18854 // 11 = index of *next* Thursday (+1 account for 1st = 12)
18855 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18858 function getThursdayThisWeek(datetime) {
18859 return new Date(datetime.getFullYear(), datetime.getMonth(),
18860 // 4 = index of Thursday
18861 datetime.getDate() + (4 - datetime.getDay()));
18864 function weekGetter(size) {
18865 return function(date) {
18866 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18867 thisThurs = getThursdayThisWeek(date);
18869 var diff = +thisThurs - +firstThurs,
18870 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18872 return padNumber(result, size);
18876 function ampmGetter(date, formats) {
18877 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18880 function eraGetter(date, formats) {
18881 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18884 function longEraGetter(date, formats) {
18885 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18888 var DATE_FORMATS = {
18889 yyyy: dateGetter('FullYear', 4),
18890 yy: dateGetter('FullYear', 2, 0, true),
18891 y: dateGetter('FullYear', 1),
18892 MMMM: dateStrGetter('Month'),
18893 MMM: dateStrGetter('Month', true),
18894 MM: dateGetter('Month', 2, 1),
18895 M: dateGetter('Month', 1, 1),
18896 dd: dateGetter('Date', 2),
18897 d: dateGetter('Date', 1),
18898 HH: dateGetter('Hours', 2),
18899 H: dateGetter('Hours', 1),
18900 hh: dateGetter('Hours', 2, -12),
18901 h: dateGetter('Hours', 1, -12),
18902 mm: dateGetter('Minutes', 2),
18903 m: dateGetter('Minutes', 1),
18904 ss: dateGetter('Seconds', 2),
18905 s: dateGetter('Seconds', 1),
18906 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18907 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18908 sss: dateGetter('Milliseconds', 3),
18909 EEEE: dateStrGetter('Day'),
18910 EEE: dateStrGetter('Day', true),
18918 GGGG: longEraGetter
18921 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
18922 NUMBER_STRING = /^\-?\d+$/;
18930 * Formats `date` to a string based on the requested `format`.
18932 * `format` string can be composed of the following elements:
18934 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
18935 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
18936 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
18937 * * `'MMMM'`: Month in year (January-December)
18938 * * `'MMM'`: Month in year (Jan-Dec)
18939 * * `'MM'`: Month in year, padded (01-12)
18940 * * `'M'`: Month in year (1-12)
18941 * * `'dd'`: Day in month, padded (01-31)
18942 * * `'d'`: Day in month (1-31)
18943 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
18944 * * `'EEE'`: Day in Week, (Sun-Sat)
18945 * * `'HH'`: Hour in day, padded (00-23)
18946 * * `'H'`: Hour in day (0-23)
18947 * * `'hh'`: Hour in AM/PM, padded (01-12)
18948 * * `'h'`: Hour in AM/PM, (1-12)
18949 * * `'mm'`: Minute in hour, padded (00-59)
18950 * * `'m'`: Minute in hour (0-59)
18951 * * `'ss'`: Second in minute, padded (00-59)
18952 * * `'s'`: Second in minute (0-59)
18953 * * `'sss'`: Millisecond in second, padded (000-999)
18954 * * `'a'`: AM/PM marker
18955 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
18956 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
18957 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
18958 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
18959 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
18961 * `format` string can also be one of the following predefined
18962 * {@link guide/i18n localizable formats}:
18964 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
18965 * (e.g. Sep 3, 2010 12:05:08 PM)
18966 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
18967 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
18968 * (e.g. Friday, September 3, 2010)
18969 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
18970 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
18971 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
18972 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
18973 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
18975 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
18976 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
18977 * (e.g. `"h 'o''clock'"`).
18979 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
18980 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
18981 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
18982 * specified in the string input, the time is considered to be in the local timezone.
18983 * @param {string=} format Formatting rules (see Description). If not specified,
18984 * `mediumDate` is used.
18985 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
18986 * continental US time zone abbreviations, but for general use, use a time zone offset, for
18987 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
18988 * If not specified, the timezone of the browser will be used.
18989 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
18993 <file name="index.html">
18994 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
18995 <span>{{1288323623006 | date:'medium'}}</span><br>
18996 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
18997 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
18998 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
18999 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19000 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19001 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19003 <file name="protractor.js" type="protractor">
19004 it('should format date', function() {
19005 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19006 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19007 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19008 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19009 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19010 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19011 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19012 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19017 dateFilter.$inject = ['$locale'];
19018 function dateFilter($locale) {
19021 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19022 // 1 2 3 4 5 6 7 8 9 10 11
19023 function jsonStringToDate(string) {
19025 if (match = string.match(R_ISO8601_STR)) {
19026 var date = new Date(0),
19029 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19030 timeSetter = match[8] ? date.setUTCHours : date.setHours;
19033 tzHour = toInt(match[9] + match[10]);
19034 tzMin = toInt(match[9] + match[11]);
19036 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19037 var h = toInt(match[4] || 0) - tzHour;
19038 var m = toInt(match[5] || 0) - tzMin;
19039 var s = toInt(match[6] || 0);
19040 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19041 timeSetter.call(date, h, m, s, ms);
19048 return function(date, format, timezone) {
19053 format = format || 'mediumDate';
19054 format = $locale.DATETIME_FORMATS[format] || format;
19055 if (isString(date)) {
19056 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19059 if (isNumber(date)) {
19060 date = new Date(date);
19063 if (!isDate(date) || !isFinite(date.getTime())) {
19068 match = DATE_FORMATS_SPLIT.exec(format);
19070 parts = concat(parts, match, 1);
19071 format = parts.pop();
19073 parts.push(format);
19078 var dateTimezoneOffset = date.getTimezoneOffset();
19080 dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19081 date = convertTimezoneToLocal(date, timezone, true);
19083 forEach(parts, function(value) {
19084 fn = DATE_FORMATS[value];
19085 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19086 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19100 * Allows you to convert a JavaScript object into JSON string.
19102 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
19103 * the binding is automatically converted to JSON.
19105 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19106 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19107 * @returns {string} JSON string.
19112 <file name="index.html">
19113 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19114 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19116 <file name="protractor.js" type="protractor">
19117 it('should jsonify filtered objects', function() {
19118 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
19119 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
19125 function jsonFilter() {
19126 return function(object, spacing) {
19127 if (isUndefined(spacing)) {
19130 return toJson(object, spacing);
19140 * Converts string to lowercase.
19141 * @see angular.lowercase
19143 var lowercaseFilter = valueFn(lowercase);
19151 * Converts string to uppercase.
19152 * @see angular.uppercase
19154 var uppercaseFilter = valueFn(uppercase);
19162 * Creates a new array or string containing only a specified number of elements. The elements
19163 * are taken from either the beginning or the end of the source array, string or number, as specified by
19164 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19165 * converted to a string.
19167 * @param {Array|string|number} input Source array, string or number to be limited.
19168 * @param {string|number} limit The length of the returned array or string. If the `limit` number
19169 * is positive, `limit` number of items from the beginning of the source array/string are copied.
19170 * If the number is negative, `limit` number of items from the end of the source array/string
19171 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19172 * the input will be returned unchanged.
19173 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19174 * indicates an offset from the end of `input`. Defaults to `0`.
19175 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19176 * had less than `limit` elements.
19179 <example module="limitToExample">
19180 <file name="index.html">
19182 angular.module('limitToExample', [])
19183 .controller('ExampleController', ['$scope', function($scope) {
19184 $scope.numbers = [1,2,3,4,5,6,7,8,9];
19185 $scope.letters = "abcdefghi";
19186 $scope.longNumber = 2345432342;
19187 $scope.numLimit = 3;
19188 $scope.letterLimit = 3;
19189 $scope.longNumberLimit = 3;
19192 <div ng-controller="ExampleController">
19194 Limit {{numbers}} to:
19195 <input type="number" step="1" ng-model="numLimit">
19197 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19199 Limit {{letters}} to:
19200 <input type="number" step="1" ng-model="letterLimit">
19202 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19204 Limit {{longNumber}} to:
19205 <input type="number" step="1" ng-model="longNumberLimit">
19207 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19210 <file name="protractor.js" type="protractor">
19211 var numLimitInput = element(by.model('numLimit'));
19212 var letterLimitInput = element(by.model('letterLimit'));
19213 var longNumberLimitInput = element(by.model('longNumberLimit'));
19214 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19215 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19216 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19218 it('should limit the number array to first three items', function() {
19219 expect(numLimitInput.getAttribute('value')).toBe('3');
19220 expect(letterLimitInput.getAttribute('value')).toBe('3');
19221 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19222 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19223 expect(limitedLetters.getText()).toEqual('Output letters: abc');
19224 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19227 // There is a bug in safari and protractor that doesn't like the minus key
19228 // it('should update the output when -3 is entered', function() {
19229 // numLimitInput.clear();
19230 // numLimitInput.sendKeys('-3');
19231 // letterLimitInput.clear();
19232 // letterLimitInput.sendKeys('-3');
19233 // longNumberLimitInput.clear();
19234 // longNumberLimitInput.sendKeys('-3');
19235 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19236 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19237 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19240 it('should not exceed the maximum size of input array', function() {
19241 numLimitInput.clear();
19242 numLimitInput.sendKeys('100');
19243 letterLimitInput.clear();
19244 letterLimitInput.sendKeys('100');
19245 longNumberLimitInput.clear();
19246 longNumberLimitInput.sendKeys('100');
19247 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19248 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19249 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19254 function limitToFilter() {
19255 return function(input, limit, begin) {
19256 if (Math.abs(Number(limit)) === Infinity) {
19257 limit = Number(limit);
19259 limit = toInt(limit);
19261 if (isNaN(limit)) return input;
19263 if (isNumber(input)) input = input.toString();
19264 if (!isArray(input) && !isString(input)) return input;
19266 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19267 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19270 return input.slice(begin, begin + limit);
19273 return input.slice(limit, input.length);
19275 return input.slice(Math.max(0, begin + limit), begin);
19287 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19288 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19289 * as expected, make sure they are actually being saved as numbers and not strings.
19291 * @param {Array} array The array to sort.
19292 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19293 * used by the comparator to determine the order of elements.
19297 * - `function`: Getter function. The result of this function will be sorted using the
19298 * `<`, `===`, `>` operator.
19299 * - `string`: An Angular expression. The result of this expression is used to compare elements
19300 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19301 * 3 first characters of a property called `name`). The result of a constant expression
19302 * is interpreted as a property name to be used in comparisons (for example `"special name"`
19303 * to sort object by the value of their `special name` property). An expression can be
19304 * optionally prefixed with `+` or `-` to control ascending or descending sort order
19305 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19306 * element itself is used to compare where sorting.
19307 * - `Array`: An array of function or string predicates. The first predicate in the array
19308 * is used for sorting, but when two items are equivalent, the next predicate is used.
19310 * If the predicate is missing or empty then it defaults to `'+'`.
19312 * @param {boolean=} reverse Reverse the order of the array.
19313 * @returns {Array} Sorted copy of the source array.
19317 * The example below demonstrates a simple ngRepeat, where the data is sorted
19318 * by age in descending order (predicate is set to `'-age'`).
19319 * `reverse` is not set, which means it defaults to `false`.
19320 <example module="orderByExample">
19321 <file name="index.html">
19323 angular.module('orderByExample', [])
19324 .controller('ExampleController', ['$scope', function($scope) {
19326 [{name:'John', phone:'555-1212', age:10},
19327 {name:'Mary', phone:'555-9876', age:19},
19328 {name:'Mike', phone:'555-4321', age:21},
19329 {name:'Adam', phone:'555-5678', age:35},
19330 {name:'Julie', phone:'555-8765', age:29}];
19333 <div ng-controller="ExampleController">
19334 <table class="friend">
19337 <th>Phone Number</th>
19340 <tr ng-repeat="friend in friends | orderBy:'-age'">
19341 <td>{{friend.name}}</td>
19342 <td>{{friend.phone}}</td>
19343 <td>{{friend.age}}</td>
19350 * The predicate and reverse parameters can be controlled dynamically through scope properties,
19351 * as shown in the next example.
19353 <example module="orderByExample">
19354 <file name="index.html">
19356 angular.module('orderByExample', [])
19357 .controller('ExampleController', ['$scope', function($scope) {
19359 [{name:'John', phone:'555-1212', age:10},
19360 {name:'Mary', phone:'555-9876', age:19},
19361 {name:'Mike', phone:'555-4321', age:21},
19362 {name:'Adam', phone:'555-5678', age:35},
19363 {name:'Julie', phone:'555-8765', age:29}];
19364 $scope.predicate = 'age';
19365 $scope.reverse = true;
19366 $scope.order = function(predicate) {
19367 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19368 $scope.predicate = predicate;
19372 <style type="text/css">
19376 .sortorder.reverse:after {
19380 <div ng-controller="ExampleController">
19381 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19383 [ <a href="" ng-click="predicate=''">unsorted</a> ]
19384 <table class="friend">
19387 <a href="" ng-click="order('name')">Name</a>
19388 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19391 <a href="" ng-click="order('phone')">Phone Number</a>
19392 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19395 <a href="" ng-click="order('age')">Age</a>
19396 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19399 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19400 <td>{{friend.name}}</td>
19401 <td>{{friend.phone}}</td>
19402 <td>{{friend.age}}</td>
19409 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19410 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19411 * desired parameters.
19416 <example module="orderByExample">
19417 <file name="index.html">
19418 <div ng-controller="ExampleController">
19419 <table class="friend">
19421 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19422 (<a href="" ng-click="order('-name',false)">^</a>)</th>
19423 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19424 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19426 <tr ng-repeat="friend in friends">
19427 <td>{{friend.name}}</td>
19428 <td>{{friend.phone}}</td>
19429 <td>{{friend.age}}</td>
19435 <file name="script.js">
19436 angular.module('orderByExample', [])
19437 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19438 var orderBy = $filter('orderBy');
19440 { name: 'John', phone: '555-1212', age: 10 },
19441 { name: 'Mary', phone: '555-9876', age: 19 },
19442 { name: 'Mike', phone: '555-4321', age: 21 },
19443 { name: 'Adam', phone: '555-5678', age: 35 },
19444 { name: 'Julie', phone: '555-8765', age: 29 }
19446 $scope.order = function(predicate, reverse) {
19447 $scope.friends = orderBy($scope.friends, predicate, reverse);
19449 $scope.order('-age',false);
19454 orderByFilter.$inject = ['$parse'];
19455 function orderByFilter($parse) {
19456 return function(array, sortPredicate, reverseOrder) {
19458 if (!(isArrayLike(array))) return array;
19460 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19461 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19463 var predicates = processPredicates(sortPredicate, reverseOrder);
19464 // Add a predicate at the end that evaluates to the element index. This makes the
19465 // sort stable as it works as a tie-breaker when all the input predicates cannot
19466 // distinguish between two elements.
19467 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19469 // The next three lines are a version of a Swartzian Transform idiom from Perl
19470 // (sometimes called the Decorate-Sort-Undecorate idiom)
19471 // See https://en.wikipedia.org/wiki/Schwartzian_transform
19472 var compareValues = Array.prototype.map.call(array, getComparisonObject);
19473 compareValues.sort(doComparison);
19474 array = compareValues.map(function(item) { return item.value; });
19478 function getComparisonObject(value, index) {
19481 predicateValues: predicates.map(function(predicate) {
19482 return getPredicateValue(predicate.get(value), index);
19487 function doComparison(v1, v2) {
19489 for (var index=0, length = predicates.length; index < length; ++index) {
19490 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19497 function processPredicates(sortPredicate, reverseOrder) {
19498 reverseOrder = reverseOrder ? -1 : 1;
19499 return sortPredicate.map(function(predicate) {
19500 var descending = 1, get = identity;
19502 if (isFunction(predicate)) {
19504 } else if (isString(predicate)) {
19505 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19506 descending = predicate.charAt(0) == '-' ? -1 : 1;
19507 predicate = predicate.substring(1);
19509 if (predicate !== '') {
19510 get = $parse(predicate);
19511 if (get.constant) {
19513 get = function(value) { return value[key]; };
19517 return { get: get, descending: descending * reverseOrder };
19521 function isPrimitive(value) {
19522 switch (typeof value) {
19523 case 'number': /* falls through */
19524 case 'boolean': /* falls through */
19532 function objectValue(value, index) {
19533 // If `valueOf` is a valid function use that
19534 if (typeof value.valueOf === 'function') {
19535 value = value.valueOf();
19536 if (isPrimitive(value)) return value;
19538 // If `toString` is a valid function and not the one from `Object.prototype` use that
19539 if (hasCustomToString(value)) {
19540 value = value.toString();
19541 if (isPrimitive(value)) return value;
19543 // We have a basic object so we use the position of the object in the collection
19547 function getPredicateValue(value, index) {
19548 var type = typeof value;
19549 if (value === null) {
19552 } else if (type === 'string') {
19553 value = value.toLowerCase();
19554 } else if (type === 'object') {
19555 value = objectValue(value, index);
19557 return { value: value, type: type };
19560 function compare(v1, v2) {
19562 if (v1.type === v2.type) {
19563 if (v1.value !== v2.value) {
19564 result = v1.value < v2.value ? -1 : 1;
19567 result = v1.type < v2.type ? -1 : 1;
19573 function ngDirective(directive) {
19574 if (isFunction(directive)) {
19579 directive.restrict = directive.restrict || 'AC';
19580 return valueFn(directive);
19589 * Modifies the default behavior of the html A tag so that the default action is prevented when
19590 * the href attribute is empty.
19592 * This change permits the easy creation of action links with the `ngClick` directive
19593 * without changing the location or causing page reloads, e.g.:
19594 * `<a href="" ng-click="list.addItem()">Add Item</a>`
19596 var htmlAnchorDirective = valueFn({
19598 compile: function(element, attr) {
19599 if (!attr.href && !attr.xlinkHref) {
19600 return function(scope, element) {
19601 // If the linked element is not an anchor tag anymore, do nothing
19602 if (element[0].nodeName.toLowerCase() !== 'a') return;
19604 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19605 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19606 'xlink:href' : 'href';
19607 element.on('click', function(event) {
19608 // if we have no href url, then don't navigate anywhere.
19609 if (!element.attr(href)) {
19610 event.preventDefault();
19625 * Using Angular markup like `{{hash}}` in an href attribute will
19626 * make the link go to the wrong URL if the user clicks it before
19627 * Angular has a chance to replace the `{{hash}}` markup with its
19628 * value. Until Angular replaces the markup the link will be broken
19629 * and will most likely return a 404 error. The `ngHref` directive
19630 * solves this problem.
19632 * The wrong way to write it:
19634 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19637 * The correct way to write it:
19639 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19643 * @param {template} ngHref any string which can contain `{{}}` markup.
19646 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19647 * in links and their different behaviors:
19649 <file name="index.html">
19650 <input ng-model="value" /><br />
19651 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19652 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19653 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19654 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19655 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19656 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19658 <file name="protractor.js" type="protractor">
19659 it('should execute ng-click but not reload when href without value', function() {
19660 element(by.id('link-1')).click();
19661 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19662 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19665 it('should execute ng-click but not reload when href empty string', function() {
19666 element(by.id('link-2')).click();
19667 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19668 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19671 it('should execute ng-click and change url when ng-href specified', function() {
19672 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19674 element(by.id('link-3')).click();
19676 // At this point, we navigate away from an Angular page, so we need
19677 // to use browser.driver to get the base webdriver.
19679 browser.wait(function() {
19680 return browser.driver.getCurrentUrl().then(function(url) {
19681 return url.match(/\/123$/);
19683 }, 5000, 'page should navigate to /123');
19686 it('should execute ng-click but not reload when href empty string and name specified', function() {
19687 element(by.id('link-4')).click();
19688 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19689 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19692 it('should execute ng-click but not reload when no href but name specified', function() {
19693 element(by.id('link-5')).click();
19694 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19695 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19698 it('should only change url when only ng-href', function() {
19699 element(by.model('value')).clear();
19700 element(by.model('value')).sendKeys('6');
19701 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19703 element(by.id('link-6')).click();
19705 // At this point, we navigate away from an Angular page, so we need
19706 // to use browser.driver to get the base webdriver.
19707 browser.wait(function() {
19708 return browser.driver.getCurrentUrl().then(function(url) {
19709 return url.match(/\/6$/);
19711 }, 5000, 'page should navigate to /6');
19724 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19725 * work right: The browser will fetch from the URL with the literal
19726 * text `{{hash}}` until Angular replaces the expression inside
19727 * `{{hash}}`. The `ngSrc` directive solves this problem.
19729 * The buggy way to write it:
19731 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19734 * The correct way to write it:
19736 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19740 * @param {template} ngSrc any string which can contain `{{}}` markup.
19750 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19751 * work right: The browser will fetch from the URL with the literal
19752 * text `{{hash}}` until Angular replaces the expression inside
19753 * `{{hash}}`. The `ngSrcset` directive solves this problem.
19755 * The buggy way to write it:
19757 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19760 * The correct way to write it:
19762 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19766 * @param {template} ngSrcset any string which can contain `{{}}` markup.
19777 * This directive sets the `disabled` attribute on the element if the
19778 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19780 * A special directive is necessary because we cannot use interpolation inside the `disabled`
19781 * attribute. The following example would make the button enabled on Chrome/Firefox
19782 * but not on older IEs:
19785 * <!-- See below for an example of ng-disabled being used correctly -->
19786 * <div ng-init="isDisabled = false">
19787 * <button disabled="{{isDisabled}}">Disabled</button>
19791 * This is because the HTML specification does not require browsers to preserve the values of
19792 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19793 * If we put an Angular interpolation expression into such an attribute then the
19794 * binding information would be lost when the browser removes the attribute.
19798 <file name="index.html">
19799 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19800 <button ng-model="button" ng-disabled="checked">Button</button>
19802 <file name="protractor.js" type="protractor">
19803 it('should toggle button', function() {
19804 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19805 element(by.model('checked')).click();
19806 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19812 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19813 * then the `disabled` attribute will be set on the element
19824 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19826 * Note that this directive should not be used together with {@link ngModel `ngModel`},
19827 * as this can lead to unexpected behavior.
19829 * ### Why do we need `ngChecked`?
19831 * The HTML specification does not require browsers to preserve the values of boolean attributes
19832 * such as checked. (Their presence means true and their absence means false.)
19833 * If we put an Angular interpolation expression into such an attribute then the
19834 * binding information would be lost when the browser removes the attribute.
19835 * The `ngChecked` directive solves this problem for the `checked` attribute.
19836 * This complementary directive is not removed by the browser and so provides
19837 * a permanent reliable place to store the binding information.
19840 <file name="index.html">
19841 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19842 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19844 <file name="protractor.js" type="protractor">
19845 it('should check both checkBoxes', function() {
19846 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19847 element(by.model('master')).click();
19848 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19854 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19855 * then the `checked` attribute will be set on the element
19866 * The HTML specification does not require browsers to preserve the values of boolean attributes
19867 * such as readonly. (Their presence means true and their absence means false.)
19868 * If we put an Angular interpolation expression into such an attribute then the
19869 * binding information would be lost when the browser removes the attribute.
19870 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19871 * This complementary directive is not removed by the browser and so provides
19872 * a permanent reliable place to store the binding information.
19875 <file name="index.html">
19876 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19877 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19879 <file name="protractor.js" type="protractor">
19880 it('should toggle readonly attr', function() {
19881 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19882 element(by.model('checked')).click();
19883 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19889 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19890 * then special attribute "readonly" will be set on the element
19901 * The HTML specification does not require browsers to preserve the values of boolean attributes
19902 * such as selected. (Their presence means true and their absence means false.)
19903 * If we put an Angular interpolation expression into such an attribute then the
19904 * binding information would be lost when the browser removes the attribute.
19905 * The `ngSelected` directive solves this problem for the `selected` attribute.
19906 * This complementary directive is not removed by the browser and so provides
19907 * a permanent reliable place to store the binding information.
19911 <file name="index.html">
19912 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19913 <select aria-label="ngSelected demo">
19914 <option>Hello!</option>
19915 <option id="greet" ng-selected="selected">Greetings!</option>
19918 <file name="protractor.js" type="protractor">
19919 it('should select Greetings!', function() {
19920 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
19921 element(by.model('selected')).click();
19922 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
19928 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
19929 * then special attribute "selected" will be set on the element
19939 * The HTML specification does not require browsers to preserve the values of boolean attributes
19940 * such as open. (Their presence means true and their absence means false.)
19941 * If we put an Angular interpolation expression into such an attribute then the
19942 * binding information would be lost when the browser removes the attribute.
19943 * The `ngOpen` directive solves this problem for the `open` attribute.
19944 * This complementary directive is not removed by the browser and so provides
19945 * a permanent reliable place to store the binding information.
19948 <file name="index.html">
19949 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
19950 <details id="details" ng-open="open">
19951 <summary>Show/Hide me</summary>
19954 <file name="protractor.js" type="protractor">
19955 it('should toggle open', function() {
19956 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
19957 element(by.model('open')).click();
19958 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
19964 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
19965 * then special attribute "open" will be set on the element
19968 var ngAttributeAliasDirectives = {};
19970 // boolean attrs are evaluated
19971 forEach(BOOLEAN_ATTR, function(propName, attrName) {
19972 // binding to multiple is not supported
19973 if (propName == "multiple") return;
19975 function defaultLinkFn(scope, element, attr) {
19976 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
19977 attr.$set(attrName, !!value);
19981 var normalized = directiveNormalize('ng-' + attrName);
19982 var linkFn = defaultLinkFn;
19984 if (propName === 'checked') {
19985 linkFn = function(scope, element, attr) {
19986 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
19987 if (attr.ngModel !== attr[normalized]) {
19988 defaultLinkFn(scope, element, attr);
19993 ngAttributeAliasDirectives[normalized] = function() {
20002 // aliased input attrs are evaluated
20003 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20004 ngAttributeAliasDirectives[ngAttr] = function() {
20007 link: function(scope, element, attr) {
20008 //special case ngPattern when a literal regular expression value
20009 //is used as the expression (this way we don't have to watch anything).
20010 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20011 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20013 attr.$set("ngPattern", new RegExp(match[1], match[2]));
20018 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20019 attr.$set(ngAttr, value);
20026 // ng-src, ng-srcset, ng-href are interpolated
20027 forEach(['src', 'srcset', 'href'], function(attrName) {
20028 var normalized = directiveNormalize('ng-' + attrName);
20029 ngAttributeAliasDirectives[normalized] = function() {
20031 priority: 99, // it needs to run after the attributes are interpolated
20032 link: function(scope, element, attr) {
20033 var propName = attrName,
20036 if (attrName === 'href' &&
20037 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20038 name = 'xlinkHref';
20039 attr.$attr[name] = 'xlink:href';
20043 attr.$observe(normalized, function(value) {
20045 if (attrName === 'href') {
20046 attr.$set(name, null);
20051 attr.$set(name, value);
20053 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20054 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20055 // to set the property as well to achieve the desired effect.
20056 // we use attr[attrName] value since $set can sanitize the url.
20057 if (msie && propName) element.prop(propName, attr[name]);
20064 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20066 var nullFormCtrl = {
20068 $$renameControl: nullFormRenameControl,
20069 $removeControl: noop,
20070 $setValidity: noop,
20072 $setPristine: noop,
20073 $setSubmitted: noop
20075 SUBMITTED_CLASS = 'ng-submitted';
20077 function nullFormRenameControl(control, name) {
20078 control.$name = name;
20083 * @name form.FormController
20085 * @property {boolean} $pristine True if user has not interacted with the form yet.
20086 * @property {boolean} $dirty True if user has already interacted with the form.
20087 * @property {boolean} $valid True if all of the containing forms and controls are valid.
20088 * @property {boolean} $invalid True if at least one containing control or form is invalid.
20089 * @property {boolean} $pending True if at least one containing control or form is pending.
20090 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20092 * @property {Object} $error Is an object hash, containing references to controls or
20093 * forms with failing validators, where:
20095 * - keys are validation tokens (error names),
20096 * - values are arrays of controls or forms that have a failing validator for given error name.
20098 * Built-in validation tokens:
20110 * - `datetimelocal`
20116 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20117 * such as being valid/invalid or dirty/pristine.
20119 * Each {@link ng.directive:form form} directive creates an instance
20120 * of `FormController`.
20123 //asks for $scope to fool the BC controller module
20124 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20125 function FormController(element, attrs, $scope, $animate, $interpolate) {
20131 form.$$success = {};
20132 form.$pending = undefined;
20133 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20134 form.$dirty = false;
20135 form.$pristine = true;
20136 form.$valid = true;
20137 form.$invalid = false;
20138 form.$submitted = false;
20139 form.$$parentForm = nullFormCtrl;
20143 * @name form.FormController#$rollbackViewValue
20146 * Rollback all form controls pending updates to the `$modelValue`.
20148 * Updates may be pending by a debounced event or because the input is waiting for a some future
20149 * event defined in `ng-model-options`. This method is typically needed by the reset button of
20150 * a form that uses `ng-model-options` to pend updates.
20152 form.$rollbackViewValue = function() {
20153 forEach(controls, function(control) {
20154 control.$rollbackViewValue();
20160 * @name form.FormController#$commitViewValue
20163 * Commit all form controls pending updates to the `$modelValue`.
20165 * Updates may be pending by a debounced event or because the input is waiting for a some future
20166 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20167 * usually handles calling this in response to input events.
20169 form.$commitViewValue = function() {
20170 forEach(controls, function(control) {
20171 control.$commitViewValue();
20177 * @name form.FormController#$addControl
20178 * @param {object} control control object, either a {@link form.FormController} or an
20179 * {@link ngModel.NgModelController}
20182 * Register a control with the form. Input elements using ngModelController do this automatically
20183 * when they are linked.
20185 * Note that the current state of the control will not be reflected on the new parent form. This
20186 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20189 * However, if the method is used programmatically, for example by adding dynamically created controls,
20190 * or controls that have been previously removed without destroying their corresponding DOM element,
20191 * it's the developers responsiblity to make sure the current state propagates to the parent form.
20193 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20194 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20196 form.$addControl = function(control) {
20197 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20198 // and not added to the scope. Now we throw an error.
20199 assertNotHasOwnProperty(control.$name, 'input');
20200 controls.push(control);
20202 if (control.$name) {
20203 form[control.$name] = control;
20206 control.$$parentForm = form;
20209 // Private API: rename a form control
20210 form.$$renameControl = function(control, newName) {
20211 var oldName = control.$name;
20213 if (form[oldName] === control) {
20214 delete form[oldName];
20216 form[newName] = control;
20217 control.$name = newName;
20222 * @name form.FormController#$removeControl
20223 * @param {object} control control object, either a {@link form.FormController} or an
20224 * {@link ngModel.NgModelController}
20227 * Deregister a control from the form.
20229 * Input elements using ngModelController do this automatically when they are destroyed.
20231 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20232 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20233 * different from case to case. For example, removing the only `$dirty` control from a form may or
20234 * may not mean that the form is still `$dirty`.
20236 form.$removeControl = function(control) {
20237 if (control.$name && form[control.$name] === control) {
20238 delete form[control.$name];
20240 forEach(form.$pending, function(value, name) {
20241 form.$setValidity(name, null, control);
20243 forEach(form.$error, function(value, name) {
20244 form.$setValidity(name, null, control);
20246 forEach(form.$$success, function(value, name) {
20247 form.$setValidity(name, null, control);
20250 arrayRemove(controls, control);
20251 control.$$parentForm = nullFormCtrl;
20257 * @name form.FormController#$setValidity
20260 * Sets the validity of a form control.
20262 * This method will also propagate to parent forms.
20264 addSetValidityMethod({
20267 set: function(object, property, controller) {
20268 var list = object[property];
20270 object[property] = [controller];
20272 var index = list.indexOf(controller);
20273 if (index === -1) {
20274 list.push(controller);
20278 unset: function(object, property, controller) {
20279 var list = object[property];
20283 arrayRemove(list, controller);
20284 if (list.length === 0) {
20285 delete object[property];
20293 * @name form.FormController#$setDirty
20296 * Sets the form to a dirty state.
20298 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20299 * state (ng-dirty class). This method will also propagate to parent forms.
20301 form.$setDirty = function() {
20302 $animate.removeClass(element, PRISTINE_CLASS);
20303 $animate.addClass(element, DIRTY_CLASS);
20304 form.$dirty = true;
20305 form.$pristine = false;
20306 form.$$parentForm.$setDirty();
20311 * @name form.FormController#$setPristine
20314 * Sets the form to its pristine state.
20316 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20317 * state (ng-pristine class). This method will also propagate to all the controls contained
20320 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20321 * saving or resetting it.
20323 form.$setPristine = function() {
20324 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20325 form.$dirty = false;
20326 form.$pristine = true;
20327 form.$submitted = false;
20328 forEach(controls, function(control) {
20329 control.$setPristine();
20335 * @name form.FormController#$setUntouched
20338 * Sets the form to its untouched state.
20340 * This method can be called to remove the 'ng-touched' class and set the form controls to their
20341 * untouched state (ng-untouched class).
20343 * Setting a form controls back to their untouched state is often useful when setting the form
20344 * back to its pristine state.
20346 form.$setUntouched = function() {
20347 forEach(controls, function(control) {
20348 control.$setUntouched();
20354 * @name form.FormController#$setSubmitted
20357 * Sets the form to its submitted state.
20359 form.$setSubmitted = function() {
20360 $animate.addClass(element, SUBMITTED_CLASS);
20361 form.$submitted = true;
20362 form.$$parentForm.$setSubmitted();
20372 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20373 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20374 * sub-group of controls needs to be determined.
20376 * Note: the purpose of `ngForm` is to group controls,
20377 * but not to be a replacement for the `<form>` tag with all of its capabilities
20378 * (e.g. posting to the server, ...).
20380 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20381 * related scope, under this name.
20391 * Directive that instantiates
20392 * {@link form.FormController FormController}.
20394 * If the `name` attribute is specified, the form controller is published onto the current scope under
20397 * # Alias: {@link ng.directive:ngForm `ngForm`}
20399 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20400 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20401 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20402 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
20403 * using Angular validation directives in forms that are dynamically generated using the
20404 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20405 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20406 * `ngForm` directive and nest these in an outer `form` element.
20410 * - `ng-valid` is set if the form is valid.
20411 * - `ng-invalid` is set if the form is invalid.
20412 * - `ng-pending` is set if the form is pending.
20413 * - `ng-pristine` is set if the form is pristine.
20414 * - `ng-dirty` is set if the form is dirty.
20415 * - `ng-submitted` is set if the form was submitted.
20417 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20420 * # Submitting a form and preventing the default action
20422 * Since the role of forms in client-side Angular applications is different than in classical
20423 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20424 * page reload that sends the data to the server. Instead some javascript logic should be triggered
20425 * to handle the form submission in an application-specific way.
20427 * For this reason, Angular prevents the default action (form submission to the server) unless the
20428 * `<form>` element has an `action` attribute specified.
20430 * You can use one of the following two ways to specify what javascript method should be called when
20431 * a form is submitted:
20433 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20434 * - {@link ng.directive:ngClick ngClick} directive on the first
20435 * button or input field of type submit (input[type=submit])
20437 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20438 * or {@link ng.directive:ngClick ngClick} directives.
20439 * This is because of the following form submission rules in the HTML specification:
20441 * - If a form has only one input field then hitting enter in this field triggers form submit
20443 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20444 * doesn't trigger submit
20445 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20446 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20447 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20449 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20450 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20451 * to have access to the updated model.
20453 * ## Animation Hooks
20455 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20456 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20457 * other validations that are performed within the form. Animations in ngForm are similar to how
20458 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20459 * as JS animations.
20461 * The following example shows a simple way to utilize CSS transitions to style a form element
20462 * that has been rendered as invalid after it has been validated:
20465 * //be sure to include ngAnimate as a module to hook into more
20466 * //advanced animations
20468 * transition:0.5s linear all;
20469 * background: white;
20471 * .my-form.ng-invalid {
20478 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20479 <file name="index.html">
20481 angular.module('formExample', [])
20482 .controller('FormController', ['$scope', function($scope) {
20483 $scope.userType = 'guest';
20488 transition:all linear 0.5s;
20489 background: transparent;
20491 .my-form.ng-invalid {
20495 <form name="myForm" ng-controller="FormController" class="my-form">
20496 userType: <input name="input" ng-model="userType" required>
20497 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20498 <code>userType = {{userType}}</code><br>
20499 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20500 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20501 <code>myForm.$valid = {{myForm.$valid}}</code><br>
20502 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20505 <file name="protractor.js" type="protractor">
20506 it('should initialize to model', function() {
20507 var userType = element(by.binding('userType'));
20508 var valid = element(by.binding('myForm.input.$valid'));
20510 expect(userType.getText()).toContain('guest');
20511 expect(valid.getText()).toContain('true');
20514 it('should be invalid if empty', function() {
20515 var userType = element(by.binding('userType'));
20516 var valid = element(by.binding('myForm.input.$valid'));
20517 var userInput = element(by.model('userType'));
20520 userInput.sendKeys('');
20522 expect(userType.getText()).toEqual('userType =');
20523 expect(valid.getText()).toContain('false');
20528 * @param {string=} name Name of the form. If specified, the form controller will be published into
20529 * related scope, under this name.
20531 var formDirectiveFactory = function(isNgForm) {
20532 return ['$timeout', '$parse', function($timeout, $parse) {
20533 var formDirective = {
20535 restrict: isNgForm ? 'EAC' : 'E',
20536 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20537 controller: FormController,
20538 compile: function ngFormCompile(formElement, attr) {
20539 // Setup initial state of the control
20540 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20542 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20545 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20546 var controller = ctrls[0];
20548 // if `action` attr is not present on the form, prevent the default action (submission)
20549 if (!('action' in attr)) {
20550 // we can't use jq events because if a form is destroyed during submission the default
20551 // action is not prevented. see #1238
20553 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20554 // page reload if the form was destroyed by submission of the form via a click handler
20555 // on a button in the form. Looks like an IE9 specific bug.
20556 var handleFormSubmission = function(event) {
20557 scope.$apply(function() {
20558 controller.$commitViewValue();
20559 controller.$setSubmitted();
20562 event.preventDefault();
20565 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20567 // unregister the preventDefault listener so that we don't not leak memory but in a
20568 // way that will achieve the prevention of the default action.
20569 formElement.on('$destroy', function() {
20570 $timeout(function() {
20571 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20576 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20577 parentFormCtrl.$addControl(controller);
20579 var setter = nameAttr ? getSetter(controller.$name) : noop;
20582 setter(scope, controller);
20583 attr.$observe(nameAttr, function(newValue) {
20584 if (controller.$name === newValue) return;
20585 setter(scope, undefined);
20586 controller.$$parentForm.$$renameControl(controller, newValue);
20587 setter = getSetter(controller.$name);
20588 setter(scope, controller);
20591 formElement.on('$destroy', function() {
20592 controller.$$parentForm.$removeControl(controller);
20593 setter(scope, undefined);
20594 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20601 return formDirective;
20603 function getSetter(expression) {
20604 if (expression === '') {
20605 //create an assignable expression, so forms with an empty name can be renamed later
20606 return $parse('this[""]').assign;
20608 return $parse(expression).assign || noop;
20613 var formDirective = formDirectiveFactory();
20614 var ngFormDirective = formDirectiveFactory(true);
20616 /* global VALID_CLASS: false,
20617 INVALID_CLASS: false,
20618 PRISTINE_CLASS: false,
20619 DIRTY_CLASS: false,
20620 UNTOUCHED_CLASS: false,
20621 TOUCHED_CLASS: false,
20622 ngModelMinErr: false,
20625 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20626 var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
20627 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20628 var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
20629 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;
20630 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20631 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20632 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20633 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20634 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20635 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20641 * @name input[text]
20644 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20647 * @param {string} ngModel Assignable angular expression to data-bind to.
20648 * @param {string=} name Property name of the form under which the control is published.
20649 * @param {string=} required Adds `required` validation error key if the value is not entered.
20650 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20651 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20652 * `required` when you want to data-bind to the `required` attribute.
20653 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20655 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20656 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20658 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20659 * that contains the regular expression body that will be converted to a regular expression
20660 * as in the ngPattern directive.
20661 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20662 * a RegExp found by evaluating the Angular expression given in the attribute value.
20663 * If the expression evaluates to a RegExp object, then this is used directly.
20664 * If the expression evaluates to a string, then it will be converted to a RegExp
20665 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20666 * `new RegExp('^abc$')`.<br />
20667 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20668 * start at the index of the last search's match, thus not taking the whole input value into
20670 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20671 * interaction with the input element.
20672 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20673 * This parameter is ignored for input[type=password] controls, which will never trim the
20677 <example name="text-input-directive" module="textInputExample">
20678 <file name="index.html">
20680 angular.module('textInputExample', [])
20681 .controller('ExampleController', ['$scope', function($scope) {
20684 word: /^\s*\w*\s*$/
20688 <form name="myForm" ng-controller="ExampleController">
20689 <label>Single word:
20690 <input type="text" name="input" ng-model="example.text"
20691 ng-pattern="example.word" required ng-trim="false">
20694 <span class="error" ng-show="myForm.input.$error.required">
20696 <span class="error" ng-show="myForm.input.$error.pattern">
20697 Single word only!</span>
20699 <tt>text = {{example.text}}</tt><br/>
20700 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20701 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20702 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20703 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20706 <file name="protractor.js" type="protractor">
20707 var text = element(by.binding('example.text'));
20708 var valid = element(by.binding('myForm.input.$valid'));
20709 var input = element(by.model('example.text'));
20711 it('should initialize to model', function() {
20712 expect(text.getText()).toContain('guest');
20713 expect(valid.getText()).toContain('true');
20716 it('should be invalid if empty', function() {
20718 input.sendKeys('');
20720 expect(text.getText()).toEqual('text =');
20721 expect(valid.getText()).toContain('false');
20724 it('should be invalid if multi word', function() {
20726 input.sendKeys('hello world');
20728 expect(valid.getText()).toContain('false');
20733 'text': textInputType,
20737 * @name input[date]
20740 * Input with date validation and transformation. In browsers that do not yet support
20741 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20742 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20743 * modern browsers do not yet support this input type, it is important to provide cues to users on the
20744 * expected input format via a placeholder or label.
20746 * The model must always be a Date object, otherwise Angular will throw an error.
20747 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20749 * The timezone to be used to read/write the `Date` instance in the model can be defined using
20750 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20752 * @param {string} ngModel Assignable angular expression to data-bind to.
20753 * @param {string=} name Property name of the form under which the control is published.
20754 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20755 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20756 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20757 * constraint validation.
20758 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20759 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20760 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20761 * constraint validation.
20762 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20763 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20764 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20765 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20766 * @param {string=} required Sets `required` validation error key if the value is not entered.
20767 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20768 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20769 * `required` when you want to data-bind to the `required` attribute.
20770 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20771 * interaction with the input element.
20774 <example name="date-input-directive" module="dateInputExample">
20775 <file name="index.html">
20777 angular.module('dateInputExample', [])
20778 .controller('DateController', ['$scope', function($scope) {
20780 value: new Date(2013, 9, 22)
20784 <form name="myForm" ng-controller="DateController as dateCtrl">
20785 <label for="exampleInput">Pick a date in 2013:</label>
20786 <input type="date" id="exampleInput" name="input" ng-model="example.value"
20787 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20789 <span class="error" ng-show="myForm.input.$error.required">
20791 <span class="error" ng-show="myForm.input.$error.date">
20792 Not a valid date!</span>
20794 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20795 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20796 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20797 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20798 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20801 <file name="protractor.js" type="protractor">
20802 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20803 var valid = element(by.binding('myForm.input.$valid'));
20804 var input = element(by.model('example.value'));
20806 // currently protractor/webdriver does not support
20807 // sending keys to all known HTML5 input controls
20808 // for various browsers (see https://github.com/angular/protractor/issues/562).
20809 function setInput(val) {
20810 // set the value of the element and force validation.
20811 var scr = "var ipt = document.getElementById('exampleInput'); " +
20812 "ipt.value = '" + val + "';" +
20813 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20814 browser.executeScript(scr);
20817 it('should initialize to model', function() {
20818 expect(value.getText()).toContain('2013-10-22');
20819 expect(valid.getText()).toContain('myForm.input.$valid = true');
20822 it('should be invalid if empty', function() {
20824 expect(value.getText()).toEqual('value =');
20825 expect(valid.getText()).toContain('myForm.input.$valid = false');
20828 it('should be invalid if over max', function() {
20829 setInput('2015-01-01');
20830 expect(value.getText()).toContain('');
20831 expect(valid.getText()).toContain('myForm.input.$valid = false');
20836 'date': createDateInputType('date', DATE_REGEXP,
20837 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20842 * @name input[datetime-local]
20845 * Input with datetime validation and transformation. In browsers that do not yet support
20846 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20847 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20849 * The model must always be a Date object, otherwise Angular will throw an error.
20850 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20852 * The timezone to be used to read/write the `Date` instance in the model can be defined using
20853 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20855 * @param {string} ngModel Assignable angular expression to data-bind to.
20856 * @param {string=} name Property name of the form under which the control is published.
20857 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20858 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20859 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20860 * Note that `min` will also add native HTML5 constraint validation.
20861 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20862 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20863 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20864 * Note that `max` will also add native HTML5 constraint validation.
20865 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20866 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20867 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20868 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20869 * @param {string=} required Sets `required` validation error key if the value is not entered.
20870 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20871 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20872 * `required` when you want to data-bind to the `required` attribute.
20873 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20874 * interaction with the input element.
20877 <example name="datetimelocal-input-directive" module="dateExample">
20878 <file name="index.html">
20880 angular.module('dateExample', [])
20881 .controller('DateController', ['$scope', function($scope) {
20883 value: new Date(2010, 11, 28, 14, 57)
20887 <form name="myForm" ng-controller="DateController as dateCtrl">
20888 <label for="exampleInput">Pick a date between in 2013:</label>
20889 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20890 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20892 <span class="error" ng-show="myForm.input.$error.required">
20894 <span class="error" ng-show="myForm.input.$error.datetimelocal">
20895 Not a valid date!</span>
20897 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20898 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20899 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20900 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20901 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20904 <file name="protractor.js" type="protractor">
20905 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20906 var valid = element(by.binding('myForm.input.$valid'));
20907 var input = element(by.model('example.value'));
20909 // currently protractor/webdriver does not support
20910 // sending keys to all known HTML5 input controls
20911 // for various browsers (https://github.com/angular/protractor/issues/562).
20912 function setInput(val) {
20913 // set the value of the element and force validation.
20914 var scr = "var ipt = document.getElementById('exampleInput'); " +
20915 "ipt.value = '" + val + "';" +
20916 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20917 browser.executeScript(scr);
20920 it('should initialize to model', function() {
20921 expect(value.getText()).toContain('2010-12-28T14:57:00');
20922 expect(valid.getText()).toContain('myForm.input.$valid = true');
20925 it('should be invalid if empty', function() {
20927 expect(value.getText()).toEqual('value =');
20928 expect(valid.getText()).toContain('myForm.input.$valid = false');
20931 it('should be invalid if over max', function() {
20932 setInput('2015-01-01T23:59:00');
20933 expect(value.getText()).toContain('');
20934 expect(valid.getText()).toContain('myForm.input.$valid = false');
20939 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
20940 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
20941 'yyyy-MM-ddTHH:mm:ss.sss'),
20945 * @name input[time]
20948 * Input with time validation and transformation. In browsers that do not yet support
20949 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20950 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
20951 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
20953 * The model must always be a Date object, otherwise Angular will throw an error.
20954 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20956 * The timezone to be used to read/write the `Date` instance in the model can be defined using
20957 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20959 * @param {string} ngModel Assignable angular expression to data-bind to.
20960 * @param {string=} name Property name of the form under which the control is published.
20961 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20962 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
20963 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
20964 * native HTML5 constraint validation.
20965 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20966 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
20967 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
20968 * native HTML5 constraint validation.
20969 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
20970 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20971 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
20972 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20973 * @param {string=} required Sets `required` validation error key if the value is not entered.
20974 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20975 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20976 * `required` when you want to data-bind to the `required` attribute.
20977 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20978 * interaction with the input element.
20981 <example name="time-input-directive" module="timeExample">
20982 <file name="index.html">
20984 angular.module('timeExample', [])
20985 .controller('DateController', ['$scope', function($scope) {
20987 value: new Date(1970, 0, 1, 14, 57, 0)
20991 <form name="myForm" ng-controller="DateController as dateCtrl">
20992 <label for="exampleInput">Pick a between 8am and 5pm:</label>
20993 <input type="time" id="exampleInput" name="input" ng-model="example.value"
20994 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
20996 <span class="error" ng-show="myForm.input.$error.required">
20998 <span class="error" ng-show="myForm.input.$error.time">
20999 Not a valid date!</span>
21001 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21002 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21003 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21004 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21005 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21008 <file name="protractor.js" type="protractor">
21009 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21010 var valid = element(by.binding('myForm.input.$valid'));
21011 var input = element(by.model('example.value'));
21013 // currently protractor/webdriver does not support
21014 // sending keys to all known HTML5 input controls
21015 // for various browsers (https://github.com/angular/protractor/issues/562).
21016 function setInput(val) {
21017 // set the value of the element and force validation.
21018 var scr = "var ipt = document.getElementById('exampleInput'); " +
21019 "ipt.value = '" + val + "';" +
21020 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21021 browser.executeScript(scr);
21024 it('should initialize to model', function() {
21025 expect(value.getText()).toContain('14:57:00');
21026 expect(valid.getText()).toContain('myForm.input.$valid = true');
21029 it('should be invalid if empty', function() {
21031 expect(value.getText()).toEqual('value =');
21032 expect(valid.getText()).toContain('myForm.input.$valid = false');
21035 it('should be invalid if over max', function() {
21036 setInput('23:59:00');
21037 expect(value.getText()).toContain('');
21038 expect(valid.getText()).toContain('myForm.input.$valid = false');
21043 'time': createDateInputType('time', TIME_REGEXP,
21044 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21049 * @name input[week]
21052 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21053 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21054 * week format (yyyy-W##), for example: `2013-W02`.
21056 * The model must always be a Date object, otherwise Angular will throw an error.
21057 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21059 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21060 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21062 * @param {string} ngModel Assignable angular expression to data-bind to.
21063 * @param {string=} name Property name of the form under which the control is published.
21064 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21065 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21066 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21067 * native HTML5 constraint validation.
21068 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21069 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21070 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21071 * native HTML5 constraint validation.
21072 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21073 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21074 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21075 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21076 * @param {string=} required Sets `required` validation error key if the value is not entered.
21077 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21078 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21079 * `required` when you want to data-bind to the `required` attribute.
21080 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21081 * interaction with the input element.
21084 <example name="week-input-directive" module="weekExample">
21085 <file name="index.html">
21087 angular.module('weekExample', [])
21088 .controller('DateController', ['$scope', function($scope) {
21090 value: new Date(2013, 0, 3)
21094 <form name="myForm" ng-controller="DateController as dateCtrl">
21095 <label>Pick a date between in 2013:
21096 <input id="exampleInput" type="week" name="input" ng-model="example.value"
21097 placeholder="YYYY-W##" min="2012-W32"
21098 max="2013-W52" required />
21101 <span class="error" ng-show="myForm.input.$error.required">
21103 <span class="error" ng-show="myForm.input.$error.week">
21104 Not a valid date!</span>
21106 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21107 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21108 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21109 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21110 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21113 <file name="protractor.js" type="protractor">
21114 var value = element(by.binding('example.value | date: "yyyy-Www"'));
21115 var valid = element(by.binding('myForm.input.$valid'));
21116 var input = element(by.model('example.value'));
21118 // currently protractor/webdriver does not support
21119 // sending keys to all known HTML5 input controls
21120 // for various browsers (https://github.com/angular/protractor/issues/562).
21121 function setInput(val) {
21122 // set the value of the element and force validation.
21123 var scr = "var ipt = document.getElementById('exampleInput'); " +
21124 "ipt.value = '" + val + "';" +
21125 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21126 browser.executeScript(scr);
21129 it('should initialize to model', function() {
21130 expect(value.getText()).toContain('2013-W01');
21131 expect(valid.getText()).toContain('myForm.input.$valid = true');
21134 it('should be invalid if empty', function() {
21136 expect(value.getText()).toEqual('value =');
21137 expect(valid.getText()).toContain('myForm.input.$valid = false');
21140 it('should be invalid if over max', function() {
21141 setInput('2015-W01');
21142 expect(value.getText()).toContain('');
21143 expect(valid.getText()).toContain('myForm.input.$valid = false');
21148 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21152 * @name input[month]
21155 * Input with month validation and transformation. In browsers that do not yet support
21156 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21157 * month format (yyyy-MM), for example: `2009-01`.
21159 * The model must always be a Date object, otherwise Angular will throw an error.
21160 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21161 * If the model is not set to the first of the month, the next view to model update will set it
21162 * to the first of the month.
21164 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21165 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21167 * @param {string} ngModel Assignable angular expression to data-bind to.
21168 * @param {string=} name Property name of the form under which the control is published.
21169 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21170 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21171 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21172 * native HTML5 constraint validation.
21173 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21174 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21175 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21176 * native HTML5 constraint validation.
21177 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21178 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21179 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21180 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21182 * @param {string=} required Sets `required` validation error key if the value is not entered.
21183 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21184 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21185 * `required` when you want to data-bind to the `required` attribute.
21186 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21187 * interaction with the input element.
21190 <example name="month-input-directive" module="monthExample">
21191 <file name="index.html">
21193 angular.module('monthExample', [])
21194 .controller('DateController', ['$scope', function($scope) {
21196 value: new Date(2013, 9, 1)
21200 <form name="myForm" ng-controller="DateController as dateCtrl">
21201 <label for="exampleInput">Pick a month in 2013:</label>
21202 <input id="exampleInput" type="month" name="input" ng-model="example.value"
21203 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21205 <span class="error" ng-show="myForm.input.$error.required">
21207 <span class="error" ng-show="myForm.input.$error.month">
21208 Not a valid month!</span>
21210 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21211 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21212 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21213 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21214 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21217 <file name="protractor.js" type="protractor">
21218 var value = element(by.binding('example.value | date: "yyyy-MM"'));
21219 var valid = element(by.binding('myForm.input.$valid'));
21220 var input = element(by.model('example.value'));
21222 // currently protractor/webdriver does not support
21223 // sending keys to all known HTML5 input controls
21224 // for various browsers (https://github.com/angular/protractor/issues/562).
21225 function setInput(val) {
21226 // set the value of the element and force validation.
21227 var scr = "var ipt = document.getElementById('exampleInput'); " +
21228 "ipt.value = '" + val + "';" +
21229 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21230 browser.executeScript(scr);
21233 it('should initialize to model', function() {
21234 expect(value.getText()).toContain('2013-10');
21235 expect(valid.getText()).toContain('myForm.input.$valid = true');
21238 it('should be invalid if empty', function() {
21240 expect(value.getText()).toEqual('value =');
21241 expect(valid.getText()).toContain('myForm.input.$valid = false');
21244 it('should be invalid if over max', function() {
21245 setInput('2015-01');
21246 expect(value.getText()).toContain('');
21247 expect(valid.getText()).toContain('myForm.input.$valid = false');
21252 'month': createDateInputType('month', MONTH_REGEXP,
21253 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21258 * @name input[number]
21261 * Text input with number validation and transformation. Sets the `number` validation
21262 * error if not a valid number.
21264 * <div class="alert alert-warning">
21265 * The model must always be of type `number` otherwise Angular will throw an error.
21266 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21267 * error docs for more information and an example of how to convert your model if necessary.
21270 * ## Issues with HTML5 constraint validation
21272 * In browsers that follow the
21273 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21274 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21275 * If a non-number is entered in the input, the browser will report the value as an empty string,
21276 * which means the view / model values in `ngModel` and subsequently the scope value
21277 * will also be an empty string.
21280 * @param {string} ngModel Assignable angular expression to data-bind to.
21281 * @param {string=} name Property name of the form under which the control is published.
21282 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21283 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21284 * @param {string=} required Sets `required` validation error key if the value is not entered.
21285 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21286 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21287 * `required` when you want to data-bind to the `required` attribute.
21288 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21290 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21291 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21293 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21294 * that contains the regular expression body that will be converted to a regular expression
21295 * as in the ngPattern directive.
21296 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21297 * a RegExp found by evaluating the Angular expression given in the attribute value.
21298 * If the expression evaluates to a RegExp object, then this is used directly.
21299 * If the expression evaluates to a string, then it will be converted to a RegExp
21300 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21301 * `new RegExp('^abc$')`.<br />
21302 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21303 * start at the index of the last search's match, thus not taking the whole input value into
21305 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21306 * interaction with the input element.
21309 <example name="number-input-directive" module="numberExample">
21310 <file name="index.html">
21312 angular.module('numberExample', [])
21313 .controller('ExampleController', ['$scope', function($scope) {
21319 <form name="myForm" ng-controller="ExampleController">
21321 <input type="number" name="input" ng-model="example.value"
21322 min="0" max="99" required>
21325 <span class="error" ng-show="myForm.input.$error.required">
21327 <span class="error" ng-show="myForm.input.$error.number">
21328 Not valid number!</span>
21330 <tt>value = {{example.value}}</tt><br/>
21331 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21332 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21333 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21334 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21337 <file name="protractor.js" type="protractor">
21338 var value = element(by.binding('example.value'));
21339 var valid = element(by.binding('myForm.input.$valid'));
21340 var input = element(by.model('example.value'));
21342 it('should initialize to model', function() {
21343 expect(value.getText()).toContain('12');
21344 expect(valid.getText()).toContain('true');
21347 it('should be invalid if empty', function() {
21349 input.sendKeys('');
21350 expect(value.getText()).toEqual('value =');
21351 expect(valid.getText()).toContain('false');
21354 it('should be invalid if over max', function() {
21356 input.sendKeys('123');
21357 expect(value.getText()).toEqual('value =');
21358 expect(valid.getText()).toContain('false');
21363 'number': numberInputType,
21371 * Text input with URL validation. Sets the `url` validation error key if the content is not a
21374 * <div class="alert alert-warning">
21375 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21376 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21377 * the built-in validators (see the {@link guide/forms Forms guide})
21380 * @param {string} ngModel Assignable angular expression to data-bind to.
21381 * @param {string=} name Property name of the form under which the control is published.
21382 * @param {string=} required Sets `required` validation error key if the value is not entered.
21383 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21384 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21385 * `required` when you want to data-bind to the `required` attribute.
21386 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21388 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21389 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21391 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21392 * that contains the regular expression body that will be converted to a regular expression
21393 * as in the ngPattern directive.
21394 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21395 * a RegExp found by evaluating the Angular expression given in the attribute value.
21396 * If the expression evaluates to a RegExp object, then this is used directly.
21397 * If the expression evaluates to a string, then it will be converted to a RegExp
21398 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21399 * `new RegExp('^abc$')`.<br />
21400 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21401 * start at the index of the last search's match, thus not taking the whole input value into
21403 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21404 * interaction with the input element.
21407 <example name="url-input-directive" module="urlExample">
21408 <file name="index.html">
21410 angular.module('urlExample', [])
21411 .controller('ExampleController', ['$scope', function($scope) {
21413 text: 'http://google.com'
21417 <form name="myForm" ng-controller="ExampleController">
21419 <input type="url" name="input" ng-model="url.text" required>
21422 <span class="error" ng-show="myForm.input.$error.required">
21424 <span class="error" ng-show="myForm.input.$error.url">
21425 Not valid url!</span>
21427 <tt>text = {{url.text}}</tt><br/>
21428 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21429 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21430 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21431 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21432 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21435 <file name="protractor.js" type="protractor">
21436 var text = element(by.binding('url.text'));
21437 var valid = element(by.binding('myForm.input.$valid'));
21438 var input = element(by.model('url.text'));
21440 it('should initialize to model', function() {
21441 expect(text.getText()).toContain('http://google.com');
21442 expect(valid.getText()).toContain('true');
21445 it('should be invalid if empty', function() {
21447 input.sendKeys('');
21449 expect(text.getText()).toEqual('text =');
21450 expect(valid.getText()).toContain('false');
21453 it('should be invalid if not url', function() {
21455 input.sendKeys('box');
21457 expect(valid.getText()).toContain('false');
21462 'url': urlInputType,
21467 * @name input[email]
21470 * Text input with email validation. Sets the `email` validation error key if not a valid email
21473 * <div class="alert alert-warning">
21474 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21475 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21476 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21479 * @param {string} ngModel Assignable angular expression to data-bind to.
21480 * @param {string=} name Property name of the form under which the control is published.
21481 * @param {string=} required Sets `required` validation error key if the value is not entered.
21482 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21483 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21484 * `required` when you want to data-bind to the `required` attribute.
21485 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21487 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21488 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21490 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21491 * that contains the regular expression body that will be converted to a regular expression
21492 * as in the ngPattern directive.
21493 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21494 * a RegExp found by evaluating the Angular expression given in the attribute value.
21495 * If the expression evaluates to a RegExp object, then this is used directly.
21496 * If the expression evaluates to a string, then it will be converted to a RegExp
21497 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21498 * `new RegExp('^abc$')`.<br />
21499 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21500 * start at the index of the last search's match, thus not taking the whole input value into
21502 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21503 * interaction with the input element.
21506 <example name="email-input-directive" module="emailExample">
21507 <file name="index.html">
21509 angular.module('emailExample', [])
21510 .controller('ExampleController', ['$scope', function($scope) {
21512 text: 'me@example.com'
21516 <form name="myForm" ng-controller="ExampleController">
21518 <input type="email" name="input" ng-model="email.text" required>
21521 <span class="error" ng-show="myForm.input.$error.required">
21523 <span class="error" ng-show="myForm.input.$error.email">
21524 Not valid email!</span>
21526 <tt>text = {{email.text}}</tt><br/>
21527 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21528 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21529 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21530 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21531 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21534 <file name="protractor.js" type="protractor">
21535 var text = element(by.binding('email.text'));
21536 var valid = element(by.binding('myForm.input.$valid'));
21537 var input = element(by.model('email.text'));
21539 it('should initialize to model', function() {
21540 expect(text.getText()).toContain('me@example.com');
21541 expect(valid.getText()).toContain('true');
21544 it('should be invalid if empty', function() {
21546 input.sendKeys('');
21547 expect(text.getText()).toEqual('text =');
21548 expect(valid.getText()).toContain('false');
21551 it('should be invalid if not email', function() {
21553 input.sendKeys('xxx');
21555 expect(valid.getText()).toContain('false');
21560 'email': emailInputType,
21565 * @name input[radio]
21568 * HTML radio button.
21570 * @param {string} ngModel Assignable angular expression to data-bind to.
21571 * @param {string} value The value to which the `ngModel` expression should be set when selected.
21572 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21573 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21574 * @param {string=} name Property name of the form under which the control is published.
21575 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21576 * interaction with the input element.
21577 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21578 * is selected. Should be used instead of the `value` attribute if you need
21579 * a non-string `ngModel` (`boolean`, `array`, ...).
21582 <example name="radio-input-directive" module="radioExample">
21583 <file name="index.html">
21585 angular.module('radioExample', [])
21586 .controller('ExampleController', ['$scope', function($scope) {
21590 $scope.specialValue = {
21596 <form name="myForm" ng-controller="ExampleController">
21598 <input type="radio" ng-model="color.name" value="red">
21602 <input type="radio" ng-model="color.name" ng-value="specialValue">
21606 <input type="radio" ng-model="color.name" value="blue">
21609 <tt>color = {{color.name | json}}</tt><br/>
21611 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21613 <file name="protractor.js" type="protractor">
21614 it('should change state', function() {
21615 var color = element(by.binding('color.name'));
21617 expect(color.getText()).toContain('blue');
21619 element.all(by.model('color.name')).get(0).click();
21621 expect(color.getText()).toContain('red');
21626 'radio': radioInputType,
21631 * @name input[checkbox]
21636 * @param {string} ngModel Assignable angular expression to data-bind to.
21637 * @param {string=} name Property name of the form under which the control is published.
21638 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21639 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21640 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21641 * interaction with the input element.
21644 <example name="checkbox-input-directive" module="checkboxExample">
21645 <file name="index.html">
21647 angular.module('checkboxExample', [])
21648 .controller('ExampleController', ['$scope', function($scope) {
21649 $scope.checkboxModel = {
21655 <form name="myForm" ng-controller="ExampleController">
21657 <input type="checkbox" ng-model="checkboxModel.value1">
21660 <input type="checkbox" ng-model="checkboxModel.value2"
21661 ng-true-value="'YES'" ng-false-value="'NO'">
21663 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21664 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21667 <file name="protractor.js" type="protractor">
21668 it('should change state', function() {
21669 var value1 = element(by.binding('checkboxModel.value1'));
21670 var value2 = element(by.binding('checkboxModel.value2'));
21672 expect(value1.getText()).toContain('true');
21673 expect(value2.getText()).toContain('YES');
21675 element(by.model('checkboxModel.value1')).click();
21676 element(by.model('checkboxModel.value2')).click();
21678 expect(value1.getText()).toContain('false');
21679 expect(value2.getText()).toContain('NO');
21684 'checkbox': checkboxInputType,
21693 function stringBasedInputType(ctrl) {
21694 ctrl.$formatters.push(function(value) {
21695 return ctrl.$isEmpty(value) ? value : value.toString();
21699 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21700 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21701 stringBasedInputType(ctrl);
21704 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21705 var type = lowercase(element[0].type);
21707 // In composition mode, users are still inputing intermediate text buffer,
21708 // hold the listener until composition is done.
21709 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21710 if (!$sniffer.android) {
21711 var composing = false;
21713 element.on('compositionstart', function(data) {
21717 element.on('compositionend', function() {
21723 var listener = function(ev) {
21725 $browser.defer.cancel(timeout);
21728 if (composing) return;
21729 var value = element.val(),
21730 event = ev && ev.type;
21732 // By default we will trim the value
21733 // If the attribute ng-trim exists we will avoid trimming
21734 // If input type is 'password', the value is never trimmed
21735 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21736 value = trim(value);
21739 // If a control is suffering from bad input (due to native validators), browsers discard its
21740 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21741 // control's value is the same empty value twice in a row.
21742 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21743 ctrl.$setViewValue(value, event);
21747 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21748 // input event on backspace, delete or cut
21749 if ($sniffer.hasEvent('input')) {
21750 element.on('input', listener);
21754 var deferListener = function(ev, input, origValue) {
21756 timeout = $browser.defer(function() {
21758 if (!input || input.value !== origValue) {
21765 element.on('keydown', function(event) {
21766 var key = event.keyCode;
21769 // command modifiers arrows
21770 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21772 deferListener(event, this, this.value);
21775 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21776 if ($sniffer.hasEvent('paste')) {
21777 element.on('paste cut', deferListener);
21781 // if user paste into input using mouse on older browser
21782 // or form autocomplete on newer browser, we need "change" event to catch it
21783 element.on('change', listener);
21785 ctrl.$render = function() {
21786 // Workaround for Firefox validation #12102.
21787 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21788 if (element.val() !== value) {
21789 element.val(value);
21794 function weekParser(isoWeek, existingDate) {
21795 if (isDate(isoWeek)) {
21799 if (isString(isoWeek)) {
21800 WEEK_REGEXP.lastIndex = 0;
21801 var parts = WEEK_REGEXP.exec(isoWeek);
21803 var year = +parts[1],
21809 firstThurs = getFirstThursdayOfYear(year),
21810 addDays = (week - 1) * 7;
21812 if (existingDate) {
21813 hours = existingDate.getHours();
21814 minutes = existingDate.getMinutes();
21815 seconds = existingDate.getSeconds();
21816 milliseconds = existingDate.getMilliseconds();
21819 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21826 function createDateParser(regexp, mapping) {
21827 return function(iso, date) {
21834 if (isString(iso)) {
21835 // When a date is JSON'ified to wraps itself inside of an extra
21836 // set of double quotes. This makes the date parsing code unable
21837 // to match the date string and parse it as a date.
21838 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21839 iso = iso.substring(1, iso.length - 1);
21841 if (ISO_DATE_REGEXP.test(iso)) {
21842 return new Date(iso);
21844 regexp.lastIndex = 0;
21845 parts = regexp.exec(iso);
21851 yyyy: date.getFullYear(),
21852 MM: date.getMonth() + 1,
21853 dd: date.getDate(),
21854 HH: date.getHours(),
21855 mm: date.getMinutes(),
21856 ss: date.getSeconds(),
21857 sss: date.getMilliseconds() / 1000
21860 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21863 forEach(parts, function(part, index) {
21864 if (index < mapping.length) {
21865 map[mapping[index]] = +part;
21868 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21876 function createDateInputType(type, regexp, parseDate, format) {
21877 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21878 badInputChecker(scope, element, attr, ctrl);
21879 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21880 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21883 ctrl.$$parserName = type;
21884 ctrl.$parsers.push(function(value) {
21885 if (ctrl.$isEmpty(value)) return null;
21886 if (regexp.test(value)) {
21887 // Note: We cannot read ctrl.$modelValue, as there might be a different
21888 // parser/formatter in the processing chain so that the model
21889 // contains some different data format!
21890 var parsedDate = parseDate(value, previousDate);
21892 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21899 ctrl.$formatters.push(function(value) {
21900 if (value && !isDate(value)) {
21901 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21903 if (isValidDate(value)) {
21904 previousDate = value;
21905 if (previousDate && timezone) {
21906 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21908 return $filter('date')(value, format, timezone);
21910 previousDate = null;
21915 if (isDefined(attr.min) || attr.ngMin) {
21917 ctrl.$validators.min = function(value) {
21918 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
21920 attr.$observe('min', function(val) {
21921 minVal = parseObservedDateValue(val);
21926 if (isDefined(attr.max) || attr.ngMax) {
21928 ctrl.$validators.max = function(value) {
21929 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
21931 attr.$observe('max', function(val) {
21932 maxVal = parseObservedDateValue(val);
21937 function isValidDate(value) {
21938 // Invalid Date: getTime() returns NaN
21939 return value && !(value.getTime && value.getTime() !== value.getTime());
21942 function parseObservedDateValue(val) {
21943 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
21948 function badInputChecker(scope, element, attr, ctrl) {
21949 var node = element[0];
21950 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
21951 if (nativeValidation) {
21952 ctrl.$parsers.push(function(value) {
21953 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
21954 // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
21955 // - also sets validity.badInput (should only be validity.typeMismatch).
21956 // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
21957 // - can ignore this case as we can still read out the erroneous email...
21958 return validity.badInput && !validity.typeMismatch ? undefined : value;
21963 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21964 badInputChecker(scope, element, attr, ctrl);
21965 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21967 ctrl.$$parserName = 'number';
21968 ctrl.$parsers.push(function(value) {
21969 if (ctrl.$isEmpty(value)) return null;
21970 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
21974 ctrl.$formatters.push(function(value) {
21975 if (!ctrl.$isEmpty(value)) {
21976 if (!isNumber(value)) {
21977 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
21979 value = value.toString();
21984 if (isDefined(attr.min) || attr.ngMin) {
21986 ctrl.$validators.min = function(value) {
21987 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
21990 attr.$observe('min', function(val) {
21991 if (isDefined(val) && !isNumber(val)) {
21992 val = parseFloat(val, 10);
21994 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
21995 // TODO(matsko): implement validateLater to reduce number of validations
22000 if (isDefined(attr.max) || attr.ngMax) {
22002 ctrl.$validators.max = function(value) {
22003 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22006 attr.$observe('max', function(val) {
22007 if (isDefined(val) && !isNumber(val)) {
22008 val = parseFloat(val, 10);
22010 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22011 // TODO(matsko): implement validateLater to reduce number of validations
22017 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22018 // Note: no badInputChecker here by purpose as `url` is only a validation
22019 // in browsers, i.e. we can always read out input.value even if it is not valid!
22020 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22021 stringBasedInputType(ctrl);
22023 ctrl.$$parserName = 'url';
22024 ctrl.$validators.url = function(modelValue, viewValue) {
22025 var value = modelValue || viewValue;
22026 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22030 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22031 // Note: no badInputChecker here by purpose as `url` is only a validation
22032 // in browsers, i.e. we can always read out input.value even if it is not valid!
22033 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22034 stringBasedInputType(ctrl);
22036 ctrl.$$parserName = 'email';
22037 ctrl.$validators.email = function(modelValue, viewValue) {
22038 var value = modelValue || viewValue;
22039 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22043 function radioInputType(scope, element, attr, ctrl) {
22044 // make the name unique, if not defined
22045 if (isUndefined(attr.name)) {
22046 element.attr('name', nextUid());
22049 var listener = function(ev) {
22050 if (element[0].checked) {
22051 ctrl.$setViewValue(attr.value, ev && ev.type);
22055 element.on('click', listener);
22057 ctrl.$render = function() {
22058 var value = attr.value;
22059 element[0].checked = (value == ctrl.$viewValue);
22062 attr.$observe('value', ctrl.$render);
22065 function parseConstantExpr($parse, context, name, expression, fallback) {
22067 if (isDefined(expression)) {
22068 parseFn = $parse(expression);
22069 if (!parseFn.constant) {
22070 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22071 '`{1}`.', name, expression);
22073 return parseFn(context);
22078 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22079 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22080 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22082 var listener = function(ev) {
22083 ctrl.$setViewValue(element[0].checked, ev && ev.type);
22086 element.on('click', listener);
22088 ctrl.$render = function() {
22089 element[0].checked = ctrl.$viewValue;
22092 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22093 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22094 // it to a boolean.
22095 ctrl.$isEmpty = function(value) {
22096 return value === false;
22099 ctrl.$formatters.push(function(value) {
22100 return equals(value, trueValue);
22103 ctrl.$parsers.push(function(value) {
22104 return value ? trueValue : falseValue;
22115 * HTML textarea element control with angular data-binding. The data-binding and validation
22116 * properties of this element are exactly the same as those of the
22117 * {@link ng.directive:input input element}.
22119 * @param {string} ngModel Assignable angular expression to data-bind to.
22120 * @param {string=} name Property name of the form under which the control is published.
22121 * @param {string=} required Sets `required` validation error key if the value is not entered.
22122 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22123 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22124 * `required` when you want to data-bind to the `required` attribute.
22125 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22127 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22128 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22130 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22131 * a RegExp found by evaluating the Angular expression given in the attribute value.
22132 * If the expression evaluates to a RegExp object, then this is used directly.
22133 * If the expression evaluates to a string, then it will be converted to a RegExp
22134 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22135 * `new RegExp('^abc$')`.<br />
22136 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22137 * start at the index of the last search's match, thus not taking the whole input value into
22139 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22140 * interaction with the input element.
22141 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22151 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22152 * input state control, and validation.
22153 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22155 * <div class="alert alert-warning">
22156 * **Note:** Not every feature offered is available for all input types.
22157 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22160 * @param {string} ngModel Assignable angular expression to data-bind to.
22161 * @param {string=} name Property name of the form under which the control is published.
22162 * @param {string=} required Sets `required` validation error key if the value is not entered.
22163 * @param {boolean=} ngRequired Sets `required` attribute if set to true
22164 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22166 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22167 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22169 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22170 * a RegExp found by evaluating the Angular expression given in the attribute value.
22171 * If the expression evaluates to a RegExp object, then this is used directly.
22172 * If the expression evaluates to a string, then it will be converted to a RegExp
22173 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22174 * `new RegExp('^abc$')`.<br />
22175 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22176 * start at the index of the last search's match, thus not taking the whole input value into
22178 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22179 * interaction with the input element.
22180 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22181 * This parameter is ignored for input[type=password] controls, which will never trim the
22185 <example name="input-directive" module="inputExample">
22186 <file name="index.html">
22188 angular.module('inputExample', [])
22189 .controller('ExampleController', ['$scope', function($scope) {
22190 $scope.user = {name: 'guest', last: 'visitor'};
22193 <div ng-controller="ExampleController">
22194 <form name="myForm">
22197 <input type="text" name="userName" ng-model="user.name" required>
22200 <span class="error" ng-show="myForm.userName.$error.required">
22205 <input type="text" name="lastName" ng-model="user.last"
22206 ng-minlength="3" ng-maxlength="10">
22209 <span class="error" ng-show="myForm.lastName.$error.minlength">
22211 <span class="error" ng-show="myForm.lastName.$error.maxlength">
22216 <tt>user = {{user}}</tt><br/>
22217 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22218 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22219 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22220 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22221 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22222 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22223 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22224 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22227 <file name="protractor.js" type="protractor">
22228 var user = element(by.exactBinding('user'));
22229 var userNameValid = element(by.binding('myForm.userName.$valid'));
22230 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22231 var lastNameError = element(by.binding('myForm.lastName.$error'));
22232 var formValid = element(by.binding('myForm.$valid'));
22233 var userNameInput = element(by.model('user.name'));
22234 var userLastInput = element(by.model('user.last'));
22236 it('should initialize to model', function() {
22237 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22238 expect(userNameValid.getText()).toContain('true');
22239 expect(formValid.getText()).toContain('true');
22242 it('should be invalid if empty when required', function() {
22243 userNameInput.clear();
22244 userNameInput.sendKeys('');
22246 expect(user.getText()).toContain('{"last":"visitor"}');
22247 expect(userNameValid.getText()).toContain('false');
22248 expect(formValid.getText()).toContain('false');
22251 it('should be valid if empty when min length is set', function() {
22252 userLastInput.clear();
22253 userLastInput.sendKeys('');
22255 expect(user.getText()).toContain('{"name":"guest","last":""}');
22256 expect(lastNameValid.getText()).toContain('true');
22257 expect(formValid.getText()).toContain('true');
22260 it('should be invalid if less than required min length', function() {
22261 userLastInput.clear();
22262 userLastInput.sendKeys('xx');
22264 expect(user.getText()).toContain('{"name":"guest"}');
22265 expect(lastNameValid.getText()).toContain('false');
22266 expect(lastNameError.getText()).toContain('minlength');
22267 expect(formValid.getText()).toContain('false');
22270 it('should be invalid if longer than max length', function() {
22271 userLastInput.clear();
22272 userLastInput.sendKeys('some ridiculously long name');
22274 expect(user.getText()).toContain('{"name":"guest"}');
22275 expect(lastNameValid.getText()).toContain('false');
22276 expect(lastNameError.getText()).toContain('maxlength');
22277 expect(formValid.getText()).toContain('false');
22282 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22283 function($browser, $sniffer, $filter, $parse) {
22286 require: ['?ngModel'],
22288 pre: function(scope, element, attr, ctrls) {
22290 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22291 $browser, $filter, $parse);
22300 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22306 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22307 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22310 * `ngValue` is useful when dynamically generating lists of radio buttons using
22311 * {@link ngRepeat `ngRepeat`}, as shown below.
22313 * Likewise, `ngValue` can be used to generate `<option>` elements for
22314 * the {@link select `select`} element. In that case however, only strings are supported
22315 * for the `value `attribute, so the resulting `ngModel` will always be a string.
22316 * Support for `select` models with non-string values is available via `ngOptions`.
22319 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22320 * of the `input` element
22323 <example name="ngValue-directive" module="valueExample">
22324 <file name="index.html">
22326 angular.module('valueExample', [])
22327 .controller('ExampleController', ['$scope', function($scope) {
22328 $scope.names = ['pizza', 'unicorns', 'robots'];
22329 $scope.my = { favorite: 'unicorns' };
22332 <form ng-controller="ExampleController">
22333 <h2>Which is your favorite?</h2>
22334 <label ng-repeat="name in names" for="{{name}}">
22336 <input type="radio"
22337 ng-model="my.favorite"
22342 <div>You chose {{my.favorite}}</div>
22345 <file name="protractor.js" type="protractor">
22346 var favorite = element(by.binding('my.favorite'));
22348 it('should initialize to model', function() {
22349 expect(favorite.getText()).toContain('unicorns');
22351 it('should bind the values to the inputs', function() {
22352 element.all(by.model('my.favorite')).get(0).click();
22353 expect(favorite.getText()).toContain('pizza');
22358 var ngValueDirective = function() {
22362 compile: function(tpl, tplAttr) {
22363 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22364 return function ngValueConstantLink(scope, elm, attr) {
22365 attr.$set('value', scope.$eval(attr.ngValue));
22368 return function ngValueLink(scope, elm, attr) {
22369 scope.$watch(attr.ngValue, function valueWatchAction(value) {
22370 attr.$set('value', value);
22384 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22385 * with the value of a given expression, and to update the text content when the value of that
22386 * expression changes.
22388 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22389 * `{{ expression }}` which is similar but less verbose.
22391 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22392 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22393 * element attribute, it makes the bindings invisible to the user while the page is loading.
22395 * An alternative solution to this problem would be using the
22396 * {@link ng.directive:ngCloak ngCloak} directive.
22400 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22403 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22404 <example module="bindExample">
22405 <file name="index.html">
22407 angular.module('bindExample', [])
22408 .controller('ExampleController', ['$scope', function($scope) {
22409 $scope.name = 'Whirled';
22412 <div ng-controller="ExampleController">
22413 <label>Enter name: <input type="text" ng-model="name"></label><br>
22414 Hello <span ng-bind="name"></span>!
22417 <file name="protractor.js" type="protractor">
22418 it('should check ng-bind', function() {
22419 var nameInput = element(by.model('name'));
22421 expect(element(by.binding('name')).getText()).toBe('Whirled');
22423 nameInput.sendKeys('world');
22424 expect(element(by.binding('name')).getText()).toBe('world');
22429 var ngBindDirective = ['$compile', function($compile) {
22432 compile: function ngBindCompile(templateElement) {
22433 $compile.$$addBindingClass(templateElement);
22434 return function ngBindLink(scope, element, attr) {
22435 $compile.$$addBindingInfo(element, attr.ngBind);
22436 element = element[0];
22437 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22438 element.textContent = isUndefined(value) ? '' : value;
22448 * @name ngBindTemplate
22451 * The `ngBindTemplate` directive specifies that the element
22452 * text content should be replaced with the interpolation of the template
22453 * in the `ngBindTemplate` attribute.
22454 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22455 * expressions. This directive is needed since some HTML elements
22456 * (such as TITLE and OPTION) cannot contain SPAN elements.
22459 * @param {string} ngBindTemplate template of form
22460 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22463 * Try it here: enter text in text box and watch the greeting change.
22464 <example module="bindExample">
22465 <file name="index.html">
22467 angular.module('bindExample', [])
22468 .controller('ExampleController', ['$scope', function($scope) {
22469 $scope.salutation = 'Hello';
22470 $scope.name = 'World';
22473 <div ng-controller="ExampleController">
22474 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22475 <label>Name: <input type="text" ng-model="name"></label><br>
22476 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22479 <file name="protractor.js" type="protractor">
22480 it('should check ng-bind', function() {
22481 var salutationElem = element(by.binding('salutation'));
22482 var salutationInput = element(by.model('salutation'));
22483 var nameInput = element(by.model('name'));
22485 expect(salutationElem.getText()).toBe('Hello World!');
22487 salutationInput.clear();
22488 salutationInput.sendKeys('Greetings');
22490 nameInput.sendKeys('user');
22492 expect(salutationElem.getText()).toBe('Greetings user!');
22497 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22499 compile: function ngBindTemplateCompile(templateElement) {
22500 $compile.$$addBindingClass(templateElement);
22501 return function ngBindTemplateLink(scope, element, attr) {
22502 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22503 $compile.$$addBindingInfo(element, interpolateFn.expressions);
22504 element = element[0];
22505 attr.$observe('ngBindTemplate', function(value) {
22506 element.textContent = isUndefined(value) ? '' : value;
22519 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22520 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22521 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22522 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22523 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22525 * You may also bypass sanitization for values you know are safe. To do so, bind to
22526 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
22527 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22529 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22530 * will have an exception (instead of an exploit.)
22533 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22537 <example module="bindHtmlExample" deps="angular-sanitize.js">
22538 <file name="index.html">
22539 <div ng-controller="ExampleController">
22540 <p ng-bind-html="myHTML"></p>
22544 <file name="script.js">
22545 angular.module('bindHtmlExample', ['ngSanitize'])
22546 .controller('ExampleController', ['$scope', function($scope) {
22548 'I am an <code>HTML</code>string with ' +
22549 '<a href="#">links!</a> and other <em>stuff</em>';
22553 <file name="protractor.js" type="protractor">
22554 it('should check ng-bind-html', function() {
22555 expect(element(by.binding('myHTML')).getText()).toBe(
22556 'I am an HTMLstring with links! and other stuff');
22561 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22564 compile: function ngBindHtmlCompile(tElement, tAttrs) {
22565 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22566 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22567 return (value || '').toString();
22569 $compile.$$addBindingClass(tElement);
22571 return function ngBindHtmlLink(scope, element, attr) {
22572 $compile.$$addBindingInfo(element, attr.ngBindHtml);
22574 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22575 // we re-evaluate the expr because we want a TrustedValueHolderType
22576 // for $sce, not a string
22577 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22589 * Evaluate the given expression when the user changes the input.
22590 * The expression is evaluated immediately, unlike the JavaScript onchange event
22591 * which only triggers at the end of a change (usually, when the user leaves the
22592 * form element or presses the return key).
22594 * The `ngChange` expression is only evaluated when a change in the input value causes
22595 * a new value to be committed to the model.
22597 * It will not be evaluated:
22598 * * if the value returned from the `$parsers` transformation pipeline has not changed
22599 * * if the input has continued to be invalid since the model will stay `null`
22600 * * if the model is changed programmatically and not by a change to the input value
22603 * Note, this directive requires `ngModel` to be present.
22606 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22610 * <example name="ngChange-directive" module="changeExample">
22611 * <file name="index.html">
22613 * angular.module('changeExample', [])
22614 * .controller('ExampleController', ['$scope', function($scope) {
22615 * $scope.counter = 0;
22616 * $scope.change = function() {
22617 * $scope.counter++;
22621 * <div ng-controller="ExampleController">
22622 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22623 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22624 * <label for="ng-change-example2">Confirmed</label><br />
22625 * <tt>debug = {{confirmed}}</tt><br/>
22626 * <tt>counter = {{counter}}</tt><br/>
22629 * <file name="protractor.js" type="protractor">
22630 * var counter = element(by.binding('counter'));
22631 * var debug = element(by.binding('confirmed'));
22633 * it('should evaluate the expression if changing from view', function() {
22634 * expect(counter.getText()).toContain('0');
22636 * element(by.id('ng-change-example1')).click();
22638 * expect(counter.getText()).toContain('1');
22639 * expect(debug.getText()).toContain('true');
22642 * it('should not evaluate the expression if changing from model', function() {
22643 * element(by.id('ng-change-example2')).click();
22645 * expect(counter.getText()).toContain('0');
22646 * expect(debug.getText()).toContain('true');
22651 var ngChangeDirective = valueFn({
22653 require: 'ngModel',
22654 link: function(scope, element, attr, ctrl) {
22655 ctrl.$viewChangeListeners.push(function() {
22656 scope.$eval(attr.ngChange);
22661 function classDirective(name, selector) {
22662 name = 'ngClass' + name;
22663 return ['$animate', function($animate) {
22666 link: function(scope, element, attr) {
22669 scope.$watch(attr[name], ngClassWatchAction, true);
22671 attr.$observe('class', function(value) {
22672 ngClassWatchAction(scope.$eval(attr[name]));
22676 if (name !== 'ngClass') {
22677 scope.$watch('$index', function($index, old$index) {
22678 // jshint bitwise: false
22679 var mod = $index & 1;
22680 if (mod !== (old$index & 1)) {
22681 var classes = arrayClasses(scope.$eval(attr[name]));
22683 addClasses(classes) :
22684 removeClasses(classes);
22689 function addClasses(classes) {
22690 var newClasses = digestClassCounts(classes, 1);
22691 attr.$addClass(newClasses);
22694 function removeClasses(classes) {
22695 var newClasses = digestClassCounts(classes, -1);
22696 attr.$removeClass(newClasses);
22699 function digestClassCounts(classes, count) {
22700 // Use createMap() to prevent class assumptions involving property
22701 // names in Object.prototype
22702 var classCounts = element.data('$classCounts') || createMap();
22703 var classesToUpdate = [];
22704 forEach(classes, function(className) {
22705 if (count > 0 || classCounts[className]) {
22706 classCounts[className] = (classCounts[className] || 0) + count;
22707 if (classCounts[className] === +(count > 0)) {
22708 classesToUpdate.push(className);
22712 element.data('$classCounts', classCounts);
22713 return classesToUpdate.join(' ');
22716 function updateClasses(oldClasses, newClasses) {
22717 var toAdd = arrayDifference(newClasses, oldClasses);
22718 var toRemove = arrayDifference(oldClasses, newClasses);
22719 toAdd = digestClassCounts(toAdd, 1);
22720 toRemove = digestClassCounts(toRemove, -1);
22721 if (toAdd && toAdd.length) {
22722 $animate.addClass(element, toAdd);
22724 if (toRemove && toRemove.length) {
22725 $animate.removeClass(element, toRemove);
22729 function ngClassWatchAction(newVal) {
22730 if (selector === true || scope.$index % 2 === selector) {
22731 var newClasses = arrayClasses(newVal || []);
22733 addClasses(newClasses);
22734 } else if (!equals(newVal,oldVal)) {
22735 var oldClasses = arrayClasses(oldVal);
22736 updateClasses(oldClasses, newClasses);
22739 oldVal = shallowCopy(newVal);
22744 function arrayDifference(tokens1, tokens2) {
22748 for (var i = 0; i < tokens1.length; i++) {
22749 var token = tokens1[i];
22750 for (var j = 0; j < tokens2.length; j++) {
22751 if (token == tokens2[j]) continue outer;
22753 values.push(token);
22758 function arrayClasses(classVal) {
22760 if (isArray(classVal)) {
22761 forEach(classVal, function(v) {
22762 classes = classes.concat(arrayClasses(v));
22765 } else if (isString(classVal)) {
22766 return classVal.split(' ');
22767 } else if (isObject(classVal)) {
22768 forEach(classVal, function(v, k) {
22770 classes = classes.concat(k.split(' '));
22786 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22787 * an expression that represents all classes to be added.
22789 * The directive operates in three different ways, depending on which of three types the expression
22792 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22795 * 2. If the expression evaluates to an object, then for each key-value pair of the
22796 * object with a truthy value the corresponding key is used as a class name.
22798 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22799 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22800 * to give you more control over what CSS classes appear. See the code below for an example of this.
22803 * The directive won't add duplicate classes if a particular class was already set.
22805 * When the expression changes, the previously added classes are removed and only then are the
22806 * new classes added.
22809 * **add** - happens just before the class is applied to the elements
22811 * **remove** - happens just before the class is removed from the element
22814 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22815 * of the evaluation can be a string representing space delimited class
22816 * names, an array, or a map of class names to boolean values. In the case of a map, the
22817 * names of the properties whose values are truthy will be added as css classes to the
22820 * @example Example that demonstrates basic bindings via ngClass directive.
22822 <file name="index.html">
22823 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22825 <input type="checkbox" ng-model="deleted">
22826 deleted (apply "strike" class)
22829 <input type="checkbox" ng-model="important">
22830 important (apply "bold" class)
22833 <input type="checkbox" ng-model="error">
22834 error (apply "has-error" class)
22837 <p ng-class="style">Using String Syntax</p>
22838 <input type="text" ng-model="style"
22839 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22841 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22842 <input ng-model="style1"
22843 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22844 <input ng-model="style2"
22845 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22846 <input ng-model="style3"
22847 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22849 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22850 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22851 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22853 <file name="style.css">
22855 text-decoration: line-through;
22865 background-color: yellow;
22871 <file name="protractor.js" type="protractor">
22872 var ps = element.all(by.css('p'));
22874 it('should let you toggle the class', function() {
22876 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22877 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22879 element(by.model('important')).click();
22880 expect(ps.first().getAttribute('class')).toMatch(/bold/);
22882 element(by.model('error')).click();
22883 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22886 it('should let you toggle string example', function() {
22887 expect(ps.get(1).getAttribute('class')).toBe('');
22888 element(by.model('style')).clear();
22889 element(by.model('style')).sendKeys('red');
22890 expect(ps.get(1).getAttribute('class')).toBe('red');
22893 it('array example should have 3 classes', function() {
22894 expect(ps.get(2).getAttribute('class')).toBe('');
22895 element(by.model('style1')).sendKeys('bold');
22896 element(by.model('style2')).sendKeys('strike');
22897 element(by.model('style3')).sendKeys('red');
22898 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22901 it('array with map example should have 2 classes', function() {
22902 expect(ps.last().getAttribute('class')).toBe('');
22903 element(by.model('style4')).sendKeys('bold');
22904 element(by.model('warning')).click();
22905 expect(ps.last().getAttribute('class')).toBe('bold orange');
22912 The example below demonstrates how to perform animations using ngClass.
22914 <example module="ngAnimate" deps="angular-animate.js" animations="true">
22915 <file name="index.html">
22916 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22917 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22919 <span class="base-class" ng-class="myVar">Sample Text</span>
22921 <file name="style.css">
22923 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22926 .base-class.my-class {
22931 <file name="protractor.js" type="protractor">
22932 it('should check ng-class', function() {
22933 expect(element(by.css('.base-class')).getAttribute('class')).not.
22934 toMatch(/my-class/);
22936 element(by.id('setbtn')).click();
22938 expect(element(by.css('.base-class')).getAttribute('class')).
22939 toMatch(/my-class/);
22941 element(by.id('clearbtn')).click();
22943 expect(element(by.css('.base-class')).getAttribute('class')).not.
22944 toMatch(/my-class/);
22950 ## ngClass and pre-existing CSS3 Transitions/Animations
22951 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
22952 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
22953 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
22954 to view the step by step details of {@link $animate#addClass $animate.addClass} and
22955 {@link $animate#removeClass $animate.removeClass}.
22957 var ngClassDirective = classDirective('', true);
22965 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22966 * {@link ng.directive:ngClass ngClass}, except they work in
22967 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22969 * This directive can be applied only within the scope of an
22970 * {@link ng.directive:ngRepeat ngRepeat}.
22973 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
22974 * of the evaluation can be a string representing space delimited class names or an array.
22978 <file name="index.html">
22979 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22980 <li ng-repeat="name in names">
22981 <span ng-class-odd="'odd'" ng-class-even="'even'">
22987 <file name="style.css">
22995 <file name="protractor.js" type="protractor">
22996 it('should check ng-class-odd and ng-class-even', function() {
22997 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22999 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23005 var ngClassOddDirective = classDirective('Odd', 0);
23009 * @name ngClassEven
23013 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23014 * {@link ng.directive:ngClass ngClass}, except they work in
23015 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23017 * This directive can be applied only within the scope of an
23018 * {@link ng.directive:ngRepeat ngRepeat}.
23021 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23022 * result of the evaluation can be a string representing space delimited class names or an array.
23026 <file name="index.html">
23027 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23028 <li ng-repeat="name in names">
23029 <span ng-class-odd="'odd'" ng-class-even="'even'">
23030 {{name}}
23035 <file name="style.css">
23043 <file name="protractor.js" type="protractor">
23044 it('should check ng-class-odd and ng-class-even', function() {
23045 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23047 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23053 var ngClassEvenDirective = classDirective('Even', 1);
23061 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23062 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23063 * directive to avoid the undesirable flicker effect caused by the html template display.
23065 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23066 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23067 * of the browser view.
23069 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23070 * `angular.min.js`.
23071 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23074 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23075 * display: none !important;
23079 * When this css rule is loaded by the browser, all html elements (including their children) that
23080 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23081 * during the compilation of the template it deletes the `ngCloak` element attribute, making
23082 * the compiled element visible.
23084 * For the best result, the `angular.js` script must be loaded in the head section of the html
23085 * document; alternatively, the css rule above must be included in the external stylesheet of the
23092 <file name="index.html">
23093 <div id="template1" ng-cloak>{{ 'hello' }}</div>
23094 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23096 <file name="protractor.js" type="protractor">
23097 it('should remove the template directive and css class', function() {
23098 expect($('#template1').getAttribute('ng-cloak')).
23100 expect($('#template2').getAttribute('ng-cloak')).
23107 var ngCloakDirective = ngDirective({
23108 compile: function(element, attr) {
23109 attr.$set('ngCloak', undefined);
23110 element.removeClass('ng-cloak');
23116 * @name ngController
23119 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23120 * supports the principles behind the Model-View-Controller design pattern.
23122 * MVC components in angular:
23124 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23125 * are accessed through bindings.
23126 * * View — The template (HTML with data bindings) that is rendered into the View.
23127 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23128 * logic behind the application to decorate the scope with functions and values
23130 * Note that you can also attach controllers to the DOM by declaring it in a route definition
23131 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23132 * again using `ng-controller` in the template itself. This will cause the controller to be attached
23133 * and executed twice.
23138 * @param {expression} ngController Name of a constructor function registered with the current
23139 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23140 * that on the current scope evaluates to a constructor function.
23142 * The controller instance can be published into a scope property by specifying
23143 * `ng-controller="as propertyName"`.
23145 * If the current `$controllerProvider` is configured to use globals (via
23146 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23147 * also be the name of a globally accessible constructor function (not recommended).
23150 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23151 * greeting are methods declared on the controller (see source tab). These methods can
23152 * easily be called from the angular markup. Any changes to the data are automatically reflected
23153 * in the View without the need for a manual update.
23155 * Two different declaration styles are included below:
23157 * * one binds methods and properties directly onto the controller using `this`:
23158 * `ng-controller="SettingsController1 as settings"`
23159 * * one injects `$scope` into the controller:
23160 * `ng-controller="SettingsController2"`
23162 * The second option is more common in the Angular community, and is generally used in boilerplates
23163 * and in this guide. However, there are advantages to binding properties directly to the controller
23164 * and avoiding scope.
23166 * * Using `controller as` makes it obvious which controller you are accessing in the template when
23167 * multiple controllers apply to an element.
23168 * * If you are writing your controllers as classes you have easier access to the properties and
23169 * methods, which will appear on the scope, from inside the controller code.
23170 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23171 * inheritance masking primitives.
23173 * This example demonstrates the `controller as` syntax.
23175 * <example name="ngControllerAs" module="controllerAsExample">
23176 * <file name="index.html">
23177 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23178 * <label>Name: <input type="text" ng-model="settings.name"/></label>
23179 * <button ng-click="settings.greet()">greet</button><br/>
23182 * <li ng-repeat="contact in settings.contacts">
23183 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23184 * <option>phone</option>
23185 * <option>email</option>
23187 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23188 * <button ng-click="settings.clearContact(contact)">clear</button>
23189 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23191 * <li><button ng-click="settings.addContact()">add</button></li>
23195 * <file name="app.js">
23196 * angular.module('controllerAsExample', [])
23197 * .controller('SettingsController1', SettingsController1);
23199 * function SettingsController1() {
23200 * this.name = "John Smith";
23201 * this.contacts = [
23202 * {type: 'phone', value: '408 555 1212'},
23203 * {type: 'email', value: 'john.smith@example.org'} ];
23206 * SettingsController1.prototype.greet = function() {
23207 * alert(this.name);
23210 * SettingsController1.prototype.addContact = function() {
23211 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
23214 * SettingsController1.prototype.removeContact = function(contactToRemove) {
23215 * var index = this.contacts.indexOf(contactToRemove);
23216 * this.contacts.splice(index, 1);
23219 * SettingsController1.prototype.clearContact = function(contact) {
23220 * contact.type = 'phone';
23221 * contact.value = '';
23224 * <file name="protractor.js" type="protractor">
23225 * it('should check controller as', function() {
23226 * var container = element(by.id('ctrl-as-exmpl'));
23227 * expect(container.element(by.model('settings.name'))
23228 * .getAttribute('value')).toBe('John Smith');
23230 * var firstRepeat =
23231 * container.element(by.repeater('contact in settings.contacts').row(0));
23232 * var secondRepeat =
23233 * container.element(by.repeater('contact in settings.contacts').row(1));
23235 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23236 * .toBe('408 555 1212');
23238 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23239 * .toBe('john.smith@example.org');
23241 * firstRepeat.element(by.buttonText('clear')).click();
23243 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23246 * container.element(by.buttonText('add')).click();
23248 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
23249 * .element(by.model('contact.value'))
23250 * .getAttribute('value'))
23251 * .toBe('yourname@example.org');
23256 * This example demonstrates the "attach to `$scope`" style of controller.
23258 * <example name="ngController" module="controllerExample">
23259 * <file name="index.html">
23260 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
23261 * <label>Name: <input type="text" ng-model="name"/></label>
23262 * <button ng-click="greet()">greet</button><br/>
23265 * <li ng-repeat="contact in contacts">
23266 * <select ng-model="contact.type" id="select_{{$index}}">
23267 * <option>phone</option>
23268 * <option>email</option>
23270 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23271 * <button ng-click="clearContact(contact)">clear</button>
23272 * <button ng-click="removeContact(contact)">X</button>
23274 * <li>[ <button ng-click="addContact()">add</button> ]</li>
23278 * <file name="app.js">
23279 * angular.module('controllerExample', [])
23280 * .controller('SettingsController2', ['$scope', SettingsController2]);
23282 * function SettingsController2($scope) {
23283 * $scope.name = "John Smith";
23284 * $scope.contacts = [
23285 * {type:'phone', value:'408 555 1212'},
23286 * {type:'email', value:'john.smith@example.org'} ];
23288 * $scope.greet = function() {
23289 * alert($scope.name);
23292 * $scope.addContact = function() {
23293 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
23296 * $scope.removeContact = function(contactToRemove) {
23297 * var index = $scope.contacts.indexOf(contactToRemove);
23298 * $scope.contacts.splice(index, 1);
23301 * $scope.clearContact = function(contact) {
23302 * contact.type = 'phone';
23303 * contact.value = '';
23307 * <file name="protractor.js" type="protractor">
23308 * it('should check controller', function() {
23309 * var container = element(by.id('ctrl-exmpl'));
23311 * expect(container.element(by.model('name'))
23312 * .getAttribute('value')).toBe('John Smith');
23314 * var firstRepeat =
23315 * container.element(by.repeater('contact in contacts').row(0));
23316 * var secondRepeat =
23317 * container.element(by.repeater('contact in contacts').row(1));
23319 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23320 * .toBe('408 555 1212');
23321 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23322 * .toBe('john.smith@example.org');
23324 * firstRepeat.element(by.buttonText('clear')).click();
23326 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23329 * container.element(by.buttonText('add')).click();
23331 * expect(container.element(by.repeater('contact in contacts').row(2))
23332 * .element(by.model('contact.value'))
23333 * .getAttribute('value'))
23334 * .toBe('yourname@example.org');
23340 var ngControllerDirective = [function() {
23356 * Angular has some features that can break certain
23357 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23359 * If you intend to implement these rules then you must tell Angular not to use these features.
23361 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23364 * The following rules affect Angular:
23366 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23367 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23368 * increase in the speed of evaluating Angular expressions.
23370 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23371 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23372 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23373 * `angular-csp.css` in your HTML manually.
23375 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23376 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23377 * however, triggers a CSP error to be logged in the console:
23380 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23381 * script in the following Content Security Policy directive: "default-src 'self'". Note that
23382 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23385 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23386 * directive on an element of the HTML document that appears before the `<script>` tag that loads
23387 * the `angular.js` file.
23389 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23391 * You can specify which of the CSP related Angular features should be deactivated by providing
23392 * a value for the `ng-csp` attribute. The options are as follows:
23394 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23396 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23398 * You can use these values in the following combinations:
23401 * * No declaration means that Angular will assume that you can do inline styles, but it will do
23402 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23405 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23406 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23409 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23410 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23412 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23413 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23415 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23416 * styles nor use eval, which is the same as an empty: ng-csp.
23417 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23420 * This example shows how to apply the `ngCsp` directive to the `html` tag.
23423 <html ng-app ng-csp>
23429 // Note: the suffix `.csp` in the example name triggers
23430 // csp mode in our http server!
23431 <example name="example.csp" module="cspExample" ng-csp="true">
23432 <file name="index.html">
23433 <div ng-controller="MainController as ctrl">
23435 <button ng-click="ctrl.inc()" id="inc">Increment</button>
23436 <span id="counter">
23442 <button ng-click="ctrl.evil()" id="evil">Evil</button>
23443 <span id="evilError">
23449 <file name="script.js">
23450 angular.module('cspExample', [])
23451 .controller('MainController', function() {
23453 this.inc = function() {
23456 this.evil = function() {
23457 // jshint evil:true
23461 this.evilError = e.message;
23466 <file name="protractor.js" type="protractor">
23467 var util, webdriver;
23469 var incBtn = element(by.id('inc'));
23470 var counter = element(by.id('counter'));
23471 var evilBtn = element(by.id('evil'));
23472 var evilError = element(by.id('evilError'));
23474 function getAndClearSevereErrors() {
23475 return browser.manage().logs().get('browser').then(function(browserLog) {
23476 return browserLog.filter(function(logEntry) {
23477 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23482 function clearErrors() {
23483 getAndClearSevereErrors();
23486 function expectNoErrors() {
23487 getAndClearSevereErrors().then(function(filteredLog) {
23488 expect(filteredLog.length).toEqual(0);
23489 if (filteredLog.length) {
23490 console.log('browser console errors: ' + util.inspect(filteredLog));
23495 function expectError(regex) {
23496 getAndClearSevereErrors().then(function(filteredLog) {
23498 filteredLog.forEach(function(log) {
23499 if (log.message.match(regex)) {
23504 throw new Error('expected an error that matches ' + regex);
23509 beforeEach(function() {
23510 util = require('util');
23511 webdriver = require('protractor/node_modules/selenium-webdriver');
23514 // For now, we only test on Chrome,
23515 // as Safari does not load the page with Protractor's injected scripts,
23516 // and Firefox webdriver always disables content security policy (#6358)
23517 if (browser.params.browser !== 'chrome') {
23521 it('should not report errors when the page is loaded', function() {
23522 // clear errors so we are not dependent on previous tests
23524 // Need to reload the page as the page is already loaded when
23526 browser.driver.getCurrentUrl().then(function(url) {
23532 it('should evaluate expressions', function() {
23533 expect(counter.getText()).toEqual('0');
23535 expect(counter.getText()).toEqual('1');
23539 it('should throw and report an error when using "eval"', function() {
23541 expect(evilError.getText()).toMatch(/Content Security Policy/);
23542 expectError(/Content Security Policy/);
23548 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23549 // bootstrap the system (before $parse is instantiated), for this reason we just have
23550 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23557 * The ngClick directive allows you to specify custom behavior when
23558 * an element is clicked.
23562 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23563 * click. ({@link guide/expression#-event- Event object is available as `$event`})
23567 <file name="index.html">
23568 <button ng-click="count = count + 1" ng-init="count=0">
23575 <file name="protractor.js" type="protractor">
23576 it('should check ng-click', function() {
23577 expect(element(by.binding('count')).getText()).toMatch('0');
23578 element(by.css('button')).click();
23579 expect(element(by.binding('count')).getText()).toMatch('1');
23585 * A collection of directives that allows creation of custom event handlers that are defined as
23586 * angular expressions and are compiled and executed within the current scope.
23588 var ngEventDirectives = {};
23590 // For events that might fire synchronously during DOM manipulation
23591 // we need to execute their event handlers asynchronously using $evalAsync,
23592 // so that they are not executed in an inconsistent state.
23593 var forceAsyncEvents = {
23598 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23599 function(eventName) {
23600 var directiveName = directiveNormalize('ng-' + eventName);
23601 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23604 compile: function($element, attr) {
23605 // We expose the powerful $event object on the scope that provides access to the Window,
23606 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
23607 // checks at the cost of speed since event handler expressions are not executed as
23608 // frequently as regular change detection.
23609 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23610 return function ngEventHandler(scope, element) {
23611 element.on(eventName, function(event) {
23612 var callback = function() {
23613 fn(scope, {$event:event});
23615 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23616 scope.$evalAsync(callback);
23618 scope.$apply(callback);
23633 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23637 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23638 * a dblclick. (The Event object is available as `$event`)
23642 <file name="index.html">
23643 <button ng-dblclick="count = count + 1" ng-init="count=0">
23644 Increment (on double click)
23654 * @name ngMousedown
23657 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23661 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23662 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23666 <file name="index.html">
23667 <button ng-mousedown="count = count + 1" ng-init="count=0">
23668 Increment (on mouse down)
23681 * Specify custom behavior on mouseup event.
23685 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23686 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23690 <file name="index.html">
23691 <button ng-mouseup="count = count + 1" ng-init="count=0">
23692 Increment (on mouse up)
23701 * @name ngMouseover
23704 * Specify custom behavior on mouseover event.
23708 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23709 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23713 <file name="index.html">
23714 <button ng-mouseover="count = count + 1" ng-init="count=0">
23715 Increment (when mouse is over)
23725 * @name ngMouseenter
23728 * Specify custom behavior on mouseenter event.
23732 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23733 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23737 <file name="index.html">
23738 <button ng-mouseenter="count = count + 1" ng-init="count=0">
23739 Increment (when mouse enters)
23749 * @name ngMouseleave
23752 * Specify custom behavior on mouseleave event.
23756 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23757 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23761 <file name="index.html">
23762 <button ng-mouseleave="count = count + 1" ng-init="count=0">
23763 Increment (when mouse leaves)
23773 * @name ngMousemove
23776 * Specify custom behavior on mousemove event.
23780 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23781 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23785 <file name="index.html">
23786 <button ng-mousemove="count = count + 1" ng-init="count=0">
23787 Increment (when mouse moves)
23800 * Specify custom behavior on keydown event.
23804 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23805 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23809 <file name="index.html">
23810 <input ng-keydown="count = count + 1" ng-init="count=0">
23811 key down count: {{count}}
23822 * Specify custom behavior on keyup event.
23826 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23827 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23831 <file name="index.html">
23832 <p>Typing in the input box below updates the key count</p>
23833 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23835 <p>Typing in the input box below updates the keycode</p>
23836 <input ng-keyup="event=$event">
23837 <p>event keyCode: {{ event.keyCode }}</p>
23838 <p>event altKey: {{ event.altKey }}</p>
23849 * Specify custom behavior on keypress event.
23852 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23853 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23854 * and can be interrogated for keyCode, altKey, etc.)
23858 <file name="index.html">
23859 <input ng-keypress="count = count + 1" ng-init="count=0">
23860 key press count: {{count}}
23871 * Enables binding angular expressions to onsubmit events.
23873 * Additionally it prevents the default action (which for form means sending the request to the
23874 * server and reloading the current page), but only if the form does not contain `action`,
23875 * `data-action`, or `x-action` attributes.
23877 * <div class="alert alert-warning">
23878 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23879 * `ngSubmit` handlers together. See the
23880 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23881 * for a detailed discussion of when `ngSubmit` may be triggered.
23886 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23887 * ({@link guide/expression#-event- Event object is available as `$event`})
23890 <example module="submitExample">
23891 <file name="index.html">
23893 angular.module('submitExample', [])
23894 .controller('ExampleController', ['$scope', function($scope) {
23896 $scope.text = 'hello';
23897 $scope.submit = function() {
23899 $scope.list.push(this.text);
23905 <form ng-submit="submit()" ng-controller="ExampleController">
23906 Enter text and hit enter:
23907 <input type="text" ng-model="text" name="text" />
23908 <input type="submit" id="submit" value="Submit" />
23909 <pre>list={{list}}</pre>
23912 <file name="protractor.js" type="protractor">
23913 it('should check ng-submit', function() {
23914 expect(element(by.binding('list')).getText()).toBe('list=[]');
23915 element(by.css('#submit')).click();
23916 expect(element(by.binding('list')).getText()).toContain('hello');
23917 expect(element(by.model('text')).getAttribute('value')).toBe('');
23919 it('should ignore empty strings', function() {
23920 expect(element(by.binding('list')).getText()).toBe('list=[]');
23921 element(by.css('#submit')).click();
23922 element(by.css('#submit')).click();
23923 expect(element(by.binding('list')).getText()).toContain('hello');
23934 * Specify custom behavior on focus event.
23936 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
23937 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23938 * during an `$apply` to ensure a consistent state.
23940 * @element window, input, select, textarea, a
23942 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
23943 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
23946 * See {@link ng.directive:ngClick ngClick}
23954 * Specify custom behavior on blur event.
23956 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
23957 * an element has lost focus.
23959 * Note: As the `blur` event is executed synchronously also during DOM manipulations
23960 * (e.g. removing a focussed input),
23961 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23962 * during an `$apply` to ensure a consistent state.
23964 * @element window, input, select, textarea, a
23966 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
23967 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
23970 * See {@link ng.directive:ngClick ngClick}
23978 * Specify custom behavior on copy event.
23980 * @element window, input, select, textarea, a
23982 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
23983 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
23987 <file name="index.html">
23988 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
23999 * Specify custom behavior on cut event.
24001 * @element window, input, select, textarea, a
24003 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24004 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24008 <file name="index.html">
24009 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24020 * Specify custom behavior on paste event.
24022 * @element window, input, select, textarea, a
24024 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24025 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24029 <file name="index.html">
24030 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24043 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24044 * {expression}. If the expression assigned to `ngIf` evaluates to a false
24045 * value then the element is removed from the DOM, otherwise a clone of the
24046 * element is reinserted into the DOM.
24048 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24049 * element in the DOM rather than changing its visibility via the `display` css property. A common
24050 * case when this difference is significant is when using css selectors that rely on an element's
24051 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24053 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24054 * is created when the element is restored. The scope created within `ngIf` inherits from
24055 * its parent scope using
24056 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24057 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24058 * a javascript primitive defined in the parent scope. In this case any modifications made to the
24059 * variable within the child scope will override (hide) the value in the parent scope.
24061 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24062 * is if an element's class attribute is directly modified after it's compiled, using something like
24063 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24064 * the added class will be lost because the original compiled state is used to regenerate the element.
24066 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24067 * and `leave` effects.
24070 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24071 * leave - happens just before the `ngIf` contents are removed from the DOM
24076 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24077 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
24078 * element is added to the DOM tree.
24081 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24082 <file name="index.html">
24083 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24085 <span ng-if="checked" class="animate-if">
24086 This is removed when the checkbox is unchecked.
24089 <file name="animations.css">
24092 border:1px solid black;
24096 .animate-if.ng-enter, .animate-if.ng-leave {
24097 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24100 .animate-if.ng-enter,
24101 .animate-if.ng-leave.ng-leave-active {
24105 .animate-if.ng-leave,
24106 .animate-if.ng-enter.ng-enter-active {
24112 var ngIfDirective = ['$animate', function($animate) {
24114 multiElement: true,
24115 transclude: 'element',
24120 link: function($scope, $element, $attr, ctrl, $transclude) {
24121 var block, childScope, previousElements;
24122 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24126 $transclude(function(clone, newScope) {
24127 childScope = newScope;
24128 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24129 // Note: We only need the first/last node of the cloned nodes.
24130 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24131 // by a directive with templateUrl when its template arrives.
24135 $animate.enter(clone, $element.parent(), $element);
24139 if (previousElements) {
24140 previousElements.remove();
24141 previousElements = null;
24144 childScope.$destroy();
24148 previousElements = getBlockNodes(block.clone);
24149 $animate.leave(previousElements).then(function() {
24150 previousElements = null;
24166 * Fetches, compiles and includes an external HTML fragment.
24168 * By default, the template URL is restricted to the same domain and protocol as the
24169 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24170 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24171 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24172 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24173 * ng.$sce Strict Contextual Escaping}.
24175 * In addition, the browser's
24176 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24177 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24178 * policy may further restrict whether the template is successfully loaded.
24179 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24180 * access on some browsers.
24183 * enter - animation is used to bring new content into the browser.
24184 * leave - animation is used to animate existing content away.
24186 * The enter and leave animation occur concurrently.
24191 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24192 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24193 * @param {string=} onload Expression to evaluate when a new partial is loaded.
24194 * <div class="alert alert-warning">
24195 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
24196 * a function with the name on the window element, which will usually throw a
24197 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
24198 * different form that {@link guide/directive#normalization matches} `onload`.
24201 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24202 * $anchorScroll} to scroll the viewport after the content is loaded.
24204 * - If the attribute is not set, disable scrolling.
24205 * - If the attribute is set without value, enable scrolling.
24206 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
24209 <example module="includeExample" deps="angular-animate.js" animations="true">
24210 <file name="index.html">
24211 <div ng-controller="ExampleController">
24212 <select ng-model="template" ng-options="t.name for t in templates">
24213 <option value="">(blank)</option>
24215 url of the template: <code>{{template.url}}</code>
24217 <div class="slide-animate-container">
24218 <div class="slide-animate" ng-include="template.url"></div>
24222 <file name="script.js">
24223 angular.module('includeExample', ['ngAnimate'])
24224 .controller('ExampleController', ['$scope', function($scope) {
24226 [ { name: 'template1.html', url: 'template1.html'},
24227 { name: 'template2.html', url: 'template2.html'} ];
24228 $scope.template = $scope.templates[0];
24231 <file name="template1.html">
24232 Content of template1.html
24234 <file name="template2.html">
24235 Content of template2.html
24237 <file name="animations.css">
24238 .slide-animate-container {
24241 border:1px solid black;
24250 .slide-animate.ng-enter, .slide-animate.ng-leave {
24251 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24262 .slide-animate.ng-enter {
24265 .slide-animate.ng-enter.ng-enter-active {
24269 .slide-animate.ng-leave {
24272 .slide-animate.ng-leave.ng-leave-active {
24276 <file name="protractor.js" type="protractor">
24277 var templateSelect = element(by.model('template'));
24278 var includeElem = element(by.css('[ng-include]'));
24280 it('should load template1.html', function() {
24281 expect(includeElem.getText()).toMatch(/Content of template1.html/);
24284 it('should load template2.html', function() {
24285 if (browser.params.browser == 'firefox') {
24286 // Firefox can't handle using selects
24287 // See https://github.com/angular/protractor/issues/480
24290 templateSelect.click();
24291 templateSelect.all(by.css('option')).get(2).click();
24292 expect(includeElem.getText()).toMatch(/Content of template2.html/);
24295 it('should change to blank', function() {
24296 if (browser.params.browser == 'firefox') {
24297 // Firefox can't handle using selects
24300 templateSelect.click();
24301 templateSelect.all(by.css('option')).get(0).click();
24302 expect(includeElem.isPresent()).toBe(false);
24311 * @name ngInclude#$includeContentRequested
24312 * @eventType emit on the scope ngInclude was declared in
24314 * Emitted every time the ngInclude content is requested.
24316 * @param {Object} angularEvent Synthetic event object.
24317 * @param {String} src URL of content to load.
24323 * @name ngInclude#$includeContentLoaded
24324 * @eventType emit on the current ngInclude scope
24326 * Emitted every time the ngInclude content is reloaded.
24328 * @param {Object} angularEvent Synthetic event object.
24329 * @param {String} src URL of content to load.
24335 * @name ngInclude#$includeContentError
24336 * @eventType emit on the scope ngInclude was declared in
24338 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24340 * @param {Object} angularEvent Synthetic event object.
24341 * @param {String} src URL of content to load.
24343 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24344 function($templateRequest, $anchorScroll, $animate) {
24349 transclude: 'element',
24350 controller: angular.noop,
24351 compile: function(element, attr) {
24352 var srcExp = attr.ngInclude || attr.src,
24353 onloadExp = attr.onload || '',
24354 autoScrollExp = attr.autoscroll;
24356 return function(scope, $element, $attr, ctrl, $transclude) {
24357 var changeCounter = 0,
24362 var cleanupLastIncludeContent = function() {
24363 if (previousElement) {
24364 previousElement.remove();
24365 previousElement = null;
24367 if (currentScope) {
24368 currentScope.$destroy();
24369 currentScope = null;
24371 if (currentElement) {
24372 $animate.leave(currentElement).then(function() {
24373 previousElement = null;
24375 previousElement = currentElement;
24376 currentElement = null;
24380 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24381 var afterAnimation = function() {
24382 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24386 var thisChangeId = ++changeCounter;
24389 //set the 2nd param to true to ignore the template request error so that the inner
24390 //contents and scope can be cleaned up.
24391 $templateRequest(src, true).then(function(response) {
24392 if (thisChangeId !== changeCounter) return;
24393 var newScope = scope.$new();
24394 ctrl.template = response;
24396 // Note: This will also link all children of ng-include that were contained in the original
24397 // html. If that content contains controllers, ... they could pollute/change the scope.
24398 // However, using ng-include on an element with additional content does not make sense...
24399 // Note: We can't remove them in the cloneAttchFn of $transclude as that
24400 // function is called before linking the content, which would apply child
24401 // directives to non existing elements.
24402 var clone = $transclude(newScope, function(clone) {
24403 cleanupLastIncludeContent();
24404 $animate.enter(clone, null, $element).then(afterAnimation);
24407 currentScope = newScope;
24408 currentElement = clone;
24410 currentScope.$emit('$includeContentLoaded', src);
24411 scope.$eval(onloadExp);
24413 if (thisChangeId === changeCounter) {
24414 cleanupLastIncludeContent();
24415 scope.$emit('$includeContentError', src);
24418 scope.$emit('$includeContentRequested', src);
24420 cleanupLastIncludeContent();
24421 ctrl.template = null;
24429 // This directive is called during the $transclude call of the first `ngInclude` directive.
24430 // It will replace and compile the content of the element with the loaded template.
24431 // We need this directive so that the element content is already filled when
24432 // the link function of another directive on the same element as ngInclude
24434 var ngIncludeFillContentDirective = ['$compile',
24435 function($compile) {
24439 require: 'ngInclude',
24440 link: function(scope, $element, $attr, ctrl) {
24441 if (/SVG/.test($element[0].toString())) {
24442 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24443 // support innerHTML, so detect this here and try to generate the contents
24446 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24447 function namespaceAdaptedClone(clone) {
24448 $element.append(clone);
24449 }, {futureParentElement: $element});
24453 $element.html(ctrl.template);
24454 $compile($element.contents())(scope);
24465 * The `ngInit` directive allows you to evaluate an expression in the
24468 * <div class="alert alert-danger">
24469 * This directive can be abused to add unnecessary amounts of logic into your templates.
24470 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24471 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24472 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24473 * rather than `ngInit` to initialize values on a scope.
24476 * <div class="alert alert-warning">
24477 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24478 * sure you have parentheses to ensure correct operator precedence:
24479 * <pre class="prettyprint">
24480 * `<div ng-init="test1 = ($index | toString)"></div>`
24487 * @param {expression} ngInit {@link guide/expression Expression} to eval.
24490 <example module="initExample">
24491 <file name="index.html">
24493 angular.module('initExample', [])
24494 .controller('ExampleController', ['$scope', function($scope) {
24495 $scope.list = [['a', 'b'], ['c', 'd']];
24498 <div ng-controller="ExampleController">
24499 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24500 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24501 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24506 <file name="protractor.js" type="protractor">
24507 it('should alias index positions', function() {
24508 var elements = element.all(by.css('.example-init'));
24509 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24510 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24511 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24512 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24517 var ngInitDirective = ngDirective({
24519 compile: function() {
24521 pre: function(scope, element, attrs) {
24522 scope.$eval(attrs.ngInit);
24533 * Text input that converts between a delimited string and an array of strings. The default
24534 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24535 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24537 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24538 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24539 * list item is respected. This implies that the user of the directive is responsible for
24540 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24541 * tab or newline character.
24542 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24543 * when joining the list items back together) and whitespace around each list item is stripped
24544 * before it is added to the model.
24546 * ### Example with Validation
24548 * <example name="ngList-directive" module="listExample">
24549 * <file name="app.js">
24550 * angular.module('listExample', [])
24551 * .controller('ExampleController', ['$scope', function($scope) {
24552 * $scope.names = ['morpheus', 'neo', 'trinity'];
24555 * <file name="index.html">
24556 * <form name="myForm" ng-controller="ExampleController">
24557 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24558 * <span role="alert">
24559 * <span class="error" ng-show="myForm.namesInput.$error.required">
24563 * <tt>names = {{names}}</tt><br/>
24564 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24565 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24566 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24567 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24570 * <file name="protractor.js" type="protractor">
24571 * var listInput = element(by.model('names'));
24572 * var names = element(by.exactBinding('names'));
24573 * var valid = element(by.binding('myForm.namesInput.$valid'));
24574 * var error = element(by.css('span.error'));
24576 * it('should initialize to model', function() {
24577 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24578 * expect(valid.getText()).toContain('true');
24579 * expect(error.getCssValue('display')).toBe('none');
24582 * it('should be invalid if empty', function() {
24583 * listInput.clear();
24584 * listInput.sendKeys('');
24586 * expect(names.getText()).toContain('');
24587 * expect(valid.getText()).toContain('false');
24588 * expect(error.getCssValue('display')).not.toBe('none');
24593 * ### Example - splitting on newline
24594 * <example name="ngList-directive-newlines">
24595 * <file name="index.html">
24596 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
24597 * <pre>{{ list | json }}</pre>
24599 * <file name="protractor.js" type="protractor">
24600 * it("should split the text by newlines", function() {
24601 * var listInput = element(by.model('list'));
24602 * var output = element(by.binding('list | json'));
24603 * listInput.sendKeys('abc\ndef\nghi');
24604 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
24610 * @param {string=} ngList optional delimiter that should be used to split the value.
24612 var ngListDirective = function() {
24616 require: 'ngModel',
24617 link: function(scope, element, attr, ctrl) {
24618 // We want to control whitespace trimming so we use this convoluted approach
24619 // to access the ngList attribute, which doesn't pre-trim the attribute
24620 var ngList = element.attr(attr.$attr.ngList) || ', ';
24621 var trimValues = attr.ngTrim !== 'false';
24622 var separator = trimValues ? trim(ngList) : ngList;
24624 var parse = function(viewValue) {
24625 // If the viewValue is invalid (say required but empty) it will be `undefined`
24626 if (isUndefined(viewValue)) return;
24631 forEach(viewValue.split(separator), function(value) {
24632 if (value) list.push(trimValues ? trim(value) : value);
24639 ctrl.$parsers.push(parse);
24640 ctrl.$formatters.push(function(value) {
24641 if (isArray(value)) {
24642 return value.join(ngList);
24648 // Override the standard $isEmpty because an empty array means the input is empty.
24649 ctrl.$isEmpty = function(value) {
24650 return !value || !value.length;
24656 /* global VALID_CLASS: true,
24657 INVALID_CLASS: true,
24658 PRISTINE_CLASS: true,
24660 UNTOUCHED_CLASS: true,
24661 TOUCHED_CLASS: true,
24664 var VALID_CLASS = 'ng-valid',
24665 INVALID_CLASS = 'ng-invalid',
24666 PRISTINE_CLASS = 'ng-pristine',
24667 DIRTY_CLASS = 'ng-dirty',
24668 UNTOUCHED_CLASS = 'ng-untouched',
24669 TOUCHED_CLASS = 'ng-touched',
24670 PENDING_CLASS = 'ng-pending';
24672 var ngModelMinErr = minErr('ngModel');
24676 * @name ngModel.NgModelController
24678 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24679 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24681 * @property {*} $modelValue The value in the model that the control is bound to.
24682 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24683 the control reads value from the DOM. The functions are called in array order, each passing
24684 its return value through to the next. The last return value is forwarded to the
24685 {@link ngModel.NgModelController#$validators `$validators`} collection.
24687 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24690 Returning `undefined` from a parser means a parse error occurred. In that case,
24691 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24692 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24693 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24696 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24697 the model value changes. The functions are called in reverse array order, each passing the value through to the
24698 next. The last return value is used as the actual DOM value.
24699 Used to format / convert values for display in the control.
24701 * function formatter(value) {
24703 * return value.toUpperCase();
24706 * ngModel.$formatters.push(formatter);
24709 * @property {Object.<string, function>} $validators A collection of validators that are applied
24710 * whenever the model value changes. The key value within the object refers to the name of the
24711 * validator while the function refers to the validation operation. The validation operation is
24712 * provided with the model value as an argument and must return a true or false value depending
24713 * on the response of that validation.
24716 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24717 * var value = modelValue || viewValue;
24718 * return /[0-9]+/.test(value) &&
24719 * /[a-z]+/.test(value) &&
24720 * /[A-Z]+/.test(value) &&
24721 * /\W+/.test(value);
24725 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24726 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24727 * is expected to return a promise when it is run during the model validation process. Once the promise
24728 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
24729 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24730 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24731 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24732 * will only run once all synchronous validators have passed.
24734 * Please note that if $http is used then it is important that the server returns a success HTTP response code
24735 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24738 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24739 * var value = modelValue || viewValue;
24741 * // Lookup user by username
24742 * return $http.get('/api/users/' + value).
24743 * then(function resolved() {
24744 * //username exists, this means validation fails
24745 * return $q.reject('exists');
24746 * }, function rejected() {
24747 * //username does not exist, therefore this validation passes
24753 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24754 * view value has changed. It is called with no arguments, and its return value is ignored.
24755 * This can be used in place of additional $watches against the model value.
24757 * @property {Object} $error An object hash with all failing validator ids as keys.
24758 * @property {Object} $pending An object hash with all pending validator ids as keys.
24760 * @property {boolean} $untouched True if control has not lost focus yet.
24761 * @property {boolean} $touched True if control has lost focus.
24762 * @property {boolean} $pristine True if user has not interacted with the control yet.
24763 * @property {boolean} $dirty True if user has already interacted with the control.
24764 * @property {boolean} $valid True if there is no error.
24765 * @property {boolean} $invalid True if at least one error on the control.
24766 * @property {string} $name The name attribute of the control.
24770 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24771 * The controller contains services for data-binding, validation, CSS updates, and value formatting
24772 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24773 * listening to DOM events.
24774 * Such DOM related logic should be provided by other directives which make use of
24775 * `NgModelController` for data-binding to control elements.
24776 * Angular provides this DOM logic for most {@link input `input`} elements.
24777 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24778 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24781 * ### Custom Control Example
24782 * This example shows how to use `NgModelController` with a custom control to achieve
24783 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24784 * collaborate together to achieve the desired result.
24786 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24787 * contents be edited in place by the user.
24789 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24790 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24791 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24792 * that content using the `$sce` service.
24794 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24795 <file name="style.css">
24796 [contenteditable] {
24797 border: 1px solid black;
24798 background-color: white;
24803 border: 1px solid red;
24807 <file name="script.js">
24808 angular.module('customControl', ['ngSanitize']).
24809 directive('contenteditable', ['$sce', function($sce) {
24811 restrict: 'A', // only activate on element attribute
24812 require: '?ngModel', // get a hold of NgModelController
24813 link: function(scope, element, attrs, ngModel) {
24814 if (!ngModel) return; // do nothing if no ng-model
24816 // Specify how UI should be updated
24817 ngModel.$render = function() {
24818 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24821 // Listen for change events to enable binding
24822 element.on('blur keyup change', function() {
24823 scope.$evalAsync(read);
24825 read(); // initialize
24827 // Write data to the model
24829 var html = element.html();
24830 // When we clear the content editable the browser leaves a <br> behind
24831 // If strip-br attribute is provided then we strip this out
24832 if ( attrs.stripBr && html == '<br>' ) {
24835 ngModel.$setViewValue(html);
24841 <file name="index.html">
24842 <form name="myForm">
24843 <div contenteditable
24844 name="myWidget" ng-model="userContent"
24846 required>Change me!</div>
24847 <span ng-show="myForm.myWidget.$error.required">Required!</span>
24849 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24852 <file name="protractor.js" type="protractor">
24853 it('should data-bind and become invalid', function() {
24854 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24855 // SafariDriver can't handle contenteditable
24856 // and Firefox driver can't clear contenteditables very well
24859 var contentEditable = element(by.css('[contenteditable]'));
24860 var content = 'Change me!';
24862 expect(contentEditable.getText()).toEqual(content);
24864 contentEditable.clear();
24865 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24866 expect(contentEditable.getText()).toEqual('');
24867 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24874 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24875 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24876 this.$viewValue = Number.NaN;
24877 this.$modelValue = Number.NaN;
24878 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24879 this.$validators = {};
24880 this.$asyncValidators = {};
24881 this.$parsers = [];
24882 this.$formatters = [];
24883 this.$viewChangeListeners = [];
24884 this.$untouched = true;
24885 this.$touched = false;
24886 this.$pristine = true;
24887 this.$dirty = false;
24888 this.$valid = true;
24889 this.$invalid = false;
24890 this.$error = {}; // keep invalid keys here
24891 this.$$success = {}; // keep valid keys here
24892 this.$pending = undefined; // keep pending keys here
24893 this.$name = $interpolate($attr.name || '', false)($scope);
24894 this.$$parentForm = nullFormCtrl;
24896 var parsedNgModel = $parse($attr.ngModel),
24897 parsedNgModelAssign = parsedNgModel.assign,
24898 ngModelGet = parsedNgModel,
24899 ngModelSet = parsedNgModelAssign,
24900 pendingDebounce = null,
24904 this.$$setOptions = function(options) {
24905 ctrl.$options = options;
24906 if (options && options.getterSetter) {
24907 var invokeModelGetter = $parse($attr.ngModel + '()'),
24908 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24910 ngModelGet = function($scope) {
24911 var modelValue = parsedNgModel($scope);
24912 if (isFunction(modelValue)) {
24913 modelValue = invokeModelGetter($scope);
24917 ngModelSet = function($scope, newValue) {
24918 if (isFunction(parsedNgModel($scope))) {
24919 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
24921 parsedNgModelAssign($scope, ctrl.$modelValue);
24924 } else if (!parsedNgModel.assign) {
24925 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
24926 $attr.ngModel, startingTag($element));
24932 * @name ngModel.NgModelController#$render
24935 * Called when the view needs to be updated. It is expected that the user of the ng-model
24936 * directive will implement this method.
24938 * The `$render()` method is invoked in the following situations:
24940 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
24941 * committed value then `$render()` is called to update the input control.
24942 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
24943 * the `$viewValue` are different from last time.
24945 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
24946 * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
24947 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
24948 * invoked if you only change a property on the objects.
24950 this.$render = noop;
24954 * @name ngModel.NgModelController#$isEmpty
24957 * This is called when we need to determine if the value of an input is empty.
24959 * For instance, the required directive does this to work out if the input has data or not.
24961 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
24963 * You can override this for input directives whose concept of being empty is different from the
24964 * default. The `checkboxInputType` directive does this because in its case a value of `false`
24967 * @param {*} value The value of the input to check for emptiness.
24968 * @returns {boolean} True if `value` is "empty".
24970 this.$isEmpty = function(value) {
24971 return isUndefined(value) || value === '' || value === null || value !== value;
24974 var currentValidationRunId = 0;
24978 * @name ngModel.NgModelController#$setValidity
24981 * Change the validity state, and notify the form.
24983 * This method can be called within $parsers/$formatters or a custom validation implementation.
24984 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
24985 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
24987 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
24988 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
24989 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
24990 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
24991 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
24992 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
24993 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
24994 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
24995 * Skipped is used by Angular when validators do not run because of parse errors and
24996 * when `$asyncValidators` do not run because any of the `$validators` failed.
24998 addSetValidityMethod({
25000 $element: $element,
25001 set: function(object, property) {
25002 object[property] = true;
25004 unset: function(object, property) {
25005 delete object[property];
25012 * @name ngModel.NgModelController#$setPristine
25015 * Sets the control to its pristine state.
25017 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25018 * state (`ng-pristine` class). A model is considered to be pristine when the control
25019 * has not been changed from when first compiled.
25021 this.$setPristine = function() {
25022 ctrl.$dirty = false;
25023 ctrl.$pristine = true;
25024 $animate.removeClass($element, DIRTY_CLASS);
25025 $animate.addClass($element, PRISTINE_CLASS);
25030 * @name ngModel.NgModelController#$setDirty
25033 * Sets the control to its dirty state.
25035 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25036 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25037 * from when first compiled.
25039 this.$setDirty = function() {
25040 ctrl.$dirty = true;
25041 ctrl.$pristine = false;
25042 $animate.removeClass($element, PRISTINE_CLASS);
25043 $animate.addClass($element, DIRTY_CLASS);
25044 ctrl.$$parentForm.$setDirty();
25049 * @name ngModel.NgModelController#$setUntouched
25052 * Sets the control to its untouched state.
25054 * This method can be called to remove the `ng-touched` class and set the control to its
25055 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25056 * by default, however this function can be used to restore that state if the model has
25057 * already been touched by the user.
25059 this.$setUntouched = function() {
25060 ctrl.$touched = false;
25061 ctrl.$untouched = true;
25062 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25067 * @name ngModel.NgModelController#$setTouched
25070 * Sets the control to its touched state.
25072 * This method can be called to remove the `ng-untouched` class and set the control to its
25073 * touched state (`ng-touched` class). A model is considered to be touched when the user has
25074 * first focused the control element and then shifted focus away from the control (blur event).
25076 this.$setTouched = function() {
25077 ctrl.$touched = true;
25078 ctrl.$untouched = false;
25079 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25084 * @name ngModel.NgModelController#$rollbackViewValue
25087 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25088 * which may be caused by a pending debounced event or because the input is waiting for a some
25091 * If you have an input that uses `ng-model-options` to set up debounced events or events such
25092 * as blur you can have a situation where there is a period when the `$viewValue`
25093 * is out of synch with the ngModel's `$modelValue`.
25095 * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25096 * programmatically before these debounced/future events have resolved/occurred, because Angular's
25097 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25099 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25100 * input which may have such events pending. This is important in order to make sure that the
25101 * input field will be updated with the new model value and any pending operations are cancelled.
25103 * <example name="ng-model-cancel-update" module="cancel-update-example">
25104 * <file name="app.js">
25105 * angular.module('cancel-update-example', [])
25107 * .controller('CancelUpdateController', ['$scope', function($scope) {
25108 * $scope.resetWithCancel = function(e) {
25109 * if (e.keyCode == 27) {
25110 * $scope.myForm.myInput1.$rollbackViewValue();
25111 * $scope.myValue = '';
25114 * $scope.resetWithoutCancel = function(e) {
25115 * if (e.keyCode == 27) {
25116 * $scope.myValue = '';
25121 * <file name="index.html">
25122 * <div ng-controller="CancelUpdateController">
25123 * <p>Try typing something in each input. See that the model only updates when you
25124 * blur off the input.
25126 * <p>Now see what happens if you start typing then press the Escape key</p>
25128 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25129 * <p id="inputDescription1">With $rollbackViewValue()</p>
25130 * <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25131 * ng-keydown="resetWithCancel($event)"><br/>
25132 * myValue: "{{ myValue }}"
25134 * <p id="inputDescription2">Without $rollbackViewValue()</p>
25135 * <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25136 * ng-keydown="resetWithoutCancel($event)"><br/>
25137 * myValue: "{{ myValue }}"
25143 this.$rollbackViewValue = function() {
25144 $timeout.cancel(pendingDebounce);
25145 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25151 * @name ngModel.NgModelController#$validate
25154 * Runs each of the registered validators (first synchronous validators and then
25155 * asynchronous validators).
25156 * If the validity changes to invalid, the model will be set to `undefined`,
25157 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25158 * If the validity changes to valid, it will set the model to the last available valid
25159 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25161 this.$validate = function() {
25162 // ignore $validate before model is initialized
25163 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25167 var viewValue = ctrl.$$lastCommittedViewValue;
25168 // Note: we use the $$rawModelValue as $modelValue might have been
25169 // set to undefined during a view -> model update that found validation
25170 // errors. We can't parse the view here, since that could change
25171 // the model although neither viewValue nor the model on the scope changed
25172 var modelValue = ctrl.$$rawModelValue;
25174 var prevValid = ctrl.$valid;
25175 var prevModelValue = ctrl.$modelValue;
25177 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25179 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25180 // If there was no change in validity, don't update the model
25181 // This prevents changing an invalid modelValue to undefined
25182 if (!allowInvalid && prevValid !== allValid) {
25183 // Note: Don't check ctrl.$valid here, as we could have
25184 // external validators (e.g. calculated on the server),
25185 // that just call $setValidity and need the model value
25186 // to calculate their validity.
25187 ctrl.$modelValue = allValid ? modelValue : undefined;
25189 if (ctrl.$modelValue !== prevModelValue) {
25190 ctrl.$$writeModelToScope();
25197 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25198 currentValidationRunId++;
25199 var localValidationRunId = currentValidationRunId;
25201 // check parser error
25202 if (!processParseErrors()) {
25203 validationDone(false);
25206 if (!processSyncValidators()) {
25207 validationDone(false);
25210 processAsyncValidators();
25212 function processParseErrors() {
25213 var errorKey = ctrl.$$parserName || 'parse';
25214 if (isUndefined(parserValid)) {
25215 setValidity(errorKey, null);
25217 if (!parserValid) {
25218 forEach(ctrl.$validators, function(v, name) {
25219 setValidity(name, null);
25221 forEach(ctrl.$asyncValidators, function(v, name) {
25222 setValidity(name, null);
25225 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25226 setValidity(errorKey, parserValid);
25227 return parserValid;
25232 function processSyncValidators() {
25233 var syncValidatorsValid = true;
25234 forEach(ctrl.$validators, function(validator, name) {
25235 var result = validator(modelValue, viewValue);
25236 syncValidatorsValid = syncValidatorsValid && result;
25237 setValidity(name, result);
25239 if (!syncValidatorsValid) {
25240 forEach(ctrl.$asyncValidators, function(v, name) {
25241 setValidity(name, null);
25248 function processAsyncValidators() {
25249 var validatorPromises = [];
25250 var allValid = true;
25251 forEach(ctrl.$asyncValidators, function(validator, name) {
25252 var promise = validator(modelValue, viewValue);
25253 if (!isPromiseLike(promise)) {
25254 throw ngModelMinErr("$asyncValidators",
25255 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25257 setValidity(name, undefined);
25258 validatorPromises.push(promise.then(function() {
25259 setValidity(name, true);
25260 }, function(error) {
25262 setValidity(name, false);
25265 if (!validatorPromises.length) {
25266 validationDone(true);
25268 $q.all(validatorPromises).then(function() {
25269 validationDone(allValid);
25274 function setValidity(name, isValid) {
25275 if (localValidationRunId === currentValidationRunId) {
25276 ctrl.$setValidity(name, isValid);
25280 function validationDone(allValid) {
25281 if (localValidationRunId === currentValidationRunId) {
25283 doneCallback(allValid);
25290 * @name ngModel.NgModelController#$commitViewValue
25293 * Commit a pending update to the `$modelValue`.
25295 * Updates may be pending by a debounced event or because the input is waiting for a some future
25296 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25297 * usually handles calling this in response to input events.
25299 this.$commitViewValue = function() {
25300 var viewValue = ctrl.$viewValue;
25302 $timeout.cancel(pendingDebounce);
25304 // If the view value has not changed then we should just exit, except in the case where there is
25305 // a native validator on the element. In this case the validation state may have changed even though
25306 // the viewValue has stayed empty.
25307 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25310 ctrl.$$lastCommittedViewValue = viewValue;
25313 if (ctrl.$pristine) {
25316 this.$$parseAndValidate();
25319 this.$$parseAndValidate = function() {
25320 var viewValue = ctrl.$$lastCommittedViewValue;
25321 var modelValue = viewValue;
25322 parserValid = isUndefined(modelValue) ? undefined : true;
25325 for (var i = 0; i < ctrl.$parsers.length; i++) {
25326 modelValue = ctrl.$parsers[i](modelValue);
25327 if (isUndefined(modelValue)) {
25328 parserValid = false;
25333 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25334 // ctrl.$modelValue has not been touched yet...
25335 ctrl.$modelValue = ngModelGet($scope);
25337 var prevModelValue = ctrl.$modelValue;
25338 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25339 ctrl.$$rawModelValue = modelValue;
25341 if (allowInvalid) {
25342 ctrl.$modelValue = modelValue;
25343 writeToModelIfNeeded();
25346 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25347 // This can happen if e.g. $setViewValue is called from inside a parser
25348 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25349 if (!allowInvalid) {
25350 // Note: Don't check ctrl.$valid here, as we could have
25351 // external validators (e.g. calculated on the server),
25352 // that just call $setValidity and need the model value
25353 // to calculate their validity.
25354 ctrl.$modelValue = allValid ? modelValue : undefined;
25355 writeToModelIfNeeded();
25359 function writeToModelIfNeeded() {
25360 if (ctrl.$modelValue !== prevModelValue) {
25361 ctrl.$$writeModelToScope();
25366 this.$$writeModelToScope = function() {
25367 ngModelSet($scope, ctrl.$modelValue);
25368 forEach(ctrl.$viewChangeListeners, function(listener) {
25372 $exceptionHandler(e);
25379 * @name ngModel.NgModelController#$setViewValue
25382 * Update the view value.
25384 * This method should be called when a control wants to change the view value; typically,
25385 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25386 * directive calls it when the value of the input changes and {@link ng.directive:select select}
25387 * calls it when an option is selected.
25389 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25390 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25391 * value sent directly for processing, finally to be applied to `$modelValue` and then the
25392 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25393 * in the `$viewChangeListeners` list, are called.
25395 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25396 * and the `default` trigger is not listed, all those actions will remain pending until one of the
25397 * `updateOn` events is triggered on the DOM element.
25398 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25399 * directive is used with a custom debounce for this particular event.
25400 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25401 * is specified, once the timer runs out.
25403 * When used with standard inputs, the view value will always be a string (which is in some cases
25404 * parsed into another type, such as a `Date` object for `input[date]`.)
25405 * However, custom controls might also pass objects to this method. In this case, we should make
25406 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25407 * perform a deep watch of objects, it only looks for a change of identity. If you only change
25408 * the property of the object then ngModel will not realise that the object has changed and
25409 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25410 * not change properties of the copy once it has been passed to `$setViewValue`.
25411 * Otherwise you may cause the model value on the scope to change incorrectly.
25413 * <div class="alert alert-info">
25414 * In any case, the value passed to the method should always reflect the current value
25415 * of the control. For example, if you are calling `$setViewValue` for an input element,
25416 * you should pass the input DOM value. Otherwise, the control and the scope model become
25417 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25418 * the control's DOM value in any way. If we want to change the control's DOM value
25419 * programmatically, we should update the `ngModel` scope expression. Its new value will be
25420 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25421 * to update the DOM, and finally call `$validate` on it.
25424 * @param {*} value value from the view.
25425 * @param {string} trigger Event that triggered the update.
25427 this.$setViewValue = function(value, trigger) {
25428 ctrl.$viewValue = value;
25429 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25430 ctrl.$$debounceViewValueCommit(trigger);
25434 this.$$debounceViewValueCommit = function(trigger) {
25435 var debounceDelay = 0,
25436 options = ctrl.$options,
25439 if (options && isDefined(options.debounce)) {
25440 debounce = options.debounce;
25441 if (isNumber(debounce)) {
25442 debounceDelay = debounce;
25443 } else if (isNumber(debounce[trigger])) {
25444 debounceDelay = debounce[trigger];
25445 } else if (isNumber(debounce['default'])) {
25446 debounceDelay = debounce['default'];
25450 $timeout.cancel(pendingDebounce);
25451 if (debounceDelay) {
25452 pendingDebounce = $timeout(function() {
25453 ctrl.$commitViewValue();
25455 } else if ($rootScope.$$phase) {
25456 ctrl.$commitViewValue();
25458 $scope.$apply(function() {
25459 ctrl.$commitViewValue();
25465 // Note: we cannot use a normal scope.$watch as we want to detect the following:
25466 // 1. scope value is 'a'
25467 // 2. user enters 'b'
25468 // 3. ng-change kicks in and reverts scope value to 'a'
25469 // -> scope value did not change since the last digest as
25470 // ng-change executes in apply phase
25471 // 4. view should be changed back to 'a'
25472 $scope.$watch(function ngModelWatch() {
25473 var modelValue = ngModelGet($scope);
25475 // if scope model value and ngModel value are out of sync
25476 // TODO(perf): why not move this to the action fn?
25477 if (modelValue !== ctrl.$modelValue &&
25478 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25479 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25481 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25482 parserValid = undefined;
25484 var formatters = ctrl.$formatters,
25485 idx = formatters.length;
25487 var viewValue = modelValue;
25489 viewValue = formatters[idx](viewValue);
25491 if (ctrl.$viewValue !== viewValue) {
25492 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25495 ctrl.$$runValidators(modelValue, viewValue, noop);
25512 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25513 * property on the scope using {@link ngModel.NgModelController NgModelController},
25514 * which is created and exposed by this directive.
25516 * `ngModel` is responsible for:
25518 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25520 * - Providing validation behavior (i.e. required, number, email, url).
25521 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25522 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25523 * - Registering the control with its parent {@link ng.directive:form form}.
25525 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25526 * current scope. If the property doesn't already exist on this scope, it will be created
25527 * implicitly and added to the scope.
25529 * For best practices on using `ngModel`, see:
25531 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25533 * For basic examples, how to use `ngModel`, see:
25535 * - {@link ng.directive:input input}
25536 * - {@link input[text] text}
25537 * - {@link input[checkbox] checkbox}
25538 * - {@link input[radio] radio}
25539 * - {@link input[number] number}
25540 * - {@link input[email] email}
25541 * - {@link input[url] url}
25542 * - {@link input[date] date}
25543 * - {@link input[datetime-local] datetime-local}
25544 * - {@link input[time] time}
25545 * - {@link input[month] month}
25546 * - {@link input[week] week}
25547 * - {@link ng.directive:select select}
25548 * - {@link ng.directive:textarea textarea}
25551 * The following CSS classes are added and removed on the associated input/select/textarea element
25552 * depending on the validity of the model.
25554 * - `ng-valid`: the model is valid
25555 * - `ng-invalid`: the model is invalid
25556 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
25557 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25558 * - `ng-pristine`: the control hasn't been interacted with yet
25559 * - `ng-dirty`: the control has been interacted with
25560 * - `ng-touched`: the control has been blurred
25561 * - `ng-untouched`: the control hasn't been blurred
25562 * - `ng-pending`: any `$asyncValidators` are unfulfilled
25564 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25566 * ## Animation Hooks
25568 * Animations within models are triggered when any of the associated CSS classes are added and removed
25569 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25570 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25571 * The animations that are triggered within ngModel are similar to how they work in ngClass and
25572 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25574 * The following example shows a simple way to utilize CSS transitions to style an input element
25575 * that has been rendered as invalid after it has been validated:
25578 * //be sure to include ngAnimate as a module to hook into more
25579 * //advanced animations
25581 * transition:0.5s linear all;
25582 * background: white;
25584 * .my-input.ng-invalid {
25591 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25592 <file name="index.html">
25594 angular.module('inputExample', [])
25595 .controller('ExampleController', ['$scope', function($scope) {
25601 transition:all linear 0.5s;
25602 background: transparent;
25604 .my-input.ng-invalid {
25609 <p id="inputDescription">
25610 Update input to see transitions when valid/invalid.
25611 Integer is a valid value.
25613 <form name="testForm" ng-controller="ExampleController">
25614 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25615 aria-describedby="inputDescription" />
25620 * ## Binding to a getter/setter
25622 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
25623 * function that returns a representation of the model when called with zero arguments, and sets
25624 * the internal state of a model when called with an argument. It's sometimes useful to use this
25625 * for models that have an internal representation that's different from what the model exposes
25628 * <div class="alert alert-success">
25629 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25630 * frequently than other parts of your code.
25633 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25634 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25635 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25636 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25638 * The following example shows how to use `ngModel` with a getter/setter:
25641 * <example name="ngModel-getter-setter" module="getterSetterExample">
25642 <file name="index.html">
25643 <div ng-controller="ExampleController">
25644 <form name="userForm">
25646 <input type="text" name="userName"
25647 ng-model="user.name"
25648 ng-model-options="{ getterSetter: true }" />
25651 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25654 <file name="app.js">
25655 angular.module('getterSetterExample', [])
25656 .controller('ExampleController', ['$scope', function($scope) {
25657 var _name = 'Brian';
25659 name: function(newName) {
25660 // Note that newName can be undefined for two reasons:
25661 // 1. Because it is called as a getter and thus called with no arguments
25662 // 2. Because the property should actually be set to undefined. This happens e.g. if the
25663 // input is invalid
25664 return arguments.length ? (_name = newName) : _name;
25671 var ngModelDirective = ['$rootScope', function($rootScope) {
25674 require: ['ngModel', '^?form', '^?ngModelOptions'],
25675 controller: NgModelController,
25676 // Prelink needs to run before any input directive
25677 // so that we can set the NgModelOptions in NgModelController
25678 // before anyone else uses it.
25680 compile: function ngModelCompile(element) {
25681 // Setup initial state of the control
25682 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25685 pre: function ngModelPreLink(scope, element, attr, ctrls) {
25686 var modelCtrl = ctrls[0],
25687 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25689 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25691 // notify others, especially parent forms
25692 formCtrl.$addControl(modelCtrl);
25694 attr.$observe('name', function(newValue) {
25695 if (modelCtrl.$name !== newValue) {
25696 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25700 scope.$on('$destroy', function() {
25701 modelCtrl.$$parentForm.$removeControl(modelCtrl);
25704 post: function ngModelPostLink(scope, element, attr, ctrls) {
25705 var modelCtrl = ctrls[0];
25706 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25707 element.on(modelCtrl.$options.updateOn, function(ev) {
25708 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25712 element.on('blur', function(ev) {
25713 if (modelCtrl.$touched) return;
25715 if ($rootScope.$$phase) {
25716 scope.$evalAsync(modelCtrl.$setTouched);
25718 scope.$apply(modelCtrl.$setTouched);
25727 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25731 * @name ngModelOptions
25734 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25735 * events that will trigger a model update and/or a debouncing delay so that the actual update only
25736 * takes place when a timer expires; this timer will be reset after another change takes place.
25738 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25739 * be different from the value in the actual model. This means that if you update the model you
25740 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25741 * order to make sure it is synchronized with the model and that any debounced action is canceled.
25743 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25744 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25745 * important because `form` controllers are published to the related scope under the name in their
25746 * `name` attribute.
25748 * Any pending changes will take place immediately when an enclosing form is submitted via the
25749 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25750 * to have access to the updated model.
25752 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
25754 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25755 * - `updateOn`: string specifying which event should the input be bound to. You can set several
25756 * events using an space delimited list. There is a special event called `default` that
25757 * matches the default events belonging of the control.
25758 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25759 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25760 * custom value for each event. For example:
25761 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25762 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25763 * not validate correctly instead of the default behavior of setting the model to undefined.
25764 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25765 `ngModel` as getters/setters.
25766 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25767 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25768 * continental US time zone abbreviations, but for general use, use a time zone offset, for
25769 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25770 * If not specified, the timezone of the browser will be used.
25774 The following example shows how to override immediate updates. Changes on the inputs within the
25775 form will update the model only when the control loses focus (blur event). If `escape` key is
25776 pressed while the input field is focused, the value is reset to the value in the current model.
25778 <example name="ngModelOptions-directive-blur" module="optionsExample">
25779 <file name="index.html">
25780 <div ng-controller="ExampleController">
25781 <form name="userForm">
25783 <input type="text" name="userName"
25784 ng-model="user.name"
25785 ng-model-options="{ updateOn: 'blur' }"
25786 ng-keyup="cancel($event)" />
25789 <input type="text" ng-model="user.data" />
25792 <pre>user.name = <span ng-bind="user.name"></span></pre>
25793 <pre>user.data = <span ng-bind="user.data"></span></pre>
25796 <file name="app.js">
25797 angular.module('optionsExample', [])
25798 .controller('ExampleController', ['$scope', function($scope) {
25799 $scope.user = { name: 'John', data: '' };
25801 $scope.cancel = function(e) {
25802 if (e.keyCode == 27) {
25803 $scope.userForm.userName.$rollbackViewValue();
25808 <file name="protractor.js" type="protractor">
25809 var model = element(by.binding('user.name'));
25810 var input = element(by.model('user.name'));
25811 var other = element(by.model('user.data'));
25813 it('should allow custom events', function() {
25814 input.sendKeys(' Doe');
25816 expect(model.getText()).toEqual('John');
25818 expect(model.getText()).toEqual('John Doe');
25821 it('should $rollbackViewValue when model changes', function() {
25822 input.sendKeys(' Doe');
25823 expect(input.getAttribute('value')).toEqual('John Doe');
25824 input.sendKeys(protractor.Key.ESCAPE);
25825 expect(input.getAttribute('value')).toEqual('John');
25827 expect(model.getText()).toEqual('John');
25832 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25833 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25835 <example name="ngModelOptions-directive-debounce" module="optionsExample">
25836 <file name="index.html">
25837 <div ng-controller="ExampleController">
25838 <form name="userForm">
25840 <input type="text" name="userName"
25841 ng-model="user.name"
25842 ng-model-options="{ debounce: 1000 }" />
25844 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25847 <pre>user.name = <span ng-bind="user.name"></span></pre>
25850 <file name="app.js">
25851 angular.module('optionsExample', [])
25852 .controller('ExampleController', ['$scope', function($scope) {
25853 $scope.user = { name: 'Igor' };
25858 This one shows how to bind to getter/setters:
25860 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25861 <file name="index.html">
25862 <div ng-controller="ExampleController">
25863 <form name="userForm">
25865 <input type="text" name="userName"
25866 ng-model="user.name"
25867 ng-model-options="{ getterSetter: true }" />
25870 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25873 <file name="app.js">
25874 angular.module('getterSetterExample', [])
25875 .controller('ExampleController', ['$scope', function($scope) {
25876 var _name = 'Brian';
25878 name: function(newName) {
25879 // Note that newName can be undefined for two reasons:
25880 // 1. Because it is called as a getter and thus called with no arguments
25881 // 2. Because the property should actually be set to undefined. This happens e.g. if the
25882 // input is invalid
25883 return arguments.length ? (_name = newName) : _name;
25890 var ngModelOptionsDirective = function() {
25893 controller: ['$scope', '$attrs', function($scope, $attrs) {
25895 this.$options = copy($scope.$eval($attrs.ngModelOptions));
25896 // Allow adding/overriding bound events
25897 if (isDefined(this.$options.updateOn)) {
25898 this.$options.updateOnDefault = false;
25899 // extract "default" pseudo-event from list of events that can trigger a model update
25900 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25901 that.$options.updateOnDefault = true;
25905 this.$options.updateOnDefault = true;
25914 function addSetValidityMethod(context) {
25915 var ctrl = context.ctrl,
25916 $element = context.$element,
25919 unset = context.unset,
25920 $animate = context.$animate;
25922 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
25924 ctrl.$setValidity = setValidity;
25926 function setValidity(validationErrorKey, state, controller) {
25927 if (isUndefined(state)) {
25928 createAndSet('$pending', validationErrorKey, controller);
25930 unsetAndCleanup('$pending', validationErrorKey, controller);
25932 if (!isBoolean(state)) {
25933 unset(ctrl.$error, validationErrorKey, controller);
25934 unset(ctrl.$$success, validationErrorKey, controller);
25937 unset(ctrl.$error, validationErrorKey, controller);
25938 set(ctrl.$$success, validationErrorKey, controller);
25940 set(ctrl.$error, validationErrorKey, controller);
25941 unset(ctrl.$$success, validationErrorKey, controller);
25944 if (ctrl.$pending) {
25945 cachedToggleClass(PENDING_CLASS, true);
25946 ctrl.$valid = ctrl.$invalid = undefined;
25947 toggleValidationCss('', null);
25949 cachedToggleClass(PENDING_CLASS, false);
25950 ctrl.$valid = isObjectEmpty(ctrl.$error);
25951 ctrl.$invalid = !ctrl.$valid;
25952 toggleValidationCss('', ctrl.$valid);
25955 // re-read the state as the set/unset methods could have
25956 // combined state in ctrl.$error[validationError] (used for forms),
25957 // where setting/unsetting only increments/decrements the value,
25958 // and does not replace it.
25960 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
25961 combinedState = undefined;
25962 } else if (ctrl.$error[validationErrorKey]) {
25963 combinedState = false;
25964 } else if (ctrl.$$success[validationErrorKey]) {
25965 combinedState = true;
25967 combinedState = null;
25970 toggleValidationCss(validationErrorKey, combinedState);
25971 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
25974 function createAndSet(name, value, controller) {
25978 set(ctrl[name], value, controller);
25981 function unsetAndCleanup(name, value, controller) {
25983 unset(ctrl[name], value, controller);
25985 if (isObjectEmpty(ctrl[name])) {
25986 ctrl[name] = undefined;
25990 function cachedToggleClass(className, switchValue) {
25991 if (switchValue && !classCache[className]) {
25992 $animate.addClass($element, className);
25993 classCache[className] = true;
25994 } else if (!switchValue && classCache[className]) {
25995 $animate.removeClass($element, className);
25996 classCache[className] = false;
26000 function toggleValidationCss(validationErrorKey, isValid) {
26001 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26003 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26004 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26008 function isObjectEmpty(obj) {
26010 for (var prop in obj) {
26011 if (obj.hasOwnProperty(prop)) {
26021 * @name ngNonBindable
26026 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26027 * DOM element. This is useful if the element contains what appears to be Angular directives and
26028 * bindings but which should be ignored by Angular. This could be the case if you have a site that
26029 * displays snippets of code, for instance.
26034 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26035 * but the one wrapped in `ngNonBindable` is left alone.
26039 <file name="index.html">
26040 <div>Normal: {{1 + 2}}</div>
26041 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26043 <file name="protractor.js" type="protractor">
26044 it('should check ng-non-bindable', function() {
26045 expect(element(by.binding('1 + 2')).getText()).toContain('3');
26046 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26051 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26053 /* global jqLiteRemove */
26055 var ngOptionsMinErr = minErr('ngOptions');
26064 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26065 * elements for the `<select>` element using the array or object obtained by evaluating the
26066 * `ngOptions` comprehension expression.
26068 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26069 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26070 * increasing speed by not creating a new scope for each repeated instance, as well as providing
26071 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26072 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26073 * to a non-string value. This is because an option element can only be bound to string values at
26076 * When an item in the `<select>` menu is selected, the array element or object property
26077 * represented by the selected option will be bound to the model identified by the `ngModel`
26080 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26081 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26082 * option. See example below for demonstration.
26084 * ## Complex Models (objects or collections)
26086 * By default, `ngModel` watches the model by reference, not value. This is important to know when
26087 * binding the select to a model that is an object or a collection.
26089 * One issue occurs if you want to preselect an option. For example, if you set
26090 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26091 * because the objects are not identical. So by default, you should always reference the item in your collection
26092 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26094 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26095 * of the item not by reference, but by the result of the `track by` expression. For example, if your
26096 * collection items have an id property, you would `track by item.id`.
26098 * A different issue with objects or collections is that ngModel won't detect if an object property or
26099 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26100 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26101 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26102 * has not changed identity, but only a property on the object or an item in the collection changes.
26104 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26105 * if the model is an array). This means that changing a property deeper than the first level inside the
26106 * object/collection will not trigger a re-rendering.
26108 * ## `select` **`as`**
26110 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26111 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26112 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26113 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26116 * ### `select` **`as`** and **`track by`**
26118 * <div class="alert alert-warning">
26119 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26122 * Given this array of items on the $scope:
26125 * $scope.items = [{
26128 * subItem: { name: 'aSubItem' }
26132 * subItem: { name: 'bSubItem' }
26139 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26142 * $scope.selected = $scope.items[0];
26145 * but this will not work:
26148 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26151 * $scope.selected = $scope.items[0].subItem;
26154 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26155 * `items` array. Because the selected option has been set programmatically in the controller, the
26156 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26157 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26158 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26159 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26160 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26163 * @param {string} ngModel Assignable angular expression to data-bind to.
26164 * @param {string=} name Property name of the form under which the control is published.
26165 * @param {string=} required The control is considered valid only if value is entered.
26166 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26167 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26168 * `required` when you want to data-bind to the `required` attribute.
26169 * @param {comprehension_expression=} ngOptions in one of the following forms:
26171 * * for array data sources:
26172 * * `label` **`for`** `value` **`in`** `array`
26173 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26174 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26175 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26176 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26177 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26178 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26179 * (for including a filter with `track by`)
26180 * * for object data sources:
26181 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26182 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26183 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26184 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26185 * * `select` **`as`** `label` **`group by`** `group`
26186 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26187 * * `select` **`as`** `label` **`disable when`** `disable`
26188 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26192 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26193 * * `value`: local variable which will refer to each item in the `array` or each property value
26194 * of `object` during iteration.
26195 * * `key`: local variable which will refer to a property name in `object` during iteration.
26196 * * `label`: The result of this expression will be the label for `<option>` element. The
26197 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26198 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
26199 * element. If not specified, `select` expression will default to `value`.
26200 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
26202 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
26203 * element. Return `true` to disable.
26204 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26205 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
26206 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26207 * even when the options are recreated (e.g. reloaded from the server).
26210 <example module="selectExample">
26211 <file name="index.html">
26213 angular.module('selectExample', [])
26214 .controller('ExampleController', ['$scope', function($scope) {
26216 {name:'black', shade:'dark'},
26217 {name:'white', shade:'light', notAnOption: true},
26218 {name:'red', shade:'dark'},
26219 {name:'blue', shade:'dark', notAnOption: true},
26220 {name:'yellow', shade:'light', notAnOption: false}
26222 $scope.myColor = $scope.colors[2]; // red
26225 <div ng-controller="ExampleController">
26227 <li ng-repeat="color in colors">
26228 <label>Name: <input ng-model="color.name"></label>
26229 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26230 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26233 <button ng-click="colors.push({})">add</button>
26237 <label>Color (null not allowed):
26238 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26240 <label>Color (null allowed):
26241 <span class="nullable">
26242 <select ng-model="myColor" ng-options="color.name for color in colors">
26243 <option value="">-- choose color --</option>
26245 </span></label><br/>
26247 <label>Color grouped by shade:
26248 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26252 <label>Color grouped by shade, with some disabled:
26253 <select ng-model="myColor"
26254 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26260 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26263 Currently selected: {{ {selected_color:myColor} }}
26264 <div style="border:solid 1px black; height:20px"
26265 ng-style="{'background-color':myColor.name}">
26269 <file name="protractor.js" type="protractor">
26270 it('should check ng-options', function() {
26271 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26272 element.all(by.model('myColor')).first().click();
26273 element.all(by.css('select[ng-model="myColor"] option')).first().click();
26274 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26275 element(by.css('.nullable select[ng-model="myColor"]')).click();
26276 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26277 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26283 // jshint maxlen: false
26284 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26285 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
26286 // 1: value expression (valueFn)
26287 // 2: label expression (displayFn)
26288 // 3: group by expression (groupByFn)
26289 // 4: disable when expression (disableWhenFn)
26290 // 5: array item variable name
26291 // 6: object item key variable name
26292 // 7: object item value variable name
26293 // 8: collection expression
26294 // 9: track by expression
26295 // jshint maxlen: 100
26298 var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26300 function parseOptionsExpression(optionsExp, selectElement, scope) {
26302 var match = optionsExp.match(NG_OPTIONS_REGEXP);
26304 throw ngOptionsMinErr('iexp',
26305 "Expected expression in form of " +
26306 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26307 " but got '{0}'. Element: {1}",
26308 optionsExp, startingTag(selectElement));
26311 // Extract the parts from the ngOptions expression
26313 // The variable name for the value of the item in the collection
26314 var valueName = match[5] || match[7];
26315 // The variable name for the key of the item in the collection
26316 var keyName = match[6];
26318 // An expression that generates the viewValue for an option if there is a label expression
26319 var selectAs = / as /.test(match[0]) && match[1];
26320 // An expression that is used to track the id of each object in the options collection
26321 var trackBy = match[9];
26322 // An expression that generates the viewValue for an option if there is no label expression
26323 var valueFn = $parse(match[2] ? match[1] : valueName);
26324 var selectAsFn = selectAs && $parse(selectAs);
26325 var viewValueFn = selectAsFn || valueFn;
26326 var trackByFn = trackBy && $parse(trackBy);
26328 // Get the value by which we are going to track the option
26329 // if we have a trackFn then use that (passing scope and locals)
26330 // otherwise just hash the given viewValue
26331 var getTrackByValueFn = trackBy ?
26332 function(value, locals) { return trackByFn(scope, locals); } :
26333 function getHashOfValue(value) { return hashKey(value); };
26334 var getTrackByValue = function(value, key) {
26335 return getTrackByValueFn(value, getLocals(value, key));
26338 var displayFn = $parse(match[2] || match[1]);
26339 var groupByFn = $parse(match[3] || '');
26340 var disableWhenFn = $parse(match[4] || '');
26341 var valuesFn = $parse(match[8]);
26344 var getLocals = keyName ? function(value, key) {
26345 locals[keyName] = key;
26346 locals[valueName] = value;
26348 } : function(value) {
26349 locals[valueName] = value;
26354 function Option(selectValue, viewValue, label, group, disabled) {
26355 this.selectValue = selectValue;
26356 this.viewValue = viewValue;
26357 this.label = label;
26358 this.group = group;
26359 this.disabled = disabled;
26362 function getOptionValuesKeys(optionValues) {
26363 var optionValuesKeys;
26365 if (!keyName && isArrayLike(optionValues)) {
26366 optionValuesKeys = optionValues;
26368 // if object, extract keys, in enumeration order, unsorted
26369 optionValuesKeys = [];
26370 for (var itemKey in optionValues) {
26371 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26372 optionValuesKeys.push(itemKey);
26376 return optionValuesKeys;
26381 getTrackByValue: getTrackByValue,
26382 getWatchables: $parse(valuesFn, function(optionValues) {
26383 // Create a collection of things that we would like to watch (watchedArray)
26384 // so that they can all be watched using a single $watchCollection
26385 // that only runs the handler once if anything changes
26386 var watchedArray = [];
26387 optionValues = optionValues || [];
26389 var optionValuesKeys = getOptionValuesKeys(optionValues);
26390 var optionValuesLength = optionValuesKeys.length;
26391 for (var index = 0; index < optionValuesLength; index++) {
26392 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26393 var value = optionValues[key];
26395 var locals = getLocals(optionValues[key], key);
26396 var selectValue = getTrackByValueFn(optionValues[key], locals);
26397 watchedArray.push(selectValue);
26399 // Only need to watch the displayFn if there is a specific label expression
26400 if (match[2] || match[1]) {
26401 var label = displayFn(scope, locals);
26402 watchedArray.push(label);
26405 // Only need to watch the disableWhenFn if there is a specific disable expression
26407 var disableWhen = disableWhenFn(scope, locals);
26408 watchedArray.push(disableWhen);
26411 return watchedArray;
26414 getOptions: function() {
26416 var optionItems = [];
26417 var selectValueMap = {};
26419 // The option values were already computed in the `getWatchables` fn,
26420 // which must have been called to trigger `getOptions`
26421 var optionValues = valuesFn(scope) || [];
26422 var optionValuesKeys = getOptionValuesKeys(optionValues);
26423 var optionValuesLength = optionValuesKeys.length;
26425 for (var index = 0; index < optionValuesLength; index++) {
26426 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26427 var value = optionValues[key];
26428 var locals = getLocals(value, key);
26429 var viewValue = viewValueFn(scope, locals);
26430 var selectValue = getTrackByValueFn(viewValue, locals);
26431 var label = displayFn(scope, locals);
26432 var group = groupByFn(scope, locals);
26433 var disabled = disableWhenFn(scope, locals);
26434 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26436 optionItems.push(optionItem);
26437 selectValueMap[selectValue] = optionItem;
26441 items: optionItems,
26442 selectValueMap: selectValueMap,
26443 getOptionFromViewValue: function(value) {
26444 return selectValueMap[getTrackByValue(value)];
26446 getViewValueFromOption: function(option) {
26447 // If the viewValue could be an object that may be mutated by the application,
26448 // we need to make a copy and not return the reference to the value on the option.
26449 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26457 // we can't just jqLite('<option>') since jqLite is not smart enough
26458 // to create it in <select> and IE barfs otherwise.
26459 var optionTemplate = document.createElement('option'),
26460 optGroupTemplate = document.createElement('optgroup');
26463 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26465 // if ngModel is not defined, we don't need to do anything
26466 var ngModelCtrl = ctrls[1];
26467 if (!ngModelCtrl) return;
26469 var selectCtrl = ctrls[0];
26470 var multiple = attr.multiple;
26472 // The emptyOption allows the application developer to provide their own custom "empty"
26473 // option when the viewValue does not match any of the option values.
26475 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26476 if (children[i].value === '') {
26477 emptyOption = children.eq(i);
26482 var providedEmptyOption = !!emptyOption;
26484 var unknownOption = jqLite(optionTemplate.cloneNode(false));
26485 unknownOption.val('?');
26488 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26491 var renderEmptyOption = function() {
26492 if (!providedEmptyOption) {
26493 selectElement.prepend(emptyOption);
26495 selectElement.val('');
26496 emptyOption.prop('selected', true); // needed for IE
26497 emptyOption.attr('selected', true);
26500 var removeEmptyOption = function() {
26501 if (!providedEmptyOption) {
26502 emptyOption.remove();
26507 var renderUnknownOption = function() {
26508 selectElement.prepend(unknownOption);
26509 selectElement.val('?');
26510 unknownOption.prop('selected', true); // needed for IE
26511 unknownOption.attr('selected', true);
26514 var removeUnknownOption = function() {
26515 unknownOption.remove();
26518 // Update the controller methods for multiple selectable options
26521 selectCtrl.writeValue = function writeNgOptionsValue(value) {
26522 var option = options.getOptionFromViewValue(value);
26524 if (option && !option.disabled) {
26525 if (selectElement[0].value !== option.selectValue) {
26526 removeUnknownOption();
26527 removeEmptyOption();
26529 selectElement[0].value = option.selectValue;
26530 option.element.selected = true;
26531 option.element.setAttribute('selected', 'selected');
26534 if (value === null || providedEmptyOption) {
26535 removeUnknownOption();
26536 renderEmptyOption();
26538 removeEmptyOption();
26539 renderUnknownOption();
26544 selectCtrl.readValue = function readNgOptionsValue() {
26546 var selectedOption = options.selectValueMap[selectElement.val()];
26548 if (selectedOption && !selectedOption.disabled) {
26549 removeEmptyOption();
26550 removeUnknownOption();
26551 return options.getViewValueFromOption(selectedOption);
26556 // If we are using `track by` then we must watch the tracked value on the model
26557 // since ngModel only watches for object identity change
26558 if (ngOptions.trackBy) {
26560 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26561 function() { ngModelCtrl.$render(); }
26567 ngModelCtrl.$isEmpty = function(value) {
26568 return !value || value.length === 0;
26572 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26573 options.items.forEach(function(option) {
26574 option.element.selected = false;
26578 value.forEach(function(item) {
26579 var option = options.getOptionFromViewValue(item);
26580 if (option && !option.disabled) option.element.selected = true;
26586 selectCtrl.readValue = function readNgOptionsMultiple() {
26587 var selectedValues = selectElement.val() || [],
26590 forEach(selectedValues, function(value) {
26591 var option = options.selectValueMap[value];
26592 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26598 // If we are using `track by` then we must watch these tracked values on the model
26599 // since ngModel only watches for object identity change
26600 if (ngOptions.trackBy) {
26602 scope.$watchCollection(function() {
26603 if (isArray(ngModelCtrl.$viewValue)) {
26604 return ngModelCtrl.$viewValue.map(function(value) {
26605 return ngOptions.getTrackByValue(value);
26609 ngModelCtrl.$render();
26616 if (providedEmptyOption) {
26618 // we need to remove it before calling selectElement.empty() because otherwise IE will
26619 // remove the label from the element. wtf?
26620 emptyOption.remove();
26622 // compile the element since there might be bindings in it
26623 $compile(emptyOption)(scope);
26625 // remove the class, which is added automatically because we recompile the element and it
26626 // becomes the compilation root
26627 emptyOption.removeClass('ng-scope');
26629 emptyOption = jqLite(optionTemplate.cloneNode(false));
26632 // We need to do this here to ensure that the options object is defined
26633 // when we first hit it in writeNgOptionsValue
26636 // We will re-render the option elements if the option values or labels change
26637 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26639 // ------------------------------------------------------------------ //
26642 function updateOptionElement(option, element) {
26643 option.element = element;
26644 element.disabled = option.disabled;
26645 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26646 // selects in certain circumstances when multiple selects are next to each other and display
26647 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26648 // See https://github.com/angular/angular.js/issues/11314 for more info.
26649 // This is unfortunately untestable with unit / e2e tests
26650 if (option.label !== element.label) {
26651 element.label = option.label;
26652 element.textContent = option.label;
26654 if (option.value !== element.value) element.value = option.selectValue;
26657 function addOrReuseElement(parent, current, type, templateElement) {
26659 // Check whether we can reuse the next element
26660 if (current && lowercase(current.nodeName) === type) {
26661 // The next element is the right type so reuse it
26664 // The next element is not the right type so create a new one
26665 element = templateElement.cloneNode(false);
26667 // There are no more elements so just append it to the select
26668 parent.appendChild(element);
26670 // The next element is not a group so insert the new one
26671 parent.insertBefore(element, current);
26678 function removeExcessElements(current) {
26681 next = current.nextSibling;
26682 jqLiteRemove(current);
26688 function skipEmptyAndUnknownOptions(current) {
26689 var emptyOption_ = emptyOption && emptyOption[0];
26690 var unknownOption_ = unknownOption && unknownOption[0];
26692 // We cannot rely on the extracted empty option being the same as the compiled empty option,
26693 // because the compiled empty option might have been replaced by a comment because
26694 // it had an "element" transclusion directive on it (such as ngIf)
26695 if (emptyOption_ || unknownOption_) {
26697 (current === emptyOption_ ||
26698 current === unknownOption_ ||
26699 current.nodeType === NODE_TYPE_COMMENT ||
26700 current.value === '')) {
26701 current = current.nextSibling;
26708 function updateOptions() {
26710 var previousValue = options && selectCtrl.readValue();
26712 options = ngOptions.getOptions();
26715 var currentElement = selectElement[0].firstChild;
26717 // Ensure that the empty option is always there if it was explicitly provided
26718 if (providedEmptyOption) {
26719 selectElement.prepend(emptyOption);
26722 currentElement = skipEmptyAndUnknownOptions(currentElement);
26724 options.items.forEach(function updateOption(option) {
26729 if (option.group) {
26731 // This option is to live in a group
26732 // See if we have already created this group
26733 group = groupMap[option.group];
26737 // We have not already created this group
26738 groupElement = addOrReuseElement(selectElement[0],
26742 // Move to the next element
26743 currentElement = groupElement.nextSibling;
26745 // Update the label on the group element
26746 groupElement.label = option.group;
26748 // Store it for use later
26749 group = groupMap[option.group] = {
26750 groupElement: groupElement,
26751 currentOptionElement: groupElement.firstChild
26756 // So now we have a group for this option we add the option to the group
26757 optionElement = addOrReuseElement(group.groupElement,
26758 group.currentOptionElement,
26761 updateOptionElement(option, optionElement);
26762 // Move to the next element
26763 group.currentOptionElement = optionElement.nextSibling;
26767 // This option is not in a group
26768 optionElement = addOrReuseElement(selectElement[0],
26772 updateOptionElement(option, optionElement);
26773 // Move to the next element
26774 currentElement = optionElement.nextSibling;
26779 // Now remove all excess options and group
26780 Object.keys(groupMap).forEach(function(key) {
26781 removeExcessElements(groupMap[key].currentOptionElement);
26783 removeExcessElements(currentElement);
26785 ngModelCtrl.$render();
26787 // Check to see if the value has changed due to the update to the options
26788 if (!ngModelCtrl.$isEmpty(previousValue)) {
26789 var nextValue = selectCtrl.readValue();
26790 if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26791 ngModelCtrl.$setViewValue(nextValue);
26792 ngModelCtrl.$render();
26802 require: ['select', '?ngModel'],
26804 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
26805 // Deactivate the SelectController.register method to prevent
26806 // option directives from accidentally registering themselves
26807 // (and unwanted $destroy handlers etc.)
26808 ctrls[0].registerOption = noop;
26810 post: ngOptionsPostLink
26817 * @name ngPluralize
26821 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26822 * These rules are bundled with angular.js, but can be overridden
26823 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26824 * by specifying the mappings between
26825 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26826 * and the strings to be displayed.
26828 * # Plural categories and explicit number rules
26830 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26831 * in Angular's default en-US locale: "one" and "other".
26833 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26834 * any number that is not 1), an explicit number rule can only match one number. For example, the
26835 * explicit number rule for "3" matches the number 3. There are examples of plural categories
26836 * and explicit number rules throughout the rest of this documentation.
26838 * # Configuring ngPluralize
26839 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26840 * You can also provide an optional attribute, `offset`.
26842 * The value of the `count` attribute can be either a string or an {@link guide/expression
26843 * Angular expression}; these are evaluated on the current scope for its bound value.
26845 * The `when` attribute specifies the mappings between plural categories and the actual
26846 * string to be displayed. The value of the attribute should be a JSON object.
26848 * The following example shows how to configure ngPluralize:
26851 * <ng-pluralize count="personCount"
26852 when="{'0': 'Nobody is viewing.',
26853 * 'one': '1 person is viewing.',
26854 * 'other': '{} people are viewing.'}">
26858 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26859 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26860 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26861 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26862 * show "a dozen people are viewing".
26864 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26865 * into pluralized strings. In the previous example, Angular will replace `{}` with
26866 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26867 * for <span ng-non-bindable>{{numberExpression}}</span>.
26869 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26870 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26872 * # Configuring ngPluralize with offset
26873 * The `offset` attribute allows further customization of pluralized text, which can result in
26874 * a better user experience. For example, instead of the message "4 people are viewing this document",
26875 * you might display "John, Kate and 2 others are viewing this document".
26876 * The offset attribute allows you to offset a number by any desired value.
26877 * Let's take a look at an example:
26880 * <ng-pluralize count="personCount" offset=2
26881 * when="{'0': 'Nobody is viewing.',
26882 * '1': '{{person1}} is viewing.',
26883 * '2': '{{person1}} and {{person2}} are viewing.',
26884 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
26885 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26889 * Notice that we are still using two plural categories(one, other), but we added
26890 * three explicit number rules 0, 1 and 2.
26891 * When one person, perhaps John, views the document, "John is viewing" will be shown.
26892 * When three people view the document, no explicit number rule is found, so
26893 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26894 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26897 * Note that when you specify offsets, you must provide explicit number rules for
26898 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26899 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26900 * plural categories "one" and "other".
26902 * @param {string|expression} count The variable to be bound to.
26903 * @param {string} when The mapping between plural category to its corresponding strings.
26904 * @param {number=} offset Offset to deduct from the total number.
26907 <example module="pluralizeExample">
26908 <file name="index.html">
26910 angular.module('pluralizeExample', [])
26911 .controller('ExampleController', ['$scope', function($scope) {
26912 $scope.person1 = 'Igor';
26913 $scope.person2 = 'Misko';
26914 $scope.personCount = 1;
26917 <div ng-controller="ExampleController">
26918 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
26919 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
26920 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
26922 <!--- Example with simple pluralization rules for en locale --->
26924 <ng-pluralize count="personCount"
26925 when="{'0': 'Nobody is viewing.',
26926 'one': '1 person is viewing.',
26927 'other': '{} people are viewing.'}">
26928 </ng-pluralize><br>
26930 <!--- Example with offset --->
26932 <ng-pluralize count="personCount" offset=2
26933 when="{'0': 'Nobody is viewing.',
26934 '1': '{{person1}} is viewing.',
26935 '2': '{{person1}} and {{person2}} are viewing.',
26936 'one': '{{person1}}, {{person2}} and one other person are viewing.',
26937 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26941 <file name="protractor.js" type="protractor">
26942 it('should show correct pluralized string', function() {
26943 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
26944 var withOffset = element.all(by.css('ng-pluralize')).get(1);
26945 var countInput = element(by.model('personCount'));
26947 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
26948 expect(withOffset.getText()).toEqual('Igor is viewing.');
26950 countInput.clear();
26951 countInput.sendKeys('0');
26953 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
26954 expect(withOffset.getText()).toEqual('Nobody is viewing.');
26956 countInput.clear();
26957 countInput.sendKeys('2');
26959 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
26960 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
26962 countInput.clear();
26963 countInput.sendKeys('3');
26965 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
26966 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
26968 countInput.clear();
26969 countInput.sendKeys('4');
26971 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
26972 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
26974 it('should show data-bound names', function() {
26975 var withOffset = element.all(by.css('ng-pluralize')).get(1);
26976 var personCount = element(by.model('personCount'));
26977 var person1 = element(by.model('person1'));
26978 var person2 = element(by.model('person2'));
26979 personCount.clear();
26980 personCount.sendKeys('4');
26982 person1.sendKeys('Di');
26984 person2.sendKeys('Vojta');
26985 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
26990 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
26992 IS_WHEN = /^when(Minus)?(.+)$/;
26995 link: function(scope, element, attr) {
26996 var numberExp = attr.count,
26997 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
26998 offset = attr.offset || 0,
26999 whens = scope.$eval(whenExp) || {},
27001 startSymbol = $interpolate.startSymbol(),
27002 endSymbol = $interpolate.endSymbol(),
27003 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27004 watchRemover = angular.noop,
27007 forEach(attr, function(expression, attributeName) {
27008 var tmpMatch = IS_WHEN.exec(attributeName);
27010 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27011 whens[whenKey] = element.attr(attr.$attr[attributeName]);
27014 forEach(whens, function(expression, key) {
27015 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27019 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27020 var count = parseFloat(newVal);
27021 var countIsNaN = isNaN(count);
27023 if (!countIsNaN && !(count in whens)) {
27024 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27025 // Otherwise, check it against pluralization rules in $locale service.
27026 count = $locale.pluralCat(count - offset);
27029 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27030 // In JS `NaN !== NaN`, so we have to exlicitly check.
27031 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27033 var whenExpFn = whensExpFns[count];
27034 if (isUndefined(whenExpFn)) {
27035 if (newVal != null) {
27036 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27038 watchRemover = noop;
27039 updateElementText();
27041 watchRemover = scope.$watch(whenExpFn, updateElementText);
27047 function updateElementText(newText) {
27048 element.text(newText || '');
27060 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27061 * instance gets its own scope, where the given loop variable is set to the current collection item,
27062 * and `$index` is set to the item index or key.
27064 * Special properties are exposed on the local scope of each template instance, including:
27066 * | Variable | Type | Details |
27067 * |-----------|-----------------|-----------------------------------------------------------------------------|
27068 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
27069 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
27070 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27071 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
27072 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
27073 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
27075 * <div class="alert alert-info">
27076 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27077 * This may be useful when, for instance, nesting ngRepeats.
27081 * # Iterating over object properties
27083 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27087 * <div ng-repeat="(key, value) in myObj"> ... </div>
27090 * You need to be aware that the JavaScript specification does not define the order of keys
27091 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27092 * used to sort the keys alphabetically.)
27094 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27095 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27096 * keys in the order in which they were defined, although there are exceptions when keys are deleted
27097 * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
27099 * If this is not desired, the recommended workaround is to convert your object into an array
27100 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
27101 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27102 * or implement a `$watch` on the object yourself.
27105 * # Tracking and Duplicates
27107 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27108 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27110 * * When an item is added, a new instance of the template is added to the DOM.
27111 * * When an item is removed, its template instance is removed from the DOM.
27112 * * When items are reordered, their respective templates are reordered in the DOM.
27114 * To minimize creation of DOM elements, `ngRepeat` uses a function
27115 * to "keep track" of all items in the collection and their corresponding DOM elements.
27116 * For example, if an item is added to the collection, ngRepeat will know that all other items
27117 * already have DOM elements, and will not re-render them.
27119 * The default tracking function (which tracks items by their identity) does not allow
27120 * duplicate items in arrays. This is because when there are duplicates, it is not possible
27121 * to maintain a one-to-one mapping between collection items and DOM elements.
27123 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27124 * with your own using the `track by` expression.
27126 * For example, you may track items by the index of each item in the collection, using the
27127 * special scope property `$index`:
27129 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27134 * You may also use arbitrary expressions in `track by`, including references to custom functions
27137 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27142 * <div class="alert alert-success">
27143 * If you are working with objects that have an identifier property, you should track
27144 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27145 * will not have to rebuild the DOM elements for items it has already rendered, even if the
27146 * JavaScript objects in the collection have been substituted for new ones. For large collections,
27147 * this signifincantly improves rendering performance. If you don't have a unique identifier,
27148 * `track by $index` can also provide a performance boost.
27151 * <div ng-repeat="model in collection track by model.id">
27156 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27157 * `$id` function, which tracks items by their identity:
27159 * <div ng-repeat="obj in collection track by $id(obj)">
27164 * <div class="alert alert-warning">
27165 * **Note:** `track by` must always be the last expression:
27168 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27173 * # Special repeat start and end points
27174 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27175 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27176 * 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)
27177 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27179 * The example below makes use of this feature:
27181 * <header ng-repeat-start="item in items">
27182 * Header {{ item }}
27184 * <div class="body">
27187 * <footer ng-repeat-end>
27188 * Footer {{ item }}
27192 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27197 * <div class="body">
27206 * <div class="body">
27214 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27215 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27218 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27220 * **.leave** - when an item is removed from the list or when an item is filtered out
27222 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27227 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27228 * formats are currently supported:
27230 * * `variable in expression` – where variable is the user defined loop variable and `expression`
27231 * is a scope expression giving the collection to enumerate.
27233 * For example: `album in artist.albums`.
27235 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27236 * and `expression` is the scope expression giving the collection to enumerate.
27238 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
27240 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27241 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27242 * is specified, ng-repeat associates elements by identity. It is an error to have
27243 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27244 * mapped to the same DOM element, which is not possible.)
27246 * Note that the tracking expression must come last, after any filters, and the alias expression.
27248 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27249 * will be associated by item identity in the array.
27251 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27252 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27253 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27254 * element in the same way in the DOM.
27256 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27257 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27258 * property is same.
27260 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27261 * to items in conjunction with a tracking expression.
27263 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27264 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27265 * when a filter is active on the repeater, but the filtered result set is empty.
27267 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27268 * the items have been processed through the filter.
27270 * Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
27271 * (and not as operator, inside an expression).
27273 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27276 * This example initializes the scope to a list of names and
27277 * then uses `ngRepeat` to display every person:
27278 <example module="ngAnimate" deps="angular-animate.js" animations="true">
27279 <file name="index.html">
27280 <div ng-init="friends = [
27281 {name:'John', age:25, gender:'boy'},
27282 {name:'Jessie', age:30, gender:'girl'},
27283 {name:'Johanna', age:28, gender:'girl'},
27284 {name:'Joy', age:15, gender:'girl'},
27285 {name:'Mary', age:28, gender:'girl'},
27286 {name:'Peter', age:95, gender:'boy'},
27287 {name:'Sebastian', age:50, gender:'boy'},
27288 {name:'Erika', age:27, gender:'girl'},
27289 {name:'Patrick', age:40, gender:'boy'},
27290 {name:'Samantha', age:60, gender:'girl'}
27292 I have {{friends.length}} friends. They are:
27293 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27294 <ul class="example-animate-container">
27295 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27296 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27298 <li class="animate-repeat" ng-if="results.length == 0">
27299 <strong>No results found...</strong>
27304 <file name="animations.css">
27305 .example-animate-container {
27307 border:1px solid black;
27316 box-sizing:border-box;
27319 .animate-repeat.ng-move,
27320 .animate-repeat.ng-enter,
27321 .animate-repeat.ng-leave {
27322 transition:all linear 0.5s;
27325 .animate-repeat.ng-leave.ng-leave-active,
27326 .animate-repeat.ng-move,
27327 .animate-repeat.ng-enter {
27332 .animate-repeat.ng-leave,
27333 .animate-repeat.ng-move.ng-move-active,
27334 .animate-repeat.ng-enter.ng-enter-active {
27339 <file name="protractor.js" type="protractor">
27340 var friends = element.all(by.repeater('friend in friends'));
27342 it('should render initial data set', function() {
27343 expect(friends.count()).toBe(10);
27344 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27345 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27346 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27347 expect(element(by.binding('friends.length')).getText())
27348 .toMatch("I have 10 friends. They are:");
27351 it('should update repeater when filter predicate changes', function() {
27352 expect(friends.count()).toBe(10);
27354 element(by.model('q')).sendKeys('ma');
27356 expect(friends.count()).toBe(2);
27357 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27358 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27363 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27364 var NG_REMOVED = '$$NG_REMOVED';
27365 var ngRepeatMinErr = minErr('ngRepeat');
27367 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27368 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27369 scope[valueIdentifier] = value;
27370 if (keyIdentifier) scope[keyIdentifier] = key;
27371 scope.$index = index;
27372 scope.$first = (index === 0);
27373 scope.$last = (index === (arrayLength - 1));
27374 scope.$middle = !(scope.$first || scope.$last);
27375 // jshint bitwise: false
27376 scope.$odd = !(scope.$even = (index&1) === 0);
27377 // jshint bitwise: true
27380 var getBlockStart = function(block) {
27381 return block.clone[0];
27384 var getBlockEnd = function(block) {
27385 return block.clone[block.clone.length - 1];
27391 multiElement: true,
27392 transclude: 'element',
27396 compile: function ngRepeatCompile($element, $attr) {
27397 var expression = $attr.ngRepeat;
27398 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27400 var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
27403 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27407 var lhs = match[1];
27408 var rhs = match[2];
27409 var aliasAs = match[3];
27410 var trackByExp = match[4];
27412 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27415 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27418 var valueIdentifier = match[3] || match[1];
27419 var keyIdentifier = match[2];
27421 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27422 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27423 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27427 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27428 var hashFnLocals = {$id: hashKey};
27431 trackByExpGetter = $parse(trackByExp);
27433 trackByIdArrayFn = function(key, value) {
27434 return hashKey(value);
27436 trackByIdObjFn = function(key) {
27441 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27443 if (trackByExpGetter) {
27444 trackByIdExpFn = function(key, value, index) {
27445 // assign key, value, and $index to the locals so that they can be used in hash functions
27446 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27447 hashFnLocals[valueIdentifier] = value;
27448 hashFnLocals.$index = index;
27449 return trackByExpGetter($scope, hashFnLocals);
27453 // Store a list of elements from previous run. This is a hash where key is the item from the
27454 // iterator, and the value is objects with following properties.
27455 // - scope: bound scope
27456 // - element: previous element.
27457 // - index: position
27459 // We are using no-proto object so that we don't need to guard against inherited props via
27461 var lastBlockMap = createMap();
27464 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27466 previousNode = $element[0], // node that cloned nodes should be inserted after
27467 // initialized to the comment node anchor
27469 // Same as lastBlockMap but it has the current state. It will become the
27470 // lastBlockMap on the next iteration.
27471 nextBlockMap = createMap(),
27473 key, value, // key/value of iteration
27477 block, // last object information {scope, element, id}
27482 $scope[aliasAs] = collection;
27485 if (isArrayLike(collection)) {
27486 collectionKeys = collection;
27487 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27489 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27490 // if object, extract keys, in enumeration order, unsorted
27491 collectionKeys = [];
27492 for (var itemKey in collection) {
27493 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27494 collectionKeys.push(itemKey);
27499 collectionLength = collectionKeys.length;
27500 nextBlockOrder = new Array(collectionLength);
27502 // locate existing items
27503 for (index = 0; index < collectionLength; index++) {
27504 key = (collection === collectionKeys) ? index : collectionKeys[index];
27505 value = collection[key];
27506 trackById = trackByIdFn(key, value, index);
27507 if (lastBlockMap[trackById]) {
27508 // found previously seen block
27509 block = lastBlockMap[trackById];
27510 delete lastBlockMap[trackById];
27511 nextBlockMap[trackById] = block;
27512 nextBlockOrder[index] = block;
27513 } else if (nextBlockMap[trackById]) {
27514 // if collision detected. restore lastBlockMap and throw an error
27515 forEach(nextBlockOrder, function(block) {
27516 if (block && block.scope) lastBlockMap[block.id] = block;
27518 throw ngRepeatMinErr('dupes',
27519 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27520 expression, trackById, value);
27522 // new never before seen block
27523 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27524 nextBlockMap[trackById] = true;
27528 // remove leftover items
27529 for (var blockKey in lastBlockMap) {
27530 block = lastBlockMap[blockKey];
27531 elementsToRemove = getBlockNodes(block.clone);
27532 $animate.leave(elementsToRemove);
27533 if (elementsToRemove[0].parentNode) {
27534 // if the element was not removed yet because of pending animation, mark it as deleted
27535 // so that we can ignore it later
27536 for (index = 0, length = elementsToRemove.length; index < length; index++) {
27537 elementsToRemove[index][NG_REMOVED] = true;
27540 block.scope.$destroy();
27543 // we are not using forEach for perf reasons (trying to avoid #call)
27544 for (index = 0; index < collectionLength; index++) {
27545 key = (collection === collectionKeys) ? index : collectionKeys[index];
27546 value = collection[key];
27547 block = nextBlockOrder[index];
27550 // if we have already seen this object, then we need to reuse the
27551 // associated scope/element
27553 nextNode = previousNode;
27555 // skip nodes that are already pending removal via leave animation
27557 nextNode = nextNode.nextSibling;
27558 } while (nextNode && nextNode[NG_REMOVED]);
27560 if (getBlockStart(block) != nextNode) {
27561 // existing item which got moved
27562 $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27564 previousNode = getBlockEnd(block);
27565 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27567 // new item which we don't know about
27568 $transclude(function ngRepeatTransclude(clone, scope) {
27569 block.scope = scope;
27570 // http://jsperf.com/clone-vs-createcomment
27571 var endNode = ngRepeatEndComment.cloneNode(false);
27572 clone[clone.length++] = endNode;
27574 // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27575 $animate.enter(clone, null, jqLite(previousNode));
27576 previousNode = endNode;
27577 // Note: We only need the first/last node of the cloned nodes.
27578 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27579 // by a directive with templateUrl when its template arrives.
27580 block.clone = clone;
27581 nextBlockMap[block.id] = block;
27582 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27586 lastBlockMap = nextBlockMap;
27593 var NG_HIDE_CLASS = 'ng-hide';
27594 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27601 * The `ngShow` directive shows or hides the given HTML element based on the expression
27602 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27603 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27604 * in AngularJS and sets the display style to none (using an !important flag).
27605 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27608 * <!-- when $scope.myValue is truthy (element is visible) -->
27609 * <div ng-show="myValue"></div>
27611 * <!-- when $scope.myValue is falsy (element is hidden) -->
27612 * <div ng-show="myValue" class="ng-hide"></div>
27615 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27616 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27617 * from the element causing the element not to appear hidden.
27619 * ## Why is !important used?
27621 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27622 * can be easily overridden by heavier selectors. For example, something as simple
27623 * as changing the display style on a HTML list item would make hidden elements appear visible.
27624 * This also becomes a bigger issue when dealing with CSS frameworks.
27626 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27627 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27628 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27630 * ### Overriding `.ng-hide`
27632 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27633 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27634 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27635 * with extra animation classes that can be added.
27638 * .ng-hide:not(.ng-hide-animate) {
27639 * /* this is just another form of hiding an element */
27640 * display: block!important;
27641 * position: absolute;
27647 * By default you don't need to override in CSS anything and the animations will work around the display style.
27649 * ## A note about animations with `ngShow`
27651 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27652 * is true and false. This system works like the animation system present with ngClass except that
27653 * you must also include the !important flag to override the display property
27654 * so that you can perform an animation when the element is hidden during the time of the animation.
27658 * //a working example can be found at the bottom of this page
27660 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27661 * /* this is required as of 1.3x to properly
27662 * apply all styling in a show/hide animation */
27663 * transition: 0s linear all;
27666 * .my-element.ng-hide-add-active,
27667 * .my-element.ng-hide-remove-active {
27668 * /* the transition is defined in the active class */
27669 * transition: 1s linear all;
27672 * .my-element.ng-hide-add { ... }
27673 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27674 * .my-element.ng-hide-remove { ... }
27675 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27678 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27679 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27682 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27683 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27686 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27687 * then the element is shown or hidden respectively.
27690 <example module="ngAnimate" deps="angular-animate.js" animations="true">
27691 <file name="index.html">
27692 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27695 <div class="check-element animate-show" ng-show="checked">
27696 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27701 <div class="check-element animate-show" ng-hide="checked">
27702 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27706 <file name="glyphicons.css">
27707 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27709 <file name="animations.css">
27714 border: 1px solid black;
27718 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27719 transition: all linear 0.5s;
27722 .animate-show.ng-hide {
27730 border: 1px solid black;
27734 <file name="protractor.js" type="protractor">
27735 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27736 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27738 it('should check ng-show / ng-hide', function() {
27739 expect(thumbsUp.isDisplayed()).toBeFalsy();
27740 expect(thumbsDown.isDisplayed()).toBeTruthy();
27742 element(by.model('checked')).click();
27744 expect(thumbsUp.isDisplayed()).toBeTruthy();
27745 expect(thumbsDown.isDisplayed()).toBeFalsy();
27750 var ngShowDirective = ['$animate', function($animate) {
27753 multiElement: true,
27754 link: function(scope, element, attr) {
27755 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27756 // we're adding a temporary, animation-specific class for ng-hide since this way
27757 // we can control when the element is actually displayed on screen without having
27758 // to have a global/greedy CSS selector that breaks when other animations are run.
27759 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27760 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27761 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27775 * The `ngHide` directive shows or hides the given HTML element based on the expression
27776 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27777 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27778 * in AngularJS and sets the display style to none (using an !important flag).
27779 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27782 * <!-- when $scope.myValue is truthy (element is hidden) -->
27783 * <div ng-hide="myValue" class="ng-hide"></div>
27785 * <!-- when $scope.myValue is falsy (element is visible) -->
27786 * <div ng-hide="myValue"></div>
27789 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27790 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27791 * from the element causing the element not to appear hidden.
27793 * ## Why is !important used?
27795 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27796 * can be easily overridden by heavier selectors. For example, something as simple
27797 * as changing the display style on a HTML list item would make hidden elements appear visible.
27798 * This also becomes a bigger issue when dealing with CSS frameworks.
27800 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27801 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27802 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27804 * ### Overriding `.ng-hide`
27806 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27807 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27812 * /* this is just another form of hiding an element */
27813 * display: block!important;
27814 * position: absolute;
27820 * By default you don't need to override in CSS anything and the animations will work around the display style.
27822 * ## A note about animations with `ngHide`
27824 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27825 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27826 * CSS class is added and removed for you instead of your own CSS class.
27830 * //a working example can be found at the bottom of this page
27832 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27833 * transition: 0.5s linear all;
27836 * .my-element.ng-hide-add { ... }
27837 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27838 * .my-element.ng-hide-remove { ... }
27839 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27842 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27843 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27846 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27847 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27850 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27851 * the element is shown or hidden respectively.
27854 <example module="ngAnimate" deps="angular-animate.js" animations="true">
27855 <file name="index.html">
27856 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27859 <div class="check-element animate-hide" ng-show="checked">
27860 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27865 <div class="check-element animate-hide" ng-hide="checked">
27866 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27870 <file name="glyphicons.css">
27871 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27873 <file name="animations.css">
27875 transition: all linear 0.5s;
27879 border: 1px solid black;
27883 .animate-hide.ng-hide {
27891 border: 1px solid black;
27895 <file name="protractor.js" type="protractor">
27896 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27897 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27899 it('should check ng-show / ng-hide', function() {
27900 expect(thumbsUp.isDisplayed()).toBeFalsy();
27901 expect(thumbsDown.isDisplayed()).toBeTruthy();
27903 element(by.model('checked')).click();
27905 expect(thumbsUp.isDisplayed()).toBeTruthy();
27906 expect(thumbsDown.isDisplayed()).toBeFalsy();
27911 var ngHideDirective = ['$animate', function($animate) {
27914 multiElement: true,
27915 link: function(scope, element, attr) {
27916 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
27917 // The comment inside of the ngShowDirective explains why we add and
27918 // remove a temporary class for the show/hide animation
27919 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
27920 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27933 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
27936 * @param {expression} ngStyle
27938 * {@link guide/expression Expression} which evals to an
27939 * object whose keys are CSS style names and values are corresponding values for those CSS
27942 * Since some CSS style names are not valid keys for an object, they must be quoted.
27943 * See the 'background-color' style in the example below.
27947 <file name="index.html">
27948 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
27949 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
27950 <input type="button" value="clear" ng-click="myStyle={}">
27952 <span ng-style="myStyle">Sample Text</span>
27953 <pre>myStyle={{myStyle}}</pre>
27955 <file name="style.css">
27960 <file name="protractor.js" type="protractor">
27961 var colorSpan = element(by.css('span'));
27963 it('should check ng-style', function() {
27964 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
27965 element(by.css('input[value=\'set color\']')).click();
27966 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
27967 element(by.css('input[value=clear]')).click();
27968 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
27973 var ngStyleDirective = ngDirective(function(scope, element, attr) {
27974 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
27975 if (oldStyles && (newStyles !== oldStyles)) {
27976 forEach(oldStyles, function(val, style) { element.css(style, '');});
27978 if (newStyles) element.css(newStyles);
27988 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
27989 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
27990 * as specified in the template.
27992 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
27993 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
27994 * matches the value obtained from the evaluated expression. In other words, you define a container element
27995 * (where you place the directive), place an expression on the **`on="..."` attribute**
27996 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
27997 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
27998 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
27999 * attribute is displayed.
28001 * <div class="alert alert-info">
28002 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28003 * as literal string values to match against.
28004 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28005 * value of the expression `$scope.someVal`.
28009 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28010 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28015 * <ANY ng-switch="expression">
28016 * <ANY ng-switch-when="matchValue1">...</ANY>
28017 * <ANY ng-switch-when="matchValue2">...</ANY>
28018 * <ANY ng-switch-default>...</ANY>
28025 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28026 * On child elements add:
28028 * * `ngSwitchWhen`: the case statement to match against. If match then this
28029 * case will be displayed. If the same match appears multiple times, all the
28030 * elements will be displayed.
28031 * * `ngSwitchDefault`: the default case when no other case match. If there
28032 * are multiple default cases, all of them will be displayed when no other
28037 <example module="switchExample" deps="angular-animate.js" animations="true">
28038 <file name="index.html">
28039 <div ng-controller="ExampleController">
28040 <select ng-model="selection" ng-options="item for item in items">
28042 <code>selection={{selection}}</code>
28044 <div class="animate-switch-container"
28045 ng-switch on="selection">
28046 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28047 <div class="animate-switch" ng-switch-when="home">Home Span</div>
28048 <div class="animate-switch" ng-switch-default>default</div>
28052 <file name="script.js">
28053 angular.module('switchExample', ['ngAnimate'])
28054 .controller('ExampleController', ['$scope', function($scope) {
28055 $scope.items = ['settings', 'home', 'other'];
28056 $scope.selection = $scope.items[0];
28059 <file name="animations.css">
28060 .animate-switch-container {
28063 border:1px solid black;
28072 .animate-switch.ng-animate {
28073 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28082 .animate-switch.ng-leave.ng-leave-active,
28083 .animate-switch.ng-enter {
28086 .animate-switch.ng-leave,
28087 .animate-switch.ng-enter.ng-enter-active {
28091 <file name="protractor.js" type="protractor">
28092 var switchElem = element(by.css('[ng-switch]'));
28093 var select = element(by.model('selection'));
28095 it('should start in settings', function() {
28096 expect(switchElem.getText()).toMatch(/Settings Div/);
28098 it('should change to home', function() {
28099 select.all(by.css('option')).get(1).click();
28100 expect(switchElem.getText()).toMatch(/Home Span/);
28102 it('should select default', function() {
28103 select.all(by.css('option')).get(2).click();
28104 expect(switchElem.getText()).toMatch(/default/);
28109 var ngSwitchDirective = ['$animate', function($animate) {
28111 require: 'ngSwitch',
28113 // asks for $scope to fool the BC controller module
28114 controller: ['$scope', function ngSwitchController() {
28117 link: function(scope, element, attr, ngSwitchController) {
28118 var watchExpr = attr.ngSwitch || attr.on,
28119 selectedTranscludes = [],
28120 selectedElements = [],
28121 previousLeaveAnimations = [],
28122 selectedScopes = [];
28124 var spliceFactory = function(array, index) {
28125 return function() { array.splice(index, 1); };
28128 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28130 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28131 $animate.cancel(previousLeaveAnimations[i]);
28133 previousLeaveAnimations.length = 0;
28135 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28136 var selected = getBlockNodes(selectedElements[i].clone);
28137 selectedScopes[i].$destroy();
28138 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28139 promise.then(spliceFactory(previousLeaveAnimations, i));
28142 selectedElements.length = 0;
28143 selectedScopes.length = 0;
28145 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28146 forEach(selectedTranscludes, function(selectedTransclude) {
28147 selectedTransclude.transclude(function(caseElement, selectedScope) {
28148 selectedScopes.push(selectedScope);
28149 var anchor = selectedTransclude.element;
28150 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28151 var block = { clone: caseElement };
28153 selectedElements.push(block);
28154 $animate.enter(caseElement, anchor.parent(), anchor);
28163 var ngSwitchWhenDirective = ngDirective({
28164 transclude: 'element',
28166 require: '^ngSwitch',
28167 multiElement: true,
28168 link: function(scope, element, attrs, ctrl, $transclude) {
28169 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28170 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28174 var ngSwitchDefaultDirective = ngDirective({
28175 transclude: 'element',
28177 require: '^ngSwitch',
28178 multiElement: true,
28179 link: function(scope, element, attr, ctrl, $transclude) {
28180 ctrl.cases['?'] = (ctrl.cases['?'] || []);
28181 ctrl.cases['?'].push({ transclude: $transclude, element: element });
28187 * @name ngTransclude
28191 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28193 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28198 <example module="transcludeExample">
28199 <file name="index.html">
28201 angular.module('transcludeExample', [])
28202 .directive('pane', function(){
28206 scope: { title:'@' },
28207 template: '<div style="border: 1px solid black;">' +
28208 '<div style="background-color: gray">{{title}}</div>' +
28209 '<ng-transclude></ng-transclude>' +
28213 .controller('ExampleController', ['$scope', function($scope) {
28214 $scope.title = 'Lorem Ipsum';
28215 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28218 <div ng-controller="ExampleController">
28219 <input ng-model="title" aria-label="title"> <br/>
28220 <textarea ng-model="text" aria-label="text"></textarea> <br/>
28221 <pane title="{{title}}">{{text}}</pane>
28224 <file name="protractor.js" type="protractor">
28225 it('should have transcluded', function() {
28226 var titleElement = element(by.model('title'));
28227 titleElement.clear();
28228 titleElement.sendKeys('TITLE');
28229 var textElement = element(by.model('text'));
28230 textElement.clear();
28231 textElement.sendKeys('TEXT');
28232 expect(element(by.binding('title')).getText()).toEqual('TITLE');
28233 expect(element(by.binding('text')).getText()).toEqual('TEXT');
28239 var ngTranscludeDirective = ngDirective({
28241 link: function($scope, $element, $attrs, controller, $transclude) {
28242 if (!$transclude) {
28243 throw minErr('ngTransclude')('orphan',
28244 'Illegal use of ngTransclude directive in the template! ' +
28245 'No parent directive that requires a transclusion found. ' +
28247 startingTag($element));
28250 $transclude(function(clone) {
28252 $element.append(clone);
28263 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28264 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28265 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28266 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28267 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28269 * @param {string} type Must be set to `'text/ng-template'`.
28270 * @param {string} id Cache name of the template.
28274 <file name="index.html">
28275 <script type="text/ng-template" id="/tpl.html">
28276 Content of the template.
28279 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28280 <div id="tpl-content" ng-include src="currentTpl"></div>
28282 <file name="protractor.js" type="protractor">
28283 it('should load template defined inside script tag', function() {
28284 element(by.css('#tpl-link')).click();
28285 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28290 var scriptDirective = ['$templateCache', function($templateCache) {
28294 compile: function(element, attr) {
28295 if (attr.type == 'text/ng-template') {
28296 var templateUrl = attr.id,
28297 text = element[0].text;
28299 $templateCache.put(templateUrl, text);
28305 var noopNgModelController = { $setViewValue: noop, $render: noop };
28307 function chromeHack(optionElement) {
28308 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28309 // Adding an <option selected="selected"> element to a <select required="required"> should
28310 // automatically select the new element
28311 if (optionElement[0].hasAttribute('selected')) {
28312 optionElement[0].selected = true;
28318 * @name select.SelectController
28320 * The controller for the `<select>` directive. This provides support for reading
28321 * and writing the selected value(s) of the control and also coordinates dynamically
28322 * added `<option>` elements, perhaps by an `ngRepeat` directive.
28324 var SelectController =
28325 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28328 optionsMap = new HashMap();
28330 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28331 self.ngModelCtrl = noopNgModelController;
28333 // The "unknown" option is one that is prepended to the list if the viewValue
28334 // does not match any of the options. When it is rendered the value of the unknown
28335 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28337 // We can't just jqLite('<option>') since jqLite is not smart enough
28338 // to create it in <select> and IE barfs otherwise.
28339 self.unknownOption = jqLite(document.createElement('option'));
28340 self.renderUnknownOption = function(val) {
28341 var unknownVal = '? ' + hashKey(val) + ' ?';
28342 self.unknownOption.val(unknownVal);
28343 $element.prepend(self.unknownOption);
28344 $element.val(unknownVal);
28347 $scope.$on('$destroy', function() {
28348 // disable unknown option so that we don't do work when the whole select is being destroyed
28349 self.renderUnknownOption = noop;
28352 self.removeUnknownOption = function() {
28353 if (self.unknownOption.parent()) self.unknownOption.remove();
28357 // Read the value of the select control, the implementation of this changes depending
28358 // upon whether the select can have multiple values and whether ngOptions is at work.
28359 self.readValue = function readSingleValue() {
28360 self.removeUnknownOption();
28361 return $element.val();
28365 // Write the value to the select control, the implementation of this changes depending
28366 // upon whether the select can have multiple values and whether ngOptions is at work.
28367 self.writeValue = function writeSingleValue(value) {
28368 if (self.hasOption(value)) {
28369 self.removeUnknownOption();
28370 $element.val(value);
28371 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28373 if (value == null && self.emptyOption) {
28374 self.removeUnknownOption();
28377 self.renderUnknownOption(value);
28383 // Tell the select control that an option, with the given value, has been added
28384 self.addOption = function(value, element) {
28385 assertNotHasOwnProperty(value, '"option value"');
28386 if (value === '') {
28387 self.emptyOption = element;
28389 var count = optionsMap.get(value) || 0;
28390 optionsMap.put(value, count + 1);
28391 self.ngModelCtrl.$render();
28392 chromeHack(element);
28395 // Tell the select control that an option, with the given value, has been removed
28396 self.removeOption = function(value) {
28397 var count = optionsMap.get(value);
28400 optionsMap.remove(value);
28401 if (value === '') {
28402 self.emptyOption = undefined;
28405 optionsMap.put(value, count - 1);
28410 // Check whether the select control has an option matching the given value
28411 self.hasOption = function(value) {
28412 return !!optionsMap.get(value);
28416 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28418 if (interpolateValueFn) {
28419 // The value attribute is interpolated
28421 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28422 if (isDefined(oldVal)) {
28423 self.removeOption(oldVal);
28426 self.addOption(newVal, optionElement);
28428 } else if (interpolateTextFn) {
28429 // The text content is interpolated
28430 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28431 optionAttrs.$set('value', newVal);
28432 if (oldVal !== newVal) {
28433 self.removeOption(oldVal);
28435 self.addOption(newVal, optionElement);
28438 // The value attribute is static
28439 self.addOption(optionAttrs.value, optionElement);
28442 optionElement.on('$destroy', function() {
28443 self.removeOption(optionAttrs.value);
28444 self.ngModelCtrl.$render();
28455 * HTML `SELECT` element with angular data-binding.
28457 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28458 * between the scope and the `<select>` control (including setting default values).
28459 * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28460 * {@link ngOptions `ngOptions`} directives.
28462 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28463 * to the model identified by the `ngModel` directive. With static or repeated options, this is
28464 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28465 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28467 * <div class="alert alert-warning">
28468 * Note that the value of a `select` directive used without `ngOptions` is always a string.
28469 * When the model needs to be bound to a non-string value, you must either explictly convert it
28470 * using a directive (see example below) or use `ngOptions` to specify the set of options.
28471 * This is because an option element can only be bound to string values at present.
28474 * If the viewValue of `ngModel` does not match any of the options, then the control
28475 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28477 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28478 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28479 * option. See example below for demonstration.
28481 * <div class="alert alert-info">
28482 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28483 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28484 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28485 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28486 * a new scope for each repeated instance.
28490 * @param {string} ngModel Assignable angular expression to data-bind to.
28491 * @param {string=} name Property name of the form under which the control is published.
28492 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28493 * bound to the model as an array.
28494 * @param {string=} required Sets `required` validation error key if the value is not entered.
28495 * @param {string=} ngRequired Adds required attribute and required validation constraint to
28496 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28497 * when you want to data-bind to the required attribute.
28498 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28499 * interaction with the select element.
28500 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28501 * set on the model on selection. See {@link ngOptions `ngOptions`}.
28504 * ### Simple `select` elements with static options
28506 * <example name="static-select" module="staticSelect">
28507 * <file name="index.html">
28508 * <div ng-controller="ExampleController">
28509 * <form name="myForm">
28510 * <label for="singleSelect"> Single select: </label><br>
28511 * <select name="singleSelect" ng-model="data.singleSelect">
28512 * <option value="option-1">Option 1</option>
28513 * <option value="option-2">Option 2</option>
28516 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28517 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28518 * <option value="">---Please select---</option> <!-- not selected / blank option -->
28519 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28520 * <option value="option-2">Option 2</option>
28522 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28523 * <tt>singleSelect = {{data.singleSelect}}</tt>
28526 * <label for="multipleSelect"> Multiple select: </label><br>
28527 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28528 * <option value="option-1">Option 1</option>
28529 * <option value="option-2">Option 2</option>
28530 * <option value="option-3">Option 3</option>
28532 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28536 * <file name="app.js">
28537 * angular.module('staticSelect', [])
28538 * .controller('ExampleController', ['$scope', function($scope) {
28540 * singleSelect: null,
28541 * multipleSelect: [],
28542 * option1: 'option-1',
28545 * $scope.forceUnknownOption = function() {
28546 * $scope.data.singleSelect = 'nonsense';
28552 * ### Using `ngRepeat` to generate `select` options
28553 * <example name="ngrepeat-select" module="ngrepeatSelect">
28554 * <file name="index.html">
28555 * <div ng-controller="ExampleController">
28556 * <form name="myForm">
28557 * <label for="repeatSelect"> Repeat select: </label>
28558 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28559 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28563 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28566 * <file name="app.js">
28567 * angular.module('ngrepeatSelect', [])
28568 * .controller('ExampleController', ['$scope', function($scope) {
28570 * repeatSelect: null,
28571 * availableOptions: [
28572 * {id: '1', name: 'Option A'},
28573 * {id: '2', name: 'Option B'},
28574 * {id: '3', name: 'Option C'}
28582 * ### Using `select` with `ngOptions` and setting a default value
28583 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28585 * <example name="select-with-default-values" module="defaultValueSelect">
28586 * <file name="index.html">
28587 * <div ng-controller="ExampleController">
28588 * <form name="myForm">
28589 * <label for="mySelect">Make a choice:</label>
28590 * <select name="mySelect" id="mySelect"
28591 * ng-options="option.name for option in data.availableOptions track by option.id"
28592 * ng-model="data.selectedOption"></select>
28595 * <tt>option = {{data.selectedOption}}</tt><br/>
28598 * <file name="app.js">
28599 * angular.module('defaultValueSelect', [])
28600 * .controller('ExampleController', ['$scope', function($scope) {
28602 * availableOptions: [
28603 * {id: '1', name: 'Option A'},
28604 * {id: '2', name: 'Option B'},
28605 * {id: '3', name: 'Option C'}
28607 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28614 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28616 * <example name="select-with-non-string-options" module="nonStringSelect">
28617 * <file name="index.html">
28618 * <select ng-model="model.id" convert-to-number>
28619 * <option value="0">Zero</option>
28620 * <option value="1">One</option>
28621 * <option value="2">Two</option>
28625 * <file name="app.js">
28626 * angular.module('nonStringSelect', [])
28627 * .run(function($rootScope) {
28628 * $rootScope.model = { id: 2 };
28630 * .directive('convertToNumber', function() {
28632 * require: 'ngModel',
28633 * link: function(scope, element, attrs, ngModel) {
28634 * ngModel.$parsers.push(function(val) {
28635 * return parseInt(val, 10);
28637 * ngModel.$formatters.push(function(val) {
28644 * <file name="protractor.js" type="protractor">
28645 * it('should initialize to model', function() {
28646 * var select = element(by.css('select'));
28647 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28653 var selectDirective = function() {
28657 require: ['select', '?ngModel'],
28658 controller: SelectController,
28665 function selectPreLink(scope, element, attr, ctrls) {
28667 // if ngModel is not defined, we don't need to do anything
28668 var ngModelCtrl = ctrls[1];
28669 if (!ngModelCtrl) return;
28671 var selectCtrl = ctrls[0];
28673 selectCtrl.ngModelCtrl = ngModelCtrl;
28675 // We delegate rendering to the `writeValue` method, which can be changed
28676 // if the select can have multiple selected values or if the options are being
28677 // generated by `ngOptions`
28678 ngModelCtrl.$render = function() {
28679 selectCtrl.writeValue(ngModelCtrl.$viewValue);
28682 // When the selected item(s) changes we delegate getting the value of the select control
28683 // to the `readValue` method, which can be changed if the select can have multiple
28684 // selected values or if the options are being generated by `ngOptions`
28685 element.on('change', function() {
28686 scope.$apply(function() {
28687 ngModelCtrl.$setViewValue(selectCtrl.readValue());
28691 // If the select allows multiple values then we need to modify how we read and write
28692 // values from and to the control; also what it means for the value to be empty and
28693 // we have to add an extra watch since ngModel doesn't work well with arrays - it
28694 // doesn't trigger rendering if only an item in the array changes.
28695 if (attr.multiple) {
28697 // Read value now needs to check each option to see if it is selected
28698 selectCtrl.readValue = function readMultipleValue() {
28700 forEach(element.find('option'), function(option) {
28701 if (option.selected) {
28702 array.push(option.value);
28708 // Write value now needs to set the selected property of each matching option
28709 selectCtrl.writeValue = function writeMultipleValue(value) {
28710 var items = new HashMap(value);
28711 forEach(element.find('option'), function(option) {
28712 option.selected = isDefined(items.get(option.value));
28716 // we have to do it on each watch since ngModel watches reference, but
28717 // we need to work of an array, so we need to see if anything was inserted/removed
28718 var lastView, lastViewRef = NaN;
28719 scope.$watch(function selectMultipleWatch() {
28720 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28721 lastView = shallowCopy(ngModelCtrl.$viewValue);
28722 ngModelCtrl.$render();
28724 lastViewRef = ngModelCtrl.$viewValue;
28727 // If we are a multiple select then value is now a collection
28728 // so the meaning of $isEmpty changes
28729 ngModelCtrl.$isEmpty = function(value) {
28730 return !value || value.length === 0;
28738 // The option directive is purely designed to communicate the existence (or lack of)
28739 // of dynamically created (and destroyed) option elements to their containing select
28740 // directive via its controller.
28741 var optionDirective = ['$interpolate', function($interpolate) {
28745 compile: function(element, attr) {
28747 if (isDefined(attr.value)) {
28748 // If the value attribute is defined, check if it contains an interpolation
28749 var interpolateValueFn = $interpolate(attr.value, true);
28751 // If the value attribute is not defined then we fall back to the
28752 // text content of the option element, which may be interpolated
28753 var interpolateTextFn = $interpolate(element.text(), true);
28754 if (!interpolateTextFn) {
28755 attr.$set('value', element.text());
28759 return function(scope, element, attr) {
28761 // This is an optimization over using ^^ since we don't want to have to search
28762 // all the way to the root of the DOM for every single option element
28763 var selectCtrlName = '$selectController',
28764 parent = element.parent(),
28765 selectCtrl = parent.data(selectCtrlName) ||
28766 parent.parent().data(selectCtrlName); // in case we are in optgroup
28769 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
28776 var styleDirective = valueFn({
28781 var requiredDirective = function() {
28784 require: '?ngModel',
28785 link: function(scope, elm, attr, ctrl) {
28787 attr.required = true; // force truthy in case we are on non input element
28789 ctrl.$validators.required = function(modelValue, viewValue) {
28790 return !attr.required || !ctrl.$isEmpty(viewValue);
28793 attr.$observe('required', function() {
28801 var patternDirective = function() {
28804 require: '?ngModel',
28805 link: function(scope, elm, attr, ctrl) {
28808 var regexp, patternExp = attr.ngPattern || attr.pattern;
28809 attr.$observe('pattern', function(regex) {
28810 if (isString(regex) && regex.length > 0) {
28811 regex = new RegExp('^' + regex + '$');
28814 if (regex && !regex.test) {
28815 throw minErr('ngPattern')('noregexp',
28816 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28817 regex, startingTag(elm));
28820 regexp = regex || undefined;
28824 ctrl.$validators.pattern = function(modelValue, viewValue) {
28825 // HTML5 pattern constraint validates the input value, so we validate the viewValue
28826 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28833 var maxlengthDirective = function() {
28836 require: '?ngModel',
28837 link: function(scope, elm, attr, ctrl) {
28840 var maxlength = -1;
28841 attr.$observe('maxlength', function(value) {
28842 var intVal = toInt(value);
28843 maxlength = isNaN(intVal) ? -1 : intVal;
28846 ctrl.$validators.maxlength = function(modelValue, viewValue) {
28847 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28853 var minlengthDirective = function() {
28856 require: '?ngModel',
28857 link: function(scope, elm, attr, ctrl) {
28861 attr.$observe('minlength', function(value) {
28862 minlength = toInt(value) || 0;
28865 ctrl.$validators.minlength = function(modelValue, viewValue) {
28866 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28872 if (window.angular.bootstrap) {
28873 //AngularJS is already loaded, so we can return here...
28874 console.log('WARNING: Tried to load angular more than once.');
28878 //try to bind to jquery now so that one can write jqLite(document).ready()
28879 //but we will rebind on bootstrap again.
28882 publishExternalAPI(angular);
28884 angular.module("ngLocale", [], ["$provide", function($provide) {
28885 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
28886 function getDecimals(n) {
28888 var i = n.indexOf('.');
28889 return (i == -1) ? 0 : n.length - i - 1;
28892 function getVF(n, opt_precision) {
28893 var v = opt_precision;
28895 if (undefined === v) {
28896 v = Math.min(getDecimals(n), 3);
28899 var base = Math.pow(10, v);
28900 var f = ((n * base) | 0) % base;
28901 return {v: v, f: f};
28904 $provide.value("$locale", {
28905 "DATETIME_FORMATS": {
28927 "FIRSTDAYOFWEEK": 6,
28969 "fullDate": "EEEE, MMMM d, y",
28970 "longDate": "MMMM d, y",
28971 "medium": "MMM d, y h:mm:ss a",
28972 "mediumDate": "MMM d, y",
28973 "mediumTime": "h:mm:ss a",
28974 "short": "M/d/yy h:mm a",
28975 "shortDate": "M/d/yy",
28976 "shortTime": "h:mm a"
28978 "NUMBER_FORMATS": {
28979 "CURRENCY_SYM": "$",
28980 "DECIMAL_SEP": ".",
29000 "negPre": "-\u00a4",
29002 "posPre": "\u00a4",
29008 "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
29012 jqLite(document).ready(function() {
29013 angularInit(document, bootstrap);
29016 })(window, document);
29018 !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');