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.
Files changed (73) hide show
  1. data/README.rdoc +127 -0
  2. data/Rakefile +25 -0
  3. data/aWee.gemspec +26 -0
  4. data/examples/ObjectSpaceBrowser.rb +191 -0
  5. data/examples/ajax.rb +73 -0
  6. data/examples/apotomo-webhunter/main.rb +75 -0
  7. data/examples/apotomo-webhunter/public/images/bear_trap_charged.png +0 -0
  8. data/examples/apotomo-webhunter/public/images/bear_trap_snapped.png +0 -0
  9. data/examples/apotomo-webhunter/public/images/cheese.png +0 -0
  10. data/examples/apotomo-webhunter/public/images/dark_forest.jpg +0 -0
  11. data/examples/apotomo-webhunter/public/images/mouse.png +0 -0
  12. data/examples/apotomo-webhunter/public/javascripts/jquery-1.3.2.min.js +19 -0
  13. data/examples/apotomo-webhunter/public/javascripts/wee-jquery.js +19 -0
  14. data/examples/apotomo-webhunter/public/stylesheets/forest.css +33 -0
  15. data/examples/arc_challenge.rb +42 -0
  16. data/examples/arc_challenge2.rb +46 -0
  17. data/examples/cheese_task.rb +27 -0
  18. data/examples/continuations.rb +28 -0
  19. data/examples/demo.rb +135 -0
  20. data/examples/demo/calculator.rb +63 -0
  21. data/examples/demo/calendar.rb +333 -0
  22. data/examples/demo/counter.rb +38 -0
  23. data/examples/demo/editable_counter.rb +36 -0
  24. data/examples/demo/example.rb +142 -0
  25. data/examples/demo/file_upload.rb +19 -0
  26. data/examples/demo/messagebox.rb +15 -0
  27. data/examples/demo/radio.rb +33 -0
  28. data/examples/demo/window.rb +71 -0
  29. data/examples/hw.rb +11 -0
  30. data/examples/i18n/app.rb +16 -0
  31. data/examples/i18n/locale/de/app.po +25 -0
  32. data/examples/i18n/locale/en/app.po +25 -0
  33. data/examples/pager.rb +102 -0
  34. data/lib/wee.rb +109 -0
  35. data/lib/wee/application.rb +89 -0
  36. data/lib/wee/callback.rb +109 -0
  37. data/lib/wee/component.rb +363 -0
  38. data/lib/wee/decoration.rb +251 -0
  39. data/lib/wee/dialog.rb +171 -0
  40. data/lib/wee/external_resource.rb +39 -0
  41. data/lib/wee/html_brushes.rb +795 -0
  42. data/lib/wee/html_canvas.rb +254 -0
  43. data/lib/wee/html_document.rb +52 -0
  44. data/lib/wee/html_writer.rb +71 -0
  45. data/lib/wee/id_generator.rb +81 -0
  46. data/lib/wee/jquery.rb +11 -0
  47. data/lib/wee/jquery/jquery-1.3.2.min.js +19 -0
  48. data/lib/wee/jquery/wee-jquery.js +19 -0
  49. data/lib/wee/locale.rb +56 -0
  50. data/lib/wee/lru_cache.rb +91 -0
  51. data/lib/wee/presenter.rb +44 -0
  52. data/lib/wee/renderer.rb +72 -0
  53. data/lib/wee/request.rb +56 -0
  54. data/lib/wee/response.rb +68 -0
  55. data/lib/wee/rightjs.rb +11 -0
  56. data/lib/wee/rightjs/rightjs-1.5.2.min.js +9 -0
  57. data/lib/wee/rightjs/wee-rightjs.js +18 -0
  58. data/lib/wee/root_component.rb +45 -0
  59. data/lib/wee/session.rb +366 -0
  60. data/lib/wee/state.rb +102 -0
  61. data/lib/wee/task.rb +16 -0
  62. data/test/bm_render.rb +34 -0
  63. data/test/component_spec.rb +40 -0
  64. data/test/stress/plotter.rb +84 -0
  65. data/test/stress/stress_client.rb +51 -0
  66. data/test/stress/stress_local.rb +86 -0
  67. data/test/stress/stress_server.rb +83 -0
  68. data/test/test_component.rb +106 -0
  69. data/test/test_html_canvas.rb +25 -0
  70. data/test/test_html_writer.rb +32 -0
  71. data/test/test_lru_cache.rb +51 -0
  72. data/test/test_request.rb +42 -0
  73. 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