angular.js/src/ng/parse.js

1251 lines
38 KiB
JavaScript
Raw Normal View History

'use strict';
var $parseMinErr = minErr('$parse');
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
var promiseWarningCache = {};
var promiseWarning;
// Sandboxing Angular Expressions
// ------------------------------
2013-10-22 21:41:21 +00:00
// Angular expressions are generally considered safe because these expressions only have direct
// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
2013-11-02 20:54:27 +00:00
// obtaining a reference to native JS functions such as the Function constructor, the global Window
feat($parse): secure expressions by hiding "private" properties BREAKING CHANGE: This commit introduces the notion of "private" properties (properties whose names begin and/or end with an underscore) on the scope chain. These properties will not be available to Angular expressions (i.e. {{ }} interpolation in templates and strings passed to `$parse`) They are freely available to JavaScript code (as before). Motivation ---------- Angular expressions execute in a limited context.  They do not have direct access to the global scope, Window, Document or the Function constructor.  However, they have direct access to names/properties on the scope chain.  It has been a long standing best practice to keep sensitive APIs outside of the scope chain (in a closure or your controller.)  That's easier said that done for two reasons: (1) JavaScript does not have a notion of private properties so if you need someone on the scope chain for JavaScript use, you also expose it to Angular expressions, and (2) the new "controller as" syntax that's now in increased usage exposes the entire controller on the scope chain greatly increaing the exposed surface.  Though Angular expressions are written and controlled by the developer, they (1) typically deal with user input and (2) don't get the kind of test coverage that JavaScript code would.  This commit provides a way, via a naming convention, to allow publishing/restricting properties from controllers/scopes to Angular expressions enabling one to only expose those properties that are actually needed by the expressions.
2013-10-18 02:44:36 +00:00
// or Document object. In addition, many powerful functions for use by JavaScript code are
// published on scope that shouldn't be available from within an Angular expression.
//
// As an example, consider the following Angular expression:
//
// {}.toString.constructor(alert("evil JS code"))
//
2013-10-22 21:41:21 +00:00
// We want to prevent this type of access. For the sake of performance, during the lexing phase we
feat($parse): secure expressions by hiding "private" properties BREAKING CHANGE: This commit introduces the notion of "private" properties (properties whose names begin and/or end with an underscore) on the scope chain. These properties will not be available to Angular expressions (i.e. {{ }} interpolation in templates and strings passed to `$parse`) They are freely available to JavaScript code (as before). Motivation ---------- Angular expressions execute in a limited context.  They do not have direct access to the global scope, Window, Document or the Function constructor.  However, they have direct access to names/properties on the scope chain.  It has been a long standing best practice to keep sensitive APIs outside of the scope chain (in a closure or your controller.)  That's easier said that done for two reasons: (1) JavaScript does not have a notion of private properties so if you need someone on the scope chain for JavaScript use, you also expose it to Angular expressions, and (2) the new "controller as" syntax that's now in increased usage exposes the entire controller on the scope chain greatly increaing the exposed surface.  Though Angular expressions are written and controlled by the developer, they (1) typically deal with user input and (2) don't get the kind of test coverage that JavaScript code would.  This commit provides a way, via a naming convention, to allow publishing/restricting properties from controllers/scopes to Angular expressions enabling one to only expose those properties that are actually needed by the expressions.
2013-10-18 02:44:36 +00:00
// disallow any "dotted" access to any member named "constructor" or to any member whose name begins
// or ends with an underscore. The latter allows one to exclude the private / JavaScript only API
// available on the scope and controllers from the context of an Angular expression.
//
feat($parse): secure expressions by hiding "private" properties BREAKING CHANGE: This commit introduces the notion of "private" properties (properties whose names begin and/or end with an underscore) on the scope chain. These properties will not be available to Angular expressions (i.e. {{ }} interpolation in templates and strings passed to `$parse`) They are freely available to JavaScript code (as before). Motivation ---------- Angular expressions execute in a limited context.  They do not have direct access to the global scope, Window, Document or the Function constructor.  However, they have direct access to names/properties on the scope chain.  It has been a long standing best practice to keep sensitive APIs outside of the scope chain (in a closure or your controller.)  That's easier said that done for two reasons: (1) JavaScript does not have a notion of private properties so if you need someone on the scope chain for JavaScript use, you also expose it to Angular expressions, and (2) the new "controller as" syntax that's now in increased usage exposes the entire controller on the scope chain greatly increaing the exposed surface.  Though Angular expressions are written and controlled by the developer, they (1) typically deal with user input and (2) don't get the kind of test coverage that JavaScript code would.  This commit provides a way, via a naming convention, to allow publishing/restricting properties from controllers/scopes to Angular expressions enabling one to only expose those properties that are actually needed by the expressions.
2013-10-18 02:44:36 +00:00
// For reflective calls (a[b]), we check that the value of the lookup is not the Function
// constructor, Window or DOM node while evaluating the expression, which is a stronger but more
// expensive test. Since reflective calls are expensive anyway, this is not such a big deal compared
// to static dereferencing.
//
2013-10-22 21:41:21 +00:00
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
// against the expression language, but not to prevent exploits that were enabled by exposing
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
// practice and therefore we are not even trying to protect against interaction with an object
// explicitly exposed in this way.
//
2013-10-22 21:41:21 +00:00
// A developer could foil the name check by aliasing the Function constructor under a different
// name on the scope.
//
2013-10-22 21:41:21 +00:00
// In general, it is not possible to access a Window object from an angular expression unless a
// window or some DOM object that has a reference to window is published onto a Scope.
feat($parse): secure expressions by hiding "private" properties BREAKING CHANGE: This commit introduces the notion of "private" properties (properties whose names begin and/or end with an underscore) on the scope chain. These properties will not be available to Angular expressions (i.e. {{ }} interpolation in templates and strings passed to `$parse`) They are freely available to JavaScript code (as before). Motivation ---------- Angular expressions execute in a limited context.  They do not have direct access to the global scope, Window, Document or the Function constructor.  However, they have direct access to names/properties on the scope chain.  It has been a long standing best practice to keep sensitive APIs outside of the scope chain (in a closure or your controller.)  That's easier said that done for two reasons: (1) JavaScript does not have a notion of private properties so if you need someone on the scope chain for JavaScript use, you also expose it to Angular expressions, and (2) the new "controller as" syntax that's now in increased usage exposes the entire controller on the scope chain greatly increaing the exposed surface.  Though Angular expressions are written and controlled by the developer, they (1) typically deal with user input and (2) don't get the kind of test coverage that JavaScript code would.  This commit provides a way, via a naming convention, to allow publishing/restricting properties from controllers/scopes to Angular expressions enabling one to only expose those properties that are actually needed by the expressions.
2013-10-18 02:44:36 +00:00
function ensureSafeMemberName(name, fullExpression, allowConstructor) {
if (typeof name !== 'string' && toString.apply(name) !== "[object String]") {
return name;
}
if (name === "constructor" && !allowConstructor) {
throw $parseMinErr('isecfld',
2013-10-22 21:41:21 +00:00
'Referencing "constructor" field in Angular expressions is disallowed! Expression: {0}',
fullExpression);
}
feat($parse): secure expressions by hiding "private" properties BREAKING CHANGE: This commit introduces the notion of "private" properties (properties whose names begin and/or end with an underscore) on the scope chain. These properties will not be available to Angular expressions (i.e. {{ }} interpolation in templates and strings passed to `$parse`) They are freely available to JavaScript code (as before). Motivation ---------- Angular expressions execute in a limited context.  They do not have direct access to the global scope, Window, Document or the Function constructor.  However, they have direct access to names/properties on the scope chain.  It has been a long standing best practice to keep sensitive APIs outside of the scope chain (in a closure or your controller.)  That's easier said that done for two reasons: (1) JavaScript does not have a notion of private properties so if you need someone on the scope chain for JavaScript use, you also expose it to Angular expressions, and (2) the new "controller as" syntax that's now in increased usage exposes the entire controller on the scope chain greatly increaing the exposed surface.  Though Angular expressions are written and controlled by the developer, they (1) typically deal with user input and (2) don't get the kind of test coverage that JavaScript code would.  This commit provides a way, via a naming convention, to allow publishing/restricting properties from controllers/scopes to Angular expressions enabling one to only expose those properties that are actually needed by the expressions.
2013-10-18 02:44:36 +00:00
if (name.charAt(0) === '_' || name.charAt(name.length-1) === '_') {
throw $parseMinErr('isecprv',
'Referencing private fields in Angular expressions is disallowed! Expression: {0}',
fullExpression);
}
return name;
2013-10-22 21:41:21 +00:00
}
function ensureSafeObject(obj, fullExpression) {
// nifty check if obj is Function that is fast and works across iframes and other contexts
if (obj && obj.constructor === obj) {
throw $parseMinErr('isecfn',
2013-10-22 21:41:21 +00:00
'Referencing Function in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (// isWindow(obj)
obj && obj.document && obj.location && obj.alert && obj.setInterval) {
throw $parseMinErr('isecwindow',
2013-10-22 21:41:21 +00:00
'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (// isElement(obj)
obj && (obj.nodeName || (obj.on && obj.find))) {
throw $parseMinErr('isecdom',
2013-10-22 21:41:21 +00:00
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else {
return obj;
}
}
var OPERATORS = {
2013-10-22 21:41:21 +00:00
/* jshint bitwise : false */
'null':function(){return null;},
'true':function(){return true;},
'false':function(){return false;},
undefined:noop,
'+':function(self, locals, a,b){
a=a(self, locals); b=b(self, locals);
if (isDefined(a)) {
if (isDefined(b)) {
return a + b;
}
return a;
}
return isDefined(b)?b:undefined;},
2013-10-22 21:41:21 +00:00
'-':function(self, locals, a,b){
a=a(self, locals); b=b(self, locals);
return (isDefined(a)?a:0)-(isDefined(b)?b:0);
},
'*':function(self, locals, a,b){return a(self, locals)*b(self, locals);},
'/':function(self, locals, a,b){return a(self, locals)/b(self, locals);},
'%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
'^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
'=':noop,
'===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
'!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
'==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
'!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
'<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
'>':function(self, locals, a,b){return a(self, locals)>b(self, locals);},
'<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);},
'>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);},
'&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);},
'||':function(self, locals, a,b){return a(self, locals)||b(self, locals);},
'&':function(self, locals, a,b){return a(self, locals)&b(self, locals);},
// '|':function(self, locals, a,b){return a|b;},
'|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));},
'!':function(self, locals, a){return !a(self, locals);}
2010-01-06 00:36:58 +00:00
};
2013-10-22 21:41:21 +00:00
/* jshint bitwise: true */
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
2010-01-06 00:36:58 +00:00
/////////////////////////////////////////
/**
* @constructor
*/
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
var Lexer = function (options) {
this.options = options;
};
Lexer.prototype = {
constructor: Lexer,
lex: function (text) {
this.text = text;
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
this.index = 0;
this.ch = undefined;
this.lastCh = ':'; // can start regexp
this.tokens = [];
var token;
var json = [];
while (this.index < this.text.length) {
this.ch = this.text.charAt(this.index);
if (this.is('"\'')) {
this.readString(this.ch);
} else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) {
this.readNumber();
} else if (this.isIdent(this.ch)) {
this.readIdent();
// identifiers can only be if the preceding char was a { or ,
if (this.was('{,') && json[0] === '{' &&
(token = this.tokens[this.tokens.length - 1])) {
token.json = token.text.indexOf('.') === -1;
}
} else if (this.is('(){}[].,;:?')) {
this.tokens.push({
index: this.index,
text: this.ch,
json: (this.was(':[,') && this.is('{[')) || this.is('}]:,')
});
if (this.is('{[')) json.unshift(this.ch);
if (this.is('}]')) json.shift();
this.index++;
} else if (this.isWhitespace(this.ch)) {
this.index++;
continue;
2010-01-12 01:32:33 +00:00
} else {
var ch2 = this.ch + this.peek();
var ch3 = ch2 + this.peek(2);
var fn = OPERATORS[this.ch];
var fn2 = OPERATORS[ch2];
var fn3 = OPERATORS[ch3];
if (fn3) {
this.tokens.push({index: this.index, text: ch3, fn: fn3});
this.index += 3;
} else if (fn2) {
this.tokens.push({index: this.index, text: ch2, fn: fn2});
this.index += 2;
} else if (fn) {
this.tokens.push({
index: this.index,
text: this.ch,
fn: fn,
json: (this.was('[,:') && this.is('+-'))
});
this.index += 1;
} else {
this.throwError('Unexpected next character ', this.index, this.index + 1);
}
2010-01-06 00:36:58 +00:00
}
this.lastCh = this.ch;
2010-01-12 01:32:33 +00:00
}
return this.tokens;
},
2010-03-24 17:35:01 +00:00
is: function(chars) {
return chars.indexOf(this.ch) !== -1;
},
was: function(chars) {
return chars.indexOf(this.lastCh) !== -1;
},
peek: function(i) {
var num = i || 1;
return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
},
isNumber: function(ch) {
return ('0' <= ch && ch <= '9');
},
isWhitespace: function(ch) {
2013-10-22 21:41:21 +00:00
// IE treats non-breaking space as \u00A0
return (ch === ' ' || ch === '\r' || ch === '\t' ||
2013-10-22 21:41:21 +00:00
ch === '\n' || ch === '\v' || ch === '\u00A0');
},
isIdent: function(ch) {
return ('a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
'_' === ch || ch === '$');
},
isExpOperator: function(ch) {
return (ch === '-' || ch === '+' || this.isNumber(ch));
},
throwError: function(error, start, end) {
end = end || this.index;
var colStr = (isDefined(start)
? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
: ' ' + end);
throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
error, colStr, this.text);
},
readNumber: function() {
var number = '';
var start = this.index;
while (this.index < this.text.length) {
var ch = lowercase(this.text.charAt(this.index));
if (ch == '.' || this.isNumber(ch)) {
number += ch;
} else {
var peekCh = this.peek();
if (ch == 'e' && this.isExpOperator(peekCh)) {
number += ch;
} else if (this.isExpOperator(ch) &&
peekCh && this.isNumber(peekCh) &&
number.charAt(number.length - 1) == 'e') {
number += ch;
} else if (this.isExpOperator(ch) &&
(!peekCh || !this.isNumber(peekCh)) &&
number.charAt(number.length - 1) == 'e') {
this.throwError('Invalid exponent');
} else {
break;
}
}
this.index++;
}
number = 1 * number;
this.tokens.push({
index: start,
text: number,
json: true,
fn: function() { return number; }
});
},
readIdent: function() {
var parser = this;
var ident = '';
var start = this.index;
var lastDot, peekIndex, methodName, ch;
while (this.index < this.text.length) {
ch = this.text.charAt(this.index);
if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) {
if (ch === '.') lastDot = this.index;
ident += ch;
} else {
break;
2010-01-12 01:32:33 +00:00
}
this.index++;
}
//check if this is not a method invocation and if it is back out to last dot
if (lastDot) {
peekIndex = this.index;
while (peekIndex < this.text.length) {
ch = this.text.charAt(peekIndex);
if (ch === '(') {
methodName = ident.substr(lastDot - start + 1);
ident = ident.substr(0, lastDot - start);
this.index = peekIndex;
break;
}
if (this.isWhitespace(ch)) {
peekIndex++;
} else {
break;
}
}
}
var token = {
index: start,
text: ident
};
// OPERATORS is our own object so we don't need to use special hasOwnPropertyFn
if (OPERATORS.hasOwnProperty(ident)) {
token.fn = OPERATORS[ident];
token.json = OPERATORS[ident];
} else {
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
var getter = getterFn(ident, this.options, this.text);
token.fn = extend(function(self, locals) {
return (getter(self, locals));
}, {
assign: function(self, value) {
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
return setter(self, ident, value, parser.text, parser.options);
}
});
}
this.tokens.push(token);
if (methodName) {
this.tokens.push({
index:lastDot,
text: '.',
json: false
});
this.tokens.push({
index: lastDot + 1,
text: methodName,
json: false
});
}
},
readString: function(quote) {
var start = this.index;
this.index++;
var string = '';
var rawString = quote;
var escape = false;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
rawString += ch;
if (escape) {
if (ch === 'u') {
var hex = this.text.substring(this.index + 1, this.index + 5);
if (!hex.match(/[\da-f]{4}/i))
this.throwError('Invalid unicode escape [\\u' + hex + ']');
this.index += 4;
string += String.fromCharCode(parseInt(hex, 16));
} else {
var rep = ESCAPE[ch];
if (rep) {
string += rep;
} else {
string += ch;
}
}
escape = false;
} else if (ch === '\\') {
escape = true;
} else if (ch === quote) {
this.index++;
this.tokens.push({
index: start,
text: rawString,
string: string,
json: true,
fn: function() { return string; }
});
return;
} else {
string += ch;
}
this.index++;
}
this.throwError('Unterminated quote', start);
}
};
2010-01-06 00:36:58 +00:00
/**
* @constructor
*/
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
var Parser = function (lexer, $filter, options) {
this.lexer = lexer;
this.$filter = $filter;
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
this.options = options;
};
Parser.ZERO = function () { return 0; };
2010-03-24 17:35:01 +00:00
Parser.prototype = {
constructor: Parser,
2010-03-24 17:35:01 +00:00
parse: function (text, json) {
this.text = text;
//TODO(i): strip all the obsolte json stuff from this file
this.json = json;
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
this.tokens = this.lexer.lex(text);
if (json) {
// The extra level of aliasing is here, just in case the lexer misses something, so that
// we prevent any accidental execution in JSON.
this.assignment = this.logicalOR;
this.functionCall =
this.fieldAccess =
this.objectIndex =
this.filterChain = function() {
this.throwError('is not valid json', {text: text, index: 0});
};
}
var value = json ? this.primary() : this.statements();
if (this.tokens.length !== 0) {
this.throwError('is an unexpected token', this.tokens[0]);
}
value.literal = !!value.literal;
value.constant = !!value.constant;
return value;
},
primary: function () {
var primary;
if (this.expect('(')) {
primary = this.filterChain();
this.consume(')');
} else if (this.expect('[')) {
primary = this.arrayDeclaration();
} else if (this.expect('{')) {
primary = this.object();
} else {
var token = this.expect();
primary = token.fn;
if (!primary) {
this.throwError('not a primary expression', token);
}
if (token.json) {
primary.constant = true;
primary.literal = true;
}
}
var next, context;
while ((next = this.expect('(', '[', '.'))) {
if (next.text === '(') {
primary = this.functionCall(primary, context);
context = null;
} else if (next.text === '[') {
context = primary;
primary = this.objectIndex(primary);
} else if (next.text === '.') {
context = primary;
primary = this.fieldAccess(primary);
} else {
this.throwError('IMPOSSIBLE');
}
}
return primary;
},
throwError: function(msg, token) {
throw $parseMinErr('syntax',
'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
},
peekToken: function() {
if (this.tokens.length === 0)
throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
return this.tokens[0];
},
peek: function(e1, e2, e3, e4) {
if (this.tokens.length > 0) {
var token = this.tokens[0];
2010-01-12 01:32:33 +00:00
var t = token.text;
if (t === e1 || t === e2 || t === e3 || t === e4 ||
2010-01-12 01:32:33 +00:00
(!e1 && !e2 && !e3 && !e4)) {
return token;
}
}
return false;
},
2010-03-24 17:35:01 +00:00
expect: function(e1, e2, e3, e4){
var token = this.peek(e1, e2, e3, e4);
2010-01-12 01:32:33 +00:00
if (token) {
if (this.json && !token.json) {
this.throwError('is not valid json', token);
}
this.tokens.shift();
2010-01-06 00:36:58 +00:00
return token;
}
2010-01-12 01:32:33 +00:00
return false;
},
2010-03-24 17:35:01 +00:00
consume: function(e1){
if (!this.expect(e1)) {
this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
2010-01-06 00:36:58 +00:00
}
},
2010-03-24 17:35:01 +00:00
unaryFn: function(fn, right) {
return extend(function(self, locals) {
return fn(self, locals, right);
}, {
constant:right.constant
});
},
2010-03-24 17:35:01 +00:00
ternaryFn: function(left, middle, right){
return extend(function(self, locals){
return left(self, locals) ? middle(self, locals) : right(self, locals);
}, {
constant: left.constant && middle.constant && right.constant
});
},
binaryFn: function(left, fn, right) {
return extend(function(self, locals) {
return fn(self, locals, left, right);
}, {
constant:left.constant && right.constant
});
},
2010-03-24 17:35:01 +00:00
statements: function() {
2010-01-12 01:32:33 +00:00
var statements = [];
while (true) {
if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
statements.push(this.filterChain());
if (!this.expect(';')) {
// optimize for the common case where there is only one statement.
// TODO(size): maybe we should not support multiple statements?
return (statements.length === 1)
? statements[0]
: function(self, locals) {
var value;
for (var i = 0; i < statements.length; i++) {
var statement = statements[i];
if (statement) {
value = statement(self, locals);
}
}
return value;
};
2010-01-12 01:32:33 +00:00
}
2010-01-06 00:36:58 +00:00
}
},
2010-03-24 17:35:01 +00:00
filterChain: function() {
var left = this.expression();
2010-01-12 01:32:33 +00:00
var token;
while (true) {
if ((token = this.expect('|'))) {
left = this.binaryFn(left, token.fn, this.filter());
2010-01-12 01:32:33 +00:00
} else {
return left;
}
}
},
2010-03-24 17:35:01 +00:00
filter: function() {
var token = this.expect();
var fn = this.$filter(token.text);
2010-01-12 01:32:33 +00:00
var argsFn = [];
while (true) {
if ((token = this.expect(':'))) {
argsFn.push(this.expression());
2010-01-12 01:32:33 +00:00
} else {
var fnInvoke = function(self, locals, input) {
2010-01-12 01:32:33 +00:00
var args = [input];
for (var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](self, locals));
2010-01-12 01:32:33 +00:00
}
return fn.apply(self, args);
2010-01-12 01:32:33 +00:00
};
return function() {
2010-01-12 01:32:33 +00:00
return fnInvoke;
};
}
}
},
2010-03-24 17:35:01 +00:00
expression: function() {
return this.assignment();
},
2010-03-24 17:35:01 +00:00
assignment: function() {
var left = this.ternary();
var right;
2010-01-12 01:32:33 +00:00
var token;
if ((token = this.expect('='))) {
if (!left.assign) {
this.throwError('implies assignment but [' +
this.text.substring(0, token.index) + '] can not be assigned to', token);
2010-01-12 01:32:33 +00:00
}
right = this.ternary();
return function(scope, locals) {
return left.assign(scope, right(scope, locals), locals);
};
2010-01-06 00:36:58 +00:00
}
return left;
},
2010-03-24 17:35:01 +00:00
ternary: function() {
var left = this.logicalOR();
var middle;
var token;
if ((token = this.expect('?'))) {
middle = this.ternary();
if ((token = this.expect(':'))) {
return this.ternaryFn(left, middle, this.ternary());
} else {
this.throwError('expected :', token);
}
} else {
return left;
}
},
logicalOR: function() {
var left = this.logicalAND();
2010-01-12 01:32:33 +00:00
var token;
while (true) {
if ((token = this.expect('||'))) {
left = this.binaryFn(left, token.fn, this.logicalAND());
2010-01-12 01:32:33 +00:00
} else {
return left;
}
}
},
2010-03-24 17:35:01 +00:00
logicalAND: function() {
var left = this.equality();
2010-01-12 01:32:33 +00:00
var token;
if ((token = this.expect('&&'))) {
left = this.binaryFn(left, token.fn, this.logicalAND());
2010-01-06 00:36:58 +00:00
}
return left;
},
2010-03-24 17:35:01 +00:00
equality: function() {
var left = this.relational();
2010-01-12 01:32:33 +00:00
var token;
if ((token = this.expect('==','!=','===','!=='))) {
left = this.binaryFn(left, token.fn, this.equality());
2010-01-12 01:32:33 +00:00
}
return left;
},
2010-03-24 17:35:01 +00:00
relational: function() {
var left = this.additive();
2010-01-12 01:32:33 +00:00
var token;
if ((token = this.expect('<', '>', '<=', '>='))) {
left = this.binaryFn(left, token.fn, this.relational());
2010-01-12 01:32:33 +00:00
}
return left;
},
2010-03-24 17:35:01 +00:00
additive: function() {
var left = this.multiplicative();
2010-01-12 01:32:33 +00:00
var token;
while ((token = this.expect('+','-'))) {
left = this.binaryFn(left, token.fn, this.multiplicative());
2010-01-12 01:32:33 +00:00
}
return left;
},
2010-03-24 17:35:01 +00:00
multiplicative: function() {
var left = this.unary();
2010-01-12 01:32:33 +00:00
var token;
while ((token = this.expect('*','/','%'))) {
left = this.binaryFn(left, token.fn, this.unary());
2010-01-12 01:32:33 +00:00
}
return left;
},
2010-03-24 17:35:01 +00:00
unary: function() {
2010-01-12 01:32:33 +00:00
var token;
if (this.expect('+')) {
return this.primary();
} else if ((token = this.expect('-'))) {
return this.binaryFn(Parser.ZERO, token.fn, this.unary());
} else if ((token = this.expect('!'))) {
return this.unaryFn(token.fn, this.unary());
2010-01-06 00:36:58 +00:00
} else {
return this.primary();
2010-01-06 00:36:58 +00:00
}
},
2010-03-24 17:35:01 +00:00
fieldAccess: function(object) {
var parser = this;
var field = this.expect().text;
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
var getter = getterFn(field, this.options, this.text);
2010-03-24 17:35:01 +00:00
return extend(function(scope, locals, self) {
return getter(self || object(scope, locals), locals);
}, {
assign: function(scope, value, locals) {
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
return setter(object(scope, locals), field, value, parser.text, parser.options);
}
});
},
objectIndex: function(obj) {
var parser = this;
2010-03-24 17:35:01 +00:00
var indexFn = this.expression();
this.consume(']');
2010-03-24 17:35:01 +00:00
return extend(function(self, locals) {
var o = obj(self, locals),
feat($parse): secure expressions by hiding "private" properties BREAKING CHANGE: This commit introduces the notion of "private" properties (properties whose names begin and/or end with an underscore) on the scope chain. These properties will not be available to Angular expressions (i.e. {{ }} interpolation in templates and strings passed to `$parse`) They are freely available to JavaScript code (as before). Motivation ---------- Angular expressions execute in a limited context.  They do not have direct access to the global scope, Window, Document or the Function constructor.  However, they have direct access to names/properties on the scope chain.  It has been a long standing best practice to keep sensitive APIs outside of the scope chain (in a closure or your controller.)  That's easier said that done for two reasons: (1) JavaScript does not have a notion of private properties so if you need someone on the scope chain for JavaScript use, you also expose it to Angular expressions, and (2) the new "controller as" syntax that's now in increased usage exposes the entire controller on the scope chain greatly increaing the exposed surface.  Though Angular expressions are written and controlled by the developer, they (1) typically deal with user input and (2) don't get the kind of test coverage that JavaScript code would.  This commit provides a way, via a naming convention, to allow publishing/restricting properties from controllers/scopes to Angular expressions enabling one to only expose those properties that are actually needed by the expressions.
2013-10-18 02:44:36 +00:00
// In the getter, we will not block looking up "constructor" by name in order to support user defined
// constructors. However, if value looked up is the Function constructor, we will still block it in the
// ensureSafeObject call right after we look up o[i] (a few lines below.)
i = ensureSafeMemberName(indexFn(self, locals), parser.text, true /* allowConstructor */),
v, p;
if (!o) return undefined;
v = ensureSafeObject(o[i], parser.text);
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
if (v && v.then && parser.options.unwrapPromises) {
p = v;
if (!('$$v' in v)) {
p.$$v = undefined;
p.then(function(val) { p.$$v = val; });
}
v = v.$$v;
}
return v;
}, {
assign: function(self, value, locals) {
feat($parse): secure expressions by hiding "private" properties BREAKING CHANGE: This commit introduces the notion of "private" properties (properties whose names begin and/or end with an underscore) on the scope chain. These properties will not be available to Angular expressions (i.e. {{ }} interpolation in templates and strings passed to `$parse`) They are freely available to JavaScript code (as before). Motivation ---------- Angular expressions execute in a limited context.  They do not have direct access to the global scope, Window, Document or the Function constructor.  However, they have direct access to names/properties on the scope chain.  It has been a long standing best practice to keep sensitive APIs outside of the scope chain (in a closure or your controller.)  That's easier said that done for two reasons: (1) JavaScript does not have a notion of private properties so if you need someone on the scope chain for JavaScript use, you also expose it to Angular expressions, and (2) the new "controller as" syntax that's now in increased usage exposes the entire controller on the scope chain greatly increaing the exposed surface.  Though Angular expressions are written and controlled by the developer, they (1) typically deal with user input and (2) don't get the kind of test coverage that JavaScript code would.  This commit provides a way, via a naming convention, to allow publishing/restricting properties from controllers/scopes to Angular expressions enabling one to only expose those properties that are actually needed by the expressions.
2013-10-18 02:44:36 +00:00
var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
// prevent overwriting of Function.constructor which would break ensureSafeObject check
var safe = ensureSafeObject(obj(self, locals), parser.text);
return safe[key] = value;
}
});
},
2010-03-24 17:35:01 +00:00
functionCall: function(fn, contextGetter) {
2010-01-12 01:32:33 +00:00
var argsFn = [];
if (this.peekToken().text !== ')') {
2010-01-12 01:32:33 +00:00
do {
argsFn.push(this.expression());
} while (this.expect(','));
2010-01-12 01:32:33 +00:00
}
this.consume(')');
var parser = this;
return function(scope, locals) {
var args = [];
var context = contextGetter ? contextGetter(scope, locals) : scope;
for (var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](scope, locals));
2010-01-12 01:32:33 +00:00
}
var fnPtr = fn(scope, locals, context) || noop;
ensureSafeObject(context, parser.text);
ensureSafeObject(fnPtr, parser.text);
// IE stupidity! (IE doesn't have apply for some native functions)
var v = fnPtr.apply
? fnPtr.apply(context, args)
: fnPtr(args[0], args[1], args[2], args[3], args[4]);
return ensureSafeObject(v, parser.text);
2010-01-06 00:36:58 +00:00
};
},
2010-03-24 17:35:01 +00:00
2010-01-12 01:32:33 +00:00
// This is used with json array declaration
arrayDeclaration: function () {
2010-01-12 01:32:33 +00:00
var elementFns = [];
var allConstant = true;
if (this.peekToken().text !== ']') {
2010-01-12 01:32:33 +00:00
do {
var elementFn = this.expression();
elementFns.push(elementFn);
if (!elementFn.constant) {
allConstant = false;
}
} while (this.expect(','));
2010-01-12 01:32:33 +00:00
}
this.consume(']');
return extend(function(self, locals) {
2010-01-12 01:32:33 +00:00
var array = [];
for (var i = 0; i < elementFns.length; i++) {
array.push(elementFns[i](self, locals));
2010-01-12 01:32:33 +00:00
}
return array;
}, {
literal: true,
constant: allConstant
});
},
2010-03-24 17:35:01 +00:00
object: function () {
2010-01-12 01:32:33 +00:00
var keyValues = [];
var allConstant = true;
if (this.peekToken().text !== '}') {
2010-01-12 01:32:33 +00:00
do {
var token = this.expect(),
key = token.string || token.text;
this.consume(':');
var value = this.expression();
keyValues.push({key: key, value: value});
if (!value.constant) {
allConstant = false;
}
} while (this.expect(','));
2010-01-12 01:32:33 +00:00
}
this.consume('}');
return extend(function(self, locals) {
2010-01-12 01:32:33 +00:00
var object = {};
for (var i = 0; i < keyValues.length; i++) {
2010-01-12 01:32:33 +00:00
var keyValue = keyValues[i];
object[keyValue.key] = keyValue.value(self, locals);
2010-01-12 01:32:33 +00:00
}
return object;
}, {
literal: true,
constant: allConstant
});
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
}
};
//////////////////////////////////////////////////
// Parser helper functions
//////////////////////////////////////////////////
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
function setter(obj, path, setValue, fullExp, options) {
//needed?
options = options || {};
var element = path.split('.'), key;
for (var i = 0; element.length > 1; i++) {
key = ensureSafeMemberName(element.shift(), fullExp);
var propertyObj = obj[key];
if (!propertyObj) {
propertyObj = {};
obj[key] = propertyObj;
}
obj = propertyObj;
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
if (obj.then && options.unwrapPromises) {
promiseWarning(fullExp);
if (!("$$v" in obj)) {
(function(promise) {
promise.then(function(val) { promise.$$v = val; }); }
)(obj);
}
if (obj.$$v === undefined) {
obj.$$v = {};
}
obj = obj.$$v;
}
}
key = ensureSafeMemberName(element.shift(), fullExp);
obj[key] = setValue;
return setValue;
}
2012-01-24 10:42:20 +00:00
var getterFnCache = {};
/**
* Implementation of the "Black Hole" variant from:
* - http://jsperf.com/angularjs-parse-getter/4
* - http://jsperf.com/path-evaluation-simplified/7
*/
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
ensureSafeMemberName(key0, fullExp);
ensureSafeMemberName(key1, fullExp);
ensureSafeMemberName(key2, fullExp);
ensureSafeMemberName(key3, fullExp);
ensureSafeMemberName(key4, fullExp);
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
return !options.unwrapPromises
? function cspSafeGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
if (pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key0];
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key1];
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key2];
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key3];
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key4];
return pathVal;
}
: function cspSafePromiseEnabledGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
promise;
if (pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key0];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key1];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key2];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key3];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
pathVal = pathVal[key4];
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
}
pathVal = pathVal.$$v;
}
return pathVal;
2013-10-22 21:41:21 +00:00
};
}
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
function getterFn(path, options, fullExp) {
// Check whether the cache has this getter already.
// We can use hasOwnProperty directly on the cache because we ensure,
// see below, that the cache never stores a path called 'hasOwnProperty'
if (getterFnCache.hasOwnProperty(path)) {
return getterFnCache[path];
}
var pathKeys = path.split('.'),
pathKeysLength = pathKeys.length,
fn;
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
if (options.csp) {
2013-10-22 21:41:21 +00:00
if (pathKeysLength < 6) {
fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
options);
} else {
fn = function(scope, locals) {
var i = 0, val;
do {
val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
pathKeys[i++], fullExp, options)(scope, locals);
locals = undefined; // clear after first iteration
scope = val;
} while (i < pathKeysLength);
return val;
};
}
} else {
var code = 'var l, fn, p;\n';
forEach(pathKeys, function(key, index) {
ensureSafeMemberName(key, fullExp);
code += 'if(s === null || s === undefined) return s;\n' +
'l=s;\n' +
's='+ (index
// we simply dereference 's' on any .dot notation
? 's'
// but if we are first then we check locals first, and if so read it first
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
(options.unwrapPromises
? 'if (s && s.then) {\n' +
' pw("' + fullExp.replace(/\"/g, '\\"') + '");\n' +
' if (!("$$v" in s)) {\n' +
' p=s;\n' +
' p.$$v = undefined;\n' +
' p.then(function(v) {p.$$v=v;});\n' +
'}\n' +
' s=s.$$v\n' +
'}\n'
: '');
});
code += 'return s;';
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
2013-10-22 21:41:21 +00:00
/* jshint -W054 */
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
/* jshint +W054 */
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
evaledFnGetter.toString = function() { return code; };
fn = function(scope, locals) {
return evaledFnGetter(scope, locals, promiseWarning);
};
}
// Only cache the value if it's not going to mess up the cache object
// This is more performant that using Object.prototype.hasOwnProperty.call
if (path !== 'hasOwnProperty') {
getterFnCache[path] = fn;
}
return fn;
}
///////////////////////////////////
2012-02-28 22:29:58 +00:00
/**
* @ngdoc function
* @name ng.$parse
2012-02-28 22:29:58 +00:00
* @function
*
* @description
*
* Converts Angular {@link guide/expression expression} into a function.
2012-02-28 22:29:58 +00:00
*
* <pre>
* var getter = $parse('user.name');
* var setter = getter.assign;
* var context = {user:{name:'angular'}};
* var locals = {user:{name:'local'}};
*
* expect(getter(context)).toEqual('angular');
* setter(context, 'newValue');
* expect(context.user.name).toEqual('newValue');
* expect(getter(context, locals)).toEqual('local');
* </pre>
*
*
* @param {string} expression String expression to compile.
* @returns {function(context, locals)} a function which represents the compiled expression:
*
* * `context` `{object}` an object against which any expressions embedded in the strings
2013-03-21 19:09:47 +00:00
* are evaluated against (typically a scope object).
* * `locals` `{object=}` local variables context object, useful for overriding values in
* `context`.
2012-02-28 22:29:58 +00:00
*
* The returned function also has the following properties:
* * `literal` `{boolean}` whether the expression's top-level node is a JavaScript
* literal.
* * `constant` `{boolean}` whether the expression is made entirely of JavaScript
* constant literals.
* * `assign` `{?function(context, value)}` if the expression is assignable, this will be
* set to a function to change its value on the given context.
2012-02-28 22:29:58 +00:00
*
*/
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
/**
* @ngdoc object
* @name ng.$parseProvider
* @function
*
* @description
2013-10-22 21:41:21 +00:00
* `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
* service.
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
*/
function $ParseProvider() {
var cache = {};
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
var $parseOptions = {
csp: false,
unwrapPromises: false,
logPromiseWarnings: true
};
/**
* @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
*
* @ngdoc method
* @name ng.$parseProvider#unwrapPromises
* @methodOf ng.$parseProvider
* @description
*
* **This feature is deprecated, see deprecation notes below for more info**
*
2013-10-22 21:41:21 +00:00
* If set to true (default is false), $parse will unwrap promises automatically when a promise is
* found at any part of the expression. In other words, if set to true, the expression will always
* result in a non-promise value.
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
*
2013-10-22 21:41:21 +00:00
* While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled,
* the fulfillment value is used in place of the promise while evaluating the expression.
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
*
* **Deprecation notice**
*
2013-10-22 21:41:21 +00:00
* This is a feature that didn't prove to be wildly useful or popular, primarily because of the
* dichotomy between data access in templates (accessed as raw values) and controller code
* (accessed as promises).
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
*
2013-10-22 21:41:21 +00:00
* In most code we ended up resolving promises manually in controllers anyway and thus unifying
* the model access there.
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
*
* Other downsides of automatic promise unwrapping:
*
* - when building components it's often desirable to receive the raw promises
* - adds complexity and slows down expression evaluation
2013-10-22 21:41:21 +00:00
* - makes expression code pre-generation unattractive due to the amount of code that needs to be
* generated
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
* - makes IDE auto-completion and tool support hard
*
* **Warning Logs**
*
2013-10-22 21:41:21 +00:00
* If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a
* promise (to reduce the noise, each expression is logged only once). To disable this logging use
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
* `$parseProvider.logPromiseWarnings(false)` api.
*
*
* @param {boolean=} value New value.
2013-10-22 21:41:21 +00:00
* @returns {boolean|self} Returns the current setting when used as getter and self if used as
* setter.
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
*/
this.unwrapPromises = function(value) {
if (isDefined(value)) {
$parseOptions.unwrapPromises = !!value;
return this;
} else {
return $parseOptions.unwrapPromises;
}
};
/**
* @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
*
* @ngdoc method
* @name ng.$parseProvider#logPromiseWarnings
* @methodOf ng.$parseProvider
* @description
*
* Controls whether Angular should log a warning on any encounter of a promise in an expression.
*
* The default is set to `true`.
*
* This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well.
*
* @param {boolean=} value New value.
2013-10-22 21:41:21 +00:00
* @returns {boolean|self} Returns the current setting when used as getter and self if used as
* setter.
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
*/
this.logPromiseWarnings = function(value) {
if (isDefined(value)) {
$parseOptions.logPromiseWarnings = value;
return this;
} else {
return $parseOptions.logPromiseWarnings;
}
};
this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
$parseOptions.csp = $sniffer.csp;
promiseWarning = function promiseWarningFn(fullExp) {
if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
promiseWarningCache[fullExp] = true;
$log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' +
'Automatic unwrapping of promises in Angular expressions is deprecated.');
};
return function(exp) {
var parsedExpression;
switch (typeof exp) {
case 'string':
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
if (cache.hasOwnProperty(exp)) {
return cache[exp];
}
fix($parse): deprecate promise unwrapping and make it an opt-in This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period. If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use `$parseProvider.logPromiseWarnings(false)`. Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled. This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises). In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way. Other downsides of automatic promise unwrapping: - when building components it's often desirable to receive the raw promises - adds complexity and slows down expression evaluation - makes expression code pre-generation unattractive due to the amount of code that needs to be generated - makes IDE auto-completion and tool support hard - adds too much magic BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via `$parseProvider.unwrapPromises(true)` api. Closes #4158 Closes #4270
2013-10-07 16:58:37 +00:00
var lexer = new Lexer($parseOptions);
var parser = new Parser(lexer, $filter, $parseOptions);
parsedExpression = parser.parse(exp, false);
if (exp !== 'hasOwnProperty') {
// Only cache the value if it's not going to mess up the cache object
// This is more performant that using Object.prototype.hasOwnProperty.call
cache[exp] = parsedExpression;
}
return parsedExpression;
case 'function':
return exp;
default:
return noop;
}
};
}];
}