kiss 0.9.4 → 1.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.
@@ -1,3 +1,5 @@
1
+ module Digest; end
2
+
1
3
  class Kiss
2
4
  class Form
3
5
  module AttributesSetter
@@ -77,53 +79,50 @@ class Kiss
77
79
  @fields << :section_break
78
80
  end
79
81
 
80
- # Creates and adds a field to the form, according to specified attributes.
81
- def add_field(attrs)
82
- raise "added second field named '#{attrs[:name]}'; field names must be unique" if @fields_hash[attrs[:name]]
82
+ def create_field(attrs)
83
+ raise "form already has field named '#{attrs[:name]}'" if @fields_hash[attrs[:name]]
83
84
 
84
85
  attrs[:type] ||= :text
85
86
  type = attrs[:type].to_sym
86
- unless @@field_types.has_key?(type)
87
- type = attrs[:type] = :text
88
- end
89
-
90
- @has_required_fields ||= attrs[:required]
87
+ type = attrs[:type] = :text unless @@field_types.has_key?(type)
91
88
 
92
89
  field_class = @@field_types[type]
93
- field = field_class.new(attrs)
90
+ field = field_class.new(self,attrs)
94
91
 
95
92
  field_name = field.name.to_s
96
93
  field.form = self
97
94
 
98
- unless type == :submit
99
- @fields << field
100
- @fields_hash[field_name] = field
95
+ while true
96
+ other_field = field.other_field
97
+ break unless other_field
98
+ other_field.form = self
99
+ @other_field = @form.create_field( { :name => @name + '.other' }.merge(@other) )
101
100
  end
102
101
 
102
+ @fields_hash[field_name] = field
103
+
103
104
  @enctype = 'multipart/form-data' if field.type == :file
104
105
 
105
- # if param submitted, set value of field to match
106
- if (param = @params[field_name])
107
- field.value = param
108
- elsif @object && !attrs.has_key?(:value)
109
- value = @object[field_name.to_sym] || @object[field_name]
106
+ field
107
+ end
108
+
109
+ # Creates and adds a field to the form, according to specified attributes.
110
+ def add_field(attrs)
111
+ # create field
112
+ field = create_field(attrs)
113
+
114
+ # used with mark_required to display required fields legend
115
+ @has_required_fields ||= attrs[:required]
110
116
 
111
- case field.format
112
- when :date:
113
- field.value = value ? value.strftime("%m/%d/%Y") : ''
114
- when :datetime:
115
- field.value = value ? value.strftime("%m/%d/%Y %I:%M%p") : ''
116
- else
117
- field.value = value.to_s
118
- end
119
- end
117
+ # add field to form array/hash
118
+ @fields << field
120
119
 
121
120
  field
122
121
  end
123
122
 
124
123
  # Creates and adds set of submit buttons to the form, per specified attributes.
125
124
  def add_submit(attrs)
