Commit d9cc23ab authored by Pascal Hartig's avatar Pascal Hartig

AngularJS: Upgrade to 1.3.8

parent 762f2d29
......@@ -2,11 +2,11 @@
"name": "todomvc-angular",
"version": "0.0.0",
"dependencies": {
"angular": "1.3.6",
"angular": "1.3.8",
"todomvc-common": "~0.3.0"
},
"devDependencies": {
"angular-mocks": "1.3.6",
"angular-route": "1.3.6"
"angular-mocks": "1.3.8",
"angular-route": "1.3.8"
}
}
/**
* @license AngularJS v1.3.6
* @license AngularJS v1.3.8
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
......
/**
* @license AngularJS v1.3.6
* @license AngularJS v1.3.8
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
......@@ -54,7 +54,7 @@ function minErr(module, ErrorConstructor) {
return match;
});
message = message + '\nhttp://errors.angularjs.org/1.3.6/' +
message = message + '\nhttp://errors.angularjs.org/1.3.8/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
......@@ -109,6 +109,7 @@ function minErr(module, ErrorConstructor) {
isWindow: true,
isScope: true,
isFile: true,
isFormData: true,
isBlob: true,
isBoolean: true,
isPromiseLike: true,
......@@ -464,6 +465,8 @@ noop.$inject = [];
return (transformationFn || angular.identity)(value);
};
```
* @param {*} value to be returned.
* @returns {*} the value passed in.
*/
function identity($) {return $;}
identity.$inject = [];
......@@ -630,6 +633,11 @@ function isFile(obj) {
}
function isFormData(obj) {
return toString.call(obj) === '[object FormData]';
}
function isBlob(obj) {
return toString.call(obj) === '[object Blob]';
}
......@@ -713,7 +721,7 @@ function arrayRemove(array, value) {
* 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 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.
* * 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.
......@@ -1051,7 +1059,7 @@ function toJson(obj, pretty) {
* Deserializes a JSON string.
*
* @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) {
return isString(json)
......@@ -1224,7 +1232,7 @@ function getNgAttribute(element, ngAttr) {
* {@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
* 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
* contain the code. See {@link angular.module} for more information.
*
......@@ -1232,7 +1240,7 @@ function getNgAttribute(element, ngAttr) {
* document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
* 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">
<file name="index.html">
......@@ -1492,7 +1500,12 @@ function reloadWithDebugInfo() {
* @param {DOMElement} element DOM element which is the root of angular application.
*/
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;
......@@ -2105,11 +2118,11 @@ function toDebugString(obj) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
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
minor: 3,
dot: 6,
codeName: 'robofunky-danceblaster'
dot: 8,
codeName: 'prophetic-narwhal'
};
......@@ -7122,7 +7135,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// support ngAttr attribute binding
ngAttrName = directiveNormalize(name);
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)$/, '');
......@@ -8034,7 +8050,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
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
if (!interpolateFn) return;
......@@ -8059,15 +8078,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
"ng- versions (such as ng-click instead of onclick) instead.");
}
// If the attribute was removed, then we are done
if (!attr[name]) {
return;
}
// we need to interpolate again, in case the attribute value has been updated
// If the attribute has changed since last $interpolate()ed
var newValue = attr[name];
if (newValue !== value) {
// we need to interpolate again since 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);
// ensure unset/empty values make interpolateFn falsy
interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
value = newValue;
}
// if attribute was updated so that there is no interpolation going on we don't want to
// register any observers
......@@ -8529,23 +8548,34 @@ function $ExceptionHandlerProvider() {
var APPLICATION_JSON = 'application/json';
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
var JSON_START = /^\s*(\[|\{[^\{])/;
var JSON_END = /[\}\]]\s*$/;
var JSON_START = /^\[|^\{(?!\{)/;
var JSON_ENDS = {
'[': /]$/,
'{': /}$/
};
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
function defaultHttpResponseTransform(data, headers) {
if (isString(data)) {
// strip json vulnerability protection prefix
data = data.replace(JSON_PROTECTION_PREFIX, '');
// Strip json vulnerability protection prefix and trim whitespace
var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
if (tempData) {
var contentType = headers('Content-Type');
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) ||
(JSON_START.test(data) && JSON_END.test(data))) {
data = fromJson(data);
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
data = fromJson(tempData);
}
}
}
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
*
......@@ -8608,16 +8638,17 @@ function headersGetter(headers) {
* This function is used for both request and response transforming
*
* @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.
* @returns {*} Transformed data.
*/
function transformData(data, headers, fns) {
function transformData(data, headers, status, fns) {
if (isFunction(fns))
return fns(data, headers);
return fns(data, headers, status);
forEach(fns, function(fn) {
data = fn(data, headers);
data = fn(data, headers, status);
});
return data;
......@@ -8669,7 +8700,7 @@ function $HttpProvider() {
// transform outgoing request data
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
......@@ -8896,7 +8927,7 @@ function $HttpProvider() {
*
* Both requests and responses can be transformed using transformation functions: `transformRequest`
* 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.
*
* ### Default Transformations
......@@ -9140,9 +9171,9 @@ function $HttpProvider() {
* See {@link ng.$http#overriding-the-default-transformations-per-request
* Overriding the Default Transformations}
* - **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
* 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
* Overriding the Default Transformations}
* - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
......@@ -9265,24 +9296,23 @@ function $HttpProvider() {
</example>
*/
function $http(requestConfig) {
var config = {
method: 'get',
transformRequest: defaults.transformRequest,
transformResponse: defaults.transformResponse
};
var headers = mergeHeaders(requestConfig);
if (!angular.isObject(requestConfig)) {
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
}
extend(config, requestConfig);
config.headers = headers;
var config = extend({
method: 'get',
transformRequest: defaults.transformRequest,
transformResponse: defaults.transformResponse
}, requestConfig);
config.headers = mergeHeaders(requestConfig);
config.method = uppercase(config.method);
var serverRequest = function(config) {
headers = config.headers;
var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
var headers = config.headers;
var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
// strip content-type if data is undefined
if (isUndefined(reqData)) {
......@@ -9298,7 +9328,7 @@ function $HttpProvider() {
}
// send request
return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
return sendReq(config, reqData).then(transformResponse, transformResponse);
};
var chain = [serverRequest, undefined];
......@@ -9343,13 +9373,30 @@ function $HttpProvider() {
if (!response.data) {
resp.data = response.data;
} 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))
? 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) {
var defHeaders = defaults.headers,
reqHeaders = extend({}, config.headers),
......@@ -9372,23 +9419,7 @@ function $HttpProvider() {
}
// execute if header value is a function for merged headers
execHeaders(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];
}
}
});
}
return executeHeaderFns(reqHeaders);
}
}
......@@ -9531,11 +9562,12 @@ function $HttpProvider() {
* !!! ACCESSES CLOSURE VARS:
* $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
*/
function sendReq(config, reqData, reqHeaders) {
function sendReq(config, reqData) {
var deferred = $q.defer(),
promise = deferred.promise,
cache,
cachedResp,
reqHeaders = config.headers,
url = buildUrl(config.url, config.params);
$http.pendingRequests.push(config);
......@@ -11235,8 +11267,8 @@ function $LocationProvider() {
* @param {string=} oldState History state object that was before it was changed.
*/
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
function($rootScope, $browser, $sniffer, $rootElement) {
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
function($rootScope, $browser, $sniffer, $rootElement, $window) {
var $location,
LocationMode,
baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
......@@ -11318,7 +11350,7 @@ function $LocationProvider() {
if ($location.absUrl() != $browser.url()) {
$rootScope.$apply();
// 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 = {
primary = this.arrayDeclaration();
} else if (this.expect('{')) {
primary = this.object();
} else if (this.peek().identifier && this.peek().text in CONSTANTS) {
primary = CONSTANTS[this.consume().text];
} else if (this.peek().identifier) {
primary = this.identifier();
} else if (this.peek().constant) {
......@@ -12036,7 +12070,7 @@ Parser.prototype = {
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() {
......@@ -12226,17 +12260,16 @@ Parser.prototype = {
},
fieldAccess: function(object) {
var expression = this.text;
var field = this.consume().text;
var getter = getterFn(field, this.options, expression);
var getter = this.identifier();
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) {
var o = object(scope, locals);
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) {
function $$RAFProvider() { //rAF
this.$get = ['$window', '$timeout', function($window, $timeout) {
var requestAnimationFrame = $window.requestAnimationFrame ||
$window.webkitRequestAnimationFrame ||
$window.mozRequestAnimationFrame;
$window.webkitRequestAnimationFrame;
var cancelAnimationFrame = $window.cancelAnimationFrame ||
$window.webkitCancelAnimationFrame ||
$window.mozCancelAnimationFrame ||
$window.webkitCancelRequestAnimationFrame;
var rafSupported = !!requestAnimationFrame;
......@@ -13528,7 +13559,6 @@ function $RootScopeProvider() {
var child = parent.$new();
parent.salutation = "Hello";
child.name = "World";
expect(child.salutation).toEqual('Hello');
child.salutation = "Welcome";
......@@ -14166,7 +14196,7 @@ function $RootScopeProvider() {
while (asyncQueue.length) {
try {
asyncTask = asyncQueue.shift();
asyncTask.scope.$eval(asyncTask.expression);
asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
} catch (e) {
$exceptionHandler(e);
}
......@@ -14381,8 +14411,9 @@ function $RootScopeProvider() {
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `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
// task also schedule async auto-flush
if (!$rootScope.$$phase && !asyncQueue.length) {
......@@ -14393,7 +14424,7 @@ function $RootScopeProvider() {
});
}
asyncQueue.push({scope: this, expression: expr});
asyncQueue.push({scope: this, expression: expr, locals: locals});
},
$$postDigest: function(fn) {
......@@ -15965,7 +15996,7 @@ var $compileMinErr = minErr('$compile');
* @description
* 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
* 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).
*
* @param {string} tpl The HTTP request template URL
......@@ -16525,19 +16556,26 @@ function $FilterProvider($provide) {
*
* Can be one of:
*
* - `string`: The string is evaluated as an expression and the resulting value is used for substring match against
* the contents of the `array`. All strings or objects with string properties in `array` that contain this string
* will be returned. The predicate can be negated by prefixing the string with `!`.
* - `string`: The string is used for matching against the contents of the `array`. All strings or
* objects with string properties in `array` that match this string will be returned. This also
* 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
* 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
* 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`
* as described above. The predicate can be negated by prefixing the string with `!`.
* For Example `{name: "!M"}` predicate will return an array of items which have property `name`
* property of the object or its nested object properties. That's equivalent to the simple
* substring match with a `string` as described above. The predicate can be negated by prefixing
* the string with `!`.
* For example `{name: "!M"}` predicate will return an array of items which have property `name`
* 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 is called for each element of `array`. The final result is an array of those
* elements that the predicate returned true for.
......@@ -16550,10 +16588,10 @@ function $FilterProvider($provide) {
*
* - `function(actual, expected)`:
* 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)}`.
* this is essentially strict comparison of expected and actual.
* - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
* 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
* insensitive way.
......@@ -16656,6 +16694,7 @@ function filterFilter() {
// Helper functions for `filterFilter`
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
var predicateFn;
if (comparator === true) {
......@@ -16674,13 +16713,16 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
}
predicateFn = function(item) {
if (shouldMatchPrimitives && !isObject(item)) {
return deepCompare(item, expression.$, comparator, false);
}
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
};
return predicateFn;
}
function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
var actualType = typeof actual;
var expectedType = typeof expected;
......@@ -16699,11 +16741,11 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
var key;
if (matchAgainstAnyProp) {
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 false;
return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
} else if (expectedType === 'object') {
for (key in expected) {
var expectedVal = expected[key];
......@@ -16711,9 +16753,9 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp) {
continue;
}
var keyIsDollar = key === '$';
var actualVal = keyIsDollar ? actual : actual[key];
if (!deepCompare(actualVal, expectedVal, comparator, keyIsDollar)) {
var matchAnyProperty = key === '$';
var actualVal = matchAnyProperty ? actual : actual[key];
if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
return false;
}
}
......@@ -17085,8 +17127,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d
* * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
* * `'a'`: AM/PM marker
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
* * `'ww'`: ISO-8601 week of year (00-53)
* * `'w'`: ISO-8601 week of year (0-53)
* * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
* * `'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
* {@link guide/i18n localizable formats}:
......@@ -17386,8 +17428,7 @@ function limitToFilter() {
}
}
var out = [],
i, n;
var i, n;
// if abs(limit) exceeds maximum length, trim it
if (limit > input.length)
......@@ -17399,15 +17440,14 @@ function limitToFilter() {
i = 0;
n = limit;
} else {
// zero and NaN check on limit - return empty array
if (!limit) return [];
i = input.length + limit;
n = input.length;
}
for (; i < n; i++) {
out.push(input[i]);
}
return out;
return input.slice(i, n);
};
}
......@@ -17541,9 +17581,7 @@ function orderByFilter($parse) {
}
if (predicate === '') {
// Effectively no predicate was passed so we compare identity
return reverseComparator(function(a, b) {
return compare(a, b);
}, descending);
return reverseComparator(compare, descending);
}
get = $parse(predicate);
if (get.constant) {
......@@ -17571,29 +17609,37 @@ function orderByFilter($parse) {
? function(a, b) {return comp(b,a);}
: 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) {
var t1 = typeof v1;
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 types are both numbers, emulate abstract ToPrimitive() operation
// in order to get primitive values suitable for comparison
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;
}
v1 = objectToString(v1);
v2 = objectToString(v2);
}
if (t1 === t2) {
if (t1 === "string") {
......@@ -24327,7 +24373,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
var aliasAs = match[3];
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) {
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');
*
* 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
* `ngOptions` comprehension_expression.
* `ngOptions` comprehension expression.
*
* 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
* more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions` should be
* used when the `select` model needs to be bound to a non-string value. This is because an option
* element can only be bound to string values at present.
* more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
* comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
* 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
* represented by the selected option will be bound to the model identified by the `ngModel`
......@@ -25258,28 +25305,51 @@ var ngOptionsMinErr = minErr('ngOptions');
* array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
* </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)
* 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.
*
* ### `select as` with `track by`
*
* Using `select as` together with `track by` is not recommended. Reasoning:
*
* - Example: &lt;select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"&gt;
* values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItem'}}],
* $scope.selected = {name: 'aSubItem'};
* - track by is always applied to `value`, with the purpose of preserving the selection,
* (to `item` in this case)
* - to calculate whether an item is selected we do the following:
* 1. apply `track by` to the values in the array, e.g.
* 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}`.
*
* ### `select` **`as`** and **`track by`**
*
* <div class="alert alert-warning">
* Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
* </div>
*
* Consider the following example:
*
* ```html
* <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
* ```
*
* ```js
* $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=} 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