hat-trick 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +5 -5
- data/app/assets/javascripts/hat-trick.js.coffee +254 -120
- data/app/views/hat_trick/_wizard_meta.html.erb +1 -1
- data/hat-trick.gemspec +3 -3
- data/lib/hat-trick.rb +1 -1
- data/lib/hat_trick/config.rb +17 -0
- data/lib/hat_trick/controller_helpers.rb +64 -0
- data/lib/hat_trick/controller_hooks.rb +20 -64
- data/lib/hat_trick/dsl.rb +86 -62
- data/lib/hat_trick/{rails_engine.rb → engine.rb} +1 -1
- data/lib/hat_trick/form_helper.rb +0 -4
- data/lib/hat_trick/model_methods.rb +30 -8
- data/lib/hat_trick/step_definition.rb +12 -12
- data/lib/hat_trick/version.rb +1 -1
- data/lib/hat_trick/wizard.rb +44 -27
- data/lib/hat_trick/wizard_definition.rb +2 -0
- data/lib/hat_trick/wizard_steps.rb +5 -2
- data/spec/lib/hat_trick/dsl_spec.rb +26 -0
- data/spec/lib/hat_trick/wizard_spec.rb +21 -19
- data/spec/spec_helper.rb +1 -9
- data/vendor/assets/javascripts/jquery.form.wizard.js +499 -438
- data/vendor/assets/javascripts/jquery.history.js +1 -0
- metadata +15 -12
- data/spec/lib/hat_trick/step_spec.rb +0 -9
- data/vendor/assets/javascripts/jquery.ba-bbq.js +0 -1137
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
## Install
|
5
5
|
gem install hat-trick
|
6
6
|
|
7
|
-
### Rails 3.
|
7
|
+
### Rails 3.1+
|
8
8
|
(older versions not supported)
|
9
9
|
|
10
10
|
Put this in your Gemfile:
|
@@ -37,8 +37,8 @@ Jumping to a step based on logged in status:
|
|
37
37
|
|
38
38
|
wizard do
|
39
39
|
step :first_step do
|
40
|
-
#
|
41
|
-
|
40
|
+
# after defines a callback to run after the current step is completed by the user
|
41
|
+
after do
|
42
42
|
# code in this block will be exec'd in the context of your controller
|
43
43
|
if user_signed_in?
|
44
44
|
next_step :third_step
|
@@ -64,8 +64,8 @@ Skipping a step under certain conditions:
|
|
64
64
|
wizard do
|
65
65
|
step :first_step
|
66
66
|
step :second_step do
|
67
|
-
#
|
68
|
-
|
67
|
+
# before defines a callback to run before the user sees this step
|
68
|
+
before do
|
69
69
|
# code in this block will be exec'd in the context of your controller
|
70
70
|
skip_this_step unless foo.present?
|
71
71
|
end
|
@@ -3,21 +3,58 @@
|
|
3
3
|
class HatTrickWizard
|
4
4
|
constructor: (formElem, @wizard) ->
|
5
5
|
@form = $(formElem)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# prevent submitting the step that happens to be the last fieldset
|
12
|
-
this.addFakeLastStep()
|
13
|
-
this.enableFormwizard() # unless this.formwizardEnabled()
|
14
|
-
this.setCurrentStepField()
|
15
|
-
# TODO: Try this out instead of putting :start first
|
16
|
-
# this.showStep(@wizard.currentStep)
|
17
|
-
this.bindEvents()
|
6
|
+
this.stepShownCallback() # because we just showed the first step (or the one in the URL)
|
7
|
+
|
8
|
+
formwizardEnabled: false
|
9
|
+
|
10
|
+
linkClass: "_ht_link"
|
18
11
|
|
19
12
|
buttons: {}
|
20
13
|
|
14
|
+
stepsNeedUpdate: false
|
15
|
+
|
16
|
+
stepShownCallback: ->
|
17
|
+
if not @formwizardEnabled
|
18
|
+
this.addStepClass()
|
19
|
+
# prevent submitting the step that happens to be the last fieldset
|
20
|
+
# TODO: Figure out a better way to do this
|
21
|
+
this.addFakeLastStep()
|
22
|
+
this.addDefaultButtons()
|
23
|
+
this.updateStepFromMetadata()
|
24
|
+
if not @formwizardEnabled
|
25
|
+
this.setupButtonsForAllSteps()
|
26
|
+
this.enableFormwizard() # this clobbers our button customizations for the current step
|
27
|
+
this.setupButtonsForCurrentStep() # so we run this to reconfigure it
|
28
|
+
this.bindEvents()
|
29
|
+
else
|
30
|
+
this.setupButtonsForCurrentStep()
|
31
|
+
# this must be run here & after setupButtonsForCurrentStep() to support setContents()
|
32
|
+
if @stepsNeedUpdate
|
33
|
+
this.updateSteps()
|
34
|
+
@stepsNeedUpdate = false
|
35
|
+
this.setCurrentStepField()
|
36
|
+
this.clearNextStepField()
|
37
|
+
this.setFormFields(hatTrick.model)
|
38
|
+
this.createDummyModelField() unless this.currentStepHasModelFields()
|
39
|
+
|
40
|
+
addStepClass: ->
|
41
|
+
@form.find("fieldset").addClass("step")
|
42
|
+
|
43
|
+
addDefaultButtons: ($scope = @form.find("fieldset")) ->
|
44
|
+
hatTrick = this
|
45
|
+
$scope.each (index) ->
|
46
|
+
id = $(this).attr('id')
|
47
|
+
buttons =
|
48
|
+
next:
|
49
|
+
id: "#{id}_next_button"
|
50
|
+
label: "Next"
|
51
|
+
if index > 0 or hatTrick.formwizardEnabled
|
52
|
+
buttons['back'] =
|
53
|
+
id: "#{id}_back_button"
|
54
|
+
label: "Back"
|
55
|
+
|
56
|
+
hatTrick.buttons[id] = buttons
|
57
|
+
|
21
58
|
findStep: (stepId) ->
|
22
59
|
@form.find("fieldset##{stepId}")
|
23
60
|
|
@@ -26,9 +63,10 @@ class HatTrickWizard
|
|
26
63
|
|
27
64
|
setAction: (url, method) ->
|
28
65
|
methodLower = method.toLowerCase()
|
29
|
-
|
66
|
+
# log "Setting form action to #{methodLower} #{url}"
|
30
67
|
@form.attr("action", url)
|
31
68
|
@form.attr("method", "post")
|
69
|
+
@form.formwizard("option", remoteAjax: this.ajaxEvents())
|
32
70
|
methodField = @form.find('input[name="_method"]')
|
33
71
|
methodField.remove()
|
34
72
|
if methodLower isnt "post"
|
@@ -41,40 +79,50 @@ class HatTrickWizard
|
|
41
79
|
stepId = this.currentStepId()
|
42
80
|
this.findStep(stepId)
|
43
81
|
|
44
|
-
nextStepFieldHTML: """<input type="hidden" name="_ht_next_step" class="_ht_link" value="" />"""
|
45
|
-
|
46
82
|
fieldsets: ->
|
47
83
|
@form.find("fieldset")
|
48
84
|
|
49
|
-
ajaxEvents:
|
85
|
+
ajaxEvents: ->
|
50
86
|
remoteAjax = {}
|
51
87
|
$fieldsets = this.fieldsets()
|
52
|
-
$fieldsets = $fieldsets.filter(":first") if firstStep
|
53
88
|
$fieldsets.each (index, element) =>
|
54
89
|
stepId = $(element).attr("id")
|
55
90
|
remoteAjax[stepId] = this.createAjaxEvent(stepId)
|
56
91
|
remoteAjax
|
57
92
|
|
58
93
|
createAjaxEvent: (step) ->
|
59
|
-
# console.log "Adding AJAX to step #{step}"
|
60
94
|
ajax =
|
61
|
-
url: @form.attr("action")
|
62
|
-
dataType: "json"
|
63
|
-
beforeSubmit: (data) =>
|
64
|
-
|
65
|
-
success: (
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
95
|
+
url: @form.attr("action")
|
96
|
+
dataType: "json"
|
97
|
+
# beforeSubmit: (data) =>
|
98
|
+
# log "Sending these data to the server: #{JSON.stringify data}"
|
99
|
+
success: (serverData) =>
|
100
|
+
this.handleServerData serverData
|
101
|
+
# log "Successful form POST; got #{JSON.stringify(serverData)}"
|
102
|
+
error: (event, status, errorThrown) =>
|
103
|
+
log "Error response: #{event.status} #{status} - #{errorThrown} - #{event.responseText}"
|
104
|
+
try
|
105
|
+
appErrors = eval "(#{event.responseText})"
|
106
|
+
catch err
|
107
|
+
appErrors =
|
108
|
+
model:
|
109
|
+
unknown: [
|
110
|
+
"There was an error communicating with the server. TurboVote staff have been notified."
|
111
|
+
]
|
112
|
+
this.clearErrors()
|
113
|
+
this.addErrorItem value[0] for key, value of appErrors.model
|
74
114
|
ajax
|
75
115
|
|
116
|
+
getErrorListElement: ->
|
117
|
+
this.currentStep().find("ul.hat_trick_errors")
|
118
|
+
|
119
|
+
clearErrors: ->
|
120
|
+
$errorList = this.getErrorListElement()
|
121
|
+
$errorList.hide()
|
122
|
+
$errorList.empty()
|
123
|
+
|
76
124
|
addErrorItem: (message) ->
|
77
|
-
$errorList = this.
|
125
|
+
$errorList = this.getErrorListElement()
|
78
126
|
if $errorList.length > 0
|
79
127
|
$errorList.append("<li>#{message}</li>")
|
80
128
|
$errorList.show()
|
@@ -84,26 +132,30 @@ class HatTrickWizard
|
|
84
132
|
@form.formwizard("option", remoteAjax: this.ajaxEvents())
|
85
133
|
|
86
134
|
goToStepId: (stepId) ->
|
87
|
-
|
88
|
-
this.setHTMeta("next_step", stepId)
|
135
|
+
this.setLinkField(stepId)
|
89
136
|
@form.formwizard("next")
|
90
137
|
|
138
|
+
# TODO: Try linking to the same step rather than cloning it.
|
139
|
+
# I'm becoming more and more convinced that that won't work, however. And this isn't as bad as it used to be.
|
91
140
|
repeatStep: (step) ->
|
92
|
-
$
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@
|
141
|
+
if $("fieldset##{step.name}").length is 0
|
142
|
+
$sourceStep = this.findStep(step.repeatOf.fieldset)
|
143
|
+
$clonedStep = $sourceStep.clone(true, true)
|
144
|
+
$clonedStep.css("display", "none")
|
145
|
+
$clonedStep.attr("id", step.name)
|
146
|
+
$sourceStep.after($clonedStep)
|
147
|
+
this.updateSteps()
|
148
|
+
this.setLinkField step.name
|
149
|
+
|
150
|
+
removeLinkField: ->
|
151
|
+
this.currentStep().find("input.#{@linkClass}").remove()
|
152
|
+
|
153
|
+
setLinkField: (stepId) ->
|
154
|
+
inputId = "_ht_link_to_#{stepId}"
|
155
|
+
this.setHiddenInput "_ht_step_link", stepId, inputId, @linkClass, this.currentStep()
|
156
|
+
|
157
|
+
LinkFieldSet: ->
|
158
|
+
this.currentStep().find("input[name='_ht_step_link']").length > 0
|
107
159
|
|
108
160
|
addFakeLastStep: ->
|
109
161
|
@form.append """<fieldset id="_ht_fake_last_step" style="display: none;" class="step"></fieldset>"""
|
@@ -111,27 +163,28 @@ class HatTrickWizard
|
|
111
163
|
enableFormwizard: ->
|
112
164
|
@form.formwizard
|
113
165
|
formPluginEnabled: true,
|
114
|
-
validationEnabled:
|
166
|
+
validationEnabled: false,
|
115
167
|
focusFirstInput: true,
|
116
168
|
historyEnabled: true,
|
117
169
|
disableUIStyles: true,
|
118
170
|
inDuration: 0,
|
119
|
-
linkClass: "
|
120
|
-
remoteAjax: this.ajaxEvents(
|
121
|
-
|
122
|
-
|
123
|
-
console.log "Successful form POST"
|
124
|
-
beforeSubmit: (data) =>
|
125
|
-
console.log "Sending these data to the server: #{$.param(data)}"
|
171
|
+
linkClass: ".#{@linkClass}",
|
172
|
+
remoteAjax: this.ajaxEvents(),
|
173
|
+
firstStep: hatTrick.metadata.currentStep.name
|
174
|
+
this.formwizardEnabled = true
|
126
175
|
|
127
|
-
|
128
|
-
|
176
|
+
setHiddenInput: (name, value, id, classes = "", scope = @form) ->
|
177
|
+
$scope = $(scope)
|
178
|
+
$input = $scope.find("""input##{id}[name="#{name}"]""")
|
179
|
+
if $input.length is 0
|
180
|
+
$input = $(this.hiddenInputHTML(name, id, classes)).prependTo $scope
|
181
|
+
$input.val value
|
182
|
+
|
183
|
+
hiddenInputHTML: (name, id, classes = "") ->
|
184
|
+
"""<input type="hidden" id="#{id}" name="#{name}" class="#{classes}" value="" />"""
|
129
185
|
|
130
186
|
setHTMeta: (key, value) ->
|
131
|
-
|
132
|
-
if $meta.length is 0
|
133
|
-
$meta = @form.prepend(this.htMetaHTML(key)).find("#_ht_#{key}")
|
134
|
-
$meta.val(value)
|
187
|
+
this.setHiddenInput "_ht_meta[#{key}]", value, "_ht_#{key}"
|
135
188
|
|
136
189
|
clearHTMeta: (key) ->
|
137
190
|
@form.find("input:hidden#_ht_#{key}").remove()
|
@@ -139,95 +192,176 @@ class HatTrickWizard
|
|
139
192
|
setCurrentStepField: ->
|
140
193
|
stepId = this.currentStepId()
|
141
194
|
this.setHTMeta("step", stepId)
|
142
|
-
console.log "Current form step: #{stepId}"
|
143
195
|
|
196
|
+
# TODO: See if we still need this "next_step" ht_meta field.
|
144
197
|
clearNextStepField: ->
|
145
198
|
this.clearHTMeta("next_step")
|
199
|
+
this.removeLinkField()
|
146
200
|
|
147
201
|
fieldRegex: /^([^\[]+)\[([^\]]+)\]$/
|
148
202
|
|
149
|
-
setFieldValues: (
|
203
|
+
setFieldValues: (model, selector, callback) ->
|
150
204
|
$currentStep = this.currentStep()
|
151
205
|
$currentStep.find(selector).each (index, element) =>
|
152
206
|
$element = $(element)
|
153
207
|
elementName = $element.attr("name")
|
154
208
|
if elementName? and elementName.search(@fieldRegex) isnt -1
|
155
209
|
[_, modelName, fieldName] = elementName.match(@fieldRegex)
|
156
|
-
if
|
157
|
-
fieldValue =
|
210
|
+
if model['__name__'] is modelName and model[fieldName]?
|
211
|
+
fieldValue = model[fieldName]
|
158
212
|
callback($element, fieldValue) if fieldValue?
|
159
213
|
|
160
|
-
fillTextFields: (
|
161
|
-
this.setFieldValues
|
214
|
+
fillTextFields: (model) ->
|
215
|
+
this.setFieldValues model, "input:text", ($input, value) =>
|
162
216
|
$input.val(value)
|
163
217
|
|
164
|
-
setSelectFields: (
|
165
|
-
this.setFieldValues
|
218
|
+
setSelectFields: (model) ->
|
219
|
+
this.setFieldValues model, "select", ($select, value) =>
|
166
220
|
$select.find("option[value=\"#{value}\"]").attr("selected", "selected")
|
167
221
|
|
168
|
-
setCheckboxes: (
|
169
|
-
this.setFieldValues
|
222
|
+
setCheckboxes: (model) ->
|
223
|
+
this.setFieldValues model, "input:checkbox", ($checkbox, value) =>
|
170
224
|
$checkbox.attr("checked", "checked") if value
|
171
225
|
|
172
|
-
setRadioButtons: (
|
173
|
-
this.setFieldValues
|
226
|
+
setRadioButtons: (model) ->
|
227
|
+
this.setFieldValues model, "input:radio", ($radio, value) =>
|
174
228
|
$radio.find("[value=\"#{value}\"]").attr("checked", "checked")
|
175
229
|
|
176
|
-
setFormFields: (
|
177
|
-
|
178
|
-
this.
|
179
|
-
this.
|
180
|
-
this.
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
230
|
+
setFormFields: (model) ->
|
231
|
+
# log "Setting form fields based on: #{JSON.stringify(model)}"
|
232
|
+
this.fillTextFields(model)
|
233
|
+
this.setSelectFields(model)
|
234
|
+
this.setCheckboxes(model)
|
235
|
+
this.setRadioButtons(model)
|
236
|
+
|
237
|
+
createButton: (name, button) ->
|
238
|
+
$button = $("""<input type="button" class="wizard_button" name="#{name}" value="#{button.label}" />""")
|
239
|
+
if button.id?
|
240
|
+
$button.attr("id", button.id)
|
241
|
+
else
|
242
|
+
$button.attr("id", "#{this.currentStepId()}_#{name}_#{button.label}")
|
243
|
+
$button.click =>
|
244
|
+
clickCallbackData =
|
245
|
+
currentStep: this.currentStepId()
|
246
|
+
button: $button.attr "id"
|
247
|
+
@form.trigger "other_button_click", clickCallbackData
|
248
|
+
$button
|
249
|
+
|
250
|
+
setButton: (stepId, name, button) ->
|
251
|
+
# log "Setting button for #{stepId} named #{name} to #{JSON.stringify(button)}"
|
252
|
+
$buttonsDiv = $("fieldset##{stepId}").find("div.buttons")
|
187
253
|
switch name
|
188
254
|
when "next"
|
189
|
-
|
190
|
-
$buttonsDiv.find('input:submit')
|
255
|
+
# log "Setting #{stepId} submit button val to #{button.label}"
|
256
|
+
$button = $buttonsDiv.find('input:submit')
|
257
|
+
unless $button.length > 0
|
258
|
+
$button = $('<input class="wizard_button wizard_next" type="submit" />').appendTo $buttonsDiv
|
259
|
+
$button.val(button.label)
|
260
|
+
$button.attr("id", button.id) if button.id?
|
191
261
|
when "back"
|
192
|
-
|
193
|
-
$buttonsDiv.find('input:reset').val(label)
|
262
|
+
# log "Setting reset button val to #{button.label}"
|
263
|
+
$button = $buttonsDiv.find('input:reset').val(button.label)
|
264
|
+
unless $button.length > 0
|
265
|
+
$button = $('<input class="wizard_button wizard_back" type="reset" />').appendTo $buttonsDiv
|
266
|
+
$button.val(button.label)
|
267
|
+
$button.attr("id", button.id) if button.id?
|
194
268
|
else
|
195
|
-
buttonSelector = """input:button[name="#{name}"][value="#{label}"]"""
|
269
|
+
buttonSelector = """input:button[name="#{name}"][value="#{button.label}"]"""
|
196
270
|
$existingButtons = $buttonsDiv.find(buttonSelector)
|
197
|
-
if $existingButtons.length
|
198
|
-
|
199
|
-
$newButton = $
|
271
|
+
if $existingButtons.length is 0
|
272
|
+
# log "Adding new #{name}:#{button.label} button"
|
273
|
+
$newButton = $(this.createButton(name, button)).appendTo($buttonsDiv)
|
200
274
|
$newButton.click (event) =>
|
201
275
|
event.preventDefault()
|
202
276
|
this.goToStepId(name)
|
203
277
|
|
278
|
+
setupButtonsForAllSteps: ->
|
279
|
+
this.setupButtonsForStep $(fieldset).attr('id') for fieldset in $('fieldset')
|
280
|
+
|
281
|
+
setupButtonsForCurrentStep: ->
|
282
|
+
this.setupButtonsForStep this.currentStepId()
|
283
|
+
|
284
|
+
setupButtonsForStep: (stepId) ->
|
285
|
+
buttons = this.buttons[stepId]
|
286
|
+
if buttons?
|
287
|
+
this.setButton(stepId, name, button) for name, button of buttons
|
288
|
+
|
289
|
+
setContents: (stepPartials) ->
|
290
|
+
for stepName, partial of stepPartials
|
291
|
+
do (stepName, partial) =>
|
292
|
+
stepId = underscoreString stepName
|
293
|
+
$partial = $(partial)
|
294
|
+
fieldsetContents = $partial.find('fieldset').html()
|
295
|
+
$step = $("fieldset##{stepId}")
|
296
|
+
$step.html fieldsetContents
|
297
|
+
this.addDefaultButtons($step)
|
298
|
+
@stepsNeedUpdate = true
|
299
|
+
|
300
|
+
handleServerData: (data) ->
|
301
|
+
if data.metadata?
|
302
|
+
this.setAction(data.metadata.url, data.metadata.method)
|
303
|
+
$.extend(window.hatTrick, data) # merge new data with hatTrick
|
304
|
+
this.updateStepFromMetadata()
|
305
|
+
|
306
|
+
updateStepContents: ->
|
307
|
+
this.setContents(hatTrick.data.hatTrickStepContents)
|
308
|
+
|
309
|
+
setButtonMetadataForCurrentStep: ->
|
310
|
+
if hatTrick.metadata?.currentStep?
|
311
|
+
currentStep = hatTrick.metadata.currentStep
|
312
|
+
if currentStep.buttons?
|
313
|
+
stepId = currentStep.fieldset
|
314
|
+
this.buttons[stepId] = currentStep.buttons
|
315
|
+
|
316
|
+
modelName: ->
|
317
|
+
hatTrick.model['__name__']
|
318
|
+
|
319
|
+
createDummyModelField: ->
|
320
|
+
this.setHiddenInput "#{this.modelName()}[_dummy]", "1"
|
321
|
+
|
322
|
+
currentStepHasModelFields: ->
|
323
|
+
this.currentStep().find("input[name^='#{this.modelName()}[']").length > 0
|
324
|
+
|
325
|
+
updateStepFromMetadata: ->
|
326
|
+
if hatTrick.metadata?.currentStep?
|
327
|
+
this.updateStepContents() if hatTrick.data.hatTrickStepContents?
|
328
|
+
this.setButtonMetadataForCurrentStep()
|
329
|
+
this.createDummyModelField() unless this.currentStepHasModelFields()
|
330
|
+
|
331
|
+
currentStep = hatTrick.metadata.currentStep
|
332
|
+
if currentStep.repeatOf?
|
333
|
+
this.repeatStep(currentStep)
|
334
|
+
else
|
335
|
+
this.setLinkField(currentStep.fieldset) unless this.LinkFieldSet()
|
336
|
+
|
204
337
|
bindEvents: ->
|
205
338
|
@form.bind "step_shown", (event, data) =>
|
206
|
-
this.
|
207
|
-
this.clearNextStepField()
|
208
|
-
this.setFormFields(htData.formModel)
|
209
|
-
|
210
|
-
buttons = this.buttons[this.currentStepId()]
|
211
|
-
if buttons?
|
212
|
-
this.setButton(name, label) for own name, label of buttons
|
213
|
-
|
214
|
-
if data.previousStep is data.firstStep
|
215
|
-
console.log "Adding additional Ajax events"
|
216
|
-
# adds additional Ajax events now that we have the update URL
|
217
|
-
@form.formwizard("option", remoteAjax: this.ajaxEvents())
|
218
|
-
|
219
|
-
@form.bind "after_remote_ajax", (event, data) =>
|
220
|
-
if htData.wizardMetadata?.currentStep.buttons?
|
221
|
-
stepId = htData.wizardMetadata.currentStep.fieldset
|
222
|
-
this.buttons[stepId] = htData.wizardMetadata.currentStep.buttons
|
223
|
-
|
224
|
-
if htData.wizardMetadata?.currentStep.repeatOf?
|
225
|
-
this.repeatStep(htData.wizardMetadata.currentStep)
|
226
|
-
else
|
227
|
-
this.showStep(htData.wizardMetadata.currentStep)
|
339
|
+
this.stepShownCallback()
|
228
340
|
|
229
341
|
$ ->
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
window.
|
342
|
+
if $("form.wizard").length > 0
|
343
|
+
$form = $("form.wizard")
|
344
|
+
window.hatTrick = {} unless window.hatTrick?
|
345
|
+
unless window.hatTrick.wizard?
|
346
|
+
# log "Creating new HatTrickWizard instance"
|
347
|
+
window.hatTrick.wizard = new HatTrickWizard($form, hatTrick.metadata)
|
348
|
+
|
349
|
+
camelizeString = (string) ->
|
350
|
+
re = /_([^_]*)/g
|
351
|
+
while matches = re.exec(string)
|
352
|
+
result = string.slice(0, matches.index) unless result?
|
353
|
+
result += "#{matches[1][0].toUpperCase()}#{matches[1].slice(1)}"
|
354
|
+
result = string unless result?
|
355
|
+
result
|
356
|
+
|
357
|
+
underscoreString = (string) ->
|
358
|
+
re = /([A-Z]+)([a-z\d]+)/g
|
359
|
+
while matches = re.exec(string)
|
360
|
+
result = string.slice(0, matches.index) unless result?
|
361
|
+
result += "_#{matches[1].toLowerCase()}#{matches[2]}"
|
362
|
+
result = string unless result?
|
363
|
+
result
|
364
|
+
|
365
|
+
log = (msg) ->
|
366
|
+
if window['console']?
|
367
|
+
console.log msg
|
@@ -1 +1 @@
|
|
1
|
-
<%= include_gon(:namespace => '
|
1
|
+
<%= include_gon(:namespace => 'hatTrick', :camel_case => true) %>
|
data/hat-trick.gemspec
CHANGED
@@ -7,9 +7,9 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = HatTrick::VERSION
|
8
8
|
s.authors = ["Wes Morgan"]
|
9
9
|
s.email = ["cap10morgan@gmail.com"]
|
10
|
-
s.homepage = ""
|
11
|
-
s.summary = %q{
|
12
|
-
s.description = %q{
|
10
|
+
s.homepage = "https://github.com/turbovote/hat-trick"
|
11
|
+
s.summary = %q{A simple DSL for creating client-side multi-step forms in Rails.}
|
12
|
+
s.description = %q{Hat-Trick brings together jQuery, validation_group, and Ajax to make multi-step forms awesome. It tries to insulate you from this complexity by providing a simple yet powerful DSL for defining the steps of your form and their behavior.}
|
13
13
|
|
14
14
|
s.rubyforge_project = "hat-trick"
|
15
15
|
|
data/lib/hat-trick.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
module HatTrick
|
2
|
+
class Config
|
3
|
+
attr_reader :wizard_def
|
4
|
+
|
5
|
+
def initialize(wizard_def)
|
6
|
+
@wizard_def = wizard_def
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_url=(url)
|
10
|
+
wizard_def.configured_create_url = url
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_url=(url)
|
14
|
+
wizard_def.configured_update_url = url
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module HatTrick
|
2
|
+
module ControllerHelpers
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def model_key
|
7
|
+
params.each do |k,v|
|
8
|
+
return k if v.is_a?(Hash) && is_model?(k)
|
9
|
+
end
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def params_model_name
|
14
|
+
class_name model_key unless model_key.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def is_model?(model_name)
|
18
|
+
begin
|
19
|
+
class_name(model_name).constantize
|
20
|
+
rescue NameError
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def class_name(hash_key)
|
27
|
+
hash_key.to_s.camelize
|
28
|
+
end
|
29
|
+
|
30
|
+
def model_class
|
31
|
+
return ht_wizard.model.class if ht_wizard.model
|
32
|
+
|
33
|
+
# if that didn't work, try to grab it from the params hash
|
34
|
+
model_name = params_model_name
|
35
|
+
return nil if model_name.nil?
|
36
|
+
|
37
|
+
begin
|
38
|
+
model_klass = params_model_name.constantize
|
39
|
+
rescue NameError
|
40
|
+
Rails.logger.error "Could not find model class #{params_model_name.camelize}"
|
41
|
+
nil
|
42
|
+
else
|
43
|
+
model_klass
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup_validation_group_for(wizard_step)
|
48
|
+
klass = model_class
|
49
|
+
return if klass.nil?
|
50
|
+
step_name = wizard_step.name
|
51
|
+
validation_groups = ::ActiveRecord::Base.validation_group_classes[klass] || []
|
52
|
+
unless validation_groups.include?(step_name)
|
53
|
+
validation_fields = params.keys # TODO: Try it without these (so only the model keys below)
|
54
|
+
model = model_key
|
55
|
+
if model
|
56
|
+
validation_fields += params[model].keys
|
57
|
+
end
|
58
|
+
validation_fields = validation_fields.map(&:to_sym)
|
59
|
+
klass.validation_group(step_name, :fields => validation_fields)
|
60
|
+
end
|
61
|
+
HatTrick::ModelMethods.set_current_validation_group_for(model_class, step_name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|