@@ -99,15 +99,16 @@ var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'
99
99
/**
100
100
* @constructor
101
101
*/
102
- var Lexer = function ( csp ) {
103
- this . csp = csp ;
102
+ var Lexer = function ( options ) {
103
+ this . options = options ;
104
104
} ;
105
105
106
106
Lexer . prototype = {
107
107
constructor : Lexer ,
108
108
109
109
lex : function ( text ) {
110
110
this . text = text ;
111
+
111
112
this . index = 0 ;
112
113
this . ch = undefined ;
113
114
this . lastCh = ':' ; // can start regexp
@@ -294,12 +295,12 @@ Lexer.prototype = {
294
295
token . fn = OPERATORS [ ident ] ;
295
296
token . json = OPERATORS [ ident ] ;
296
297
} else {
297
- var getter = getterFn ( ident , this . csp , this . text ) ;
298
+ var getter = getterFn ( ident , this . options , this . text ) ;
298
299
token . fn = extend ( function ( self , locals ) {
299
300
return ( getter ( self , locals ) ) ;
300
301
} , {
301
302
assign : function ( self , value ) {
302
- return setter ( self , ident , value , parser . text ) ;
303
+ return setter ( self , ident , value , parser . text , parser . options ) ;
303
304
}
304
305
} ) ;
305
306
}
@@ -370,10 +371,10 @@ Lexer.prototype = {
370
371
/**
371
372
* @constructor
372
373
*/
373
- var Parser = function ( lexer , $filter , csp ) {
374
+ var Parser = function ( lexer , $filter , options ) {
374
375
this . lexer = lexer ;
375
376
this . $filter = $filter ;
376
- this . csp = csp ;
377
+ this . options = options ;
377
378
} ;
378
379
379
380
Parser . ZERO = function ( ) { return 0 ; } ;
@@ -387,7 +388,7 @@ Parser.prototype = {
387
388
//TODO(i): strip all the obsolte json stuff from this file
388
389
this . json = json ;
389
390
390
- this . tokens = this . lexer . lex ( text , this . csp ) ;
391
+ this . tokens = this . lexer . lex ( text ) ;
391
392
392
393
if ( json ) {
393
394
// The extra level of aliasing is here, just in case the lexer misses something, so that
@@ -687,13 +688,13 @@ Parser.prototype = {
687
688
fieldAccess : function ( object ) {
688
689
var parser = this ;
689
690
var field = this . expect ( ) . text ;
690
- var getter = getterFn ( field , this . csp , this . text ) ;
691
+ var getter = getterFn ( field , this . options , this . text ) ;
691
692
692
693
return extend ( function ( scope , locals , self ) {
693
694
return getter ( self || object ( scope , locals ) , locals ) ;
694
695
} , {
695
696
assign : function ( scope , value , locals ) {
696
- return setter ( object ( scope , locals ) , field , value , parser . text ) ;
697
+ return setter ( object ( scope , locals ) , field , value , parser . text , parser . options ) ;
697
698
}
698
699
} ) ;
699
700
} ,
@@ -711,7 +712,7 @@ Parser.prototype = {
711
712
712
713
if ( ! o ) return undefined ;
713
714
v = ensureSafeObject ( o [ i ] , parser . text ) ;
714
- if ( v && v . then ) {
715
+ if ( v && v . then && parser . options . unwrapPromises ) {
715
716
p = v ;
716
717
if ( ! ( '$$v' in v ) ) {
717
718
p . $$v = undefined ;
@@ -758,7 +759,7 @@ Parser.prototype = {
758
759
: fnPtr ( args [ 0 ] , args [ 1 ] , args [ 2 ] , args [ 3 ] , args [ 4 ] ) ;
759
760
760
761
// Check for promise
761
- if ( v && v . then ) {
762
+ if ( v && v . then && parser . options . unwrapPromises ) {
762
763
var p = v ;
763
764
if ( ! ( '$$v' in v ) ) {
764
765
p . $$v = undefined ;
@@ -826,15 +827,17 @@ Parser.prototype = {
826
827
literal : true ,
827
828
constant : allConstant
828
829
} ) ;
829
- } ,
830
+ }
830
831
} ;
831
832
832
833
833
834
//////////////////////////////////////////////////
834
835
// Parser helper functions
835
836
//////////////////////////////////////////////////
836
837
837
- function setter ( obj , path , setValue , fullExp ) {
838
+ function setter ( obj , path , setValue , fullExp , options ) {
839
+ options = options || { } ;
840
+
838
841
var element = path . split ( '.' ) , key ;
839
842
for ( var i = 0 ; element . length > 1 ; i ++ ) {
840
843
key = ensureSafeMemberName ( element . shift ( ) , fullExp ) ;
@@ -844,7 +847,7 @@ function setter(obj, path, setValue, fullExp) {
844
847
obj [ key ] = propertyObj ;
845
848
}
846
849
obj = propertyObj ;
847
- if ( obj . then ) {
850
+ if ( obj . then && options . unwrapPromises ) {
848
851
if ( ! ( "$$v" in obj ) ) {
849
852
( function ( promise ) {
850
853
promise . then ( function ( val ) { promise . $$v = val ; } ) ; }
@@ -868,7 +871,7 @@ var getterFnCache = {};
868
871
* - http://jsperf.com/angularjs-parse-getter/4
869
872
* - http://jsperf.com/path-evaluation-simplified/7
870
873
*/
871
- function cspSafeGetterFn ( key0 , key1 , key2 , key3 , key4 , fullExp ) {
874
+ function cspSafeGetterFn ( key0 , key1 , key2 , key3 , key4 , fullExp , options ) {
872
875
ensureSafeMemberName ( key0 , fullExp ) ;
873
876
ensureSafeMemberName ( key1 , fullExp ) ;
874
877
ensureSafeMemberName ( key2 , fullExp ) ;
@@ -881,7 +884,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) {
881
884
if ( pathVal === null || pathVal === undefined ) return pathVal ;
882
885
883
886
pathVal = pathVal [ key0 ] ;
884
- if ( pathVal && pathVal . then ) {
887
+ if ( pathVal && pathVal . then && options . unwrapPromises ) {
885
888
if ( ! ( "$$v" in pathVal ) ) {
886
889
promise = pathVal ;
887
890
promise . $$v = undefined ;
@@ -892,7 +895,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) {
892
895
if ( ! key1 || pathVal === null || pathVal === undefined ) return pathVal ;
893
896
894
897
pathVal = pathVal [ key1 ] ;
895
- if ( pathVal && pathVal . then ) {
898
+ if ( pathVal && pathVal . then && options . unwrapPromises ) {
896
899
if ( ! ( "$$v" in pathVal ) ) {
897
900
promise = pathVal ;
898
901
promise . $$v = undefined ;
@@ -903,7 +906,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) {
903
906
if ( ! key2 || pathVal === null || pathVal === undefined ) return pathVal ;
904
907
905
908
pathVal = pathVal [ key2 ] ;
906
- if ( pathVal && pathVal . then ) {
909
+ if ( pathVal && pathVal . then && options . unwrapPromises ) {
907
910
if ( ! ( "$$v" in pathVal ) ) {
908
911
promise = pathVal ;
909
912
promise . $$v = undefined ;
@@ -914,7 +917,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) {
914
917
if ( ! key3 || pathVal === null || pathVal === undefined ) return pathVal ;
915
918
916
919
pathVal = pathVal [ key3 ] ;
917
- if ( pathVal && pathVal . then ) {
920
+ if ( pathVal && pathVal . then && options . unwrapPromises ) {
918
921
if ( ! ( "$$v" in pathVal ) ) {
919
922
promise = pathVal ;
920
923
promise . $$v = undefined ;
@@ -925,7 +928,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) {
925
928
if ( ! key4 || pathVal === null || pathVal === undefined ) return pathVal ;
926
929
927
930
pathVal = pathVal [ key4 ] ;
928
- if ( pathVal && pathVal . then ) {
931
+ if ( pathVal && pathVal . then && options . unwrapPromises ) {
929
932
if ( ! ( "$$v" in pathVal ) ) {
930
933
promise = pathVal ;
931
934
promise . $$v = undefined ;
@@ -937,23 +940,25 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) {
937
940
} ;
938
941
}
939
942
940
- function getterFn ( path , csp , fullExp ) {
941
- if ( getterFnCache . hasOwnProperty ( path ) ) {
942
- return getterFnCache [ path ] ;
943
+ function getterFn ( path , options , fullExp ) {
944
+ var cacheKey = path ;
945
+ cacheKey += '#unwrapPromises:' + ( ! ! options . unwrapPromises ) . toString ( ) ;
946
+ if ( getterFnCache . hasOwnProperty ( cacheKey ) ) {
947
+ return getterFnCache [ cacheKey ] ;
943
948
}
944
949
945
950
var pathKeys = path . split ( '.' ) ,
946
951
pathKeysLength = pathKeys . length ,
947
952
fn ;
948
953
949
- if ( csp ) {
954
+ if ( options . csp ) {
950
955
fn = ( pathKeysLength < 6 )
951
- ? cspSafeGetterFn ( pathKeys [ 0 ] , pathKeys [ 1 ] , pathKeys [ 2 ] , pathKeys [ 3 ] , pathKeys [ 4 ] , fullExp )
956
+ ? cspSafeGetterFn ( pathKeys [ 0 ] , pathKeys [ 1 ] , pathKeys [ 2 ] , pathKeys [ 3 ] , pathKeys [ 4 ] , fullExp , options )
952
957
: function ( scope , locals ) {
953
958
var i = 0 , val ;
954
959
do {
955
960
val = cspSafeGetterFn (
956
- pathKeys [ i ++ ] , pathKeys [ i ++ ] , pathKeys [ i ++ ] , pathKeys [ i ++ ] , pathKeys [ i ++ ] , fullExp
961
+ pathKeys [ i ++ ] , pathKeys [ i ++ ] , pathKeys [ i ++ ] , pathKeys [ i ++ ] , pathKeys [ i ++ ] , fullExp , options
957
962
) ( scope , locals ) ;
958
963
959
964
locals = undefined ; // clear after first iteration
@@ -972,21 +977,23 @@ function getterFn(path, csp, fullExp) {
972
977
? 's'
973
978
// but if we are first then we check locals first, and if so read it first
974
979
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)' ) + '["' + key + '"]' + ';\n' +
975
- 'if (s && s.then) {\n' +
976
- ' if (!("$$v" in s)) {\n' +
977
- ' p=s;\n' +
978
- ' p.$$v = undefined;\n' +
979
- ' p.then(function(v) {p.$$v=v;});\n' +
980
- '}\n' +
981
- ' s=s.$$v\n' +
982
- '}\n' ;
980
+ ( options . unwrapPromises
981
+ ? 'if (s && s.then) {\n' +
982
+ ' if (!("$$v" in s)) {\n' +
983
+ ' p=s;\n' +
984
+ ' p.$$v = undefined;\n' +
985
+ ' p.then(function(v) {p.$$v=v;});\n' +
986
+ '}\n' +
987
+ ' s=s.$$v\n' +
988
+ '}\n'
989
+ : '' ) ;
983
990
} ) ;
984
991
code += 'return s;' ;
985
992
fn = Function ( 's' , 'k' , code ) ; // s=scope, k=locals
986
993
fn . toString = function ( ) { return code ; } ;
987
994
}
988
995
989
- return getterFnCache [ path ] = fn ;
996
+ return getterFnCache [ cacheKey ] = fn ;
990
997
}
991
998
992
999
///////////////////////////////////
@@ -1030,19 +1037,92 @@ function getterFn(path, csp, fullExp) {
1030
1037
* set to a function to change its value on the given context.
1031
1038
*
1032
1039
*/
1040
+
1041
+
1042
+ /**
1043
+ * @ngdoc object
1044
+ * @name ng.$parseProvider
1045
+ * @function
1046
+ *
1047
+ * @description
1048
+ * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} service.
1049
+ */
1033
1050
function $ParseProvider ( ) {
1034
1051
var cache = { } ;
1052
+
1053
+ var defaultOptions = {
1054
+ csp : false ,
1055
+ unwrapPromises : false
1056
+ } ;
1057
+
1058
+
1059
+ /**
1060
+ * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
1061
+ *
1062
+ * @ngdoc method
1063
+ * @name ng.$parseProvider#unwrapPromises
1064
+ * @methodOf ng.$parseProvider
1065
+ * @description
1066
+ *
1067
+ * **This feature is deprecated, see deprecation notes below for more info**
1068
+ *
1069
+ * If set to true (default is false), $parse will unwrap promises automatically when a promise is found at any part of
1070
+ * the expression. In other words, if set to true, the expression will always result in a non-promise value.
1071
+ *
1072
+ * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled, the fulfillment value
1073
+ * is used in place of the promise while evaluating the expression.
1074
+ *
1075
+ * # Deprecation notice
1076
+ *
1077
+ * This is a feature that didn't prove to be wildly useful or popular,
1078
+ * primarily because of the dichotomy between data access in templates
1079
+ * (accessed as raw values) and controller code (accessed as promises).
1080
+ *
1081
+ * In most code we ended up resolving promises manually in controllers
1082
+ * anyway and thus unifying the model access there.
1083
+ *
1084
+ * Other downsides of automatic promise unwrapping:
1085
+ *
1086
+ * - when building components it's often desirable to receive the
1087
+ * raw promises
1088
+ * - adds complexity and slows down expression evaluation
1089
+ * - makes expression code pre-generation unattractive due to the
1090
+ * amount of code that needs to be generated
1091
+ * - makes IDE auto-completion and tool support hard
1092
+ *
1093
+ *
1094
+ * @param {boolean= } value New value.
1095
+ * @returns {boolean|self } Returns the current setting when used as getter and self if used as setter.
1096
+ */
1097
+ this . unwrapPromises = function ( value ) {
1098
+ if ( isDefined ( value ) ) {
1099
+ defaultOptions . unwrapPromises = value ;
1100
+ } else {
1101
+ return defaultOptions . unwrapPromises ;
1102
+ }
1103
+ } ;
1104
+
1105
+
1035
1106
this . $get = [ '$filter' , '$sniffer' , function ( $filter , $sniffer ) {
1036
- return function ( exp ) {
1107
+ defaultOptions . csp = $sniffer . csp ;
1108
+
1109
+ return function ( exp , options ) {
1037
1110
switch ( typeof exp ) {
1038
1111
case 'string' :
1039
- if ( cache . hasOwnProperty ( exp ) ) {
1040
- return cache [ exp ] ;
1112
+ options = extend ( { } , defaultOptions , options ) ;
1113
+
1114
+ var cacheKey = exp ;
1115
+ forEach ( options , function ( optionValue , optionName ) {
1116
+ cacheKey += '#' + optionName + ':' + optionValue ;
1117
+ } ) ;
1118
+
1119
+ if ( cache . hasOwnProperty ( cacheKey ) ) {
1120
+ return cache [ cacheKey ] ;
1041
1121
}
1042
1122
1043
- var lexer = new Lexer ( $sniffer . csp ) ;
1044
- var parser = new Parser ( lexer , $filter , $sniffer . csp ) ;
1045
- return cache [ exp ] = parser . parse ( exp , false ) ;
1123
+ var lexer = new Lexer ( options ) ;
1124
+ var parser = new Parser ( lexer , $filter , options ) ;
1125
+ return cache [ cacheKey ] = parser . parse ( exp , false ) ;
1046
1126
1047
1127
case 'function' :
1048
1128
return exp ;
0 commit comments