Class: Showoff::Compiler::Form

Inherits:
Object
  • Object
show all
Defined in:
lib/showoff/compiler/form.rb

Overview

Adds form processing to the compiler

Class Method Summary collapse

Class Method Details

.form_checked?(modifier) ⇒ Boolean

Returns:

  • (Boolean)


232
233
234
# File 'lib/showoff/compiler/form.rb', line 232

def self.form_checked?(modifier)
  modifier.downcase.include?('x') ? "checked='checked'" : ''
end

.form_classes(modifier) ⇒ Object



224
225
226
227
228
229
230
# File 'lib/showoff/compiler/form.rb', line 224

def self.form_classes(modifier)
  modifier.downcase!
  classes = ['response']
  classes << 'correct' if modifier.include?('=')

  classes.join(' ')
end

.form_element(id, code, name, required, rhs, text) ⇒ String

Generates markup for any supported form element type

Parameters:

  • id (String)

    The HTML ID for the generated markup

  • code (String)

    The question code; used for indexing

  • name (String)

    The full text of the question

  • required (Boolean)

    Whether the rendered element should be marked as required

  • rhs (String)

    The right hand side of the question specification, if on one line.

  • text (String)

    The full content of the content, used for recursive multiline calls

Returns:

  • (String)

    The HTML markup for all the HTML nodes that the full element renders to.

See Also:



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/showoff/compiler/form.rb', line 89

