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.
@@ -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'));