hat-trick 0.0.1 → 0.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.
- 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
|