campo 0.3.4 → 0.9.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.
@@ -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