@@ -28,19 +28,70 @@ var Emitter = require('./emitter'),
28
28
// an observed array
29
29
var ArrayProxy = Object . create ( Array . prototype )
30
30
31
- // Define mutation interceptors so we can emit the mutation info
31
+ /**
32
+ * Define mutation interceptors so we can emit the mutation info
33
+ */
32
34
methods . forEach ( function ( method ) {
33
35
def ( ArrayProxy , method , function ( ) {
34
- var result = Array . prototype [ method ] . apply ( this , arguments )
35
- this . __emitter__ . emit ( 'mutate' , null , this , {
36
- method : method ,
37
- args : slice . call ( arguments ) ,
38
- result : result
39
- } )
40
- return result
36
+ var mutation = applyMutation ( this , method , slice . call ( arguments ) )
37
+ linkArrayElements ( this , mutation . inserted )
38
+ unlinkArrayElements ( this , mutation . removed )
39
+ this . __emitter__ . emit ( 'mutate' , null , this , mutation )
40
+ return mutation . result
41
41
} , ! hasProto )
42
42
} )
43
43
44
+ /**
45
+ * Mutate the Array and extract mutation info
46
+ */
47
+ function applyMutation ( arr , method , args ) {
48
+ var result = Array . prototype [ method ] . apply ( arr , args ) ,
49
+ mutation = {
50
+ method : method ,
51
+ args : args ,
52
+ result : result
53
+ }
54
+ if ( method === 'push' || method === 'unshift' ) {
55
+ mutation . inserted = args
56
+ } else if ( method === 'pop' || method === 'shift' ) {
57
+ mutation . removed = [ result ]
58
+ } else if ( method === 'splice' ) {
59
+ mutation . inserted = args . slice ( 2 )
60
+ mutation . removed = result
61
+ }
62
+ return mutation
63
+ }
64
+
65
+ function linkArrayElements ( arr , items ) {
66
+ if ( items ) {
67
+ var i = items . length , item
68
+ while ( i -- ) {
69
+ item = items [ i ]
70
+ if ( typeOf ( item ) === 'Object' ) {
71
+ convert ( item )
72
+ watchObject ( item )
73
+ if ( ! item . __ownerArrays__ ) {
74
+ def ( item , '__ownerArrays__' , [ ] )
75
+ }
76
+ item . __ownerArrays__ . push ( arr )
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ function unlinkArrayElements ( arr , items ) {
83
+ if ( items ) {
84
+ var i = items . length , item
85
+ while ( i -- ) {
86
+ item = items [ i ]
87
+ if ( typeOf ( item ) === 'Object' ) {
88
+ var owners = item . __ownerArrays__
89
+ owners . splice ( owners . indexOf ( arr ) )
90
+ }
91
+ }
92
+ }
93
+ }
94
+
44
95
/**
45
96
* Convenience method to remove an element in an Array
46
97
* This will be attached to observed Array instances
@@ -101,7 +152,7 @@ def(ArrayProxy, 'replace', replaceElement, !hasProto)
101
152
*/
102
153
function watchObject ( obj ) {
103
154
for ( var key in obj ) {
104
- convert ( obj , key )
155
+ convertKey ( obj , key )
105
156
}
106
157
}
107
158
@@ -122,14 +173,15 @@ function watchArray (arr) {
122
173
def ( arr , key , ArrayProxy [ key ] )
123
174
}
124
175
}
176
+ linkArrayElements ( arr , arr )
125
177
}
126
178
127
179
/**
128
180
* Define accessors for a property on an Object
129
181
* so it emits get/set events.
130
182
* Then watch the value itself.
131
183
*/
132
- function convert ( obj , key ) {
184
+ function convertKey ( obj , key ) {
133
185
var keyPrefix = key . charAt ( 0 )
134
186
if ( keyPrefix === '$' || keyPrefix === '_' ) {
135
187
return
@@ -238,19 +290,36 @@ function ensurePath (obj, key) {
238
290
sec = path [ i ]
239
291
if ( ! obj [ sec ] ) {
240
292
obj [ sec ] = { }
241
- if ( obj . __emitter__ ) convert ( obj , sec )
293
+ if ( obj . __emitter__ ) convertKey ( obj , sec )
242
294
}
243
295
obj = obj [ sec ]
244
296
}
245
297
if ( typeOf ( obj ) === OBJECT ) {
246
298
sec = path [ i ]
247
299
if ( ! ( sec in obj ) ) {
248
300
obj [ sec ] = undefined
249
- if ( obj . __emitter__ ) convert ( obj , sec )
301
+ if ( obj . __emitter__ ) convertKey ( obj , sec )
250
302
}
251
303
}
252
304
}
253
305
306
+ function convert ( obj ) {
307
+ if ( obj . __emitter__ ) return false
308
+ var emitter = new Emitter ( )
309
+ def ( obj , '__emitter__' , emitter )
310
+ emitter . on ( 'set' , function ( ) {
311
+ var owners = obj . __ownerArrays__
312
+ if ( owners ) {
313
+ var i = owners . length
314
+ while ( i -- ) {
315
+ owners [ i ] . __emitter__ . emit ( 'set' , '' )
316
+ }
317
+ }
318
+ } )
319
+ emitter . values = utils . hash ( )
320
+ return true
321
+ }
322
+
254
323
/**
255
324
* Observe an object with a given path,
256
325
* and proxy get/set/mutate events to the provided observer.
@@ -260,15 +329,8 @@ function observe (obj, rawPath, observer) {
260
329
if ( ! isWatchable ( obj ) ) return
261
330
262
331
var path = rawPath ? rawPath + '.' : '' ,
263
- alreadyConverted = ! ! obj . __emitter__ ,
264
- emitter
265
-
266
- if ( ! alreadyConverted ) {
267
- def ( obj , '__emitter__' , new Emitter ( ) )
268
- }
269
-
270
- emitter = obj . __emitter__
271
- emitter . values = emitter . values || utils . hash ( )
332
+ alreadyConverted = ! convert ( obj ) ,
333
+ emitter = obj . __emitter__
272
334
273
335
// setup proxy listeners on the parent observer.
274
336
// we need to keep reference to them so that they
@@ -351,7 +413,7 @@ var pub = module.exports = {
351
413
observe : observe ,
352
414
unobserve : unobserve ,
353
415
ensurePath : ensurePath ,
354
- convert : convert ,
416
+ convertKey : convertKey ,
355
417
copyPaths : copyPaths ,
356
418
watchArray : watchArray
357
419
}
0 commit comments