299b1c25 by Michael Richards

Implement a portable keypath observer.

1 parent 20f4b3c6
...@@ -20,6 +20,7 @@ module.exports = (grunt) -> ...@@ -20,6 +20,7 @@ module.exports = (grunt) ->
20 'src/view.coffee' 20 'src/view.coffee'
21 'src/bindings.coffee' 21 'src/bindings.coffee'
22 'src/parsers.coffee' 22 'src/parsers.coffee'
23 'src/keypath_observer.coffee'
23 'src/binders.coffee' 24 'src/binders.coffee'
24 'src/adapters.coffee' 25 'src/adapters.coffee'
25 'src/export.coffee' 26 'src/export.coffee'
......
...@@ -27,35 +27,15 @@ class Rivets.Binding ...@@ -27,35 +27,15 @@ class Rivets.Binding
27 @binder = {routine: @binder} if @binder instanceof Function 27 @binder = {routine: @binder} if @binder instanceof Function
28 28
29 setModel: => 29 setModel: =>
30 interfaces = (k for k, v of @view.adapters) 30 observer = new KeypathObserver @view, @view.models, @keypath, (target) =>
31 tokens = Rivets.KeypathParser.parse @keypath, interfaces, @view.config.rootInterface
32
33 @rootKey = tokens.shift()
34 @key = tokens.pop()
35 @objectPath ?= []
36
37 model = @view.adapters[@rootKey.interface].read @view.models, @rootKey.path
38
39 for token, index in tokens
40 current = @view.adapters[token.interface].read model, token.path
41
42 if @objectPath[index]?
43 if current isnt @objectPath[index]
44 @view.adapters[token.interface].unsubscribe model, token.path, @setModel
45 @view.adapters[token.interface].subscribe current, token.path, @setModel
46 else
47 @view.adapters[token.interface].subscribe model, token.path, @setModel
48
49 model = current
50
51 if @model
52 if @model isnt model
53 @unbind true if @key 31 @unbind true if @key
54 @model = model 32 @model = target
55 @bind true if @key 33 @bind true if @key
56 @sync() 34 @sync()
57 else 35
58 @model = model 36 @rootKey = observer.root
37 @key = observer.key
38 @model = observer.target
59 39
60 # Applies all the current formatters to the supplied value and returns the 40 # Applies all the current formatters to the supplied value and returns the
61 # formatted value. 41 # formatted value.
......
1 class KeypathObserver
2 constructor: (@view, @model, @keypath, @callback) ->
3 @interfaces = (k for k, v of @view.adapters)
4 @objectPath = []
5 @tokens = Rivets.KeypathParser.parse @keypath, @interfaces, @view.config.rootInterface
6 @root = @tokens.shift()
7 @key = @tokens.pop()
8 @target = @realize()
9
10 update: =>
11 unless (next = @realize()) is @target
12 @callback @target = next
13
14 realize: =>
15 current = @view.adapters[@root.interface].read @view.models, @root.path
16
17 for token, index in @tokens
18 next = @view.adapters[token.interface].read current, token.path
19
20 if @objectPath[index]?
21 if next isnt @objectPath[index]
22 @view.adapters[token.interface].unsubscribe current, token.path, @update
23 @view.adapters[token.interface].subscribe next, token.path, @update
24 else
25 @view.adapters[token.interface].subscribe current, token.path, @update
26
27 current = next
28
29 current