9a9eafb1 by Michael Richards

Set local options on the Rivets.View instance by merging the global with the pas…

…sed-in options in the constructor. Access config, binders and formatters from the view inside of Rivets.Binding so that we get the local options to take precedence over globally set options. [Closes #81, #134]
1 parent d9c1dbac
...@@ -11,12 +11,12 @@ unless String::trim then String::trim = -> @replace /^\s+|\s+$/g, '' ...@@ -11,12 +11,12 @@ unless String::trim then String::trim = -> @replace /^\s+|\s+$/g, ''
11 11
12 # A single binding between a model attribute and a DOM element. 12 # A single binding between a model attribute and a DOM element.
13 class Rivets.Binding 13 class Rivets.Binding
14 # All information about the binding is passed into the constructor; the DOM 14 # All information about the binding is passed into the constructor; the
15 # element, the type of binding, the model object and the keypath at which 15 # containing view, the DOM node, the type of binding, the model object and the
16 # to listen for changes. 16 # keypath at which to listen for changes.
17 constructor: (@el, @type, @model, @keypath, @options = {}) -> 17 constructor: (@view, @el, @type, @model, @keypath, @options = {}) ->
18 unless @binder = Rivets.binders[type] 18 unless @binder = @view.binders[type]
19 for identifier, value of Rivets.binders 19 for identifier, value of @view.binders
20 if identifier isnt '*' and identifier.indexOf('*') isnt -1 20 if identifier isnt '*' and identifier.indexOf('*') isnt -1
21 regexp = new RegExp "^#{identifier.replace('*', '.+')}$" 21 regexp = new RegExp "^#{identifier.replace('*', '.+')}$"
22 if regexp.test type 22 if regexp.test type
...@@ -24,7 +24,7 @@ class Rivets.Binding ...@@ -24,7 +24,7 @@ class Rivets.Binding
24 @args = new RegExp("^#{identifier.replace('*', '(.+)')}$").exec type 24 @args = new RegExp("^#{identifier.replace('*', '(.+)')}$").exec type
25 @args.shift() 25 @args.shift()
26 26
27 @binder or= Rivets.binders['*'] 27 @binder or= @view.binders['*']
28 28
29 if @binder instanceof Function 29 if @binder instanceof Function
30 @binder = {routine: @binder} 30 @binder = {routine: @binder}
...@@ -43,7 +43,7 @@ class Rivets.Binding ...@@ -43,7 +43,7 @@ class Rivets.Binding
43 else if @options?.bindingOptions?.formatters?[id] instanceof Function 43 else if @options?.bindingOptions?.formatters?[id] instanceof Function
44 @options.bindingOptions.formatters[id] 44 @options.bindingOptions.formatters[id]
45 else 45 else
46 Rivets.formatters[id] 46 @view.formatters[id]
47 47
48 if formatter?.read instanceof Function 48 if formatter?.read instanceof Function
49 value = formatter.read value, args... 49 value = formatter.read value, args...
...@@ -67,7 +67,7 @@ class Rivets.Binding ...@@ -67,7 +67,7 @@ class Rivets.Binding
67 @set if @options.bypass 67 @set if @options.bypass
68 @model[@keypath] 68 @model[@keypath]
69 else 69 else
70 Rivets.config.adapter.read @model, @keypath 70 @view.config.adapter.read @model, @keypath
71 71
72 # Publishes the value currently set on the input element back to the model. 72 # Publishes the value currently set on the input element back to the model.
73 publish: => 73 publish: =>
...@@ -77,10 +77,10 @@ class Rivets.Binding ...@@ -77,10 +77,10 @@ class Rivets.Binding
77 args = formatter.split /\s+/ 77 args = formatter.split /\s+/
78 id = args.shift() 78 id = args.shift()
79 79
80 if Rivets.formatters[id]?.publish 80 if @view.formatters[id]?.publish
81 value = Rivets.formatters[id].publish value, args... 81 value = @view.formatters[id].publish value, args...
82 82
83 Rivets.config.adapter.publish @model, @keypath, value 83 @view.config.adapter.publish @model, @keypath, value
84 84
85 # Subscribes to the model for changes at the specified keypath. Bi-directional 85 # Subscribes to the model for changes at the specified keypath. Bi-directional
86 # routines will also listen for changes on the element to propagate them back 86 # routines will also listen for changes on the element to propagate them back
...@@ -91,8 +91,8 @@ class Rivets.Binding ...@@ -91,8 +91,8 @@ class Rivets.Binding
91 if @options.bypass 91 if @options.bypass
92 @sync() 92 @sync()
93 else 93 else
94 Rivets.config.adapter.subscribe @model, @keypath, @sync 94 @view.config.adapter.subscribe @model, @keypath, @sync
95 @sync() if Rivets.config.preloadData 95 @sync() if @view.config.preloadData
96 96
97 if @options.dependencies?.length 97 if @options.dependencies?.length
98 for dependency in @options.dependencies 98 for dependency in @options.dependencies
...@@ -104,14 +104,14 @@ class Rivets.Binding ...@@ -104,14 +104,14 @@ class Rivets.Binding
104 model = @view.models[dependency.shift()] 104 model = @view.models[dependency.shift()]
105 keypath = dependency.join '.' 105 keypath = dependency.join '.'
106 106
107 Rivets.config.adapter.subscribe model, keypath, @sync 107 @view.config.adapter.subscribe model, keypath, @sync
108 108
109 # Unsubscribes from the model and the element. 109 # Unsubscribes from the model and the element.
110 unbind: => 110 unbind: =>
111 @binder.unbind?.call @, @el 111 @binder.unbind?.call @, @el
112 112
113 unless @options.bypass 113 unless @options.bypass
114 Rivets.config.adapter.unsubscribe @model, @keypath, @sync 114 @view.config.adapter.unsubscribe @model, @keypath, @sync
115 115
116 if @options.dependencies?.length 116 if @options.dependencies?.length
117 for dependency in @options.dependencies 117 for dependency in @options.dependencies
...@@ -123,19 +123,26 @@ class Rivets.Binding ...@@ -123,19 +123,26 @@ class Rivets.Binding
123 model = @view.models[dependency.shift()] 123 model = @view.models[dependency.shift()]
124 keypath = dependency.join '.' 124 keypath = dependency.join '.'
125 125
126 Rivets.config.adapter.unsubscribe model, keypath, @sync 126 @view.config.adapter.unsubscribe model, keypath, @sync
127 127
128 # A collection of bindings built from a set of parent elements. 128 # A collection of bindings built from a set of parent elements.
129 class Rivets.View 129 class Rivets.View
130 # The DOM elements and the model objects for binding are passed into the 130 # The DOM elements and the model objects for binding are passed into the
131 # constructor. 131 # constructor along with any local options that should be used throughout the
132 constructor: (@els, @models, @options) -> 132 # context of the view and it's bindings.
133 constructor: (@els, @models, @options = {}) ->
133 @els = [@els] unless (@els.jquery || @els instanceof Array) 134 @els = [@els] unless (@els.jquery || @els instanceof Array)
135
136 for option in ['config', 'binders', 'formatters']
137 @[option] = {}
138 @[option][k] = v for k, v of @options[option] if @options[option]
139 @[option][k] ?= v for k, v of Rivets[option]
140
134 @build() 141 @build()
135 142
136 # Regular expression used to match binding attributes. 143 # Regular expression used to match binding attributes.
137 bindingRegExp: => 144 bindingRegExp: =>
138 prefix = Rivets.config.prefix 145 prefix = @config.prefix
139 if prefix then new RegExp("^data-#{prefix}-") else /^data-/ 146 if prefix then new RegExp("^data-#{prefix}-") else /^data-/
140 147
141 # Parses the DOM tree and builds Rivets.Binding instances for every matched 148 # Parses the DOM tree and builds Rivets.Binding instances for every matched
...@@ -151,14 +158,14 @@ class Rivets.View ...@@ -151,14 +158,14 @@ class Rivets.View
151 for attribute in node.attributes 158 for attribute in node.attributes
152 if bindingRegExp.test attribute.name 159 if bindingRegExp.test attribute.name
153 type = attribute.name.replace bindingRegExp, '' 160 type = attribute.name.replace bindingRegExp, ''
154 unless binder = Rivets.binders[type] 161 unless binder = @binders[type]
155 for identifier, value of Rivets.binders 162 for identifier, value of @binders
156 if identifier isnt '*' and identifier.indexOf('*') isnt -1 163 if identifier isnt '*' and identifier.indexOf('*') isnt -1
157 regexp = new RegExp "^#{identifier.replace('*', '.+')}$" 164 regexp = new RegExp "^#{identifier.replace('*', '.+')}$"
158 if regexp.test type 165 if regexp.test type
159 binder = value 166 binder = value
160 167
161 binder or= Rivets.binders['*'] 168 binder or= @binders['*']
162 169
163 if binder.block 170 if binder.block
164 skipNodes.push n for n in node.getElementsByTagName '*' 171 skipNodes.push n for n in node.getElementsByTagName '*'
...@@ -167,10 +174,6 @@ class Rivets.View ...@@ -167,10 +174,6 @@ class Rivets.View
167 for attribute in attributes or node.attributes 174 for attribute in attributes or node.attributes
168 if bindingRegExp.test attribute.name 175 if bindingRegExp.test attribute.name
169 options = {} 176 options = {}
170
171 if @options? and typeof @options is 'object'
172 options.bindingOptions = @options
173
174 type = attribute.name.replace bindingRegExp, '' 177 type = attribute.name.replace bindingRegExp, ''
175 pipes = (pipe.trim() for pipe in attribute.value.split '|') 178 pipes = (pipe.trim() for pipe in attribute.value.split '|')
176 context = (ctx.trim() for ctx in pipes.shift().split '<') 179 context = (ctx.trim() for ctx in pipes.shift().split '<')
...@@ -189,10 +192,7 @@ class Rivets.View ...@@ -189,10 +192,7 @@ class Rivets.View
189 if dependencies = context.shift() 192 if dependencies = context.shift()
190 options.dependencies = dependencies.split /\s+/ 193 options.dependencies = dependencies.split /\s+/
191 194
192 binding = new Rivets.Binding node, type, model, keypath, options 195 @bindings.push new Rivets.Binding @, node, type, model, keypath, options
193 binding.view = @
194
195 @bindings.push binding
196 196
197 attributes = null if attributes 197 attributes = null if attributes
198 198
...@@ -344,7 +344,7 @@ Rivets.binders = ...@@ -344,7 +344,7 @@ Rivets.binders =
344 "each-*": 344 "each-*":
345 block: true 345 block: true
346 bind: (el, collection) -> 346 bind: (el, collection) ->
347 el.removeAttribute ['data', Rivets.config.prefix, @type].join('-').replace '--', '-' 347 el.removeAttribute ['data', @view.config.prefix, @type].join('-').replace '--', '-'
348 routine: (el, collection) -> 348 routine: (el, collection) ->
349 if @iterated? 349 if @iterated?
350 for view in @iterated 350 for view in @iterated
...@@ -370,7 +370,7 @@ Rivets.binders = ...@@ -370,7 +370,7 @@ Rivets.binders =
370 @marker 370 @marker
371 371
372 @marker.parentNode.insertBefore itemEl, previous.nextSibling ? null 372 @marker.parentNode.insertBefore itemEl, previous.nextSibling ? null
373 view = new Rivets.View(itemEl, data) 373 view = new Rivets.View(itemEl, data, @view.options)
374 view.bind() 374 view.bind()
375 @iterated.push view 375 @iterated.push view
376 376
......