Commit d9cc23ab authored by Pascal Hartig's avatar Pascal Hartig

AngularJS: Upgrade to 1.3.8

parent 762f2d29
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
"name": "todomvc-angular", "name": "todomvc-angular",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"angular": "1.3.6", "angular": "1.3.8",
"todomvc-common": "~0.3.0" "todomvc-common": "~0.3.0"
}, },
"devDependencies": { "devDependencies": {
"angular-mocks": "1.3.6", "angular-mocks": "1.3.8",
"angular-route": "1.3.6" "angular-route": "1.3.8"
} }
} }
/** /**
* @license AngularJS v1.3.6 * @license AngularJS v1.3.8
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
......
/** /**
* @license AngularJS v1.3.6 * @license AngularJS v1.3.8
* (c) 2010-2014 Google, Inc. http://angularjs.org * (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
...@@ -54,7 +54,7 @@ function minErr(module, ErrorConstructor) { ...@@ -54,7 +54,7 @@ function minErr(module, ErrorConstructor) {
return match; return match;
}); });
message = message + '\nhttp://errors.angularjs.org/1.3.6/' + message = message + '\nhttp://errors.angularjs.org/1.3.8/' +
(module ? module + '/' : '') + code; (module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) { for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' + message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
...@@ -109,6 +109,7 @@ function minErr(module, ErrorConstructor) { ...@@ -109,6 +109,7 @@ function minErr(module, ErrorConstructor) {
isWindow: true, isWindow: true,
isScope: true, isScope: true,
isFile: true, isFile: true,
isFormData: true,
isBlob: true, isBlob: true,
isBoolean: true, isBoolean: true,
isPromiseLike: true, isPromiseLike: true,
...@@ -464,6 +465,8 @@ noop.$inject = []; ...@@ -464,6 +465,8 @@ noop.$inject = [];
return (transformationFn || angular.identity)(value); return (transformationFn || angular.identity)(value);
}; };
``` ```
* @param {*} value to be returned.
* @returns {*} the value passed in.
*/ */
function identity($) {return $;} function identity($) {return $;}
identity.$inject = []; identity.$inject = [];
...@@ -630,6 +633,11 @@ function isFile(obj) { ...@@ -630,6 +633,11 @@ function isFile(obj) {
} }
function isFormData(obj) {
return toString.call(obj) === '[object FormData]';
}
function isBlob(obj) { function isBlob(obj) {
return toString.call(obj) === '[object Blob]'; return toString.call(obj) === '[object Blob]';
} }
...@@ -713,7 +721,7 @@ function arrayRemove(array, value) { ...@@ -713,7 +721,7 @@ function arrayRemove(array, value) {
* Creates a deep copy of `source`, which should be an object or an array. * Creates a deep copy of `source`, which should be an object or an array.
* *
* * If no destination is supplied, a copy of the object or array is created. * * If no destination is supplied, a copy of the object or array is created.
* * If a destination is provided, all of its elements (for array) or properties (for objects) * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
* are deleted and then all elements/properties from the source are copied to it. * are deleted and then all elements/properties from the source are copied to it.
* * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
* * If `source` is identical to 'destination' an exception will be thrown. * * If `source` is identical to 'destination' an exception will be thrown.
...@@ -1051,7 +1059,7 @@ function toJson(obj, pretty) { ...@@ -1051,7 +1059,7 @@ function toJson(obj, pretty) {
* Deserializes a JSON string. * Deserializes a JSON string.
* *
* @param {string} json JSON string to deserialize. * @param {string} json JSON string to deserialize.
* @returns {Object|Array|string|number} Deserialized thingy. * @returns {Object|Array|string|number} Deserialized JSON string.
*/ */
function fromJson(json) { function fromJson(json) {
return isString(json) return isString(json)
...@@ -1224,7 +1232,7 @@ function getNgAttribute(element, ngAttr) { ...@@ -1224,7 +1232,7 @@ function getNgAttribute(element, ngAttr) {
* {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
* *
* You can specify an **AngularJS module** to be used as the root module for the application. This * You can specify an **AngularJS module** to be used as the root module for the application. This
* module will be loaded into the {@link auto.$injector} when the application is bootstrapped and * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
* should contain the application code needed or have dependencies on other modules that will * should contain the application code needed or have dependencies on other modules that will
* contain the code. See {@link angular.module} for more information. * contain the code. See {@link angular.module} for more information.
* *
...@@ -1232,7 +1240,7 @@ function getNgAttribute(element, ngAttr) { ...@@ -1232,7 +1240,7 @@ function getNgAttribute(element, ngAttr) {
* document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
* would not be resolved to `3`. * would not be resolved to `3`.
* *
* `ngApp` is the easiest, and most common, way to bootstrap an application. * `ngApp` is the easiest, and most common way to bootstrap an application.
* *
<example module="ngAppDemo"> <example module="ngAppDemo">
<file name="index.html"> <file name="index.html">
...@@ -1492,7 +1500,12 @@ function reloadWithDebugInfo() { ...@@ -1492,7 +1500,12 @@ function reloadWithDebugInfo() {
* @param {DOMElement} element DOM element which is the root of angular application. * @param {DOMElement} element DOM element which is the root of angular application.
*/ */
function getTestability(rootElement) { function getTestability(rootElement) {
return angular.element(rootElement).injector().get('$$testability'); var injector = angular.element(rootElement).injector();
if (!injector) {
throw ngMinErr('test',
'no injector found for element argument to getTestability');
}
return injector.get('$$testability');
} }
var SNAKE_CASE_REGEXP = /[A-Z]/g; var SNAKE_CASE_REGEXP = /[A-Z]/g;
...@@ -2105,11 +2118,11 @@ function toDebugString(obj) { ...@@ -2105,11 +2118,11 @@ function toDebugString(obj) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/ */
var version = { var version = {
full: '1.3.6', // all of these placeholder strings will be replaced by grunt's full: '1.3.8', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task major: 1, // package task
minor: 3, minor: 3,
dot: 6, dot: 8,
codeName: 'robofunky-danceblaster' codeName: 'prophetic-narwhal'
}; };
...@@ -7122,7 +7135,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { ...@@ -7122,7 +7135,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// support ngAttr attribute binding // support ngAttr attribute binding
ngAttrName = directiveNormalize(name); ngAttrName = directiveNormalize(name);
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
name = snake_case(ngAttrName.substr(6), '-'); name = name.replace(PREFIX_REGEXP, '')
.substr(8).replace(/_(.)/g, function(match, letter) {
return letter.toUpperCase();
});
} }
var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
...@@ -8034,7 +8050,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { ...@@ -8034,7 +8050,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) { function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
var interpolateFn = $interpolate(value, true); var trustedContext = getTrustedContext(node, name);
allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
// no interpolation found -> ignore // no interpolation found -> ignore
if (!interpolateFn) return; if (!interpolateFn) return;
...@@ -8059,16 +8078,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { ...@@ -8059,16 +8078,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
"ng- versions (such as ng-click instead of onclick) instead."); "ng- versions (such as ng-click instead of onclick) instead.");
} }
// If the attribute was removed, then we are done // If the attribute has changed since last $interpolate()ed
if (!attr[name]) { var newValue = attr[name];
return; if (newValue !== value) {
// we need to interpolate again since the attribute value has been updated
// (e.g. by another directive's compile function)
// ensure unset/empty values make interpolateFn falsy
interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
value = newValue;
} }
// we need to interpolate again, in case the attribute value has been updated
// (e.g. by another directive's compile function)
interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
ALL_OR_NOTHING_ATTRS[name] || allOrNothing);
// if attribute was updated so that there is no interpolation going on we don't want to // if attribute was updated so that there is no interpolation going on we don't want to
// register any observers // register any observers
if (!interpolateFn) return; if (!interpolateFn) return;
...@@ -8529,23 +8548,34 @@ function $ExceptionHandlerProvider() { ...@@ -8529,23 +8548,34 @@ function $ExceptionHandlerProvider() {
var APPLICATION_JSON = 'application/json'; var APPLICATION_JSON = 'application/json';
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
var JSON_START = /^\s*(\[|\{[^\{])/; var JSON_START = /^\[|^\{(?!\{)/;
var JSON_END = /[\}\]]\s*$/; var JSON_ENDS = {
'[': /]$/,
'{': /}$/
};
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/; var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
function defaultHttpResponseTransform(data, headers) { function defaultHttpResponseTransform(data, headers) {
if (isString(data)) { if (isString(data)) {
// strip json vulnerability protection prefix // Strip json vulnerability protection prefix and trim whitespace
data = data.replace(JSON_PROTECTION_PREFIX, ''); var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
var contentType = headers('Content-Type');
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) || if (tempData) {
(JSON_START.test(data) && JSON_END.test(data))) { var contentType = headers('Content-Type');
data = fromJson(data); if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
data = fromJson(tempData);
}
} }
} }
return data; return data;
} }
function isJsonLike(str) {
var jsonStart = str.match(JSON_START);
return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
}
/** /**
* Parse headers into key value object * Parse headers into key value object
* *
...@@ -8608,16 +8638,17 @@ function headersGetter(headers) { ...@@ -8608,16 +8638,17 @@ function headersGetter(headers) {
* This function is used for both request and response transforming * This function is used for both request and response transforming
* *
* @param {*} data Data to transform. * @param {*} data Data to transform.
* @param {function(string=)} headers Http headers getter fn. * @param {function(string=)} headers HTTP headers getter fn.
* @param {number} status HTTP status code of the response.
* @param {(Function|Array.<Function>)} fns Function or an array of functions. * @param {(Function|Array.<Function>)} fns Function or an array of functions.
* @returns {*} Transformed data. * @returns {*} Transformed data.
*/ */
function transformData(data, headers, fns) { function transformData(data, headers, status, fns) {
if (isFunction(fns)) if (isFunction(fns))
return fns(data, headers); return fns(data, headers, status);
forEach(fns, function(fn) { forEach(fns, function(fn) {
data = fn(data, headers); data = fn(data, headers, status);
}); });
return data; return data;
...@@ -8669,7 +8700,7 @@ function $HttpProvider() { ...@@ -8669,7 +8700,7 @@ function $HttpProvider() {
// transform outgoing request data // transform outgoing request data
transformRequest: [function(d) { transformRequest: [function(d) {
return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d; return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
}], }],
// default headers // default headers
...@@ -8896,7 +8927,7 @@ function $HttpProvider() { ...@@ -8896,7 +8927,7 @@ function $HttpProvider() {
* *
* Both requests and responses can be transformed using transformation functions: `transformRequest` * Both requests and responses can be transformed using transformation functions: `transformRequest`
* and `transformResponse`. These properties can be a single function that returns * and `transformResponse`. These properties can be a single function that returns
* the transformed value (`{function(data, headersGetter)`) or an array of such transformation functions, * the transformed value (`{function(data, headersGetter, status)`) or an array of such transformation functions,
* which allows you to `push` or `unshift` a new transformation function into the transformation chain. * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
* *
* ### Default Transformations * ### Default Transformations
...@@ -9140,9 +9171,9 @@ function $HttpProvider() { ...@@ -9140,9 +9171,9 @@ function $HttpProvider() {
* See {@link ng.$http#overriding-the-default-transformations-per-request * See {@link ng.$http#overriding-the-default-transformations-per-request
* Overriding the Default Transformations} * Overriding the Default Transformations}
* - **transformResponse** – * - **transformResponse** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
* transform function or an array of such functions. The transform function takes the http * transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version. * response body, headers and status and returns its transformed (typically deserialized) version.
* See {@link ng.$http#overriding-the-default-transformations-per-request * See {@link ng.$http#overriding-the-default-transformations-per-request
* Overriding the Default Transformations} * Overriding the Default Transformations}
* - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
...@@ -9265,24 +9296,23 @@ function $HttpProvider() { ...@@ -9265,24 +9296,23 @@ function $HttpProvider() {
</example> </example>
*/ */
function $http(requestConfig) { function $http(requestConfig) {
var config = {
method: 'get',
transformRequest: defaults.transformRequest,
transformResponse: defaults.transformResponse
};
var headers = mergeHeaders(requestConfig);
if (!angular.isObject(requestConfig)) { if (!angular.isObject(requestConfig)) {
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
} }
extend(config, requestConfig); var config = extend({
config.headers = headers; method: 'get',
transformRequest: defaults.transformRequest,
transformResponse: defaults.transformResponse
}, requestConfig);
config.headers = mergeHeaders(requestConfig);
config.method = uppercase(config.method); config.method = uppercase(config.method);
var serverRequest = function(config) { var serverRequest = function(config) {
headers = config.headers; var headers = config.headers;
var reqData = transformData(config.data, headersGetter(headers), config.transformRequest); var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
// strip content-type if data is undefined // strip content-type if data is undefined
if (isUndefined(reqData)) { if (isUndefined(reqData)) {
...@@ -9298,7 +9328,7 @@ function $HttpProvider() { ...@@ -9298,7 +9328,7 @@ function $HttpProvider() {
} }
// send request // send request
return sendReq(config, reqData, headers).then(transformResponse, transformResponse); return sendReq(config, reqData).then(transformResponse, transformResponse);
}; };
var chain = [serverRequest, undefined]; var chain = [serverRequest, undefined];
...@@ -9343,13 +9373,30 @@ function $HttpProvider() { ...@@ -9343,13 +9373,30 @@ function $HttpProvider() {
if (!response.data) { if (!response.data) {
resp.data = response.data; resp.data = response.data;
} else { } else {
resp.data = transformData(response.data, response.headers, config.transformResponse); resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
} }
return (isSuccess(response.status)) return (isSuccess(response.status))
? resp ? resp
: $q.reject(resp); : $q.reject(resp);
} }
function executeHeaderFns(headers) {
var headerContent, processedHeaders = {};
forEach(headers, function(headerFn, header) {
if (isFunction(headerFn)) {
headerContent = headerFn();
if (headerContent != null) {
processedHeaders[header] = headerContent;
}
} else {
processedHeaders[header] = headerFn;
}
});
return processedHeaders;
}
function mergeHeaders(config) { function mergeHeaders(config) {
var defHeaders = defaults.headers, var defHeaders = defaults.headers,
reqHeaders = extend({}, config.headers), reqHeaders = extend({}, config.headers),
...@@ -9372,23 +9419,7 @@ function $HttpProvider() { ...@@ -9372,23 +9419,7 @@ function $HttpProvider() {
} }
// execute if header value is a function for merged headers // execute if header value is a function for merged headers
execHeaders(reqHeaders); return executeHeaderFns(reqHeaders);
return reqHeaders;
function execHeaders(headers) {
var headerContent;
forEach(headers, function(headerFn, header) {
if (isFunction(headerFn)) {
headerContent = headerFn();
if (headerContent != null) {
headers[header] = headerContent;
} else {
delete headers[header];
}
}
});
}
} }
} }
...@@ -9531,11 +9562,12 @@ function $HttpProvider() { ...@@ -9531,11 +9562,12 @@ function $HttpProvider() {
* !!! ACCESSES CLOSURE VARS: * !!! ACCESSES CLOSURE VARS:
* $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
*/ */
function sendReq(config, reqData, reqHeaders) { function sendReq(config, reqData) {
var deferred = $q.defer(), var deferred = $q.defer(),
promise = deferred.promise, promise = deferred.promise,
cache, cache,
cachedResp, cachedResp,
reqHeaders = config.headers,
url = buildUrl(config.url, config.params); url = buildUrl(config.url, config.params);
$http.pendingRequests.push(config); $http.pendingRequests.push(config);
...@@ -11235,8 +11267,8 @@ function $LocationProvider() { ...@@ -11235,8 +11267,8 @@ function $LocationProvider() {
* @param {string=} oldState History state object that was before it was changed. * @param {string=} oldState History state object that was before it was changed.
*/ */
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
function($rootScope, $browser, $sniffer, $rootElement) { function($rootScope, $browser, $sniffer, $rootElement, $window) {
var $location, var $location,
LocationMode, LocationMode,
baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
...@@ -11318,7 +11350,7 @@ function $LocationProvider() { ...@@ -11318,7 +11350,7 @@ function $LocationProvider() {
if ($location.absUrl() != $browser.url()) { if ($location.absUrl() != $browser.url()) {
$rootScope.$apply(); $rootScope.$apply();
// hack to work around FF6 bug 684208 when scenario runner clicks on links // hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true; $window.angular['ff-684208-preventDefault'] = true;
} }
} }
} }
...@@ -11934,6 +11966,8 @@ Parser.prototype = { ...@@ -11934,6 +11966,8 @@ Parser.prototype = {
primary = this.arrayDeclaration(); primary = this.arrayDeclaration();
} else if (this.expect('{')) { } else if (this.expect('{')) {
primary = this.object(); primary = this.object();
} else if (this.peek().identifier && this.peek().text in CONSTANTS) {
primary = CONSTANTS[this.consume().text];
} else if (this.peek().identifier) { } else if (this.peek().identifier) {
primary = this.identifier(); primary = this.identifier();
} else if (this.peek().constant) { } else if (this.peek().constant) {
...@@ -12036,7 +12070,7 @@ Parser.prototype = { ...@@ -12036,7 +12070,7 @@ Parser.prototype = {
id += this.consume().text + this.consume().text; id += this.consume().text + this.consume().text;
} }
return CONSTANTS[id] || getterFn(id, this.options, this.text); return getterFn(id, this.options, this.text);
}, },
constant: function() { constant: function() {
...@@ -12226,17 +12260,16 @@ Parser.prototype = { ...@@ -12226,17 +12260,16 @@ Parser.prototype = {
}, },
fieldAccess: function(object) { fieldAccess: function(object) {
var expression = this.text; var getter = this.identifier();
var field = this.consume().text;
var getter = getterFn(field, this.options, expression);
return extend(function $parseFieldAccess(scope, locals, self) { return extend(function $parseFieldAccess(scope, locals, self) {
return getter(self || object(scope, locals)); var o = self || object(scope, locals);
return (o == null) ? undefined : getter(o);
}, { }, {
assign: function(scope, value, locals) { assign: function(scope, value, locals) {
var o = object(scope, locals); var o = object(scope, locals);
if (!o) object.assign(scope, o = {}); if (!o) object.assign(scope, o = {});
return setter(o, field, value, expression); return getter.assign(o, value);
} }
}); });
}, },
...@@ -13394,12 +13427,10 @@ function qFactory(nextTick, exceptionHandler) { ...@@ -13394,12 +13427,10 @@ function qFactory(nextTick, exceptionHandler) {
function $$RAFProvider() { //rAF function $$RAFProvider() { //rAF
this.$get = ['$window', '$timeout', function($window, $timeout) { this.$get = ['$window', '$timeout', function($window, $timeout) {
var requestAnimationFrame = $window.requestAnimationFrame || var requestAnimationFrame = $window.requestAnimationFrame ||
$window.webkitRequestAnimationFrame || $window.webkitRequestAnimationFrame;
$window.mozRequestAnimationFrame;
var cancelAnimationFrame = $window.cancelAnimationFrame || var cancelAnimationFrame = $window.cancelAnimationFrame ||
$window.webkitCancelAnimationFrame || $window.webkitCancelAnimationFrame ||
$window.mozCancelAnimationFrame ||
$window.webkitCancelRequestAnimationFrame; $window.webkitCancelRequestAnimationFrame;
var rafSupported = !!requestAnimationFrame; var rafSupported = !!requestAnimationFrame;
...@@ -13528,7 +13559,6 @@ function $RootScopeProvider() { ...@@ -13528,7 +13559,6 @@ function $RootScopeProvider() {
var child = parent.$new(); var child = parent.$new();
parent.salutation = "Hello"; parent.salutation = "Hello";
child.name = "World";
expect(child.salutation).toEqual('Hello'); expect(child.salutation).toEqual('Hello');
child.salutation = "Welcome"; child.salutation = "Welcome";
...@@ -14166,7 +14196,7 @@ function $RootScopeProvider() { ...@@ -14166,7 +14196,7 @@ function $RootScopeProvider() {
while (asyncQueue.length) { while (asyncQueue.length) {
try { try {
asyncTask = asyncQueue.shift(); asyncTask = asyncQueue.shift();
asyncTask.scope.$eval(asyncTask.expression); asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
} catch (e) { } catch (e) {
$exceptionHandler(e); $exceptionHandler(e);
} }
...@@ -14381,8 +14411,9 @@ function $RootScopeProvider() { ...@@ -14381,8 +14411,9 @@ function $RootScopeProvider() {
* - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with the current `scope` parameter. * - `function(scope)`: execute the function with the current `scope` parameter.
* *
* @param {(object)=} locals Local variables object, useful for overriding values in scope.
*/ */
$evalAsync: function(expr) { $evalAsync: function(expr, locals) {
// if we are outside of an $digest loop and this is the first time we are scheduling async // if we are outside of an $digest loop and this is the first time we are scheduling async
// task also schedule async auto-flush // task also schedule async auto-flush
if (!$rootScope.$$phase && !asyncQueue.length) { if (!$rootScope.$$phase && !asyncQueue.length) {
...@@ -14393,7 +14424,7 @@ function $RootScopeProvider() { ...@@ -14393,7 +14424,7 @@ function $RootScopeProvider() {
}); });
} }
asyncQueue.push({scope: this, expression: expr}); asyncQueue.push({scope: this, expression: expr, locals: locals});
}, },
$$postDigest: function(fn) { $$postDigest: function(fn) {
...@@ -15965,7 +15996,7 @@ var $compileMinErr = minErr('$compile'); ...@@ -15965,7 +15996,7 @@ var $compileMinErr = minErr('$compile');
* @description * @description
* The `$templateRequest` service downloads the provided template using `$http` and, upon success, * The `$templateRequest` service downloads the provided template using `$http` and, upon success,
* stores the contents inside of `$templateCache`. If the HTTP request fails or the response data * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
* of the HTTP request is empty then a `$compile` error will be thrown (the exception can be thwarted * of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted
* by setting the 2nd parameter of the function to true). * by setting the 2nd parameter of the function to true).
* *
* @param {string} tpl The HTTP request template URL * @param {string} tpl The HTTP request template URL
...@@ -16525,19 +16556,26 @@ function $FilterProvider($provide) { ...@@ -16525,19 +16556,26 @@ function $FilterProvider($provide) {
* *
* Can be one of: * Can be one of:
* *
* - `string`: The string is evaluated as an expression and the resulting value is used for substring match against * - `string`: The string is used for matching against the contents of the `array`. All strings or
* the contents of the `array`. All strings or objects with string properties in `array` that contain this string * objects with string properties in `array` that match this string will be returned. This also
* will be returned. The predicate can be negated by prefixing the string with `!`. * applies to nested object properties.
* The predicate can be negated by prefixing the string with `!`.
* *
* - `Object`: A pattern object can be used to filter specific properties on objects contained * - `Object`: A pattern object can be used to filter specific properties on objects contained
* by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
* which have property `name` containing "M" and property `phone` containing "1". A special * which have property `name` containing "M" and property `phone` containing "1". A special
* property name `$` can be used (as in `{$:"text"}`) to accept a match against any * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
* property of the object. That's equivalent to the simple substring match with a `string` * property of the object or its nested object properties. That's equivalent to the simple
* as described above. The predicate can be negated by prefixing the string with `!`. * substring match with a `string` as described above. The predicate can be negated by prefixing
* For Example `{name: "!M"}` predicate will return an array of items which have property `name` * the string with `!`.
* For example `{name: "!M"}` predicate will return an array of items which have property `name`
* not containing "M". * not containing "M".
* *
* Note that a named property will match properties on the same level only, while the special
* `$` property will match properties on the same level or deeper. E.g. an array item like
* `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
* **will** be matched by `{$: 'John'}`.
*
* - `function(value, index)`: A predicate function can be used to write arbitrary filters. The * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
* function is called for each element of `array`. The final result is an array of those * function is called for each element of `array`. The final result is an array of those
* elements that the predicate returned true for. * elements that the predicate returned true for.
...@@ -16550,10 +16588,10 @@ function $FilterProvider($provide) { ...@@ -16550,10 +16588,10 @@ function $FilterProvider($provide) {
* *
* - `function(actual, expected)`: * - `function(actual, expected)`:
* The function will be given the object value and the predicate value to compare and * The function will be given the object value and the predicate value to compare and
* should return true if the item should be included in filtered result. * should return true if both values should be considered equal.
* *
* - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`. * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
* this is essentially strict comparison of expected and actual. * This is essentially strict comparison of expected and actual.
* *
* - `false|undefined`: A short hand for a function which will look for a substring match in case * - `false|undefined`: A short hand for a function which will look for a substring match in case
* insensitive way. * insensitive way.
...@@ -16656,6 +16694,7 @@ function filterFilter() { ...@@ -16656,6 +16694,7 @@ function filterFilter() {
// Helper functions for `filterFilter` // Helper functions for `filterFilter`
function createPredicateFn(expression, comparator, matchAgainstAnyProp) { function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
var predicateFn; var predicateFn;
if (comparator === true) { if (comparator === true) {
...@@ -16674,13 +16713,16 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) { ...@@ -16674,13 +16713,16 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
} }
predicateFn = function(item) { predicateFn = function(item) {
if (shouldMatchPrimitives && !isObject(item)) {
return deepCompare(item, expression.$, comparator, false);
}
return deepCompare(item, expression, comparator, matchAgainstAnyProp); return deepCompare(item, expression, comparator, matchAgainstAnyProp);
}; };
return predicateFn; return predicateFn;
} }
function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
var actualType = typeof actual; var actualType = typeof actual;
var expectedType = typeof expected; var expectedType = typeof expected;
...@@ -16699,11 +16741,11 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { ...@@ -16699,11 +16741,11 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
var key; var key;
if (matchAgainstAnyProp) { if (matchAgainstAnyProp) {
for (key in actual) { for (key in actual) {
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator)) { if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
return true; return true;
} }
} }
return false; return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
} else if (expectedType === 'object') { } else if (expectedType === 'object') {
for (key in expected) { for (key in expected) {
var expectedVal = expected[key]; var expectedVal = expected[key];
...@@ -16711,9 +16753,9 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) { ...@@ -16711,9 +16753,9 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
continue; continue;
} }
var keyIsDollar = key === '$'; var matchAnyProperty = key === '$';
var actualVal = keyIsDollar ? actual : actual[key]; var actualVal = matchAnyProperty ? actual : actual[key];
if (!deepCompare(actualVal, expectedVal, comparator, keyIsDollar)) { if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
return false; return false;
} }
} }
...@@ -17085,8 +17127,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d ...@@ -17085,8 +17127,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d
* * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
* * `'a'`: AM/PM marker * * `'a'`: AM/PM marker
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
* * `'ww'`: ISO-8601 week of year (00-53) * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
* * `'w'`: ISO-8601 week of year (0-53) * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
* *
* `format` string can also be one of the following predefined * `format` string can also be one of the following predefined
* {@link guide/i18n localizable formats}: * {@link guide/i18n localizable formats}:
...@@ -17386,8 +17428,7 @@ function limitToFilter() { ...@@ -17386,8 +17428,7 @@ function limitToFilter() {
} }
} }
var out = [], var i, n;
i, n;
// if abs(limit) exceeds maximum length, trim it // if abs(limit) exceeds maximum length, trim it
if (limit > input.length) if (limit > input.length)
...@@ -17399,15 +17440,14 @@ function limitToFilter() { ...@@ -17399,15 +17440,14 @@ function limitToFilter() {
i = 0; i = 0;
n = limit; n = limit;
} else { } else {
// zero and NaN check on limit - return empty array
if (!limit) return [];
i = input.length + limit; i = input.length + limit;
n = input.length; n = input.length;
} }
for (; i < n; i++) { return input.slice(i, n);
out.push(input[i]);
}
return out;
}; };
} }
...@@ -17541,9 +17581,7 @@ function orderByFilter($parse) { ...@@ -17541,9 +17581,7 @@ function orderByFilter($parse) {
} }
if (predicate === '') { if (predicate === '') {
// Effectively no predicate was passed so we compare identity // Effectively no predicate was passed so we compare identity
return reverseComparator(function(a, b) { return reverseComparator(compare, descending);
return compare(a, b);
}, descending);
} }
get = $parse(predicate); get = $parse(predicate);
if (get.constant) { if (get.constant) {
...@@ -17571,29 +17609,37 @@ function orderByFilter($parse) { ...@@ -17571,29 +17609,37 @@ function orderByFilter($parse) {
? function(a, b) {return comp(b,a);} ? function(a, b) {return comp(b,a);}
: comp; : comp;
} }
function isPrimitive(value) {
switch (typeof value) {
case 'number': /* falls through */
case 'boolean': /* falls through */
case 'string':
return true;
default:
return false;
}
}
function objectToString(value) {
if (value === null) return 'null';
if (typeof value.valueOf === 'function') {
value = value.valueOf();
if (isPrimitive(value)) return value;
}
if (typeof value.toString === 'function') {
value = value.toString();
if (isPrimitive(value)) return value;
}
return '';
}
function compare(v1, v2) { function compare(v1, v2) {
var t1 = typeof v1; var t1 = typeof v1;
var t2 = typeof v2; var t2 = typeof v2;
// Prepare values for Abstract Relational Comparison
// (http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5):
// If the resulting values are identical, return 0 to prevent
// incorrect re-ordering.
if (t1 === t2 && t1 === "object") { if (t1 === t2 && t1 === "object") {
// If types are both numbers, emulate abstract ToPrimitive() operation v1 = objectToString(v1);
// in order to get primitive values suitable for comparison v2 = objectToString(v2);
t1 = typeof (v1.valueOf ? v1 = v1.valueOf() : v1);
t2 = typeof (v2.valueOf ? v2 = v2.valueOf() : v2);
if (t1 === t2 && t1 === "object") {
// Object.prototype.valueOf will return the original object, by
// default. If we do not receive a primitive value, use ToString()
// instead.
t1 = typeof (v1.toString ? v1 = v1.toString() : v1);
t2 = typeof (v2.toString ? v2 = v2.toString() : v2);
// If the end result of toString() for each item is the same, do not
// perform relational comparison, and do not re-order objects.
if (t1 === t2 && v1 === v2 || t1 === "object") return 0;
}
} }
if (t1 === t2) { if (t1 === t2) {
if (t1 === "string") { if (t1 === "string") {
...@@ -24327,7 +24373,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { ...@@ -24327,7 +24373,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
var aliasAs = match[3]; var aliasAs = match[3];
var trackByExp = match[4]; var trackByExp = match[4];
match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
if (!match) { if (!match) {
throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
...@@ -25236,14 +25282,15 @@ var ngOptionsMinErr = minErr('ngOptions'); ...@@ -25236,14 +25282,15 @@ var ngOptionsMinErr = minErr('ngOptions');
* *
* The `ngOptions` attribute can be used to dynamically generate a list of `<option>` * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
* elements for the `<select>` element using the array or object obtained by evaluating the * elements for the `<select>` element using the array or object obtained by evaluating the
* `ngOptions` comprehension_expression. * `ngOptions` comprehension expression.
* *
* In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
* similar result. However, the `ngOptions` provides some benefits such as reducing memory and * similar result. However, `ngOptions` provides some benefits such as reducing memory and
* increasing speed by not creating a new scope for each repeated instance, as well as providing * increasing speed by not creating a new scope for each repeated instance, as well as providing
* more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions` should be * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
* used when the `select` model needs to be bound to a non-string value. This is because an option * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
* element can only be bound to string values at present. * to a non-string value. This is because an option element can only be bound to string values at
* present.
* *
* When an item in the `<select>` menu is selected, the array element or object property * When an item in the `<select>` menu is selected, the array element or object property
* represented by the selected option will be bound to the model identified by the `ngModel` * represented by the selected option will be bound to the model identified by the `ngModel`
...@@ -25258,28 +25305,51 @@ var ngOptionsMinErr = minErr('ngOptions'); ...@@ -25258,28 +25305,51 @@ var ngOptionsMinErr = minErr('ngOptions');
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
* </div> * </div>
* *
* ## `select as` * ## `select` **`as`**
* *
* Using `select as` will bind the result of the `select as` expression to the model, but * Using `select` **`as`** will bind the result of the `select` expression to the model, but
* the value of the `<select>` and `<option>` html elements will be either the index (for array data sources) * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
* or property name (for object data sources) of the value within the collection. If a `track by` expression * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
* is used, the result of that expression will be set as the value of the `option` and `select` elements. * is used, the result of that expression will be set as the value of the `option` and `select` elements.
* *
* ### `select as` with `track by` *
* * ### `select` **`as`** and **`track by`**
* Using `select as` together with `track by` is not recommended. Reasoning: *
* * <div class="alert alert-warning">
* - Example: &lt;select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"&gt; * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
* values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItem'}}], * </div>
* $scope.selected = {name: 'aSubItem'}; *
* - track by is always applied to `value`, with the purpose of preserving the selection, * Consider the following example:
* (to `item` in this case) *
* - to calculate whether an item is selected we do the following: * ```html
* 1. apply `track by` to the values in the array, e.g. * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
* In the example: [1,2] * ```
* 2. apply `track by` to the already selected value in `ngModel`: *
* In the example: this is not possible, as `track by` refers to `item.id`, but the selected * ```js
* value from `ngModel` is `{name: aSubItem}`. * $scope.values = [{
* id: 1,
* label: 'aLabel',
* subItem: { name: 'aSubItem' }
* }, {
* id: 2,
* label: 'bLabel',
* subItem: { name: 'bSubItem' }
* }];
*
* $scope.selected = { name: 'aSubItem' };
* ```
*
* With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
* of the data source (to `item` in this example). To calculate whether an element is selected, we do the
* following:
*
* 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
* 2. Apply **`track by`** to the already selected value in `ngModel`.
* In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
* value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
* a wrong object, the selected element can't be found, `<select>` is always reset to the "not
* selected" option.
*
* *
* @param {string} ngModel Assignable angular expression to data-bind to. * @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=} name Property name of the form under which the control is published.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment