fe 2.1.0 → 2.1.2
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.
- checksums.yaml +4 -4
- data/README.md +9 -5
- data/app/assets/javascripts/fe/fe.admin.js +1 -1
- data/app/assets/javascripts/fe/fe.public.nojquery.js.erb +410 -408
- data/app/controllers/concerns/fe/admin/question_sheets_controller_concern.rb +3 -3
- data/app/controllers/concerns/fe/answer_sheets_controller_concern.rb +16 -2
- data/app/controllers/fe/admin/elements_controller.rb +0 -1
- data/app/controllers/fe/admin/email_templates_controller.rb +15 -15
- data/app/controllers/fe/admin/question_pages_controller.rb +0 -1
- data/app/models/concerns/fe/answer_pages_presenter_concern.rb +13 -0
- data/app/models/concerns/fe/choice_field_concern.rb +10 -4
- data/app/models/fe/element.rb +14 -7
- data/app/models/fe/page.rb +6 -2
- data/app/models/fe/question.rb +2 -2
- data/app/models/fe/question_set.rb +1 -1
- data/app/models/fe/question_sheet.rb +7 -3
- data/app/views/fe/answer_sheets/_incomplete.html.erb +1 -0
- data/db/migrate/20181108201746_create_versions.rb +2 -6
- data/lib/fe/version.rb +1 -1
- data/spec/dummy/app/views/layouts/fe/fe.admin.html.erb +1 -0
- data/spec/dummy/log/test.log +67388 -0
- data/spec/models/fe/application_spec.rb +1 -1
- metadata +27 -10
- data/config/initializers/paper_trail.rb +0 -3
@@ -2,6 +2,416 @@
|
|
2
2
|
|
3
3
|
// used by answer sheets
|
4
4
|
|
5
|
+
window.fe = {};
|
6
|
+
fe.pageHandler = {
|
7
|
+
|
8
|
+
initialize : function(page) {
|
9
|
+
this.auto_save_frequency = 30; // seconds
|
10
|
+
this.timer_id = null;
|
11
|
+
|
12
|
+
this.current_page = page;
|
13
|
+
$('#' + page).data('form_data', this.captureForm($('#' + page)));
|
14
|
+
this.registerAutoSave();
|
15
|
+
|
16
|
+
this.page_validation = {}; // validation objects for each page
|
17
|
+
this.enableValidation(page);
|
18
|
+
|
19
|
+
$(document).trigger('feShowPage'); // allow other code to handle show page event by using $(document).on('feShowPage', function() { ... });
|
20
|
+
|
21
|
+
// this.background_load = false;
|
22
|
+
// this.final_submission = false;
|
23
|
+
},
|
24
|
+
|
25
|
+
// swap to a different page
|
26
|
+
showPage : function(page) {
|
27
|
+
// hide the old
|
28
|
+
$('#' + this.current_page + '-li').removeClass('active');
|
29
|
+
$('#' + this.current_page).hide();
|
30
|
+
|
31
|
+
// HACK: Need to clear the main error message when returning to the submit page
|
32
|
+
// It is very confusing to users to be there when they revisit the page
|
33
|
+
$('#submit_message, .submit_message').hide();
|
34
|
+
$('#application_errors, .application_errors').html('');
|
35
|
+
|
36
|
+
// show the new
|
37
|
+
// $('#' + page + '-li').removeClass('incomplete');
|
38
|
+
// $('#' + page + '-li').removeClass('valid');
|
39
|
+
$('#' + page + '-li').addClass('active');
|
40
|
+
$('#' + page).show();
|
41
|
+
this.current_page = page;
|
42
|
+
this.registerAutoSave(page);
|
43
|
+
fixGridColumnWidths();
|
44
|
+
$(document).trigger('feShowPage'); // allow other code to handle show page event by using $(document).on('feShowPage', function() { ... });
|
45
|
+
},
|
46
|
+
|
47
|
+
// callback onSuccess
|
48
|
+
pageLoadedBackground : function(response, textStatus, jqXHR) {
|
49
|
+
//this.pageLoaded(response, textStatus, jqXHR, true)
|
50
|
+
fe.pageHandler.pageLoaded(response, textStatus, jqXHR, true)
|
51
|
+
},
|
52
|
+
|
53
|
+
// callback onSuccess
|
54
|
+
pageLoaded : function(response, textStatus, jqXHR, background_load) {
|
55
|
+
background_load = typeof background_load !== 'undefined' ? background_load : false;
|
56
|
+
|
57
|
+
// var response = new String(transport.responseText);
|
58
|
+
let match = response.match(/<div id=\"(.*?)\"/i); // what did I just load? parse out the first div id
|
59
|
+
if( match != null )
|
60
|
+
{
|
61
|
+
let page = match[1];
|
62
|
+
if (response.match(/<html/)) {
|
63
|
+
alert("There was a problem loading that page. To work on other pages, please refresh the website.");
|
64
|
+
document.location = document.location;
|
65
|
+
} else if ($('#'+page).length > 0) {
|
66
|
+
$('#'+page).replaceWith(response);
|
67
|
+
} else {
|
68
|
+
$('#preview').append(response);
|
69
|
+
}
|
70
|
+
|
71
|
+
if (!background_load) { fe.pageHandler.showPage(page); } // show after load, unless loading in background
|
72
|
+
setUpJsHelpers();
|
73
|
+
fe.pageHandler.enableValidation(page);
|
74
|
+
if (background_load) { fe.pageHandler.validatePage(page, true); }
|
75
|
+
$('#' + page).data('form_data', fe.pageHandler.captureForm($('#' + page)));
|
76
|
+
}
|
77
|
+
$('#page_ajax_spinner').hide();
|
78
|
+
$('.reference_send_invite').button();
|
79
|
+
updateTotals();
|
80
|
+
$(document).trigger('fePageLoaded'); // allow other code to handle page load event by using $(document).on('fePageLoaded', function() { ... });
|
81
|
+
},
|
82
|
+
|
83
|
+
loadPage : function(page, url, background_load, validate_current_page) {
|
84
|
+
background_load = typeof background_load !== 'undefined' ? background_load : false;
|
85
|
+
validate_current_page = typeof validate_current_page !== 'undefined' ? validate_current_page : true;
|
86
|
+
|
87
|
+
let isValid;
|
88
|
+
if (validate_current_page) {
|
89
|
+
isValid = this.validatePage(this.current_page); // mark current page as valid (or not) as we're leaving
|
90
|
+
} else {
|
91
|
+
isValid = true
|
92
|
+
}
|
93
|
+
|
94
|
+
// Provide a way for enclosing apps to not go to the next page until the current page is valid
|
95
|
+
// They can do that with this:
|
96
|
+
//
|
97
|
+
// $(document).on 'fePageLoaded', (evt, page) ->
|
98
|
+
// $(".page > form").addClass('enforce-valid-before-next');
|
99
|
+
//
|
100
|
+
if (!isValid && $('#' + this.current_page + "-form").hasClass('enforce-valid-before-next')) {
|
101
|
+
// scroll up to where the error is
|
102
|
+
scrollTo($(".help-block:visible")[0].closest("li"));
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
|
106
|
+
this.unregisterAutoSave(); // don't auto-save while loading/saving
|
107
|
+
// will register auto-save on new page once loaded/shown
|
108
|
+
|
109
|
+
this.savePage();
|
110
|
+
|
111
|
+
if (!background_load) {
|
112
|
+
if ($('a[name="main"]').length == 1) {
|
113
|
+
scrollTo('a[name="main"]');
|
114
|
+
} else {
|
115
|
+
scrollTo('#main');
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
if (fe.pageHandler.isPageLoaded(page) && page.match('no_cache') == null) {
|
120
|
+
// if already loaded (element exists) excluding pages that need reloading
|
121
|
+
if (!background_load) { fe.pageHandler.showPage(page); }
|
122
|
+
$('#page_ajax_spinner').hide();
|
123
|
+
} else {
|
124
|
+
$.ajax({
|
125
|
+
url: url,
|
126
|
+
type: 'GET',
|
127
|
+
data: {'answer_sheet_type':answer_sheet_type},
|
128
|
+
success: background_load ? fe.pageHandler.pageLoadedBackground : fe.pageHandler.pageLoaded,
|
129
|
+
error: function (xhr, status, error) {
|
130
|
+
alert("There was a problem loading that page. We've been notified and will fix it as soon as possible. To work on other pages, please refresh the website.");
|
131
|
+
document.location = document.location;
|
132
|
+
},
|
133
|
+
beforeSend: function (xhr) {
|
134
|
+
$('body').trigger('ajax:loading', xhr);
|
135
|
+
},
|
136
|
+
complete: function (xhr) {
|
137
|
+
$('body').trigger('ajax:complete', xhr);
|
138
|
+
}
|
139
|
+
});
|
140
|
+
}
|
141
|
+
},
|
142
|
+
|
143
|
+
// save form if any changes were made
|
144
|
+
savePage : function(page, force, blocking) {
|
145
|
+
|
146
|
+
if (page == null) page = $('#' + this.current_page);
|
147
|
+
if (typeof blocking == "undefined") blocking = false;
|
148
|
+
|
149
|
+
// don't save more than once per second
|
150
|
+
let timeNow = new Date();
|
151
|
+
let lastSave;
|
152
|
+
if (typeof lastSave !== "undefined" && lastSave && !force && (timeNow - lastSave < 1000)) {
|
153
|
+
return true;
|
154
|
+
}
|
155
|
+
lastSave = timeNow;
|
156
|
+
let form_data = this.captureForm(page);
|
157
|
+
if( form_data ) {
|
158
|
+
if( page.data('form_data') == null || page.data('form_data').data !== form_data.data || force === true) { // if any changes
|
159
|
+
page.data('form_data', form_data);
|
160
|
+
$.ajax({url: form_data.url, type: 'put', data: form_data.data,
|
161
|
+
beforeSend: function (xhr) {
|
162
|
+
$('#spinner_' + page.attr('id')).show();
|
163
|
+
},
|
164
|
+
complete: function (xhr) {
|
165
|
+
$('#spinner_' + page.attr('id')).hide();
|
166
|
+
},
|
167
|
+
success: function (xhr) {
|
168
|
+
page.data('save_fails', 0)
|
169
|
+
$(document).trigger('fePageSaved'); // allow other code to handle save event by using $(document).on('fePageSaved', function() { ... });
|
170
|
+
},
|
171
|
+
async: !blocking,
|
172
|
+
error: function() {
|
173
|
+
let save_fails = page.data('save_fails') == null ? 0 : page.data('save_fails');
|
174
|
+
save_fails += 1;
|
175
|
+
page.data('save_fails', save_fails)
|
176
|
+
|
177
|
+
if (save_fails >= 3) {
|
178
|
+
alert("There was a problem saving that page. We've been notified and will fix it as soon as possible. This might happen if you logged out on another tab. The page will now reload.");
|
179
|
+
document.location = document.location;
|
180
|
+
} else {
|
181
|
+
page.data('save_fails', save_fails + 1)
|
182
|
+
page.data('form_data', null); // on error, force save for next call to save
|
183
|
+
// WARNING: race conditions with load & show?
|
184
|
+
// sort of a mess if save fails while another page is already loading!!
|
185
|
+
}
|
186
|
+
}});
|
187
|
+
}
|
188
|
+
}
|
189
|
+
// Update last saved stamp
|
190
|
+
},
|
191
|
+
|
192
|
+
savePages : function(force) {
|
193
|
+
$('.answer-page').each(function() {fe.pageHandler.savePage(null, force)})
|
194
|
+
},
|
195
|
+
|
196
|
+
// setup a timer to auto-save (only one timer, for the page being viewed)
|
197
|
+
registerAutoSave: function(page) {
|
198
|
+
this.timer_id = setInterval(this.savePages, this.auto_save_frequency * 1000);
|
199
|
+
},
|
200
|
+
|
201
|
+
unregisterAutoSave: function() {
|
202
|
+
if( this.timer_id != null )
|
203
|
+
{
|
204
|
+
clearInterval(this.timer_id);
|
205
|
+
this.timer_id = null;
|
206
|
+
}
|
207
|
+
},
|
208
|
+
|
209
|
+
// serialize form data and extract url to post to
|
210
|
+
captureForm : function(page) {
|
211
|
+
let form_el = $('#' + page.attr('id') + '-form');
|
212
|
+
if( form_el[0] == null ) return null;
|
213
|
+
let form_all_el = form_el.find("input:not(.dont_submit), textarea:not(.dont_submit), select:not(.dont_submit)");
|
214
|
+
return {url: form_el.attr('action'), data: form_all_el.serialize() + '&answer_sheet_type=' + answer_sheet_type};
|
215
|
+
},
|
216
|
+
|
217
|
+
|
218
|
+
// enable form validation (when form is loaded)
|
219
|
+
enableValidation : function(page) {
|
220
|
+
// Provide a way for enclosing apps to not have validation continually as people fill things out
|
221
|
+
// They can do that with this:
|
222
|
+
//
|
223
|
+
// $(document).on 'fePageLoaded', (evt, page) ->
|
224
|
+
// $(".page > form").addClass('no-ongoing-validation');
|
225
|
+
//
|
226
|
+
$('#' + page + '-form:not(.no-ongoing-validation)').validate({onsubmit:false, focusInvalid:true, onfocusout: function(element) { this.element(element);}});
|
227
|
+
$('#' + page + '-form:not(.no-ongoing-validation):input').change(function() {
|
228
|
+
fe.pageHandler.validatePage(page, true);
|
229
|
+
});
|
230
|
+
},
|
231
|
+
|
232
|
+
validatePage : function(page, page_classes_only) {
|
233
|
+
page_classes_only = typeof page_classes_only !== 'undefined' ? page_classes_only : false;
|
234
|
+
|
235
|
+
// Provide a way for enclosing apps to never validate a form
|
236
|
+
// They can do that with this:
|
237
|
+
//
|
238
|
+
// $(document).on 'fePageLoaded', (evt, page) ->
|
239
|
+
// $(".page > form").addClass('no-validation');
|
240
|
+
//
|
241
|
+
if ($('#' + this.current_page + "-form").hasClass('no-validation')) { return; }
|
242
|
+
|
243
|
+
try {
|
244
|
+
let li = $('#' + page + '-li');
|
245
|
+
let form = $('#' + page + '-form');
|
246
|
+
|
247
|
+
let valid = form.valid();
|
248
|
+
|
249
|
+
if (!page_classes_only) {
|
250
|
+
// Move radio button errors up
|
251
|
+
$('input[type=radio].error').closest('tr').addClass('error');
|
252
|
+
$('.choice_field input[type=radio].error').removeClass('error')
|
253
|
+
.closest('.choice_field')
|
254
|
+
.addClass('error');
|
255
|
+
$('div.yesno label.error').hide();
|
256
|
+
}
|
257
|
+
|
258
|
+
if (valid) {
|
259
|
+
li.removeClass('incomplete');
|
260
|
+
li.addClass('complete');
|
261
|
+
$(document).trigger('fePageValid', page); // allow other code to handle show page event by using $(document).on('fePageValid', function() { ... });
|
262
|
+
} else {
|
263
|
+
li.removeClass('complete');
|
264
|
+
li.addClass('incomplete');
|
265
|
+
$(document).trigger('fePageInvalid', page); // allow other code to handle show page event by using $(document).on('fePageInvalid', function() { ... });
|
266
|
+
}
|
267
|
+
return valid;
|
268
|
+
}
|
269
|
+
catch(err) {
|
270
|
+
|
271
|
+
// If the user clicks too quickly, sometimes the page element isn't properly defined yet.
|
272
|
+
// If we don't catch the error, js stops execution. If we catch it, the user just has to click again.
|
273
|
+
}
|
274
|
+
$('page_ajax_spinner').hide();
|
275
|
+
},
|
276
|
+
|
277
|
+
// callback when falls to 0 active Ajax requests
|
278
|
+
completeAll : function()
|
279
|
+
{
|
280
|
+
$('.page:visible #submit_button').attr('disabled', true)
|
281
|
+
$('#submit_message, .submit_message').html('');
|
282
|
+
$('#submit_message, .submit_message').hide();
|
283
|
+
// validate all the pages
|
284
|
+
$('.page_link').each(function(index, page) {
|
285
|
+
fe.pageHandler.validatePage($(page).attr('data-page-id'));
|
286
|
+
});
|
287
|
+
let all_valid = ($('#list-pages li.incomplete').length == 0);
|
288
|
+
|
289
|
+
// Make sure any necessary payments are made
|
290
|
+
let payments_made = $('.payment_question.required').length <= $('.payment').length
|
291
|
+
|
292
|
+
|
293
|
+
if( payments_made)
|
294
|
+
{
|
295
|
+
// force an async save (so it finishes before submitting) in case any input fields on submit_page
|
296
|
+
this.savePage(null, true, true);
|
297
|
+
|
298
|
+
// submit the application
|
299
|
+
if($('#submit_to')[0] != null)
|
300
|
+
{
|
301
|
+
let url = $('#submit_to').val();
|
302
|
+
// clear out pages array to force reload. This enables "frozen" apps
|
303
|
+
// immediately after submission - :onSuccess (for USCM which stays in the application vs. redirecting to the dashboard)
|
304
|
+
let curr = fe.pageHandler.current_page;
|
305
|
+
$.ajax({url: url, dataType:'script',
|
306
|
+
data: {answer_sheet_type: answer_sheet_type, a: $('input[type=hidden][name=a]').val()},
|
307
|
+
type:'post',
|
308
|
+
beforeSend: function(xhr) {
|
309
|
+
$('body').trigger('ajax:loading', xhr);
|
310
|
+
},
|
311
|
+
success: function(xhr) {
|
312
|
+
$('#list-pages a').each(function() {
|
313
|
+
if ($(this).attr('data-page-id') != curr) $('#' + $(this).attr('data-page-id')).remove();
|
314
|
+
})
|
315
|
+
},
|
316
|
+
complete: function(xhr) {
|
317
|
+
$('body').trigger('ajax:complete', xhr);
|
318
|
+
let btn = $('#submit_button');
|
319
|
+
if (btn) { btn.attr('disabled', false); }
|
320
|
+
}
|
321
|
+
});
|
322
|
+
}
|
323
|
+
}
|
324
|
+
else
|
325
|
+
{
|
326
|
+
// some pages aren't valid
|
327
|
+
$('#submit_message, .submit_message').html("Please make a payment");
|
328
|
+
$('#submit_message, .submit_message').show();
|
329
|
+
|
330
|
+
let btn = $('#submit_button'); if (btn) { btn.attr('disabled', false); }
|
331
|
+
}
|
332
|
+
},
|
333
|
+
|
334
|
+
// is page loaded? (useful for toggling enabled state of questions)
|
335
|
+
isPageLoaded : function(page)
|
336
|
+
{
|
337
|
+
return $('#' + page)[0] != null
|
338
|
+
},
|
339
|
+
|
340
|
+
checkConditional : function($element) {
|
341
|
+
let vals;
|
342
|
+
let matchable_answers = String($element.data('conditional_answer')).split(';').map(function(s) { return s.trim(); })
|
343
|
+
if ($element.hasClass('fe_choicefield') && ($element.hasClass('style_yes-no') || $element.hasClass('yes-no'))) {
|
344
|
+
if ($(matchable_answers).filter([1, '1', true, 'true', 'yes', 'Yes']).length > 0) {
|
345
|
+
matchable_answers = [1, '1', true, 'true', 'yes', 'Yes'];
|
346
|
+
}
|
347
|
+
if ($(matchable_answers).filter([0, '0', false, 'false', 'no', 'No']).length > 0) {
|
348
|
+
matchable_answers = [0, '0', false, 'false', 'no', 'No'];
|
349
|
+
}
|
350
|
+
vals = $([$element.find("input[type=radio]:checked").val()]);
|
351
|
+
} else if ($element.hasClass('fe_choicefield') && $element.hasClass('checkbox')) {
|
352
|
+
vals = $element.find("input[type=checkbox]:checked").map(function(i, el) { return $(el).val(); });
|
353
|
+
} else if ($element.hasClass('fe_choicefield') && $element.hasClass('radio')) {
|
354
|
+
vals = $([$element.find("input[type=radio]:checked").val()]);
|
355
|
+
} else {
|
356
|
+
vals = $([$element.find("input:visible, select:visible").val()]);
|
357
|
+
}
|
358
|
+
let match = $(matchable_answers).filter(vals).length > 0 || (matchable_answers == "" && vals.length == 0);
|
359
|
+
|
360
|
+
switch ($element.data('conditional_type')) {
|
361
|
+
case 'Fe::Element':
|
362
|
+
if (match) {
|
363
|
+
$("#element_" + $element.data('conditional_id')).show();
|
364
|
+
} else {
|
365
|
+
$("#element_" + $element.data('conditional_id')).hide();
|
366
|
+
}
|
367
|
+
break;
|
368
|
+
case 'Fe::Page':
|
369
|
+
let prefix = $element.data('answer_sheet_id_prefix');
|
370
|
+
let pg = prefix + '_' + $element.data('application_id') + '-fe_page_' + $element.data('conditional_id');
|
371
|
+
let li_id = 'li#'+pg+'-li';
|
372
|
+
li_id += ', li#'+pg+'-no_cache-li';
|
373
|
+
|
374
|
+
if (match) {
|
375
|
+
$(li_id).show();
|
376
|
+
// load the page (in the background) to determine validity
|
377
|
+
this.loadPage(pg, $(li_id).find('a').attr('href'), true);
|
378
|
+
} else {
|
379
|
+
$(li_id).hide();
|
380
|
+
}
|
381
|
+
break;
|
382
|
+
}
|
383
|
+
},
|
384
|
+
|
385
|
+
next : function(validate_current_page) {
|
386
|
+
validate_current_page = typeof validate_current_page !== 'undefined' ? validate_current_page : false;
|
387
|
+
|
388
|
+
let curr_page_link = $('#'+fe.pageHandler.current_page+"-link");
|
389
|
+
//fe.pageHandler.loadPage('application_22544-fe_page_165-no_cache','/fe/answer_sheets/22544/page/165/edit'); return false;
|
390
|
+
let page_link = curr_page_link
|
391
|
+
.parents('.application_section')
|
392
|
+
.nextAll()
|
393
|
+
.filter(function() { return $(this).find('a.page_link:visible').length > 0 })
|
394
|
+
.first()
|
395
|
+
.find('a.page_link');
|
396
|
+
$(".answer-page:visible div.buttons button").prop("disabled", true)
|
397
|
+
fe.pageHandler.loadPage(page_link.data('page-id'), page_link.attr('href'), false, validate_current_page);
|
398
|
+
},
|
399
|
+
|
400
|
+
prev : function() {
|
401
|
+
let curr_page_link = $('#'+fe.pageHandler.current_page+"-link");
|
402
|
+
//fe.pageHandler.loadPage('application_22544-fe_page_165-no_cache','/fe/answer_sheets/22544/page/165/edit'); return false;
|
403
|
+
let page_link = curr_page_link
|
404
|
+
.parents('.application_section')
|
405
|
+
.prevAll()
|
406
|
+
.filter(function() { return $(this).find('a.page_link:visible').length > 0 })
|
407
|
+
.first()
|
408
|
+
.find('a.page_link');
|
409
|
+
$(".answer-page:visible div.buttons button").prop("disabled", true)
|
410
|
+
fe.pageHandler.loadPage(page_link.data('page-id'), page_link.attr('href'));
|
411
|
+
}
|
412
|
+
|
413
|
+
};
|
414
|
+
|
5
415
|
$(document).on('ready turbo:load', function () {
|
6
416
|
<% if Fe.bootstrap %>
|
7
417
|
// http://stackoverflow.com/questions/18754020/bootstrap-3-with-jquery-validation-plugin
|
@@ -60,414 +470,6 @@ $(document).on('ready turbo:load', function () {
|
|
60
470
|
$(this).parent().find('.charsRemaining').html('');
|
61
471
|
});
|
62
472
|
|
63
|
-
window.fe = {};
|
64
|
-
fe.pageHandler = {
|
65
|
-
|
66
|
-
initialize : function(page) {
|
67
|
-
this.auto_save_frequency = 30; // seconds
|
68
|
-
this.timer_id = null;
|
69
|
-
|
70
|
-
this.current_page = page;
|
71
|
-
$('#' + page).data('form_data', this.captureForm($('#' + page)));
|
72
|
-
this.registerAutoSave();
|
73
|
-
|
74
|
-
this.page_validation = {}; // validation objects for each page
|
75
|
-
this.enableValidation(page);
|
76
|
-
|
77
|
-
$(document).trigger('feShowPage'); // allow other code to handle show page event by using $(document).on('feShowPage', function() { ... });
|
78
|
-
|
79
|
-
// this.background_load = false;
|
80
|
-
// this.final_submission = false;
|
81
|
-
},
|
82
|
-
|
83
|
-
// swap to a different page
|
84
|
-
showPage : function(page) {
|
85
|
-
// hide the old
|
86
|
-
$('#' + this.current_page + '-li').removeClass('active');
|
87
|
-
$('#' + this.current_page).hide();
|
88
|
-
|
89
|
-
// HACK: Need to clear the main error message when returning to the submit page
|
90
|
-
// It is very confusing to users to be there when they revisit the page
|
91
|
-
$('#submit_message, .submit_message').hide();
|
92
|
-
$('#application_errors, .application_errors').html('');
|
93
|
-
|
94
|
-
// show the new
|
95
|
-
// $('#' + page + '-li').removeClass('incomplete');
|
96
|
-
// $('#' + page + '-li').removeClass('valid');
|
97
|
-
$('#' + page + '-li').addClass('active');
|
98
|
-
$('#' + page).show();
|
99
|
-
this.current_page = page;
|
100
|
-
this.registerAutoSave(page);
|
101
|
-
fixGridColumnWidths();
|
102
|
-
$(document).trigger('feShowPage'); // allow other code to handle show page event by using $(document).on('feShowPage', function() { ... });
|
103
|
-
},
|
104
|
-
|
105
|
-
// callback onSuccess
|
106
|
-
pageLoadedBackground : function(response, textStatus, jqXHR) {
|
107
|
-
//this.pageLoaded(response, textStatus, jqXHR, true)
|
108
|
-
fe.pageHandler.pageLoaded(response, textStatus, jqXHR, true)
|
109
|
-
},
|
110
|
-
|
111
|
-
// callback onSuccess
|
112
|
-
pageLoaded : function(response, textStatus, jqXHR, background_load) {
|
113
|
-
background_load = typeof background_load !== 'undefined' ? background_load : false;
|
114
|
-
|
115
|
-
// var response = new String(transport.responseText);
|
116
|
-
let match = response.match(/<div id=\"(.*?)\"/i); // what did I just load? parse out the first div id
|
117
|
-
if( match != null )
|
118
|
-
{
|
119
|
-
let page = match[1];
|
120
|
-
if (response.match(/<html/)) {
|
121
|
-
alert("There was a problem loading that page. To work on other pages, please refresh the website.");
|
122
|
-
document.location = document.location;
|
123
|
-
} else if ($('#'+page).length > 0) {
|
124
|
-
$('#'+page).replaceWith(response);
|
125
|
-
} else {
|
126
|
-
$('#preview').append(response);
|
127
|
-
}
|
128
|
-
|
129
|
-
if (!background_load) { fe.pageHandler.showPage(page); } // show after load, unless loading in background
|
130
|
-
setUpJsHelpers();
|
131
|
-
fe.pageHandler.enableValidation(page);
|
132
|
-
if (background_load) { fe.pageHandler.validatePage(page, true); }
|
133
|
-
$('#' + page).data('form_data', fe.pageHandler.captureForm($('#' + page)));
|
134
|
-
}
|
135
|
-
$('#page_ajax_spinner').hide();
|
136
|
-
$('.reference_send_invite').button();
|
137
|
-
updateTotals();
|
138
|
-
$(document).trigger('fePageLoaded'); // allow other code to handle page load event by using $(document).on('fePageLoaded', function() { ... });
|
139
|
-
},
|
140
|
-
|
141
|
-
loadPage : function(page, url, background_load, validate_current_page) {
|
142
|
-
background_load = typeof background_load !== 'undefined' ? background_load : false;
|
143
|
-
validate_current_page = typeof validate_current_page !== 'undefined' ? validate_current_page : true;
|
144
|
-
|
145
|
-
let isValid;
|
146
|
-
if (validate_current_page) {
|
147
|
-
isValid = this.validatePage(this.current_page); // mark current page as valid (or not) as we're leaving
|
148
|
-
} else {
|
149
|
-
isValid = true
|
150
|
-
}
|
151
|
-
|
152
|
-
// Provide a way for enclosing apps to not go to the next page until the current page is valid
|
153
|
-
// They can do that with this:
|
154
|
-
//
|
155
|
-
// $(document).on 'fePageLoaded', (evt, page) ->
|
156
|
-
// $(".page > form").addClass('enforce-valid-before-next');
|
157
|
-
//
|
158
|
-
if (!isValid && $('#' + this.current_page + "-form").hasClass('enforce-valid-before-next')) {
|
159
|
-
// scroll up to where the error is
|
160
|
-
scrollTo($(".help-block:visible")[0].closest("li"));
|
161
|
-
return;
|
162
|
-
}
|
163
|
-
|
164
|
-
this.unregisterAutoSave(); // don't auto-save while loading/saving
|
165
|
-
// will register auto-save on new page once loaded/shown
|
166
|
-
|
167
|
-
this.savePage();
|
168
|
-
|
169
|
-
if (!background_load) {
|
170
|
-
if ($('a[name="main"]').length == 1) {
|
171
|
-
scrollTo('a[name="main"]');
|
172
|
-
} else {
|
173
|
-
scrollTo('#main');
|
174
|
-
}
|
175
|
-
}
|
176
|
-
|
177
|
-
if (fe.pageHandler.isPageLoaded(page) && page.match('no_cache') == null) {
|
178
|
-
// if already loaded (element exists) excluding pages that need reloading
|
179
|
-
if (!background_load) { fe.pageHandler.showPage(page); }
|
180
|
-
$('#page_ajax_spinner').hide();
|
181
|
-
} else {
|
182
|
-
$.ajax({
|
183
|
-
url: url,
|
184
|
-
type: 'GET',
|
185
|
-
data: {'answer_sheet_type':answer_sheet_type},
|
186
|
-
success: background_load ? fe.pageHandler.pageLoadedBackground : fe.pageHandler.pageLoaded,
|
187
|
-
error: function (xhr, status, error) {
|
188
|
-
alert("There was a problem loading that page. We've been notified and will fix it as soon as possible. To work on other pages, please refresh the website.");
|
189
|
-
document.location = document.location;
|
190
|
-
},
|
191
|
-
beforeSend: function (xhr) {
|
192
|
-
$('body').trigger('ajax:loading', xhr);
|
193
|
-
},
|
194
|
-
complete: function (xhr) {
|
195
|
-
$('body').trigger('ajax:complete', xhr);
|
196
|
-
}
|
197
|
-
});
|
198
|
-
}
|
199
|
-
},
|
200
|
-
|
201
|
-
// save form if any changes were made
|
202
|
-
savePage : function(page, force, blocking) {
|
203
|
-
|
204
|
-
if (page == null) page = $('#' + this.current_page);
|
205
|
-
if (typeof blocking == "undefined") blocking = false;
|
206
|
-
|
207
|
-
// don't save more than once per second
|
208
|
-
let timeNow = new Date();
|
209
|
-
let lastSave;
|
210
|
-
if (typeof lastSave !== "undefined" && lastSave && !force && (timeNow - lastSave < 1000)) {
|
211
|
-
return true;
|
212
|
-
}
|
213
|
-
lastSave = timeNow;
|
214
|
-
let form_data = this.captureForm(page);
|
215
|
-
if( form_data ) {
|
216
|
-
if( page.data('form_data') == null || page.data('form_data').data !== form_data.data || force === true) { // if any changes
|
217
|
-
page.data('form_data', form_data);
|
218
|
-
$.ajax({url: form_data.url, type: 'put', data: form_data.data,
|
219
|
-
beforeSend: function (xhr) {
|
220
|
-
$('#spinner_' + page.attr('id')).show();
|
221
|
-
},
|
222
|
-
complete: function (xhr) {
|
223
|
-
$('#spinner_' + page.attr('id')).hide();
|
224
|
-
},
|
225
|
-
success: function (xhr) {
|
226
|
-
page.data('save_fails', 0)
|
227
|
-
},
|
228
|
-
async: !blocking,
|
229
|
-
error: function() {
|
230
|
-
let save_fails = page.data('save_fails') == null ? 0 : page.data('save_fails');
|
231
|
-
save_fails += 1;
|
232
|
-
page.data('save_fails', save_fails)
|
233
|
-
|
234
|
-
if (save_fails >= 3) {
|
235
|
-
alert("There was a problem saving that page. We've been notified and will fix it as soon as possible. This might happen if you logged out on another tab. The page will now reload.");
|
236
|
-
document.location = document.location;
|
237
|
-
} else {
|
238
|
-
page.data('save_fails', save_fails + 1)
|
239
|
-
page.data('form_data', null); // on error, force save for next call to save
|
240
|
-
// WARNING: race conditions with load & show?
|
241
|
-
// sort of a mess if save fails while another page is already loading!!
|
242
|
-
}
|
243
|
-
}});
|
244
|
-
}
|
245
|
-
}
|
246
|
-
// Update last saved stamp
|
247
|
-
},
|
248
|
-
|
249
|
-
savePages : function(force) {
|
250
|
-
$('.answer-page').each(function() {fe.pageHandler.savePage(null, force)})
|
251
|
-
},
|
252
|
-
|
253
|
-
// setup a timer to auto-save (only one timer, for the page being viewed)
|
254
|
-
registerAutoSave: function(page) {
|
255
|
-
this.timer_id = setInterval(this.savePages, this.auto_save_frequency * 1000);
|
256
|
-
},
|
257
|
-
|
258
|
-
unregisterAutoSave: function() {
|
259
|
-
if( this.timer_id != null )
|
260
|
-
{
|
261
|
-
clearInterval(this.timer_id);
|
262
|
-
this.timer_id = null;
|
263
|
-
}
|
264
|
-
},
|
265
|
-
|
266
|
-
// serialize form data and extract url to post to
|
267
|
-
captureForm : function(page) {
|
268
|
-
let form_el = $('#' + page.attr('id') + '-form');
|
269
|
-
if( form_el[0] == null ) return null;
|
270
|
-
let form_all_el = form_el.find("input:not(.dont_submit), textarea:not(.dont_submit), select:not(.dont_submit)");
|
271
|
-
return {url: form_el.attr('action'), data: form_all_el.serialize() + '&answer_sheet_type=' + answer_sheet_type};
|
272
|
-
},
|
273
|
-
|
274
|
-
|
275
|
-
// enable form validation (when form is loaded)
|
276
|
-
enableValidation : function(page) {
|
277
|
-
// Provide a way for enclosing apps to not have validation continually as people fill things out
|
278
|
-
// They can do that with this:
|
279
|
-
//
|
280
|
-
// $(document).on 'fePageLoaded', (evt, page) ->
|
281
|
-
// $(".page > form").addClass('no-ongoing-validation');
|
282
|
-
//
|
283
|
-
$('#' + page + '-form:not(.no-ongoing-validation)').validate({onsubmit:false, focusInvalid:true, onfocusout: function(element) { this.element(element);}});
|
284
|
-
$('#' + page + '-form:not(.no-ongoing-validation):input').change(function() {
|
285
|
-
fe.pageHandler.validatePage(page, true);
|
286
|
-
});
|
287
|
-
},
|
288
|
-
|
289
|
-
validatePage : function(page, page_classes_only) {
|
290
|
-
page_classes_only = typeof page_classes_only !== 'undefined' ? page_classes_only : false;
|
291
|
-
|
292
|
-
// Provide a way for enclosing apps to never validate a form
|
293
|
-
// They can do that with this:
|
294
|
-
//
|
295
|
-
// $(document).on 'fePageLoaded', (evt, page) ->
|
296
|
-
// $(".page > form").addClass('no-validation');
|
297
|
-
//
|
298
|
-
if ($('#' + this.current_page + "-form").hasClass('no-validation')) { return; }
|
299
|
-
|
300
|
-
try {
|
301
|
-
let li = $('#' + page + '-li');
|
302
|
-
let form = $('#' + page + '-form');
|
303
|
-
|
304
|
-
let valid = form.valid();
|
305
|
-
|
306
|
-
if (!page_classes_only) {
|
307
|
-
// Move radio button errors up
|
308
|
-
$('input[type=radio].error').closest('tr').addClass('error');
|
309
|
-
$('.choice_field input[type=radio].error').removeClass('error')
|
310
|
-
.closest('.choice_field')
|
311
|
-
.addClass('error');
|
312
|
-
$('div.yesno label.error').hide();
|
313
|
-
}
|
314
|
-
|
315
|
-
if (valid) {
|
316
|
-
li.removeClass('incomplete');
|
317
|
-
li.addClass('complete');
|
318
|
-
$(document).trigger('fePageValid', page); // allow other code to handle show page event by using $(document).on('fePageValid', function() { ... });
|
319
|
-
} else {
|
320
|
-
li.removeClass('complete');
|
321
|
-
li.addClass('incomplete');
|
322
|
-
$(document).trigger('fePageInvalid', page); // allow other code to handle show page event by using $(document).on('fePageInvalid', function() { ... });
|
323
|
-
}
|
324
|
-
return valid;
|
325
|
-
}
|
326
|
-
catch(err) {
|
327
|
-
|
328
|
-
// If the user clicks too quickly, sometimes the page element isn't properly defined yet.
|
329
|
-
// If we don't catch the error, js stops execution. If we catch it, the user just has to click again.
|
330
|
-
}
|
331
|
-
$('page_ajax_spinner').hide();
|
332
|
-
},
|
333
|
-
|
334
|
-
// callback when falls to 0 active Ajax requests
|
335
|
-
completeAll : function()
|
336
|
-
{
|
337
|
-
$('.page:visible #submit_button').attr('disabled', true)
|
338
|
-
$('#submit_message, .submit_message').html('');
|
339
|
-
$('#submit_message, .submit_message').hide();
|
340
|
-
// validate all the pages
|
341
|
-
$('.page_link').each(function(index, page) {
|
342
|
-
fe.pageHandler.validatePage($(page).attr('data-page-id'));
|
343
|
-
});
|
344
|
-
let all_valid = ($('#list-pages li.incomplete').length == 0);
|
345
|
-
|
346
|
-
// Make sure any necessary payments are made
|
347
|
-
let payments_made = $('.payment_question.required').length <= $('.payment').length
|
348
|
-
|
349
|
-
|
350
|
-
if( payments_made)
|
351
|
-
{
|
352
|
-
// force an async save (so it finishes before submitting) in case any input fields on submit_page
|
353
|
-
this.savePage(null, true, true);
|
354
|
-
|
355
|
-
// submit the application
|
356
|
-
if($('#submit_to')[0] != null)
|
357
|
-
{
|
358
|
-
let url = $('#submit_to').val();
|
359
|
-
// clear out pages array to force reload. This enables "frozen" apps
|
360
|
-
// immediately after submission - :onSuccess (for USCM which stays in the application vs. redirecting to the dashboard)
|
361
|
-
let curr = fe.pageHandler.current_page;
|
362
|
-
$.ajax({url: url, dataType:'script',
|
363
|
-
data: {answer_sheet_type: answer_sheet_type, a: $('input[type=hidden][name=a]').val()},
|
364
|
-
type:'post',
|
365
|
-
beforeSend: function(xhr) {
|
366
|
-
$('body').trigger('ajax:loading', xhr);
|
367
|
-
},
|
368
|
-
success: function(xhr) {
|
369
|
-
$('#list-pages a').each(function() {
|
370
|
-
if ($(this).attr('data-page-id') != curr) $('#' + $(this).attr('data-page-id')).remove();
|
371
|
-
})
|
372
|
-
},
|
373
|
-
complete: function(xhr) {
|
374
|
-
$('body').trigger('ajax:complete', xhr);
|
375
|
-
let btn = $('#submit_button');
|
376
|
-
if (btn) { btn.attr('disabled', false); }
|
377
|
-
}
|
378
|
-
});
|
379
|
-
}
|
380
|
-
}
|
381
|
-
else
|
382
|
-
{
|
383
|
-
// some pages aren't valid
|
384
|
-
$('#submit_message, .submit_message').html("Please make a payment");
|
385
|
-
$('#submit_message, .submit_message').show();
|
386
|
-
|
387
|
-
let btn = $('#submit_button'); if (btn) { btn.attr('disabled', false); }
|
388
|
-
}
|
389
|
-
},
|
390
|
-
|
391
|
-
// is page loaded? (useful for toggling enabled state of questions)
|
392
|
-
isPageLoaded : function(page)
|
393
|
-
{
|
394
|
-
return $('#' + page)[0] != null
|
395
|
-
},
|
396
|
-
|
397
|
-
checkConditional : function($element) {
|
398
|
-
let vals;
|
399
|
-
let matchable_answers = String($element.data('conditional_answer')).split(';').map(function(s) { return s.trim(); })
|
400
|
-
if ($element.hasClass('fe_choicefield') && ($element.hasClass('style_yes-no') || $element.hasClass('yes-no'))) {
|
401
|
-
if ($(matchable_answers).filter([1, '1', true, 'true', 'yes', 'Yes']).length > 0) {
|
402
|
-
matchable_answers = [1, '1', true, 'true', 'yes', 'Yes'];
|
403
|
-
}
|
404
|
-
if ($(matchable_answers).filter([0, '0', false, 'false', 'no', 'No']).length > 0) {
|
405
|
-
matchable_answers = [0, '0', false, 'false', 'no', 'No'];
|
406
|
-
}
|
407
|
-
vals = $([$element.find("input[type=radio]:checked").val()]);
|
408
|
-
} else if ($element.hasClass('fe_choicefield') && $element.hasClass('checkbox')) {
|
409
|
-
vals = $element.find("input[type=checkbox]:checked").map(function(i, el) { return $(el).val(); });
|
410
|
-
} else if ($element.hasClass('fe_choicefield') && $element.hasClass('radio')) {
|
411
|
-
vals = $([$element.find("input[type=radio]:checked").val()]);
|
412
|
-
} else {
|
413
|
-
vals = $([$element.find("input:visible, select:visible").val()]);
|
414
|
-
}
|
415
|
-
let match = $(matchable_answers).filter(vals).length > 0 || (matchable_answers == "" && vals.length == 0);
|
416
|
-
|
417
|
-
switch ($element.data('conditional_type')) {
|
418
|
-
case 'Fe::Element':
|
419
|
-
if (match) {
|
420
|
-
$("#element_" + $element.data('conditional_id')).show();
|
421
|
-
} else {
|
422
|
-
$("#element_" + $element.data('conditional_id')).hide();
|
423
|
-
}
|
424
|
-
break;
|
425
|
-
case 'Fe::Page':
|
426
|
-
let prefix = $element.data('answer_sheet_id_prefix');
|
427
|
-
let pg = prefix + '_' + $element.data('application_id') + '-fe_page_' + $element.data('conditional_id');
|
428
|
-
let li_id = 'li#'+pg+'-li';
|
429
|
-
li_id += ', li#'+pg+'-no_cache-li';
|
430
|
-
|
431
|
-
if (match) {
|
432
|
-
$(li_id).show();
|
433
|
-
// load the page (in the background) to determine validity
|
434
|
-
this.loadPage(pg, $(li_id).find('a').attr('href'), true);
|
435
|
-
} else {
|
436
|
-
$(li_id).hide();
|
437
|
-
}
|
438
|
-
break;
|
439
|
-
}
|
440
|
-
},
|
441
|
-
|
442
|
-
next : function(validate_current_page) {
|
443
|
-
validate_current_page = typeof validate_current_page !== 'undefined' ? validate_current_page : false;
|
444
|
-
|
445
|
-
let curr_page_link = $('#'+fe.pageHandler.current_page+"-link");
|
446
|
-
//fe.pageHandler.loadPage('application_22544-fe_page_165-no_cache','/fe/answer_sheets/22544/page/165/edit'); return false;
|
447
|
-
let page_link = curr_page_link
|
448
|
-
.parents('.application_section')
|
449
|
-
.nextAll()
|
450
|
-
.filter(function() { return $(this).find('a.page_link:visible').length > 0 })
|
451
|
-
.first()
|
452
|
-
.find('a.page_link');
|
453
|
-
$(".answer-page:visible div.buttons button").prop("disabled", true)
|
454
|
-
fe.pageHandler.loadPage(page_link.data('page-id'), page_link.attr('href'), false, validate_current_page);
|
455
|
-
},
|
456
|
-
|
457
|
-
prev : function() {
|
458
|
-
let curr_page_link = $('#'+fe.pageHandler.current_page+"-link");
|
459
|
-
//fe.pageHandler.loadPage('application_22544-fe_page_165-no_cache','/fe/answer_sheets/22544/page/165/edit'); return false;
|
460
|
-
let page_link = curr_page_link
|
461
|
-
.parents('.application_section')
|
462
|
-
.prevAll()
|
463
|
-
.filter(function() { return $(this).find('a.page_link:visible').length > 0 })
|
464
|
-
.first()
|
465
|
-
.find('a.page_link');
|
466
|
-
$(".answer-page:visible div.buttons button").prop("disabled", true)
|
467
|
-
fe.pageHandler.loadPage(page_link.data('page-id'), page_link.attr('href'));
|
468
|
-
}
|
469
|
-
|
470
|
-
};
|
471
473
|
|
472
474
|
$(document).on('click', ".conditional input, .conditional select", function() {
|
473
475
|
fe.pageHandler.checkConditional($(this).closest('.conditional'));
|