campo 0.3.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,740 @@
1
+ # encoding: UTF-8
2
+
3
+ module Campo
4
+
5
+ # Deals with adding children and tracking parents.
6
+ module Childish
7
+
8
+ # Push something onto the end of the fields array.
9
+ # @param [Object] child The object to push.
10
+ # @return [Object] self
11
+ def push=( child )
12
+ @fields << child
13
+ child.parent = self
14
+ self
15
+ end
16
+
17
+ # @see #push=
18
+ alias :<< :push=
19
+
20
+ attr_accessor :parent
21
+ end # Childish
22
+
23
+ # Helpers for id'ing fields.
24
+ module Iding
25
+
26
+ # Helps to create a unique id tag.
27
+ # @param [String,nil] val
28
+ def id_tag( val )
29
+ val.nil? ? "" : "_#{val}"
30
+ end
31
+ end # Iding
32
+
33
+
34
+ def self.constantize(camel_cased_word)
35
+ names = camel_cased_word.split('::')
36
+ names.shift if names.empty? || names.first.empty?
37
+
38
+ constant = Object
39
+ names.each do |name|
40
+ constant = constant.const_defined?(name) ? constant.const_get(name,false) : constant.const_missing(name)
41
+ end
42
+ constant
43
+ end
44
+
45
+
46
+ # keeps track of the current plugins
47
+ def self.plugins
48
+ @plugins ||= {}
49
+ end
50
+
51
+
52
+ def self.plugin( name, options={} )
53
+ unless plugins.include? name
54
+ modname = (str = name.to_s) && (str[0,1].upcase + str[1..-1])
55
+ plugins[name] = constantize("Campo::Plugins::#{modname}").new options
56
+ plugins[name].plugged_in
57
+ end
58
+ end
59
+
60
+ # Here to make life a bit easier and cut down on RSI.
61
+ module Convenience
62
+
63
+ # @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
64
+ # @example Fieldset as a block is easiest to read
65
+ # form.fieldset("Your details") do |f|
66
+ # f.text( "full_name", size: 60 )
67
+ # f.text( "dob", "Date of birth: ", size: 8 )
68
+ # end
69
+ def fieldset( text, attributes={}, &block )
70
+ self << Fieldset.new( text, attributes, &block )
71
+ end
72
+
73
+
74
+ # @example Add a bit of code to the markup
75
+ # form.bit_of_ruby( "= 5 + 1" ) }
76
+ def bit_of_ruby( *args, &block )
77
+ tag = Campo::Haml_Ruby_Insert.new( *args, &block )
78
+ self << tag
79
+ tag
80
+ end
81
+
82
+ alias :haml_ruby_insert :bit_of_ruby
83
+
84
+
85
+ # @example Output a literal string
86
+ # form.literal %Q!%p= "This is a paragraph "!
87
+ # @see Campo::Literal#initialize
88
+ def literal( *args, &block )
89
+ tag = Campo::Literal.new( *args, &block )
90
+ self << tag
91
+ tag
92
+ end
93
+
94
+
95
+ # @example
96
+ # # Select with a block of options
97
+ # f.select("teas") do |s|
98
+ # s.with_default
99
+ # s.option("ceylon")
100
+ # s.option("breakfast")
101
+ # s.option("earl grey")
102
+ # s.option("oolong")
103
+ # s.option("sencha")
104
+ # end.labelled("Favourite tea:")
105
+ #
106
+ # # Select using chain of options
107
+ # form.select("bands").option("Suede").option("Blur").option("Oasis").option("Echobelly").option("Pulp").option("Supergrass").with_default.labelled("Favourite band:")
108
+ #
109
+ # @see Select#initialize
110
+ def select( *args, &block )
111
+ select = Campo::Select.new( *args, &block )
112
+ self << select
113
+ select
114
+ end
115
+
116
+
117
+ # Add an input with type of text
118
+ # @param [String] name The name html attribute.
119
+ # @param [optional, String, nil] label Give the label a name. Defaults to a capitalised name with _ replaced by spaces.
120
+ # @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
121
+ # @example
122
+ # f.text "full_name", size: 60
123
+ # f.text "dob", "Date of birth: ", size: 8
124
+ # @return [Input]
125
+ # With the attribute `type=text`
126
+ def text( name, label=nil, attributes={} )
127
+ input( name, :text, label, attributes )
128
+ end
129
+
130
+
131
+ def hidden( name, attributes={} )
132
+ self << Campo::Input.new( name, :hidden, attributes )
133
+ end
134
+
135
+
136
+ # @param (see #text)
137
+ def password( name, label=nil, attributes={} )
138
+ input( name, :password, label, attributes )
139
+ end
140
+
141
+
142
+ # @param (see #text)
143
+ def radio( name, label=nil, attributes={} )
144
+ input( name, :radio, label, attributes )
145
+ end
146
+
147
+
148
+ # @param (see #text)
149
+ def checkbox( name, label=nil, attributes={} )
150
+ input( name, :checkbox, label, attributes )
151
+ end
152
+
153
+
154
+ # @param (see #text)
155
+ # @param [:symbol] type The type html attribute.
156
+ def input( name, type, label=nil, attributes={} )
157
+ if label.kind_of? Hash
158
+ attributes = label
159
+ label = nil
160
+ end
161
+
162
+ field = Campo::Input.new( name, type, attributes ).labelled( label )
163
+ self << field
164
+ field
165
+ end
166
+
167
+
168
+ # @param [optional,String] name
169
+ # @param [optional, Hash] attributes Any attributes you wish to add to the haml element.
170
+ def submit( name="Submit", attributes={} )
171
+ submit = Campo::Input.new( name, :submit, {value: name}.merge(attributes) )
172
+ self << submit
173
+ submit
174
+ end
175
+
176
+
177
+ # There is no easy way to give this convenience method in the same convention as the other methods, so this uses a hash argument for the label
178
+ # @example
179
+ # textarea "name", "The text wrapped inside", label: "Example textarea"
180
+ def textarea( name, inner=nil, attributes={}, &block )
181
+ if inner.kind_of? Hash
182
+ attributes = inner
183
+ inner = nil
184
+ end
185
+ label = attributes.delete(:label) || attributes.delete(:labelled)
186
+ textarea = Campo::Textarea.new( name, inner, attributes ).labelled( label )
187
+ self << textarea
188
+ textarea
189
+ end
190
+ end # Convenience
191
+
192
+
193
+ module Helpers
194
+
195
+ # [ [id, lookup, selected || false], ... ]
196
+ def self.options_builder( name, opts )
197
+ return [] if opts.nil? || opts.empty?
198
+
199
+ if opts.respond_to? :each_pair
200
+ opts.map do |id, (inner, selected, atts)|
201
+ Campo::Option.new( name, id, inner, selected, atts )
202
+ end
203
+ else
204
+ opts.map do |id, inner, selected, atts|
205
+ Campo::Option.new( name, id, inner, selected, atts )
206
+ end
207
+ end
208
+ end # def
209
+
210
+
211
+ def self.options_outputter( opts=[] )
212
+ return "" if opts.nil? || opts.empty?
213
+ opts.map{|o| "#{o.output}\n" }.reduce(:+)
214
+ end
215
+ end # Helpers
216
+
217
+ @atts = {}
218
+
219
+
220
+ class << self
221
+ attr_accessor :atts
222
+ end
223
+
224
+
225
+ # Almost every Campo class inherits from this.
226
+ # @abstract Not entirely abstract, but should always be subclassed.
227
+ class Base
228
+ include Childish
229
+ include Iding
230
+ include Enumerable
231
+ alias_method :enumerable_select, :select
232
+ include Convenience
233
+
234
+ # Default attributes.
235
+ DEFAULT = { tabindex: nil }
236
+
237
+ # @!attribute [r] attributes The element's html attributes.
238
+ # @return [Hash]
239
+
240
+ # @!attribute [r] fields The element's child elements.
241
+ # @return [Array<Base>]
242
+
243
+ attr_accessor :attributes, :fields
244
+
245
+ # @param [String] name The value of the element's name attribute.
246
+ # @param [Hash,optional] attributes Any attributes for the element. Defaults to a generated tabindex (dependent on the order of form elements).
247
+ # @yield Any fields defined in the passed block become children of this element.
248
+ def initialize( name, attributes={}, &block )
249
+ @attributes = DEFAULT.merge( {id: name}.merge(attributes.merge({name: name})) ).reject{|k,v| v.nil? }
250
+ @fields = []
251
+
252
+ instance_eval( &block ) if block
253
+ end
254
+
255
+
256
+ # Iterates over the fields array.
257
+ def each(&block)
258
+ block.call self if block
259
+ if respond_to?(:fields) &! fields.empty?
260
+ fields.each{|field| field.each &block }
261
+ end
262
+ end
263
+
264
+
265
+ # Takes a block that handles the rendering.
266
+ def on_output( &block )
267
+ @output_listener = block
268
+ end
269
+
270
+
271
+ # Render to Haml
272
+ # @param [Integer] n
273
+ # @param [Integer] tab
274
+ def output( n=0, tab=2 )
275
+ n ||= 0
276
+ tab ||= 2
277
+ @output_listener.call n, tab
278
+ end
279
+
280
+
281
+ # Bit of a convenience method for adding a label around any element.
282
+ # @param [String] inner The text for the label.
283
+ # @return [Base]
284
+ def labelled( inner=nil )
285
+ inner ||= self.attributes[:name].gsub(/\[\]/, "").gsub("_"," ").capitalize
286
+ parent = self.parent
287
+ label = Label.new( %Q!#{@attributes[:id]}!, inner ) << self
288
+ retval = if parent.nil?
289
+ label
290
+ else
291
+ parent.fields.delete self
292
+ parent << label
293
+ label
294
+ end
295
+
296
+ retval
297
+ end # labelled
298
+
299
+
300
+ # Takes a hash and transforms the key value pairs into a stringified version that Haml can consume.
301
+ # @param [Hash] hash The hash to stringify.
302
+ # @param [Array<#to_s>] skips Keys to skip.
303
+ # @todo Make an Attributes class < Hash that deals with this.
304
+ # @api private
305
+ def self.unhash( hash, skips=nil )
306
+ skips = skips.nil? ? [] : skips.map(&:to_sym) # all keys are symbols
307
+ hash.reject{|k,v| v.nil? }.reject{|k,v| skips.include? k.to_sym }.reduce(""){|mem, (k,v)| mem + %Q!#{k.to_s.include?("-") ? ":\"#{k}\" =>" : "#{k}:"} #{Base.quotable(v)}, !}
308
+ end
309
+
310
+
311
+ # @api private
312
+ # if the string provided begins with a double quote but does not end in one, make it an unquoted string on output
313
+ # else, wrap it in quotes
314
+ # @param [String] s
315
+ def self.quotable( s )
316
+ retval = if s.respond_to?(:start_with?) && s.start_with?( %Q!"! ) &! s.end_with?( %Q!"! )
317
+ s[1.. -1] # chop the first character
318
+ else
319
+ %Q!"#{s}"! # wrap
320
+ end
321
+ end
322
+
323
+
324
+ # Where the magic of output happens.
325
+ # @param [Base] top
326
+ # @param [String] so_far
327
+ # @param [Integer] depth
328
+ # @param [Integer] tab Number of spaces for a tab.
329
+ def self.output( top, so_far="", depth=0, tab=2)
330
+ so_far << "#{top.output( depth, tab )}\n"
331
+ depth += 1
332
+ if top.respond_to?( :fields ) && top.fields.length >= 1
333
+ top.fields.each do |field|
334
+ so_far = Base.output( field, so_far, depth, tab )
335
+ end
336
+ end
337
+
338
+ so_far
339
+ end
340
+
341
+ alias :render :output
342
+
343
+ end # Base
344
+
345
+
346
+ # @see Convenience#literal
347
+ def self.literal( *args, &block )
348
+ Campo::Literal.new( *args, &block )
349
+ end
350
+
351
+
352
+ # Pass anything but the form for the first argument to *not* have the local variable defaults added to the top
353
+ # @example
354
+ # Campo.output form # would add the default locals
355
+ # # these won't
356
+ # Campo.output :partial, input_field
357
+ # Campo.output false, label
358
+ # Campo.output true, fieldset
359
+ def self.output( fields, options={} )
360
+ Outputter.new( options.delete(:tab) ).run( fields, options )
361
+ end # self.output
362
+
363
+ # end Campo methods
364
+
365
+
366
+ class Outputter
367
+
368
+ def before_output( &block )
369
+ befores << block
370
+ end
371
+
372
+
373
+ def after_output( &block )
374
+ afters << block
375
+ end
376
+
377
+
378
+ def befores
379
+ @befores ||= check_for_plugins :befores
380
+ end
381
+
382
+
383
+ def afters
384
+ @afters ||= check_for_plugins :afters
385
+ end
386
+
387
+
388
+ def check_for_plugins( type )
389
+ Campo.plugins.reduce [] do |mem, (_,plugin)|
390
+ mem + plugin.send(:"#{type}" )
391
+ end
392
+ end
393
+
394
+
395
+ def initialize( tab=nil, &block )
396
+ options[:tab] = tab unless tab.nil?
397
+ instance_eval( &block ) if block
398
+ end
399
+
400
+ attr_accessor :output
401
+
402
+ DEFAULT_OPTIONS={n: 0, tab: 2}
403
+
404
+
405
+ def options
406
+ @options ||= DEFAULT_OPTIONS
407
+ end
408
+
409
+
410
+ def run( fields, opts={} )
411
+ opts = options.merge opts
412
+ tab = opts.delete(:tab) || @tab
413
+
414
+ output = ""
415
+ befores.each{|f| instance_exec( fields, options, &f ) }
416
+ output = Base.output( fields, output, options[:n], tab )
417
+ output = afters.reduce(output){|mem,obj| instance_exec mem, opts, &obj }
418
+ output
419
+ end
420
+ end
421
+
422
+
423
+ class Form < Base
424
+ DEFAULT = { method: "POST" }
425
+
426
+ # @param [String] name The form's name (html) attribute.
427
+ # @param [optional, Hash] attributes Html attributes. They can be anything you like. Defaults follow:
428
+ # @option attributes [String] :method ("POST")
429
+ # @example
430
+ # form = Campo::Form.new "example", "/path/to/post/to/" do |form|
431
+ # form.text "first_field"
432
+ # #... more fields follow
433
+ # end
434
+ def initialize(name, attributes={} )
435
+ super( name, DEFAULT.merge( attributes ) )
436
+ self.on_output do |n=0, tab=2|
437
+ %Q!#{" " * n * tab}%form{ atts[:#{name.gsub(/\W/, "_").downcase}], #{Base.unhash( @attributes )} }!
438
+ end
439
+ end
440
+
441
+ end # Form
442
+
443
+
444
+ # Probably, the first method you'll call.
445
+ # @example
446
+ # # Form with a block
447
+ # form = Campo.form "form1", action: "/go/for/it/" do |f|
448
+ # f.text "Hello"
449
+ # #... more fields follow
450
+ # end
451
+ #
452
+ # @param [String] name The form's name (html) attribute.
453
+ # @param [optional, Hash] attributes Html attributes. They can be anything you like. Defaults follow:
454
+ # @option attributes [String] :method ("POST") The method attribute for the form.
455
+ # @see Form#initialize
456
+ def self.form( name, attributes={}, &block )
457
+ Form.new( name, attributes, &block )
458
+ end
459
+
460
+
461
+ class Haml_Ruby_Insert < Base
462
+ def initialize( s )
463
+ raise ArgumentError, "you may only pass a string to Haml_Ruby_Insert/bit_of_ruby" unless s.kind_of?( String )
464
+ super( nil ) # no name needed
465
+
466
+ # @todo Don't enforce the equals sign, as a hyphen is also valid for adding a bit of ruby. Raise an exception
467
+ @s = s.start_with?( '=' ) ? s : "= " + s.to_s
468
+
469
+ self.on_output do |n=0, tab=2|
470
+ (" " * n * tab) + @s
471
+ end
472
+ end
473
+ end # Haml_Ruby_Insert
474
+
475
+
476
+ # Add whatever you need to with a literal.
477
+ class Literal < Base
478
+
479
+ # @param [String] s The literal string.
480
+ # @param [Hash,optional] attributes Any html attributes you wish the literal to have.
481
+ def initialize( s, attributes={} )
482
+ super( nil, attributes ) # no name needed
483
+ @s = s
484
+
485
+ self.on_output do |n=0, tab=2|
486
+ left,right = if @attributes.empty?
487
+ ['','']
488
+ else
489
+ ['{ ', '}']
490
+ end
491
+ %Q!#{" " * n * tab}#{@s}! + left + Base.unhash( @attributes ) + right
492
+ end
493
+ self
494
+ end
495
+ end # Literal
496
+
497
+
498
+ class Select < Base
499
+ def initialize( name, params={} )
500
+ opts = params[:opts] || []
501
+ attributes = params[:attributes] || {}
502
+ haml_insert = params[:haml_insert] || nil
503
+
504
+ super( name, { tabindex: %q!#{@campo_tabindex += 1}! }.merge(attributes) )
505
+
506
+ self.on_output do |n=0, tab=2|
507
+ %Q!#{" " * n * tab}%select{ atts[:#{name.gsub(/\W/, "_").downcase}], #{Base.unhash( @attributes )} }!
508
+ end
509
+
510
+ self.fields += Helpers.options_builder( name, opts ) unless opts.nil? || opts.empty?
511
+
512
+ self.fields << Haml_Ruby_Insert.new( haml_insert ) unless haml_insert.nil?
513
+
514
+ self
515
+ end # initialize
516
+
517
+
518
+ # @example (see Convenience#select)
519
+ def option( *args )
520
+ value = args.shift
521
+ inner = args.shift
522
+ selected, attributes = *args
523
+ inner = value.capitalize if inner.nil?
524
+ self << Campo::Option.new( @attributes[:name], value, inner, selected, attributes )
525
+ self
526
+ end
527
+
528
+
529
+ # Adds a default selection to a select list. By default it is disabled.
530
+ # @param [String,nil] The display string for the option. Default is "Choose one:".
531
+ # @param [Hash,nil] attributes Attributes for the option. Defaults to {disabled: "disabled"}, pass in an empty hash to override (or a filled one), or nil for the default.
532
+ # @example
533
+ # As a default:
534
+ # form.select("teas").with_default.option("ceylon")
535
+ # # output:
536
+ # %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", name: "teas", }
537
+ # %option{ value: "", disabled: "disabled", name: "teas", }Choose one:
538
+ # %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
539
+ #
540
+ # form.select("teas").with_default("My fave tea is:").option("ceylon")
541
+ # # output:
542
+ # %select{ atts[:teas], tabindex: "#{@campo_tabindex += 1}", name: "teas", }
543
+ # %option{ value: "", disabled: "disabled", name: "teas", }My fave tea is:
544
+ # %option{ atts[:teas_ceylon], value: "ceylon", id: "teas_ceylon", name: "teas", }Ceylon
545
+ def with_default( inner="Choose one:", attributes={disabled: "disabled"} )
546
+ unless inner.nil? || inner.kind_of?( String )
547
+ attributes = inner
548
+ inner = nil
549
+ end
550
+
551
+ inner ||="Choose one:"
552
+ attributes ||= {disabled: "disabled"}
553
+ attributes = {id: "#{@attributes[:name]}_default" }.merge! attributes
554
+ self.fields.unshift Campo::Option.new( @attributes[:name], "", inner , nil, attributes )
555
+ self
556
+ end
557
+
558
+ end # Select
559
+
560
+
561
+ # Options for your Selectas!
562
+ class Option < Base
563
+
564
+ # @param [String] name
565
+ # @param [String] value
566
+ # @param [String] inner
567
+ # @param [true,false,nil] selected
568
+ # @param [Hash] attributes
569
+ def initialize( name, value, inner=nil, selected=nil, attributes={} )
570
+ unless inner.nil? || inner.kind_of?( String )
571
+ attributes = selected
572
+ selected = inner
573
+ inner = nil
574
+ end
575
+
576
+ unless selected.nil? || selected.kind_of?( TrueClass )
577
+ if selected.respond_to? :each_pair
578
+ attributes = selected
579
+ selected = nil
580
+ else
581
+ selected = true
582
+ @selected = true
583
+ end
584
+ end
585
+
586
+ attributes ||= {}
587
+
588
+ @inner = (inner || value.gsub("_"," ").capitalize)
589
+
590
+ attributes = { id: "#{(name.gsub(/\W/, "_") + id_tag(value).gsub(/\W/, "_")).downcase}" }.merge(attributes) unless value.nil? || value.to_s.empty?
591
+
592
+ super( name, {
593
+ value: value,
594
+ selected: (selected ? "selected" : nil)
595
+ }.merge( attributes ) )
596
+
597
+ atts_string = "atts[:#{@attributes[:id]}]," unless @attributes[:id].nil?
598
+
599
+ self.on_output do |n=0, tab=2|
600
+ %Q!#{" " * n * tab}%option{ #{atts_string} #{Base.unhash( @attributes )} }#{@inner}!
601
+ end
602
+
603
+ end #initialize
604
+ end # Option
605
+
606
+
607
+ # form << Campo::Input.new( "submit", :submit )
608
+ class Input < Base
609
+
610
+ # @param [String] name
611
+ # @param [Symbol] type
612
+ # @param [Hash] attributes
613
+ # @example
614
+ # Campo::Input.new( "abc", :text, maxlength: 50 )
615
+ # # => %input{ atts[:abc], tabindex: "#{@campo_tabindex += 1}", id: "abc", type: "text", maxlength: "50", name: "abc", }
616
+ def initialize( name, type=:text, attributes={} )
617
+ id_tag = id_tag(
618
+ [:text,:hidden,:submit,:password].include?(type) ?
619
+ nil :
620
+ attributes[:value]
621
+ ).gsub(/\W/, "_")
622
+
623
+ name2 = name.gsub(/\[\]/, "") # remove any [] that may have been used for an array like / grouped object
624
+
625
+ atts_name = "#{name2.gsub(/\W/, "_")}#{id_tag}"
626
+
627
+ super( name,
628
+ { type: type.to_s,
629
+ id: "#{name2}#{id_tag}",
630
+ tabindex: %q!#{@campo_tabindex += 1}!,
631
+ }.merge( attributes ) )
632
+
633
+
634
+ @attributes.delete(:name) if type == :submit
635
+ @attributes.delete(:tabindex) if type == :hidden
636
+
637
+ self.on_output do |n=0, tab=2|
638
+ %Q!#{" " * n * tab}%input{ atts[:#{atts_name}], #{Base.unhash( @attributes )} }!
639
+ end
640
+ end
641
+ end
642
+
643
+
644
+ class Fieldset < Base
645
+
646
+ # @param [String,nil] text Text for the legend tag
647
+ # @param [Hash] attributes Hash of html attributes
648
+ def initialize( text=nil, attributes={} )
649
+ if text.kind_of? Hash
650
+ attributes = text
651
+ text = nil
652
+ end
653
+ super( nil, attributes )
654
+ @attributes.delete(:name)
655
+
656
+ self.on_output do |n=0, tab=2|
657
+ %Q!#{" " * n * tab}%fieldset{ #{Base.unhash( @attributes )} }!
658
+ end
659
+ @fields.unshift Legend.new( text ) unless text.nil?
660
+ end # initialize
661
+ end # Fieldset
662
+
663
+
664
+ class Legend < Base
665
+
666
+ def initialize( inner, attributes={} )
667
+ super( nil, attributes )
668
+ @attributes.delete(:name)
669
+ @inner = inner
670
+
671
+ self.on_output do |n=0, tab=2|
672
+ %Q!#{" " * n * tab}%legend{ #{Base.unhash( @attributes )} }#{@inner}!
673
+ end
674
+ end # initialize
675
+ end # Fieldset
676
+
677
+
678
+ class Label < Base
679
+
680
+ DEFAULT = { for: nil }
681
+
682
+ attr_reader :attributes, :fields
683
+
684
+ def initialize( for_element, inner=nil, attributes={} )
685
+ if inner.kind_of? Hash
686
+ attributes = inner
687
+ inner = nil
688
+ end
689
+ super( nil, attributes.merge(for: for_element) )
690
+
691
+ @inner = inner
692
+
693
+ self.on_output do |n=0, tab=2|
694
+ %Q!#{" " * n * tab}%label{ #{Base.unhash( @attributes )} }\n#{" " * (n + 1) * tab}#{@inner}!
695
+ end
696
+ end
697
+
698
+ end # Label
699
+
700
+
701
+ class Textarea < Base
702
+ DEFAULT = { cols: 40, rows: 10, tabindex: %q!#{@campo_tabindex += 1}! }
703
+
704
+ def initialize( name, inner=nil, attributes={} )
705
+ if inner.kind_of? Hash
706
+ attributes = inner
707
+ inner = nil
708
+ end
709
+ super( name, DEFAULT.merge( attributes ) )
710
+ @inner = inner
711
+ self.on_output do |n=0, tab=2|
712
+ %Q!#{" " * n * tab}%textarea{ atts[:#{name.gsub(/\W/, "_")}], #{Base.unhash( @attributes )} }= inners[:#{name.gsub(/\W/, "_")}] !
713
+ end
714
+ end
715
+ end # Textarea
716
+
717
+ # add whatever you need to with a literal
718
+ class Span < Base
719
+
720
+ def initialize( id, inner, attributes={} )
721
+ if inner.kind_of? Hash
722
+ attributes = inner
723
+ inner = nil
724
+ end
725
+ super( id, attributes )
726
+ @attributes.delete(:name) # only id for this element
727
+ @inner = inner
728
+
729
+ unless @inner.nil? or @inner.empty?
730
+ self.fields.push Campo.literal(@inner)
731
+ end
732
+
733
+ self.on_output do |n=0, tab=2|
734
+ %Q!#{" " * n * tab}%span{#{Base.unhash( @attributes )}}!
735
+ end
736
+ self
737
+ end
738
+ end # Literal
739
+
740
+ end