fe 2.1.1 → 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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 +25 -8
- 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'));
|