https://katholiekzfl.nl/wp-content/plugins/laposta-signup-basic/assets/js/lsb-form/main.js?ver=3.5.0
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.