hat-trick 0.1.2 → 0.1.3

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/.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