pakyow-presenter 0.8.rc4 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/pakyow-presenter/CHANGES +4 -0
- data/pakyow-presenter/lib/presenter/attributes.rb +31 -7
- data/pakyow-presenter/lib/presenter/base.rb +3 -0
- data/pakyow-presenter/lib/presenter/binder.rb +10 -6
- data/pakyow-presenter/lib/presenter/binder_set.rb +12 -5
- data/pakyow-presenter/lib/presenter/container.rb +6 -0
- data/pakyow-presenter/lib/presenter/doc_helpers.rb +7 -0
- data/pakyow-presenter/lib/presenter/exceptions.rb +4 -0
- data/pakyow-presenter/lib/presenter/helpers.rb +6 -4
- data/pakyow-presenter/lib/presenter/page.rb +44 -51
- data/pakyow-presenter/lib/presenter/partial.rb +1 -19
- data/pakyow-presenter/lib/presenter/presenter.rb +67 -51
- data/pakyow-presenter/lib/presenter/template.rb +11 -15
- data/pakyow-presenter/lib/presenter/title_helpers.rb +21 -0
- data/pakyow-presenter/lib/presenter/view.rb +280 -124
- data/pakyow-presenter/lib/presenter/view_collection.rb +108 -12
- data/pakyow-presenter/lib/presenter/view_composer.rb +194 -0
- data/pakyow-presenter/lib/presenter/view_store.rb +65 -18
- metadata +18 -15
@@ -2,6 +2,9 @@ module Pakyow
|
|
2
2
|
module Presenter
|
3
3
|
class View
|
4
4
|
include DocHelpers
|
5
|
+
include TitleHelpers
|
6
|
+
|
7
|
+
PARTIAL_REGEX = /<!--\s*@include\s*([a-zA-Z0-9\-_]*)\s*-->/
|
5
8
|
|
6
9
|
class << self
|
7
10
|
attr_accessor :binders
|
@@ -19,15 +22,9 @@ module Pakyow
|
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
|
-
attr_accessor :doc, :scoped_as, :scopes, :related_views
|
25
|
+
attr_accessor :doc, :scoped_as, :scopes, :related_views, :context, :composer
|
23
26
|
attr_writer :bindings
|
24
27
|
|
25
|
-
def dup
|
26
|
-
view = self.class.from_doc(@doc.dup)
|
27
|
-
view.scoped_as = scoped_as
|
28
|
-
return view
|
29
|
-
end
|
30
|
-
|
31
28
|
def initialize(contents = '', format = :html)
|
32
29
|
@related_views = []
|
33
30
|
|
@@ -40,6 +37,15 @@ module Pakyow
|
|
40
37
|
end
|
41
38
|
end
|
42
39
|
|
40
|
+
def initialize_copy(original_view)
|
41
|
+
super
|
42
|
+
|
43
|
+
@doc = original_view.doc.dup
|
44
|
+
@scoped_as = original_view.scoped_as
|
45
|
+
@context = @context
|
46
|
+
@composer = @composer
|
47
|
+
end
|
48
|
+
|
43
49
|
def self.from_doc(doc)
|
44
50
|
view = self.new
|
45
51
|
view.doc = doc
|
@@ -47,35 +53,20 @@ module Pakyow
|
|
47
53
|
end
|
48
54
|
|
49
55
|
def self.load(path)
|
50
|
-
format =
|
56
|
+
format = Utils::String.split_at_last_dot(path)[-1]
|
51
57
|
contents = File.read(path)
|
52
58
|
|
53
59
|
return self.new(contents, format)
|
54
60
|
end
|
55
61
|
|
56
|
-
def title=(title)
|
57
|
-
if @doc
|
58
|
-
if o = @doc.css('title').first
|
59
|
-
o.inner_html = Nokogiri::HTML::fragment(title)
|
60
|
-
else
|
61
|
-
if o = @doc.css('head').first
|
62
|
-
o.add_child(Nokogiri::HTML::fragment("<title>#{title}</title>"))
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def title
|
69
|
-
o = @doc.css('title').first
|
70
|
-
o.inner_html if o
|
71
|
-
end
|
72
|
-
|
73
62
|
# Allows multiple attributes to be set at once.
|
74
63
|
# root_view.find(selector).attributes(:class => my_class, :style => my_style)
|
75
64
|
#
|
76
65
|
def attributes(attrs = {})
|
66
|
+
#TODO this is not invalidating composer
|
67
|
+
|
77
68
|
if attrs.empty?
|
78
|
-
return Attributes.new(self.doc)
|
69
|
+
return Attributes.new(self.doc, @composer)
|
79
70
|
else
|
80
71
|
self.bind_attributes_to_doc(attrs, doc)
|
81
72
|
end
|
@@ -84,8 +75,14 @@ module Pakyow
|
|
84
75
|
alias :attrs :attributes
|
85
76
|
|
86
77
|
def remove
|
87
|
-
|
88
|
-
|
78
|
+
if doc.parent.nil?
|
79
|
+
# best we can do is to remove the children
|
80
|
+
doc.children.remove
|
81
|
+
else
|
82
|
+
doc.remove
|
83
|
+
end
|
84
|
+
|
85
|
+
invalidate!
|
89
86
|
end
|
90
87
|
|
91
88
|
alias :delete :remove
|
@@ -93,7 +90,7 @@ module Pakyow
|
|
93
90
|
def clear
|
94
91
|
return if self.doc.blank?
|
95
92
|
self.doc.inner_html = ''
|
96
|
-
self.
|
93
|
+
self.invalidate!
|
97
94
|
end
|
98
95
|
|
99
96
|
def text
|
@@ -103,7 +100,7 @@ module Pakyow
|
|
103
100
|
def text=(text)
|
104
101
|
text = text.call(self.text) if text.is_a?(Proc)
|
105
102
|
self.doc.content = text.to_s
|
106
|
-
self.
|
103
|
+
self.invalidate!
|
107
104
|
end
|
108
105
|
|
109
106
|
def html
|
@@ -113,7 +110,7 @@ module Pakyow
|
|
113
110
|
def html=(html)
|
114
111
|
html = html.call(self.html) if html.is_a?(Proc)
|
115
112
|
self.doc.inner_html = Nokogiri::HTML.fragment(html.to_s)
|
116
|
-
self.
|
113
|
+
self.invalidate!
|
117
114
|
end
|
118
115
|
|
119
116
|
def append(view)
|
@@ -124,7 +121,7 @@ module Pakyow
|
|
124
121
|
self.doc.add_child(view.doc)
|
125
122
|
|
126
123
|
self.update_binding_offset_at_path(num, path)
|
127
|
-
self.
|
124
|
+
self.invalidate!
|
128
125
|
end
|
129
126
|
|
130
127
|
def prepend(view)
|
@@ -139,7 +136,7 @@ module Pakyow
|
|
139
136
|
end
|
140
137
|
|
141
138
|
self.update_binding_offset_at_path(num, path)
|
142
|
-
self.
|
139
|
+
self.invalidate!
|
143
140
|
end
|
144
141
|
|
145
142
|
def after(view)
|
@@ -150,7 +147,7 @@ module Pakyow
|
|
150
147
|
self.doc.after(view.doc)
|
151
148
|
|
152
149
|
self.update_binding_offset_at_path(num, path)
|
153
|
-
self.
|
150
|
+
self.invalidate!
|
154
151
|
end
|
155
152
|
|
156
153
|
def before(view)
|
@@ -161,22 +158,35 @@ module Pakyow
|
|
161
158
|
self.doc.before(view.doc)
|
162
159
|
|
163
160
|
self.update_binding_offset_at_path(num, path)
|
164
|
-
self.
|
161
|
+
self.invalidate!
|
165
162
|
end
|
166
163
|
|
167
164
|
def replace(view)
|
168
|
-
doc.
|
165
|
+
view = view.doc if view.is_a?(View)
|
166
|
+
|
167
|
+
if doc.parent.nil?
|
168
|
+
doc.children.remove
|
169
|
+
doc.inner_html = view
|
170
|
+
else
|
171
|
+
doc.replace(view)
|
172
|
+
end
|
173
|
+
|
174
|
+
invalidate!
|
169
175
|
end
|
170
176
|
|
171
177
|
def scope(name)
|
172
178
|
name = name.to_sym
|
173
179
|
|
174
180
|
views = ViewCollection.new
|
181
|
+
views.context = @context
|
182
|
+
views.composer = @composer
|
175
183
|
self.bindings.select{|b| b[:scope] == name}.each{|s|
|
176
184
|
v = self.view_from_path(s[:path])
|
177
185
|
|
178
186
|
v.bindings = self.update_binding_paths_from_path([s].concat(s[:nested_bindings]), s[:path])
|
179
187
|
v.scoped_as = s[:scope]
|
188
|
+
v.context = @context
|
189
|
+
v.composer = @composer
|
180
190
|
|
181
191
|
views << v
|
182
192
|
}
|
@@ -188,6 +198,8 @@ module Pakyow
|
|
188
198
|
name = name.to_sym
|
189
199
|
|
190
200
|
views = ViewCollection.new
|
201
|
+
views.context = @context
|
202
|
+
views.composer = @composer
|
191
203
|
|
192
204
|
if binding = self.bindings.select{|binding| binding[:scope] == self.scoped_as}[0]
|
193
205
|
binding[:props].each {|prop|
|
@@ -195,6 +207,8 @@ module Pakyow
|
|
195
207
|
v = self.view_from_path(prop[:path])
|
196
208
|
|
197
209
|
v.scoped_as = self.scoped_as
|
210
|
+
v.context = @context
|
211
|
+
v.composer = @composer
|
198
212
|
views << v
|
199
213
|
end
|
200
214
|
}
|
@@ -208,11 +222,13 @@ module Pakyow
|
|
208
222
|
#
|
209
223
|
# Creates a context in which view manipulations can be performed.
|
210
224
|
#
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
225
|
+
def with(&block)
|
226
|
+
if block.arity == 0
|
227
|
+
self.instance_exec(&block)
|
228
|
+
else
|
229
|
+
yield(self)
|
230
|
+
end
|
231
|
+
|
216
232
|
self
|
217
233
|
end
|
218
234
|
|
@@ -228,7 +244,27 @@ module Pakyow
|
|
228
244
|
def for(data, &block)
|
229
245
|
data = data.to_a if data.is_a?(Enumerator)
|
230
246
|
data = [data] if (!data.is_a?(Enumerable) || data.is_a?(Hash))
|
231
|
-
|
247
|
+
|
248
|
+
if block.arity == 1
|
249
|
+
self.instance_exec(data[0], &block)
|
250
|
+
else
|
251
|
+
block.call(self, data[0])
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# call-seq:
|
256
|
+
# for_with_index {|view, datum, i| block}
|
257
|
+
#
|
258
|
+
# Yields a view, its matching dataum, and the index. See #for.
|
259
|
+
#
|
260
|
+
def for_with_index(data, &block)
|
261
|
+
self.for(data) do |ctx, datum|
|
262
|
+
if block.arity == 2
|
263
|
+
ctx.instance_exec(datum, 0, &block)
|
264
|
+
else
|
265
|
+
block.call(ctx, datum, 0)
|
266
|
+
end
|
267
|
+
end
|
232
268
|
end
|
233
269
|
|
234
270
|
# call-seq:
|
@@ -243,6 +279,8 @@ module Pakyow
|
|
243
279
|
data = [data] if (!data.is_a?(Enumerable) || data.is_a?(Hash))
|
244
280
|
|
245
281
|
views = ViewCollection.new
|
282
|
+
views.context = @context
|
283
|
+
views.composer = @composer
|
246
284
|
data.each {|datum|
|
247
285
|
d_v = self.doc.dup
|
248
286
|
self.doc.before(d_v)
|
@@ -250,6 +288,8 @@ module Pakyow
|
|
250
288
|
v = View.from_doc(d_v)
|
251
289
|
v.bindings = self.bindings.dup
|
252
290
|
v.scoped_as = self.scoped_as
|
291
|
+
v.context = @context
|
292
|
+
v.composer = @composer
|
253
293
|
|
254
294
|
views << v
|
255
295
|
}
|
@@ -267,16 +307,51 @@ module Pakyow
|
|
267
307
|
self.match(data).for(data, &block)
|
268
308
|
end
|
269
309
|
|
310
|
+
# call-seq:
|
311
|
+
# repeat_with_index(data) {|view, datum, i| block}
|
312
|
+
#
|
313
|
+
# Matches self with data and yields a view/datum pair with index.
|
314
|
+
#
|
315
|
+
def repeat_with_index(data, &block)
|
316
|
+
self.match(data).for_with_index(data, &block)
|
317
|
+
end
|
318
|
+
|
270
319
|
# call-seq:
|
271
320
|
# bind(data)
|
272
321
|
#
|
273
322
|
# Binds data across existing scopes.
|
274
323
|
#
|
275
324
|
def bind(data, bindings = {}, &block)
|
325
|
+
data = data.to_a if data.is_a?(Enumerator)
|
326
|
+
data = [data] if (!data.is_a?(Enumerable) || data.is_a?(Hash))
|
327
|
+
|
276
328
|
scope_info = self.bindings.first
|
277
329
|
|
278
|
-
self.bind_data_to_scope(data, scope_info, bindings)
|
279
|
-
|
330
|
+
self.bind_data_to_scope(data[0], scope_info, bindings)
|
331
|
+
invalidate!(true)
|
332
|
+
|
333
|
+
return if block.nil?
|
334
|
+
|
335
|
+
if block.arity == 1
|
336
|
+
self.instance_exec(data[0], &block)
|
337
|
+
else
|
338
|
+
block.call(self, data[0])
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# call-seq:
|
343
|
+
# bind_with_index(data)
|
344
|
+
#
|
345
|
+
# Binds data across existing scopes, yielding a view/datum pair with index.
|
346
|
+
#
|
347
|
+
def bind_with_index(data, bindings = {}, &block)
|
348
|
+
self.bind(data) do |ctx, datum|
|
349
|
+
if block.arity == 2
|
350
|
+
ctx.instance_exec(datum, 0, &block)
|
351
|
+
else
|
352
|
+
block.call(ctx, datum, 0)
|
353
|
+
end
|
354
|
+
end
|
280
355
|
end
|
281
356
|
|
282
357
|
# call-seq:
|
@@ -285,15 +360,74 @@ module Pakyow
|
|
285
360
|
# Matches self to data then binds data to the view.
|
286
361
|
#
|
287
362
|
def apply(data, bindings = {}, &block)
|
288
|
-
|
363
|
+
self.match(data).bind(data, bindings, &block)
|
289
364
|
end
|
290
365
|
|
291
366
|
def bindings(refind = false)
|
292
367
|
@bindings = (!@bindings || refind) ? self.find_bindings : @bindings
|
293
368
|
end
|
294
369
|
|
370
|
+
def includes(partial_map)
|
371
|
+
partial_map = partial_map.dup
|
372
|
+
|
373
|
+
# mixin all the partials
|
374
|
+
partials.each do |partial|
|
375
|
+
partial[1].replace(partial_map[partial[0]].to_s)
|
376
|
+
end
|
377
|
+
|
378
|
+
# now delete them from the map
|
379
|
+
partials.each do |partial|
|
380
|
+
partial_map.delete(partial[0])
|
381
|
+
end
|
382
|
+
|
383
|
+
# we have more partials
|
384
|
+
if partial_map.count > 0
|
385
|
+
# initiate another build if content contains partials
|
386
|
+
includes(partial_map) if partials(true).count > 0
|
387
|
+
end
|
388
|
+
|
389
|
+
return self
|
390
|
+
end
|
391
|
+
|
392
|
+
def invalidate!(composer_only = false)
|
393
|
+
self.bindings(true) unless composer_only
|
394
|
+
@composer.dirty! unless @composer.nil?
|
395
|
+
|
396
|
+
@related_views.each {|v|
|
397
|
+
v.invalidate!(composer_only)
|
398
|
+
}
|
399
|
+
end
|
400
|
+
|
295
401
|
protected
|
296
402
|
|
403
|
+
def partials(refind = false)
|
404
|
+
@partials = (!@partials || refind) ? find_partials : @partials
|
405
|
+
end
|
406
|
+
|
407
|
+
def partials_in(content)
|
408
|
+
partials = []
|
409
|
+
|
410
|
+
content.scan(PARTIAL_REGEX) do |m|
|
411
|
+
partials << m[0].to_sym
|
412
|
+
end
|
413
|
+
|
414
|
+
return partials
|
415
|
+
end
|
416
|
+
|
417
|
+
def find_partials
|
418
|
+
partials = []
|
419
|
+
|
420
|
+
@doc.traverse { |e|
|
421
|
+
next unless e.is_a?(Nokogiri::XML::Comment)
|
422
|
+
next unless match = e.to_html.strip.match(PARTIAL_REGEX)
|
423
|
+
|
424
|
+
name = match[1]
|
425
|
+
partials << [name.to_sym, e]
|
426
|
+
}
|
427
|
+
|
428
|
+
return partials
|
429
|
+
end
|
430
|
+
|
297
431
|
# populates the root_view using view_store data by recursively building
|
298
432
|
# and substituting in child views named in the structure
|
299
433
|
def populate_view(root_view, view_store, view_info)
|
@@ -301,6 +435,8 @@ module Pakyow
|
|
301
435
|
next unless path = view_info[e[:name]]
|
302
436
|
|
303
437
|
v = self.populate_view(View.new(path, view_store), view_store, view_info)
|
438
|
+
v.context = @context
|
439
|
+
v.composer = @composer
|
304
440
|
self.reset_container(e[:doc])
|
305
441
|
self.add_content_to_container(v, e[:doc])
|
306
442
|
}
|
@@ -325,7 +461,7 @@ module Pakyow
|
|
325
461
|
# this is the root node, which we need as the first hash in the
|
326
462
|
# list of bindings, but we don't want to nest other scopes inside
|
327
463
|
# of it in this case
|
328
|
-
bindings.last[:nested_bindings] =
|
464
|
+
bindings.last[:nested_bindings] = []
|
329
465
|
else
|
330
466
|
bindings.last[:nested_bindings] = find_bindings(o, true)
|
331
467
|
# reject so children aren't traversed
|
@@ -334,12 +470,14 @@ module Pakyow
|
|
334
470
|
}
|
335
471
|
|
336
472
|
# find unscoped props
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
473
|
+
unless doc[Config::Presenter.scope_attribute]
|
474
|
+
bindings.unshift({
|
475
|
+
:scope => nil,
|
476
|
+
:path => [],
|
477
|
+
:props => find_props(doc),
|
478
|
+
:nested_bindings => []
|
479
|
+
})
|
480
|
+
end
|
343
481
|
|
344
482
|
return bindings
|
345
483
|
end
|
@@ -388,42 +526,42 @@ module Pakyow
|
|
388
526
|
}
|
389
527
|
end
|
390
528
|
|
391
|
-
def refind_significant_nodes
|
392
|
-
self.bindings(true)
|
393
|
-
|
394
|
-
@related_views.each {|v|
|
395
|
-
v.refind_significant_nodes
|
396
|
-
}
|
397
|
-
end
|
398
|
-
|
399
529
|
def bind_data_to_scope(data, scope_info, bindings = {})
|
400
530
|
return unless data
|
401
531
|
|
402
532
|
scope = scope_info[:scope]
|
403
533
|
|
404
|
-
|
405
|
-
if value = Pakyow.app.presenter.binder.value_for_prop(:_root, scope, data, bindings)
|
406
|
-
value.is_a?(Hash) ? self.bind_attributes_to_doc(value, self.doc) : self.bind_value_to_doc(value, self.doc)
|
407
|
-
end
|
534
|
+
bind_data_to_root(data, scope, bindings)
|
408
535
|
|
409
|
-
scope_info[:props].each {|prop_info|
|
536
|
+
scope_info[:props].each { |prop_info|
|
410
537
|
catch(:unbound) {
|
411
538
|
prop = prop_info[:prop]
|
412
539
|
|
413
|
-
|
414
|
-
|
540
|
+
if data_has_prop?(data, prop) || Pakyow.app.presenter.binder.has_prop?(prop, scope, bindings)
|
541
|
+
value = Pakyow.app.presenter.binder.value_for_prop(prop, scope, data, bindings, context)
|
542
|
+
doc = doc_from_path(prop_info[:path])
|
415
543
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
self.bind_to_form_field(doc, scope, prop, value, data) if View.form_field?(doc.name)
|
544
|
+
if View.form_field?(doc.name)
|
545
|
+
bind_to_form_field(doc, scope, prop, value, data)
|
546
|
+
end
|
420
547
|
|
421
|
-
|
422
|
-
|
548
|
+
bind_data_to_doc(doc, value)
|
549
|
+
else
|
550
|
+
handle_unbound_data(scope, prop)
|
551
|
+
end
|
423
552
|
}
|
424
553
|
}
|
425
554
|
end
|
426
555
|
|
556
|
+
def bind_data_to_root(data, scope, bindings)
|
557
|
+
return unless value = Pakyow.app.presenter.binder.value_for_prop(:_root, scope, data, bindings, context)
|
558
|
+
value.is_a?(Hash) ? self.bind_attributes_to_doc(value, self.doc) : self.bind_value_to_doc(value, self.doc)
|
559
|
+
end
|
560
|
+
|
561
|
+
def bind_data_to_doc(doc, data)
|
562
|
+
data.is_a?(Hash) ? self.bind_attributes_to_doc(data, doc) : self.bind_value_to_doc(data, doc)
|
563
|
+
end
|
564
|
+
|
427
565
|
def data_has_prop?(data, prop)
|
428
566
|
(data.is_a?(Hash) && (data.key?(prop) || data.key?(prop.to_s))) || (!data.is_a?(Hash) && data.class.method_defined?(prop))
|
429
567
|
end
|
@@ -433,6 +571,7 @@ module Pakyow
|
|
433
571
|
|
434
572
|
tag = doc.name
|
435
573
|
return if View.tag_without_value?(tag)
|
574
|
+
|
436
575
|
if View.self_closing_tag?(tag)
|
437
576
|
# don't override value if set
|
438
577
|
if !doc['value'] || doc['value'].empty?
|
@@ -458,72 +597,89 @@ module Pakyow
|
|
458
597
|
attr = attr.to_s
|
459
598
|
attrs = Attributes.new(doc)
|
460
599
|
v = v.call(attrs.send(attr)) if v.is_a?(Proc)
|
461
|
-
|
600
|
+
|
601
|
+
if v.nil?
|
602
|
+
doc.remove_attribute(attr)
|
603
|
+
else
|
604
|
+
attrs.send(:"#{attr}=", v)
|
605
|
+
end
|
462
606
|
end
|
463
607
|
end
|
464
608
|
|
465
609
|
def bind_to_form_field(doc, scope, prop, value, bindable)
|
466
|
-
|
467
|
-
# don't overwrite the name if already defined
|
468
|
-
if !doc['name'] || doc['name'].empty?
|
469
|
-
# set name on form element
|
470
|
-
doc['name'] = "#{scope}[#{prop}]"
|
471
|
-
end
|
610
|
+
set_form_field_name(doc, scope, prop)
|
472
611
|
|
473
612
|
# special binding for checkboxes and radio buttons
|
474
613
|
if doc.name == 'input' && (doc[:type] == 'checkbox' || doc[:type] == 'radio')
|
475
|
-
|
476
|
-
doc[:checked] = 'checked'
|
477
|
-
else
|
478
|
-
doc.delete('checked')
|
479
|
-
end
|
480
|
-
|
481
|
-
# coerce to string since booleans are often used
|
482
|
-
# and fail when binding to a view
|
483
|
-
value = value.to_s
|
614
|
+
bind_to_checked_field(doc, value)
|
484
615
|
# special binding for selects
|
485
616
|
elsif doc.name == 'select'
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
617
|
+
bind_to_select_field(doc, scope, prop, value, bindable)
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
def bind_to_checked_field(doc, value)
|
622
|
+
if value == true || (doc[:value] && doc[:value] == value.to_s)
|
623
|
+
doc[:checked] = 'checked'
|
624
|
+
else
|
625
|
+
doc.delete('checked')
|
626
|
+
end
|
627
|
+
|
628
|
+
# coerce to string since booleans are often used and fail when binding to a view
|
629
|
+
value = value.to_s
|
630
|
+
end
|
631
|
+
|
632
|
+
def bind_to_select_field(doc, scope, prop, value, bindable)
|
633
|
+
create_select_options(doc, scope, prop, value, bindable)
|
634
|
+
select_option_with_value(doc, value)
|
635
|
+
end
|
636
|
+
|
637
|
+
def set_form_field_name(doc, scope, prop)
|
638
|
+
return if doc['name'] && !doc['name'].empty? # don't overwrite the name if already defined
|
639
|
+
doc['name'] = "#{scope}[#{prop}]"
|
640
|
+
end
|
641
|
+
|
642
|
+
def create_select_options(doc, scope, prop, value, bindable)
|
643
|
+
return unless options = Pakyow.app.presenter.binder.options_for_prop(prop, scope, bindable, context)
|
644
|
+
|
645
|
+
option_nodes = Nokogiri::HTML::DocumentFragment.parse ""
|
646
|
+
Nokogiri::HTML::Builder.with(option_nodes) do |h|
|
647
|
+
until options.length == 0
|
648
|
+
catch :optgroup do
|
649
|
+
o = options.first
|
650
|
+
|
651
|
+
# an array containing value/content
|
652
|
+
if o.is_a?(Array)
|
653
|
+
h.option o[1], :value => o[0]
|
654
|
+
options.shift
|
655
|
+
# likely an object (e.g. string); start a group
|
656
|
+
else
|
657
|
+
h.optgroup(:label => o) {
|
658
|
+
options.shift
|
659
|
+
|
660
|
+
options[0..-1].each_with_index { |o2,i2|
|
661
|
+
# starting a new group
|
662
|
+
throw :optgroup if !o2.is_a?(Array)
|
663
|
+
|
664
|
+
h.option o2[1], :value => o2[0]
|
496
665
|
options.shift
|
497
|
-
|
498
|
-
|
499
|
-
h.optgroup(:label => o) {
|
500
|
-
options.shift
|
501
|
-
|
502
|
-
options[0..-1].each_with_index { |o2,i2|
|
503
|
-
# starting a new group
|
504
|
-
throw :optgroup if !o2.is_a?(Array)
|
505
|
-
|
506
|
-
h.option o2[1], :value => o2[0]
|
507
|
-
options.shift
|
508
|
-
}
|
509
|
-
}
|
510
|
-
end
|
511
|
-
end
|
666
|
+
}
|
667
|
+
}
|
512
668
|
end
|
513
669
|
end
|
670
|
+
end
|
671
|
+
end
|
514
672
|
|
515
|
-
|
516
|
-
|
673
|
+
# remove existing options
|
674
|
+
doc.children.remove
|
517
675
|
|
518
|
-
|
519
|
-
|
520
|
-
|
676
|
+
# add generated options
|
677
|
+
doc.add_child(option_nodes)
|
678
|
+
end
|
521
679
|
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
end
|
526
|
-
end
|
680
|
+
def select_option_with_value(doc, value)
|
681
|
+
return unless o = doc.css('option[value="' + value.to_s + '"]').first
|
682
|
+
o[:selected] = 'selected'
|
527
683
|
end
|
528
684
|
|
529
685
|
def handle_unbound_data(scope, prop)
|