@@ -376,6 +376,10 @@ Lexer.prototype = {
376
376
} ;
377
377
378
378
379
+ function isConstant ( exp ) {
380
+ return exp . constant ;
381
+ }
382
+
379
383
/**
380
384
* @constructor
381
385
*/
@@ -493,7 +497,8 @@ Parser.prototype = {
493
497
return extend ( function ( self , locals ) {
494
498
return fn ( self , locals , right ) ;
495
499
} , {
496
- constant :right . constant
500
+ constant :right . constant ,
501
+ inputs : [ right ]
497
502
} ) ;
498
503
} ,
499
504
@@ -505,11 +510,12 @@ Parser.prototype = {
505
510
} ) ;
506
511
} ,
507
512
508
- binaryFn : function ( left , fn , right ) {
513
+ binaryFn : function ( left , fn , right , isBranching ) {
509
514
return extend ( function ( self , locals ) {
510
515
return fn ( self , locals , left , right ) ;
511
516
} , {
512
- constant :left . constant && right . constant
517
+ constant : left . constant && right . constant ,
518
+ inputs : ! isBranching && [ left , right ]
513
519
} ) ;
514
520
} ,
515
521
@@ -557,7 +563,9 @@ Parser.prototype = {
557
563
}
558
564
}
559
565
560
- return function $parseFilter ( self , locals ) {
566
+ var inputs = [ inputFn ] . concat ( argsFn || [ ] ) ;
567
+
568
+ return extend ( function $parseFilter ( self , locals ) {
561
569
var input = inputFn ( self , locals ) ;
562
570
if ( args ) {
563
571
args [ 0 ] = input ;
@@ -571,7 +579,10 @@ Parser.prototype = {
571
579
}
572
580
573
581
return fn ( input ) ;
574
- } ;
582
+ } , {
583
+ constant : ! fn . $stateful && inputs . every ( isConstant ) ,
584
+ inputs : ! fn . $stateful && inputs
585
+ } ) ;
575
586
} ,
576
587
577
588
expression : function ( ) {
@@ -588,9 +599,11 @@ Parser.prototype = {
588
599
this . text . substring ( 0 , token . index ) + '] can not be assigned to' , token ) ;
589
600
}
590
601
right = this . ternary ( ) ;
591
- return function $parseAssignment ( scope , locals ) {
602
+ return extend ( function $parseAssignment ( scope , locals ) {
592
603
return left . assign ( scope , right ( scope , locals ) , locals ) ;
593
- } ;
604
+ } , {
605
+ inputs : [ left , right ]
606
+ } ) ;
594
607
}
595
608
return left ;
596
609
} ,
@@ -615,7 +628,7 @@ Parser.prototype = {
615
628
var left = this . logicalAND ( ) ;
616
629
var token ;
617
630
while ( ( token = this . expect ( '||' ) ) ) {
618
- left = this . binaryFn ( left , token . fn , this . logicalAND ( ) ) ;
631
+ left = this . binaryFn ( left , token . fn , this . logicalAND ( ) , true ) ;
619
632
}
620
633
return left ;
621
634
} ,
@@ -624,7 +637,7 @@ Parser.prototype = {
624
637
var left = this . equality ( ) ;
625
638
var token ;
626
639
if ( ( token = this . expect ( '&&' ) ) ) {
627
- left = this . binaryFn ( left , token . fn , this . logicalAND ( ) ) ;
640
+ left = this . binaryFn ( left , token . fn , this . logicalAND ( ) , true ) ;
628
641
}
629
642
return left ;
630
643
} ,
@@ -759,7 +772,6 @@ Parser.prototype = {
759
772
// This is used with json array declaration
760
773
arrayDeclaration : function ( ) {
761
774
var elementFns = [ ] ;
762
- var allConstant = true ;
763
775
if ( this . peekToken ( ) . text !== ']' ) {
764
776
do {
765
777
if ( this . peek ( ']' ) ) {
@@ -768,9 +780,6 @@ Parser.prototype = {
768
780
}
769
781
var elementFn = this . expression ( ) ;
770
782
elementFns . push ( elementFn ) ;
771
- if ( ! elementFn . constant ) {
772
- allConstant = false ;
773
- }
774
783
} while ( this . expect ( ',' ) ) ;
775
784
}
776
785
this . consume ( ']' ) ;
@@ -783,13 +792,13 @@ Parser.prototype = {
783
792
return array ;
784
793
} , {
785
794
literal : true ,
786
- constant : allConstant
795
+ constant : elementFns . every ( isConstant ) ,
796
+ inputs : elementFns
787
797
} ) ;
788
798
} ,
789
799
790
800
object : function ( ) {
791
801
var keys = [ ] , valueFns = [ ] ;
792
- var allConstant = true ;
793
802
if ( this . peekToken ( ) . text !== '}' ) {
794
803
do {
795
804
if ( this . peek ( '}' ) ) {
@@ -801,9 +810,6 @@ Parser.prototype = {
801
810
this . consume ( ':' ) ;
802
811
var value = this . expression ( ) ;
803
812
valueFns . push ( value ) ;
804
- if ( ! value . constant ) {
805
- allConstant = false ;
806
- }
807
813
} while ( this . expect ( ',' ) ) ;
808
814
}
809
815
this . consume ( '}' ) ;
@@ -816,7 +822,8 @@ Parser.prototype = {
816
822
return object ;
817
823
} , {
818
824
literal : true ,
819
- constant : allConstant
825
+ constant : valueFns . every ( isConstant ) ,
826
+ inputs : valueFns
820
827
} ) ;
821
828
}
822
829
} ;
@@ -1043,6 +1050,8 @@ function $ParseProvider() {
1043
1050
parsedExpression = wrapSharedExpression ( parsedExpression ) ;
1044
1051
parsedExpression . $$watchDelegate = parsedExpression . literal ?
1045
1052
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate ;
1053
+ } else if ( parsedExpression . inputs ) {
1054
+ parsedExpression . $$watchDelegate = inputsWatchDelegate ;
1046
1055
}
1047
1056
1048
1057
cache [ cacheKey ] = parsedExpression ;
@@ -1057,6 +1066,88 @@ function $ParseProvider() {
1057
1066
}
1058
1067
} ;
1059
1068
1069
+ function collectExpressionInputs ( inputs , list ) {
1070
+ for ( var i = 0 , ii = inputs . length ; i < ii ; i ++ ) {
1071
+ var input = inputs [ i ] ;
1072
+ if ( ! input . constant ) {
1073
+ if ( input . inputs ) {
1074
+ collectExpressionInputs ( input . inputs , list ) ;
1075
+ } else if ( list . indexOf ( input ) === - 1 ) { // TODO(perf) can we do better?
1076
+ list . push ( input ) ;
1077
+ }
1078
+ }
1079
+ }
1080
+
1081
+ return list ;
1082
+ }
1083
+
1084
+ function expressionInputDirtyCheck ( newValue , oldValueOfValue ) {
1085
+
1086
+ if ( newValue == null || oldValueOfValue == null ) { // null/undefined
1087
+ return newValue === oldValueOfValue ;
1088
+ }
1089
+
1090
+ if ( typeof newValue === 'object' ) {
1091
+
1092
+ // attempt to convert the value to a primitive type
1093
+ // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
1094
+ // be cheaply dirty-checked
1095
+ newValue = newValue . valueOf ( ) ;
1096
+
1097
+ if ( typeof newValue === 'object' ) {
1098
+ // objects/arrays are not supported - deep-watching them would be too expensive
1099
+ return false ;
1100
+ }
1101
+
1102
+ // fall-through to the primitive equality check
1103
+ }
1104
+
1105
+ //Primitive or NaN
1106
+ return newValue === oldValueOfValue || ( newValue !== newValue && oldValueOfValue !== oldValueOfValue ) ;
1107
+ }
1108
+
1109
+ function inputsWatchDelegate ( scope , listener , objectEquality , parsedExpression ) {
1110
+ var inputExpressions = parsedExpression . $$inputs ||
1111
+ ( parsedExpression . $$inputs = collectExpressionInputs ( parsedExpression . inputs , [ ] ) ) ;
1112
+
1113
+ var lastResult ;
1114
+
1115
+ if ( inputExpressions . length === 1 ) {
1116
+ var oldInputValue = expressionInputDirtyCheck ; // init to something unique so that equals check fails
1117
+ inputExpressions = inputExpressions [ 0 ] ;
1118
+ return scope . $watch ( function expressionInputWatch ( scope ) {
1119
+ var newInputValue = inputExpressions ( scope ) ;
1120
+ if ( ! expressionInputDirtyCheck ( newInputValue , oldInputValue ) ) {
1121
+ lastResult = parsedExpression ( scope ) ;
1122
+ oldInputValue = newInputValue && newInputValue . valueOf ( ) ;
1123
+ }
1124
+ return lastResult ;
1125
+ } , listener , objectEquality ) ;
1126
+ }
1127
+
1128
+ var oldInputValueOfValues = [ ] ;
1129
+ for ( var i = 0 , ii = inputExpressions . length ; i < ii ; i ++ ) {
1130
+ oldInputValueOfValues [ i ] = expressionInputDirtyCheck ; // init to something unique so that equals check fails
1131
+ }
1132
+
1133
+ return scope . $watch ( function expressionInputsWatch ( scope ) {
1134
+ var changed = false ;
1135
+
1136
+ for ( var i = 0 , ii = inputExpressions . length ; i < ii ; i ++ ) {
1137
+ var newInputValue = inputExpressions [ i ] ( scope ) ;
1138
+ if ( changed || ( changed = ! expressionInputDirtyCheck ( newInputValue , oldInputValueOfValues [ i ] ) ) ) {
1139
+ oldInputValueOfValues [ i ] = newInputValue && newInputValue . valueOf ( ) ;
1140
+ }
1141
+ }
1142
+
1143
+ if ( changed ) {
1144
+ lastResult = parsedExpression ( scope ) ;
1145
+ }
1146
+
1147
+ return lastResult ;
1148
+ } , listener , objectEquality ) ;
1149
+ }
1150
+
1060
1151
function oneTimeWatchDelegate ( scope , listener , objectEquality , parsedExpression ) {
1061
1152
var unwatch , lastValue ;
1062
1153
return unwatch = scope . $watch ( function oneTimeWatch ( scope ) {
@@ -1122,7 +1213,18 @@ function $ParseProvider() {
1122
1213
// initial value is defined (for bind-once)
1123
1214
return isDefined ( value ) ? result : value ;
1124
1215
} ;
1125
- fn . $$watchDelegate = parsedExpression . $$watchDelegate ;
1216
+
1217
+ // Propagate $$watchDelegates other then inputsWatchDelegate
1218
+ if ( parsedExpression . $$watchDelegate &&
1219
+ parsedExpression . $$watchDelegate !== inputsWatchDelegate ) {
1220
+ fn . $$watchDelegate = parsedExpression . $$watchDelegate ;
1221
+ } else if ( ! interceptorFn . $stateful ) {
1222
+ // If there is an interceptor, but no watchDelegate then treat the interceptor like
1223
+ // we treat filters - it is assumed to be a pure function unless flagged with $stateful
1224
+ fn . $$watchDelegate = inputsWatchDelegate ;
1225
+ fn . inputs = [ parsedExpression ] ;
1226
+ }
1227
+
1126
1228
return fn ;
1127
1229
}
1128
1230
} ] ;
0 commit comments