PageSourceSearch

https://katholiekzfl.nl/wp-content/plugins/laposta-signup-basic/assets/js/lsb-form/main.js?ver=3.5.0

js katholiekzfl.nl collected 2026-10-09 17:08:28 UTC 14,361 bytes, 437 lines download raw bytes

1(function ($) {
2  var CLASS_VISUALLY_HIDDEN = 'lsb-visually-hidden';
3  var SELECTOR_FORM = '.js-lsb-form';
4  var SELECTOR_FIELD_WRAPPER = '.lsb-form-field-wrapper';
5  var SELECTOR_INPUT = '.lsb-form-input';
6  var SELECTOR_CHECKBOX_INPUT = '.lsb-form-check-input';
7  var SELECTOR_FIELD_ERROR = '.lsb-form-field-error-feedback';
8
9  // Laposta spam protection: a single-use token (with behavioural signals signed into it) is
10  // requested from the Laposta form service on submit and the proof of work that comes with it
11  // is solved in the browser; both travel with the form to the API. The visitor is never
12  // delayed for long: without a proof the submission is sent anyway.
13  var TOKEN_WAIT_MS = 2500;
14  var POW_WAIT_MS = 8000;
15
16  var lsb = {};
17
18  // pointer or touch activity anywhere on the page, shared by all forms
19  lsb.pointerSeen = false;
20  lsb.watchPointer = function() {
21    var events = ['pointerdown', 'mousedown', 'mousemove', 'touchstart'];
22    var seen = function(event) {
23      if (event.isTrusted === false) return;
24      lsb.pointerSeen = true;
25      for (var i = 0; i < events.length; i++) document.removeEventListener(events[i], seen);
26    };
27    for (var i = 0; i < events.length; i++) document.addEventListener(events[i], seen);
28  };
29
30  // field interaction per form: focus/input/keydown on a visible field
31  lsb.watchInteraction = function(form) {
32    var mark = function(event) {
33      if (event.isTrusted === false) return;
34      if (event.target && event.target.type === 'hidden') return;
35      form.lsbInteracted = true;
36      if (!form.lsbFirstInteractionAt) form.lsbFirstInteractionAt = new Date().getTime();
37    };
38    form.addEventListener('focusin', mark);
39    form.addEventListener('input', mark);
40    form.addEventListener('keydown', mark);
41  };
42
43  // the compact "i.p.w.c.t" signal string the Laposta server verifies
44  lsb.behaviourValue = function(form) {
45    var nav = typeof navigator !== 'undefined' ? navigator : null;
46    var headless = !!(nav && nav.userAgent && nav.userAgent.indexOf('Chrome/') !== -1 && !window.chrome);
47    return [
48      form.lsbInteracted ? 1 : 0,
49      lsb.pointerSeen ? 1 : 0,
50      (nav && nav.webdriver) ? 1 : 0,
51      headless ? 1 : 0,
52      Math.min(Math.max(0, form.lsbFirstInteractionAt ? new Date().getTime() - form.lsbFirstInteractionAt : 0), 9999999)
53    ].join('.');
54  };
55
56  lsb.loadSolver = function(powUrl) {
57    if (!powUrl || window.LapostaPow || lsb.solverRequested) return;
58    lsb.solverRequested = true;
59    try {
60      var script = document.createElement('script');
61      script.src = powUrl;
62      script.async = true;
63      document.head.appendChild(script);
64    } catch (error) {}
65  };
66
67  // resolves to {token, challenge} or null, never rejects
68  lsb.requestToken = function(tokenUrl, behaviour) {
69    if (!tokenUrl || !window.fetch) return null;
70    var url = tokenUrl + '&b=' + encodeURIComponent(behaviour);
71    return fetch(url, { credentials: 'omit', headers: { 'Accept': 'application/json' } })
72      .then(function(response) {
73        if (!response.ok) throw new Error('Token request failed');
74        return response.json();
75      })
76      .then(function(response) {
77        if (!response || !response.token) return null;
78        return { token: response.token, challenge: response.challenge || null };
79      })
80      .catch(function() {
81        return null;
82      });
83  };
84
85  // fills the token and proof-of-work inputs of the form, then calls done() exactly once
86  lsb.prepareProof = function($form, done) {
87    var form = $form[0];
88    var $tokenInput = $form.find('.js-token-input');
89    var $powInput = $form.find('.js-pow-input');
90    if ($tokenInput.length) $tokenInput.val('');
91    if ($powInput.length) $powInput.val('');
92
93    var finished = false;
94    var totalTimer = window.setTimeout(finish, TOKEN_WAIT_MS + POW_WAIT_MS);
95
96    function finish() {
97      if (finished) return;
98      finished = true;
99      window.clearTimeout(totalTimer);
100      done();
101    }
102
103    var request = lsb.requestToken($form.data('tokenUrl'), lsb.behaviourValue(form));
104    if (!request) { finish(); return; }
105
106    request.then(function(result) {
107      if (finished) return;
108      if (!result || !result.token) { finish(); return; }
109
110      if ($tokenInput.length) $tokenInput.val(result.token);
111      if (result.challenge && window.LapostaPow) {
112        try {
113          window.LapostaPow.solve(result.challenge.salt, result.challenge.difficulty, function(solution) {
114            if (finished) return;
115            if (solution && $powInput.length) $powInput.val(solution);
116            finish();
117          }, { maxMs: POW_WAIT_MS });
118        } catch (error) {
119          finish();
120        }
121      } else {
122        finish();
123      }
124    }, finish);
125  };
126
127  lsb.getElementValue = function($element) {
128    if (!$element.length) return null;
129
130    var type = $element.attr('type');
131    var tag = $element.prop('tagName').toLowerCase();
132
133    if (tag === 'input') {
134      if (type === 'checkbox') {
135        return $element.is(':checked');
136      } else if (type === 'radio') {
137        var $fieldset = $element.closest('fieldset');
138        if ($fieldset.length) {
139          return $fieldset.find('input[name="' + $element.attr('name') + '"]:checked').val();
140        }
141        return $element.is(':checked') ? $element.val() : null;
142      } else {
143        return $element.val();
144      }
145    } else if (tag === 'select') {
146      return $element.prop('multiple') ? $element.val() : $element.find('option:selected').val();
147    } else if (tag === 'textarea') {
148      return $element.val();
149    }
150    return null;
151  }
152
153  lsb.validateForm = function($form) {
154    var isValid = true;
155    $form.find(SELECTOR_FIELD_WRAPPER).each(function() {
156      var $field = $(this);
157      if (!lsb.validateField($field) && isValid) {
158        isValid = false;
159        var $input = $field.find(SELECTOR_INPUT);
160        if (!$input.length) {
161          $input = $field.find(SELECTOR_CHECKBOX_INPUT);
162        }
163        $input.focus();
164      }
165    });
166
167    return isValid;
168  }
169  
170  lsb.validateField = function($field) {
171    var $input = $field.find(SELECTOR_INPUT);
172    if (!$input.length) {
173      $input = $field.find(SELECTOR_CHECKBOX_INPUT);
174    }
175    if (!$input.length) {
176      console.error('[LSB] no input found for field', $field);
177      return false;
178    }
179
180    var val = lsb.getElementValue($input);
181    if (typeof val === "string") {
182      val = val.trim();
183    }
184
185    var fieldType = $field.data('fieldType');
186    var fieldName = $field.find('.lsb-form-label-name').text();
187    var error = null;
188    var required = $field.data('required');
189    if (!val) {
190      if (required) {
191        error = lsbConfig.trans['field.error.required.'+fieldType];
192        if (error === undefined) {
193          error = lsbConfig.trans['field.error.required'].replace('%field_name%', fieldName);
194        }
195      }
196    } else if (fieldType === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) {
197      error = lsbConfig.trans['field.error.invalid.email'];
198    } else if (fieldType === 'date') {
199      var date = new Date(val);
200      // Check of de datum geldig is
201      if (isNaN(date.getTime())) {
202        error = lsbConfig.trans['field.error.invalid.date'];
203      }
204    } else if (fieldType === 'number') {
205      if (val.indexOf(',') >= 0 && val.indexOf('.') === -1) {
206        // assume wrong decimal
207        val = val.replace(',', '.');
208        $input.val(val); // give direct feedback
209      }
210      if (isNaN(parseFloat(val)) || !isFinite(val)) {
211        error = lsbConfig.trans['field.error.invalid.number'];
212      }
213    }
214
215    var hasError = error !== null;
216    if (hasError) {
217      lsb.showFieldError($field, $input, error);
218    } else {
219      lsb.hideFieldError($field, $input, error);
220    }
221
222    return !hasError
223  }
224
225  /**
226   * @param $field
227   * @param $input
228   * @param error html must be escaped
229   */
230  lsb.showFieldError = function($field, $input, error) {
231    if ($field.attr('aria-invalid') !== undefined) {
232      $field.attr('aria-invalid', 'true');
233    }
234    if ($input.attr('aria-invalid') !== undefined) {
235      $input.attr('aria-invalid', 'true');
236    }
237
238    $input.addClass(lsbConfig.class.inputHasErrorClass);
239    $field
240      .addClass(lsbConfig.class.fieldHasErrorClass)
241      .find(SELECTOR_FIELD_ERROR)
242      .removeClass(CLASS_VISUALLY_HIDDEN)
243      .html(error);
244  }
245
246  lsb.hideFieldError = function($field, $input) {
247    if ($field.attr('aria-invalid') !== undefined) {
248      $field.attr('aria-invalid', 'false');
249    }
250    if ($input.attr('aria-invalid') !== undefined) {
251      $input.attr('aria-invalid', 'false');
252    }
253
254    $input.removeClass(lsbConfig.class.inputHasErrorClass);
255    $field
256      .removeClass(lsbConfig.class.fieldHasErrorClass)
257      .find(SELECTOR_FIELD_ERROR)
258      .addClass(CLASS_VISUALLY_HIDDEN)
259      .text('');
260  }
261
262  lsb.showGlobalError = function($form, error) {
263    $form.find('.lsb-form-global-error')
264      .removeClass(CLASS_VISUALLY_HIDDEN)
265      .html(error);
266  }
267
268  lsb.hideGlobalError = function($form) {
269    $form.find('.lsb-form-global-error')
270      .addClass(CLASS_VISUALLY_HIDDEN)
271      .text('');
272  }
273
274  lsb.onSubmitForm = function($form) {
275    var defaultErrorMessage = lsbConfig.trans['global.unknown_error'];
276    var $submitButton = $form.find('button[name=lsb_form_submit]');
277    var $nonceInput = $form.find('.js-nonce-input');
278    var $loader = $form.find('.lsb-loader');
279    var $loaderAria = $form.find('.lsb-loader-aria');
280
281    var isValid = lsb.validateForm($form);
282    if (!isValid) {
283      lsb.showGlobalError($form, lsbConfig.trans['global.form_contains_errors']);
284      return;
285    }
286
287    lsb.hideGlobalError($form);
288    $loader.show();
289    $loaderAria.text(lsbConfig.trans['global.loading']);
290    $submitButton.attr('aria-disabled', 'true');
291    $submitButton.attr('disabled', 'disabled')
292    $submitButton[0].disabled = true;
293
294
295    var url = $form.data('formPostUrl');
296    lsb.prepareProof($form, function() {
297      lsb.postForm($form, url, doOnSuccess, doOnError);
298    });
299
300    function doOnSuccess(html) {
301      $form.find('.lsb-form-body').remove();
302      var $successMessageContainer = $form.find('.lsb-form-success-container');
303      $successMessageContainer
304        .html(html)
305        .removeClass(CLASS_VISUALLY_HIDDEN);
306
307      var containerTop = $successMessageContainer.offset().top;
308      var windowTop = $(window).scrollTop();
309      var windowHeight = $(window).height();
310
311      var $header = $('header');
312      var headerHeight = 0;
313      if ($header.length) {
314        var headerPosition = $header.css('position');
315        if (headerPosition === 'fixed' || headerPosition === 'absolute') {
316          headerHeight = $header.outerHeight();
317        }
318      }
319
320      if (containerTop < windowTop || containerTop > (windowTop + windowHeight)) {
321        $('html, body').animate({
322          scrollTop: containerTop - headerHeight - 20
323        }, 'smooth');
324      }
325    }
326
327    function doOnError(html) {
328      lsb.showGlobalError($form, html);
329      $loader.hide();
330      $loaderAria.text('');
331      $submitButton.removeAttr('disabled');
332      $submitButton.removeAttr('aria-disabled');
333      $submitButton[0].disabled = 0;
334    }
335  }
336
337  lsb.postForm = function($form, url, doOnSuccess, doOnError) {
338    var defaultErrorMessage = lsbConfig.trans['global.unknown_error'];
339    var $nonceInput = $form.find('.js-nonce-input');
340    var data = $form.serialize();
341    $.ajax({
342      data: data,
343      url: url,
344      method: 'post',
345      dataType: 'json',
346      success: function(data) {
347        var status = null;
348        if (data && data.hasOwnProperty('status')) {
349          status = data.status;
350        }
351
352        if (status === 'invalid_nonce' && data.hasOwnProperty('nonce')) {
353          $nonceInput.val(data.nonce);
354          var nonceRefreshedAt = $nonceInput.data('refreshedAt');
355          if (nonceRefreshedAt && Date.now() - nonceRefreshedAt < 1000 * 10) {
356            // prevent refresh loop
357            doOnError(defaultErrorMessage);
358            return;
359          }
360          $nonceInput.data('refreshedAt', Date.now())
361          $form.trigger('submit');
362          return;
363        }
364
365        var html = defaultErrorMessage;
366        if (data && data.hasOwnProperty('html')) {
367          html = data.html;
368        }
369
370        if (status === 'success') {
371          doOnSuccess(html)
372        } else {
373          doOnError(html);
374        }
375      },
376      error: function() {
377        doOnError(defaultErrorMessage)
378      }
379    });
380  }
381
382  $(function() {
383    // spam protection: behavioural signals and the proof-of-work solver
384    var $forms = $(SELECTOR_FORM);
385    if ($forms.length) {
386      lsb.watchPointer();
387      $forms.each(function() {
388        lsb.watchInteraction(this);
389      });
390      lsb.loadSolver($forms.first().data('powUrl'));
391    }
392
393    // submit form
394    $('body').on('submit', SELECTOR_FORM, function(e) {
395      e.preventDefault();
396      var $form = $(this);
397      lsb.onSubmitForm($form);
398    });
399
400    // validation on blur
401    $('body').on('blur', SELECTOR_FORM + ' ' + SELECTOR_FIELD_WRAPPER, function(e) {
402      var $form = $(this).closest(SELECTOR_FORM);
403      var $field = $(this).closest(SELECTOR_FIELD_WRAPPER);
404
405      // The timeout is required to get the correct active element
406      setTimeout(function() {
407        var $activeField = null;
408        if (document.activeElement) {
409          $activeField = $(document.activeElement).closest(SELECTOR_FIELD_WRAPPER);
410          // Validation is done if the current focus is not in same group.
411          // This prevents errors from showing up when user uses tab to go to the preferred checkbox.
412          if ($activeField.length && $activeField[0] === $field[0]) {
413            return;
414          }
415        }
416
417        lsb.validateField($field);
418        if ($activeField && $activeField.length) {
419          // only hide the global error if there is an active field
420          lsb.hideGlobalError($form);
421        }
422      }, 10);
423    });
424
425    // Validation of previous fields
426    $('body').on('focus', SELECTOR_FORM + ' ' + SELECTOR_FIELD_WRAPPER, function(e) {
427      var $form = $(this).closest(SELECTOR_FORM);
428      var $fields = $form.find(SELECTOR_FIELD_WRAPPER);
429      var currentFieldIndex = $fields.index($(this));
430
431      $fields.slice(0, currentFieldIndex).each(function() {
432        lsb.validateField($(this));
433      });
434    });
435
436  });
437})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.