hat-trick 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ .tags*
data/Changelog CHANGED
@@ -1,5 +1,19 @@
1
- * 0.1.2 - Fixes GH issue #4 - Don't run skipped steps' include_data callbacks
1
+ * 0.1.3 - The "production" release. TurboVote.org runs on this version.
2
+ - Handle non-root URLs correctly in pushState (Closes GH issue #5)
3
+ - Don't loop past the last step when looking for the next one (Closes GH issue #6)
4
+ - Only run the include_data callback for the current step (Closes GH issue #8)
5
+ - Trigger ajaxErrors event on form element when we get errors back from an ajax call
6
+ - Trigger ajaxSuccess event on form element when ajax calls return successfully
7
+ - Don't add slashes to wizard step URLs
8
+ - Fix buggy radio button selection logic
9
+ - Use <button> elements for all buttons & improve button creation logic
10
+ - Allow HTML inside <button> elements; use button_to label: "[your html]"
11
+ - Don't focus the first input in fieldsets with the "no-focus" CSS class
12
+ - Allow multiple next buttons on each step so they can have different labels, names, values
13
+ - SO many other changes that I stopped listing them here. Oops.
2
14
 
3
- * 0.1.1 - Added LICENSE file to license hat-trick under the MIT license.
15
+ * 0.1.2 - Closes GH issue #4 - Don't run skipped steps' include_data callbacks
4
16
 
5
- * 0.1 - Initial working release
17
+ * 0.1.1 - Added LICENSE file to license hat-trick under the MIT license.
18
+
19
+ * 0.1 - Initial working release
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Hat Trick
2
- > Combines jQuery FormWizard, validation_group, and gon for the perfect triple-play of Rails wizarding.
2
+
3
+ > Multi-step "wizard" forms in Rails using jQuery. Keeps your controller CRUD
4
+ > methods clean with a wizard DSL.
3
5
 
4
6
  ## Install
5
7
  gem install hat-trick
@@ -10,7 +12,7 @@
10
12
  Put this in your Gemfile:
11
13
 
12
14
  gem 'hat-trick'
13
-
15
+
14
16
  ## Setup
15
17
  In your controller:
16
18
 
@@ -20,6 +22,9 @@ In your controller:
20
22
  step :third_step
21
23
  end
22
24
 
25
+ *** Make sure your controller's CRUD methods know how to return JSON responses
26
+ containing the model instance you're building in the wizard. ***
27
+
23
28
  In your view:
24
29
 
25
30
  <%= wizard_form_for @model do |f| %>
@@ -27,9 +32,11 @@ In your view:
27
32
  <fieldset id="second_step">...</fieldset>
28
33
  <fieldset id="third_step">...</fieldset>
29
34
  <% end %>
30
-
35
+
31
36
  The id's of the fieldsets in your form should match the step names you define in your controller.
32
-
37
+
38
+ Each fieldset will be displayed as a step with Next and Back buttons.
39
+
33
40
  ## Controlling the wizard flow
34
41
  Sometimes you need to specify different paths through a wizard based on certain conditions. The way you do that with hat-trick is in the wizard DSL in the controller. Here are some examples:
35
42
 
@@ -40,25 +47,16 @@ Jumping to a step based on logged in status:
40
47
  # after defines a callback to run after the current step is completed by the user
41
48
  after do
42
49
  # code in this block will be exec'd in the context of your controller
43
- if user_signed_in?
50
+ if current_user?
44
51
  next_step :third_step
45
52
  end
46
53
  end
47
54
  end
48
-
55
+
49
56
  step :second_step # wizard will go here after :first_step if user is not signed in
50
-
57
+
51
58
  step :third_step # wizard will go here after :first_step if user is signed in
52
-
53
- Repeating a previous step (for example, to show address sanitization results to the user):
54
-
55
- wizard do
56
- step :enter_address
57
-
58
- step :confirm_santized_address do
59
- repeat_step :enter_address
60
- end
61
-
59
+
62
60
  Skipping a step under certain conditions:
63
61
 
64
62
  wizard do
@@ -67,7 +65,34 @@ Skipping a step under certain conditions:
67
65
  # before defines a callback to run before the user sees this step
68
66
  before do
69
67
  # code in this block will be exec'd in the context of your controller
70
- skip_this_step unless foo.present?
68
+ skip_this_step unless model.foo.present?
71
69
  end
72
70
  end
73
- end
71
+ end
72
+
73
+ Using the model instance in before and after callbacks:
74
+
75
+ wizard do
76
+ step :first_step do
77
+ before do |model_instance|
78
+ if model_instance.attr.present?
79
+ next_step :third_step
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ Customizing the button labels:
86
+
87
+ wizard do
88
+ button_label :next, "Onward!"
89
+ button_label :back, "Engines reverse full"
90
+ end
91
+
92
+ Adding a custom button to a step:
93
+
94
+ wizard do
95
+ step :first_step do
96
+ button_to :next, name: "model[button_name]", label: "Foobar"
97
+ end
98
+ end
@@ -3,58 +3,47 @@
3
3
  class HatTrickWizard
4
4
  constructor: (formElem, @wizard) ->
5
5
  @form = $(formElem)
6
- this.stepShownCallback() # because we just showed the first step (or the one in the URL)
6
+ this.enableFormwizard()
7
7
 
8
- formwizardEnabled: false
8
+ debug: true
9
9
 
10
10
  linkClass: "_ht_link"
11
11
 
12
- buttons: {}
12
+ buttons: []
13
13
 
14
14
  stepsNeedUpdate: false
15
15
 
16
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()
17
+ currentStepId = this.currentStepId()
18
+
19
+ @lastButtonChanged = null
20
+
21
+ if hatTrick.stepMetadata[currentStepId]?
22
+ hatTrick.metadata.currentStep = hatTrick.stepMetadata[currentStepId]
23
+ else
24
+ this.requestMetadataFromServer()
25
+ return
26
+
23
27
  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()
28
+ currentStepId = this.currentStepId()
29
+ # can't go back from the first step
30
+ if hatTrick.metadata.currentStep.first
31
+ this.buttons[currentStepId] = this.buttons[currentStepId].filter (button) ->
32
+ not button.back?
33
+ this.setupButtonsForCurrentStep()
34
+ if @stepsNeedUpdate
35
+ this.updateSteps()
36
+ @stepsNeedUpdate = false
29
37
  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()
38
+ this.updateButtons()
39
+ this.removeLinkFields()
37
40
  this.setFormFields(hatTrick.model)
38
41
  this.createDummyModelField() unless this.currentStepHasModelFields()
42
+ @form.trigger 'step_changed', { currentStep: this.currentStepId() }
39
43
 
40
44
  addStepClass: ->
41
45
  @form.find("fieldset").addClass("step")
42
46
 
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
-
58
47
  findStep: (stepId) ->
59
48
  @form.find("fieldset##{stepId}")
60
49
 
@@ -63,7 +52,6 @@ class HatTrickWizard
63
52
 
64
53
  setAction: (url, method) ->
65
54
  methodLower = method.toLowerCase()
66
- # log "Setting form action to #{methodLower} #{url}"
67
55
  @form.attr("action", url)
68
56
  @form.attr("method", "post")
69
57
  @form.formwizard("option", remoteAjax: this.ajaxEvents())
@@ -73,7 +61,10 @@ class HatTrickWizard
73
61
  @form.prepend(this.createMethodField(method))
74
62
 
75
63
  currentStepId: ->
76
- @form.formwizard("state").currentStep
64
+ stepId = @form.formwizard("state").currentStep
65
+ unless stepId? and stepId isnt ""
66
+ stepId = hatTrick.metadata.currentStep.name
67
+ stepId
77
68
 
78
69
  currentStep: ->
79
70
  stepId = this.currentStepId()
@@ -94,10 +85,16 @@ class HatTrickWizard
94
85
  ajax =
95
86
  url: @form.attr("action")
96
87
  dataType: "json"
88
+ beforeSerialize: (form, options) =>
89
+ if options.data._ht_step_link == this.currentStepId()
90
+ log "Warning: Tried to link to the current step; this is probably not what you want."
91
+ return true;
97
92
  # beforeSubmit: (data) =>
98
93
  # log "Sending these data to the server: #{JSON.stringify data}"
99
94
  success: (serverData) =>
95
+ this.clearErrors()
100
96
  this.handleServerData serverData
97
+ @form.trigger 'ajaxSuccess', serverData
101
98
  # log "Successful form POST; got #{JSON.stringify(serverData)}"
102
99
  error: (event, status, errorThrown) =>
103
100
  log "Error response: #{event.status} #{status} - #{errorThrown} - #{event.responseText}"
@@ -109,8 +106,12 @@ class HatTrickWizard
109
106
  unknown: [
110
107
  "There was an error communicating with the server. TurboVote staff have been notified."
111
108
  ]
109
+ status: status
110
+ event: event
112
111
  this.clearErrors()
113
- this.addErrorItem value[0] for key, value of appErrors.model
112
+ this.addErrorItem value[0] for key, value of appErrors.model when key isnt "__name__"
113
+ this.removeLinkFields()
114
+ @form.trigger 'ajaxErrors', appErrors
114
115
  ajax
115
116
 
116
117
  getErrorListElement: ->
@@ -127,6 +128,9 @@ class HatTrickWizard
127
128
  $errorList.append("<li>#{message}</li>")
128
129
  $errorList.show()
129
130
 
131
+ updateButtons: ->
132
+ @form.formwizard("update_buttons")
133
+
130
134
  updateSteps: ->
131
135
  @form.formwizard("update_steps")
132
136
  @form.formwizard("option", remoteAjax: this.ajaxEvents())
@@ -135,50 +139,57 @@ class HatTrickWizard
135
139
  this.setLinkField(stepId)
136
140
  @form.formwizard("next")
137
141
 
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.
140
- repeatStep: (step) ->
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()
142
+ removeLinkFields: ->
143
+ @form.find("input.#{@linkClass}").remove()
152
144
 
153
145
  setLinkField: (stepId) ->
154
146
  inputId = "_ht_link_to_#{stepId}"
155
147
  this.setHiddenInput "_ht_step_link", stepId, inputId, @linkClass, this.currentStep()
156
148
 
157
- LinkFieldSet: ->
149
+ linkFieldSet: ->
158
150
  this.currentStep().find("input[name='_ht_step_link']").length > 0
159
151
 
160
152
  addFakeLastStep: ->
161
153
  @form.append """<fieldset id="_ht_fake_last_step" style="display: none;" class="step"></fieldset>"""
162
154
 
163
155
  enableFormwizard: ->
156
+ this.addStepClass()
157
+ this.saveStepMetadata()
158
+ this.setAction(hatTrick.metadata.url, hatTrick.metadata.method)
159
+ # prevent submitting the step that happens to be the last fieldset
160
+ # TODO: Figure out a better way to do this
161
+ this.addFakeLastStep()
162
+ this.bindEvents()
163
+ firstStep = if hatTrick.metadata.currentStep?.redirect
164
+ hatTrick.metadata.currentStep.redirectFrom
165
+ else
166
+ hatTrick.metadata.currentStep.fieldset
164
167
  @form.formwizard
165
168
  formPluginEnabled: true,
166
169
  validationEnabled: false,
167
170
  focusFirstInput: true,
168
- historyEnabled: true,
169
171
  disableUIStyles: true,
170
- inDuration: 0,
172
+ outDuration: 200,
173
+ inDuration: 10, # don't set this to 0 or step_shown will be triggered too early
174
+ next: "button:submit",
175
+ back: "button:reset",
171
176
  linkClass: ".#{@linkClass}",
172
177
  remoteAjax: this.ajaxEvents(),
173
- firstStep: hatTrick.metadata.currentStep.name
174
- this.formwizardEnabled = true
178
+ firstStep: firstStep
179
+ # see if we got a redirect & follow if so
180
+ currentStepId = this.currentStepId()
181
+ if hatTrick.metadata.currentStep?
182
+ currentStepData = hatTrick.metadata.currentStep
183
+ if currentStepData.redirect and currentStepData.redirectFrom is currentStepId
184
+ @form.formwizard("redirect", currentStepData.fieldset)
175
185
 
176
186
  setHiddenInput: (name, value, id, classes = "", scope = @form) ->
177
187
  $scope = $(scope)
178
- $input = $scope.find("""input##{id}[name="#{name}"]""")
188
+ $input = $scope.find("""input[name="#{name}"]""")
179
189
  if $input.length is 0
180
190
  $input = $(this.hiddenInputHTML(name, id, classes)).prependTo $scope
181
191
  $input.val value
192
+ $input
182
193
 
183
194
  hiddenInputHTML: (name, id, classes = "") ->
184
195
  """<input type="hidden" id="#{id}" name="#{name}" class="#{classes}" value="" />"""
@@ -193,11 +204,6 @@ class HatTrickWizard
193
204
  stepId = this.currentStepId()
194
205
  this.setHTMeta("step", stepId)
195
206
 
196
- # TODO: See if we still need this "next_step" ht_meta field.
197
- clearNextStepField: ->
198
- this.clearHTMeta("next_step")
199
- this.removeLinkField()
200
-
201
207
  fieldRegex: /^([^\[]+)\[([^\]]+)\]$/
202
208
 
203
209
  setFieldValues: (model, selector, callback) ->
@@ -223,88 +229,173 @@ class HatTrickWizard
223
229
  this.setFieldValues model, "input:checkbox", ($checkbox, value) =>
224
230
  $checkbox.attr("checked", "checked") if value
225
231
 
232
+ # TODO: DRY this up as much as possible. Radio buttons a little different
233
+ # than the other form controls since they share names and behave as a
234
+ # named group.
226
235
  setRadioButtons: (model) ->
227
- this.setFieldValues model, "input:radio", ($radio, value) =>
228
- $radio.find("[value=\"#{value}\"]").attr("checked", "checked")
236
+ $currentStep = this.currentStep()
237
+ selector = "input:radio"
238
+ radioGroups = {}
239
+ $currentStep.find(selector).each ->
240
+ radioGroups[$(this).attr("name")] = true
241
+ for radioGroup of radioGroups
242
+ do (radioGroup) =>
243
+ if radioGroup.search(@fieldRegex) isnt -1
244
+ [_, modelName, fieldName] = radioGroup.match(@fieldRegex)
245
+ if model['__name__'] is modelName and model[fieldName]?
246
+ fieldValue = model[fieldName]
247
+ $radioGroup = $("input:radio[name=\"#{radioGroup}\"]")
248
+ $radioGroup.removeAttr("checked")
249
+ $radioGroup.filter("[value=\"#{fieldValue}\"]").attr("checked", "checked")
229
250
 
230
251
  setFormFields: (model) ->
231
- # log "Setting form fields based on: #{JSON.stringify(model)}"
232
252
  this.fillTextFields(model)
233
253
  this.setSelectFields(model)
234
254
  this.setCheckboxes(model)
235
255
  this.setRadioButtons(model)
236
256
 
237
- createButton: (name, button) ->
238
- $button = $("""<input type="button" class="wizard_button" name="#{name}" value="#{button.label}" />""")
257
+ createButtonElement: (name, value, label, type="button") ->
258
+ $elem = $("""<button type="#{type}" class="wizard_button"></button>""")
259
+ $elem.attr "name", name if name?
260
+ $elem.html label
261
+ $elem.val value if value?
262
+ $elem
263
+
264
+ createButton: (toStep, button) ->
265
+ switch toStep
266
+ when "next"
267
+ if button.class is ""
268
+ button.class = "wizard_next"
269
+ else
270
+ button["class"] += " wizard_next"
271
+ type = "submit"
272
+ when "back"
273
+ if button.class is ""
274
+ button.class = "wizard_back"
275
+ else
276
+ button["class"] += " wizard_back"
277
+ button.name = "back"
278
+ unless button.id?
279
+ button.id = "#{this.currentStepId()}_back_button"
280
+ delete button["value"]
281
+ type = "reset"
282
+ else
283
+ type = "button"
284
+ $button = this.createButtonElement button.name, button.value, button.label, type
239
285
  if button.id?
240
286
  $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
287
+ else if button.name? and button.value?
288
+ $button.attr("id", "#{this.currentStepId()}_#{button.name}_#{button.value}")
289
+ if button["class"]?
290
+ $button.addClass(button["class"])
248
291
  $button
249
292
 
250
- setButton: (stepId, name, button) ->
251
- # log "Setting button for #{stepId} named #{name} to #{JSON.stringify(button)}"
293
+ setButton: (stepId, toStep, button) ->
252
294
  $buttonsDiv = $("fieldset##{stepId}").find("div.buttons")
253
- switch name
254
- when "next"
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?
261
- when "back"
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?
295
+ if $buttonsDiv.find("button").length > 0
296
+ @lastButtonChanged ?= $buttonsDiv.find("button:first")
297
+ buttonSelector = """button[name="#{button.name}"][value="#{button.value}"]"""
298
+ $existingButtons = $buttonsDiv.find(buttonSelector)
299
+ if $existingButtons.length is 0
300
+ $newButton = $(this.createButton(toStep, button))
301
+ if @lastButtonChanged?
302
+ @lastButtonChanged.after $newButton
268
303
  else
269
- buttonSelector = """input:button[name="#{name}"][value="#{button.label}"]"""
270
- $existingButtons = $buttonsDiv.find(buttonSelector)
271
- if $existingButtons.length is 0
272
- # log "Adding new #{name}:#{button.label} button"
273
- $newButton = $(this.createButton(name, button)).appendTo($buttonsDiv)
274
- $newButton.click (event) =>
275
- event.preventDefault()
276
- this.goToStepId(name)
277
-
278
- setupButtonsForAllSteps: ->
279
- this.setupButtonsForStep $(fieldset).attr('id') for fieldset in $('fieldset')
304
+ $buttonsDiv.append $newButton
305
+ @lastButtonChanged = $newButton
306
+ unless toStep is "next" or toStep is "back"
307
+ $newButton.click (event) =>
308
+ event.preventDefault()
309
+ fieldId = "button_#{$newButton.attr("name")}_#{$newButton.val()}_field"
310
+ this.setHiddenInput $newButton.attr("name"), $newButton.val(), fieldId, "", $buttonsDiv
311
+ this.goToStepId(toStep)
280
312
 
281
313
  setupButtonsForCurrentStep: ->
282
- this.setupButtonsForStep this.currentStepId()
314
+ $currentStep = this.currentStep()
315
+ $buttonsDiv = $currentStep.find("div.buttons")
316
+ $buttons = $buttonsDiv.find("button")
317
+ unless $buttonsDiv.data "buttonsAdded"
318
+ this.setupButtonsForStep this.currentStepId()
283
319
 
284
320
  setupButtonsForStep: (stepId) ->
285
321
  buttons = this.buttons[stepId]
286
322
  if buttons?
287
- this.setButton(stepId, name, button) for name, button of buttons
323
+ for button in buttons
324
+ do (button) =>
325
+ this.setButton(stepId, toStep, buttonData) for toStep, buttonData of button
326
+ $("##{stepId} div.buttons").data "buttonsAdded", true
288
327
 
289
328
  setContents: (stepPartials) ->
290
329
  for stepName, partial of stepPartials
291
330
  do (stepName, partial) =>
292
331
  stepId = underscoreString stepName
293
332
  $partial = $(partial)
294
- fieldsetContents = $partial.find('fieldset').html()
333
+ fieldsetContents = if $partial.filter("fieldset").length > 0
334
+ $partial.filter("fieldset").html()
335
+ else
336
+ $partial.find('fieldset').html()
295
337
  $step = $("fieldset##{stepId}")
296
338
  $step.html fieldsetContents
297
- this.addDefaultButtons($step)
339
+ $step.filter("fieldset:not(.no-focus)").find(":input:not(input[type=hidden]):first").focus();
340
+ $step.data("contents", "loaded")
298
341
  @stepsNeedUpdate = true
299
342
 
300
- handleServerData: (data) ->
301
- if data.metadata?
343
+ saveStepMetadata: (stepId=this.currentStepId(), metadata=hatTrick.metadata.currentStep) ->
344
+ hatTrick.stepMetadata = {} unless hatTrick.stepMetadata?
345
+ hatTrick.stepMetadata[stepId] = metadata
346
+
347
+ handleServerData: (data) =>
348
+ if data.metadata?.externalRedirectURL?
349
+ externalRedirectURL = data.metadata.externalRedirectURL
350
+ if externalRedirectURL isnt ""
351
+ location.href = data.metadata.externalRedirectURL
352
+ # TODO: pop out of window if in iframe
353
+ # if (top.location == self.location)
354
+ # location.href = data.metadata.externalRedirectURL
355
+ # else
356
+ # window.open(data.metadata.externalRedirectURL)
357
+
358
+ if data.metadata?.url? and data.metadata?.method?
302
359
  this.setAction(data.metadata.url, data.metadata.method)
303
- $.extend(window.hatTrick, data) # merge new data with hatTrick
360
+ this.saveStepMetadata(data.metadata.currentStep.name, data.metadata.currentStep)
361
+ $.extend(hatTrick, data) # merge new data with hatTrick
304
362
  this.updateStepFromMetadata()
305
363
 
364
+ metadataRequestCallback: (data) =>
365
+ stepId = this.currentStepId()
366
+ # set empty step contents if we didn't get any;
367
+ # this makes sure we can tell whether or not we've already requested metadata
368
+ emptyStepContents = { hatTrickStepContents: {} }
369
+ stepKey = camelizeString(stepId)
370
+ emptyStepContents["hatTrickStepContents"][stepKey] = ""
371
+ unless data.data.hatTrickStepContents? and data.data.hatTrickStepContents[stepKey]?
372
+ data.data = $.extend({}, data.data, emptyStepContents)
373
+ this.handleServerData(data)
374
+ this.removeLinkFields() # updateStepFromMetadata sets this to currentStep
375
+ this.setupButtonsForCurrentStep()
376
+ this.updateButtons()
377
+ this.setFormFields(hatTrick.model)
378
+ @form.trigger "step_changed", { currentStep: stepId }
379
+
380
+ requestMetadataFromServer: ->
381
+ metadataUrl = document.location.pathname
382
+ lastChar = metadataUrl.charAt(metadataUrl.length - 1)
383
+ metadataUrl += "/" unless lastChar is "/"
384
+ stepId = this.currentStepId()
385
+ metadataUrl += "#{stepId}/" if metadataUrl.search("#{stepId}/$") is -1
386
+ metadataUrl += "metadata"
387
+ $.ajax
388
+ type: "GET"
389
+ url: metadataUrl
390
+ success: this.metadataRequestCallback
391
+ dataType: "json"
392
+
306
393
  updateStepContents: ->
307
- this.setContents(hatTrick.data.hatTrickStepContents)
394
+ stepKey = camelizeString(this.currentStepId())
395
+ if hatTrick.data?.hatTrickStepContents?[stepKey]?
396
+ this.setContents(hatTrick.data.hatTrickStepContents)
397
+ else
398
+ this.requestMetadataFromServer()
308
399
 
309
400
  setButtonMetadataForCurrentStep: ->
310
401
  if hatTrick.metadata?.currentStep?
@@ -317,22 +408,23 @@ class HatTrickWizard
317
408
  hatTrick.model['__name__']
318
409
 
319
410
  createDummyModelField: ->
320
- this.setHiddenInput "#{this.modelName()}[_dummy]", "1"
411
+ this.setHiddenInput "#{this.modelName()}[_dummy]", "1", "", this.currentStep()
321
412
 
322
413
  currentStepHasModelFields: ->
323
414
  this.currentStep().find("input[name^='#{this.modelName()}[']").length > 0
324
415
 
325
416
  updateStepFromMetadata: ->
417
+ currentStepId = this.currentStepId()
418
+
419
+ if $("fieldset##{currentStepId}").data("contents") is "server"
420
+ this.updateStepContents()
421
+
326
422
  if hatTrick.metadata?.currentStep?
327
- this.updateStepContents() if hatTrick.data.hatTrickStepContents?
423
+ currentStepData = hatTrick.metadata.currentStep
424
+ this.setCurrentStepField()
328
425
  this.setButtonMetadataForCurrentStep()
329
426
  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()
427
+ this.setLinkField(currentStepData.fieldset)
336
428
 
337
429
  bindEvents: ->
338
430
  @form.bind "step_shown", (event, data) =>
@@ -341,9 +433,8 @@ class HatTrickWizard
341
433
  $ ->
342
434
  if $("form.wizard").length > 0
343
435
  $form = $("form.wizard")
344
- window.hatTrick = {} unless window.hatTrick?
436
+ window.hatTrick ?= {}
345
437
  unless window.hatTrick.wizard?
346
- # log "Creating new HatTrickWizard instance"
347
438
  window.hatTrick.wizard = new HatTrickWizard($form, hatTrick.metadata)
348
439
 
349
440
  camelizeString = (string) ->
@@ -363,5 +454,5 @@ underscoreString = (string) ->
363
454
  result
364
455
 
365
456
  log = (msg) ->
366
- if window['console']?
457
+ if window['console']? and hatTrick.wizard.debug
367
458
  console.log msg
data/lib/hat-trick.rb CHANGED
@@ -4,5 +4,3 @@ require "active_support/core_ext/module"
4
4
  require "hat_trick/engine"
5
5
  require "hat_trick/dsl"
6
6
  require "gon"
7
-
8
- ::ActionController::Base.send(:include, HatTrick::DSL)
@@ -1,17 +1,16 @@
1
1
  module HatTrick
2
2
  class Config
3
- attr_reader :wizard_def
3
+ attr_accessor :create_url, :update_url, :back_button_label,
4
+ :next_button_label, :back_button_label_i18n_key,
5
+ :next_button_label_i18n_key
4
6
 
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
7
+ def initialize(settings={})
8
+ settings.each do |k,v|
9
+ setter = "#{k}="
10
+ if respond_to?(setter)
11
+ send(setter, v)
12
+ end
13
+ end
15
14
  end
16
15
  end
17
16
  end