@@ -52,6 +52,7 @@ defineScalyrAngularModule('gatedScope', [])
5252 result . $$parentGatingFunction = this . $$gatingFunction ;
5353 result . $$shouldGateFunction = this . $$shouldGateFunction ;
5454 result . $$gatedWatchers = [ ] ;
55+ result . $$cleanUpQueue = this . $$cleanUpQueue ;
5556
5657 return result ;
5758 } ;
@@ -83,6 +84,14 @@ defineScalyrAngularModule('gatedScope', [])
8384 if ( watch . gatingFunction !== targetGatingFunction )
8485 continue ;
8586
87+ // Since we are about to execute the watcher as part of a digestGated
88+ // call, we can remove it from the normal digest queue if it was placed
89+ // there because the watcher was added after the gate function's first
90+ // evaluation.
91+ if ( watch && ! isNull ( watch . cleanUp ) ) {
92+ watch . cleanUp ( ) ;
93+ watch . cleanUp = null ;
94+ }
8695 // Most common watches are on primitives, in which case we can short
8796 // circuit it with === operator, only when === fails do we use .equals
8897 if ( watch && ( value = watch . get ( current ) ) !== ( last = watch . last ) &&
@@ -117,6 +126,8 @@ defineScalyrAngularModule('gatedScope', [])
117126 }
118127 } while ( ( current = next ) ) ;
119128
129+ // Mark that this gating function has digested all children.
130+ targetGatingFunction . hasDigested = true ;
120131 return dirty ;
121132 } ;
122133
@@ -141,11 +152,38 @@ defineScalyrAngularModule('gatedScope', [])
141152 var result = scopePrototype . $watch . call ( this , watchExpression , listener , objectEquality ) ;
142153 this . $$watchers = tmp ;
143154 this . $$gatedWatchers [ 0 ] . gatingFunction = this . $$gatingFunction ;
155+ this . $$gatedWatchers [ 0 ] . cleanUp = null ;
144156
145157 // We know that the last field of the watcher object will be set to initWatchVal, so we
146158 // grab it here.
147159 initWatchVal = this . $$gatedWatchers [ 0 ] . last ;
160+ var watch = this . $$gatedWatchers [ 0 ] ;
161+
162+ // We should make sure the watch expression gets evaluated fully on at least one
163+ // digest cycle even if the gate function is now closed if requested by the gating function's
164+ // value for shouldEvalNewWatchers. We do this by adding in normal watcher that will execute
165+ // the watcher we just added and remove itself after the digest cycle completes.
166+ if ( this . $$gatingFunction . shouldEvalNewWatchers && this . $$gatingFunction . hasDigested ) {
167+ var self = this ;
168+ watch . cleanUp = scopePrototype . $watch . call ( self , function ( ) {
169+ if ( ! isNull ( watch . cleanUp ) ) {
170+ self . $$cleanUpQueue . unshift ( watch . cleanUp ) ;
171+ watch . cleanUp = null ;
172+ }
173+ var value ;
174+ var last = initWatchVal ;
148175
176+ if ( watch && ( value = watch . get ( self ) ) !== ( last = watch . last ) &&
177+ ! ( watch . eq
178+ ? areEqual ( value , last )
179+ : ( typeof value == 'number' && typeof last == 'number'
180+ && isNaN ( value ) && isNaN ( last ) ) ) ) {
181+ watch . last = watch . eq ? copy ( value ) : value ;
182+ watch . fn ( value , ( ( last === initWatchVal ) ? value : last ) , self ) ;
183+ }
184+ return watch . last ;
185+ } ) ;
186+ }
149187 return result ;
150188 } else {
151189 return scopePrototype . $watch . call ( this , watchExpression , listener , objectEquality ) ;
@@ -168,8 +206,8 @@ defineScalyrAngularModule('gatedScope', [])
168206 // functions and should be evaluated at all. However, if a caller is invoking
169207 // $digest on a particular scope, we assume the caller is doing that because it
170208 // knows the watchers should be evaluated.
209+ var dirty = false ;
171210 if ( ! isNull ( this . $$parentGatingFunction ) && this . $$parentGatingFunction ( ) ) {
172- var dirty = false ;
173211 var ttl = 5 ;
174212 do {
175213 dirty = this . $digestGated ( this . $$parentGatingFunction ) ;
@@ -181,7 +219,19 @@ defineScalyrAngularModule('gatedScope', [])
181219 }
182220 } while ( dirty ) ;
183221 }
184- return scopePrototype . $digest . call ( this ) || dirty ;
222+
223+ dirty = scopePrototype . $digest . call ( this ) || dirty ;
224+
225+ var cleanUpQueue = this . $$cleanUpQueue ;
226+
227+ while ( cleanUpQueue . length )
228+ try {
229+ cleanUpQueue . shift ( ) ( ) ;
230+ } catch ( e ) {
231+ $exceptionHandler ( e ) ;
232+ }
233+
234+ return dirty ;
185235 }
186236
187237 /**
@@ -198,8 +248,13 @@ defineScalyrAngularModule('gatedScope', [])
198248 * a new watcher will be gated using gatingFunction. It is evaluated with the
199249 * arguments to $watch and should return true if the watcher created by those
200250 * arguments should be gated
251+ * @param {Boolean } shouldEvalNewWatchers If true, if a watcher is added
252+ * after the gating function has returned true on a previous digest cycle, the
253+ * the new watcher will be evaluated on the next digest cycle even if the
254+ * gating function is currently return false.
201255 */
202- methodsToAdd . $addWatcherGate = function ( gatingFunction , shouldGateFunction ) {
256+ methodsToAdd . $addWatcherGate = function ( gatingFunction , shouldGateFunction ,
257+ shouldEvalNewWatchers ) {
203258 var changeCount = 0 ;
204259 var self = this ;
205260
@@ -215,30 +270,36 @@ defineScalyrAngularModule('gatedScope', [])
215270 // true (which we can tell if the watcher we register here is evaluated), then
216271 // we always evaluate our watcher until our gating function returns true.
217272 var hasNestedGates = ! isNull ( this . $$gatingFunction ) ;
218- var promotedWatcher = null ;
219-
220- this . $watch ( function ( ) {
221- if ( gatingFunction ( ) ) {
222- if ( self . $digestGated ( gatingFunction ) )
223- ++ changeCount ;
224- } else if ( hasNestedGates && isNull ( promotedWatcher ) ) {
225- promotedWatcher = scopePrototype . $watch . call ( self , function ( ) {
226- if ( gatingFunction ( ) ) {
227- promotedWatcher ( ) ;
228- promotedWatcher = null ;
229- if ( self . $digestGated ( gatingFunction ) )
230- ++ changeCount ;
231- }
232- return changeCount ;
233- } ) ;
234- }
235- return changeCount ;
236- } ) ;
273+
274+ ( function ( ) {
275+ var promotedWatcher = null ;
276+
277+ self . $watch ( function ( ) {
278+ if ( gatingFunction ( ) ) {
279+ if ( self . $digestGated ( gatingFunction ) )
280+ ++ changeCount ;
281+ } else if ( hasNestedGates && isNull ( promotedWatcher ) ) {
282+ promotedWatcher = scopePrototype . $watch . call ( self , function ( ) {
283+ if ( gatingFunction ( ) ) {
284+ promotedWatcher ( ) ;
285+ promotedWatcher = null ;
286+ if ( self . $digestGated ( gatingFunction ) )
287+ ++ changeCount ;
288+ }
289+ return changeCount ;
290+ } ) ;
291+ }
292+ return changeCount ;
293+ } ) ;
294+ } ) ( ) ;
237295
238296
239297 if ( isUndefined ( shouldGateFunction ) )
240298 shouldGateFunction = null ;
299+ if ( isUndefined ( shouldEvalNewWatchers ) )
300+ shouldEvalNewWatchers = false ;
241301 this . $$gatingFunction = gatingFunction ;
302+ this . $$gatingFunction . shouldEvalNewWatchers = shouldEvalNewWatchers ;
242303 this . $$shouldGateFunction = shouldGateFunction ;
243304 } ;
244305
@@ -254,6 +315,7 @@ defineScalyrAngularModule('gatedScope', [])
254315 $rootScope . $$parentGatingFunction = null ;
255316 $rootScope . $$shouldGateFunction = null ;
256317 $rootScope . $$gatedWatchers = [ ] ;
318+ $rootScope . $$cleanUpQueue = [ ] ;
257319
258320 return $rootScope ;
259321 } ] ) ;
0 commit comments