@@ -1663,7 +1663,7 @@ var getFunctionNames = function(str) {
1663
1663
// To fully fix these issues we would need a full blown expression parser.
1664
1664
var results = removeStringExp ( str . replace ( / \s + / g, '' ) )
1665
1665
. replace ( / \( .* ?\) / g, '' )
1666
- . split ( / [ \+ \- \/ \| \< \> \^ = & ! % ~ ; ] / g) . map ( function ( x ) {
1666
+ . split ( / [ \+ \- \/ \| \< \> \^ = & ! % ~ ? : ; ] / g) . map ( function ( x ) {
1667
1667
if ( isNaN ( + x ) ) {
1668
1668
if ( x . match ( / \w + \( .* \) $ / ) ) {
1669
1669
return x . substr ( 0 , x . indexOf ( '(' ) ) ;
@@ -1699,22 +1699,31 @@ function ngEventDirectivesDecorator(ngEventAttrName) {
1699
1699
1700
1700
return function ngEventHandler ( scope , element , attrs ) {
1701
1701
var boundFuncs = getFunctionNames ( attrs [ ngEventAttrName ] ) ;
1702
- boundFuncs . forEach ( function ( boundFn ) {
1703
- var property , propChain , lastProp = '' ;
1704
- while ( ( property = boundFn . match ( / ^ .+ ?( [ ^ \. \[ ] ) * / ) ) !== null ) {
1705
- property = property [ 0 ] ;
1706
- propChain = lastProp + property ;
1707
- if ( $parse ( propChain ) ( scope ) === undefined ) {
1708
- angular . hint . emit ( MODULE_NAME + ':undef' , propChain + ' is undefined' ) ;
1709
- }
1710
- boundFn = boundFn . replace ( property , '' ) ;
1711
- lastProp += property ;
1712
- if ( boundFn . charAt ( 0 ) === '.' ) {
1713
- lastProp += '.' ;
1714
- boundFn = boundFn . substr ( 1 ) ;
1702
+
1703
+ // guard against any parsing errors since the parsing code
1704
+ // to split the expression is pretty simple and naive.
1705
+ try {
1706
+ boundFuncs . forEach ( function ( boundFn ) {
1707
+ var property , propChain , lastProp = '' ;
1708
+ while ( ( property = boundFn . match ( / ^ .+ ?( [ ^ \. \[ ] ) * / ) ) !== null ) {
1709
+ property = property [ 0 ] ;
1710
+ propChain = lastProp + property ;
1711
+ if ( $parse ( propChain ) ( scope ) === undefined ) {
1712
+ angular . hint . emit ( MODULE_NAME + ':undef' , propChain + ' is undefined' ) ;
1713
+ }
1714
+ boundFn = boundFn . replace ( property , '' ) ;
1715
+ lastProp += property ;
1716
+ if ( boundFn . charAt ( 0 ) === '.' ) {
1717
+ lastProp += '.' ;
1718
+ boundFn = boundFn . substr ( 1 ) ;
1719
+ }
1715
1720
}
1716
- }
1717
- } ) ;
1721
+ } ) ;
1722
+ } catch ( e ) {
1723
+ angular . hint . emit ( MODULE_NAME + ':undef' , '' +
1724
+ 'parsing error: please inform the angular-hint ' +
1725
+ 'or batarang teams. expression: ' + boundFuncs . join ( '' ) ) ;
1726
+ }
1718
1727
1719
1728
return linkFn . apply ( this , arguments ) ;
1720
1729
} ;
@@ -1863,14 +1872,36 @@ function decorateRootScope($delegate, $parse) {
1863
1872
1864
1873
var _watch = scopePrototype . $watch ;
1865
1874
var _digestEvents = [ ] ;
1875
+ var skipNextPerfWatchers = false ;
1866
1876
scopePrototype . $watch = function ( watchExpression , reactionFunction ) {
1877
+ // if `skipNextPerfWatchers` is true, this means the previous run of the
1878
+ // `$watch` decorator was a one time binding expression and this invocation
1879
+ // of the $watch function has the `oneTimeInterceptedExpression` (internal angular function)
1880
+ // as the `watchExpression` parameter. If we decorate it with the performance
1881
+ // timers function this will cause us to invoke `oneTimeInterceptedExpression`
1882
+ // on subsequent digest loops and will update the one time bindings
1883
+ // if anything mutated the property.
1884
+ if ( skipNextPerfWatchers ) {
1885
+ skipNextPerfWatchers = false ;
1886
+ return _watch . apply ( this , arguments ) ;
1887
+ }
1888
+
1867
1889
if ( typeof watchExpression === 'string' &&
1868
1890
isOneTimeBindExp ( watchExpression ) ) {
1891
+ skipNextPerfWatchers = true ;
1869
1892
return _watch . apply ( this , arguments ) ;
1870
1893
}
1871
1894
var watchStr = humanReadableWatchExpression ( watchExpression ) ;
1872
1895
var scopeId = this . $id ;
1896
+ var expressions = null ;
1873
1897
if ( typeof watchExpression === 'function' ) {
1898
+ expressions = watchExpression . expressions ;
1899
+ if ( Object . prototype . toString . call ( expressions ) === '[object Array]' &&
1900
+ expressions . some ( isOneTimeBindExp ) ) {
1901
+ skipNextPerfWatchers = true ;
1902
+ return _watch . apply ( this , arguments ) ;
1903
+ }
1904
+
1874
1905
arguments [ 0 ] = function ( ) {
1875
1906
var start = perf . now ( ) ;
1876
1907
var ret = watchExpression . apply ( this , arguments ) ;
0 commit comments