126
- @submit = add_field({
125
+ @submit = create_field({
127
126
  :type => :submit,
128
127
  :name => 'submit',
129
128
  :save => false
@@ -131,13 +130,13 @@ class Kiss
131
130
  end
132
131
 
133
132
  # Gets hash of form values ready to be saved to Sequel::Model object.
134
- def sequel_values
135
- @sequel_values ||= begin
136
- hash = {}
137
- @fields.each {|field| hash[field.name] = field.sequel_value unless field == :section_break }
138
- hash
139
- end
140
- end
133
+ # def sequel_values
134
+ # @sequel_values ||= begin
135
+ # hash = {}
136
+ # @fields.each {|field| hash[field.name] = field.sequel_value unless field == :section_break }
137
+ # hash
138
+ # end
139
+ # end
141
140
 
142
141
  # Gets hash of form values.
143
142
  def values
@@ -194,9 +193,13 @@ class Kiss
194
193
  # don't save 'ignore' fields to the database
195
194
  next if field.ignore || !field.save
196
195
 
197
- object[field.name.to_sym] = field.sequel_value
196
+ value = (field.value != nil || object.class.db_schema[field.name.to_sym].allow_null) ?
197
+ field.value : (object.class.db_schema[field.name.to_sym][:default] ||= field.format.default)
198
+
199
+ object[field.name.to_sym] = field.save == true ? value :
200
+ (digest_class = Kiss.digest_class(field.save)) ? digest_class.hexdigest(value) : value
198
201
  end
199
-
202
+
200
203
  object.save
201
204
  end
202
205
 
@@ -211,7 +214,7 @@ class Kiss
211
214
  # and returns true. Otherwise, returns false.
212
215
  def process(object = @object)
213
216
  return false unless submitted
214
-
217
+
215
218
  if accepted
216
219
  validate
217
220
  return false if has_errors
@@ -233,7 +236,7 @@ class Kiss
233
236
  def errors_html
234
237
  return nil unless has_errors
235
238
 
236
- @errors << "Please remedy the errors shown below." if @has_field_errors
239
+ @errors << "Please correct the errors highlighted below." if @has_field_errors
237
240
 
238
241
  if @errors.size == 1
239
242
  content = @errors[0]
@@ -244,7 +247,7 @@ class Kiss
244
247
  @errors.pop if @has_field_errors
245
248
 
246
249
  plural = @errors.size > 1 || @has_field_errors ? 's' : nil
247
- %Q(<table border=0 cellspacing=0><tr class="kiss_error"><th>Error#{plural}</th></td><td class="kiss_required"></td><td>#{content}</td></tr></table>)
250
+ %Q(<div class="kiss_error">#{content}</div><br clear="all" />)
248
251
  end
249
252
 
250
253
  # Renders current action using form's HTML as action render content.
@@ -266,74 +269,71 @@ class Kiss
266
269
  # style tag
267
270
  styles = []
268
271
  styles.push( <<-EOT
269
- .kiss_form table {
272
+ table.kiss_form {
270
273
  margin-bottom: 6px;
271
274
  }
272
275
  .kiss_form td {
273
276
  padding: 2px 4px;
277
+ vertical-align: middle;
274
278
  }
275
- .kiss_form tr.kiss_error {
279
+ .kiss_form .kiss_error {
276
280
  background-color: #ff8;
277
- }
278
- .kiss_form tr.kiss_error th {
279
- vertical-align: top;
280
- padding: 2px 4px 3px 4px;
281
- text-align: right;
282
- background-color: #fc7;
281
+ padding: 2px 4px;
282
+ line-height: 135%;
283
+ float: left;
283
284
  color: #900;
284
- border: 1px solid #f96;
285
- border-right: none;
286
- }
287
- .kiss_form tr.kiss_error td {
288
- vertical-align: top;
289
- padding: 2px 6px 3px 6px;
290
- border: 1px solid #f96;
291
- border-left: none;
292
- color: #000;
285
+ border: 1px solid #ea4;
286
+ margin-bottom: 4px;
293
287
  }
294
- .kiss_form tr.kiss_error ul {
288
+ .kiss_form .kiss_error ul {
295
289
  padding-left: 16px;
296
290
  margin: 0;
297
291
  }
298
- .kiss_form tr.kiss_error td.kiss_required {
299
- padding: 0;
300
- border-right: none;
301
- }
302
292
  .kiss_form .kiss_help {
303
- padding: 0px 3px 8px 6px;
293
+ padding: 0px 3px 0 6px;
304
294
  font-size: 90%;
305
295
  color: #555;
306
296
  }
307
- .kiss_form td.kiss_required {
308
- padding: 2px 0px;
309
- color: #c00;
297
+ .kiss_required {
298
+ color: #a21;
299
+ font-size: 150%;
300
+ line-height: 50%;
301
+ position: relative;
302
+ top: 4px;
310
303
  }
311
304
  .kiss_form td.kiss_label {
312
305
  text-align: right;
313
306
  white-space: nowrap;
314
307
  }
308
+ .kiss_form td.kiss_label.error {
309
+ color: #b10;
310
+ }
315
311
  .kiss_form tr.kiss_prompt td {
316
- padding: 8px 3px 2px 4px;
312
+ padding: 8px 3px 0px 4px;
317
313
  }
318
314
  .kiss_form tr.kiss_submit td.kiss_submit {
319
315
  padding: 6px 3px;
320
316
  }
321
- .kiss_form input[type="text"],.kiss_form textarea {
317
+ .kiss_form input[type="text"],.kiss_form input[type=password],.kiss_form textarea {
322
318
  width: 250px;
319
+ margin-left: 0;
320
+ margin-right: 0;
323
321
  }
324
322
  EOT
325
323
  )
326
324
  styles.push( <<-EOT
327
325
  .kiss_form_error_message {
328
- padding: 1px 4px 2px 4px;
329
- border: 1px solid #f96;
326
+ padding: 1px 4px;
327
+ border: 1px solid #edc;
328
+ border-top: 0;
329
+ border-bottom: 1px solid #db4;
330
330
  background-color: #ff8;
331
331
  color: #900;
332
- font-size: 85%;
332
+ font-size: 90%;
333
+ line-height: 135%;
333
334
  display: block;
334
335
  float: left;
335
- margin-top: 1px;
336
-
336
+ margin-bottom: 2px;
337
337
  }
338
338
  .kiss_form_error_message div {
339
339
  color: #c00;
@@ -343,6 +343,21 @@ EOT
343
343
  padding-left: 16px;
344
344
  margin: 0;
345
345
  }
346
+ tr.kiss_form_error_row td {
347
+ padding-top: 0;
348
+ }
349
+ tr.kiss_form_error_row .kiss_form_error_message {
350
+ position: relative;
351
+ top: -2px;
352
+ margin-bottom: 0;
353
+ }
354
+ .kiss_form table.kiss_field_columns {
355
+ border-spacing: 0;
356
+ border-collapse: collapse;
357
+ }
358
+ .kiss_form table.kiss_field_columns td {
359
+ padding: 0 0 2px 0;
360
+ }
346
361
  EOT
347
362
  ) if @error_class == 'kiss_form_error_message'
348
363
  style_tag = styles.size == 0 ? '' : "<style>" + styles.join('') + "</style>"
@@ -367,8 +382,8 @@ EOT
367
382
 
368
383
  # Renders open of form table.
369
384
  def table_html_open
370
- %Q(<table border=0 cellspacing=0>) +
371
- (@has_required_fields && @mark_required ? %Q( <tr><td></td><td class="kiss_required">*</td><td colspan=2 class="kiss_help">Required field.</td></tr> ) : '')
385
+ %Q(<table class="kiss_form" border=0 cellspacing=0>) +
386
+ (@has_required_fields && @mark_required ? %Q( <tr><td></td><td class="kiss_help">Required fields marked by <span class="kiss_required">*</span></td></tr> ) : '')
372
387
  end
373
388
 
374
389
  # Renders close of form table.
@@ -378,7 +393,7 @@ EOT
378
393
 
379
394
  # Renders form submit buttons.
380
395
  def submit_html
381
- field_html(@submit)
396
+ @submit ? field_html(@submit) : ''
382
397
  end
383
398
 
384
399
  # Renders complete form HTML.
@@ -396,19 +411,23 @@ EOT
396
411
  # Renders HTML for specified form field.
397
412
  def field_html(field)
398
413
  type = field.type
399
- prompt = field.prompt.to_s
400
- label = field.label.to_s
414
+ prompt = field.prompt
415
+ label = field.label
416
+ errors = field.errors_html
417
+ required = field.required ? %Q(<span class="kiss_required">#{@mark_required}</span> ) : ''
401
418
 
402
- [
403
- (prompt != '' ? %Q(<tr class="kiss_prompt"><td colspan=2></td><td colspan=2>#{prompt}</td></tr>) : ''),
404
- "",
405
- %Q(<tr class="kiss_#{type}"><td class="kiss_label">),
406
- label != '' && prompt == '' ? label + ':' : '',
407
- '</td><td class="kiss_required">',
408
- field.required ? @mark_required : '',
419
+ ([
420
+ prompt ? %Q(<tr class="kiss_prompt"><td class="kiss_label">#{required}</td><td>#{prompt.to_s}</td></tr>) : '',
421
+
422
+ %Q(<tr class="kiss_#{type}"><td class="kiss_label#{errors ? ' error' : ''}">),
423
+ !prompt ? (required + (label ? label.to_s + ':' : '' )) : '',
409
424
  %Q(</td><td class="kiss_#{type}">),
410
- field.html, "</td></tr>"
411
- ].join
425
+ field.element_html, "</td></tr>"
426
+ ] + (errors ? [
427
+ '<tr class="kiss_form_error_row"><td class="kiss_required"></td><td>',
428
+ errors,
429
+ '</td></tr>'
430
+ ] : [])).join
412
431
  end
413
432
  end
414
433
  end
@@ -1,20 +1,58 @@
1
1
  class Kiss
2
2
  class Form
3
3
  class Field
4
- attr_accessor :name,:type,:form,:format,:currency,:label,:no_label,:prompt,:value,:ignore,:save,
5
- :options,:options_value_key,:options_display_key,:required,:cancel,:columns,:style,:hidden_join
4
+
5
+ attr_accessor :name,:type,:form,:currency,:label,:no_label,:prompt,:value,:ignore,:save,
6
+ :options,:options_value_key,:options_display_key,:required,:cancel,:columns,:style,:hidden_join,
7
+ :html,:other,:other_field,:object,:format,:display_format
6
8
 
7
9
  include Kiss::Form::AttributesSetter
8
10
 
9
- def initialize(attrs)
11
+ def initialize(form,attrs)
12
+ @form = form
10
13
  @save = true
11
- @value = ''
14
+ @currency = nil
15
+
16
+ set_attributes({
17
+ :type => :text,
18
+ :object => @form.object,
19
+ :save => true
20
+ }.merge(attrs))
21
+
22
+ @value = (@object[@name.to_sym] if @object) || @value
12
23
 
13
- set_attributes(attrs,[:name,:type])
24
+ @format = Kiss::Format.lookup(@format)
25
+ @display_format = Kiss::Format.lookup(@display_format)
26
+
27
+ @currency = '$' if @currency == :dollars
14
28
 
15
29
  @errors = []
16
30
  end
17
31
 
32
+ def other_field_html
33
+ return '' unless @other
34
+
35
+ other_checked = @value && !option_pairs.any? {|v,d| v == @value }
36
+
37
+ (@columns ? '<br/>' : '&nbsp; ') + [
38
+ input_tag_html(
39
+ { :value => 'other', :html => { :id => @name+'.other' } },
40
+ other_checked ? 'checked' : ''
41
+ ),
42
+ @other[:label] || 'Other',
43
+ ': ',
44
+ @currency.to_s,
45
+ input_tag_html({
46
+ :type => :text,
47
+ :name => @name+'.other',
48
+ :value => other_checked ? value_to_s(@value) : nil,
49
+ :html => {
50
+ :onfocus => "document.getElementById('#{@name}.other').checked = true"
51
+ }
52
+ }.merge(@other))
53
+ ].join
54
+ end
55
+
18
56
  def column_layout(elements_html)
19
57
  if @columns
20
58
  # format elements into cells
@@ -28,50 +66,76 @@ class Kiss
28
66
  return ['<table class="kiss_field_columns">',element_ranges.map do |range|
29
67
  ["<tr>", elements_html[range], "</tr>"]
30
68
  end,'</table>'].flatten.join
31
- join_with = ''
32
- else
33
- join_with = '&nbsp; '
34
69
  end
35
-
70
+
36
71
  # else columns <= 1
37
- elements_html.join(join_with)
72
+ elements_html.join('&nbsp; ')
38
73
  end
39
74
 
40
- def sequel_value
41
- case @format
42
- when :date:
43
- return Kiss.mdy_to_ymd(value)
44
- when :datetime:
45
- datetime = value.sub(/([ap]m)\s*\Z/i,' \1')
46
- date, time, ampm = datetime.split(/\s+/)
47
-
48
- hours, minutes = time.split(/:/)
49
- hours = 0 if hours.to_i == 12 && ampm
50
- if (ampm.downcase == 'pm')
51
- hours = hours.to_i + 12
52
- end
53
-
54
- return Kiss.mdy_to_ymd(date) + " #{hours}:#{minutes}"
55
- else
56
- return value.is_a?(Array) ? value.map {|v| v.gsub(/,/,'\,')}.join(',') : value
57
- end
75
+ # def sequel_value
76
+ # case @format
77
+ # when :date:
78
+ # return Kiss.mdy_to_ymd(value)
79
+ # when :datetime:
80
+ # datetime = value.sub(/([ap]m)\s*\Z/i,' \1')
81
+ # date, time, ampm = datetime.split(/\s+/)
82
+ #
83
+ # hours, minutes = time.split(/:/)
84
+ # hours = 0 if hours.to_i == 12 && ampm
85
+ # if (ampm.downcase == 'pm')
86
+ # hours = hours.to_i + 12
87
+ # end
88
+ #
89
+ # return Kiss.mdy_to_ymd(date) + " #{hours}:#{minutes}"
90
+ # when :month_year:
91
+ # month, year = value.sub(/\A\s*/,'').sub(/\s*\Z/,'').split(/\D+/)
92
+ # # convert two-digit years to four-digit years
93
+ # year = year.to_i
94
+ # if year < 100
95
+ # year += 1900
96
+ # year += 100 if year < Time.now.year - 95
97
+ # end
98
+ # return sprintf("%04d-%02d-01",year,month.to_i)
99
+ # else
100
+ # return value.is_a?(Array) ? value.map {|v| v.gsub(/,/,'\,')}.join(',') : value
101
+ # end
102
+ # end
103
+
104
+ def param
105
+ @param ||= @form.params[@name.to_s]
58
106
  end
59
107
 
60
108
  def value
61
- @value ||= @form.params[@name]
109
+ @value
62
110
  end
63
111
 
64
112
  def add_error(message)
65
113
  @errors << message
66
114
  @form.has_field_errors = true
67
115
  end
68
-
69
- def validate
70
- if (error = Kiss.validate_value(value,@format,@required))
71
- add_error("#{@label} #{error}")
116
+
117
+ def value_to_s(value)
118
+ value ? @format.value_to_s(value) : ''
119
+ end
120
+
121
+ def display_to_s(value)
122
+ value ? @display_format.value_to_s(value) : ''
123
+ end
124
+
125
+ def validate(enter = 'enter')
126
+ if @required && (param !~ /\S/)
127
+ # value required
128
+ add_error("Please #{enter} #{@label}")
129
+ return
130
+ end
131
+
132
+ begin
133
+ @value = @format.validate(param)
134
+ rescue Kiss::Format::ValidateError => e
135
+ add_error("#{e.message.capitalize}")
72
136
  end
73
137
 
74
- value
138
+ @value
75
139
  end
76
140
 
77
141
  def errors_html
@@ -87,33 +151,62 @@ class Kiss
87
151
  end
88
152
 
89
153
  def element_html
90
- if @currency == :dollars
91
- return '$' + input_tag_html( :value => @value, :style => 'width: 80px' )
154
+ attrs = { :value => param || value_to_s(@value) }
155
+ if width = @format.input_width
156
+ attrs[:style] = "width: #{width}px"
92
157
  end
93
- input_tag_html( :value => @value )
158
+
159
+ @currency.to_s + input_tag_html( attrs ) +
160
+ ((legend = @format.legend) ? " <small>(#{legend})</small>" : '')
94
161
  end
95
162
 
96
163
  def html
97
164
  errors = errors_html
98
- element_html + (errors ? %Q(<br/>#{errors}) : '')
165
+ element_html + (errors ? (@columns ? '' : '<br/>') + %Q(#{errors}) : '')
99
166
  end
100
-
101
- def input_tag_html(attrs = {}, extra_html = '')
167
+
168
+ def tag_start_html(tag_name, attrs = {}, extra_html = nil)
102
169
  attrs = attrs.clone
103
170
  attrs[:name] ||= @name
104
- attrs[:type] ||= @type
105
- attrs[:style] ||= @style
171
+ attrs[:size] ||= @size if @size
172
+ attrs[:style] ||= @style if @style
106
173
 
107
- attrs_html = []
108
- attrs.each_pair {|k,v| attrs_html.push %Q(#{k}="#{v}") }
109
- [ '<input', attrs_html, extra_html + '/>' ].flatten.join(' ')
174
+ html_parts = ["<#{tag_name}"]
175
+
176
+ html = attrs.delete(:html) || @html
177
+ attrs.each_pair {|k,v| html_parts.push %Q(#{k}="#{v}") }
178
+
179
+ if html
180
+ if html.is_a?(Hash)
181
+ html.each_pair {|k,v| html_parts.push %Q(#{k}="#{v}") }
182
+ else
183
+ html_parts.push(@html.to_s)
184
+ end
185
+ end
186
+
187
+ html_parts.push(extra_html) if extra_html
188
+ html_parts.join(' ')
189
+ end
190
+
191
+ def tag_html(tag_name, attrs = {}, extra_html = nil)
192
+ tag_start_html(tag_name,attrs,extra_html) + ' />'
193
+ end
194
+
195
+ def content_tag_html(tag_name, content, attrs = {}, extra_html = nil)
196
+ tag_start_html(tag_name,attrs,extra_html) + ">#{content}</#{tag_name}>"
197
+ end
198
+
199
+ def input_tag_html(attrs = {}, extra_html = nil)
200
+ tag_html(
201
+ 'input',
202
+ {:type => @type}.merge(attrs),
203
+ extra_html
204
+ )
110
205
  end
111
206
  end
112
207
 
113
- class HiddenField < Field
114
- end
115
- class TextField < Field
116
- end
208
+ class HiddenField < Field; end
209
+ class TextField < Field; end
117
210
 
118
211
  class TextAreaField < Field
119
212
  attr_accessor :rows,:cols
@@ -123,8 +216,16 @@ class Kiss
123
216
  @cols = 20
124
217
  super(*args)
125
218
  end
219
+
126
220
  def element_html
127
- %Q(<textarea name="#{@name}" rows="#{@rows ||= 1}" cols="#{@cols ||= 1}" style="#{@style ||= ''}">#{@value}</textarea>)
221
+ content_tag_html(
222
+ 'textarea',
223
+ param || value_to_s(@value),
224
+ {
225
+ :rows => @rows ||= 1,
226
+ :cols => @cols ||= 1
227
+ }
228
+ )
128
229
  end
129
230
  end
130
231
 
@@ -136,7 +237,7 @@ class Kiss
136
237
 
137
238
  class BooleanField < Field
138
239
  def element_html
139
- input_tag_html({ :value => 1 }, @value ? 'CHECKED ' : '')
240
+ input_tag_html({ :value => 1 }, @value ? 'checked' : '')
140
241
  end
141
242
  end
142
243
 
@@ -164,14 +265,9 @@ class Kiss
164
265
  elements_html.join(' ')
165
266
  end
166
267
 
167
- def validate
168
- # do nothing for now
169
- value
170
- end
171
-
172
268
  def elements_html
173
269
  @options.map do |option|
174
- input_tag_html({ :value => option })
270
+ input_tag_html({ :value => value_to_s(option) })
175
271
  end
176
272
  end
177
273
  end
@@ -180,13 +276,12 @@ class Kiss
180
276
 
181
277
  class MultiChoiceField < Field
182
278
  def option_pairs
183
- if (defined? @options_value_key)
184
- # options_display_map not supported
185
- # default to options_display_key
186
- return @options.map {|option| [ option[@options_value_key], option[@options_display_key] ]}
187
- end
188
-
189
- @options.map {|option| [ option, option ] }
279
+ (defined? @options_value_key) ?
280
+ @options.map {|option| [ option[@options_value_key], option[@options_display_key] ]} :
281
+ begin
282
+ @display_format = @format
283
+ @options.map {|option| [ option, option ] }
284
+ end
190
285
  end
191
286
 
192
287
  def matched_options(value)
@@ -197,12 +292,13 @@ class Kiss
197
292
  end
198
293
 
199
294
  def validate
200
- @value = @form.params[@name]
295
+ if @other && param == 'other'
296
+ @param = @form.params[@name+'.other']
297
+ end
298
+ super('select')
201
299
 
202
- if (@value =~ /\S/)
203
- add_error "#{@label} invalid" unless matched_options(@value).size > 0
204
- elsif @required
205
- add_error "#{@label} required"
300
+ if @value =~ /\S/ && matched_options(@value).size == 0
301
+ add_error "Invalid selection"
206
302
  end
207
303
  end
208
304
  end
@@ -215,21 +311,24 @@ class Kiss
215
311
 
216
312
  options_html = option_pairs.map do |value,display|
217
313
  selected = (@value.to_s == value.to_s) ? ' selected' : ''
218
- %Q(<option value="#{value}"#{selected}>#{display}</option>)
219
- end
220
-
221
- [%Q(<select name="#{@name}" style="#{@style}">), placeholder_html, options_html, '</select>'].flatten.join
314
+ %Q(<option value="#{value_to_s(value)}"#{selected}>#{display}</option>)
315
+ end.join
316
+
317
+ content_tag_html('select', placeholder_html + options_html) + other_field_html
222
318
  end
223
319
  end
224
320
 
225
321
  class RadioField < MultiChoiceField
226
322
  def element_html
227
- column_layout(elements_html)
323
+ column_layout(elements_html) + other_field_html
228
324
  end
229
325
 
230
326
  def elements_html
231
- return option_pairs.map do |value,display|
232
- input_tag_html({ :value => value }, (@value.to_s == value.to_s) ? ' checked' : '' ) + display
327
+ option_pairs.map do |value,display|
328
+ input_tag_html(
329
+ { :value => value_to_s(value) },
330
+ (@value.to_s == value.to_s) ? 'checked' : ''
331
+ ) + @currency.to_s + display_to_s(display)
233
332
  end
234
333
  end
235
334
  end
@@ -238,18 +337,27 @@ class Kiss
238
337
  # ------ MultiValueField
239
338
 
240
339
  class MultiValueField < MultiChoiceField
340
+ def param
341
+ @form.params[@name.to_s+'[]'] || []
342
+ end
343
+
241
344
  def validate
242
- @value = @form.params[@name.to_s+'[]'] || []
243
-
345
+ begin
346
+ @value = param.map { |p| @format.validate(p) }
347
+ rescue Kiss::Format::ValidateError => e
348
+ add_error("#{e.message.capitalize}")
349
+ return
350
+ end
351
+
244
352
  if @value.size > 0
245
- @value.each do |value|
246
- unless (matched_options(value).size > 0)
247
- add_error "Invalid selection for #{@label}"
353
+ @value.each do |v|
354
+ unless (matched_options(v).size > 0)
355
+ add_error "#Invalid selection"
248
356
  return
249
357
  end
250
358
  end
251
359
  elsif @required
252
- add_error "#{@label} required"
360
+ add_error "Please select at least one #{@label}"
253
361
  end
254
362
  end
255
363
 
@@ -260,21 +368,22 @@ class Kiss
260
368
 
261
369
  class CheckboxField < MultiValueField
262
370
  def element_html
263
- if @hidden_join
264
- hidden_options = input_tag_html(
265
- :type => 'hidden',
266
- :name => "#{@name}_options",
267
- :value => option_pairs.map {|v,d| v}.join(@hidden_join)
268
- )
269
- else
270
- hidden_options = ''
271
- end
272
- column_layout(elements_html) + hidden_options
371
+ hidden_options = @hidden_join ? input_tag_html(
372
+ :type => 'hidden',
373
+ :name => "#{@name}_options",
374
+ :value => option_pairs.map {|v,d| value_to_s(v) }.join(@hidden_join)
375
+ ) : ''
376
+
377
+ column_layout(elements_html) + other_field_html + hidden_options
273
378
  end
274
379
 
275
380
  def elements_html
276
- return option_pairs.map do |value,display|
277
- input_tag_html({ :name => @name.to_s+'[]', :value => value }, selected_option_values[value.to_s] ? ' checked' : '' ) + display.to_s
381
+ name = @name.to_s+'[]'
382
+ option_pairs.map do |value,display|
383
+ input_tag_html(
384
+ { :name => name, :value => value_to_s(value) },
385
+ selected_option_values[value.to_s] ? 'checked' : ''
386
+ ) + @currency.to_s + display_to_s(display)
278
387
  end
279
388
  end
280
389
  end
@@ -283,10 +392,15 @@ class Kiss
283
392
  def element_html
284
393
  options_html = option_pairs.map do |value,display|
285
394
  selected = selected_option_values[value] ? ' selected' : ''
286
- %Q(<option value="#{value}"#{selected}>#{display}</option>)
287
- end
288
-
289
- [%Q(<select multiple name="#{@name}[]" size="#{@size}" style="#{@style}">), options_html, '</select>'].flatten.join
395
+ %Q(<option value="#{value_to_s(value)}"#{selected}>#{display}</option>)
396
+ end.join
397
+
398
+ content_tag_html(
399
+ 'select',
400
+ options_html,
401
+ {},
402
+ 'multiple'
403
+ )
290
404
  end
291
405
  end
292
406
  end