@@ -28,19 +28,70 @@ var Emitter = require('./emitter'),
2828// an observed array
2929var ArrayProxy = Object . create ( Array . prototype )
3030
31- // Define mutation interceptors so we can emit the mutation info
31+ /**
32+ * Define mutation interceptors so we can emit the mutation info
33+ */
3234methods . forEach ( function ( method ) {
3335 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
4141 } , ! hasProto )
4242} )
4343
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+
4495/**
4596 * Convenience method to remove an element in an Array
4697 * This will be attached to observed Array instances
@@ -101,7 +152,7 @@ def(ArrayProxy, 'replace', replaceElement, !hasProto)
101152 */
102153function watchObject ( obj ) {
103154 for ( var key in obj ) {
104- convert ( obj , key )
155+ convertKey ( obj , key )
105156 }
106157}
107158
@@ -122,14 +173,15 @@ function watchArray (arr) {
122173 def ( arr , key , ArrayProxy [ key ] )
123174 }
124175 }
176+ linkArrayElements ( arr , arr )
125177}
126178
127179/**
128180 * Define accessors for a property on an Object
129181 * so it emits get/set events.
130182 * Then watch the value itself.
131183 */
132- function convert ( obj , key ) {
184+ function convertKey ( obj , key ) {
133185 var keyPrefix = key . charAt ( 0 )
134186 if ( keyPrefix === '$' || keyPrefix === '_' ) {
135187 return
@@ -238,19 +290,36 @@ function ensurePath (obj, key) {
238290 sec = path [ i ]
239291 if ( ! obj [ sec ] ) {
240292 obj [ sec ] = { }
241- if ( obj . __emitter__ ) convert ( obj , sec )
293+ if ( obj . __emitter__ ) convertKey ( obj , sec )
242294 }
243295 obj = obj [ sec ]
244296 }
245297 if ( typeOf ( obj ) === OBJECT ) {
246298 sec = path [ i ]
247299 if ( ! ( sec in obj ) ) {
248300 obj [ sec ] = undefined
249- if ( obj . __emitter__ ) convert ( obj , sec )
301+ if ( obj . __emitter__ ) convertKey ( obj , sec )
250302 }
251303 }
252304}
253305
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+
254323/**
255324 * Observe an object with a given path,
256325 * and proxy get/set/mutate events to the provided observer.
@@ -260,15 +329,8 @@ function observe (obj, rawPath, observer) {
260329 if ( ! isWatchable ( obj ) ) return
261330
262331 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__
272334
273335 // setup proxy listeners on the parent observer.
274336 // we need to keep reference to them so that they
@@ -351,7 +413,7 @@ var pub = module.exports = {
351413 observe : observe ,
352414 unobserve : unobserve ,
353415 ensurePath : ensurePath ,
354- convert : convert ,
416+ convertKey : convertKey ,
355417 copyPaths : copyPaths ,
356418 watchArray : watchArray
357419}
0 commit comments