def self.form_element(id, code, name, required, rhs, text)
  required = required ? 'required' : ''
  str =  "<div class='form element #{required}' id='#{id}' data-name='#{code}'>"
  str << "<label class='question' for='#{id}'>#{name}</label>"
  case rhs
  when /^\[\s+(\d*)\]$$/             # value = [    5]                                     (textarea)
    str << self.form_element_textarea(id, code, $1)
  when /^___+(?:\[(\d+)\])?$/        # value = ___[50]                                     (text)
    str << self.form_element_text(id, code, $1)
  when /^\(.?\)/                     # value = (x) option one (=) opt2 () opt3 -> option 3 (radio)
    str << self.form_element_radio(id, code, rhs.scan(/\((.?)\)\s*([^()]+)\s*/))
  when /^\[.?\]/                     # value = [x] option one [=] opt2 [] opt3 -> option 3 (checkboxes)
    str << self.form_element_checkboxes(id, code, rhs.scan(/\[(.?)\] ?([^\[\]]+)/))
  when /^\{(.*)\}$/                  # value = {BOS, [SFO], (NYC)}                         (select shorthand)
    str << self.form_element_select(id, code, rhs.scan(/[(\[]?\w+[)\]]?/))
  when /^\{$/                        # value = {                                           (select)
    str << self.form_element_select_multiline(id, code, text)
  when ''                            # value =                                             (radio/checkbox list)
    str << self.form_element_multiline(id, code, text)
  else
    Showoff::Logger.warn "Unmatched form element: #{rhs}"
  end
  str << '</div>'
end

.form_element_check_or_radio(type, id, code, value, label, modifier) ⇒ Object



214
215
216
217
218
219
220
221
222
# File 'lib/showoff/compiler/form.rb', line 214

def self.form_element_check_or_radio(type, id, code, value, label, modifier)
  # yes, value and id are conflated, because this is the id of the parent widget
  checked = self.form_checked?(modifier)
  classes = self.form_classes(modifier)

  name = (type == 'checkbox') ? "#{code}[]" : code
  str  =  "<input type='#{type}' name='#{name}' id='#{id}_#{value}' value='#{value}' class='#{classes}' #{checked} />"
  str << "<label for='#{id}_#{value}' class='#{classes}'>#{label}</label>"
end

.form_element_check_or_radio_set(type, id, code, items) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/showoff/compiler/form.rb', line 197

def self.form_element_check_or_radio_set(type, id, code, items)
  str = ''
  items.each do |item|
    modifier = item[0]

    if item[1] =~ /^(\w*) -> (.*)$/
      value = $1
      label = $2
    else
      value = label = item[1].strip
    end

    str << self.form_element_check_or_radio(type, id, code, value, label, modifier)
  end
  str
end

.form_element_checkboxes(id, code, items) ⇒ Object



127
128
129
# File 'lib/showoff/compiler/form.rb', line 127

def self.form_element_checkboxes(id, code, items)
  self.form_element_check_or_radio_set('checkbox', id, code, items)
end

.form_element_multiline(id, code, text) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/showoff/compiler/form.rb', line 173

def self.form_element_multiline(id, code, text)
  str = '<ul>'

  text.split("\n")[1..-1].each do |item|
    case item
    when /\((.?)\)\s*(\w+)\s*(?:->\s*(.*)?)?/
      modifier = $1
      type     = 'radio'
      value    = $2
      label    = $3 || $2
    when /\[(.?)\]\s*(\w+)\s*(?:->\s*(.*)?)?/
      modifier = $1
      type     = 'checkbox'
      value    = $2
      label    = $3 || $2
    end

    str << '<li>'
    str << self.form_element_check_or_radio(type, id, code, value, label, modifier)
    str << '</li>'
  end
  str << '</ul>'
end

.form_element_radio(id, code, items) ⇒ Object



123
124
125
# File 'lib/showoff/compiler/form.rb', line 123

def self.form_element_radio(id, code, items)
  self.form_element_check_or_radio_set('radio', id, code, items)
end

.form_element_select(id, code, items) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/showoff/compiler/form.rb', line 131

def self.form_element_select(id, code, items)
  str =  "<select id='#{id}_response' name='#{code}'>"
  str << '<option value="">----</option>'

  items.each do |item|
    selected = classes = ''
    case item
    when /\((\w+)\)/
      item     = $1
      selected = 'selected'
    when /\[(\w+)\]/
      item     = $1
      classes  = 'correct'
    end
    str << "<option value='#{item}' class='#{classes}' #{selected}>#{item}</option>"
  end
  str << '</select>'
end

.form_element_select_multiline(id, code, text) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/showoff/compiler/form.rb', line 150

def self.form_element_select_multiline(id, code, text)
  str =  "<select id='#{id}_response' name='#{code}'>"
  str << '<option value="">----</option>'

  text.split("\n")[1..-1].each do |item|
    case item
    when /^   +\((\w+) -> (.+)\),?$/         # (NYC -> New York City)
      str << "<option value='#{$1}' selected>#{$2}</option>"
    when /^   +\[(\w+) -> (.+)\],?$/         # [NYC -> New York City]
      str << "<option value='#{$1}' class='correct'>#{$2}</option>"
    when /^   +(\w+) -> (.+),?$/             # NYC -> New, York City
      str << "<option value='#{$1}'>#{$2}</option>"
    when /^   +\((.+)\)$/                    # (Boston)
      str << "<option value='#{$1}' selected>#{$1}</option>"
    when /^   +\[(.+)\]$/                    # [Boston]
      str << "<option value='#{$1}' class='correct'>#{$1}</option>"
    when /^   +([^\(].+[^\),]),?$/           # Boston
      str << "<option value='#{$1}'>#{$1}</option>"
    end
  end
  str << '</select>'
end

.form_element_text(id, code, length) ⇒ Object



114
115
116
# File 'lib/showoff/compiler/form.rb', line 114

def self.form_element_text(id, code, length)
  "<input type='text' id='#{id}_response' name='#{code}' size='#{length}' />"
end

.form_element_textarea(id, code, rows) ⇒ Object



118
119
120
121
# File 'lib/showoff/compiler/form.rb', line 118

def self.form_element_textarea(id, code, rows)
  rows = 3 if rows.empty?
  "<textarea id='#{id}_response' name='#{code}' rows='#{rows}'></textarea>"
end

.render!(doc, options = {}) ⇒ Nokogiri::HTML::DocumentFragment

TODO:

UI elements to translate once i18n is baked in.

TODO:

Someday this should be rearchitected into the markdown renderer.

Add the form markup to the slide and then render all elements

Returns:

  • (Nokogiri::HTML::DocumentFragment)

    The slide DOM with all form elements rendered.

See Also:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/showoff/compiler/form.rb', line 16

def self.render!(doc, options={})
  title = options[:form]
  return unless title

  begin
    tools = Nokogiri::XML::Node.new('div', doc)
      tools.add_class('tools')
      doc.add_child(tools)

    button = Nokogiri::XML::Node.new('input', doc)
      button.add_class('display')
      button.set_attribute('type', 'button')
      button.set_attribute('value', I18n.t('forms.display'))
      tools.add_child(button)

    submit = Nokogiri::XML::Node.new('input', doc)
      submit.add_class('save')
      submit.set_attribute('type', 'submit')
      submit.set_attribute('value', I18n.t('forms.save'))
      submit.set_attribute('disabled', 'disabled')
      tools.add_child(submit)

    form = Nokogiri::XML::Node.new('form', doc)
      form.set_attribute('id', title)
      form.set_attribute('action', "form/#{title}")
      form.set_attribute('method', 'POST')
      doc.add_child(form)

    doc.children.each do |elem|
      next if elem == form
      elem.parent = form
    end

    doc.css('p').each do |p|
      if p.text =~ /^(\w*) ?(?:->)? ?(.*)? (\*?)= ?(.*)?$/
        code     = $1
        id       = "#{title}_#{code}"
        name     = $2.empty? ? code : $2
        required = ! $3.empty?
        rhs      = $4

        p.replace self.form_element(id, code, name, required, rhs, p.text)
      end
    end

  rescue Exception => e
    Showoff::Logger.warn "Form parsing failed: #{e.message}"
    Showoff::Logger.debug "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
  end

  doc
end