mullen-wee 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +127 -0
- data/Rakefile +25 -0
- data/aWee.gemspec +26 -0
- data/examples/ObjectSpaceBrowser.rb +191 -0
- data/examples/ajax.rb +73 -0
- data/examples/apotomo-webhunter/main.rb +75 -0
- data/examples/apotomo-webhunter/public/images/bear_trap_charged.png +0 -0
- data/examples/apotomo-webhunter/public/images/bear_trap_snapped.png +0 -0
- data/examples/apotomo-webhunter/public/images/cheese.png +0 -0
- data/examples/apotomo-webhunter/public/images/dark_forest.jpg +0 -0
- data/examples/apotomo-webhunter/public/images/mouse.png +0 -0
- data/examples/apotomo-webhunter/public/javascripts/jquery-1.3.2.min.js +19 -0
- data/examples/apotomo-webhunter/public/javascripts/wee-jquery.js +19 -0
- data/examples/apotomo-webhunter/public/stylesheets/forest.css +33 -0
- data/examples/arc_challenge.rb +42 -0
- data/examples/arc_challenge2.rb +46 -0
- data/examples/cheese_task.rb +27 -0
- data/examples/continuations.rb +28 -0
- data/examples/demo.rb +135 -0
- data/examples/demo/calculator.rb +63 -0
- data/examples/demo/calendar.rb +333 -0
- data/examples/demo/counter.rb +38 -0
- data/examples/demo/editable_counter.rb +36 -0
- data/examples/demo/example.rb +142 -0
- data/examples/demo/file_upload.rb +19 -0
- data/examples/demo/messagebox.rb +15 -0
- data/examples/demo/radio.rb +33 -0
- data/examples/demo/window.rb +71 -0
- data/examples/hw.rb +11 -0
- data/examples/i18n/app.rb +16 -0
- data/examples/i18n/locale/de/app.po +25 -0
- data/examples/i18n/locale/en/app.po +25 -0
- data/examples/pager.rb +102 -0
- data/lib/wee.rb +109 -0
- data/lib/wee/application.rb +89 -0
- data/lib/wee/callback.rb +109 -0
- data/lib/wee/component.rb +363 -0
- data/lib/wee/decoration.rb +251 -0
- data/lib/wee/dialog.rb +171 -0
- data/lib/wee/external_resource.rb +39 -0
- data/lib/wee/html_brushes.rb +795 -0
- data/lib/wee/html_canvas.rb +254 -0
- data/lib/wee/html_document.rb +52 -0
- data/lib/wee/html_writer.rb +71 -0
- data/lib/wee/id_generator.rb +81 -0
- data/lib/wee/jquery.rb +11 -0
- data/lib/wee/jquery/jquery-1.3.2.min.js +19 -0
- data/lib/wee/jquery/wee-jquery.js +19 -0
- data/lib/wee/locale.rb +56 -0
- data/lib/wee/lru_cache.rb +91 -0
- data/lib/wee/presenter.rb +44 -0
- data/lib/wee/renderer.rb +72 -0
- data/lib/wee/request.rb +56 -0
- data/lib/wee/response.rb +68 -0
- data/lib/wee/rightjs.rb +11 -0
- data/lib/wee/rightjs/rightjs-1.5.2.min.js +9 -0
- data/lib/wee/rightjs/wee-rightjs.js +18 -0
- data/lib/wee/root_component.rb +45 -0
- data/lib/wee/session.rb +366 -0
- data/lib/wee/state.rb +102 -0
- data/lib/wee/task.rb +16 -0
- data/test/bm_render.rb +34 -0
- data/test/component_spec.rb +40 -0
- data/test/stress/plotter.rb +84 -0
- data/test/stress/stress_client.rb +51 -0
- data/test/stress/stress_local.rb +86 -0
- data/test/stress/stress_server.rb +83 -0
- data/test/test_component.rb +106 -0
- data/test/test_html_canvas.rb +25 -0
- data/test/test_html_writer.rb +32 -0
- data/test/test_lru_cache.rb +51 -0
- data/test/test_request.rb +42 -0
- metadata +185 -0
@@ -0,0 +1,795 @@
|
|
1
|
+
module Wee
|
2
|
+
|
3
|
+
class Brush
|
4
|
+
attr_accessor :canvas, :document
|
5
|
+
|
6
|
+
# This method is called right after #initialize. It's only here to
|
7
|
+
# simplify the implementation of Brushes, mainly to avoid passing all those
|
8
|
+
# arguments to super.
|
9
|
+
#
|
10
|
+
# There is a bit of redundancy with canvas and document here. It's there to
|
11
|
+
# avoid method calls.
|
12
|
+
#
|
13
|
+
# A brush is considered to be closed, when @document is nil.
|
14
|
+
#
|
15
|
+
def setup(canvas, document)
|
16
|
+
@canvas = canvas
|
17
|
+
@document = document
|
18
|
+
end
|
19
|
+
|
20
|
+
def with(*args, &block)
|
21
|
+
@canvas.nest(&block) if block
|
22
|
+
@document = @canvas = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def close
|
26
|
+
with if @document
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.nesting?() true end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Brush::GenericTextBrush < Brush
|
33
|
+
def with(text)
|
34
|
+
@document.text(text)
|
35
|
+
@document = @canvas = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.nesting?() false end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Brush::GenericEncodedTextBrush < Brush::GenericTextBrush
|
42
|
+
def with(text)
|
43
|
+
@document.encode_text(text)
|
44
|
+
@document = @canvas = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Brush::GenericTagBrush < Brush
|
49
|
+
def self.html_attr(attr, hash={})
|
50
|
+
name = hash[:html_name] || attr
|
51
|
+
if hash[:type] == :bool
|
52
|
+
class_eval %{
|
53
|
+
def #{ attr }(bool=true)
|
54
|
+
if bool
|
55
|
+
@attributes[:"#{ name }"] = nil
|
56
|
+
else
|
57
|
+
@attributes.delete(:"#{ name }")
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
}
|
62
|
+
else
|
63
|
+
class_eval %{
|
64
|
+
def #{ attr }(value)
|
65
|
+
if value == nil
|
66
|
+
@attributes.delete(:"#{ name }")
|
67
|
+
else
|
68
|
+
@attributes[:"#{ name }"] = value
|
69
|
+
end
|
70
|
+
self
|
71
|
+
end
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
(hash[:aliases] || []).each do |a|
|
76
|
+
class_eval "alias #{ a } #{ attr }"
|
77
|
+
end
|
78
|
+
|
79
|
+
(hash[:shortcuts] || {}).each_pair do |k, v|
|
80
|
+
class_eval "def #{ k }() #{ attr }(#{ v.inspect }) end"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Brush::GenericTagBrush < Brush
|
86
|
+
html_attr :id
|
87
|
+
html_attr :name # XXX
|
88
|
+
html_attr :css_class, :html_name => :class
|
89
|
+
html_attr :css_style, :html_name => :style, :aliases => [:style]
|
90
|
+
html_attr :onclick
|
91
|
+
html_attr :ondblclick
|
92
|
+
|
93
|
+
def initialize(tag)
|
94
|
+
super()
|
95
|
+
@tag = tag
|
96
|
+
@attributes = Hash.new
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Assigns a unique DOM id
|
101
|
+
#
|
102
|
+
def oid
|
103
|
+
id(get_oid())
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Returns a unique DOM id for the underlying component
|
108
|
+
#
|
109
|
+
def get_oid
|
110
|
+
"wee_#{@canvas.current_component.object_id}"
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# generic support for onXXX events
|
115
|
+
#
|
116
|
+
|
117
|
+
EVENTS = {:click => 'onclick'.freeze,
|
118
|
+
:dblclick => 'ondblclick'.freeze,
|
119
|
+
:mouseover => 'onmouseover'.freeze,
|
120
|
+
:mouseout => 'onmouseout'.freeze,
|
121
|
+
:change => 'onchange'.freeze}.freeze
|
122
|
+
|
123
|
+
def javascript_on(event, javascript)
|
124
|
+
ev = EVENTS[event]
|
125
|
+
raise ArgumentError unless ev
|
126
|
+
@attributes[ev] = "javascript: #{javascript};"
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
def callback_on(event, &block)
|
131
|
+
raise ArgumentError unless block
|
132
|
+
url = @canvas.url_for_callback(block)
|
133
|
+
javascript_on(event, "document.location.href='#{ url }'")
|
134
|
+
self
|
135
|
+
end
|
136
|
+
|
137
|
+
def update_on(event, &render_block)
|
138
|
+
raise ArgumentError unless render_block
|
139
|
+
url = @canvas.url_for_callback(@canvas.session.render_ajax_proc(render_block, @canvas.current_component))
|
140
|
+
javascript_on(event, "wee.update('#{ url }')")
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
def update_component_on(event, component=nil, &callback_block)
|
145
|
+
component ||= @canvas.current_component
|
146
|
+
|
147
|
+
render_block = proc {|r|
|
148
|
+
callback_block.call if callback_block
|
149
|
+
r.render(component)
|
150
|
+
}
|
151
|
+
|
152
|
+
url = @canvas.url_for_callback(@canvas.session.render_ajax_proc(render_block, component))
|
153
|
+
javascript_on(event, "wee.update('#{ url }')")
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
157
|
+
def onclick_javascript(v)
|
158
|
+
javascript_on(:click, v)
|
159
|
+
end
|
160
|
+
|
161
|
+
def onclick_callback(&block)
|
162
|
+
callback_on(:click, &block)
|
163
|
+
end
|
164
|
+
|
165
|
+
def ondblclick_callback(&block)
|
166
|
+
callback_on(:dblclick, &block)
|
167
|
+
end
|
168
|
+
|
169
|
+
def with(text=nil, &block)
|
170
|
+
@document.start_tag(@tag, @attributes)
|
171
|
+
@document.text(text) if text
|
172
|
+
@canvas.nest(&block) if block
|
173
|
+
@document.end_tag(@tag)
|
174
|
+
@document = @canvas = nil
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
class Brush::GenericSingleTagBrush < Brush::GenericTagBrush
|
180
|
+
def with
|
181
|
+
@document.single_tag(@tag, @attributes)
|
182
|
+
@document = @canvas = nil
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.nesting?() false end
|
186
|
+
end
|
187
|
+
|
188
|
+
class Brush::ImageTag < Brush::GenericSingleTagBrush
|
189
|
+
HTML_TAG = 'img'.freeze
|
190
|
+
|
191
|
+
html_attr :src
|
192
|
+
html_attr :width
|
193
|
+
html_attr :height
|
194
|
+
html_attr :border
|
195
|
+
html_attr :alt
|
196
|
+
|
197
|
+
def initialize
|
198
|
+
super(HTML_TAG)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class Brush::JavascriptTag < Brush::GenericTagBrush
|
203
|
+
HTML_TAG = 'script'.freeze
|
204
|
+
HTML_TYPE = 'text/javascript'.freeze
|
205
|
+
|
206
|
+
html_attr :src
|
207
|
+
html_attr :type
|
208
|
+
|
209
|
+
def initialize
|
210
|
+
super(HTML_TAG)
|
211
|
+
type(HTML_TYPE)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
class Brush::StyleTag < Brush::GenericTagBrush
|
216
|
+
HTML_TAG = 'style'.freeze
|
217
|
+
|
218
|
+
html_attr :type
|
219
|
+
|
220
|
+
def initialize
|
221
|
+
super(HTML_TAG)
|
222
|
+
end
|
223
|
+
|
224
|
+
def with(text=nil, &block)
|
225
|
+
@document.start_tag(@tag, @attributes)
|
226
|
+
@document.write("<!--\n")
|
227
|
+
@document.text(text) if text
|
228
|
+
@canvas.nest(&block) if block
|
229
|
+
@document.write("-->\n")
|
230
|
+
@document.end_tag(@tag)
|
231
|
+
@document = @canvas = nil
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
#---------------------------------------------------------------------
|
236
|
+
# Table
|
237
|
+
#---------------------------------------------------------------------
|
238
|
+
|
239
|
+
class Brush::TableTag < Brush::GenericTagBrush
|
240
|
+
HTML_TAG = 'table'.freeze
|
241
|
+
|
242
|
+
html_attr :cellspacing
|
243
|
+
html_attr :border
|
244
|
+
|
245
|
+
def initialize
|
246
|
+
super(HTML_TAG)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
class Brush::TableRowTag < Brush::GenericTagBrush
|
251
|
+
HTML_TAG = 'tr'.freeze
|
252
|
+
|
253
|
+
html_attr :align, :shortcuts => {
|
254
|
+
:align_top => :top, :align_bottom => :bottom
|
255
|
+
}
|
256
|
+
|
257
|
+
def initialize
|
258
|
+
super(HTML_TAG)
|
259
|
+
end
|
260
|
+
|
261
|
+
def columns(*cols, &block)
|
262
|
+
with {
|
263
|
+
cols.each {|col|
|
264
|
+
@canvas.table_data.with {
|
265
|
+
if block
|
266
|
+
block.call(col)
|
267
|
+
else
|
268
|
+
@canvas.text(col)
|
269
|
+
end
|
270
|
+
}
|
271
|
+
}
|
272
|
+
}
|
273
|
+
end
|
274
|
+
|
275
|
+
def headings(*headers, &block)
|
276
|
+
with {
|
277
|
+
headers.each {|header|
|
278
|
+
@canvas.table_header.with {
|
279
|
+
if block
|
280
|
+
block.call(header)
|
281
|
+
else
|
282
|
+
@canvas.text(header)
|
283
|
+
end
|
284
|
+
}
|
285
|
+
}
|
286
|
+
}
|
287
|
+
end
|
288
|
+
|
289
|
+
def spanning_column(str, colspan)
|
290
|
+
with { @canvas.table_data.col_span(colspan).with(str) }
|
291
|
+
end
|
292
|
+
|
293
|
+
def spacer
|
294
|
+
with { @canvas.table_data { @canvas.space } }
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class Brush::TableDataTag < Brush::GenericTagBrush
|
299
|
+
HTML_TAG = 'td'.freeze
|
300
|
+
|
301
|
+
html_attr :colspan
|
302
|
+
html_attr :align, :shortcuts => {
|
303
|
+
:align_top => :top,
|
304
|
+
:align_bottom => :bottom
|
305
|
+
}
|
306
|
+
|
307
|
+
def initialize
|
308
|
+
super(HTML_TAG)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
class Brush::TableHeaderTag < Brush::GenericTagBrush
|
313
|
+
HTML_TAG = 'th'.freeze
|
314
|
+
|
315
|
+
html_attr :colspan
|
316
|
+
html_attr :align, :shortcuts => {
|
317
|
+
:align_top => :top,
|
318
|
+
:align_bottom => :bottom
|
319
|
+
}
|
320
|
+
|
321
|
+
def initialize
|
322
|
+
super(HTML_TAG)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
#---------------------------------------------------------------------
|
327
|
+
# Callback Mixin
|
328
|
+
#---------------------------------------------------------------------
|
329
|
+
|
330
|
+
module CallbackMixin
|
331
|
+
|
332
|
+
def callback_method(id, *args)
|
333
|
+
@callback = self
|
334
|
+
@callback_object = @canvas.current_component
|
335
|
+
@callback_id = id
|
336
|
+
@callback_args = args
|
337
|
+
__callback()
|
338
|
+
return self
|
339
|
+
end
|
340
|
+
|
341
|
+
def callback(&block)
|
342
|
+
@callback = block
|
343
|
+
__callback()
|
344
|
+
return self
|
345
|
+
end
|
346
|
+
|
347
|
+
#
|
348
|
+
# Is called when #callback_method was used.
|
349
|
+
#
|
350
|
+
def call(*args)
|
351
|
+
args.push(*@callback_args)
|
352
|
+
@callback_object.send(@callback_id, *args)
|
353
|
+
end
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
#---------------------------------------------------------------------
|
358
|
+
# Form
|
359
|
+
#---------------------------------------------------------------------
|
360
|
+
|
361
|
+
class Brush::FormTag < Brush::GenericTagBrush
|
362
|
+
HTML_TAG = 'form'.freeze
|
363
|
+
HTML_METHOD_POST = 'POST'.freeze
|
364
|
+
|
365
|
+
html_attr :action
|
366
|
+
html_attr :enctype
|
367
|
+
|
368
|
+
#
|
369
|
+
# Use this enctype when you have a FileUploadTag field.
|
370
|
+
#
|
371
|
+
def enctype_multipart
|
372
|
+
enctype('multipart/form-data')
|
373
|
+
end
|
374
|
+
|
375
|
+
def initialize
|
376
|
+
super(HTML_TAG)
|
377
|
+
@attributes[:method] = HTML_METHOD_POST
|
378
|
+
end
|
379
|
+
|
380
|
+
def with(&block)
|
381
|
+
# If no action was specified, use a dummy one.
|
382
|
+
unless @attributes.has_key?(:action)
|
383
|
+
@attributes[:action] = @canvas.build_url
|
384
|
+
end
|
385
|
+
super
|
386
|
+
end
|
387
|
+
|
388
|
+
include CallbackMixin
|
389
|
+
|
390
|
+
def __callback; action(@canvas.url_for_callback(@callback)) end
|
391
|
+
|
392
|
+
=begin
|
393
|
+
def onsubmit_update(update_id, &block)
|
394
|
+
raise ArgumentError if symbol and block
|
395
|
+
url = @canvas.url_for_callback(block, :live_update)
|
396
|
+
onsubmit("javascript: new Ajax.Updater('#{ update_id }', '#{ url }', {method:'get', parameters: Form.serialize(this)}); return false;")
|
397
|
+
end
|
398
|
+
=end
|
399
|
+
end
|
400
|
+
|
401
|
+
#---------------------------------------------------------------------
|
402
|
+
# Form - Input
|
403
|
+
#---------------------------------------------------------------------
|
404
|
+
|
405
|
+
class Brush::InputTag < Brush::GenericSingleTagBrush
|
406
|
+
HTML_TAG = 'input'.freeze
|
407
|
+
|
408
|
+
html_attr :type
|
409
|
+
html_attr :name
|
410
|
+
html_attr :value
|
411
|
+
html_attr :size
|
412
|
+
html_attr :maxlength
|
413
|
+
html_attr :src
|
414
|
+
html_attr :checked, :type => :bool
|
415
|
+
html_attr :disabled, :type => :bool
|
416
|
+
html_attr :readonly, :type => :bool
|
417
|
+
|
418
|
+
def initialize(_type)
|
419
|
+
super(HTML_TAG)
|
420
|
+
type(_type)
|
421
|
+
end
|
422
|
+
|
423
|
+
include CallbackMixin
|
424
|
+
|
425
|
+
def __callback; name(@canvas.register_callback(:input, @callback)) end
|
426
|
+
end
|
427
|
+
|
428
|
+
class Brush::TextInputTag < Brush::InputTag
|
429
|
+
HTML_TYPE = 'text'.freeze
|
430
|
+
|
431
|
+
def initialize
|
432
|
+
super(HTML_TYPE)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
class Brush::HiddenInputTag < Brush::InputTag
|
437
|
+
HTML_TYPE = 'hidden'.freeze
|
438
|
+
|
439
|
+
def initialize
|
440
|
+
super(HTML_TYPE)
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
class Brush::PasswordInputTag < Brush::InputTag
|
445
|
+
HTML_TYPE = 'password'.freeze
|
446
|
+
|
447
|
+
def initialize
|
448
|
+
super(HTML_TYPE)
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
class Brush::CheckboxTag < Brush::InputTag
|
453
|
+
HTML_TYPE = 'checkbox'.freeze
|
454
|
+
|
455
|
+
def initialize
|
456
|
+
super(HTML_TYPE)
|
457
|
+
end
|
458
|
+
|
459
|
+
def __callback; end # do nothing
|
460
|
+
|
461
|
+
def with
|
462
|
+
if @callback
|
463
|
+
n = @canvas.register_callback(:input, proc {|input|
|
464
|
+
@callback.call(input.send(input.kind_of?(Array) ? :include? : :==, '1'))
|
465
|
+
})
|
466
|
+
@document.single_tag('input', :type => 'hidden', :name => n, :value => '0')
|
467
|
+
name(n)
|
468
|
+
value('1')
|
469
|
+
end
|
470
|
+
super
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
#
|
475
|
+
# Use a <form> tag with enctype_multipart!
|
476
|
+
#
|
477
|
+
class Brush::FileUploadTag < Brush::InputTag
|
478
|
+
HTML_TYPE = 'file'.freeze
|
479
|
+
|
480
|
+
def initialize
|
481
|
+
super(HTML_TYPE)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
#---------------------------------------------------------------------
|
486
|
+
# Form - Buttons
|
487
|
+
#---------------------------------------------------------------------
|
488
|
+
|
489
|
+
class Brush::ActionInputTag < Brush::InputTag
|
490
|
+
include CallbackMixin
|
491
|
+
|
492
|
+
def __callback; name(@canvas.register_callback(:action, @callback)) end
|
493
|
+
end
|
494
|
+
|
495
|
+
class Brush::SubmitButtonTag < Brush::ActionInputTag
|
496
|
+
HTML_TYPE = 'submit'.freeze
|
497
|
+
|
498
|
+
def initialize
|
499
|
+
super(HTML_TYPE)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
#
|
504
|
+
# NOTE: The form-fields returned by a image-button-tag is browser-specific.
|
505
|
+
# Most browsers do not send the "name" key together with the value specified
|
506
|
+
# by "value", only "name.x" and "name.y". This conforms to the standard. But
|
507
|
+
# Firefox also sends "name"="value". This is why I raise an exception from
|
508
|
+
# the #value method. Note that it's neccessary to parse the passed
|
509
|
+
# form-fields and generate a "name" fields in the request, to make this
|
510
|
+
# image-button work.
|
511
|
+
#
|
512
|
+
class Brush::ImageButtonTag < Brush::ActionInputTag
|
513
|
+
HTML_TYPE = 'image'.freeze
|
514
|
+
|
515
|
+
def initialize
|
516
|
+
super(HTML_TYPE)
|
517
|
+
end
|
518
|
+
|
519
|
+
undef :value
|
520
|
+
end
|
521
|
+
|
522
|
+
#---------------------------------------------------------------------
|
523
|
+
# Form - Textarea
|
524
|
+
#---------------------------------------------------------------------
|
525
|
+
|
526
|
+
class Brush::TextAreaTag < Brush::GenericTagBrush
|
527
|
+
HTML_TAG = 'textarea'.freeze
|
528
|
+
|
529
|
+
html_attr :name
|
530
|
+
html_attr :rows
|
531
|
+
html_attr :cols
|
532
|
+
html_attr :tabindex
|
533
|
+
html_attr :accesskey
|
534
|
+
html_attr :onfocus
|
535
|
+
html_attr :onblur
|
536
|
+
html_attr :onselect
|
537
|
+
html_attr :onchange
|
538
|
+
html_attr :disabled, :type => :bool
|
539
|
+
html_attr :readonly, :type => :bool
|
540
|
+
|
541
|
+
def initialize
|
542
|
+
super(HTML_TAG)
|
543
|
+
end
|
544
|
+
|
545
|
+
def value(val)
|
546
|
+
@value = val
|
547
|
+
self
|
548
|
+
end
|
549
|
+
|
550
|
+
def with(value=nil)
|
551
|
+
super(value || @value)
|
552
|
+
end
|
553
|
+
|
554
|
+
include CallbackMixin
|
555
|
+
|
556
|
+
def __callback; name(@canvas.register_callback(:input, @callback)) end
|
557
|
+
end
|
558
|
+
|
559
|
+
#---------------------------------------------------------------------
|
560
|
+
# Form - Select
|
561
|
+
#---------------------------------------------------------------------
|
562
|
+
|
563
|
+
class Brush::SelectListTag < Brush::GenericTagBrush
|
564
|
+
HTML_TAG = 'select'.freeze
|
565
|
+
|
566
|
+
html_attr :size
|
567
|
+
html_attr :disabled, :type => :bool
|
568
|
+
html_attr :readonly, :type => :bool
|
569
|
+
html_attr :multiple, :type => :bool, :aliases => [:multi]
|
570
|
+
|
571
|
+
def initialize(items)
|
572
|
+
super(HTML_TAG)
|
573
|
+
@items = items
|
574
|
+
end
|
575
|
+
|
576
|
+
def items(items)
|
577
|
+
@items = items
|
578
|
+
self
|
579
|
+
end
|
580
|
+
|
581
|
+
def selected(arg=nil, &block)
|
582
|
+
raise ArgumentError if arg and block
|
583
|
+
@selected = block || arg
|
584
|
+
self
|
585
|
+
end
|
586
|
+
|
587
|
+
def labels(arg=nil, &block)
|
588
|
+
raise ArgumentError if arg and block
|
589
|
+
if block
|
590
|
+
@labels = proc {|i| block.call(@items[i])}
|
591
|
+
else
|
592
|
+
@labels = arg
|
593
|
+
end
|
594
|
+
self
|
595
|
+
end
|
596
|
+
|
597
|
+
include CallbackMixin
|
598
|
+
|
599
|
+
def __callback
|
600
|
+
#
|
601
|
+
# A callback was specified. We have to wrap it inside another
|
602
|
+
# callback, as we want to perform some additional actions.
|
603
|
+
#
|
604
|
+
name(@canvas.register_callback(:input, method(:handler)) + "[]")
|
605
|
+
end
|
606
|
+
|
607
|
+
def handler(input)
|
608
|
+
choosen = input.map {|idx|
|
609
|
+
idx = Integer(idx)
|
610
|
+
raise IndexError if idx < 0 or idx > @items.size
|
611
|
+
@items[idx]
|
612
|
+
}
|
613
|
+
|
614
|
+
if @attributes.has_key?(:multiple)
|
615
|
+
@callback.call(choosen)
|
616
|
+
elsif choosen.size > 1
|
617
|
+
raise "more than one element was choosen from a not-multiple SelectListTag"
|
618
|
+
else
|
619
|
+
@callback.call(choosen.first)
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
protected :handler
|
624
|
+
|
625
|
+
def with
|
626
|
+
@labels ||= @items.collect {|i| i.to_s}
|
627
|
+
|
628
|
+
if @attributes.has_key?(:multiple)
|
629
|
+
@selected ||= Array.new
|
630
|
+
meth = @selected.kind_of?(Proc) ? (:call) : (:include?)
|
631
|
+
else
|
632
|
+
meth = @selected.kind_of?(Proc) ? (:call) : (:==)
|
633
|
+
end
|
634
|
+
|
635
|
+
super {
|
636
|
+
@items.each_index do |i|
|
637
|
+
@canvas.option.value(i).selected(@selected.send(meth, @items[i])).with(@labels[i])
|
638
|
+
end
|
639
|
+
}
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
class Brush::SelectOptionTag < Brush::GenericTagBrush
|
644
|
+
HTML_TAG = 'option'.freeze
|
645
|
+
|
646
|
+
html_attr :value
|
647
|
+
html_attr :selected, :type => :bool
|
648
|
+
|
649
|
+
def initialize
|
650
|
+
super(HTML_TAG)
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
#---------------------------------------------------------------------
|
655
|
+
# Form - Radio
|
656
|
+
#---------------------------------------------------------------------
|
657
|
+
|
658
|
+
class Brush::RadioGroup
|
659
|
+
def initialize(canvas)
|
660
|
+
@name = canvas.register_callback(:input, self)
|
661
|
+
@callbacks = {}
|
662
|
+
@ids = Wee::IdGenerator::Sequential.new
|
663
|
+
end
|
664
|
+
|
665
|
+
def add_callback(callback)
|
666
|
+
value = @ids.next.to_s
|
667
|
+
@callbacks[value] = callback
|
668
|
+
return [@name, value]
|
669
|
+
end
|
670
|
+
|
671
|
+
def call(value)
|
672
|
+
if @callbacks.has_key?(value)
|
673
|
+
cb = @callbacks[value]
|
674
|
+
cb.call(value) if cb
|
675
|
+
else
|
676
|
+
raise "invalid radio button/group value"
|
677
|
+
end
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
class Brush::RadioButtonTag < Brush::InputTag
|
682
|
+
HTML_TYPE = 'radio'.freeze
|
683
|
+
|
684
|
+
def initialize
|
685
|
+
super(HTML_TYPE)
|
686
|
+
end
|
687
|
+
|
688
|
+
def group(radio_group)
|
689
|
+
@group = radio_group
|
690
|
+
self
|
691
|
+
end
|
692
|
+
|
693
|
+
include CallbackMixin
|
694
|
+
|
695
|
+
def __callback; end # do nothing
|
696
|
+
|
697
|
+
def with
|
698
|
+
if @group
|
699
|
+
n, v = @group.add_callback(@callback)
|
700
|
+
name(n)
|
701
|
+
value(v)
|
702
|
+
end
|
703
|
+
super
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
#---------------------------------------------------------------------
|
708
|
+
# Misc
|
709
|
+
#---------------------------------------------------------------------
|
710
|
+
|
711
|
+
class Brush::LinkTag < Brush::GenericTagBrush
|
712
|
+
HTML_TAG = 'link'.freeze
|
713
|
+
|
714
|
+
html_attr :href, :aliases => [:url]
|
715
|
+
html_attr :type
|
716
|
+
html_attr :rel
|
717
|
+
|
718
|
+
def initialize
|
719
|
+
super(HTML_TAG)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
class Brush::AnchorTag < Brush::GenericTagBrush
|
724
|
+
HTML_TAG = 'a'.freeze
|
725
|
+
|
726
|
+
html_attr :href, :aliases => [:url]
|
727
|
+
html_attr :title, :aliases => [:tooltip]
|
728
|
+
|
729
|
+
def initialize
|
730
|
+
super(HTML_TAG)
|
731
|
+
href('#')
|
732
|
+
end
|
733
|
+
|
734
|
+
def info(info=nil)
|
735
|
+
@info = info
|
736
|
+
self
|
737
|
+
end
|
738
|
+
|
739
|
+
include CallbackMixin
|
740
|
+
|
741
|
+
def __callback
|
742
|
+
url(@canvas.url_for_callback(@callback, :action, @info ? {:info => @info} : {}))
|
743
|
+
end
|
744
|
+
end
|
745
|
+
|
746
|
+
class Brush::Page < Brush
|
747
|
+
HTML_HTML = 'html'.freeze
|
748
|
+
HTML_HEAD = 'head'.freeze
|
749
|
+
HTML_TITLE = 'title'.freeze
|
750
|
+
HTML_BODY = 'body'.freeze
|
751
|
+
|
752
|
+
def with(text=nil, &block)
|
753
|
+
@document.start_tag(HTML_HTML)
|
754
|
+
@document.start_tag(HTML_HEAD)
|
755
|
+
|
756
|
+
if @title
|
757
|
+
@document.start_tag(HTML_TITLE)
|
758
|
+
@document.text(@title)
|
759
|
+
@document.end_tag(HTML_TITLE)
|
760
|
+
end
|
761
|
+
|
762
|
+
if @head
|
763
|
+
@canvas.nest(&@head)
|
764
|
+
end
|
765
|
+
|
766
|
+
@document.end_tag(HTML_HEAD)
|
767
|
+
@document.start_tag(HTML_BODY)
|
768
|
+
|
769
|
+
if text
|
770
|
+
raise ArgumentError if block
|
771
|
+
@document.text(text)
|
772
|
+
else
|
773
|
+
@canvas.nest(&block) if block
|
774
|
+
end
|
775
|
+
|
776
|
+
@document.end_tag(HTML_BODY)
|
777
|
+
@document.end_tag(HTML_HTML)
|
778
|
+
|
779
|
+
@document = @canvas = nil
|
780
|
+
end
|
781
|
+
|
782
|
+
def title(t)
|
783
|
+
@title = t
|
784
|
+
self
|
785
|
+
end
|
786
|
+
|
787
|
+
def head(&block)
|
788
|
+
raise ArgumentError unless block
|
789
|
+
@head = block
|
790
|
+
self
|
791
|
+
end
|
792
|
+
|
793
|
+
end
|
794
|
+
|
795
|
+
end # module Wee
|