@@ -21,6 +21,93 @@ function Chains() { }
21
21
22
22
Chains . prototype = Object . create ( null ) ;
23
23
24
+ function ChainWatchers ( obj ) {
25
+ // this obj would be the referencing chain node's parent node's value
26
+ this . obj = obj ;
27
+ // chain nodes that reference a key in this obj by key
28
+ // we only create ChainWatchers when we are going to add them
29
+ // so create this upfront
30
+ this . chains = new Chains ( ) ;
31
+ }
32
+
33
+ ChainWatchers . prototype = {
34
+ add ( key , node ) {
35
+ let nodes = this . chains [ key ] ;
36
+ if ( nodes === undefined ) {
37
+ this . chains [ key ] = [ node ] ;
38
+ } else {
39
+ nodes . push ( node ) ;
40
+ }
41
+ } ,
42
+
43
+ remove ( key , node ) {
44
+ let nodes = this . chains [ key ] ;
45
+ if ( nodes ) {
46
+ for ( var i = 0 , l = nodes . length ; i < l ; i ++ ) {
47
+ if ( nodes [ i ] === node ) {
48
+ nodes . splice ( i , 1 ) ;
49
+ break ;
50
+ }
51
+ }
52
+ }
53
+ } ,
54
+
55
+ has ( key , node ) {
56
+ let nodes = this . chains [ key ] ;
57
+ if ( nodes ) {
58
+ for ( var i = 0 , l = nodes . length ; i < l ; i ++ ) {
59
+ if ( nodes [ i ] === node ) {
60
+ return true ;
61
+ }
62
+ }
63
+ }
64
+ return false ;
65
+ } ,
66
+
67
+ revalidateAll ( ) {
68
+ for ( let key in this . chains ) {
69
+ this . notify ( key , true , undefined ) ;
70
+ }
71
+ } ,
72
+
73
+ revalidate ( key ) {
74
+ this . notify ( key , true , undefined ) ;
75
+ } ,
76
+
77
+ // key: the string key that is part of a path changed
78
+ // revalidate: boolean the chains that are watching this value should revalidate
79
+ // callback: function that will be called with the the object and path that
80
+ // will be/are invalidated by this key change depending on the
81
+ // whether the revalidate flag is passed
82
+ notify ( key , revalidate , callback ) {
83
+ let nodes = this . chains [ key ] ;
84
+ if ( nodes === undefined || nodes . length === 0 ) {
85
+ return ;
86
+ }
87
+
88
+ let affected ;
89
+
90
+ if ( callback ) {
91
+ affected = [ ] ;
92
+ }
93
+
94
+ for ( let i = 0 , l = nodes . length ; i < l ; i ++ ) {
95
+ nodes [ i ] . notify ( revalidate , affected ) ;
96
+ }
97
+
98
+ if ( callback === undefined ) {
99
+ return ;
100
+ }
101
+
102
+ // we gather callbacks so we don't notify them during revalidation
103
+ for ( let i = 0 , l = affected . length ; i < l ; i += 2 ) {
104
+ let obj = affected [ i ] ;
105
+ let path = affected [ i + 1 ] ;
106
+ callback ( obj , path ) ;
107
+ }
108
+ }
109
+ } ;
110
+
24
111
var pendingQueue = [ ] ;
25
112
26
113
// attempts to add the pendingQueue chains again. If some of them end up
@@ -49,19 +136,13 @@ function addChainWatcher(obj, keyName, node) {
49
136
return ;
50
137
}
51
138
52
- var m = metaFor ( obj ) ;
53
- var nodes = m . chainWatchers ;
139
+ let m = metaFor ( obj ) ;
54
140
55
- // TODO remove hasOwnProperty check
56
- if ( nodes === undefined || ! m . hasOwnProperty ( 'chainWatchers' ) ) {
57
- nodes = m . chainWatchers = new Chains ( ) ;
141
+ if ( m . chainWatchers === undefined || m . chainWatchers . obj !== obj ) {
142
+ m . chainWatchers = new ChainWatchers ( obj ) ;
58
143
}
59
144
60
- if ( ! nodes [ keyName ] ) {
61
- nodes [ keyName ] = [ node ] ;
62
- } else {
63
- nodes [ keyName ] . push ( node ) ;
64
- }
145
+ m . chainWatchers . add ( keyName , node ) ;
65
146
66
147
watchKey ( obj , keyName , m ) ;
67
148
}
@@ -71,20 +152,18 @@ function removeChainWatcher(obj, keyName, node) {
71
152
return ;
72
153
}
73
154
74
- var m = obj [ '__ember_meta__' ] ;
75
- if ( m && ! m . hasOwnProperty ( 'chainWatchers' ) ) { return ; } // nothing to do
76
-
77
- var nodes = m && m . chainWatchers ;
155
+ let m = obj . __ember_meta__ ;
78
156
79
- if ( nodes && nodes [ keyName ] ) {
80
- nodes = nodes [ keyName ] ;
81
- for ( var i = 0 , l = nodes . length ; i < l ; i ++ ) {
82
- if ( nodes [ i ] === node ) {
83
- nodes . splice ( i , 1 ) ;
84
- break ;
85
- }
86
- }
157
+ if ( ! m ||
158
+ m . chainWatchers === undefined || m . chainWatchers . obj !== obj ) {
159
+ return ;
87
160
}
161
+
162
+ // make meta writable
163
+ m = metaFor ( obj ) ;
164
+
165
+ m . chainWatchers . remove ( keyName , node ) ;
166
+
88
167
unwatchKey ( obj , keyName , m ) ;
89
168
}
90
169
@@ -287,44 +366,8 @@ ChainNode.prototype = {
287
366
}
288
367
} ,
289
368
290
- willChange ( events ) {
291
- var chains = this . _chains ;
292
- var node ;
293
- if ( chains ) {
294
- for ( var key in chains ) {
295
- node = chains [ key ] ;
296
- if ( node !== undefined ) {
297
- node . willChange ( events ) ;
298
- }
299
- }
300
- }
301
-
302
- if ( this . _parent ) {
303
- this . _parent . notifyChainChange ( this , this . _key , 1 , events ) ;
304
- }
305
- } ,
306
-
307
- notifyChainChange ( chain , path , depth , events ) {
308
- if ( this . _key ) {
309
- path = this . _key + '.' + path ;
310
- }
311
-
312
- if ( this . _parent ) {
313
- this . _parent . notifyChainChange ( this , path , depth + 1 , events ) ;
314
- } else {
315
- if ( depth > 1 ) {
316
- events . push ( this . value ( ) , path ) ;
317
- }
318
- path = 'this.' + path ;
319
- if ( this . _paths [ path ] > 0 ) {
320
- events . push ( this . value ( ) , path ) ;
321
- }
322
- }
323
- } ,
324
-
325
- didChange ( events ) {
326
- // invalidate my own value first.
327
- if ( this . _watching ) {
369
+ notify ( revalidate , affected ) {
370
+ if ( revalidate && this . _watching ) {
328
371
var obj = this . _parent . value ( ) ;
329
372
if ( obj !== this . _object ) {
330
373
removeChainWatcher ( this . _object , this . _key , this ) ;
@@ -347,48 +390,49 @@ ChainNode.prototype = {
347
390
for ( var key in chains ) {
348
391
node = chains [ key ] ;
349
392
if ( node !== undefined ) {
350
- node . didChange ( events ) ;
393
+ node . notify ( revalidate , affected ) ;
351
394
}
352
395
}
353
396
}
354
397
355
- // if no events are passed in then we only care about the above wiring update
356
- if ( events === null ) {
357
- return ;
398
+ if ( affected && this . _parent ) {
399
+ this . _parent . populateAffected ( this , this . _key , 1 , affected ) ;
400
+ }
401
+ } ,
402
+
403
+ populateAffected ( chain , path , depth , affected ) {
404
+ if ( this . _key ) {
405
+ path = this . _key + '.' + path ;
358
406
}
359
407
360
- // and finally tell parent about my path changing...
361
408
if ( this . _parent ) {
362
- this . _parent . notifyChainChange ( this , this . _key , 1 , events ) ;
409
+ this . _parent . populateAffected ( this , path , depth + 1 , affected ) ;
410
+ } else {
411
+ if ( depth > 1 ) {
412
+ affected . push ( this . value ( ) , path ) ;
413
+ }
414
+ path = 'this.' + path ;
415
+ if ( this . _paths [ path ] > 0 ) {
416
+ affected . push ( this . value ( ) , path ) ;
417
+ }
363
418
}
364
419
}
365
420
} ;
366
421
367
422
export function finishChains ( obj ) {
368
423
// We only create meta if we really have to
369
- var m = obj [ '__ember_meta__' ] ;
370
- var chains , chainWatchers , chainNodes ;
371
-
424
+ let m = obj . __ember_meta__ ;
372
425
if ( m ) {
373
426
// finish any current chains node watchers that reference obj
374
- chainWatchers = m . chainWatchers ;
427
+ let chainWatchers = m . chainWatchers ;
375
428
if ( chainWatchers ) {
376
- for ( var key in chainWatchers ) {
377
- chainNodes = chainWatchers [ key ] ;
378
- if ( chainNodes ) {
379
- for ( var i = 0 , l = chainNodes . length ; i < l ; i ++ ) {
380
- var node = chainNodes [ i ] ;
381
- if ( node ) {
382
- node . didChange ( null ) ;
383
- }
384
- }
385
- }
386
- }
429
+ chainWatchers . revalidateAll ( ) ;
387
430
}
388
431
// copy chains from prototype
389
- chains = m . chains ;
432
+ let chains = m . chains ;
390
433
if ( chains && chains . value ( ) !== obj ) {
391
- metaFor ( obj ) . chains = chains = chains . copy ( obj ) ;
434
+ // need to check if meta is writable
435
+ metaFor ( obj ) . chains = chains . copy ( obj ) ;
392
436
}
393
437
}
394
438
}
0 commit comments