fe 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'));