aboutsummaryrefslogtreecommitdiffstats
path: root/vnfmarket/src/main/webapp/vnfmarket/common/thirdparty/angular/angular.js
diff options
context:
space:
mode:
Diffstat (limited to 'vnfmarket/src/main/webapp/vnfmarket/common/thirdparty/angular/angular.js')
-rw-r--r--vnfmarket/src/main/webapp/vnfmarket/common/thirdparty/angular/angular.js3579
1 files changed, 2402 insertions, 1177 deletions
diff --git a/vnfmarket/src/main/webapp/vnfmarket/common/thirdparty/angular/angular.js b/vnfmarket/src/main/webapp/vnfmarket/common/thirdparty/angular/angular.js
index c6e9577c..b9d847e5 100644
--- a/vnfmarket/src/main/webapp/vnfmarket/common/thirdparty/angular/angular.js
+++ b/vnfmarket/src/main/webapp/vnfmarket/common/thirdparty/angular/angular.js
@@ -1,15 +1,65 @@
/**
- * @license AngularJS v1.6.2
- * (c) 2010-2017 Google, Inc. http://angularjs.org
+ * @license AngularJS v1.6.9
+ * (c) 2010-2018 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window) {'use strict';
+/* exported
+ minErrConfig,
+ errorHandlingConfig,
+ isValidObjectMaxDepth
+*/
+
+var minErrConfig = {
+ objectMaxDepth: 5
+};
+
+/**
+ * @ngdoc function
+ * @name angular.errorHandlingConfig
+ * @module ng
+ * @kind function
+ *
+ * @description
+ * Configure several aspects of error handling in AngularJS if used as a setter or return the
+ * current configuration if used as a getter. The following options are supported:
+ *
+ * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
+ *
+ * Omitted or undefined options will leave the corresponding configuration values unchanged.
+ *
+ * @param {Object=} config - The configuration object. May only contain the options that need to be
+ * updated. Supported keys:
+ *
+ * * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
+ * non-positive or non-numeric value, removes the max depth limit.
+ * Default: 5
+ */
+function errorHandlingConfig(config) {
+ if (isObject(config)) {
+ if (isDefined(config.objectMaxDepth)) {
+ minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
+ }
+ } else {
+ return minErrConfig;
+ }
+}
+
+/**
+ * @private
+ * @param {Number} maxDepth
+ * @return {boolean}
+ */
+function isValidObjectMaxDepth(maxDepth) {
+ return isNumber(maxDepth) && maxDepth > 0;
+}
+
/**
* @description
*
* This object provides a utility for producing rich Error messages within
- * Angular. It can be called as follows:
+ * AngularJS. It can be called as follows:
*
* var exampleMinErr = minErr('example');
* throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
@@ -38,31 +88,29 @@
function minErr(module, ErrorConstructor) {
ErrorConstructor = ErrorConstructor || Error;
return function() {
- var SKIP_INDEXES = 2;
-
- var templateArgs = arguments,
- code = templateArgs[0],
+ var code = arguments[0],
+ template = arguments[1],
message = '[' + (module ? module + ':' : '') + code + '] ',
- template = templateArgs[1],
+ templateArgs = sliceArgs(arguments, 2).map(function(arg) {
+ return toDebugString(arg, minErrConfig.objectMaxDepth);
+ }),
paramPrefix, i;
message += template.replace(/\{\d+\}/g, function(match) {
- var index = +match.slice(1, -1),
- shiftedIndex = index + SKIP_INDEXES;
+ var index = +match.slice(1, -1);
- if (shiftedIndex < templateArgs.length) {
- return toDebugString(templateArgs[shiftedIndex]);
+ if (index < templateArgs.length) {
+ return templateArgs[index];
}
return match;
});
- message += '\nhttp://errors.angularjs.org/1.6.2/' +
+ message += '\nhttp://errors.angularjs.org/1.6.9/' +
(module ? module + '/' : '') + code;
- for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
- message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
- encodeURIComponent(toDebugString(templateArgs[i]));
+ for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
+ message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
}
return new ErrorConstructor(message);
@@ -79,6 +127,9 @@ function minErr(module, ErrorConstructor) {
splice,
push,
toString,
+ minErrConfig,
+ errorHandlingConfig,
+ isValidObjectMaxDepth,
ngMinErr,
angularModule,
uid,
@@ -111,6 +162,7 @@ function minErr(module, ErrorConstructor) {
isNumber,
isNumberNaN,
isDate,
+ isError,
isArray,
isFunction,
isRegExp,
@@ -128,6 +180,7 @@ function minErr(module, ErrorConstructor) {
includes,
arrayRemove,
copy,
+ simpleCompare,
equals,
csp,
jq,
@@ -176,13 +229,11 @@ function minErr(module, ErrorConstructor) {
* @installation
* @description
*
- * # ng (core module)
* The ng module is loaded by default when an AngularJS application is started. The module itself
* contains the essential components for an AngularJS application to function. The table below
* lists a high level breakdown of each of the services/factories, filters, directives and testing
* components available within this core module.
*
- * <div doc-module-components="ng"></div>
*/
var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
@@ -501,6 +552,20 @@ function extend(dst) {
* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
* objects, performing a deep copy.
*
+* @deprecated
+* sinceVersion="1.6.5"
+* This function is deprecated, but will not be removed in the 1.x lifecycle.
+* There are edge cases (see {@link angular.merge#known-issues known issues}) that are not
+* supported by this function. We suggest
+* using [lodash's merge()](https://lodash.com/docs/4.17.4#merge) instead.
+*
+* @knownIssue
+* This is a list of (known) object types that are not handled correctly by this function:
+* - [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob)
+* - [`MediaStream`](https://developer.mozilla.org/docs/Web/API/MediaStream)
+* - [`CanvasGradient`](https://developer.mozilla.org/docs/Web/API/CanvasGradient)
+* - AngularJS {@link $rootScope.Scope scopes};
+*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
@@ -711,6 +776,24 @@ function isDate(value) {
var isArray = Array.isArray;
/**
+ * @description
+ * Determines if a reference is an `Error`.
+ * Loosely based on https://www.npmjs.com/package/iserror
+ *
+ * @param {*} value Reference to check.
+ * @returns {boolean} True if `value` is an `Error`.
+ */
+function isError(value) {
+ var tag = toString.call(value);
+ switch (tag) {
+ case '[object Error]': return true;
+ case '[object Exception]': return true;
+ case '[object DOMException]': return true;
+ default: return value instanceof Error;
+ }
+}
+
+/**
* @ngdoc function
* @name angular.isFunction
* @module ng
@@ -891,7 +974,7 @@ function arrayRemove(array, value) {
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
- <pre>master = {{master | json}}</pre>
+ <pre>leader = {{leader | json}}</pre>
</div>
</file>
<file name="script.js">
@@ -899,16 +982,16 @@ function arrayRemove(array, value) {
angular.
module('copyExample', []).
controller('ExampleController', ['$scope', function($scope) {
- $scope.master = {};
+ $scope.leader = {};
$scope.reset = function() {
// Example with 1 argument
- $scope.user = angular.copy($scope.master);
+ $scope.user = angular.copy($scope.leader);
};
$scope.update = function(user) {
// Example with 2 arguments
- angular.copy(user, $scope.master);
+ angular.copy(user, $scope.leader);
};
$scope.reset();
@@ -916,9 +999,10 @@ function arrayRemove(array, value) {
</file>
</example>
*/
-function copy(source, destination) {
+function copy(source, destination, maxDepth) {
var stackSource = [];
var stackDest = [];
+ maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
if (destination) {
if (isTypedArray(destination) || isArrayBuffer(destination)) {
@@ -941,35 +1025,39 @@ function copy(source, destination) {
stackSource.push(source);
stackDest.push(destination);
- return copyRecurse(source, destination);
+ return copyRecurse(source, destination, maxDepth);
}
- return copyElement(source);
+ return copyElement(source, maxDepth);
- function copyRecurse(source, destination) {
+ function copyRecurse(source, destination, maxDepth) {
+ maxDepth--;
+ if (maxDepth < 0) {
+ return '...';
+ }
var h = destination.$$hashKey;
var key;
if (isArray(source)) {
for (var i = 0, ii = source.length; i < ii; i++) {
- destination.push(copyElement(source[i]));
+ destination.push(copyElement(source[i], maxDepth));
}
} else if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
- destination[key] = copyElement(source[key]);
+ destination[key] = copyElement(source[key], maxDepth);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
- destination[key] = copyElement(source[key]);
+ destination[key] = copyElement(source[key], maxDepth);
}
}
} else {
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
- destination[key] = copyElement(source[key]);
+ destination[key] = copyElement(source[key], maxDepth);
}
}
}
@@ -977,7 +1065,7 @@ function copy(source, destination) {
return destination;
}
- function copyElement(source) {
+ function copyElement(source, maxDepth) {
// Simple values
if (!isObject(source)) {
return source;
@@ -1006,7 +1094,7 @@ function copy(source, destination) {
stackDest.push(destination);
return needsRecurse
- ? copyRecurse(source, destination)
+ ? copyRecurse(source, destination, maxDepth)
: destination;
}
@@ -1057,6 +1145,10 @@ function copy(source, destination) {
}
+// eslint-disable-next-line no-self-compare
+function simpleCompare(a, b) { return a === b || (a !== a && b !== b); }
+
+
/**
* @ngdoc function
* @name angular.equals
@@ -1137,7 +1229,7 @@ function equals(o1, o2) {
}
} else if (isDate(o1)) {
if (!isDate(o2)) return false;
- return equals(o1.getTime(), o2.getTime());
+ return simpleCompare(o1.getTime(), o2.getTime());
} else if (isRegExp(o1)) {
if (!isRegExp(o2)) return false;
return o1.toString() === o2.toString();
@@ -1210,7 +1302,7 @@ var csp = function() {
* used to force either jqLite by leaving ng-jq blank or setting the name of
* the jquery variable under window (eg. jQuery).
*
- * Since angular looks for this directive when it is loaded (doesn't wait for the
+ * Since AngularJS looks for this directive when it is loaded (doesn't wait for the
* DOMContentLoaded event), it must be placed on an element that comes before the script
* which loads angular. Also, only the first instance of `ng-jq` will be used and all
* others ignored.
@@ -1323,7 +1415,7 @@ function toJsonReplacer(key, value) {
*
* @description
* Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
- * stripped since angular uses this notation internally.
+ * stripped since AngularJS uses this notation internally.
*
* @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON.
* @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
@@ -1381,7 +1473,7 @@ function fromJson(json) {
var ALL_COLONS = /:/g;
function timezoneToOffset(timezone, fallback) {
- // Support: IE 9-11 only, Edge 13-14+
+ // Support: IE 9-11 only, Edge 13-15+
// IE/Edge do not "understand" colon (`:`) in timezone
timezone = timezone.replace(ALL_COLONS, '');
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
@@ -1408,12 +1500,7 @@ function convertTimezoneToLocal(date, timezone, reverse) {
* @returns {string} Returns the string representation of the element.
*/
function startingTag(element) {
- element = jqLite(element).clone();
- try {
- // turns out IE does not let you set .html() on elements which
- // are not allowed to have children. So we just ignore it.
- element.empty();
- } catch (e) { /* empty */ }
+ element = jqLite(element).clone().empty();
var elemHtml = jqLite('<div>').append(element).html();
try {
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
@@ -1549,33 +1636,51 @@ function getNgAttribute(element, ngAttr) {
function allowAutoBootstrap(document) {
var script = document.currentScript;
- var src = script && script.getAttribute('src');
- if (!src) {
+ if (!script) {
+ // Support: IE 9-11 only
+ // IE does not have `document.currentScript`
return true;
}
- var link = document.createElement('a');
- link.href = src;
-
- if (document.location.origin === link.origin) {
- // Same-origin resources are always allowed, even for non-whitelisted schemes.
- return true;
+ // If the `currentScript` property has been clobbered just return false, since this indicates a probable attack
+ if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) {
+ return false;
}
- // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
- // This is to prevent angular.js bundled with browser extensions from being used to bypass the
- // content security policy in web pages and other browser extensions.
- switch (link.protocol) {
- case 'http:':
- case 'https:':
- case 'ftp:':
- case 'blob:':
- case 'file:':
- case 'data:':
+
+ var attributes = script.attributes;
+ var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')];
+
+ return srcs.every(function(src) {
+ if (!src) {
return true;
- default:
+ }
+ if (!src.value) {
return false;
- }
+ }
+
+ var link = document.createElement('a');
+ link.href = src.value;
+
+ if (document.location.origin === link.origin) {
+ // Same-origin resources are always allowed, even for non-whitelisted schemes.
+ return true;
+ }
+ // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
+ // This is to prevent angular.js bundled with browser extensions from being used to bypass the
+ // content security policy in web pages and other browser extensions.
+ switch (link.protocol) {
+ case 'http:':
+ case 'https:':
+ case 'ftp:':
+ case 'blob:':
+ case 'file:':
+ case 'data:':
+ return true;
+ default:
+ return false;
+ }
+ });
}
// Cached as it has to run during loading so that document.currentScript is available.
@@ -1622,6 +1727,10 @@ var isAutoBootstrapAllowed = allowAutoBootstrap(window.document);
* document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
* would not be resolved to `3`.
*
+ * @example
+ *
+ * ### Simple Usage
+ *
* `ngApp` is the easiest, and most common way to bootstrap an application.
*
<example module="ngAppDemo" name="ng-app">
@@ -1638,6 +1747,10 @@ var isAutoBootstrapAllowed = allowAutoBootstrap(window.document);
</file>
</example>
*
+ * @example
+ *
+ * ### With `ngStrictDi`
+ *
* Using `ngStrictDi`, you would see something like this:
*
<example ng-app-included="true" name="strict-di">
@@ -1740,7 +1853,7 @@ function angularInit(element, bootstrap) {
});
if (appElement) {
if (!isAutoBootstrapAllowed) {
- window.console.error('Angular: disabling automatic bootstrap. <script> protocol indicates ' +
+ window.console.error('AngularJS: disabling automatic bootstrap. <script> protocol indicates ' +
'an extension, document.location.href does not match.');
return;
}
@@ -1754,14 +1867,14 @@ function angularInit(element, bootstrap) {
* @name angular.bootstrap
* @module ng
* @description
- * Use this function to manually start up angular application.
+ * Use this function to manually start up AngularJS application.
*
* For more information, see the {@link guide/bootstrap Bootstrap guide}.
*
- * Angular will detect if it has been loaded into the browser more than once and only allow the
+ * AngularJS will detect if it has been loaded into the browser more than once and only allow the
* first loaded script to be bootstrapped and will report a warning to the browser console for
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
- * multiple instances of Angular try to work on the DOM.
+ * multiple instances of AngularJS try to work on the DOM.
*
* <div class="alert alert-warning">
* **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
@@ -1795,7 +1908,7 @@ function angularInit(element, bootstrap) {
* </html>
* ```
*
- * @param {DOMElement} element DOM element which is the root of angular application.
+ * @param {DOMElement} element DOM element which is the root of AngularJS application.
* @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
* Each item in the array should be the name of a predefined module or a (DI annotated)
* function that will be invoked by the injector as a `config` block.
@@ -1895,9 +2008,9 @@ function reloadWithDebugInfo() {
* @name angular.getTestability
* @module ng
* @description
- * Get the testability service for the instance of Angular on the given
+ * Get the testability service for the instance of AngularJS on the given
* element.
- * @param {DOMElement} element DOM element which is the root of angular application.
+ * @param {DOMElement} element DOM element which is the root of AngularJS application.
*/
function getTestability(rootElement) {
var injector = angular.element(rootElement).injector();
@@ -1931,8 +2044,8 @@ function bindJQuery() {
window[jqName]; // use jQuery specified by `ngJq`
// Use jQuery if it exists with proper functionality, otherwise default to us.
- // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
- // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
+ // AngularJS 1.2+ requires jQuery 1.7+ for on()/off() support.
+ // AngularJS 1.3+ technically requires at least jQuery 2.1+ but it may work with older
// versions. It will not work for sure with jQuery <1.7, though.
if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
@@ -2099,7 +2212,7 @@ var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
* @module ng
* @description
*
- * Interface for configuring angular {@link angular.module modules}.
+ * Interface for configuring AngularJS {@link angular.module modules}.
*/
function setupModuleLoader(window) {
@@ -2126,9 +2239,9 @@ function setupModuleLoader(window) {
* @module ng
* @description
*
- * The `angular.module` is a global place for creating, registering and retrieving Angular
+ * The `angular.module` is a global place for creating, registering and retrieving AngularJS
* modules.
- * All modules (angular core or 3rd party) that should be available to an application must be
+ * All modules (AngularJS core or 3rd party) that should be available to an application must be
* registered using this mechanism.
*
* Passing one argument retrieves an existing {@link angular.Module},
@@ -2172,6 +2285,9 @@ function setupModuleLoader(window) {
* @returns {angular.Module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
+
+ var info = {};
+
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
@@ -2208,6 +2324,45 @@ function setupModuleLoader(window) {
_runBlocks: runBlocks,
/**
+ * @ngdoc method
+ * @name angular.Module#info
+ * @module ng
+ *
+ * @param {Object=} info Information about the module
+ * @returns {Object|Module} The current info object for this module if called as a getter,
+ * or `this` if called as a setter.
+ *
+ * @description
+ * Read and write custom information about this module.
+ * For example you could put the version of the module in here.
+ *
+ * ```js
+ * angular.module('myModule', []).info({ version: '1.0.0' });
+ * ```
+ *
+ * The version could then be read back out by accessing the module elsewhere:
+ *
+ * ```
+ * var version = angular.module('myModule').info().version;
+ * ```
+ *
+ * You can also retrieve this information during runtime via the
+ * {@link $injector#modules `$injector.modules`} property:
+ *
+ * ```js
+ * var version = $injector.modules['myModule'].info().version;
+ * ```
+ */
+ info: function(value) {
+ if (isDefined(value)) {
+ if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
+ info = value;
+ return this;
+ }
+ return info;
+ },
+
+ /**
* @ngdoc property
* @name angular.Module#requires
* @module ng
@@ -2336,13 +2491,13 @@ function setupModuleLoader(window) {
* @ngdoc method
* @name angular.Module#filter
* @module ng
- * @param {string} name Filter name - this must be a valid angular expression identifier
+ * @param {string} name Filter name - this must be a valid AngularJS expression identifier
* @param {Function} filterFactory Factory function for creating new instance of filter.
* @description
* See {@link ng.$filterProvider#register $filterProvider.register()}.
*
* <div class="alert alert-warning">
- * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
+ * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`.
* Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
* your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
* (`myapp_subsection_filterx`).
@@ -2395,7 +2550,13 @@ function setupModuleLoader(window) {
* @param {Function} configFn Execute this function on module load. Useful for service
* configuration.
* @description
- * Use this method to register work which needs to be performed on module loading.
+ * Use this method to configure services by injecting their
+ * {@link angular.Module#provider `providers`}, e.g. for adding routes to the
+ * {@link ngRoute.$routeProvider $routeProvider}.
+ *
+ * Note that you can only inject {@link angular.Module#provider `providers`} and
+ * {@link angular.Module#constant `constants`} into this function.
+ *
* For more about how to configure services, see
* {@link providers#provider-recipe Provider Recipe}.
*/
@@ -2483,11 +2644,19 @@ function shallowCopy(src, dst) {
return dst || src;
}
-/* global toDebugString: true */
+/* exported toDebugString */
-function serializeObject(obj) {
+function serializeObject(obj, maxDepth) {
var seen = [];
+ // There is no direct way to stringify object until reaching a specific depth
+ // and a very deep object can cause a performance issue, so we copy the object
+ // based on this specific depth and then stringify it.
+ if (isValidObjectMaxDepth(maxDepth)) {
+ // This file is also included in `angular-loader`, so `copy()` might not always be available in
+ // the closure. Therefore, it is lazily retrieved as `angular.copy()` when needed.
+ obj = angular.copy(obj, null, maxDepth);
+ }
return JSON.stringify(obj, function(key, val) {
val = toJsonReplacer(key, val);
if (isObject(val)) {
@@ -2500,13 +2669,13 @@ function serializeObject(obj) {
});
}
-function toDebugString(obj) {
+function toDebugString(obj, maxDepth) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
} else if (isUndefined(obj)) {
return 'undefined';
} else if (typeof obj !== 'string') {
- return serializeObject(obj);
+ return serializeObject(obj, maxDepth);
}
return obj;
}
@@ -2627,16 +2796,17 @@ function toDebugString(obj) {
var version = {
// These placeholder strings will be replaced by grunt's `build` task.
// They need to be double- or single-quoted.
- full: '1.6.2',
+ full: '1.6.9',
major: 1,
minor: 6,
- dot: 2,
- codeName: 'llamacorn-lovehug'
+ dot: 9,
+ codeName: 'fiery-basilisk'
};
function publishExternalAPI(angular) {
extend(angular, {
+ 'errorHandlingConfig': errorHandlingConfig,
'bootstrap': bootstrap,
'copy': copy,
'extend': extend,
@@ -2775,7 +2945,8 @@ function publishExternalAPI(angular) {
$$cookieReader: $$CookieReaderProvider
});
}
- ]);
+ ])
+ .info({ angularVersion: '1.6.9' });
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -2810,24 +2981,24 @@ function publishExternalAPI(angular) {
*
* If jQuery is available, `angular.element` is an alias for the
* [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
- * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
+ * delegates to AngularJS's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
*
* jqLite is a tiny, API-compatible subset of jQuery that allows
- * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
+ * AngularJS to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
* commonly needed functionality with the goal of having a very small footprint.
*
* To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
* {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
* specific version of jQuery if multiple versions exist on the page.
*
- * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
+ * <div class="alert alert-info">**Note:** All element references in AngularJS are always wrapped with jQuery or
* jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
*
* <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
* by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
* or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
*
- * ## Angular's jqLite
+ * ## AngularJS's jqLite
* jqLite provides only the following jQuery methods:
*
* - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
@@ -2868,7 +3039,7 @@ function publishExternalAPI(angular) {
* - [`wrap()`](http://api.jquery.com/wrap/)
*
* ## jQuery/jqLite Extras
- * Angular also provides the following additional methods and events to both jQuery and jqLite:
+ * AngularJS also provides the following additional methods and events to both jQuery and jqLite:
*
* ### Events
* - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
@@ -2979,12 +3150,6 @@ function jqLiteHasData(node) {
return false;
}
-function jqLiteCleanData(nodes) {
- for (var i = 0, ii = nodes.length; i < ii; i++) {
- jqLiteRemoveData(nodes[i]);
- }
-}
-
function jqLiteBuildFragment(html, context) {
var tmp, tag, wrap,
fragment = context.createDocumentFragment(),
@@ -3087,13 +3252,10 @@ function jqLiteClone(element) {
}
function jqLiteDealoc(element, onlyDescendants) {
- if (!onlyDescendants) jqLiteRemoveData(element);
+ if (!onlyDescendants && jqLiteAcceptsData(element)) jqLite.cleanData([element]);
if (element.querySelectorAll) {
- var descendants = element.querySelectorAll('*');
- for (var i = 0, l = descendants.length; i < l; i++) {
- jqLiteRemoveData(descendants[i]);
- }
+ jqLite.cleanData(element.querySelectorAll('*'));
}
}
@@ -3207,13 +3369,18 @@ function jqLiteHasClass(element, selector) {
function jqLiteRemoveClass(element, cssClasses) {
if (cssClasses && element.setAttribute) {
+ var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
+ .replace(/[\n\t]/g, ' ');
+ var newClasses = existingClasses;
+
forEach(cssClasses.split(' '), function(cssClass) {
- element.setAttribute('class', trim(
- (' ' + (element.getAttribute('class') || '') + ' ')
- .replace(/[\n\t]/g, ' ')
- .replace(' ' + trim(cssClass) + ' ', ' '))
- );
+ cssClass = trim(cssClass);
+ newClasses = newClasses.replace(' ' + cssClass + ' ', ' ');
});
+
+ if (newClasses !== existingClasses) {
+ element.setAttribute('class', trim(newClasses));
+ }
}
}
@@ -3221,15 +3388,18 @@ function jqLiteAddClass(element, cssClasses) {
if (cssClasses && element.setAttribute) {
var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
.replace(/[\n\t]/g, ' ');
+ var newClasses = existingClasses;
forEach(cssClasses.split(' '), function(cssClass) {
cssClass = trim(cssClass);
- if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
- existingClasses += cssClass + ' ';
+ if (newClasses.indexOf(' ' + cssClass + ' ') === -1) {
+ newClasses += cssClass + ' ';
}
});
- element.setAttribute('class', trim(existingClasses));
+ if (newClasses !== existingClasses) {
+ element.setAttribute('class', trim(newClasses));
+ }
}
}
@@ -3391,7 +3561,11 @@ forEach({
data: jqLiteData,
removeData: jqLiteRemoveData,
hasData: jqLiteHasData,
- cleanData: jqLiteCleanData
+ cleanData: function jqLiteCleanData(nodes) {
+ for (var i = 0, ii = nodes.length; i < ii; i++) {
+ jqLiteRemoveData(nodes[i]);
+ }
+ }
}, function(fn, name) {
JQLite[name] = fn;
});
@@ -4016,8 +4190,8 @@ var $$MapProvider = [/** @this */function() {
* });
* ```
*
- * Sometimes you want to get access to the injector of a currently running Angular app
- * from outside Angular. Perhaps, you want to inject and compile some markup after the
+ * Sometimes you want to get access to the injector of a currently running AngularJS app
+ * from outside AngularJS. Perhaps, you want to inject and compile some markup after the
* application has been bootstrapped. You can do this using the extra `injector()` added
* to JQuery/jqLite elements. See {@link angular.element}.
*
@@ -4133,7 +4307,7 @@ function annotate(fn, strictDi, name) {
* })).toBe($injector);
* ```
*
- * # Injection Function Annotation
+ * ## Injection Function Annotation
*
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
* following are all valid ways of annotating function with injection arguments and are equivalent.
@@ -4151,7 +4325,7 @@ function annotate(fn, strictDi, name) {
* $injector.invoke(['serviceA', function(serviceA){}]);
* ```
*
- * ## Inference
+ * ### Inference
*
* In JavaScript calling `toString()` on a function returns the function definition. The definition
* can then be parsed and the function arguments can be extracted. This method of discovering
@@ -4159,14 +4333,36 @@ function annotate(fn, strictDi, name) {
* *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
* argument names.
*
- * ## `$inject` Annotation
+ * ### `$inject` Annotation
* By adding an `$inject` property onto a function the injection parameters can be specified.
*
- * ## Inline
+ * ### Inline
* As an array of injection names, where the last item in the array is the function to call.
*/
/**
+ * @ngdoc property
+ * @name $injector#modules
+ * @type {Object}
+ * @description
+ * A hash containing all the modules that have been loaded into the
+ * $injector.
+ *
+ * You can use this property to find out information about a module via the
+ * {@link angular.Module#info `myModule.info(...)`} method.
+ *
+ * For example:
+ *
+ * ```
+ * var info = $injector.modules['ngAnimate'].info();
+ * ```
+ *
+ * **Do not use this property to attempt to modify the modules after the application
+ * has been bootstrapped.**
+ */
+
+
+/**
* @ngdoc method
* @name $injector#get
*
@@ -4228,7 +4424,7 @@ function annotate(fn, strictDi, name) {
* function is invoked. There are three ways in which the function can be annotated with the needed
* dependencies.
*
- * # Argument names
+ * #### Argument names
*
* The simplest form is to extract the dependencies from the arguments of the function. This is done
* by converting the function into a string using `toString()` method and extracting the argument
@@ -4248,7 +4444,7 @@ function annotate(fn, strictDi, name) {
* This method does not work with code minification / obfuscation. For this reason the following
* annotation strategies are supported.
*
- * # The `$inject` property
+ * #### The `$inject` property
*
* If a function has an `$inject` property and its value is an array of strings, then the strings
* represent names of services to be injected into the function.
@@ -4264,7 +4460,7 @@ function annotate(fn, strictDi, name) {
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
* ```
*
- * # The array notation
+ * #### The array notation
*
* It is often desirable to inline Injected functions and that's when setting the `$inject` property
* is very inconvenient. In these situations using the array notation to specify the dependencies in
@@ -4301,7 +4497,45 @@ function annotate(fn, strictDi, name) {
*
* @returns {Array.<string>} The names of the services which the function requires.
*/
-
+/**
+ * @ngdoc method
+ * @name $injector#loadNewModules
+ *
+ * @description
+ *
+ * **This is a dangerous API, which you use at your own risk!**
+ *
+ * Add the specified modules to the current injector.
+ *
+ * This method will add each of the injectables to the injector and execute all of the config and run
+ * blocks for each module passed to the method.
+ *
+ * If a module has already been loaded into the injector then it will not be loaded again.
+ *
+ * * The application developer is responsible for loading the code containing the modules; and for
+ * ensuring that lazy scripts are not downloaded and executed more often that desired.
+ * * Previously compiled HTML will not be affected by newly loaded directives, filters and components.
+ * * Modules cannot be unloaded.
+ *
+ * You can use {@link $injector#modules `$injector.modules`} to check whether a module has been loaded
+ * into the injector, which may indicate whether the script has been executed already.
+ *
+ * @example
+ * Here is an example of loading a bundle of modules, with a utility method called `getScript`:
+ *
+ * ```javascript
+ * app.factory('loadModule', function($injector) {
+ * return function loadModule(moduleName, bundleUrl) {
+ * return getScript(bundleUrl).then(function() { $injector.loadNewModules([moduleName]); });
+ * };
+ * })
+ * ```
+ *
+ * @param {Array<String|Function|Array>=} mods an array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a `config` block.
+ * See: {@link angular.module modules}
+ */
/**
@@ -4314,7 +4548,7 @@ function annotate(fn, strictDi, name) {
* with the {@link auto.$injector $injector}. Many of these functions are also exposed on
* {@link angular.Module}.
*
- * An Angular **service** is a singleton object created by a **service factory**. These **service
+ * An AngularJS **service** is a singleton object created by a **service factory**. These **service
* factories** are functions which, in turn, are created by a **service provider**.
* The **service providers** are constructor functions. When instantiated they must contain a
* property called `$get`, which holds the **service factory** function.
@@ -4366,6 +4600,9 @@ function annotate(fn, strictDi, name) {
* which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
* console or not.
*
+ * It is possible to inject other providers into the provider function,
+ * but the injected provider must have been defined before the one that requires it.
+ *
* @param {string} name The name of the instance. NOTE: the provider will be available under `name +
'Provider'` key.
* @param {(Object|function())} provider If the provider is:
@@ -4542,7 +4779,7 @@ function annotate(fn, strictDi, name) {
*
* Value services are similar to constant services, except that they cannot be injected into a
* module configuration function (see {@link angular.Module#config}) but they can be overridden by
- * an Angular {@link auto.$provide#decorator decorator}.
+ * an AngularJS {@link auto.$provide#decorator decorator}.
*
* @param {string} name The name of the instance.
* @param {*} value The value.
@@ -4573,7 +4810,7 @@ function annotate(fn, strictDi, name) {
*
* But unlike {@link auto.$provide#value value}, a constant can be
* injected into a module configuration function (see {@link angular.Module#config}) and it cannot
- * be overridden by an Angular {@link auto.$provide#decorator decorator}.
+ * be overridden by an AngularJS {@link auto.$provide#decorator decorator}.
*
* @param {string} name The name of the constant.
* @param {*} value The constant value.
@@ -4659,11 +4896,17 @@ function createInjector(modulesToLoad, strictDi) {
instanceInjector = protoInstanceInjector;
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
+ instanceInjector.modules = providerInjector.modules = createMap();
var runBlocks = loadModules(modulesToLoad);
instanceInjector = protoInstanceInjector.get('$injector');
instanceInjector.strictDi = strictDi;
forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
+ instanceInjector.loadNewModules = function(mods) {
+ forEach(loadModules(mods), function(fn) { if (fn) instanceInjector.invoke(fn); });
+ };
+
+
return instanceInjector;
////////////////////////////////////
@@ -4754,6 +4997,7 @@ function createInjector(modulesToLoad, strictDi) {
try {
if (isString(module)) {
moduleFn = angularModule(module);
+ instanceInjector.modules[module] = moduleFn;
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
runInvokeQueue(moduleFn._invokeQueue);
runInvokeQueue(moduleFn._configBlocks);
@@ -5344,6 +5588,8 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
*/
var $AnimateProvider = ['$provide', /** @this */ function($provide) {
var provider = this;
+ var classNameFilter = null;
+ var customFilter = null;
this.$$registeredAnimations = Object.create(null);
@@ -5398,6 +5644,51 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
/**
* @ngdoc method
+ * @name $animateProvider#customFilter
+ *
+ * @description
+ * Sets and/or returns the custom filter function that is used to "filter" animations, i.e.
+ * determine if an animation is allowed or not. When no filter is specified (the default), no
+ * animation will be blocked. Setting the `customFilter` value will only allow animations for
+ * which the filter function's return value is truthy.
+ *
+ * This allows to easily create arbitrarily complex rules for filtering animations, such as
+ * allowing specific events only, or enabling animations on specific subtrees of the DOM, etc.
+ * Filtering animations can also boost performance for low-powered devices, as well as
+ * applications containing a lot of structural operations.
+ *
+ * <div class="alert alert-success">
+ * **Best Practice:**
+ * Keep the filtering function as lean as possible, because it will be called for each DOM
+ * action (e.g. insertion, removal, class change) performed by "animation-aware" directives.
+ * See {@link guide/animations#which-directives-support-animations- here} for a list of built-in
+ * directives that support animations.
+ * Performing computationally expensive or time-consuming operations on each call of the
+ * filtering function can make your animations sluggish.
+ * </div>
+ *
+ * **Note:** If present, `customFilter` will be checked before
+ * {@link $animateProvider#classNameFilter classNameFilter}.
+ *
+ * @param {Function=} filterFn - The filter function which will be used to filter all animations.
+ * If a falsy value is returned, no animation will be performed. The function will be called
+ * with the following arguments:
+ * - **node** `{DOMElement}` - The DOM element to be animated.
+ * - **event** `{String}` - The name of the animation event (e.g. `enter`, `leave`, `addClass`
+ * etc).
+ * - **options** `{Object}` - A collection of options/styles used for the animation.
+ * @return {Function} The current filter function or `null` if there is none set.
+ */
+ this.customFilter = function(filterFn) {
+ if (arguments.length === 1) {
+ customFilter = isFunction(filterFn) ? filterFn : null;
+ }
+
+ return customFilter;
+ };
+
+ /**
+ * @ngdoc method
* @name $animateProvider#classNameFilter
*
* @description
@@ -5407,20 +5698,26 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* When setting the `classNameFilter` value, animations will only be performed on elements
* that successfully match the filter expression. This in turn can boost performance
* for low-powered devices as well as applications containing a lot of structural operations.
+ *
+ * **Note:** If present, `classNameFilter` will be checked after
+ * {@link $animateProvider#customFilter customFilter}. If `customFilter` is present and returns
+ * false, `classNameFilter` will not be checked.
+ *
* @param {RegExp=} expression The className expression which will be checked against all animations
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
*/
this.classNameFilter = function(expression) {
if (arguments.length === 1) {
- this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
- if (this.$$classNameFilter) {
- var reservedRegex = new RegExp('(\\s+|\\/)' + NG_ANIMATE_CLASSNAME + '(\\s+|\\/)');
- if (reservedRegex.test(this.$$classNameFilter.toString())) {
- throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
+ classNameFilter = (expression instanceof RegExp) ? expression : null;
+ if (classNameFilter) {
+ var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]');
+ if (reservedRegex.test(classNameFilter.toString())) {
+ classNameFilter = null;
+ throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
}
}
}
- return this.$$classNameFilter;
+ return classNameFilter;
};
this.$get = ['$$animateQueue', function($$animateQueue) {
@@ -5528,7 +5825,7 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* @name $animate#pin
* @kind function
* @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
- * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
+ * outside of the DOM structure of the AngularJS application. By doing so, any animation triggered via `$animate` can be issued on the
* element despite being outside the realm of the application or within another application. Say for example if the application
* was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
* as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
@@ -6150,7 +6447,6 @@ function Browser(window, document, $log, $sniffer) {
/**
* @private
- * Note: this method is used only by scenario runner
* TODO(vojta): prefix this method with $$ ?
* @param {function()} callback Function that will be called when no outstanding request
*/
@@ -6320,7 +6616,7 @@ function Browser(window, document, $log, $sniffer) {
* @description
* Register callback function that will be called, when url changes.
*
- * It's only called when the url is changed from outside of angular:
+ * It's only called when the url is changed from outside of AngularJS:
* - user types different url into address bar
* - user clicks on history (forward/back) button
* - user clicks on a link
@@ -6330,7 +6626,7 @@ function Browser(window, document, $log, $sniffer) {
* The listener gets called with new url as parameter.
*
* NOTE: this api is intended for use only by the $location service. Please use the
- * {@link ng.$location $location service} to monitor url changes in angular apps.
+ * {@link ng.$location $location service} to monitor url changes in AngularJS apps.
*
* @param {function(string)} listener Listener function to be called when url changes.
* @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
@@ -6338,8 +6634,8 @@ function Browser(window, document, $log, $sniffer) {
self.onUrlChange = function(callback) {
// TODO(vojta): refactor to use node's syntax for events
if (!urlChangeInit) {
- // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
- // don't fire popstate when user change the address bar and don't fire hashchange when url
+ // We listen on both (hashchange/popstate) when available, as some browsers don't
+ // fire popstate when user changes the address bar and don't fire hashchange when url
// changed by push/replaceState
// html5 history api - popstate event
@@ -6365,7 +6661,7 @@ function Browser(window, document, $log, $sniffer) {
};
/**
- * Checks whether the url has changed outside of Angular.
+ * Checks whether the url has changed outside of AngularJS.
* Needs to be exported to be able to check for changes that have been done in sync,
* as hashchange/popstate events fire in async.
*/
@@ -6551,8 +6847,8 @@ function $CacheFactoryProvider() {
*
* @description
* A cache object used to store and retrieve data, primarily used by
- * {@link $http $http} and the {@link ng.directive:script script} directive to cache
- * templates and other data.
+ * {@link $templateRequest $templateRequest} and the {@link ng.directive:script script}
+ * directive to cache templates and other data.
*
* ```js
* angular.module('superCache')
@@ -6805,9 +7101,12 @@ function $CacheFactoryProvider() {
* @this
*
* @description
+ * `$templateCache` is a {@link $cacheFactory.Cache Cache object} created by the
+ * {@link ng.$cacheFactory $cacheFactory}.
+ *
* The first time a template is used, it is loaded in the template cache for quick retrieval. You
- * can load templates directly into the cache in a `script` tag, or by consuming the
- * `$templateCache` service directly.
+ * can load templates directly into the cache in a `script` tag, by using {@link $templateRequest},
+ * or by consuming the `$templateCache` service directly.
*
* Adding via the `script` tag:
*
@@ -6818,8 +7117,8 @@ function $CacheFactoryProvider() {
* ```
*
* **Note:** the `script` tag containing the template does not need to be included in the `head` of
- * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
- * element with ng-app attribute), otherwise the template will be ignored.
+ * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (e.g.
+ * element with {@link ngApp} attribute), otherwise the template will be ignored.
*
* Adding via the `$templateCache` service:
*
@@ -6842,8 +7141,6 @@ function $CacheFactoryProvider() {
* $templateCache.get('templateId.html')
* ```
*
- * See {@link ng.$cacheFactory $cacheFactory}.
- *
*/
function $TemplateCacheProvider() {
this.$get = ['$cacheFactory', function($cacheFactory) {
@@ -6971,7 +7268,7 @@ function $TemplateCacheProvider() {
* ```
*
* ### Life-cycle hooks
- * Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the
+ * Directive controllers can provide the following methods that are called by AngularJS at points in the life-cycle of the
* directive:
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre &amp; post linking functions for the directives on
@@ -6985,7 +7282,7 @@ function $TemplateCacheProvider() {
* changes. Any actions that you wish to take in response to the changes that you detect must be
* invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
* could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
- * be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
+ * be detected by AngularJS's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
* if detecting changes, you must store the previous value(s) for comparison to the current values.
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
@@ -6997,18 +7294,18 @@ function $TemplateCacheProvider() {
* they are waiting for their template to load asynchronously and their own compilation and linking has been
* suspended until that occurs.
*
- * #### Comparison with Angular 2 life-cycle hooks
- * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
- * some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
+ * #### Comparison with life-cycle hooks in the new Angular
+ * The new Angular also uses life-cycle hooks for its components. While the AngularJS life-cycle hooks are similar there are
+ * some differences that you should be aware of, especially when it comes to moving your code from AngularJS to Angular:
*
- * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
- * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
- * In Angular 2 you can only define hooks on the prototype of the Component class.
- * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
- * `ngDoCheck` in Angular 2
+ * * AngularJS hooks are prefixed with `$`, such as `$onInit`. Angular hooks are prefixed with `ng`, such as `ngOnInit`.
+ * * AngularJS hooks can be defined on the controller prototype or added to the controller inside its constructor.
+ * In Angular you can only define hooks on the prototype of the Component class.
+ * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in AngularJS than you would to
+ * `ngDoCheck` in Angular.
* * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
* propagated throughout the application.
- * Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
+ * Angular does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
* error or do nothing depending upon the state of `enableProdMode()`.
*
* #### Life-cycle hook examples
@@ -7128,10 +7425,12 @@ function $TemplateCacheProvider() {
* the directive's element. If multiple directives on the same element request a new scope,
* only one new scope is created.
*
- * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
- * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
- * scope. This is useful when creating reusable components, which should not accidentally read or modify
- * data in the parent scope.
+ * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
+ * The 'isolate' scope differs from normal scope in that it does not prototypically
+ * inherit from its parent scope. This is useful when creating reusable components, which should not
+ * accidentally read or modify data in the parent scope. Note that an isolate scope
+ * directive without a `template` or `templateUrl` will not apply the isolate scope
+ * to its children elements.
*
* The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
* directive's element. These local properties are useful for aliasing values for templates. The keys in
@@ -7224,9 +7523,9 @@ function $TemplateCacheProvider() {
* initialized.
*
* <div class="alert alert-warning">
- * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
- * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
- * code that relies upon bindings inside a `$onInit` method on the controller, instead.
+ * **Deprecation warning:** if `$compileProcvider.preAssignBindingsEnabled(true)` was called, bindings for non-ES6 class
+ * controllers are bound to `this` before the controller constructor is called but this use is now deprecated. Please
+ * place initialization code that relies upon bindings inside a `$onInit` method on the controller, instead.
* </div>
*
* It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
@@ -7355,8 +7654,11 @@ function $TemplateCacheProvider() {
* $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
*
*
- * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
- * specify what the template should replace. Defaults to `false`.
+ * #### `replace` (*DEPRECATED*)
+ *
+ * `replace` will be removed in next major release - i.e. v2.0).
+ *
+ * Specifies what the template should replace. Defaults to `false`.
*
* * `true` - the template will replace the directive's element.
* * `false` - the template will replace the contents of the directive's element.
@@ -7696,7 +7998,7 @@ function $TemplateCacheProvider() {
});
})
.controller('GreeterController', ['$scope', function($scope) {
- $scope.name = 'Angular';
+ $scope.name = 'AngularJS';
$scope.html = 'Hello {{name}}';
}]);
</script>
@@ -7710,11 +8012,11 @@ function $TemplateCacheProvider() {
it('should auto compile', function() {
var textarea = $('textarea');
var output = $('div[compile]');
- // The initial state reads 'Hello Angular'.
- expect(output.getText()).toBe('Hello Angular');
+ // The initial state reads 'Hello AngularJS'.
+ expect(output.getText()).toBe('Hello AngularJS');
textarea.clear();
textarea.sendKeys('{{name}}!');
- expect(output.getText()).toBe('Angular!');
+ expect(output.getText()).toBe('AngularJS!');
});
</file>
</example>
@@ -7768,7 +8070,7 @@ function $TemplateCacheProvider() {
* element passed in, or the clone of the element if the `cloneAttachFn` is provided.
*
* After linking the view is not updated until after a call to $digest which typically is done by
- * Angular automatically.
+ * AngularJS automatically.
*
* If you need access to the bound view, there are two ways to do it:
*
@@ -7794,7 +8096,7 @@ function $TemplateCacheProvider() {
*
*
* For information on how the compiler works, see the
- * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
+ * {@link guide/compiler AngularJS HTML Compiler} section of the Developer Guide.
*
* @knownIssue
*
@@ -7993,7 +8295,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @ngdoc method
* @name $compileProvider#component
* @module ng
- * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
+ * @param {string|Object} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`),
+ * or an object map of components where the keys are the names and the values are the component definition objects.
* @param {Object} options Component definition object (a simplified
* {@link ng.$compile#directive-definition-object directive definition object}),
* with the following properties (all optional):
@@ -8076,6 +8379,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
this.component = function registerComponent(name, options) {
+ if (!isString(name)) {
+ forEach(name, reverseParams(bind(this, registerComponent)));
+ return this;
+ }
+
var controller = options.controller || function() {};
function factory($injector) {
@@ -8112,7 +8420,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// TODO(pete) remove the following `forEach` before we release 1.6.0
// The component-router@0.2.0 looks for the annotations on the controller constructor
- // Nothing in Angular looks for annotations on the factory function but we can't remove
+ // Nothing in AngularJS looks for annotations on the factory function but we can't remove
// it from 1.5.x yet.
// Copy any annotation properties (starting with $) over to the factory and controller constructor functions
@@ -8205,7 +8513,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* binding information and a reference to the current scope on to DOM elements.
* If enabled, the compiler will add the following to DOM elements that have been bound to the scope
* * `ng-binding` CSS class
+ * * `ng-scope` and `ng-isolated-scope` CSS classes
* * `$binding` data property containing an array of the binding expressions
+ * * Data properties used by the {@link angular.element#methods `scope()`/`isolateScope()` methods} to return
+ * the element's scope.
+ * * Placeholder comments will contain information about what directive and binding caused the placeholder.
+ * E.g. `<!-- ngIf: shouldShow() -->`.
*
* You may want to disable this in production for a significant performance boost. See
* {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
@@ -8239,7 +8552,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
*
* If disabled (false), the compiler calls the constructor first before assigning bindings.
*
- * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x.
+ * The default value is false.
+ *
+ * @deprecated
+ * sinceVersion="1.6.0"
+ * removeVersion="1.7.0"
+ *
+ * This method and the option to assign the bindings before calling the controller's constructor
+ * will be removed in v1.7.0.
*/
var preAssignBindingsEnabled = false;
this.preAssignBindingsEnabled = function(enabled) {
@@ -8250,6 +8570,31 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return preAssignBindingsEnabled;
};
+ /**
+ * @ngdoc method
+ * @name $compileProvider#strictComponentBindingsEnabled
+ *
+ * @param {boolean=} enabled update the strictComponentBindingsEnabled state if provided, otherwise just return the
+ * current strictComponentBindingsEnabled state
+ * @returns {*} current value if used as getter or itself (chaining) if used as setter
+ *
+ * @kind function
+ *
+ * @description
+ * Call this method to enable/disable strict component bindings check. If enabled, the compiler will enforce that
+ * for all bindings of a component that are not set as optional with `?`, an attribute needs to be provided
+ * on the component's HTML tag.
+ *
+ * The default value is false.
+ */
+ var strictComponentBindingsEnabled = false;
+ this.strictComponentBindingsEnabled = function(enabled) {
+ if (isDefined(enabled)) {
+ strictComponentBindingsEnabled = enabled;
+ return this;
+ }
+ return strictComponentBindingsEnabled;
+ };
var TTL = 10;
/**
@@ -10004,7 +10349,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
linkQueue = null;
}).catch(function(error) {
- if (error instanceof Error) {
+ if (isError(error)) {
$exceptionHandler(error);
}
});
@@ -10242,7 +10587,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
if (jqLite.hasData(firstElementToRemove)) {
- // Copy over user data (that includes Angular's $scope etc.). Don't copy private
+ // Copy over user data (that includes AngularJS's $scope etc.). Don't copy private
// data here because there's no public interface in jQuery to do that and copying over
// event listeners (which is the main use of private data) wouldn't work anyway.
jqLite.data(newNode, jqLite.data(firstElementToRemove));
@@ -10277,12 +10622,20 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}
+ function strictBindingsCheck(attrName, directiveName) {
+ if (strictComponentBindingsEnabled) {
+ throw $compileMinErr('missingattr',
+ 'Attribute \'{0}\' of \'{1}\' is non-optional and must be set!',
+ attrName, directiveName);
+ }
+ }
// Set up $watches for isolate scope and controller bindings.
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
var removeWatchCollection = [];
var initialChanges = {};
var changes;
+
forEach(bindings, function initializeBinding(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
@@ -10294,7 +10647,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
case '@':
if (!optional && !hasOwnProperty.call(attrs, attrName)) {
+ strictBindingsCheck(attrName, directive.name);
destination[scopeName] = attrs[attrName] = undefined;
+
}
removeWatch = attrs.$observe(attrName, function(value) {
if (isString(value) || isBoolean(value)) {
@@ -10310,7 +10665,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// the value is there for use in the link fn
destination[scopeName] = $interpolate(lastValue)(scope);
} else if (isBoolean(lastValue)) {
- // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
+ // If the attributes is one of the BOOLEAN_ATTR then AngularJS will have converted
// the value to boolean rather than a string, so we special case this situation
destination[scopeName] = lastValue;
}
@@ -10321,6 +10676,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
case '=':
if (!hasOwnProperty.call(attrs, attrName)) {
if (optional) break;
+ strictBindingsCheck(attrName, directive.name);
attrs[attrName] = undefined;
}
if (optional && !attrs[attrName]) break;
@@ -10329,8 +10685,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (parentGet.literal) {
compare = equals;
} else {
- // eslint-disable-next-line no-self-compare
- compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
+ compare = simpleCompare;
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
@@ -10366,6 +10721,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
case '<':
if (!hasOwnProperty.call(attrs, attrName)) {
if (optional) break;
+ strictBindingsCheck(attrName, directive.name);
attrs[attrName] = undefined;
}
if (optional && !attrs[attrName]) break;
@@ -10391,6 +10747,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
break;
case '&':
+ if (!optional && !hasOwnProperty.call(attrs, attrName)) {
+ strictBindingsCheck(attrName, directive.name);
+ }
// Don't assign Object.prototype method to scope
parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
@@ -10405,9 +10764,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
});
function recordChanges(key, currentValue, previousValue) {
- if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
- // eslint-disable-next-line no-self-compare
- (currentValue === currentValue || previousValue === previousValue)) {
+ if (isFunction(destination.$onChanges) && !simpleCompare(currentValue, previousValue)) {
// If we have not already scheduled the top level onChangesQueue handler then do so now
if (!onChangesQueue) {
scope.$$postDigest(flushOnChangesQueue);
@@ -10462,7 +10819,9 @@ var SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g;
function directiveNormalize(name) {
return name
.replace(PREFIX_REGEXP, '')
- .replace(SPECIAL_CHARS_REGEXP, fnCamelCaseReplace);
+ .replace(SPECIAL_CHARS_REGEXP, function(_, letter, offset) {
+ return offset ? letter.toUpperCase() : letter;
+ });
}
/**
@@ -10472,7 +10831,7 @@ function directiveNormalize(name) {
* @description
* A shared object between directive compile / linking functions which contains normalized DOM
* element attributes. The values reflect current binding state `{{ }}`. The normalization is
- * needed since all of these are treated as equivalent in Angular:
+ * needed since all of these are treated as equivalent in AngularJS:
*
* ```
* <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
@@ -10578,7 +10937,7 @@ function identifierForController(controller, ident) {
* @this
*
* @description
- * The {@link ng.$controller $controller service} is used by Angular to create new
+ * The {@link ng.$controller $controller service} is used by AngularJS to create new
* controllers.
*
* This provider allows controller registration via the
@@ -10816,7 +11175,7 @@ function $$IsDocumentHiddenProvider() {
* @this
*
* @description
- * Any uncaught exception in angular expressions is delegated to this service.
+ * Any uncaught exception in AngularJS expressions is delegated to this service.
* The default implementation simply delegates to `$log.error` which logs it into
* the browser console.
*
@@ -10925,7 +11284,7 @@ function $HttpParamSerializerProvider() {
if (!params) return '';
var parts = [];
forEachSorted(params, function(value, key) {
- if (value === null || isUndefined(value)) return;
+ if (value === null || isUndefined(value) || isFunction(value)) return;
if (isArray(value)) {
forEach(value, function(v) {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
@@ -11021,8 +11380,18 @@ function defaultHttpResponseTransform(data, headers) {
if (tempData) {
var contentType = headers('Content-Type');
- if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
- data = fromJson(tempData);
+ var hasJsonContentType = contentType && (contentType.indexOf(APPLICATION_JSON) === 0);
+
+ if (hasJsonContentType || isJsonLike(tempData)) {
+ try {
+ data = fromJson(tempData);
+ } catch (e) {
+ if (!hasJsonContentType) {
+ return data;
+ }
+ throw $httpMinErr('baddata', 'Data must be a valid JSON object. Received: "{0}". ' +
+ 'Parse error: "{1}"', data, e);
+ }
}
}
}
@@ -11145,12 +11514,6 @@ function $HttpProvider() {
* {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
* by default. See {@link $http#caching $http Caching} for more information.
*
- * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
- * Defaults value is `'XSRF-TOKEN'`.
- *
- * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
- * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
- *
* - **`defaults.headers`** - {Object} - Default headers for all $http requests.
* Refer to {@link ng.$http#setting-http-headers $http} for documentation on
* setting default headers.
@@ -11159,15 +11522,38 @@ function $HttpProvider() {
* - **`defaults.headers.put`**
* - **`defaults.headers.patch`**
*
+ * - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the
+ * callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the
+ * {@link $jsonpCallbacks} service. Defaults to `'callback'`.
*
* - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
* used to the prepare string representation of request parameters (specified as an object).
* If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
* Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
*
- * - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the
- * callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the
- * {@link $jsonpCallbacks} service. Defaults to `'callback'`.
+ * - **`defaults.transformRequest`** -
+ * `{Array<function(data, headersGetter)>|function(data, headersGetter)}` -
+ * An array of functions (or a single function) which are applied to the request data.
+ * By default, this is an array with one request transformation function:
+ *
+ * - If the `data` property of the request configuration object contains an object, serialize it
+ * into JSON format.
+ *
+ * - **`defaults.transformResponse`** -
+ * `{Array<function(data, headersGetter, status)>|function(data, headersGetter, status)}` -
+ * An array of functions (or a single function) which are applied to the response data. By default,
+ * this is an array which applies one response transformation function that does two things:
+ *
+ * - If XSRF prefix is detected, strip it
+ * (see {@link ng.$http#security-considerations Security Considerations in the $http docs}).
+ * - If the `Content-Type` is `application/json` or the response looks like JSON,
+ * deserialize it using a JSON parser.
+ *
+ * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
+ * Defaults value is `'XSRF-TOKEN'`.
+ *
+ * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
+ * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
*
**/
var defaults = this.defaults = {
@@ -11274,7 +11660,7 @@ function $HttpProvider() {
* @requires $injector
*
* @description
- * The `$http` service is a core Angular service that facilitates communication with the remote
+ * The `$http` service is a core AngularJS service that facilitates communication with the remote
* HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
* object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
*
@@ -11315,6 +11701,7 @@ function $HttpProvider() {
* - **headers** – `{function([headerName])}` – Header getter function.
* - **config** – `{Object}` – The configuration object that was used to generate the request.
* - **statusText** – `{string}` – HTTP status text of the response.
+ * - **xhrStatus** – `{string}` – Status of the XMLHttpRequest (`complete`, `error`, `timeout` or `abort`).
*
* A response status code between 200 and 299 is considered a success status and will result in
* the success callback being called. Any response status code outside of that range is
@@ -11412,7 +11799,7 @@ function $HttpProvider() {
* which allows you to `push` or `unshift` a new transformation function into the transformation chain.
*
* <div class="alert alert-warning">
- * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
+ * **Note:** AngularJS does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
* That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
* For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
* function will be reflected on the scope and in any templates where the object is data-bound.
@@ -11429,17 +11816,20 @@ function $HttpProvider() {
* You can augment or replace the default transformations by modifying these properties by adding to or
* replacing the array.
*
- * Angular provides the following default transformations:
+ * AngularJS provides the following default transformations:
*
- * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
+ * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`) is
+ * an array with one function that does the following:
*
* - If the `data` property of the request configuration object contains an object, serialize it
* into JSON format.
*
- * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
+ * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`) is
+ * an array with one function that does the following:
*
* - If XSRF prefix is detected, strip it (see Security Considerations section below).
- * - If JSON response is detected, deserialize it using a JSON parser.
+ * - If the `Content-Type` is `application/json` or the response looks like JSON,
+ * deserialize it using a JSON parser.
*
*
* ### Overriding the Default Transformations Per Request
@@ -11599,7 +11989,7 @@ function $HttpProvider() {
* - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
* - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
*
- * Both server and the client must cooperate in order to eliminate these threats. Angular comes
+ * Both server and the client must cooperate in order to eliminate these threats. AngularJS comes
* pre-configured with strategies that address these issues, but for this to work backend server
* cooperation is required.
*
@@ -11609,7 +11999,7 @@ function $HttpProvider() {
* allows third party website to turn your JSON resource URL into
* [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
* counter this your server can prefix all JSON requests with following string `")]}',\n"`.
- * Angular will automatically strip the prefix before processing it as JSON.
+ * AngularJS will automatically strip the prefix before processing it as JSON.
*
* For example if your server needs to return:
* ```js
@@ -11622,14 +12012,14 @@ function $HttpProvider() {
* ['one','two']
* ```
*
- * Angular will strip the prefix, before processing the JSON.
+ * AngularJS will strip the prefix, before processing the JSON.
*
*
* ### Cross Site Request Forgery (XSRF) Protection
*
* [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
* which the attacker can trick an authenticated user into unknowingly executing actions on your
- * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
+ * website. AngularJS provides a mechanism to counter XSRF. When performing XHR requests, the
* $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
* header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
* cookie, your server can be assured that the XHR came from JavaScript running on your domain.
@@ -11648,7 +12038,7 @@ function $HttpProvider() {
* properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
* or the per-request config object.
*
- * In order to prevent collisions in environments where multiple Angular apps share the
+ * In order to prevent collisions in environments where multiple AngularJS apps share the
* same domain or subdomain, we recommend that each application uses unique cookie name.
*
* @param {object} config Object describing the request to be made and how it should be
@@ -11952,7 +12342,7 @@ function $HttpProvider() {
*
* @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
* or an object created by a call to `$sce.trustAsResourceUrl(url)`.
- * @param {Object=} config Optional configuration object
+ * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage
* @returns {HttpPromise} Future object
*/
@@ -11965,7 +12355,7 @@ function $HttpProvider() {
*
* @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
* or an object created by a call to `$sce.trustAsResourceUrl(url)`.
- * @param {Object=} config Optional configuration object
+ * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage
* @returns {HttpPromise} Future object
*/
@@ -11978,7 +12368,7 @@ function $HttpProvider() {
*
* @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
* or an object created by a call to `$sce.trustAsResourceUrl(url)`.
- * @param {Object=} config Optional configuration object
+ * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage
* @returns {HttpPromise} Future object
*/
@@ -11995,6 +12385,10 @@ function $HttpProvider() {
* {@link $sceDelegateProvider#resourceUrlWhitelist `$sceDelegateProvider.resourceUrlWhitelist`} or
* by explicitly trusting the URL via {@link $sce#trustAsResourceUrl `$sce.trustAsResourceUrl(url)`}.
*
+ * You should avoid generating the URL for the JSONP request from user provided data.
+ * Provide additional query parameters via `params` property of the `config` parameter, rather than
+ * modifying the URL itself.
+ *
* JSONP requests must specify a callback to be used in the response from the server. This callback
* is passed as a query parameter in the request. You must specify the name of this parameter by
* setting the `jsonpCallbackParam` property on the request config object.
@@ -12016,7 +12410,7 @@ function $HttpProvider() {
*
* @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
* or an object created by a call to `$sce.trustAsResourceUrl(url)`.
- * @param {Object=} config Optional configuration object
+ * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage
* @returns {HttpPromise} Future object
*/
createShortMethods('get', 'delete', 'head', 'jsonp');
@@ -12030,7 +12424,7 @@ function $HttpProvider() {
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
- * @param {Object=} config Optional configuration object
+ * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage
* @returns {HttpPromise} Future object
*/
@@ -12043,7 +12437,7 @@ function $HttpProvider() {
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
- * @param {Object=} config Optional configuration object
+ * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage
* @returns {HttpPromise} Future object
*/
@@ -12056,7 +12450,7 @@ function $HttpProvider() {
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
- * @param {Object=} config Optional configuration object
+ * @param {Object=} config Optional configuration object. See https://docs.angularjs.org/api/ng/service/$http#usage
* @returns {HttpPromise} Future object
*/
createShortMethodsWithData('post', 'put', 'patch');
@@ -12153,9 +12547,9 @@ function $HttpProvider() {
} else {
// serving from cache
if (isArray(cachedResp)) {
- resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
+ resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3], cachedResp[4]);
} else {
- resolvePromise(cachedResp, 200, {}, 'OK');
+ resolvePromise(cachedResp, 200, {}, 'OK', 'complete');
}
}
} else {
@@ -12212,10 +12606,10 @@ function $HttpProvider() {
* - resolves the raw $http promise
* - calls $apply
*/
- function done(status, response, headersString, statusText) {
+ function done(status, response, headersString, statusText, xhrStatus) {
if (cache) {
if (isSuccess(status)) {
- cache.put(url, [status, response, parseHeaders(headersString), statusText]);
+ cache.put(url, [status, response, parseHeaders(headersString), statusText, xhrStatus]);
} else {
// remove promise from the cache
cache.remove(url);
@@ -12223,7 +12617,7 @@ function $HttpProvider() {
}
function resolveHttpPromise() {
- resolvePromise(response, status, headersString, statusText);
+ resolvePromise(response, status, headersString, statusText, xhrStatus);
}
if (useApplyAsync) {
@@ -12238,7 +12632,7 @@ function $HttpProvider() {
/**
* Resolves the raw $http promise.
*/
- function resolvePromise(response, status, headers, statusText) {
+ function resolvePromise(response, status, headers, statusText, xhrStatus) {
//status: HTTP response status code, 0, -1 (aborted by timeout / promise)
status = status >= -1 ? status : 0;
@@ -12247,12 +12641,13 @@ function $HttpProvider() {
status: status,
headers: headersGetter(headers),
config: config,
- statusText: statusText
+ statusText: statusText,
+ xhrStatus: xhrStatus
});
}
function resolvePromiseWithResult(result) {
- resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
+ resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText, result.xhrStatus);
}
function removePendingReq() {
@@ -12269,20 +12664,26 @@ function $HttpProvider() {
return url;
}
- function sanitizeJsonpCallbackParam(url, key) {
- if (/[&?][^=]+=JSON_CALLBACK/.test(url)) {
- // Throw if the url already contains a reference to JSON_CALLBACK
- throw $httpMinErr('badjsonp', 'Illegal use of JSON_CALLBACK in url, "{0}"', url);
- }
-
- var callbackParamRegex = new RegExp('[&?]' + key + '=');
- if (callbackParamRegex.test(url)) {
- // Throw if the callback param was already provided
- throw $httpMinErr('badjsonp', 'Illegal use of callback param, "{0}", in url, "{1}"', key, url);
+ function sanitizeJsonpCallbackParam(url, cbKey) {
+ var parts = url.split('?');
+ if (parts.length > 2) {
+ // Throw if the url contains more than one `?` query indicator
+ throw $httpMinErr('badjsonp', 'Illegal use more than one "?", in url, "{1}"', url);
}
+ var params = parseKeyValue(parts[1]);
+ forEach(params, function(value, key) {
+ if (value === 'JSON_CALLBACK') {
+ // Throw if the url already contains a reference to JSON_CALLBACK
+ throw $httpMinErr('badjsonp', 'Illegal use of JSON_CALLBACK in url, "{0}"', url);
+ }
+ if (key === cbKey) {
+ // Throw if the callback param was already provided
+ throw $httpMinErr('badjsonp', 'Illegal use of callback param, "{0}", in url, "{1}"', cbKey, url);
+ }
+ });
// Add in the JSON_CALLBACK callback param value
- url += ((url.indexOf('?') === -1) ? '?' : '&') + key + '=JSON_CALLBACK';
+ url += ((url.indexOf('?') === -1) ? '?' : '&') + cbKey + '=JSON_CALLBACK';
return url;
}
@@ -12353,7 +12754,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
// jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
var response = (status === 200) && callbacks.getResponse(callbackPath);
- completeRequest(callback, status, response, '', text);
+ completeRequest(callback, status, response, '', text, 'complete');
callbacks.removeCallback(callbackPath);
});
} else {
@@ -12388,18 +12789,29 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
status,
response,
xhr.getAllResponseHeaders(),
- statusText);
+ statusText,
+ 'complete');
};
var requestError = function() {
// The response is always empty
// See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
- completeRequest(callback, -1, null, null, '');
+ completeRequest(callback, -1, null, null, '', 'error');
+ };
+
+ var requestAborted = function() {
+ completeRequest(callback, -1, null, null, '', 'abort');
+ };
+
+ var requestTimeout = function() {
+ // The response is always empty
+ // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
+ completeRequest(callback, -1, null, null, '', 'timeout');
};
xhr.onerror = requestError;
- xhr.onabort = requestError;
- xhr.ontimeout = requestError;
+ xhr.onabort = requestAborted;
+ xhr.ontimeout = requestTimeout;
forEach(eventHandlers, function(value, key) {
xhr.addEventListener(key, value);
@@ -12449,14 +12861,14 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
}
}
- function completeRequest(callback, status, response, headersString, statusText) {
+ function completeRequest(callback, status, response, headersString, statusText, xhrStatus) {
// cancel timeout and subsequent timeout promise resolution
if (isDefined(timeoutId)) {
$browserDefer.cancel(timeoutId);
}
jsonpDone = xhr = null;
- callback(status, response, headersString, statusText);
+ callback(status, response, headersString, statusText, xhrStatus);
}
};
@@ -12520,9 +12932,9 @@ $interpolateMinErr.interr = function(text, err) {
* Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
*
* <div class="alert alert-danger">
- * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
+ * This feature is sometimes used to mix different markup languages, e.g. to wrap an AngularJS
* template within a Python Jinja template (or any other template language). Mixing templating
- * languages is **very dangerous**. The embedding template language will not safely escape Angular
+ * languages is **very dangerous**. The embedding template language will not safely escape AngularJS
* expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
* security bugs!
* </div>
@@ -12638,7 +13050,7 @@ function $InterpolateProvider() {
* ```js
* var $interpolate = ...; // injected
* var exp = $interpolate('Hello {{name | uppercase}}!');
- * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
+ * expect(exp({name:'AngularJS'})).toEqual('Hello ANGULAR!');
* ```
*
* `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
@@ -12656,8 +13068,8 @@ function $InterpolateProvider() {
* // "allOrNothing" mode
* exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
* expect(exp(context)).toBeUndefined();
- * context.name = 'Angular';
- * expect(exp(context)).toEqual('Hello Angular!');
+ * context.name = 'AngularJS';
+ * expect(exp(context)).toEqual('Hello AngularJS!');
* ```
*
* `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
@@ -12831,9 +13243,7 @@ function $InterpolateProvider() {
var lastValue;
return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) {
var currValue = compute(values);
- if (isFunction(listener)) {
- listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
- }
+ listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
lastValue = currValue;
});
}
@@ -12898,7 +13308,7 @@ function $IntervalProvider() {
* @name $interval
*
* @description
- * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
+ * AngularJS's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
* milliseconds.
*
* The return value of registering an interval function is a promise. This promise will be
@@ -12927,7 +13337,7 @@ function $IntervalProvider() {
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
* @param {...*=} Pass additional parameters to the executed function.
- * @returns {promise} A promise which will be notified on each iteration.
+ * @returns {promise} A promise which will be notified on each iteration. It will resolve once all iterations of the interval complete.
*
* @example
* <example module="intervalExample" name="interval-service">
@@ -13076,7 +13486,7 @@ function $IntervalProvider() {
interval.cancel = function(promise) {
if (promise && promise.$$intervalId in intervals) {
// Interval cancels should not report as unhandled promise.
- intervals[promise.$$intervalId].promise.catch(noop);
+ markQExceptionHandled(intervals[promise.$$intervalId].promise);
intervals[promise.$$intervalId].reject('canceled');
$window.clearInterval(promise.$$intervalId);
delete intervals[promise.$$intervalId];
@@ -13099,8 +13509,8 @@ function $IntervalProvider() {
* how they vary compared to the requested url.
*/
var $jsonpCallbacksProvider = /** @this */ function() {
- this.$get = ['$window', function($window) {
- var callbacks = $window.angular.callbacks;
+ this.$get = function() {
+ var callbacks = angular.callbacks;
var callbackMap = {};
function createCallback(callbackId) {
@@ -13167,7 +13577,7 @@ var $jsonpCallbacksProvider = /** @this */ function() {
delete callbackMap[callbackPath];
}
};
- }];
+ };
};
/**
@@ -13175,7 +13585,7 @@ var $jsonpCallbacksProvider = /** @this */ function() {
* @name $locale
*
* @description
- * $locale service provides localization rules for various Angular components. As of right now the
+ * $locale service provides localization rules for various AngularJS components. As of right now the
* only public api is:
*
* * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
@@ -13197,7 +13607,23 @@ function encodePath(path) {
i = segments.length;
while (i--) {
- segments[i] = encodeUriSegment(segments[i]);
+ // decode forward slashes to prevent them from being double encoded
+ segments[i] = encodeUriSegment(segments[i].replace(/%2F/g, '/'));
+ }
+
+ return segments.join('/');
+}
+
+function decodePath(path, html5Mode) {
+ var segments = path.split('/'),
+ i = segments.length;
+
+ while (i--) {
+ segments[i] = decodeURIComponent(segments[i]);
+ if (html5Mode) {
+ // encode forward slashes to prevent them from being mistaken for path separators
+ segments[i] = segments[i].replace(/\//g, '%2F');
+ }
}
return segments.join('/');
@@ -13212,7 +13638,7 @@ function parseAbsoluteUrl(absoluteUrl, locationObj) {
}
var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
-function parseAppUrl(url, locationObj) {
+function parseAppUrl(url, locationObj, html5Mode) {
if (DOUBLE_SLASH_REGEX.test(url)) {
throw $locationMinErr('badpath', 'Invalid url "{0}".', url);
@@ -13223,8 +13649,8 @@ function parseAppUrl(url, locationObj) {
url = '/' + url;
}
var match = urlResolve(url);
- locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
- match.pathname.substring(1) : match.pathname);
+ var path = prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname;
+ locationObj.$$path = decodePath(path, html5Mode);
locationObj.$$search = parseKeyValue(match.search);
locationObj.$$hash = decodeURIComponent(match.hash);
@@ -13299,7 +13725,7 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
appBaseNoFile);
}
- parseAppUrl(pathUrl, this);
+ parseAppUrl(pathUrl, this, true);
if (!this.$$path) {
this.$$path = '/';
@@ -13402,7 +13828,7 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
}
}
- parseAppUrl(withoutHashUrl, this);
+ parseAppUrl(withoutHashUrl, this, false);
this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
@@ -13416,7 +13842,7 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
* * a.setAttribute('href', '/foo')
* * a.pathname === '/C:/foo' //true
*
- * Inside of Angular, we're always using pathnames that
+ * Inside of AngularJS, we're always using pathnames that
* do not include drive names for routing.
*/
function removeWindowsDriveName(path, url, base) {
@@ -13623,7 +14049,7 @@ var locationPrototype = {
*
* Return host of current URL.
*
- * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
+ * Note: compared to the non-AngularJS version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
*
*
* ```js
@@ -14096,7 +14522,7 @@ function $LocationProvider() {
if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
if ($location.$$parseLinkUrl(absHref, relHref)) {
- // We do a preventDefault for all urls that are part of the angular application,
+ // We do a preventDefault for all urls that are part of the AngularJS application,
// in html5mode and also without, so that we are able to abort navigation without
// getting double entries in the location history.
event.preventDefault();
@@ -14218,6 +14644,14 @@ function $LocationProvider() {
*
* The main purpose of this service is to simplify debugging and troubleshooting.
*
+ * To reveal the location of the calls to `$log` in the JavaScript console,
+ * you can "blackbox" the AngularJS source in your browser:
+ *
+ * [Mozilla description of blackboxing](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Black_box_a_source).
+ * [Chrome description of blackboxing](https://developer.chrome.com/devtools/docs/blackboxing).
+ *
+ * Note: Not all browsers support blackboxing.
+ *
* The default is to log `debug` messages. You can use
* {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
*
@@ -14274,6 +14708,15 @@ function $LogProvider() {
};
this.$get = ['$window', function($window) {
+ // Support: IE 9-11, Edge 12-14+
+ // IE/Edge display errors in such a way that it requires the user to click in 4 places
+ // to see the stack trace. There is no way to feature-detect it so there's a chance
+ // of the user agent sniffing to go wrong but since it's only about logging, this shouldn't
+ // break apps. Other browsers display errors in a sensible way and some of them map stack
+ // traces along source maps if available so it makes sense to let browsers display it
+ // as they want.
+ var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent);
+
return {
/**
* @ngdoc method
@@ -14330,8 +14773,8 @@ function $LogProvider() {
};
function formatError(arg) {
- if (arg instanceof Error) {
- if (arg.stack) {
+ if (isError(arg)) {
+ if (arg.stack && formatStackTrace) {
arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
? 'Error: ' + arg.message + '\n' + arg.stack
: arg.stack;
@@ -14344,29 +14787,17 @@ function $LogProvider() {
function consoleLog(type) {
var console = $window.console || {},
- logFn = console[type] || console.log || noop,
- hasApply = false;
-
- // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
- // The reason behind this is that console.log has type "object" in IE8...
- try {
- hasApply = !!logFn.apply;
- } catch (e) { /* empty */ }
-
- if (hasApply) {
- return function() {
- var args = [];
- forEach(arguments, function(arg) {
- args.push(formatError(arg));
- });
- return logFn.apply(console, args);
- };
- }
+ logFn = console[type] || console.log || noop;
- // we are IE which either doesn't have window.console => this is noop and we do nothing,
- // or we are IE where console.log doesn't have apply so we log at least first 2 args
- return function(arg1, arg2) {
- logFn(arg1, arg2 == null ? '' : arg2);
+ return function() {
+ var args = [];
+ forEach(arguments, function(arg) {
+ args.push(formatError(arg));
+ });
+ // Support: IE 9 only
+ // console methods don't inherit from Function.prototype in IE 9 so we can't
+ // call `logFn.apply(console, args)` directly.
+ return Function.prototype.apply.call(logFn, console, args);
};
}
}];
@@ -14387,12 +14818,12 @@ var $parseMinErr = minErr('$parse');
var objectValueOf = {}.constructor.prototype.valueOf;
-// Sandboxing Angular Expressions
+// Sandboxing AngularJS Expressions
// ------------------------------
-// Angular expressions are no longer sandboxed. So it is now even easier to access arbitrary JS code by
+// AngularJS expressions are no longer sandboxed. So it is now even easier to access arbitrary JS code by
// various means such as obtaining a reference to native JS functions like the Function constructor.
//
-// As an example, consider the following Angular expression:
+// As an example, consider the following AngularJS expression:
//
// {}.toString.constructor('alert("evil JS code")')
//
@@ -14994,15 +15425,47 @@ function isStateless($filter, filterName) {
return !fn.$stateful;
}
-function findConstantAndWatchExpressions(ast, $filter) {
+var PURITY_ABSOLUTE = 1;
+var PURITY_RELATIVE = 2;
+
+// Detect nodes which could depend on non-shallow state of objects
+function isPure(node, parentIsPure) {
+ switch (node.type) {
+ // Computed members might invoke a stateful toString()
+ case AST.MemberExpression:
+ if (node.computed) {
+ return false;
+ }
+ break;
+
+ // Unary always convert to primative
+ case AST.UnaryExpression:
+ return PURITY_ABSOLUTE;
+
+ // The binary + operator can invoke a stateful toString().
+ case AST.BinaryExpression:
+ return node.operator !== '+' ? PURITY_ABSOLUTE : false;
+
+ // Functions / filters probably read state from within objects
+ case AST.CallExpression:
+ return false;
+ }
+
+ return (undefined === parentIsPure) ? PURITY_RELATIVE : parentIsPure;
+}
+
+function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
var allConstants;
var argsToWatch;
var isStatelessFilter;
+
+ var astIsPure = ast.isPure = isPure(ast, parentIsPure);
+
switch (ast.type) {
case AST.Program:
allConstants = true;
forEach(ast.body, function(expr) {
- findConstantAndWatchExpressions(expr.expression, $filter);
+ findConstantAndWatchExpressions(expr.expression, $filter, astIsPure);
allConstants = allConstants && expr.expression.constant;
});
ast.constant = allConstants;
@@ -15012,26 +15475,26 @@ function findConstantAndWatchExpressions(ast, $filter) {
ast.toWatch = [];
break;
case AST.UnaryExpression:
- findConstantAndWatchExpressions(ast.argument, $filter);
+ findConstantAndWatchExpressions(ast.argument, $filter, astIsPure);
ast.constant = ast.argument.constant;
ast.toWatch = ast.argument.toWatch;
break;
case AST.BinaryExpression:
- findConstantAndWatchExpressions(ast.left, $filter);
- findConstantAndWatchExpressions(ast.right, $filter);
+ findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
+ findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
ast.constant = ast.left.constant && ast.right.constant;
ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
break;
case AST.LogicalExpression:
- findConstantAndWatchExpressions(ast.left, $filter);
- findConstantAndWatchExpressions(ast.right, $filter);
+ findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
+ findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
ast.constant = ast.left.constant && ast.right.constant;
ast.toWatch = ast.constant ? [] : [ast];
break;
case AST.ConditionalExpression:
- findConstantAndWatchExpressions(ast.test, $filter);
- findConstantAndWatchExpressions(ast.alternate, $filter);
- findConstantAndWatchExpressions(ast.consequent, $filter);
+ findConstantAndWatchExpressions(ast.test, $filter, astIsPure);
+ findConstantAndWatchExpressions(ast.alternate, $filter, astIsPure);
+ findConstantAndWatchExpressions(ast.consequent, $filter, astIsPure);
ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
ast.toWatch = ast.constant ? [] : [ast];
break;
@@ -15040,30 +15503,28 @@ function findConstantAndWatchExpressions(ast, $filter) {
ast.toWatch = [ast];
break;
case AST.MemberExpression:
- findConstantAndWatchExpressions(ast.object, $filter);
+ findConstantAndWatchExpressions(ast.object, $filter, astIsPure);
if (ast.computed) {
- findConstantAndWatchExpressions(ast.property, $filter);
+ findConstantAndWatchExpressions(ast.property, $filter, astIsPure);
}
ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
- ast.toWatch = [ast];
+ ast.toWatch = ast.constant ? [] : [ast];
break;
case AST.CallExpression:
isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false;
allConstants = isStatelessFilter;
argsToWatch = [];
forEach(ast.arguments, function(expr) {
- findConstantAndWatchExpressions(expr, $filter);
+ findConstantAndWatchExpressions(expr, $filter, astIsPure);
allConstants = allConstants && expr.constant;
- if (!expr.constant) {
- argsToWatch.push.apply(argsToWatch, expr.toWatch);
- }
+ argsToWatch.push.apply(argsToWatch, expr.toWatch);
});
ast.constant = allConstants;
ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
break;
case AST.AssignmentExpression:
- findConstantAndWatchExpressions(ast.left, $filter);
- findConstantAndWatchExpressions(ast.right, $filter);
+ findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
+ findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
ast.constant = ast.left.constant && ast.right.constant;
ast.toWatch = [ast];
break;
@@ -15071,11 +15532,9 @@ function findConstantAndWatchExpressions(ast, $filter) {
allConstants = true;
argsToWatch = [];
forEach(ast.elements, function(expr) {
- findConstantAndWatchExpressions(expr, $filter);
+ findConstantAndWatchExpressions(expr, $filter, astIsPure);
allConstants = allConstants && expr.constant;
- if (!expr.constant) {
- argsToWatch.push.apply(argsToWatch, expr.toWatch);
- }
+ argsToWatch.push.apply(argsToWatch, expr.toWatch);
});
ast.constant = allConstants;
ast.toWatch = argsToWatch;
@@ -15084,18 +15543,15 @@ function findConstantAndWatchExpressions(ast, $filter) {
allConstants = true;
argsToWatch = [];
forEach(ast.properties, function(property) {
- findConstantAndWatchExpressions(property.value, $filter);
- allConstants = allConstants && property.value.constant && !property.computed;
- if (!property.value.constant) {
- argsToWatch.push.apply(argsToWatch, property.value.toWatch);
- }
+ findConstantAndWatchExpressions(property.value, $filter, astIsPure);
+ allConstants = allConstants && property.value.constant;
+ argsToWatch.push.apply(argsToWatch, property.value.toWatch);
if (property.computed) {
- findConstantAndWatchExpressions(property.key, $filter);
- if (!property.key.constant) {
- argsToWatch.push.apply(argsToWatch, property.key.toWatch);
- }
+ //`{[key]: value}` implicitly does `key.toString()` which may be non-pure
+ findConstantAndWatchExpressions(property.key, $filter, /*parentIsPure=*/false);
+ allConstants = allConstants && property.key.constant;
+ argsToWatch.push.apply(argsToWatch, property.key.toWatch);
}
-
});
ast.constant = allConstants;
ast.toWatch = argsToWatch;
@@ -15141,15 +15597,13 @@ function isConstant(ast) {
return ast.constant;
}
-function ASTCompiler(astBuilder, $filter) {
- this.astBuilder = astBuilder;
+function ASTCompiler($filter) {
this.$filter = $filter;
}
ASTCompiler.prototype = {
- compile: function(expression) {
+ compile: function(ast) {
var self = this;
- var ast = this.astBuilder.ast(expression);
this.state = {
nextId: 0,
filters: {},
@@ -15177,7 +15631,7 @@ ASTCompiler.prototype = {
var intoId = self.nextId();
self.recurse(watch, intoId);
self.return_(intoId);
- self.state.inputs.push(fnKey);
+ self.state.inputs.push({name: fnKey, isPure: watch.isPure});
watch.watchId = key;
});
this.state.computing = 'fn';
@@ -15204,8 +15658,6 @@ ASTCompiler.prototype = {
ifDefined,
plusFn);
this.state = this.stage = undefined;
- fn.literal = isLiteral(ast);
- fn.constant = isConstant(ast);
return fn;
},
@@ -15215,13 +15667,16 @@ ASTCompiler.prototype = {
watchFns: function() {
var result = [];
- var fns = this.state.inputs;
+ var inputs = this.state.inputs;
var self = this;
- forEach(fns, function(name) {
- result.push('var ' + name + '=' + self.generateFunction(name, 's'));
+ forEach(inputs, function(input) {
+ result.push('var ' + input.name + '=' + self.generateFunction(input.name, 's'));
+ if (input.isPure) {
+ result.push(input.name, '.isPure=' + JSON.stringify(input.isPure) + ';');
+ }
});
- if (fns.length) {
- result.push('fn.inputs=[' + fns.join(',') + '];');
+ if (inputs.length) {
+ result.push('fn.inputs=[' + inputs.map(function(i) { return i.name; }).join(',') + '];');
}
return result.join('');
},
@@ -15608,15 +16063,13 @@ ASTCompiler.prototype = {
};
-function ASTInterpreter(astBuilder, $filter) {
- this.astBuilder = astBuilder;
+function ASTInterpreter($filter) {
this.$filter = $filter;
}
ASTInterpreter.prototype = {
- compile: function(expression) {
+ compile: function(ast) {
var self = this;
- var ast = this.astBuilder.ast(expression);
findConstantAndWatchExpressions(ast, self.$filter);
var assignable;
var assign;
@@ -15629,6 +16082,7 @@ ASTInterpreter.prototype = {
inputs = [];
forEach(toWatch, function(watch, key) {
var input = self.recurse(watch);
+ input.isPure = watch.isPure;
watch.input = input;
inputs.push(input);
watch.watchId = key;
@@ -15655,8 +16109,6 @@ ASTInterpreter.prototype = {
if (inputs) {
fn.inputs = inputs;
}
- fn.literal = isLiteral(ast);
- fn.constant = isConstant(ast);
return fn;
},
@@ -15985,20 +16437,36 @@ ASTInterpreter.prototype = {
/**
* @constructor
*/
-var Parser = function Parser(lexer, $filter, options) {
- this.lexer = lexer;
- this.$filter = $filter;
- this.options = options;
+function Parser(lexer, $filter, options) {
this.ast = new AST(lexer, options);
- this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
- new ASTCompiler(this.ast, $filter);
-};
+ this.astCompiler = options.csp ? new ASTInterpreter($filter) :
+ new ASTCompiler($filter);
+}
Parser.prototype = {
constructor: Parser,
parse: function(text) {
- return this.astCompiler.compile(text);
+ var ast = this.getAst(text);
+ var fn = this.astCompiler.compile(ast.ast);
+ fn.literal = isLiteral(ast.ast);
+ fn.constant = isConstant(ast.ast);
+ fn.oneTime = ast.oneTime;
+ return fn;
+ },
+
+ getAst: function(exp) {
+ var oneTime = false;
+ exp = exp.trim();
+
+ if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
+ oneTime = true;
+ exp = exp.substring(2);
+ }
+ return {
+ ast: this.ast.ast(exp),
+ oneTime: oneTime
+ };
}
};
@@ -16015,15 +16483,15 @@ function getValueOf(value) {
*
* @description
*
- * Converts Angular {@link guide/expression expression} into a function.
+ * Converts AngularJS {@link guide/expression expression} into a function.
*
* ```js
* var getter = $parse('user.name');
* var setter = getter.assign;
- * var context = {user:{name:'angular'}};
+ * var context = {user:{name:'AngularJS'}};
* var locals = {user:{name:'local'}};
*
- * expect(getter(context)).toEqual('angular');
+ * expect(getter(context)).toEqual('AngularJS');
* setter(context, 'newValue');
* expect(context.user.name).toEqual('newValue');
* expect(getter(context, locals)).toEqual('local');
@@ -16089,7 +16557,7 @@ function $ParseProvider() {
*
* @description
*
- * Allows defining the set of characters that are allowed in Angular expressions. The function
+ * Allows defining the set of characters that are allowed in AngularJS expressions. The function
* `identifierStart` will get called to know if a given character is a valid character to be the
* first character for an identifier. The function `identifierContinue` will get called to know if
* a given character is a valid character to be a follow-up identifier character. The functions
@@ -16121,10 +16589,11 @@ function $ParseProvider() {
isIdentifierStart: isFunction(identStart) && identStart,
isIdentifierContinue: isFunction(identContinue) && identContinue
};
+ $parse.$$getAst = $$getAst;
return $parse;
function $parse(exp, interceptorFn) {
- var parsedExpression, oneTime, cacheKey;
+ var parsedExpression, cacheKey;
switch (typeof exp) {
case 'string':
@@ -16134,16 +16603,12 @@ function $ParseProvider() {
parsedExpression = cache[cacheKey];
if (!parsedExpression) {
- if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
- oneTime = true;
- exp = exp.substring(2);
- }
var lexer = new Lexer($parseOptions);
var parser = new Parser(lexer, $filter, $parseOptions);
parsedExpression = parser.parse(exp);
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate;
- } else if (oneTime) {
+ } else if (parsedExpression.oneTime) {
parsedExpression.$$watchDelegate = parsedExpression.literal ?
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
} else if (parsedExpression.inputs) {
@@ -16161,20 +16626,26 @@ function $ParseProvider() {
}
}
+ function $$getAst(exp) {
+ var lexer = new Lexer($parseOptions);
+ var parser = new Parser(lexer, $filter, $parseOptions);
+ return parser.getAst(exp).ast;
+ }
+
function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
if (newValue == null || oldValueOfValue == null) { // null/undefined
return newValue === oldValueOfValue;
}
- if (typeof newValue === 'object' && !compareObjectIdentity) {
+ if (typeof newValue === 'object') {
// attempt to convert the value to a primitive type
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
// be cheaply dirty-checked
newValue = getValueOf(newValue);
- if (typeof newValue === 'object') {
+ if (typeof newValue === 'object' && !compareObjectIdentity) {
// objects/arrays are not supported - deep-watching them would be too expensive
return false;
}
@@ -16196,7 +16667,7 @@ function $ParseProvider() {
inputExpressions = inputExpressions[0];
return scope.$watch(function expressionInputWatch(scope) {
var newInputValue = inputExpressions(scope);
- if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) {
+ if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, inputExpressions.isPure)) {
lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
oldInputValueOf = newInputValue && getValueOf(newInputValue);
}
@@ -16216,7 +16687,7 @@ function $ParseProvider() {
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
var newInputValue = inputExpressions[i](scope);
- if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) {
+ if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], inputExpressions[i].isPure))) {
oldInputValues[i] = newInputValue;
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
}
@@ -16314,17 +16785,26 @@ function $ParseProvider() {
// Propagate $$watchDelegates other then inputsWatchDelegate
useInputs = !parsedExpression.inputs;
- if (parsedExpression.$$watchDelegate &&
- parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
- fn.$$watchDelegate = parsedExpression.$$watchDelegate;
+ if (watchDelegate && watchDelegate !== inputsWatchDelegate) {
+ fn.$$watchDelegate = watchDelegate;
fn.inputs = parsedExpression.inputs;
} else if (!interceptorFn.$stateful) {
- // If there is an interceptor, but no watchDelegate then treat the interceptor like
- // we treat filters - it is assumed to be a pure function unless flagged with $stateful
+ // Treat interceptor like filters - assume non-stateful by default and use the inputsWatchDelegate
fn.$$watchDelegate = inputsWatchDelegate;
fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
}
+ if (fn.inputs) {
+ fn.inputs = fn.inputs.map(function(e) {
+ // Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a
+ // potentially non-pure interceptor function.
+ if (e.isPure === PURITY_RELATIVE) {
+ return function depurifier(s) { return e(s); };
+ }
+ return e;
+ });
+ }
+
return fn;
}
}];
@@ -16345,7 +16825,7 @@ function $ParseProvider() {
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
* implementations, and the other which resembles ES6 (ES2015) promises to some degree.
*
- * # $q constructor
+ * ## $q constructor
*
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
* function as the first argument. This is similar to the native Promise implementation from ES6,
@@ -16433,7 +16913,7 @@ function $ParseProvider() {
* For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
* section on serial or parallel joining of promises.
*
- * # The Deferred API
+ * ## The Deferred API
*
* A new instance of deferred is constructed by calling `$q.defer()`.
*
@@ -16455,7 +16935,7 @@ function $ParseProvider() {
* - promise – `{Promise}` – promise object associated with this deferred.
*
*
- * # The Promise API
+ * ## The Promise API
*
* A new promise instance is created when a deferred instance is created and can be retrieved by
* calling `deferred.promise`.
@@ -16487,7 +16967,7 @@ function $ParseProvider() {
* specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
* more information.
*
- * # Chaining promises
+ * ## Chaining promises
*
* Because calling the `then` method of a promise returns a new derived promise, it is easily
* possible to create a chain of promises:
@@ -16507,17 +16987,17 @@ function $ParseProvider() {
* $http's response interceptors.
*
*
- * # Differences between Kris Kowal's Q and $q
+ * ## Differences between Kris Kowal's Q and $q
*
* There are two main differences:
*
* - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
- * mechanism in angular, which means faster propagation of resolution or rejection into your
+ * mechanism in AngularJS, which means faster propagation of resolution or rejection into your
* models and avoiding unnecessary browser repaints, which would result in flickering UI.
* - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
* all the important functionality needed for common async tasks.
*
- * # Testing
+ * ## Testing
*
* ```js
* it('should simulate promise', inject(function($q, $rootScope) {
@@ -16610,7 +17090,7 @@ function $$QProvider() {
* @param {function(function)} nextTick Function for executing functions in the next turn.
* @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
* debugging purposes.
- @ param {=boolean} errorOnUnhandledRejections Whether an error should be generated on unhandled
+ * @param {boolean=} errorOnUnhandledRejections Whether an error should be generated on unhandled
* promises rejections.
* @returns {object} Promise manager.
*/
@@ -16681,7 +17161,7 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
state.pending = undefined;
try {
for (var i = 0, ii = pending.length; i < ii; ++i) {
- state.pur = true;
+ markQStateExceptionHandled(state);
promise = pending[i][0];
fn = pending[i][state.status];
try {
@@ -16694,6 +17174,10 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
}
} catch (e) {
rejectPromise(promise, e);
+ // This error is explicitly marked for being passed to the $exceptionHandler
+ if (e && e.$$passToExceptionHandler === true) {
+ exceptionHandler(e);
+ }
}
}
} finally {
@@ -16708,10 +17192,10 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
// eslint-disable-next-line no-unmodified-loop-condition
while (!queueSize && checkQueue.length) {
var toCheck = checkQueue.shift();
- if (!toCheck.pur) {
- toCheck.pur = true;
+ if (!isStateExceptionHandled(toCheck)) {
+ markQStateExceptionHandled(toCheck);
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
- if (toCheck.value instanceof Error) {
+ if (isError(toCheck.value)) {
exceptionHandler(toCheck.value, errorMessage);
} else {
exceptionHandler(errorMessage);
@@ -16721,7 +17205,7 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
}
function scheduleProcessQueue(state) {
- if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !state.pur) {
+ if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !isStateExceptionHandled(state)) {
if (queueSize === 0 && checkQueue.length === 0) {
nextTick(processChecks);
}
@@ -17002,6 +17486,16 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
return $Q;
}
+function isStateExceptionHandled(state) {
+ return !!state.pur;
+}
+function markQStateExceptionHandled(state) {
+ state.pur = true;
+}
+function markQExceptionHandled(q) {
+ markQStateExceptionHandled(q.$$state);
+}
+
/** @this */
function $$RAFProvider() { //rAF
this.$get = ['$window', '$timeout', function($window, $timeout) {
@@ -17176,7 +17670,7 @@ function $RootScopeProvider() {
* an in-depth introduction and usage examples.
*
*
- * # Inheritance
+ * ## Inheritance
* A scope can inherit from a parent scope, as in this example:
* ```js
var parent = $rootScope;
@@ -17351,7 +17845,7 @@ function $RootScopeProvider() {
*
*
*
- * # Example
+ * @example
* ```js
// let's assume that scope was dependency injected as the $rootScope
var scope = $rootScope;
@@ -17427,14 +17921,15 @@ function $RootScopeProvider() {
*/
$watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
var get = $parse(watchExp);
+ var fn = isFunction(listener) ? listener : noop;
if (get.$$watchDelegate) {
- return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
+ return get.$$watchDelegate(this, fn, objectEquality, get, watchExp);
}
var scope = this,
array = scope.$$watchers,
watcher = {
- fn: listener,
+ fn: fn,
last: initWatchVal,
get: get,
exp: prettyPrintExpression || watchExp,
@@ -17443,10 +17938,6 @@ function $RootScopeProvider() {
lastDirtyWatch = null;
- if (!isFunction(listener)) {
- watcher.fn = noop;
- }
-
if (!array) {
array = scope.$$watchers = [];
array.$$digestWatchIndex = -1;
@@ -17482,6 +17973,12 @@ function $RootScopeProvider() {
* values are examined for changes on every call to `$digest`.
* - The `listener` is called whenever any expression in the `watchExpressions` array changes.
*
+ * `$watchGroup` is more performant than watching each expression individually, and should be
+ * used when the listener does not need to know which expression has changed.
+ * If the listener needs to know which expression has changed,
+ * {@link ng.$rootScope.Scope#$watch $watch()} or
+ * {@link ng.$rootScope.Scope#$watchCollection $watchCollection()} should be used.
+ *
* @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
* watched using {@link ng.$rootScope.Scope#$watch $watch()}
*
@@ -17490,7 +17987,34 @@ function $RootScopeProvider() {
* The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
* those of `watchExpression`
* and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
- * those of `watchExpression`
+ * those of `watchExpression`.
+ *
+ * Note that `newValues` and `oldValues` reflect the differences in each **individual**
+ * expression, and not the difference of the values between each call of the listener.
+ * That means the difference between `newValues` and `oldValues` cannot be used to determine
+ * which expression has changed / remained stable:
+ *
+ * ```js
+ *
+ * $scope.$watchGroup(['v1', 'v2'], function(newValues, oldValues) {
+ * console.log(newValues, oldValues);
+ * });
+ *
+ * // newValues, oldValues initially
+ * // [undefined, undefined], [undefined, undefined]
+ *
+ * $scope.v1 = 'a';
+ * $scope.v2 = 'a';
+ *
+ * // ['a', 'a'], [undefined, undefined]
+ *
+ * $scope.v2 = 'b'
+ *
+ * // v1 hasn't changed since it became `'a'`, therefore its oldValue is still `undefined`
+ * // ['a', 'b'], [undefined, 'a']
+ *
+ * ```
+ *
* The `scope` refers to the current scope.
* @returns {function()} Returns a de-registration function for all listeners.
*/
@@ -17569,7 +18093,7 @@ function $RootScopeProvider() {
* adding, removing, and moving items belonging to an object or array.
*
*
- * # Example
+ * @example
* ```js
$scope.names = ['igor', 'matias', 'misko', 'james'];
$scope.dataCount = 4;
@@ -17767,7 +18291,7 @@ function $RootScopeProvider() {
*
* In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
*
- * # Example
+ * @example
* ```js
var scope = ...;
scope.name = 'misko';
@@ -17819,12 +18343,13 @@ function $RootScopeProvider() {
current = target;
// It's safe for asyncQueuePosition to be a local variable here because this loop can't
- // be reentered recursively. Calling $digest from a function passed to $applyAsync would
+ // be reentered recursively. Calling $digest from a function passed to $evalAsync would
// lead to a '$digest already in progress' error.
for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
try {
asyncTask = asyncQueue[asyncQueuePosition];
- asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
+ fn = asyncTask.fn;
+ fn(asyncTask.scope, asyncTask.locals);
} catch (e) {
$exceptionHandler(e);
}
@@ -17992,10 +18517,10 @@ function $RootScopeProvider() {
*
* @description
* Executes the `expression` on the current scope and returns the result. Any exceptions in
- * the expression are propagated (uncaught). This is useful when evaluating Angular
+ * the expression are propagated (uncaught). This is useful when evaluating AngularJS
* expressions.
*
- * # Example
+ * @example
* ```js
var scope = ng.$rootScope.Scope();
scope.a = 1;
@@ -18005,7 +18530,7 @@ function $RootScopeProvider() {
expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
* ```
*
- * @param {(string|function())=} expression An angular expression to be executed.
+ * @param {(string|function())=} expression An AngularJS expression to be executed.
*
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with the current `scope` parameter.
@@ -18040,7 +18565,7 @@ function $RootScopeProvider() {
* will be scheduled. However, it is encouraged to always call code that changes the model
* from within an `$apply` call. That includes code evaluated via `$evalAsync`.
*
- * @param {(string|function())=} expression An angular expression to be executed.
+ * @param {(string|function())=} expression An AngularJS expression to be executed.
*
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with the current `scope` parameter.
@@ -18058,7 +18583,7 @@ function $RootScopeProvider() {
});
}
- asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
+ asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
},
$$postDigest: function(fn) {
@@ -18071,15 +18596,14 @@ function $RootScopeProvider() {
* @kind function
*
* @description
- * `$apply()` is used to execute an expression in angular from outside of the angular
+ * `$apply()` is used to execute an expression in AngularJS from outside of the AngularJS
* framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
- * Because we are calling into the angular framework we need to perform proper scope life
+ * Because we are calling into the AngularJS framework we need to perform proper scope life
* cycle of {@link ng.$exceptionHandler exception handling},
* {@link ng.$rootScope.Scope#$digest executing watches}.
*
- * ## Life cycle
+ * **Life cycle: Pseudo-Code of `$apply()`**
*
- * # Pseudo-Code of `$apply()`
* ```js
function $apply(expr) {
try {
@@ -18103,7 +18627,7 @@ function $RootScopeProvider() {
* expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
*
*
- * @param {(string|function())=} exp An angular expression to be executed.
+ * @param {(string|function())=} exp An AngularJS expression to be executed.
*
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with current `scope` parameter.
@@ -18143,7 +18667,7 @@ function $RootScopeProvider() {
* This can be used to queue up multiple expressions which need to be evaluated in the same
* digest.
*
- * @param {(string|function())=} exp An angular expression to be executed.
+ * @param {(string|function())=} exp An AngularJS expression to be executed.
*
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with current `scope` parameter.
@@ -18207,7 +18731,10 @@ function $RootScopeProvider() {
return function() {
var indexOfListener = namedListeners.indexOf(listener);
if (indexOfListener !== -1) {
- namedListeners[indexOfListener] = null;
+ // Use delete in the hope of the browser deallocating the memory for the array entry,
+ // while not shifting the array indexes of other listeners.
+ // See issue https://github.com/angular/angular.js/issues/16135
+ delete namedListeners[indexOfListener];
decrementListenerCount(self, 1, name);
}
};
@@ -18274,8 +18801,7 @@ function $RootScopeProvider() {
}
//if any listener on the current scope stops propagation, prevent bubbling
if (stopPropagation) {
- event.currentScope = null;
- return event;
+ break;
}
//traverse upwards
scope = scope.$parent;
@@ -18435,7 +18961,7 @@ function $RootScopeProvider() {
* @name $rootElement
*
* @description
- * The root element of Angular application. This is either the element where {@link
+ * The root element of AngularJS application. This is either the element where {@link
* ng.directive:ngApp ngApp} was declared or the element passed into
* {@link angular.bootstrap}. The element represents the root element of application. It is also the
* location where the application's {@link auto.$injector $injector} service gets
@@ -18451,7 +18977,7 @@ function $RootScopeProvider() {
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
*/
function $$SanitizeUriProvider() {
- var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
+ var aHrefSanitizationWhitelist = /^\s*(https?|s?ftp|mailto|tel|file):/,
imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
/**
@@ -18507,7 +19033,7 @@ function $$SanitizeUriProvider() {
return function sanitizeUri(uri, isImage) {
var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
var normalizedVal;
- normalizedVal = urlResolve(uri).href;
+ normalizedVal = urlResolve(uri && uri.trim()).href;
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
return 'unsafe:' + normalizedVal;
}
@@ -18532,12 +19058,21 @@ function $$SanitizeUriProvider() {
var $sceMinErr = minErr('$sce');
var SCE_CONTEXTS = {
+ // HTML is used when there's HTML rendered (e.g. ng-bind-html, iframe srcdoc binding).
HTML: 'html',
+
+ // Style statements or stylesheets. Currently unused in AngularJS.
CSS: 'css',
+
+ // An URL used in a context where it does not refer to a resource that loads code. Currently
+ // unused in AngularJS.
URL: 'url',
- // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
- // url. (e.g. ng-include, script src, templateUrl)
+
+ // RESOURCE_URL is a subtype of URL used where the referred-to resource could be interpreted as
+ // code. (e.g. ng-include, script src binding, templateUrl)
RESOURCE_URL: 'resourceUrl',
+
+ // Script. Currently unused in AngularJS.
JS: 'js'
};
@@ -18599,6 +19134,16 @@ function adjustMatchers(matchers) {
* `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
* Contextual Escaping (SCE)} services to AngularJS.
*
+ * For an overview of this service and the functionnality it provides in AngularJS, see the main
+ * page for {@link ng.$sce SCE}. The current page is targeted for developers who need to alter how
+ * SCE works in their application, which shouldn't be needed in most cases.
+ *
+ * <div class="alert alert-danger">
+ * AngularJS strongly relies on contextual escaping for the security of bindings: disabling or
+ * modifying this might cause cross site scripting (XSS) vulnerabilities. For libraries owners,
+ * changes to this service will also influence users, so be extra careful and document your changes.
+ * </div>
+ *
* Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
* the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
* because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
@@ -18624,12 +19169,16 @@ function adjustMatchers(matchers) {
* @description
*
* The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
- * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
- * that the URLs used for sourcing Angular templates are safe. Refer {@link
- * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
- * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
+ * $sceDelegate service}, used as a delegate for {@link ng.$sce Strict Contextual Escaping (SCE)}.
+ *
+ * The `$sceDelegateProvider` allows one to get/set the whitelists and blacklists used to ensure
+ * that the URLs used for sourcing AngularJS templates and other script-running URLs are safe (all
+ * places that use the `$sce.RESOURCE_URL` context). See
+ * {@link ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist}
+ * and
+ * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist},
*
- * For the general details about this service in Angular, read the main page for {@link ng.$sce
+ * For the general details about this service in AngularJS, read the main page for {@link ng.$sce
* Strict Contextual Escaping (SCE)}.
*
* **Example**: Consider the following case. <a name="example"></a>
@@ -18656,6 +19205,13 @@ function adjustMatchers(matchers) {
* ]);
* });
* ```
+ * Note that an empty whitelist will block every resource URL from being loaded, and will require
+ * you to manually mark each one as trusted with `$sce.trustAsResourceUrl`. However, templates
+ * requested by {@link ng.$templateRequest $templateRequest} that are present in
+ * {@link ng.$templateCache $templateCache} will not go through this check. If you have a mechanism
+ * to populate your templates in that cache at config time, then it is a good idea to remove 'self'
+ * from that whitelist. This helps to mitigate the security impact of certain types of issues, like
+ * for instance attacker-controlled `ng-includes`.
*/
function $SceDelegateProvider() {
@@ -18671,23 +19227,23 @@ function $SceDelegateProvider() {
* @kind function
*
* @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
- * provided. This must be an array or null. A snapshot of this array is used so further
- * changes to the array are ignored.
+ * provided. This must be an array or null. A snapshot of this array is used so further
+ * changes to the array are ignored.
+ * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+ * allowed in this array.
*
- * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
- * allowed in this array.
+ * @return {Array} The currently set whitelist array.
*
- * <div class="alert alert-warning">
- * **Note:** an empty whitelist array will block all URLs!
- * </div>
- *
- * @return {Array} the currently set whitelist array.
+ * @description
+ * Sets/Gets the whitelist of trusted resource URLs.
*
* The **default value** when no whitelist has been explicitly set is `['self']` allowing only
* same origin resource requests.
*
- * @description
- * Sets/Gets the whitelist of trusted resource URLs.
+ * <div class="alert alert-warning">
+ * **Note:** the default whitelist of 'self' is not recommended if your app shares its origin
+ * with other apps! It is a good idea to limit it to only your application's directory.
+ * </div>
*/
this.resourceUrlWhitelist = function(value) {
if (arguments.length) {
@@ -18702,25 +19258,23 @@ function $SceDelegateProvider() {
* @kind function
*
* @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
- * provided. This must be an array or null. A snapshot of this array is used so further
- * changes to the array are ignored.
- *
- * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
- * allowed in this array.
- *
- * The typical usage for the blacklist is to **block
- * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
- * these would otherwise be trusted but actually return content from the redirected domain.
+ * provided. This must be an array or null. A snapshot of this array is used so further
+ * changes to the array are ignored.</p><p>
+ * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
+ * allowed in this array.</p><p>
+ * The typical usage for the blacklist is to **block
+ * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
+ * these would otherwise be trusted but actually return content from the redirected domain.
+ * </p><p>
+ * Finally, **the blacklist overrides the whitelist** and has the final say.
+ *
+ * @return {Array} The currently set blacklist array.
*
- * Finally, **the blacklist overrides the whitelist** and has the final say.
- *
- * @return {Array} the currently set blacklist array.
+ * @description
+ * Sets/Gets the blacklist of trusted resource URLs.
*
* The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
* is no blacklist.)
- *
- * @description
- * Sets/Gets the blacklist of trusted resource URLs.
*/
this.resourceUrlBlacklist = function(value) {
@@ -18804,17 +19358,24 @@ function $SceDelegateProvider() {
* @name $sceDelegate#trustAs
*
* @description
- * Returns an object that is trusted by angular for use in specified strict
- * contextual escaping contexts (such as ng-bind-html, ng-include, any src
- * attribute interpolation, any dom event binding attribute interpolation
- * such as for onclick, etc.) that uses the provided value.
- * See {@link ng.$sce $sce} for enabling strict contextual escaping.
+ * Returns a trusted representation of the parameter for the specified context. This trusted
+ * object will later on be used as-is, without any security check, by bindings or directives
+ * that require this security context.
+ * For instance, marking a string as trusted for the `$sce.HTML` context will entirely bypass
+ * the potential `$sanitize` call in corresponding `$sce.HTML` bindings or directives, such as
+ * `ng-bind-html`. Note that in most cases you won't need to call this function: if you have the
+ * sanitizer loaded, passing the value itself will render all the HTML that does not pose a
+ * security risk.
+ *
+ * See {@link ng.$sceDelegate#getTrusted getTrusted} for the function that will consume those
+ * trusted values, and {@link ng.$sce $sce} for general documentation about strict contextual
+ * escaping.
*
- * @param {string} type The kind of context in which this value is safe for use. e.g. url,
- * resourceUrl, html, js and css.
- * @param {*} value The value that that should be considered trusted/safe.
- * @returns {*} A value that can be used to stand in for the provided `value` in places
- * where Angular expects a $sce.trustAs() return value.
+ * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
+ * `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
+ *
+ * @param {*} value The value that should be considered trusted.
+ * @return {*} A trusted representation of value, that can be used in the given context.
*/
function trustAs(type, trustedValue) {
var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
@@ -18846,11 +19407,11 @@ function $SceDelegateProvider() {
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
*
* If the passed parameter is not a value that had been returned by {@link
- * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
+ * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, it must be returned as-is.
*
* @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
- * call or anything else.
- * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
+ * call or anything else.
+ * @return {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
* `value` unchanged.
*/
@@ -18867,33 +19428,38 @@ function $SceDelegateProvider() {
* @name $sceDelegate#getTrusted
*
* @description
- * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
- * returns the originally supplied value if the queried context type is a supertype of the
- * created type. If this condition isn't satisfied, throws an exception.
+ * Takes any input, and either returns a value that's safe to use in the specified context, or
+ * throws an exception.
*
- * <div class="alert alert-danger">
- * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
- * (XSS) vulnerability in your application.
- * </div>
+ * In practice, there are several cases. When given a string, this function runs checks
+ * and sanitization to make it safe without prior assumptions. When given the result of a {@link
+ * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call, it returns the originally supplied
+ * value if that value's context is valid for this call's context. Finally, this function can
+ * also throw when there is no way to turn `maybeTrusted` in a safe value (e.g., no sanitization
+ * is available or possible.)
*
- * @param {string} type The kind of context in which this value is to be used.
+ * @param {string} type The context in which this value is to be used (such as `$sce.HTML`).
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
- * `$sceDelegate.trustAs`} call.
- * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
- * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
+ * `$sceDelegate.trustAs`} call, or anything else (which will not be considered trusted.)
+ * @return {*} A version of the value that's safe to use in the given context, or throws an
+ * exception if this is impossible.
*/
function getTrusted(type, maybeTrusted) {
if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
return maybeTrusted;
}
var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
+ // If maybeTrusted is a trusted class instance or subclass instance, then unwrap and return
+ // as-is.
if (constructor && maybeTrusted instanceof constructor) {
return maybeTrusted.$$unwrapTrustedValue();
}
- // If we get here, then we may only take one of two actions.
- // 1. sanitize the value for the requested type, or
- // 2. throw an exception.
+ // Otherwise, if we get here, then we may either make it safe, or throw an exception. This
+ // depends on the context: some are sanitizatible (HTML), some use whitelists (RESOURCE_URL),
+ // some are impossible to do (JS). This step isn't implemented for CSS and URL, as AngularJS
+ // has no corresponding sinks.
if (type === SCE_CONTEXTS.RESOURCE_URL) {
+ // RESOURCE_URL uses a whitelist.
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
return maybeTrusted;
} else {
@@ -18902,8 +19468,10 @@ function $SceDelegateProvider() {
maybeTrusted.toString());
}
} else if (type === SCE_CONTEXTS.HTML) {
+ // htmlSanitizer throws its own error when no sanitizer is available.
return htmlSanitizer(maybeTrusted);
}
+ // Default error when the $sce service has no way to make the input safe.
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
}
@@ -18937,23 +19505,29 @@ function $SceDelegateProvider() {
*
* `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
*
- * # Strict Contextual Escaping
+ * ## Strict Contextual Escaping
+ *
+ * Strict Contextual Escaping (SCE) is a mode in which AngularJS constrains bindings to only render
+ * trusted values. Its goal is to assist in writing code in a way that (a) is secure by default, and
+ * (b) makes auditing for security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
*
- * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
- * contexts to result in a value that is marked as safe to use for that context. One example of
- * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
- * to these contexts as privileged or SCE contexts.
+ * ### Overview
*
- * As of version 1.2, Angular ships with SCE enabled by default.
+ * To systematically block XSS security bugs, AngularJS treats all values as untrusted by default in
+ * HTML or sensitive URL bindings. When binding untrusted values, AngularJS will automatically
+ * run security checks on them (sanitizations, whitelists, depending on context), or throw when it
+ * cannot guarantee the security of the result. That behavior depends strongly on contexts: HTML
+ * can be sanitized, but template URLs cannot, for instance.
*
- * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
- * one to execute arbitrary javascript by the use of the expression() syntax. Refer
- * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
- * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
- * to the top of your HTML document.
+ * To illustrate this, consider the `ng-bind-html` directive. It renders its value directly as HTML:
+ * we call that the *context*. When given an untrusted input, AngularJS will attempt to sanitize it
+ * before rendering if a sanitizer is available, and throw otherwise. To bypass sanitization and
+ * render the input as-is, you will need to mark it as trusted for that context before attempting
+ * to bind it.
*
- * SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for
- * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
+ * As of version 1.2, AngularJS ships with SCE enabled by default.
+ *
+ * ### In practice
*
* Here's an example of a binding in a privileged context:
*
@@ -18963,10 +19537,10 @@ function $SceDelegateProvider() {
* ```
*
* Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
- * disabled, this application allows the user to render arbitrary HTML into the DIV.
- * In a more realistic example, one may be rendering user comments, blog articles, etc. via
- * bindings. (HTML is just one example of a context where rendering user controlled input creates
- * security vulnerabilities.)
+ * disabled, this application allows the user to render arbitrary HTML into the DIV, which would
+ * be an XSS security bug. In a more realistic example, one may be rendering user comments, blog
+ * articles, etc. via bindings. (HTML is just one example of a context where rendering user
+ * controlled input creates security vulnerabilities.)
*
* For the case of HTML, you might use a library, either on the client side, or on the server side,
* to sanitize unsafe HTML before binding to the value and rendering it in the document.
@@ -18976,25 +19550,29 @@ function $SceDelegateProvider() {
* ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
* properties/fields and forgot to update the binding to the sanitized value?
*
- * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
- * determine that something explicitly says it's safe to use a value for binding in that
- * context. You can then audit your code (a simple grep would do) to ensure that this is only done
- * for those values that you can easily tell are safe - because they were received from your server,
- * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
- * allowing only the files in a specific directory to do this. Ensuring that the internal API
- * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
+ * To be secure by default, AngularJS makes sure bindings go through that sanitization, or
+ * any similar validation process, unless there's a good reason to trust the given value in this
+ * context. That trust is formalized with a function call. This means that as a developer, you
+ * can assume all untrusted bindings are safe. Then, to audit your code for binding security issues,
+ * you just need to ensure the values you mark as trusted indeed are safe - because they were
+ * received from your server, sanitized by your library, etc. You can organize your codebase to
+ * help with this - perhaps allowing only the files in a specific directory to do this.
+ * Ensuring that the internal API exposed by that code doesn't markup arbitrary values as safe then
+ * becomes a more manageable task.
*
* In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
* (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
- * obtain values that will be accepted by SCE / privileged contexts.
- *
+ * build the trusted versions of your values.
*
- * ## How does it work?
+ * ### How does it work?
*
* In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
- * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
- * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
- * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
+ * $sce.getTrusted(context, value)} rather than to the value directly. Think of this function as
+ * a way to enforce the required security context in your data sink. Directives use {@link
+ * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs
+ * the {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. Also,
+ * when binding without directives, AngularJS will understand the context of your bindings
+ * automatically.
*
* As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
* ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
@@ -19010,12 +19588,12 @@ function $SceDelegateProvider() {
* }];
* ```
*
- * ## Impact on loading templates
+ * ### Impact on loading templates
*
* This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
* `templateUrl`'s specified by {@link guide/directive directives}.
*
- * By default, Angular only loads templates from the same domain and protocol as the application
+ * By default, AngularJS only loads templates from the same domain and protocol as the application
* document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
* $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
* protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
@@ -19030,16 +19608,17 @@ function $SceDelegateProvider() {
* won't work on all browsers. Also, loading templates from `file://` URL does not work on some
* browsers.
*
- * ## This feels like too much overhead
+ * ### This feels like too much overhead
*
* It's important to remember that SCE only applies to interpolation expressions.
*
* If your expressions are constant literals, they're automatically trusted and you don't need to
- * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
- * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
- *
- * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
- * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
+ * call `$sce.trustAs` on them (e.g.
+ * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. The `$sceDelegate` will
+ * also use the `$sanitize` service if it is available when binding untrusted values to
+ * `$sce.HTML` context. AngularJS provides an implementation in `angular-sanitize.js`, and if you
+ * wish to use it, you will also need to depend on the {@link ngSanitize `ngSanitize`} module in
+ * your application.
*
* The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
* templates in `ng-include` from your application's domain without having to even know about SCE.
@@ -19053,17 +19632,23 @@ function $SceDelegateProvider() {
* security onto an application later.
*
* <a name="contexts"></a>
- * ## What trusted context types are supported?
+ * ### What trusted context types are supported?
*
* | Context | Notes |
* |---------------------|----------------|
- * | `$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. |
- * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
- * | `$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. |
- * | `$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`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (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. |
- * | `$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. |
+ * | `$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 $sanitize} service is available (implemented by the {@link ngSanitize ngSanitize} module) this will sanitize the value instead of throwing an error. |
+ * | `$sce.CSS` | For CSS that's safe to source into the application. Currently, no bindings require this context. Feel free to use it in your own directives. |
+ * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=`, `<img src=`, and some others sanitize their urls and don't constitute an SCE context.) |
+ * | `$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`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does (it's not just the URL that matters, but also what is at the end of it), and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
+ * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently, no bindings require this context. Feel free to use it in your own directives. |
+ *
*
- * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
+ * Be aware that `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
+ * through {@link ng.$sce#getTrusted $sce.getTrusted}. There's no CSS-, URL-, or JS-context bindings
+ * in AngularJS currently, so their corresponding `$sce.trustAs` functions aren't useful yet. This
+ * might evolve.
+ *
+ * ### Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
*
* Each element in these arrays must be one of the following:
*
@@ -19110,7 +19695,7 @@ function $SceDelegateProvider() {
*
* Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
*
- * ## Show me an example using SCE.
+ * ### Show me an example using SCE.
*
* <example module="mySceApp" deps="angular-sanitize.js" name="sce-service">
* <file name="index.html">
@@ -19180,14 +19765,15 @@ function $SceDelegateProvider() {
* for little coding overhead. It will be much harder to take an SCE disabled application and
* either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
* for cases where you have a lot of existing code that was written before SCE was introduced and
- * you're migrating them a module at a time.
+ * you're migrating them a module at a time. Also do note that this is an app-wide setting, so if
+ * you are writing a library, you will cause security bugs applications using it.
*
* That said, here's how you can completely disable SCE:
*
* ```
* angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
* // Completely disable SCE. For demonstration purposes only!
- * // Do not use in new projects.
+ * // Do not use in new projects or libraries.
* $sceProvider.enabled(false);
* });
* ```
@@ -19202,8 +19788,8 @@ function $SceProvider() {
* @name $sceProvider#enabled
* @kind function
*
- * @param {boolean=} value If provided, then enables/disables SCE.
- * @return {boolean} true if SCE is enabled, false otherwise.
+ * @param {boolean=} value If provided, then enables/disables SCE application-wide.
+ * @return {boolean} True if SCE is enabled, false otherwise.
*
* @description
* Enables/disables SCE and returns the current value.
@@ -19257,9 +19843,9 @@ function $SceProvider() {
* getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
* will also succeed.
*
- * Inheritance happens to capture this in a natural way. In some future, we
- * may not use inheritance anymore. That is OK because no code outside of
- * sce.js and sceSpecs.js would need to be aware of this detail.
+ * Inheritance happens to capture this in a natural way. In some future, we may not use
+ * inheritance anymore. That is OK because no code outside of sce.js and sceSpecs.js would need to
+ * be aware of this detail.
*/
this.$get = ['$parse', '$sceDelegate', function(
@@ -19281,8 +19867,8 @@ function $SceProvider() {
* @name $sce#isEnabled
* @kind function
*
- * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
- * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
+ * @return {Boolean} True if SCE is enabled, false otherwise. If you want to set the value, you
+ * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
*
* @description
* Returns a boolean indicating if SCE is enabled.
@@ -19304,19 +19890,19 @@ function $SceProvider() {
* @name $sce#parseAs
*
* @description
- * Converts Angular {@link guide/expression expression} into a function. This is like {@link
+ * Converts AngularJS {@link guide/expression expression} into a function. This is like {@link
* ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
* wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
* *result*)}
*
- * @param {string} type The kind of SCE context in which this result will be used.
+ * @param {string} type The SCE context in which this result will be used.
* @param {string} expression String expression to compile.
- * @returns {function(context, locals)} a function which represents the compiled expression:
+ * @return {function(context, locals)} A function which represents the compiled expression:
*
- * * `context` – `{object}` – an object against which any expressions embedded in the strings
- * are evaluated against (typically a scope object).
- * * `locals` – `{object=}` – local variables context object, useful for overriding values in
- * `context`.
+ * * `context` – `{object}` – an object against which any expressions embedded in the
+ * strings are evaluated against (typically a scope object).
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values
+ * in `context`.
*/
sce.parseAs = function sceParseAs(type, expr) {
var parsed = $parse(expr);
@@ -19334,18 +19920,18 @@ function $SceProvider() {
* @name $sce#trustAs
*
* @description
- * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
- * returns an object that is trusted by angular for use in specified strict contextual
- * escaping contexts (such as ng-bind-html, ng-include, any src attribute
- * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
- * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
- * escaping.
+ * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, returns a
+ * wrapped object that represents your value, and the trust you have in its safety for the given
+ * context. AngularJS can then use that value as-is in bindings of the specified secure context.
+ * This is used in bindings for `ng-bind-html`, `ng-include`, and most `src` attribute
+ * interpolations. See {@link ng.$sce $sce} for strict contextual escaping.
*
- * @param {string} type The kind of context in which this value is safe for use. e.g. url,
- * resourceUrl, html, js and css.
- * @param {*} value The value that that should be considered trusted/safe.
- * @returns {*} A value that can be used to stand in for the provided `value` in places
- * where Angular expects a $sce.trustAs() return value.
+ * @param {string} type The context in which this value is safe for use, e.g. `$sce.URL`,
+ * `$sce.RESOURCE_URL`, `$sce.HTML`, `$sce.JS` or `$sce.CSS`.
+ *
+ * @param {*} value The value that that should be considered trusted.
+ * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
+ * in the context you specified.
*/
/**
@@ -19356,11 +19942,23 @@ function $SceProvider() {
* Shorthand method. `$sce.trustAsHtml(value)` →
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
*
- * @param {*} value The value to trustAs.
- * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
- * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
- * only accept expressions that are either literal constants or are the
- * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
+ * @param {*} value The value to mark as trusted for `$sce.HTML` context.
+ * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
+ * in `$sce.HTML` context (like `ng-bind-html`).
+ */
+
+ /**
+ * @ngdoc method
+ * @name $sce#trustAsCss
+ *
+ * @description
+ * Shorthand method. `$sce.trustAsCss(value)` →
+ * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.CSS, value)`}
+ *
+ * @param {*} value The value to mark as trusted for `$sce.CSS` context.
+ * @return {*} A wrapped version of value that can be used as a trusted variant
+ * of your `value` in `$sce.CSS` context. This context is currently unused, so there are
+ * almost no reasons to use this function so far.
*/
/**
@@ -19371,11 +19969,10 @@ function $SceProvider() {
* Shorthand method. `$sce.trustAsUrl(value)` →
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
*
- * @param {*} value The value to trustAs.
- * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
- * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
- * only accept expressions that are either literal constants or are the
- * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
+ * @param {*} value The value to mark as trusted for `$sce.URL` context.
+ * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
+ * in `$sce.URL` context. That context is currently unused, so there are almost no reasons
+ * to use this function so far.
*/
/**
@@ -19386,11 +19983,10 @@ function $SceProvider() {
* Shorthand method. `$sce.trustAsResourceUrl(value)` →
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
*
- * @param {*} value The value to trustAs.
- * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
- * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
- * only accept expressions that are either literal constants or are the return
- * value of {@link ng.$sce#trustAs $sce.trustAs}.)
+ * @param {*} value The value to mark as trusted for `$sce.RESOURCE_URL` context.
+ * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
+ * in `$sce.RESOURCE_URL` context (template URLs in `ng-include`, most `src` attribute
+ * bindings, ...)
*/
/**
@@ -19401,11 +19997,10 @@ function $SceProvider() {
* Shorthand method. `$sce.trustAsJs(value)` →
* {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
*
- * @param {*} value The value to trustAs.
- * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
- * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
- * only accept expressions that are either literal constants or are the
- * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
+ * @param {*} value The value to mark as trusted for `$sce.JS` context.
+ * @return {*} A wrapped version of value that can be used as a trusted variant of your `value`
+ * in `$sce.JS` context. That context is currently unused, so there are almost no reasons to
+ * use this function so far.
*/
/**
@@ -19414,16 +20009,17 @@ function $SceProvider() {
*
* @description
* Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
- * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
- * originally supplied value if the queried context type is a supertype of the created type.
- * If this condition isn't satisfied, throws an exception.
+ * takes any input, and either returns a value that's safe to use in the specified context,
+ * or throws an exception. This function is aware of trusted values created by the `trustAs`
+ * function and its shorthands, and when contexts are appropriate, returns the unwrapped value
+ * as-is. Finally, this function can also throw when there is no way to turn `maybeTrusted` in a
+ * safe value (e.g., no sanitization is available or possible.)
*
- * @param {string} type The kind of context in which this value is to be used.
- * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
- * call.
- * @returns {*} The value the was originally provided to
- * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
- * Otherwise, throws an exception.
+ * @param {string} type The context in which this value is to be used.
+ * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs
+ * `$sce.trustAs`} call, or anything else (which will not be considered trusted.)
+ * @return {*} A version of the value that's safe to use in the given context, or throws an
+ * exception if this is impossible.
*/
/**
@@ -19435,7 +20031,7 @@ function $SceProvider() {
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
*
* @param {*} value The value to pass to `$sce.getTrusted`.
- * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
+ * @return {*} The return value of `$sce.getTrusted($sce.HTML, value)`
*/
/**
@@ -19447,7 +20043,7 @@ function $SceProvider() {
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
*
* @param {*} value The value to pass to `$sce.getTrusted`.
- * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
+ * @return {*} The return value of `$sce.getTrusted($sce.CSS, value)`
*/
/**
@@ -19459,7 +20055,7 @@ function $SceProvider() {
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
*
* @param {*} value The value to pass to `$sce.getTrusted`.
- * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
+ * @return {*} The return value of `$sce.getTrusted($sce.URL, value)`
*/
/**
@@ -19471,7 +20067,7 @@ function $SceProvider() {
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
*
* @param {*} value The value to pass to `$sceDelegate.getTrusted`.
- * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
+ * @return {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
*/
/**
@@ -19483,7 +20079,7 @@ function $SceProvider() {
* {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
*
* @param {*} value The value to pass to `$sce.getTrusted`.
- * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
+ * @return {*} The return value of `$sce.getTrusted($sce.JS, value)`
*/
/**
@@ -19495,12 +20091,12 @@ function $SceProvider() {
* {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
*
* @param {string} expression String expression to compile.
- * @returns {function(context, locals)} a function which represents the compiled expression:
+ * @return {function(context, locals)} A function which represents the compiled expression:
*
- * * `context` – `{object}` – an object against which any expressions embedded in the strings
- * are evaluated against (typically a scope object).
- * * `locals` – `{object=}` – local variables context object, useful for overriding values in
- * `context`.
+ * * `context` – `{object}` – an object against which any expressions embedded in the
+ * strings are evaluated against (typically a scope object).
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values
+ * in `context`.
*/
/**
@@ -19512,12 +20108,12 @@ function $SceProvider() {
* {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
*
* @param {string} expression String expression to compile.
- * @returns {function(context, locals)} a function which represents the compiled expression:
+ * @return {function(context, locals)} A function which represents the compiled expression:
*
- * * `context` – `{object}` – an object against which any expressions embedded in the strings
- * are evaluated against (typically a scope object).
- * * `locals` – `{object=}` – local variables context object, useful for overriding values in
- * `context`.
+ * * `context` – `{object}` – an object against which any expressions embedded in the
+ * strings are evaluated against (typically a scope object).
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values
+ * in `context`.
*/
/**
@@ -19529,12 +20125,12 @@ function $SceProvider() {
* {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
*
* @param {string} expression String expression to compile.
- * @returns {function(context, locals)} a function which represents the compiled expression:
+ * @return {function(context, locals)} A function which represents the compiled expression:
*
- * * `context` – `{object}` – an object against which any expressions embedded in the strings
- * are evaluated against (typically a scope object).
- * * `locals` – `{object=}` – local variables context object, useful for overriding values in
- * `context`.
+ * * `context` – `{object}` – an object against which any expressions embedded in the
+ * strings are evaluated against (typically a scope object).
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values
+ * in `context`.
*/
/**
@@ -19546,12 +20142,12 @@ function $SceProvider() {
* {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
*
* @param {string} expression String expression to compile.
- * @returns {function(context, locals)} a function which represents the compiled expression:
+ * @return {function(context, locals)} A function which represents the compiled expression:
*
- * * `context` – `{object}` – an object against which any expressions embedded in the strings
- * are evaluated against (typically a scope object).
- * * `locals` – `{object=}` – local variables context object, useful for overriding values in
- * `context`.
+ * * `context` – `{object}` – an object against which any expressions embedded in the
+ * strings are evaluated against (typically a scope object).
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values
+ * in `context`.
*/
/**
@@ -19563,12 +20159,12 @@ function $SceProvider() {
* {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
*
* @param {string} expression String expression to compile.
- * @returns {function(context, locals)} a function which represents the compiled expression:
+ * @return {function(context, locals)} A function which represents the compiled expression:
*
- * * `context` – `{object}` – an object against which any expressions embedded in the strings
- * are evaluated against (typically a scope object).
- * * `locals` – `{object=}` – local variables context object, useful for overriding values in
- * `context`.
+ * * `context` – `{object}` – an object against which any expressions embedded in the
+ * strings are evaluated against (typically a scope object).
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values
+ * in `context`.
*/
// Shorthand delegations.
@@ -19729,6 +20325,12 @@ function $TemplateRequestProvider() {
* If you want to pass custom options to the `$http` service, such as setting the Accept header you
* can configure this via {@link $templateRequestProvider#httpOptions}.
*
+ * `$templateRequest` is used internally by {@link $compile}, {@link ngRoute.$route}, and directives such
+ * as {@link ngInclude} to download and cache templates.
+ *
+ * 3rd party modules should use `$templateRequest` if their services or directives are loading
+ * templates.
+ *
* @param {string|TrustedResourceUrl} tpl The HTTP request template URL
* @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
*
@@ -19744,7 +20346,7 @@ function $TemplateRequestProvider() {
// We consider the template cache holds only trusted templates, so
// there's no need to go through whitelisting again for keys that already
- // are included in there. This also makes Angular accept any script
+ // are included in there. This also makes AngularJS accept any script
// directive, no matter its name. However, we still need to unwrap trusted
// types.
if (!isString(tpl) || isUndefined($templateCache.get(tpl))) {
@@ -19922,7 +20524,7 @@ function $TimeoutProvider() {
* @name $timeout
*
* @description
- * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
+ * AngularJS's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
* block and delegates any exceptions to
* {@link ng.$exceptionHandler $exceptionHandler} service.
*
@@ -19994,7 +20596,7 @@ function $TimeoutProvider() {
timeout.cancel = function(promise) {
if (promise && promise.$$timeoutId in deferreds) {
// Timeout cancels should not report an unhandled promise.
- deferreds[promise.$$timeoutId].promise.catch(noop);
+ markQExceptionHandled(deferreds[promise.$$timeoutId].promise);
deferreds[promise.$$timeoutId].reject('canceled');
delete deferreds[promise.$$timeoutId];
return $browser.defer.cancel(promise.$$timeoutId);
@@ -20026,7 +20628,7 @@ var originUrl = urlResolve(window.location.href);
* URL will be resolved into an absolute URL in the context of the application document.
* Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
* properties are all populated to reflect the normalized URL. This approach has wide
- * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
+ * compatibility - Safari 1+, Mozilla 1+ etc. See
* http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
*
* Implementation Notes for IE
@@ -20111,7 +20713,7 @@ function urlIsSameOrigin(requestUrl) {
* @description
* A reference to the browser's `window` object. While `window`
* is globally available in JavaScript, it causes testability problems, because
- * it is a global variable. In angular we always refer to it through the
+ * it is a global variable. In AngularJS we always refer to it through the
* `$window` service, so it may be overridden, removed or mocked for testing.
*
* Expressions, like the one defined for the `ngClick` directive in the example
@@ -20234,7 +20836,7 @@ function $$CookieReaderProvider() {
* annotated with dependencies and is responsible for creating a filter function.
*
* <div class="alert alert-warning">
- * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
+ * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`.
* Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
* your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
* (`myapp_subsection_filterx`).
@@ -20277,8 +20879,8 @@ function $$CookieReaderProvider() {
* ```
*
*
- * For more information about how angular filters work, and how to create your own filters, see
- * {@link guide/filter Filters} in the Angular Developer Guide.
+ * For more information about how AngularJS filters work, and how to create your own filters, see
+ * {@link guide/filter Filters} in the AngularJS Developer Guide.
*/
/**
@@ -20288,7 +20890,7 @@ function $$CookieReaderProvider() {
* @description
* Filters are used for formatting data displayed to the user.
*
- * They can be used in view templates, controllers or services.Angular comes
+ * They can be used in view templates, controllers or services. AngularJS comes
* with a collection of [built-in filters](api/ng/filter), but it is easy to
* define your own as well.
*
@@ -20330,7 +20932,7 @@ function $FilterProvider($provide) {
* the keys are the filter names and the values are the filter factories.
*
* <div class="alert alert-warning">
- * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
+ * **Note:** Filter names must be valid AngularJS {@link expression} identifiers, such as `uppercase` or `orderBy`.
* Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
* your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
* (`myapp_subsection_filterx`).
@@ -20392,6 +20994,9 @@ function $FilterProvider($provide) {
* Selects a subset of items from `array` and returns it as a new array.
*
* @param {Array} array The source array.
+ * <div class="alert alert-info">
+ * **Note**: If the array contains objects that reference themselves, filtering is not possible.
+ * </div>
* @param {string|Object|function()} expression The predicate to be used for selecting items from
* `array`.
*
@@ -20425,8 +21030,9 @@ function $FilterProvider($provide) {
* The final result is an array of those elements that the predicate returned true for.
*
* @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
- * determining if the expected value (from the filter expression) and actual value (from
- * the object in the array) should be considered a match.
+ * determining if values retrieved using `expression` (when it is not a function) should be
+ * considered a match based on the expected value (from the filter expression) and actual
+ * value (from the object in the array).
*
* Can be one of:
*
@@ -20609,7 +21215,10 @@ function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstA
var key;
if (matchAgainstAnyProp) {
for (key in actual) {
- if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
+ // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
+ // See: https://github.com/angular/angular.js/issues/15644
+ if (key.charAt && (key.charAt(0) !== '$') &&
+ deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
return true;
}
}
@@ -20711,11 +21320,14 @@ function currencyFilter($locale) {
fractionSize = formats.PATTERNS[1].maxFrac;
}
+ // If the currency symbol is empty, trim whitespace around the symbol
+ var currencySymbolRe = !currencySymbol ? /\s*\u00A4\s*/g : /\u00A4/g;
+
// if null or undefined pass it through
return (amount == null)
? amount
: formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
- replace(/\u00A4/g, currencySymbol);
+ replace(currencySymbolRe, currencySymbol);
};
}
@@ -21118,7 +21730,7 @@ var DATE_FORMATS = {
GGGG: longEraGetter
};
-var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
+var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))([\s\S]*)/,
NUMBER_STRING = /^-?\d+$/;
/**
@@ -21177,6 +21789,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+
* `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
* (e.g. `"h 'o''clock'"`).
*
+ * Any other characters in the `format` string will be output as-is.
+ *
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
@@ -21339,6 +21953,9 @@ function jsonFilter() {
* @kind function
* @description
* Converts string to lowercase.
+ *
+ * See the {@link ng.uppercase uppercase filter documentation} for a functionally identical example.
+ *
* @see angular.lowercase
*/
var lowercaseFilter = valueFn(lowercase);
@@ -21350,7 +21967,23 @@ var lowercaseFilter = valueFn(lowercase);
* @kind function
* @description
* Converts string to uppercase.
- * @see angular.uppercase
+ * @example
+ <example module="uppercaseFilterExample" name="filter-uppercase">
+ <file name="index.html">
+ <script>
+ angular.module('uppercaseFilterExample', [])
+ .controller('ExampleController', ['$scope', function($scope) {
+ $scope.title = 'This is a title';
+ }]);
+ </script>
+ <div ng-controller="ExampleController">
+ <!-- This title should be formatted normally -->
+ <h1>{{title}}</h1>
+ <!-- This title should be capitalized -->
+ <h1>{{title | uppercase}}</h1>
+ </div>
+ </file>
+ </example>
*/
var uppercaseFilter = valueFn(uppercase);
@@ -21539,6 +22172,9 @@ function sliceFn(input, begin, end) {
* dummy predicate that returns the item's index as `value`.
* (If you are using a custom comparator, make sure it can handle this predicate as well.)
*
+ * If a custom comparator still can't distinguish between two items, then they will be sorted based
+ * on their index using the built-in comparator.
+ *
* Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
* value for an item, `orderBy` will try to convert that object to a primitive value, before passing
* it to the comparator. The following rules govern the conversion:
@@ -21584,7 +22220,7 @@ function sliceFn(input, begin, end) {
*
* - `Function`: A getter function. This function will be called with each item as argument and
* the return value will be used for sorting.
- * - `string`: An Angular expression. This expression will be evaluated against each item and the
+ * - `string`: An AngularJS expression. This expression will be evaluated against each item and the
* result will be used for sorting. For example, use `'label'` to sort by a property called
* `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
* property.<br />
@@ -22085,7 +22721,7 @@ function orderByFilter($parse) {
}
}
- return compare(v1.tieBreaker, v2.tieBreaker) * descending;
+ return (compare(v1.tieBreaker, v2.tieBreaker) || defaultCompare(v1.tieBreaker, v2.tieBreaker)) * descending;
}
};
@@ -22230,10 +22866,10 @@ var htmlAnchorDirective = valueFn({
* @priority 99
*
* @description
- * Using Angular markup like `{{hash}}` in an href attribute will
+ * Using AngularJS markup like `{{hash}}` in an href attribute will
* make the link go to the wrong URL if the user clicks it before
- * Angular has a chance to replace the `{{hash}}` markup with its
- * value. Until Angular replaces the markup the link will be broken
+ * AngularJS has a chance to replace the `{{hash}}` markup with its
+ * value. Until AngularJS replaces the markup the link will be broken
* and will most likely return a 404 error. The `ngHref` directive
* solves this problem.
*
@@ -22281,7 +22917,7 @@ var htmlAnchorDirective = valueFn({
element(by.id('link-3')).click();
- // At this point, we navigate away from an Angular page, so we need
+ // At this point, we navigate away from an AngularJS page, so we need
// to use browser.driver to get the base webdriver.
browser.wait(function() {
@@ -22310,7 +22946,7 @@ var htmlAnchorDirective = valueFn({
element(by.id('link-6')).click();
- // At this point, we navigate away from an Angular page, so we need
+ // At this point, we navigate away from an AngularJS page, so we need
// to use browser.driver to get the base webdriver.
browser.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
@@ -22329,9 +22965,9 @@ var htmlAnchorDirective = valueFn({
* @priority 99
*
* @description
- * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
+ * Using AngularJS markup like `{{hash}}` in a `src` attribute doesn't
* work right: The browser will fetch from the URL with the literal
- * text `{{hash}}` until Angular replaces the expression inside
+ * text `{{hash}}` until AngularJS replaces the expression inside
* `{{hash}}`. The `ngSrc` directive solves this problem.
*
* The buggy way to write it:
@@ -22355,9 +22991,9 @@ var htmlAnchorDirective = valueFn({
* @priority 99
*
* @description
- * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
+ * Using AngularJS markup like `{{hash}}` in a `srcset` attribute doesn't
* work right: The browser will fetch from the URL with the literal
- * text `{{hash}}` until Angular replaces the expression inside
+ * text `{{hash}}` until AngularJS replaces the expression inside
* `{{hash}}`. The `ngSrcset` directive solves this problem.
*
* The buggy way to write it:
@@ -22428,14 +23064,14 @@ var htmlAnchorDirective = valueFn({
* @example
<example name="ng-checked">
<file name="index.html">
- <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
- <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
+ <label>Check me to check both: <input type="checkbox" ng-model="leader"></label><br/>
+ <input id="checkFollower" type="checkbox" ng-checked="leader" aria-label="Follower input">
</file>
<file name="protractor.js" type="protractor">
it('should check both checkBoxes', function() {
- expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
- element(by.model('master')).click();
- expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
+ expect(element(by.id('checkFollower')).getAttribute('checked')).toBeFalsy();
+ element(by.model('leader')).click();
+ expect(element(by.id('checkFollower')).getAttribute('checked')).toBeTruthy();
});
</file>
</example>
@@ -22465,7 +23101,7 @@ var htmlAnchorDirective = valueFn({
<example name="ng-readonly">
<file name="index.html">
<label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
- <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
+ <input type="text" ng-readonly="checked" value="I'm AngularJS" aria-label="Readonly field" />
</file>
<file name="protractor.js" type="protractor">
it('should toggle readonly attr', function() {
@@ -22540,15 +23176,20 @@ var htmlAnchorDirective = valueFn({
*
* ## A note about browser compatibility
*
- * Edge, Firefox, and Internet Explorer do not support the `details` element, it is
+ * Internet Explorer and Edge do not support the `details` element, it is
* recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
*
* @example
<example name="ng-open">
<file name="index.html">
- <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
+ <label>Toggle details: <input type="checkbox" ng-model="open"></label><br/>
<details id="details" ng-open="open">
- <summary>Show/Hide me</summary>
+ <summary>List</summary>
+ <ul>
+ <li>Apple</li>
+ <li>Orange</li>
+ <li>Durian</li>
+ </ul>
</details>
</file>
<file name="protractor.js" type="protractor">
@@ -22688,17 +23329,23 @@ function nullFormRenameControl(control, name) {
* @property {boolean} $dirty True if user has already interacted with the form.
* @property {boolean} $valid True if all of the containing forms and controls are valid.
* @property {boolean} $invalid True if at least one containing control or form is invalid.
- * @property {boolean} $pending True if at least one containing control or form is pending.
* @property {boolean} $submitted True if user has submitted the form even if its invalid.
*
- * @property {Object} $error Is an object hash, containing references to controls or
- * forms with failing validators, where:
+ * @property {Object} $pending An object hash, containing references to controls or forms with
+ * pending validators, where:
+ *
+ * - keys are validations tokens (error names).
+ * - values are arrays of controls or forms that have a pending validator for the given error name.
+ *
+ * See {@link form.FormController#$error $error} for a list of built-in validation tokens.
+ *
+ * @property {Object} $error An object hash, containing references to controls or forms with failing
+ * validators, where:
*
* - keys are validation tokens (error names),
- * - values are arrays of controls or forms that have a failing validator for given error name.
+ * - values are arrays of controls or forms that have a failing validator for the given error name.
*
* Built-in validation tokens:
- *
* - `email`
* - `max`
* - `maxlength`
@@ -22944,9 +23591,24 @@ FormController.prototype = {
* @name form.FormController#$setValidity
*
* @description
- * Sets the validity of a form control.
- *
- * This method will also propagate to parent forms.
+ * Change the validity state of the form, and notify the parent form (if any).
+ *
+ * Application developers will rarely need to call this method directly. It is used internally, by
+ * {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a
+ * control's validity state to the parent `FormController`.
+ *
+ * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be
+ * assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for
+ * unfulfilled `$asyncValidators`), so that it is available for data-binding. The
+ * `validationErrorKey` should be in camelCase and will get converted into dash-case for
+ * class name. Example: `myError` will result in `ng-valid-my-error` and
+ * `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`.
+ * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending
+ * (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
+ * Skipped is used by AngularJS when validators do not run because of parse errors and when
+ * `$asyncValidators` do not run because any of the `$validators` failed.
+ * @param {NgModelController | FormController} controller - The controller whose validity state is
+ * triggering the change.
*/
addSetValidityMethod({
clazz: FormController,
@@ -23004,15 +23666,15 @@ addSetValidityMethod({
* If the `name` attribute is specified, the form controller is published onto the current scope under
* this name.
*
- * # Alias: {@link ng.directive:ngForm `ngForm`}
+ * ## Alias: {@link ng.directive:ngForm `ngForm`}
*
- * In Angular, forms can be nested. This means that the outer form is valid when all of the child
+ * In AngularJS, forms can be nested. This means that the outer form is valid when all of the child
* forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
- * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
+ * AngularJS provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
* `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
* of controls needs to be determined.
*
- * # CSS classes
+ * ## CSS classes
* - `ng-valid` is set if the form is valid.
* - `ng-invalid` is set if the form is invalid.
* - `ng-pending` is set if the form is pending.
@@ -23023,14 +23685,14 @@ addSetValidityMethod({
* Keep in mind that ngAnimate can detect each of these classes when added and removed.
*
*
- * # Submitting a form and preventing the default action
+ * ## Submitting a form and preventing the default action
*
- * Since the role of forms in client-side Angular applications is different than in classical
+ * Since the role of forms in client-side AngularJS applications is different than in classical
* roundtrip apps, it is desirable for the browser not to translate the form submission into a full
* page reload that sends the data to the server. Instead some javascript logic should be triggered
* to handle the form submission in an application-specific way.
*
- * For this reason, Angular prevents the default action (form submission to the server) unless the
+ * For this reason, AngularJS prevents the default action (form submission to the server) unless the
* `<form>` element has an `action` attribute specified.
*
* You can use one of the following two ways to specify what javascript method should be called when
@@ -23056,8 +23718,7 @@ addSetValidityMethod({
* submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
* to have access to the updated model.
*
- * ## Animation Hooks
- *
+ * @animations
* Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
* These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
* other validations that are performed within the form. Animations in ngForm are similar to how
@@ -23369,10 +24030,10 @@ var inputType = {
* @name input[text]
*
* @description
- * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
+ * Standard HTML text input with AngularJS data binding, inherited by most of the `input` elements.
*
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Adds `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
@@ -23387,7 +24048,7 @@ var inputType = {
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -23395,9 +24056,9 @@ var inputType = {
* **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
* start at the index of the last search's match, thus not taking the whole input value into
* account.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
- * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
+ * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input.
* This parameter is ignored for input[type=password] controls, which will never trim the
* input.
*
@@ -23471,13 +24132,13 @@ var inputType = {
* modern browsers do not yet support this input type, it is important to provide cues to users on the
* expected input format via a placeholder or label.
*
- * The model must always be a Date object, otherwise Angular will throw an error.
+ * The model must always be a Date object, otherwise AngularJS will throw an error.
* Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
*
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
* valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
@@ -23495,7 +24156,7 @@ var inputType = {
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -23573,13 +24234,13 @@ var inputType = {
* the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
* local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
*
- * The model must always be a Date object, otherwise Angular will throw an error.
+ * The model must always be a Date object, otherwise AngularJS will throw an error.
* Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
*
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
* This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
@@ -23597,7 +24258,7 @@ var inputType = {
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -23676,13 +24337,13 @@ var inputType = {
* local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
* Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
*
- * The model must always be a Date object, otherwise Angular will throw an error.
+ * The model must always be a Date object, otherwise AngularJS will throw an error.
* Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
*
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
* This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
@@ -23700,7 +24361,7 @@ var inputType = {
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -23778,13 +24439,13 @@ var inputType = {
* the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
* week format (yyyy-W##), for example: `2013-W02`.
*
- * The model must always be a Date object, otherwise Angular will throw an error.
+ * The model must always be a Date object, otherwise AngularJS will throw an error.
* Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
*
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
* This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
@@ -23802,7 +24463,7 @@ var inputType = {
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -23880,7 +24541,7 @@ var inputType = {
* the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
* month format (yyyy-MM), for example: `2009-01`.
*
- * The model must always be a Date object, otherwise Angular will throw an error.
+ * The model must always be a Date object, otherwise AngularJS will throw an error.
* Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
* If the model is not set to the first of the month, the next view to model update will set it
* to the first of the month.
@@ -23888,7 +24549,7 @@ var inputType = {
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
* This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
@@ -23907,7 +24568,7 @@ var inputType = {
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
* `required` when you want to data-bind to the `required` attribute.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -23985,7 +24646,7 @@ var inputType = {
* error if not a valid number.
*
* <div class="alert alert-warning">
- * The model must always be of type `number` otherwise Angular will throw an error.
+ * The model must always be of type `number` otherwise AngularJS will throw an error.
* Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
* error docs for more information and an example of how to convert your model if necessary.
* </div>
@@ -24000,7 +24661,7 @@ var inputType = {
* will also be an empty string.
*
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
* Can be interpolated.
@@ -24027,7 +24688,7 @@ var inputType = {
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -24035,7 +24696,7 @@ var inputType = {
* **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
* start at the index of the last search's match, thus not taking the whole input value into
* account.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -24110,7 +24771,7 @@ var inputType = {
* the built-in validators (see the {@link guide/forms Forms guide})
* </div>
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
@@ -24125,7 +24786,7 @@ var inputType = {
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -24133,7 +24794,7 @@ var inputType = {
* **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
* start at the index of the last search's match, thus not taking the whole input value into
* account.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -24209,7 +24870,7 @@ var inputType = {
* use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
* </div>
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
@@ -24224,7 +24885,7 @@ var inputType = {
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -24232,7 +24893,7 @@ var inputType = {
* **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
* start at the index of the last search's match, thus not taking the whole input value into
* account.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -24300,14 +24961,14 @@ var inputType = {
* @description
* HTML radio button.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string} value The value to which the `ngModel` expression should be set when selected.
* Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
* too. Use `ngValue` if you need complex models (`number`, `object`, ...).
* @param {string=} name Property name of the form under which the control is published.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
- * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
+ * @param {string} ngValue AngularJS expression to which `ngModel` will be be set when the radio
* is selected. Should be used instead of the `value` attribute if you need
* a non-string `ngModel` (`boolean`, `array`, ...).
*
@@ -24385,34 +25046,34 @@ var inputType = {
* See the [HTML Spec on input[type=range]](https://www.w3.org/TR/html5/forms.html#range-state-(type=range))
* for more info.
*
- * This has the following consequences for Angular:
+ * This has the following consequences for AngularJS:
*
* Since the element value should always reflect the current model value, a range input
* will set the bound ngModel expression to the value that the browser has set for the
* input element. For example, in the following input `<input type="range" ng-model="model.value">`,
* if the application sets `model.value = null`, the browser will set the input to `'50'`.
- * Angular will then set the model to `50`, to prevent input and model value being out of sync.
+ * AngularJS will then set the model to `50`, to prevent input and model value being out of sync.
*
* That means the model for range will immediately be set to `50` after `ngModel` has been
* initialized. It also means a range input can never have the required error.
*
* This does not only affect changes to the model value, but also to the values of the `min`,
* `max`, and `step` attributes. When these change in a way that will cause the browser to modify
- * the input value, Angular will also update the model value.
+ * the input value, AngularJS will also update the model value.
*
* Automatic value adjustment also means that a range input element can never have the `required`,
* `min`, or `max` errors.
*
* However, `step` is currently only fully implemented by Firefox. Other browsers have problems
* when the step value changes dynamically - they do not adjust the element value correctly, but
- * instead may set the `stepMismatch` error. If that's the case, the Angular will set the `step`
+ * instead may set the `stepMismatch` error. If that's the case, the AngularJS will set the `step`
* error on the input, and set the model to `undefined`.
*
* Note that `input[range]` is not compatible with`ngMax`, `ngMin`, and `ngStep`, because they do
* not set the `min` and `max` attributes, which means that the browser won't automatically adjust
* the input value based on their values, and will always assume min = 0, max = 100, and step = 1.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation to ensure that the value entered is greater
* than `min`. Can be interpolated.
@@ -24420,7 +25081,7 @@ var inputType = {
* Can be interpolated.
* @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step`
* Can be interpolated.
- * @param {string=} ngChange Angular expression to be executed when the ngModel value changes due
+ * @param {string=} ngChange AngularJS expression to be executed when the ngModel value changes due
* to user interaction with the input element.
* @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the
* element. **Note** : `ngChecked` should not be used alongside `ngModel`.
@@ -24487,11 +25148,11 @@ var inputType = {
* @description
* HTML checkbox.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {expression=} ngTrueValue The value to which the expression should be set when selected.
* @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
*
* @example
@@ -24626,9 +25287,9 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
deferListener(event, this, this.value);
});
- // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
+ // if user modifies input value using context menu in IE, we need "paste", "cut" and "drop" events to catch it
if ($sniffer.hasEvent('paste')) {
- element.on('paste cut', deferListener);
+ element.on('paste cut drop', deferListener);
}
}
@@ -25203,11 +25864,11 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* @restrict E
*
* @description
- * HTML textarea element control with angular data-binding. The data-binding and validation
+ * HTML textarea element control with AngularJS data-binding. The data-binding and validation
* properties of this element are exactly the same as those of the
* {@link ng.directive:input input element}.
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
@@ -25219,7 +25880,7 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -25227,15 +25888,15 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
* start at the index of the last search's match, thus not taking the whole input value into
* account.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
- * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
+ * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input.
*
* @knownIssue
*
* When specifying the `placeholder` attribute of `<textarea>`, Internet Explorer will temporarily
* insert the placeholder value as the textarea's content. If the placeholder value contains
- * interpolation (`{{ ... }}`), an error will be logged in the console when Angular tries to update
+ * interpolation (`{{ ... }}`), an error will be logged in the console when AngularJS tries to update
* the value of the by-then-removed text node. This doesn't affect the functionality of the
* textarea, but can be undesirable.
*
@@ -25262,7 +25923,7 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
* </div>
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {boolean=} ngRequired Sets `required` attribute if set to true
@@ -25272,7 +25933,7 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
- * value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
+ * value does not match a RegExp found by evaluating the AngularJS expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -25280,9 +25941,9 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
* start at the index of the last search's match, thus not taking the whole input value into
* account.
- * @param {string=} ngChange Angular expression to be executed when input changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when input changes due to user
* interaction with the input element.
- * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
+ * @param {boolean=} [ngTrim=true] If set to false AngularJS will not automatically trim the input.
* This parameter is ignored for input[type=password] controls, which will never trim the
* input.
*
@@ -25406,6 +26067,8 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
/**
* @ngdoc directive
* @name ngValue
+ * @restrict A
+ * @priority 100
*
* @description
* Binds the given expression to the value of the element.
@@ -25418,8 +26081,8 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
* It can also be used to achieve one-way binding of a given expression to an input element
* such as an `input[text]` or a `textarea`, when that element does not use ngModel.
*
- * @element input
- * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
+ * @element ANY
+ * @param {string=} ngValue AngularJS expression, whose value will be bound to the `value` attribute
* and `value` property of the element.
*
* @example
@@ -25499,7 +26162,7 @@ var ngValueDirective = function() {
* @restrict AC
*
* @description
- * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
+ * The `ngBind` attribute tells AngularJS to replace the text content of the specified HTML element
* with the value of a given expression, and to update the text content when the value of that
* expression changes.
*
@@ -25507,7 +26170,7 @@ var ngValueDirective = function() {
* `{{ expression }}` which is similar but less verbose.
*
* It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
- * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
+ * displayed by the browser in its raw state before AngularJS compiles it. Since `ngBind` is an
* element attribute, it makes the bindings invisible to the user while the page is loading.
*
* An alternative solution to this problem would be using the
@@ -25637,7 +26300,7 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
* Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
* the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
* To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
- * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
+ * ngSanitize} in your module's dependencies (not in core AngularJS). In order to use {@link ngSanitize}
* in your module's dependencies, you need to include "angular-sanitize.js" in your application.
*
* You may also bypass sanitization for values you know are safe. To do so, bind to
@@ -25703,6 +26366,7 @@ var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse,
/**
* @ngdoc directive
* @name ngChange
+ * @restrict A
*
* @description
* Evaluate the given expression when the user changes the input.
@@ -25721,7 +26385,7 @@ var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse,
*
* Note, this directive requires `ngModel` to be present.
*
- * @element input
+ * @element ANY
* @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
* in input value.
*
@@ -25963,6 +26627,7 @@ function classDirective(name, selector) {
* @ngdoc directive
* @name ngClass
* @restrict AC
+ * @element ANY
*
* @description
* The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
@@ -25998,14 +26663,21 @@ function classDirective(name, selector) {
* | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
* | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
*
- * @element ANY
+ * ### ngClass and pre-existing CSS3 Transitions/Animations
+ The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
+ Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
+ any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
+ to view the step by step details of {@link $animate#addClass $animate.addClass} and
+ {@link $animate#removeClass $animate.removeClass}.
+ *
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
* of the evaluation can be a string representing space delimited class
* names, an array, or a map of class names to boolean values. In the case of a map, the
* names of the properties whose values are truthy will be added as css classes to the
* element.
*
- * @example Example that demonstrates basic bindings via ngClass directive.
+ * @example
+ * ### Basic
<example name="ng-class">
<file name="index.html">
<p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
@@ -26095,7 +26767,8 @@ function classDirective(name, selector) {
</file>
</example>
- ## Animations
+ @example
+ ### Animations
The example below demonstrates how to perform animations using ngClass.
@@ -26133,14 +26806,6 @@ function classDirective(name, selector) {
});
</file>
</example>
-
-
- ## ngClass and pre-existing CSS3 Transitions/Animations
- The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
- Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
- any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
- to view the step by step details of {@link $animate#addClass $animate.addClass} and
- {@link $animate#removeClass $animate.removeClass}.
*/
var ngClassDirective = classDirective('', true);
@@ -26246,7 +26911,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
* @restrict AC
*
* @description
- * The `ngCloak` directive is used to prevent the Angular html template from being briefly
+ * The `ngCloak` directive is used to prevent the AngularJS html template from being briefly
* displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
* directive to avoid the undesirable flicker effect caused by the html template display.
*
@@ -26265,7 +26930,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
* ```
*
* When this css rule is loaded by the browser, all html elements (including their children) that
- * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
+ * are tagged with the `ngCloak` directive are hidden. When AngularJS encounters this directive
* during the compilation of the template it deletes the `ngCloak` element attribute, making
* the compiled element visible.
*
@@ -26337,7 +27002,7 @@ var ngCloakDirective = ngDirective({
* @example
* Here is a simple form for editing user contact information. Adding, removing, clearing, and
* greeting are methods declared on the controller (see source tab). These methods can
- * easily be called from the angular markup. Any changes to the data are automatically reflected
+ * easily be called from the AngularJS markup. Any changes to the data are automatically reflected
* in the View without the need for a manual update.
*
* Two different declaration styles are included below:
@@ -26347,7 +27012,7 @@ var ngCloakDirective = ngDirective({
* * one injects `$scope` into the controller:
* `ng-controller="SettingsController2"`
*
- * The second option is more common in the Angular community, and is generally used in boilerplates
+ * The second option is more common in the AngularJS community, and is generally used in boilerplates
* and in this guide. However, there are advantages to binding properties directly to the controller
* and avoiding scope.
*
@@ -26544,31 +27209,31 @@ var ngControllerDirective = [function() {
* @element ANY
* @description
*
- * Angular has some features that can conflict with certain restrictions that are applied when using
+ * AngularJS has some features that can conflict with certain restrictions that are applied when using
* [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
*
- * If you intend to implement CSP with these rules then you must tell Angular not to use these
+ * If you intend to implement CSP with these rules then you must tell AngularJS not to use these
* features.
*
* This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
*
*
- * The following default rules in CSP affect Angular:
+ * The following default rules in CSP affect AngularJS:
*
* * The use of `eval()`, `Function(string)` and similar functions to dynamically create and execute
- * code from strings is forbidden. Angular makes use of this in the {@link $parse} service to
- * provide a 30% increase in the speed of evaluating Angular expressions. (This CSP rule can be
+ * code from strings is forbidden. AngularJS makes use of this in the {@link $parse} service to
+ * provide a 30% increase in the speed of evaluating AngularJS expressions. (This CSP rule can be
* disabled with the CSP keyword `unsafe-eval`, but it is generally not recommended as it would
* weaken the protections offered by CSP.)
*
* * The use of inline resources, such as inline `<script>` and `<style>` elements, are forbidden.
- * This prevents apps from injecting custom styles directly into the document. Angular makes use of
+ * This prevents apps from injecting custom styles directly into the document. AngularJS makes use of
* this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). To make these
* directives work when a CSP rule is blocking inline styles, you must link to the `angular-csp.css`
* in your HTML manually. (This CSP rule can be disabled with the CSP keyword `unsafe-inline`, but
* it is generally not recommended as it would weaken the protections offered by CSP.)
*
- * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking dynamic code
+ * If you do not provide `ngCsp` then AngularJS tries to autodetect if CSP is blocking dynamic code
* creation from strings (e.g., `unsafe-eval` not specified in CSP header) and automatically
* deactivates this feature in the {@link $parse} service. This autodetection, however, triggers a
* CSP error to be logged in the console:
@@ -26585,35 +27250,36 @@ var ngControllerDirective = [function() {
*
* *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
*
- * You can specify which of the CSP related Angular features should be deactivated by providing
+ * You can specify which of the CSP related AngularJS features should be deactivated by providing
* a value for the `ng-csp` attribute. The options are as follows:
*
- * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
+ * * no-inline-style: this stops AngularJS from injecting CSS styles into the DOM
*
- * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
+ * * no-unsafe-eval: this stops AngularJS from optimizing $parse with unsafe eval of strings
*
* You can use these values in the following combinations:
*
*
- * * No declaration means that Angular will assume that you can do inline styles, but it will do
+ * * No declaration means that AngularJS will assume that you can do inline styles, but it will do
* a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous
- * versions of Angular.
+ * versions of AngularJS.
*
- * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
+ * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell AngularJS to deactivate both inline
* styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous
- * versions of Angular.
+ * versions of AngularJS.
*
- * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can
+ * * Specifying only `no-unsafe-eval` tells AngularJS that we must not use eval, but that we can
* inject inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
*
- * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
+ * * Specifying only `no-inline-style` tells AngularJS that we must not inject styles, but that we can
* run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
*
- * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
+ * * Specifying both `no-unsafe-eval` and `no-inline-style` tells AngularJS that we must not inject
* styles nor use eval, which is the same as an empty: ng-csp.
* E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
*
* @example
+ *
* This example shows how to apply the `ngCsp` directive to the `html` tag.
```html
<!doctype html>
@@ -26622,122 +27288,122 @@ var ngControllerDirective = [function() {
...
</html>
```
- * @example
- <!-- Note: the `.csp` suffix in the example name triggers CSP mode in our http server! -->
- <example name="example.csp" module="cspExample" ng-csp="true">
- <file name="index.html">
- <div ng-controller="MainController as ctrl">
- <div>
- <button ng-click="ctrl.inc()" id="inc">Increment</button>
- <span id="counter">
- {{ctrl.counter}}
- </span>
- </div>
-
- <div>
- <button ng-click="ctrl.evil()" id="evil">Evil</button>
- <span id="evilError">
- {{ctrl.evilError}}
- </span>
- </div>
- </div>
- </file>
- <file name="script.js">
- angular.module('cspExample', [])
- .controller('MainController', function MainController() {
- this.counter = 0;
- this.inc = function() {
- this.counter++;
- };
- this.evil = function() {
- try {
- eval('1+2'); // eslint-disable-line no-eval
- } catch (e) {
- this.evilError = e.message;
- }
- };
- });
- </file>
- <file name="protractor.js" type="protractor">
- var util, webdriver;
-
- var incBtn = element(by.id('inc'));
- var counter = element(by.id('counter'));
- var evilBtn = element(by.id('evil'));
- var evilError = element(by.id('evilError'));
- function getAndClearSevereErrors() {
- return browser.manage().logs().get('browser').then(function(browserLog) {
- return browserLog.filter(function(logEntry) {
- return logEntry.level.value > webdriver.logging.Level.WARNING.value;
- });
- });
- }
-
- function clearErrors() {
- getAndClearSevereErrors();
- }
+ <!-- Note: the `.csp` suffix in the example name triggers CSP mode in our http server! -->
+ <example name="example.csp" module="cspExample" ng-csp="true">
+ <file name="index.html">
+ <div ng-controller="MainController as ctrl">
+ <div>
+ <button ng-click="ctrl.inc()" id="inc">Increment</button>
+ <span id="counter">
+ {{ctrl.counter}}
+ </span>
+ </div>
- function expectNoErrors() {
- getAndClearSevereErrors().then(function(filteredLog) {
- expect(filteredLog.length).toEqual(0);
- if (filteredLog.length) {
- console.log('browser console errors: ' + util.inspect(filteredLog));
+ <div>
+ <button ng-click="ctrl.evil()" id="evil">Evil</button>
+ <span id="evilError">
+ {{ctrl.evilError}}
+ </span>
+ </div>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('cspExample', [])
+ .controller('MainController', function MainController() {
+ this.counter = 0;
+ this.inc = function() {
+ this.counter++;
+ };
+ this.evil = function() {
+ try {
+ eval('1+2'); // eslint-disable-line no-eval
+ } catch (e) {
+ this.evilError = e.message;
}
- });
- }
+ };
+ });
+ </file>
+ <file name="protractor.js" type="protractor">
+ var util, webdriver;
- function expectError(regex) {
- getAndClearSevereErrors().then(function(filteredLog) {
- var found = false;
- filteredLog.forEach(function(log) {
- if (log.message.match(regex)) {
- found = true;
- }
- });
- if (!found) {
- throw new Error('expected an error that matches ' + regex);
- }
- });
- }
+ var incBtn = element(by.id('inc'));
+ var counter = element(by.id('counter'));
+ var evilBtn = element(by.id('evil'));
+ var evilError = element(by.id('evilError'));
- beforeEach(function() {
- util = require('util');
- webdriver = require('selenium-webdriver');
+ function getAndClearSevereErrors() {
+ return browser.manage().logs().get('browser').then(function(browserLog) {
+ return browserLog.filter(function(logEntry) {
+ return logEntry.level.value > webdriver.logging.Level.WARNING.value;
});
+ });
+ }
- // For now, we only test on Chrome,
- // as Safari does not load the page with Protractor's injected scripts,
- // and Firefox webdriver always disables content security policy (#6358)
- if (browser.params.browser !== 'chrome') {
- return;
+ function clearErrors() {
+ getAndClearSevereErrors();
+ }
+
+ function expectNoErrors() {
+ getAndClearSevereErrors().then(function(filteredLog) {
+ expect(filteredLog.length).toEqual(0);
+ if (filteredLog.length) {
+ console.log('browser console errors: ' + util.inspect(filteredLog));
}
+ });
+ }
- it('should not report errors when the page is loaded', function() {
- // clear errors so we are not dependent on previous tests
- clearErrors();
- // Need to reload the page as the page is already loaded when
- // we come here
- browser.driver.getCurrentUrl().then(function(url) {
- browser.get(url);
- });
- expectNoErrors();
+ function expectError(regex) {
+ getAndClearSevereErrors().then(function(filteredLog) {
+ var found = false;
+ filteredLog.forEach(function(log) {
+ if (log.message.match(regex)) {
+ found = true;
+ }
});
+ if (!found) {
+ throw new Error('expected an error that matches ' + regex);
+ }
+ });
+ }
- it('should evaluate expressions', function() {
- expect(counter.getText()).toEqual('0');
- incBtn.click();
- expect(counter.getText()).toEqual('1');
- expectNoErrors();
- });
+ beforeEach(function() {
+ util = require('util');
+ webdriver = require('selenium-webdriver');
+ });
- it('should throw and report an error when using "eval"', function() {
- evilBtn.click();
- expect(evilError.getText()).toMatch(/Content Security Policy/);
- expectError(/Content Security Policy/);
- });
- </file>
- </example>
+ // For now, we only test on Chrome,
+ // as Safari does not load the page with Protractor's injected scripts,
+ // and Firefox webdriver always disables content security policy (#6358)
+ if (browser.params.browser !== 'chrome') {
+ return;
+ }
+
+ it('should not report errors when the page is loaded', function() {
+ // clear errors so we are not dependent on previous tests
+ clearErrors();
+ // Need to reload the page as the page is already loaded when
+ // we come here
+ browser.driver.getCurrentUrl().then(function(url) {
+ browser.get(url);
+ });
+ expectNoErrors();
+ });
+
+ it('should evaluate expressions', function() {
+ expect(counter.getText()).toEqual('0');
+ incBtn.click();
+ expect(counter.getText()).toEqual('1');
+ expectNoErrors();
+ });
+
+ it('should throw and report an error when using "eval"', function() {
+ evilBtn.click();
+ expect(evilError.getText()).toMatch(/Content Security Policy/);
+ expectError(/Content Security Policy/);
+ });
+ </file>
+ </example>
*/
// `ngCsp` is not implemented as a proper directive any more, because we need it be processed while
@@ -26747,13 +27413,14 @@ var ngControllerDirective = [function() {
/**
* @ngdoc directive
* @name ngClick
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* The ngClick directive allows you to specify custom behavior when
* an element is clicked.
*
- * @element ANY
- * @priority 0
* @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
* click. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -26778,7 +27445,7 @@ var ngControllerDirective = [function() {
*/
/*
* A collection of directives that allows creation of custom event handlers that are defined as
- * angular expressions and are compiled and executed within the current scope.
+ * AngularJS expressions and are compiled and executed within the current scope.
*/
var ngEventDirectives = {};
@@ -26823,12 +27490,13 @@ forEach(
/**
* @ngdoc directive
* @name ngDblclick
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
* a dblclick. (The Event object is available as `$event`)
*
@@ -26847,12 +27515,13 @@ forEach(
/**
* @ngdoc directive
* @name ngMousedown
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* The ngMousedown directive allows you to specify custom behavior on mousedown event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
* mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -26871,12 +27540,13 @@ forEach(
/**
* @ngdoc directive
* @name ngMouseup
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* Specify custom behavior on mouseup event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
* mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -26894,12 +27564,13 @@ forEach(
/**
* @ngdoc directive
* @name ngMouseover
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* Specify custom behavior on mouseover event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
* mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -26918,12 +27589,13 @@ forEach(
/**
* @ngdoc directive
* @name ngMouseenter
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* Specify custom behavior on mouseenter event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
* mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -26942,12 +27614,13 @@ forEach(
/**
* @ngdoc directive
* @name ngMouseleave
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* Specify custom behavior on mouseleave event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
* mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -26966,12 +27639,13 @@ forEach(
/**
* @ngdoc directive
* @name ngMousemove
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* Specify custom behavior on mousemove event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
* mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -26990,12 +27664,13 @@ forEach(
/**
* @ngdoc directive
* @name ngKeydown
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* Specify custom behavior on keydown event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
@@ -27012,12 +27687,13 @@ forEach(
/**
* @ngdoc directive
* @name ngKeyup
+ * @restrict A
+ * @element ANY
+ * @priority 0
*
* @description
* Specify custom behavior on keyup event.
*
- * @element ANY
- * @priority 0
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
@@ -27039,11 +27715,12 @@ forEach(
/**
* @ngdoc directive
* @name ngKeypress
+ * @restrict A
+ * @element ANY
*
* @description
* Specify custom behavior on keypress event.
*
- * @element ANY
* @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
* keypress. ({@link guide/expression#-event- Event object is available as `$event`}
* and can be interrogated for keyCode, altKey, etc.)
@@ -27061,9 +27738,12 @@ forEach(
/**
* @ngdoc directive
* @name ngSubmit
+ * @restrict A
+ * @element form
+ * @priority 0
*
* @description
- * Enables binding angular expressions to onsubmit events.
+ * Enables binding AngularJS expressions to onsubmit events.
*
* Additionally it prevents the default action (which for form means sending the request to the
* server and reloading the current page), but only if the form does not contain `action`,
@@ -27076,8 +27756,6 @@ forEach(
* for a detailed discussion of when `ngSubmit` may be triggered.
* </div>
*
- * @element form
- * @priority 0
* @param {expression} ngSubmit {@link guide/expression Expression} to eval.
* ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -27124,6 +27802,9 @@ forEach(
/**
* @ngdoc directive
* @name ngFocus
+ * @restrict A
+ * @element window, input, select, textarea, a
+ * @priority 0
*
* @description
* Specify custom behavior on focus event.
@@ -27132,8 +27813,6 @@ forEach(
* AngularJS executes the expression using `scope.$evalAsync` if the event is fired
* during an `$apply` to ensure a consistent state.
*
- * @element window, input, select, textarea, a
- * @priority 0
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
* focus. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -27144,6 +27823,9 @@ forEach(
/**
* @ngdoc directive
* @name ngBlur
+ * @restrict A
+ * @element window, input, select, textarea, a
+ * @priority 0
*
* @description
* Specify custom behavior on blur event.
@@ -27156,8 +27838,6 @@ forEach(
* AngularJS executes the expression using `scope.$evalAsync` if the event is fired
* during an `$apply` to ensure a consistent state.
*
- * @element window, input, select, textarea, a
- * @priority 0
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
* blur. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -27168,12 +27848,13 @@ forEach(
/**
* @ngdoc directive
* @name ngCopy
+ * @restrict A
+ * @element window, input, select, textarea, a
+ * @priority 0
*
* @description
* Specify custom behavior on copy event.
*
- * @element window, input, select, textarea, a
- * @priority 0
* @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
* copy. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -27189,12 +27870,13 @@ forEach(
/**
* @ngdoc directive
* @name ngCut
+ * @restrict A
+ * @element window, input, select, textarea, a
+ * @priority 0
*
* @description
* Specify custom behavior on cut event.
*
- * @element window, input, select, textarea, a
- * @priority 0
* @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
* cut. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -27210,12 +27892,13 @@ forEach(
/**
* @ngdoc directive
* @name ngPaste
+ * @restrict A
+ * @element window, input, select, textarea, a
+ * @priority 0
*
* @description
* Specify custom behavior on paste event.
*
- * @element window, input, select, textarea, a
- * @priority 0
* @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
* paste. ({@link guide/expression#-event- Event object is available as `$event`})
*
@@ -27358,6 +28041,8 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
* @ngdoc directive
* @name ngInclude
* @restrict ECA
+ * @scope
+ * @priority -400
*
* @description
* Fetches, compiles and includes an external HTML fragment.
@@ -27366,7 +28051,7 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
* application document. This is done by calling {@link $sce#getTrustedResourceUrl
* $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
* you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
- * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
+ * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to AngularJS's {@link
* ng.$sce Strict Contextual Escaping}.
*
* In addition, the browser's
@@ -27384,10 +28069,7 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
*
* The enter and leave animation occur concurrently.
*
- * @scope
- * @priority 400
- *
- * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
+ * @param {string} ngInclude|src AngularJS expression evaluating to URL. If the source is a string constant,
* make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
* @param {string=} onload Expression to evaluate when a new partial is loaded.
* <div class="alert alert-warning">
@@ -27664,6 +28346,10 @@ var ngIncludeFillContentDirective = ['$compile',
* @ngdoc directive
* @name ngInit
* @restrict AC
+ * @priority 450
+ * @element ANY
+ *
+ * @param {expression} ngInit {@link guide/expression Expression} to eval.
*
* @description
* The `ngInit` directive allows you to evaluate an expression in the
@@ -27671,10 +28357,16 @@ var ngIncludeFillContentDirective = ['$compile',
*
* <div class="alert alert-danger">
* This directive can be abused to add unnecessary amounts of logic into your templates.
- * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
- * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
- * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
- * rather than `ngInit` to initialize values on a scope.
+ * There are only a few appropriate uses of `ngInit`:
+ * <ul>
+ * <li>aliasing special properties of {@link ng.directive:ngRepeat `ngRepeat`},
+ * as seen in the demo below.</li>
+ * <li>initializing data during development, or for examples, as seen throughout these docs.</li>
+ * <li>injecting data via server side scripting.</li>
+ * </ul>
+ *
+ * Besides these few cases, you should use {@link guide/component Components} or
+ * {@link guide/controller Controllers} rather than `ngInit` to initialize values on a scope.
* </div>
*
* <div class="alert alert-warning">
@@ -27685,11 +28377,6 @@ var ngIncludeFillContentDirective = ['$compile',
* </pre>
* </div>
*
- * @priority 450
- *
- * @element ANY
- * @param {expression} ngInit {@link guide/expression Expression} to eval.
- *
* @example
<example module="initExample" name="ng-init">
<file name="index.html">
@@ -27732,6 +28419,10 @@ var ngInitDirective = ngDirective({
/**
* @ngdoc directive
* @name ngList
+ * @restrict A
+ * @priority 100
+ *
+ * @param {string=} ngList optional delimiter that should be used to split the value.
*
* @description
* Text input that converts between a delimited string and an array of strings. The default
@@ -27747,7 +28438,8 @@ var ngInitDirective = ngDirective({
* when joining the list items back together) and whitespace around each list item is stripped
* before it is added to the model.
*
- * ### Example with Validation
+ * @example
+ * ### Validation
*
* <example name="ngList-directive" module="listExample">
* <file name="app.js">
@@ -27794,7 +28486,9 @@ var ngInitDirective = ngDirective({
* </file>
* </example>
*
- * ### Example - splitting on newline
+ * @example
+ * ### Splitting on newline
+ *
* <example name="ngList-directive-newlines">
* <file name="index.html">
* <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
@@ -27810,8 +28504,6 @@ var ngInitDirective = ngDirective({
* </file>
* </example>
*
- * @element input
- * @param {string=} ngList optional delimiter that should be used to split the value.
*/
var ngListDirective = function() {
return {
@@ -27882,36 +28574,60 @@ var ngModelMinErr = minErr('ngModel');
/**
* @ngdoc type
* @name ngModel.NgModelController
- *
* @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
* String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
* is set.
+ *
* @property {*} $modelValue The value in the model that the control is bound to.
+ *
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
- the control reads value from the DOM. The functions are called in array order, each passing
- its return value through to the next. The last return value is forwarded to the
- {@link ngModel.NgModelController#$validators `$validators`} collection.
+ * the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue
+ `$viewValue`} from the DOM, usually via user input.
+ See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation.
+ Note that the `$parsers` are not called when the bound ngModel expression changes programmatically.
+
+ The functions are called in array order, each passing
+ its return value through to the next. The last return value is forwarded to the
+ {@link ngModel.NgModelController#$validators `$validators`} collection.
-Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
-`$viewValue`}.
+ Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
+ `$viewValue`}.
-Returning `undefined` from a parser means a parse error occurred. In that case,
-no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
-will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
-is set to `true`. The parse error is stored in `ngModel.$error.parse`.
+ Returning `undefined` from a parser means a parse error occurred. In that case,
+ no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
+ will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
+ is set to `true`. The parse error is stored in `ngModel.$error.parse`.
+
+ This simple example shows a parser that would convert text input value to lowercase:
+ * ```js
+ * function parse(value) {
+ * if (value) {
+ * return value.toLowerCase();
+ * }
+ * }
+ * ngModelController.$parsers.push(parse);
+ * ```
*
* @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
- the model value changes. The functions are called in reverse array order, each passing the value through to the
- next. The last return value is used as the actual DOM value.
- Used to format / convert values for display in the control.
+ the bound ngModel expression changes programmatically. The `$formatters` are not called when the
+ value of the control is changed by user interaction.
+
+ Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue
+ `$modelValue`} for display in the control.
+
+ The functions are called in reverse array order, each passing the value through to the
+ next. The last return value is used as the actual DOM value.
+
+ This simple example shows a formatter that would convert the model value to uppercase:
+
* ```js
- * function formatter(value) {
+ * function format(value) {
* if (value) {
* return value.toUpperCase();
* }
* }
- * ngModel.$formatters.push(formatter);
+ * ngModel.$formatters.push(format);
* ```
*
* @property {Object.<string, function>} $validators A collection of validators that are applied
@@ -27958,8 +28674,10 @@ is set to `true`. The parse error is stored in `ngModel.$error.parse`.
* };
* ```
*
- * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
- * view value has changed. It is called with no arguments, and its return value is ignored.
+ * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever
+ * a change to {@link ngModel.NgModelController#$viewValue `$viewValue`} has caused a change
+ * to {@link ngModel.NgModelController#$modelValue `$modelValue`}.
+ * It is called with no arguments, and its return value is ignored.
* This can be used in place of additional $watches against the model value.
*
* @property {Object} $error An object hash with all failing validator ids as keys.
@@ -27981,7 +28699,7 @@ is set to `true`. The parse error is stored in `ngModel.$error.parse`.
* listening to DOM events.
* Such DOM related logic should be provided by other directives which make use of
* `NgModelController` for data-binding to control elements.
- * Angular provides this DOM logic for most {@link input `input`} elements.
+ * AngularJS provides this DOM logic for most {@link input `input`} elements.
* At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
* custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
*
@@ -28101,6 +28819,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
this.$name = $interpolate($attr.name || '', false)($scope);
this.$$parentForm = nullFormCtrl;
this.$options = defaultModelOptions;
+ this.$$updateEvents = '';
+ // Attach the correct context to the event handler function for updateOn
+ this.$$updateEventHandler = this.$$updateEventHandler.bind(this);
this.$$parsedNgModel = $parse($attr.ngModel);
this.$$parsedNgModelAssign = this.$$parsedNgModel.assign;
@@ -28111,7 +28832,9 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
this.$$currentValidationRunId = 0;
- this.$$scope = $scope;
+ // https://github.com/angular/angular.js/issues/15833
+ // Prevent `$$scope` from being iterated over by `copy` when NgModelController is deep watched
+ Object.defineProperty(this, '$$scope', {value: $scope});
this.$$attr = $attr;
this.$$element = $element;
this.$$animate = $animate;
@@ -28295,13 +29018,14 @@ NgModelController.prototype = {
* and reset the input to the last committed view value.
*
* It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
- * programmatically before these debounced/future events have resolved/occurred, because Angular's
+ * programmatically before these debounced/future events have resolved/occurred, because AngularJS's
* dirty checking mechanism is not able to tell whether the model has actually changed or not.
*
* The `$rollbackViewValue()` method should be called before programmatically changing the model of an
* input which may have such events pending. This is important in order to make sure that the
* input field will be updated with the new model value and any pending operations are cancelled.
*
+ * @example
* <example name="ng-model-cancel-update" module="cancel-update-example">
* <file name="app.js">
* angular.module('cancel-update-example', [])
@@ -28619,9 +29343,10 @@ NgModelController.prototype = {
*
* When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
* and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
- * value sent directly for processing, finally to be applied to `$modelValue` and then the
- * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
- * in the `$viewChangeListeners` list, are called.
+ * value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and
+ * `$asyncValidators` are called and the value is applied to `$modelValue`.
+ * Finally, the value is set to the **expression** specified in the `ng-model` attribute and
+ * all the registered change listeners, in the `$viewChangeListeners` list are called.
*
* In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
* and the `default` trigger is not listed, all those actions will remain pending until one of the
@@ -28702,11 +29427,184 @@ NgModelController.prototype = {
* See {@link ngModelOptions} for information about what options can be specified
* and how model option inheritance works.
*
+ * <div class="alert alert-warning">
+ * **Note:** this function only affects the options set on the `ngModelController`,
+ * and not the options on the {@link ngModelOptions} directive from which they might have been
+ * obtained initially.
+ * </div>
+ *
+ * <div class="alert alert-danger">
+ * **Note:** it is not possible to override the `getterSetter` option.
+ * </div>
+ *
* @param {Object} options a hash of settings to override the previous options
*
*/
$overrideModelOptions: function(options) {
this.$options = this.$options.createChild(options);
+ this.$$setUpdateOnEvents();
+ },
+
+ /**
+ * @ngdoc method
+ *
+ * @name ngModel.NgModelController#$processModelValue
+
+ * @description
+ *
+ * Runs the model -> view pipeline on the current
+ * {@link ngModel.NgModelController#$modelValue $modelValue}.
+ *
+ * The following actions are performed by this method:
+ *
+ * - the `$modelValue` is run through the {@link ngModel.NgModelController#$formatters $formatters}
+ * and the result is set to the {@link ngModel.NgModelController#$viewValue $viewValue}
+ * - the `ng-empty` or `ng-not-empty` class is set on the element
+ * - if the `$viewValue` has changed:
+ * - {@link ngModel.NgModelController#$render $render} is called on the control
+ * - the {@link ngModel.NgModelController#$validators $validators} are run and
+ * the validation status is set.
+ *
+ * This method is called by ngModel internally when the bound scope value changes.
+ * Application developers usually do not have to call this function themselves.
+ *
+ * This function can be used when the `$viewValue` or the rendered DOM value are not correctly
+ * formatted and the `$modelValue` must be run through the `$formatters` again.
+ *
+ * @example
+ * Consider a text input with an autocomplete list (for fruit), where the items are
+ * objects with a name and an id.
+ * A user enters `ap` and then selects `Apricot` from the list.
+ * Based on this, the autocomplete widget will call `$setViewValue({name: 'Apricot', id: 443})`,
+ * but the rendered value will still be `ap`.
+ * The widget can then call `ctrl.$processModelValue()` to run the model -> view
+ * pipeline again, which formats the object to the string `Apricot`,
+ * then updates the `$viewValue`, and finally renders it in the DOM.
+ *
+ * <example module="inputExample" name="ng-model-process">
+ <file name="index.html">
+ <div ng-controller="inputController" style="display: flex;">
+ <div style="margin-right: 30px;">
+ Search Fruit:
+ <basic-autocomplete items="items" on-select="selectedFruit = item"></basic-autocomplete>
+ </div>
+ <div>
+ Model:<br>
+ <pre>{{selectedFruit | json}}</pre>
+ </div>
+ </div>
+ </file>
+ <file name="app.js">
+ angular.module('inputExample', [])
+ .controller('inputController', function($scope) {
+ $scope.items = [
+ {name: 'Apricot', id: 443},
+ {name: 'Clementine', id: 972},
+ {name: 'Durian', id: 169},
+ {name: 'Jackfruit', id: 982},
+ {name: 'Strawberry', id: 863}
+ ];
+ })
+ .component('basicAutocomplete', {
+ bindings: {
+ items: '<',
+ onSelect: '&'
+ },
+ templateUrl: 'autocomplete.html',
+ controller: function($element, $scope) {
+ var that = this;
+ var ngModel;
+
+ that.$postLink = function() {
+ ngModel = $element.find('input').controller('ngModel');
+
+ ngModel.$formatters.push(function(value) {
+ return (value && value.name) || value;
+ });
+
+ ngModel.$parsers.push(function(value) {
+ var match = value;
+ for (var i = 0; i < that.items.length; i++) {
+ if (that.items[i].name === value) {
+ match = that.items[i];
+ break;
+ }
+ }
+
+ return match;
+ });
+ };
+
+ that.selectItem = function(item) {
+ ngModel.$setViewValue(item);
+ ngModel.$processModelValue();
+ that.onSelect({item: item});
+ };
+ }
+ });
+ </file>
+ <file name="autocomplete.html">
+ <div>
+ <input type="search" ng-model="$ctrl.searchTerm" />
+ <ul>
+ <li ng-repeat="item in $ctrl.items | filter:$ctrl.searchTerm">
+ <button ng-click="$ctrl.selectItem(item)">{{ item.name }}</button>
+ </li>
+ </ul>
+ </div>
+ </file>
+ * </example>
+ *
+ */
+ $processModelValue: function() {
+ var viewValue = this.$$format();
+
+ if (this.$viewValue !== viewValue) {
+ this.$$updateEmptyClasses(viewValue);
+ this.$viewValue = this.$$lastCommittedViewValue = viewValue;
+ this.$render();
+ // It is possible that model and view value have been updated during render
+ this.$$runValidators(this.$modelValue, this.$viewValue, noop);
+ }
+ },
+
+ /**
+ * This method is called internally to run the $formatters on the $modelValue
+ */
+ $$format: function() {
+ var formatters = this.$formatters,
+ idx = formatters.length;
+
+ var viewValue = this.$modelValue;
+ while (idx--) {
+ viewValue = formatters[idx](viewValue);
+ }
+
+ return viewValue;
+ },
+
+ /**
+ * This method is called internally when the bound scope value changes.
+ */
+ $$setModelValue: function(modelValue) {
+ this.$modelValue = this.$$rawModelValue = modelValue;
+ this.$$parserValid = undefined;
+ this.$processModelValue();
+ },
+
+ $$setUpdateOnEvents: function() {
+ if (this.$$updateEvents) {
+ this.$$element.off(this.$$updateEvents, this.$$updateEventHandler);
+ }
+
+ this.$$updateEvents = this.$options.getOption('updateOn');
+ if (this.$$updateEvents) {
+ this.$$element.on(this.$$updateEvents, this.$$updateEventHandler);
+ }
+ },
+
+ $$updateEventHandler: function(ev) {
+ this.$$debounceViewValueCommit(ev && ev.type);
}
};
@@ -28719,34 +29617,18 @@ function setupModelWatcher(ctrl) {
// -> scope value did not change since the last digest as
// ng-change executes in apply phase
// 4. view should be changed back to 'a'
- ctrl.$$scope.$watch(function ngModelWatch() {
- var modelValue = ctrl.$$ngModelGet(ctrl.$$scope);
+ ctrl.$$scope.$watch(function ngModelWatch(scope) {
+ var modelValue = ctrl.$$ngModelGet(scope);
// if scope model value and ngModel value are out of sync
- // TODO(perf): why not move this to the action fn?
+ // This cannot be moved to the action function, because it would not catch the
+ // case where the model is changed in the ngChange function or the model setter
if (modelValue !== ctrl.$modelValue &&
- // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
- // eslint-disable-next-line no-self-compare
- (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
+ // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
+ // eslint-disable-next-line no-self-compare
+ (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
) {
- ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
- ctrl.$$parserValid = undefined;
-
- var formatters = ctrl.$formatters,
- idx = formatters.length;
-
- var viewValue = modelValue;
- while (idx--) {
- viewValue = formatters[idx](viewValue);
- }
- if (ctrl.$viewValue !== viewValue) {
- ctrl.$$updateEmptyClasses(viewValue);
- ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
- ctrl.$render();
-
- // It is possible that model and view value have been updated during render
- ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
- }
+ ctrl.$$setModelValue(modelValue);
}
return modelValue;
@@ -28769,10 +29651,10 @@ function setupModelWatcher(ctrl) {
* (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
* The `validationErrorKey` should be in camelCase and will get converted into dash-case
* for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
- * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
+ * classes and can be bound to as `{{ someForm.someControl.$error.myError }}`.
* @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
* or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
- * Skipped is used by Angular when validators do not run because of parse errors and
+ * Skipped is used by AngularJS when validators do not run because of parse errors and
* when `$asyncValidators` do not run because any of the `$validators` failed.
*/
addSetValidityMethod({
@@ -28789,9 +29671,9 @@ addSetValidityMethod({
/**
* @ngdoc directive
* @name ngModel
- *
- * @element input
+ * @restrict A
* @priority 1
+ * @param {expression} ngModel assignable {@link guide/expression Expression} to bind to.
*
* @description
* The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
@@ -28833,7 +29715,7 @@ addSetValidityMethod({
* - {@link ng.directive:select select}
* - {@link ng.directive:textarea textarea}
*
- * # Complex Models (objects or collections)
+ * ## Complex Models (objects or collections)
*
* By default, `ngModel` watches the model by reference, not value. This is important to know when
* binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
@@ -28849,7 +29731,7 @@ addSetValidityMethod({
* first level of the object (or only changing the properties of an item in the collection if it's an array) will still
* not trigger a re-rendering of the model.
*
- * # CSS classes
+ * ## CSS classes
* The following CSS classes are added and removed on the associated input/select/textarea element
* depending on the validity of the model.
*
@@ -28868,8 +29750,7 @@ addSetValidityMethod({
*
* Keep in mind that ngAnimate can detect each of these classes when added and removed.
*
- * ## Animation Hooks
- *
+ * @animations
* Animations within models are triggered when any of the associated CSS classes are added and removed
* on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
* `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
@@ -28893,6 +29774,7 @@ addSetValidityMethod({
* </pre>
*
* @example
+ * ### Basic Usage
* <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample" name="ng-model">
<file name="index.html">
<script>
@@ -28922,7 +29804,8 @@ addSetValidityMethod({
</file>
* </example>
*
- * ## Binding to a getter/setter
+ * @example
+ * ### Binding to a getter/setter
*
* Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
* function that returns a representation of the model when called with zero arguments, and sets
@@ -28931,7 +29814,7 @@ addSetValidityMethod({
* to the view.
*
* <div class="alert alert-success">
- * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
+ * **Best Practice:** It's best to keep getters fast because AngularJS is likely to call them more
* frequently than other parts of your code.
* </div>
*
@@ -29013,11 +29896,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
},
post: function ngModelPostLink(scope, element, attr, ctrls) {
var modelCtrl = ctrls[0];
- if (modelCtrl.$options.getOption('updateOn')) {
- element.on(modelCtrl.$options.getOption('updateOn'), function(ev) {
- modelCtrl.$$debounceViewValueCommit(ev && ev.type);
- });
- }
+ modelCtrl.$$setUpdateOnEvents();
function setTouched() {
modelCtrl.$setTouched();
@@ -29130,6 +30009,8 @@ defaultModelOptions = new ModelOptions({
/**
* @ngdoc directive
* @name ngModelOptions
+ * @restrict A
+ * @priority 10
*
* @description
* This directive allows you to modify the behaviour of {@link ngModel} directives within your
@@ -29137,8 +30018,8 @@ defaultModelOptions = new ModelOptions({
* directives will use the options of their nearest `ngModelOptions` ancestor.
*
* The `ngModelOptions` settings are found by evaluating the value of the attribute directive as
- * an Angular expression. This expression should evaluate to an object, whose properties contain
- * the settings. For example: `<div "ng-model-options"="{ debounce: 100 }"`.
+ * an AngularJS expression. This expression should evaluate to an object, whose properties contain
+ * the settings. For example: `<div ng-model-options="{ debounce: 100 }"`.
*
* ## Inheriting Options
*
@@ -29213,6 +30094,8 @@ defaultModelOptions = new ModelOptions({
* `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
* to have access to the updated model.
*
+ * ### Overriding immediate updates
+ *
* The following example shows how to override immediate updates. Changes on the inputs within the
* form will update the model only when the control loses focus (blur event). If `escape` key is
* pressed while the input field is focused, the value is reset to the value in the current model.
@@ -29272,6 +30155,8 @@ defaultModelOptions = new ModelOptions({
* </file>
* </example>
*
+ * ### Debouncing updates
+ *
* The next example shows how to debounce model changes. Model will be updated only 1 sec after last change.
* If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
*
@@ -29296,6 +30181,7 @@ defaultModelOptions = new ModelOptions({
* </file>
* </example>
*
+ *
* ## Model updates and validation
*
* The default behaviour in `ngModel` is that the model value is set to `undefined` when the
@@ -29343,20 +30229,41 @@ defaultModelOptions = new ModelOptions({
* You can specify the timezone that date/time input directives expect by providing its name in the
* `timezone` property.
*
+ *
+ * ## Programmatically changing options
+ *
+ * The `ngModelOptions` expression is only evaluated once when the directive is linked; it is not
+ * watched for changes. However, it is possible to override the options on a single
+ * {@link ngModel.NgModelController} instance with
+ * {@link ngModel.NgModelController#$overrideModelOptions `NgModelController#$overrideModelOptions()`}.
+ *
+ *
* @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and
* and its descendents. Valid keys are:
* - `updateOn`: string specifying which event should the input be bound to. You can set several
* events using an space delimited list. There is a special event called `default` that
- * matches the default events belonging to the control.
+ * matches the default events belonging to the control. These are the events that are bound to
+ * the control, and when fired, update the `$viewValue` via `$setViewValue`.
+ *
+ * `ngModelOptions` considers every event that is not listed in `updateOn` a "default" event,
+ * since different control types use different default events.
+ *
+ * See also the section {@link ngModelOptions#triggering-and-debouncing-model-updates
+ * Triggering and debouncing model updates}.
+ *
* - `debounce`: integer value which contains the debounce model update value in milliseconds. A
* value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
* custom value for each event. For example:
* ```
* ng-model-options="{
- * updateOn: 'default blur',
+ * updateOn: 'default blur click',
* debounce: { 'default': 500, 'blur': 0 }
* }"
* ```
+ *
+ * "default" also applies to all events that are listed in `updateOn` but are not
+ * listed in `debounce`, i.e. "click" would also be debounced by 500 milliseconds.
+ *
* - `allowInvalid`: boolean value which indicates that the model can be set with values that did
* not validate correctly instead of the default behavior of setting the model to undefined.
* - `getterSetter`: boolean value which determines whether or not to treat functions bound to
@@ -29408,32 +30315,31 @@ function defaults(dst, src) {
* @name ngNonBindable
* @restrict AC
* @priority 1000
+ * @element ANY
*
* @description
- * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
- * DOM element. This is useful if the element contains what appears to be Angular directives and
- * bindings but which should be ignored by Angular. This could be the case if you have a site that
- * displays snippets of code, for instance.
- *
- * @element ANY
+ * The `ngNonBindable` directive tells AngularJS not to compile or bind the contents of the current
+ * DOM element, including directives on the element itself that have a lower priority than
+ * `ngNonBindable`. This is useful if the element contains what appears to be AngularJS directives
+ * and bindings but which should be ignored by AngularJS. This could be the case if you have a site
+ * that displays snippets of code, for instance.
*
* @example
* In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
* but the one wrapped in `ngNonBindable` is left alone.
*
- * @example
- <example name="ng-non-bindable">
- <file name="index.html">
- <div>Normal: {{1 + 2}}</div>
- <div ng-non-bindable>Ignored: {{1 + 2}}</div>
- </file>
- <file name="protractor.js" type="protractor">
- it('should check ng-non-bindable', function() {
- expect(element(by.binding('1 + 2')).getText()).toContain('3');
- expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
- });
- </file>
- </example>
+ <example name="ng-non-bindable">
+ <file name="index.html">
+ <div>Normal: {{1 + 2}}</div>
+ <div ng-non-bindable>Ignored: {{1 + 2}}</div>
+ </file>
+ <file name="protractor.js" type="protractor">
+ it('should check ng-non-bindable', function() {
+ expect(element(by.binding('1 + 2')).getText()).toContain('3');
+ expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
+ });
+ </file>
+ </example>
*/
var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
@@ -29548,13 +30454,8 @@ var ngOptionsMinErr = minErr('ngOptions');
* is not matched against any `<option>` and the `<select>` appears as having no selected value.
*
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
- * @param {string=} name Property name of the form under which the control is published.
- * @param {string=} required The control is considered valid only if value is entered.
- * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
- * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
- * `required` when you want to data-bind to the `required` attribute.
- * @param {comprehension_expression=} ngOptions in one of the following forms:
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
+ * @param {comprehension_expression} ngOptions in one of the following forms:
*
* * for array data sources:
* * `label` **`for`** `value` **`in`** `array`
@@ -29593,6 +30494,13 @@ var ngOptionsMinErr = minErr('ngOptions');
* used to identify the objects in the array. The `trackexpr` will most likely refer to the
* `value` variable (e.g. `value.propertyName`). With this the selection is preserved
* even when the options are recreated (e.g. reloaded from the server).
+ * @param {string=} name Property name of the form under which the control is published.
+ * @param {string=} required The control is considered valid only if value is entered.
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
+ * `required` when you want to data-bind to the `required` attribute.
+ * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
+ * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
*
* @example
<example module="selectExample" name="select">
@@ -29842,7 +30750,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
- // we can't just jqLite('<option>') since jqLite is not smart enough
+ // Support: IE 9 only
+ // We can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
var optionTemplate = window.document.createElement('option'),
optGroupTemplate = window.document.createElement('optgroup');
@@ -29863,6 +30772,9 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
}
+ // The empty option will be compiled and rendered before we first generate the options
+ selectElement.empty();
+
var providedEmptyOption = !!selectCtrl.emptyOption;
var unknownOption = jqLite(optionTemplate.cloneNode(false));
@@ -29884,12 +30796,15 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
if (!multiple) {
selectCtrl.writeValue = function writeNgOptionsValue(value) {
- var selectedOption = options.selectValueMap[selectElement.val()];
+ // The options might not be defined yet when ngModel tries to render
+ if (!options) return;
+
+ var selectedOption = selectElement[0].options[selectElement[0].selectedIndex];
var option = options.getOptionFromViewValue(value);
// Make sure to remove the selected attribute from the previously selected option
// Otherwise, screen readers might get confused
- if (selectedOption) selectedOption.element.removeAttribute('selected');
+ if (selectedOption) selectedOption.removeAttribute('selected');
if (option) {
// Don't update the option when it is already selected.
@@ -29899,7 +30814,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
if (selectElement[0].value !== option.selectValue) {
selectCtrl.removeUnknownOption();
- selectCtrl.unselectEmptyOption();
selectElement[0].value = option.selectValue;
option.element.selected = true;
@@ -29907,14 +30821,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
option.element.setAttribute('selected', 'selected');
} else {
-
- if (providedEmptyOption) {
- selectCtrl.selectEmptyOption();
- } else if (selectCtrl.unknownOption.parent().length) {
- selectCtrl.updateUnknownOption(value);
- } else {
- selectCtrl.renderUnknownOption(value);
- }
+ selectCtrl.selectUnknownOrEmptyOption(value);
}
};
@@ -29943,9 +30850,11 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
} else {
selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
+ // The options might not be defined yet when ngModel tries to render
+ if (!options) return;
+
// Only set `<option>.selected` if necessary, in order to prevent some browsers from
// scrolling to `<option>` elements that are outside the `<select>` element's viewport.
-
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
options.items.forEach(function(option) {
@@ -29987,13 +30896,11 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
if (providedEmptyOption) {
- // we need to remove it before calling selectElement.empty() because otherwise IE will
- // remove the label from the element. wtf?
- selectCtrl.emptyOption.remove();
-
// compile the element since there might be bindings in it
$compile(selectCtrl.emptyOption)(scope);
+ selectElement.prepend(selectCtrl.emptyOption);
+
if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
// This means the empty option has currently no actual DOM node, probably because
// it has been modified by a transclusion directive.
@@ -30011,8 +30918,12 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
ngModelCtrl.$render();
optionEl.on('$destroy', function() {
+ var needsRerender = selectCtrl.$isEmptyOptionSelected();
+
selectCtrl.hasEmptyOption = false;
selectCtrl.emptyOption = undefined;
+
+ if (needsRerender) ngModelCtrl.$render();
});
}
};
@@ -30025,12 +30936,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
}
- selectElement.empty();
-
- // We need to do this here to ensure that the options object is defined
- // when we first hit it in writeNgOptionsValue
- updateOptions();
-
// We will re-render the option elements if the option values or labels change
scope.$watchCollection(ngOptions.getWatchables, updateOptions);
@@ -30054,7 +30959,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
function updateOptionElement(option, element) {
option.element = element;
element.disabled = option.disabled;
- // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
+ // Support: IE 11 only, Edge 12-13 only
+ // NOTE: The label must be set before the value, otherwise IE 11 & Edge create unresponsive
// selects in certain circumstances when multiple selects are next to each other and display
// the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
// See https://github.com/angular/angular.js/issues/11314 for more info.
@@ -30090,11 +30996,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
var groupElementMap = {};
- // Ensure that the empty option is always there if it was explicitly provided
- if (providedEmptyOption) {
- selectElement.prepend(selectCtrl.emptyOption);
- }
-
options.items.forEach(function addOption(option) {
var groupElement;
@@ -30139,7 +31040,6 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
ngModelCtrl.$render();
}
}
-
}
}
@@ -30167,27 +31067,27 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
* @description
* `ngPluralize` is a directive that displays messages according to en-US localization rules.
* These rules are bundled with angular.js, but can be overridden
- * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
+ * (see {@link guide/i18n AngularJS i18n} dev guide). You configure ngPluralize directive
* by specifying the mappings between
* [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
* and the strings to be displayed.
*
- * # Plural categories and explicit number rules
+ * ## Plural categories and explicit number rules
* There are two
* [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
- * in Angular's default en-US locale: "one" and "other".
+ * in AngularJS's default en-US locale: "one" and "other".
*
* While a plural category may match many numbers (for example, in en-US locale, "other" can match
* any number that is not 1), an explicit number rule can only match one number. For example, the
* explicit number rule for "3" matches the number 3. There are examples of plural categories
* and explicit number rules throughout the rest of this documentation.
*
- * # Configuring ngPluralize
+ * ## Configuring ngPluralize
* You configure ngPluralize by providing 2 attributes: `count` and `when`.
* You can also provide an optional attribute, `offset`.
*
* The value of the `count` attribute can be either a string or an {@link guide/expression
- * Angular expression}; these are evaluated on the current scope for its bound value.
+ * AngularJS expression}; these are evaluated on the current scope for its bound value.
*
* The `when` attribute specifies the mappings between plural categories and the actual
* string to be displayed. The value of the attribute should be a JSON object.
@@ -30209,14 +31109,14 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
* show "a dozen people are viewing".
*
* You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
- * into pluralized strings. In the previous example, Angular will replace `{}` with
+ * into pluralized strings. In the previous example, AngularJS will replace `{}` with
* <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
* for <span ng-non-bindable>{{numberExpression}}</span>.
*
* If no rule is defined for a category, then an empty string is displayed and a warning is generated.
* Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
*
- * # Configuring ngPluralize with offset
+ * ## Configuring ngPluralize with offset
* The `offset` attribute allows further customization of pluralized text, which can result in
* a better user experience. For example, instead of the message "4 people are viewing this document",
* you might display "John, Kate and 2 others are viewing this document".
@@ -30237,7 +31137,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
* three explicit number rules 0, 1 and 2.
* When one person, perhaps John, views the document, "John is viewing" will be shown.
* When three people view the document, no explicit number rule is found, so
- * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
+ * an offset of 2 is taken off 3, and AngularJS uses 1 to decide the plural category.
* In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
* is shown.
*
@@ -30404,6 +31304,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* @ngdoc directive
* @name ngRepeat
* @multiElement
+ * @restrict A
*
* @description
* The `ngRepeat` directive instantiates a template once per item from a collection. Each template
@@ -30427,7 +31328,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* </div>
*
*
- * # Iterating over object properties
+ * ## Iterating over object properties
*
* It is possible to get `ngRepeat` to iterate over the properties of an object using the following
* syntax:
@@ -30439,14 +31340,14 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* However, there are a few limitations compared to array iteration:
*
* - The JavaScript specification does not define the order of keys
- * returned for an object, so Angular relies on the order returned by the browser
+ * returned for an object, so AngularJS relies on the order returned by the browser
* when running `for key in myObj`. Browsers generally follow the strategy of providing
* keys in the order in which they were defined, although there are exceptions when keys are deleted
* 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).
*
* - `ngRepeat` will silently *ignore* object keys starting with `$`, because
- * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
+ * it's a prefix used by AngularJS for public (`$`) and private (`$$`) properties.
*
* - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
* objects, and will throw an error if used with one.
@@ -30457,7 +31358,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* or implement a `$watch` on the object yourself.
*
*
- * # Tracking and Duplicates
+ * ## Tracking and Duplicates
*
* `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
* the collection. When a change happens, `ngRepeat` then makes the corresponding changes to the DOM:
@@ -30471,73 +31372,150 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* For example, if an item is added to the collection, `ngRepeat` will know that all other items
* already have DOM elements, and will not re-render them.
*
- * The default tracking function (which tracks items by their identity) does not allow
- * duplicate items in arrays. This is because when there are duplicates, it is not possible
- * to maintain a one-to-one mapping between collection items and DOM elements.
- *
- * If you do need to repeat duplicate items, you can substitute the default tracking behavior
- * with your own using the `track by` expression.
- *
- * For example, you may track items by the index of each item in the collection, using the
- * special scope property `$index`:
- * ```html
- * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
- * {{n}}
- * </div>
- * ```
- *
- * You may also use arbitrary expressions in `track by`, including references to custom functions
- * on the scope:
- * ```html
- * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
- * {{n}}
- * </div>
- * ```
+ * All different types of tracking functions, their syntax, and and their support for duplicate
+ * items in collections can be found in the
+ * {@link ngRepeat#ngRepeat-arguments ngRepeat expression description}.
*
* <div class="alert alert-success">
- * If you are working with objects that have a unique identifier property, you should track
- * by this identifier instead of the object instance. Should you reload your data later, `ngRepeat`
- * will not have to rebuild the DOM elements for items it has already rendered, even if the
- * JavaScript objects in the collection have been substituted for new ones. For large collections,
- * this significantly improves rendering performance. If you don't have a unique identifier,
- * `track by $index` can also provide a performance boost.
+ * **Best Practice:** If you are working with objects that have a unique identifier property, you
+ * should track by this identifier instead of the object instance,
+ * e.g. `item in items track by item.id`.
+ * Should you reload your data later, `ngRepeat` will not have to rebuild the DOM elements for items
+ * it has already rendered, even if the JavaScript objects in the collection have been substituted
+ * for new ones. For large collections, this significantly improves rendering performance.
* </div>
*
- * ```html
- * <div ng-repeat="model in collection track by model.id">
- * {{model.name}}
- * </div>
- * ```
+ * ### Effects of DOM Element re-use
*
- * <br />
- * <div class="alert alert-warning">
- * Avoid using `track by $index` when the repeated template contains
- * {@link guide/expression#one-time-binding one-time bindings}. In such cases, the `nth` DOM
- * element will always be matched with the `nth` item of the array, so the bindings on that element
- * will not be updated even when the corresponding item changes, essentially causing the view to get
- * out-of-sync with the underlying data.
- * </div>
+ * When DOM elements are re-used, ngRepeat updates the scope for the element, which will
+ * automatically update any active bindings on the template. However, other
+ * functionality will not be updated, because the element is not re-created:
*
- * When no `track by` expression is provided, it is equivalent to tracking by the built-in
- * `$id` function, which tracks items by their identity:
- * ```html
- * <div ng-repeat="obj in collection track by $id(obj)">
- * {{obj.prop}}
- * </div>
- * ```
+ * - Directives are not re-compiled
+ * - {@link guide/expression#one-time-binding one-time expressions} on the repeated template are not
+ * updated if they have stabilized.
*
- * <br />
- * <div class="alert alert-warning">
- * **Note:** `track by` must always be the last expression:
- * </div>
- * ```
- * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
- * {{model.name}}
- * </div>
- * ```
+ * The above affects all kinds of element re-use due to tracking, but may be especially visible
+ * when tracking by `$index` due to the way ngRepeat re-uses elements.
*
+ * The following example shows the effects of different actions with tracking:
+
+ <example module="ngRepeat" name="ngRepeat-tracking" deps="angular-animate.js" animations="true">
+ <file name="script.js">
+ angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
+ var friends = [
+ {name:'John', age:25},
+ {name:'Mary', age:40},
+ {name:'Peter', age:85}
+ ];
+
+ $scope.removeFirst = function() {
+ $scope.friends.shift();
+ };
+
+ $scope.updateAge = function() {
+ $scope.friends.forEach(function(el) {
+ el.age = el.age + 5;
+ });
+ };
+
+ $scope.copy = function() {
+ $scope.friends = angular.copy($scope.friends);
+ };
+
+ $scope.reset = function() {
+ $scope.friends = angular.copy(friends);
+ };
+
+ $scope.reset();
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="repeatController">
+ <ol>
+ <li>When you click "Update Age", only the first list updates the age, because all others have
+ a one-time binding on the age property. If you then click "Copy", the current friend list
+ is copied, and now the second list updates the age, because the identity of the collection items
+ has changed and the list must be re-rendered. The 3rd and 4th list stay the same, because all the
+ items are already known according to their tracking functions.
+ </li>
+ <li>When you click "Remove First", the 4th list has the wrong age on both remaining items. This is
+ due to tracking by $index: when the first collection item is removed, ngRepeat reuses the first
+ DOM element for the new first collection item, and so on. Since the age property is one-time
+ bound, the value remains from the collection item which was previously at this index.
+ </li>
+ </ol>
+
+ <button ng-click="removeFirst()">Remove First</button>
+ <button ng-click="updateAge()">Update Age</button>
+ <button ng-click="copy()">Copy</button>
+ <br><button ng-click="reset()">Reset List</button>
+ <br>
+ <code>track by $id(friend)</code> (default):
+ <ul class="example-animate-container">
+ <li class="animate-repeat" ng-repeat="friend in friends">
+ {{friend.name}} is {{friend.age}} years old.
+ </li>
+ </ul>
+ <code>track by $id(friend)</code> (default), with age one-time binding:
+ <ul class="example-animate-container">
+ <li class="animate-repeat" ng-repeat="friend in friends">
+ {{friend.name}} is {{::friend.age}} years old.
+ </li>
+ </ul>
+ <code>track by friend.name</code>, with age one-time binding:
+ <ul class="example-animate-container">
+ <li class="animate-repeat" ng-repeat="friend in friends track by friend.name">
+ {{friend.name}} is {{::friend.age}} years old.
+ </li>
+ </ul>
+ <code>track by $index</code>, with age one-time binding:
+ <ul class="example-animate-container">
+ <li class="animate-repeat" ng-repeat="friend in friends track by $index">
+ {{friend.name}} is {{::friend.age}} years old.
+ </li>
+ </ul>
+ </div>
+ </file>
+ <file name="animations.css">
+ .example-animate-container {
+ background:white;
+ border:1px solid black;
+ list-style:none;
+ margin:0;
+ padding:0 10px;
+ }
+
+ .animate-repeat {
+ line-height:30px;
+ list-style:none;
+ box-sizing:border-box;
+ }
+
+ .animate-repeat.ng-move,
+ .animate-repeat.ng-enter,
+ .animate-repeat.ng-leave {
+ transition:all linear 0.5s;
+ }
+
+ .animate-repeat.ng-leave.ng-leave-active,
+ .animate-repeat.ng-move,
+ .animate-repeat.ng-enter {
+ opacity:0;
+ max-height:0;
+ }
+
+ .animate-repeat.ng-leave,
+ .animate-repeat.ng-move.ng-move-active,
+ .animate-repeat.ng-enter.ng-enter-active {
+ opacity:1;
+ max-height:30px;
+ }
+ </file>
+ </example>
+
*
- * # Special repeat start and end points
+ * ## Special repeat start and end points
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
* the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
* 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)
@@ -30612,22 +31590,38 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
* mapped to the same DOM element, which is not possible.)
*
- * Note that the tracking expression must come last, after any filters, and the alias expression.
- *
- * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
- * will be associated by item identity in the array.
- *
- * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
- * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
- * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
- * element in the same way in the DOM.
- *
- * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
- * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
- * property is same.
+ * *Default tracking: $id()*: `item in items` is equivalent to `item in items track by $id(item)`.
+ * This implies that the DOM elements will be associated by item identity in the collection.
+ *
+ * The built-in `$id()` function can be used to assign a unique
+ * `$$hashKey` property to each item in the collection. This property is then used as a key to associated DOM elements
+ * with the corresponding item in the collection by identity. Moving the same object would move
+ * the DOM element in the same way in the DOM.
+ * Note that the default id function does not support duplicate primitive values (`number`, `string`),
+ * but supports duplictae non-primitive values (`object`) that are *equal* in shape.
+ *
+ * *Custom Expression*: It is possible to use any AngularJS expression to compute the tracking
+ * id, for example with a function, or using a property on the collection items.
+ * `item in items track by item.id` is a typical pattern when the items have a unique identifier,
+ * e.g. database id. In this case the object identity does not matter. Two objects are considered
+ * equivalent as long as their `id` property is same.
+ * Tracking by unique identifier is the most performant way and should be used whenever possible.
+ *
+ * *$index*: This special property tracks the collection items by their index, and
+ * re-uses the DOM elements that match that index, e.g. `item in items track by $index`. This can
+ * be used for a performance improvement if no unique identfier is available and the identity of
+ * the collection items cannot be easily computed. It also allows duplicates.
+ *
+ * <div class="alert alert-warning">
+ * <strong>Note:</strong> Re-using DOM elements can have unforeseen effects. Read the
+ * {@link ngRepeat#tracking-and-duplicates section on tracking and duplicates} for
+ * more info.
+ * </div>
*
- * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
- * to items in conjunction with a tracking expression.
+ * <div class="alert alert-warning">
+ * <strong>Note:</strong> the `track by` expression must come last - after any filters, and the alias expression:
+ * `item in items | filter:searchText as results track by item.id`
+ * </div>
*
* * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
* intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
@@ -30636,21 +31630,21 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
* the items have been processed through the filter.
*
- * 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
- * (and not as operator, inside an expression).
+ * Please note that `as [variable name] is not an operator but rather a part of ngRepeat
+ * micro-syntax so it can be used only after all filters (and not as operator, inside an expression).
*
- * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
+ * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results track by item.id` .
*
* @example
* This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
* results by name or by age. New (entering) and removed (leaving) items are animated.
- <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true" name="ng-repeat">
+ <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
<file name="index.html">
<div ng-controller="repeatController">
I have {{friends.length}} friends. They are:
<input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
<ul class="example-animate-container">
- <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
+ <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results track by friend.name">
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
</li>
<li class="animate-repeat" ng-if="results.length === 0">
@@ -30826,7 +31820,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
// Store a list of elements from previous run. This is a hash where key is the item from the
// iterator, and the value is objects with following properties.
// - scope: bound scope
- // - element: previous element.
+ // - clone: previous element.
// - index: position
//
// We are using no-proto object so that we don't need to guard against inherited props via
@@ -31022,7 +32016,11 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* By default you don't need to override anything in CSS and the animations will work around the
* display style.
*
- * ## A note about animations with `ngShow`
+ * @animations
+ * | Animation | Occurs |
+ * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
+ * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. |
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. |
*
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
* directive expression is true and false. This system works like the animation system present with
@@ -31044,12 +32042,6 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
*
- * @animations
- * | Animation | Occurs |
- * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
- * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. |
- * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. |
- *
* @element ANY
* @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
* element is shown/hidden respectively.
@@ -31224,7 +32216,11 @@ var ngShowDirective = ['$animate', function($animate) {
* By default you don't need to override in CSS anything and the animations will work around the
* display style.
*
- * ## A note about animations with `ngHide`
+ * @animations
+ * | Animation | Occurs |
+ * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
+ * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. |
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
*
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
* directive expression is true and false. This system works like the animation system present with
@@ -31246,13 +32242,6 @@ var ngShowDirective = ['$animate', function($animate) {
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
*
- * @animations
- * | Animation | Occurs |
- * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
- * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. |
- * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
- *
- *
* @element ANY
* @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
* element is hidden/shown respectively.
@@ -31812,7 +32801,6 @@ var ngTranscludeMinErr = minErr('ngTransclude');
var ngTranscludeDirective = ['$compile', function($compile) {
return {
restrict: 'EAC',
- terminal: true,
compile: function ngTranscludeCompile(tElement) {
// Remove and cache any original content to act as a fallback
@@ -31928,13 +32916,152 @@ var scriptDirective = ['$templateCache', function($templateCache) {
var noopNgModelController = { $setViewValue: noop, $render: noop };
+function setOptionSelectedStatus(optionEl, value) {
+ optionEl.prop('selected', value);
+ /**
+ * When unselecting an option, setting the property to null / false should be enough
+ * However, screenreaders might react to the selected attribute instead, see
+ * https://github.com/angular/angular.js/issues/14419
+ * Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
+ * or null
+ */
+ optionEl.attr('selected', value);
+}
+
/**
* @ngdoc type
* @name select.SelectController
+ *
* @description
- * The controller for the `<select>` directive. This provides support for reading
- * and writing the selected value(s) of the control and also coordinates dynamically
- * added `<option>` elements, perhaps by an `ngRepeat` directive.
+ * The controller for the {@link ng.select select} directive. The controller exposes
+ * a few utility methods that can be used to augment the behavior of a regular or an
+ * {@link ng.ngOptions ngOptions} select element.
+ *
+ * @example
+ * ### Set a custom error when the unknown option is selected
+ *
+ * This example sets a custom error "unknownValue" on the ngModelController
+ * when the select element's unknown option is selected, i.e. when the model is set to a value
+ * that is not matched by any option.
+ *
+ * <example name="select-unknown-value-error" module="staticSelect">
+ * <file name="index.html">
+ * <div ng-controller="ExampleController">
+ * <form name="myForm">
+ * <label for="testSelect"> Single select: </label><br>
+ * <select name="testSelect" ng-model="selected" unknown-value-error>
+ * <option value="option-1">Option 1</option>
+ * <option value="option-2">Option 2</option>
+ * </select><br>
+ * <span class="error" ng-if="myForm.testSelect.$error.unknownValue">
+ * Error: The current model doesn't match any option</span><br>
+ *
+ * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
+ * </form>
+ * </div>
+ * </file>
+ * <file name="app.js">
+ * angular.module('staticSelect', [])
+ * .controller('ExampleController', ['$scope', function($scope) {
+ * $scope.selected = null;
+ *
+ * $scope.forceUnknownOption = function() {
+ * $scope.selected = 'nonsense';
+ * };
+ * }])
+ * .directive('unknownValueError', function() {
+ * return {
+ * require: ['ngModel', 'select'],
+ * link: function(scope, element, attrs, ctrls) {
+ * var ngModelCtrl = ctrls[0];
+ * var selectCtrl = ctrls[1];
+ *
+ * ngModelCtrl.$validators.unknownValue = function(modelValue, viewValue) {
+ * if (selectCtrl.$isUnknownOptionSelected()) {
+ * return false;
+ * }
+ *
+ * return true;
+ * };
+ * }
+ *
+ * };
+ * });
+ * </file>
+ *</example>
+ *
+ *
+ * @example
+ * ### Set the "required" error when the unknown option is selected.
+ *
+ * By default, the "required" error on the ngModelController is only set on a required select
+ * when the empty option is selected. This example adds a custom directive that also sets the
+ * error when the unknown option is selected.
+ *
+ * <example name="select-unknown-value-required" module="staticSelect">
+ * <file name="index.html">
+ * <div ng-controller="ExampleController">
+ * <form name="myForm">
+ * <label for="testSelect"> Select: </label><br>
+ * <select name="testSelect" ng-model="selected" required unknown-value-required>
+ * <option value="option-1">Option 1</option>
+ * <option value="option-2">Option 2</option>
+ * </select><br>
+ * <span class="error" ng-if="myForm.testSelect.$error.required">Error: Please select a value</span><br>
+ *
+ * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
+ * </form>
+ * </div>
+ * </file>
+ * <file name="app.js">
+ * angular.module('staticSelect', [])
+ * .controller('ExampleController', ['$scope', function($scope) {
+ * $scope.selected = null;
+ *
+ * $scope.forceUnknownOption = function() {
+ * $scope.selected = 'nonsense';
+ * };
+ * }])
+ * .directive('unknownValueRequired', function() {
+ * return {
+ * priority: 1, // This directive must run after the required directive has added its validator
+ * require: ['ngModel', 'select'],
+ * link: function(scope, element, attrs, ctrls) {
+ * var ngModelCtrl = ctrls[0];
+ * var selectCtrl = ctrls[1];
+ *
+ * var originalRequiredValidator = ngModelCtrl.$validators.required;
+ *
+ * ngModelCtrl.$validators.required = function() {
+ * if (attrs.required && selectCtrl.$isUnknownOptionSelected()) {
+ * return false;
+ * }
+ *
+ * return originalRequiredValidator.apply(this, arguments);
+ * };
+ * }
+ * };
+ * });
+ * </file>
+ * <file name="protractor.js" type="protractor">
+ * it('should show the error message when the unknown option is selected', function() {
+
+ var error = element(by.className('error'));
+
+ expect(error.getText()).toBe('Error: Please select a value');
+
+ element(by.cssContainingText('option', 'Option 1')).click();
+
+ expect(error.isPresent()).toBe(false);
+
+ element(by.tagName('button')).click();
+
+ expect(error.getText()).toBe('Error: Please select a value');
+ });
+ * </file>
+ *</example>
+ *
+ *
*/
var SelectController =
['$element', '$scope', /** @this */ function($element, $scope) {
@@ -31952,15 +33079,18 @@ var SelectController =
// does not match any of the options. When it is rendered the value of the unknown
// option is '? XXX ?' where XXX is the hashKey of the value that is not known.
//
+ // Support: IE 9 only
// We can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
self.unknownOption = jqLite(window.document.createElement('option'));
- // The empty option is an option with the value '' that te application developer can
- // provide inside the select. When the model changes to a value that doesn't match an option,
- // it is selected - so if an empty option is provided, no unknown option is generated.
- // However, the empty option is not removed when the model matches an option. It is always selectable
- // and indicates that a "null" selection has been made.
+ // The empty option is an option with the value '' that the application developer can
+ // provide inside the select. It is always selectable and indicates that a "null" selection has
+ // been made by the user.
+ // If the select has an empty option, and the model of the select is set to "undefined" or "null",
+ // the empty option is selected.
+ // If the model is set to a different unmatched value, the unknown option is rendered and
+ // selected, i.e both are present, because a "null" selection and an unknown value are different.
self.hasEmptyOption = false;
self.emptyOption = undefined;
@@ -31968,14 +33098,14 @@ var SelectController =
var unknownVal = self.generateUnknownOptionValue(val);
self.unknownOption.val(unknownVal);
$element.prepend(self.unknownOption);
- setOptionAsSelected(self.unknownOption);
+ setOptionSelectedStatus(self.unknownOption, true);
$element.val(unknownVal);
};
self.updateUnknownOption = function(val) {
var unknownVal = self.generateUnknownOptionValue(val);
self.unknownOption.val(unknownVal);
- setOptionAsSelected(self.unknownOption);
+ setOptionSelectedStatus(self.unknownOption, true);
$element.val(unknownVal);
};
@@ -31990,13 +33120,13 @@ var SelectController =
self.selectEmptyOption = function() {
if (self.emptyOption) {
$element.val('');
- setOptionAsSelected(self.emptyOption);
+ setOptionSelectedStatus(self.emptyOption, true);
}
};
self.unselectEmptyOption = function() {
if (self.hasEmptyOption) {
- self.emptyOption.removeAttr('selected');
+ setOptionSelectedStatus(self.emptyOption, false);
}
};
@@ -32026,7 +33156,7 @@ var SelectController =
// Make sure to remove the selected attribute from the previously selected option
// Otherwise, screen readers might get confused
var currentlySelectedOption = $element[0].options[$element[0].selectedIndex];
- if (currentlySelectedOption) currentlySelectedOption.removeAttribute('selected');
+ if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
if (self.hasOption(value)) {
self.removeUnknownOption();
@@ -32036,16 +33166,9 @@ var SelectController =
// Set selected attribute and property on selected option for screen readers
var selectedOption = $element[0].options[$element[0].selectedIndex];
- setOptionAsSelected(jqLite(selectedOption));
+ setOptionSelectedStatus(jqLite(selectedOption), true);
} else {
- if (value == null && self.emptyOption) {
- self.removeUnknownOption();
- self.selectEmptyOption();
- } else if (self.unknownOption.parent().length) {
- self.updateUnknownOption(value);
- } else {
- self.renderUnknownOption(value);
- }
+ self.selectUnknownOrEmptyOption(value);
}
};
@@ -32088,6 +33211,59 @@ var SelectController =
return !!optionsMap.get(value);
};
+ /**
+ * @ngdoc method
+ * @name select.SelectController#$hasEmptyOption
+ *
+ * @description
+ *
+ * Returns `true` if the select element currently has an empty option
+ * element, i.e. an option that signifies that the select is empty / the selection is null.
+ *
+ */
+ self.$hasEmptyOption = function() {
+ return self.hasEmptyOption;
+ };
+
+ /**
+ * @ngdoc method
+ * @name select.SelectController#$isUnknownOptionSelected
+ *
+ * @description
+ *
+ * Returns `true` if the select element's unknown option is selected. The unknown option is added
+ * and automatically selected whenever the select model doesn't match any option.
+ *
+ */
+ self.$isUnknownOptionSelected = function() {
+ // Presence of the unknown option means it is selected
+ return $element[0].options[0] === self.unknownOption[0];
+ };
+
+ /**
+ * @ngdoc method
+ * @name select.SelectController#$isEmptyOptionSelected
+ *
+ * @description
+ *
+ * Returns `true` if the select element has an empty option and this empty option is currently
+ * selected. Returns `false` if the select element has no empty option or it is not selected.
+ *
+ */
+ self.$isEmptyOptionSelected = function() {
+ return self.hasEmptyOption && $element[0].options[$element[0].selectedIndex] === self.emptyOption[0];
+ };
+
+ self.selectUnknownOrEmptyOption = function(value) {
+ if (value == null && self.emptyOption) {
+ self.removeUnknownOption();
+ self.selectEmptyOption();
+ } else if (self.unknownOption.parent().length) {
+ self.updateUnknownOption(value);
+ } else {
+ self.renderUnknownOption(value);
+ }
+ };
var renderScheduled = false;
function scheduleRender() {
@@ -32216,11 +33392,6 @@ var SelectController =
}
});
};
-
- function setOptionAsSelected(optionEl) {
- optionEl.prop('selected', true); // needed for IE
- optionEl.attr('selected', true);
- }
}];
/**
@@ -32229,7 +33400,7 @@ var SelectController =
* @restrict E
*
* @description
- * HTML `select` element with angular data-binding.
+ * HTML `select` element with AngularJS data-binding.
*
* The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
* between the scope and the `<select>` control (including setting default values).
@@ -32241,6 +33412,9 @@ var SelectController =
* the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
* Value and textContent can be interpolated.
*
+ * The {@link select.SelectController select controller} exposes utility functions that can be used
+ * to manipulate the select's behavior.
+ *
* ## Matching model and option values
*
* In general, the match between the model and an option is evaluated by strictly comparing the model
@@ -32278,7 +33452,7 @@ var SelectController =
* Chrome and Internet Explorer / Edge.
*
*
- * @param {string} ngModel Assignable angular expression to data-bind to.
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} multiple Allows multiple options to be selected. The selected values will be
* bound to the model as an array.
@@ -32286,10 +33460,25 @@ var SelectController =
* @param {string=} ngRequired Adds required attribute and required validation constraint to
* the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
* when you want to data-bind to the required attribute.
- * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
+ * @param {string=} ngChange AngularJS expression to be executed when selected option(s) changes due to user
* interaction with the select element.
* @param {string=} ngOptions sets the options that the select is populated with and defines what is
* set on the model on selection. See {@link ngOptions `ngOptions`}.
+ * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
+ * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
+ *
+ *
+ * @knownIssue
+ *
+ * In Firefox, the select model is only updated when the select element is blurred. For example,
+ * when switching between options with the keyboard, the select model is only set to the
+ * currently selected option when the select is blurred, e.g via tab key or clicking the mouse
+ * outside the select.
+ *
+ * This is due to an ambiguity in the select element specification. See the
+ * [issue on the Firefox bug tracker](https://bugzilla.mozilla.org/show_bug.cgi?id=126379)
+ * for more information, and this
+ * [Github comment for a workaround](https://github.com/angular/angular.js/issues/9134#issuecomment-130800488)
*
* @example
* ### Simple `select` elements with static options
@@ -32340,6 +33529,7 @@ var SelectController =
* </file>
*</example>
*
+ * @example
* ### Using `ngRepeat` to generate `select` options
* <example name="select-ngrepeat" module="ngrepeatSelect">
* <file name="index.html">
@@ -32369,6 +33559,7 @@ var SelectController =
* </file>
*</example>
*
+ * @example
* ### Using `ngValue` to bind the model to an array of objects
* <example name="select-ngvalue" module="ngvalueSelect">
* <file name="index.html">
@@ -32401,6 +33592,7 @@ var SelectController =
* </file>
*</example>
*
+ * @example
* ### Using `select` with `ngOptions` and setting a default value
* See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
*
@@ -32432,7 +33624,7 @@ var SelectController =
* </file>
*</example>
*
- *
+ * @example
* ### Binding `select` to a non-string value via `ngModel` parsing / formatting
*
* <example name="select-with-non-string-options" module="nonStringSelect">
@@ -32531,8 +33723,21 @@ var selectDirective = function() {
// Write value now needs to set the selected property of each matching option
selectCtrl.writeValue = function writeMultipleValue(value) {
forEach(element.find('option'), function(option) {
- option.selected = !!value && (includes(value, option.value) ||
- includes(value, selectCtrl.selectValueMap[option.value]));
+ var shouldBeSelected = !!value && (includes(value, option.value) ||
+ includes(value, selectCtrl.selectValueMap[option.value]));
+ var currentlySelected = option.selected;
+
+ // Support: IE 9-11 only, Edge 12-15+
+ // In IE and Edge adding options to the selection via shift+click/UP/DOWN
+ // will de-select already selected options if "selected" on those options was set
+ // more than once (i.e. when the options were already selected)
+ // So we only modify the selected property if necessary.
+ // Note: this behavior cannot be replicated via unit tests because it only shows in the
+ // actual user interface.
+ if (shouldBeSelected !== currentlySelected) {
+ setOptionSelectedStatus(jqLite(option), shouldBeSelected);
+ }
+
});
};
@@ -32620,13 +33825,17 @@ var optionDirective = ['$interpolate', function($interpolate) {
* @name ngRequired
* @restrict A
*
+ * @param {expression} ngRequired AngularJS expression. If it evaluates to `true`, it sets the
+ * `required` attribute to the element and adds the `required`
+ * {@link ngModel.NgModelController#$validators `validator`}.
+ *
* @description
*
* ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
* It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
* applied to custom controls.
*
- * The directive sets the `required` attribute on the element if the Angular expression inside
+ * The directive sets the `required` attribute on the element if the AngularJS expression inside
* `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
* cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
* for more info.
@@ -32696,6 +33905,11 @@ var requiredDirective = function() {
/**
* @ngdoc directive
* @name ngPattern
+ * @restrict A
+ *
+ * @param {expression|RegExp} ngPattern AngularJS expression that must evaluate to a `RegExp` or a `String`
+ * parsable into a `RegExp`, or a `RegExp` literal. See above for
+ * more details.
*
* @description
*
@@ -32703,11 +33917,12 @@ var requiredDirective = function() {
* It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
*
* The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * does not match a RegExp which is obtained by evaluating the Angular expression given in the
- * `ngPattern` attribute value:
- * * If the expression evaluates to a RegExp object, then this is used directly.
- * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
- * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
+ * does not match a RegExp which is obtained from the `ngPattern` attribute value:
+ * - the value is an AngularJS expression:
+ * - If the expression evaluates to a RegExp object, then this is used directly.
+ * - If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
+ * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
+ * - If the value is a RegExp literal, e.g. `ngPattern="/^\d+$/"`, it is used directly.
*
* <div class="alert alert-info">
* **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
@@ -32802,6 +34017,11 @@ var patternDirective = function() {
/**
* @ngdoc directive
* @name ngMaxlength
+ * @restrict A
+ *
+ * @param {expression} ngMaxlength AngularJS expression that must evaluate to a `Number` or `String`
+ * parsable into a `Number`. Used as value for the `maxlength`
+ * {@link ngModel.NgModelController#$validators validator}.
*
* @description
*
@@ -32809,7 +34029,7 @@ var patternDirective = function() {
* It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
*
* The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * is longer than the integer obtained by evaluating the Angular expression given in the
+ * is longer than the integer obtained by evaluating the AngularJS expression given in the
* `ngMaxlength` attribute value.
*
* <div class="alert alert-info">
@@ -32888,6 +34108,11 @@ var maxlengthDirective = function() {
/**
* @ngdoc directive
* @name ngMinlength
+ * @restrict A
+ *
+ * @param {expression} ngMinlength AngularJS expression that must evaluate to a `Number` or `String`
+ * parsable into a `Number`. Used as value for the `minlength`
+ * {@link ngModel.NgModelController#$validators validator}.
*
* @description
*
@@ -32895,7 +34120,7 @@ var maxlengthDirective = function() {
* It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
*
* The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
- * is shorter than the integer obtained by evaluating the Angular expression given in the
+ * is shorter than the integer obtained by evaluating the AngularJS expression given in the
* `ngMinlength` attribute value.
*
* <div class="alert alert-info">
@@ -32971,7 +34196,7 @@ var minlengthDirective = function() {
if (window.angular.bootstrap) {
// AngularJS is already loaded, so we can return here...
if (window.console) {
- console.log('WARNING: Tried to load angular more than once.');
+ console.log('WARNING: Tried to load AngularJS more than once.');
}
return;
}