https://pouillelescoteaux.fr/_assets/6b4f2a2766cf7ae23f682cf290eb0…/PowermailCondition.js?1788344467
1class PowermailCondition { 2 'use strict'; 3 4 /** 5 * Form element (filled via constructor) 6 */ 7 #form; 8 9 /** 10 * Is the form a morestep form? 11 */ 12 #isMoreStepForm; 13 14 /** 15 * Selector for fields to be excluded from being sent to backend. 16 * Might be useful for file upload fields. 17 * 18 * @type {string} 19 */ 20 #excludedFieldsSelector = 'data-powermail-cond-excluded-fields'; 21 22 constructor(form) { 23 this.#form = form; 24 this.#form.powermailConditions = this; 25 this.#isMoreStepForm = this.#form.classList.contains('powermail_morestep'); 26 } 27 28 initialize = function () { 29 const that = this; 30 31 let formUid = this.#form.querySelector('input.powermail_form_uid').value; 32 let formActionSelector = '#form-' + formUid + '-actions'; 33 34 if (document.querySelector(formActionSelector) === null) { 35 // Loading conditions via AJAX 36 that.#sendFormValuesToPowermailCond(); 37 } else { 38 // Using prerendered conditions 39 let actions = JSON.parse(document.querySelector(formActionSelector).textContent); 40 that.#processActions(actions); 41 } 42 43 that.#fieldListener(); 44 that.#submitListener(); 45 } 46 47 /** 48 * Prevents a specific race condition when submitting forms 49 * 50 * Technical background: 51 * When a user's first action is to focus a field, enter a value, and immediately 52 * click submit, it can cause problems. This sequence triggers #sendFormValuesToPowermailCond 53 * and #enableAllFields, but the form submission cancels the network request to the 54 * condition endpoint. As a result, all fields get enabled and transmitted for processing. 55 * 56 * How it works: 57 * This listener checks if any form element is currently focused. If so, it: 58 * 1. Blurs the focused input first 59 * 2. Waits a brief moment (50ms) 60 * 3. Then submits the form 61 * 62 * This ensures field processing completes properly before submission, avoiding the race condition. 63 */ 64 #submitListener() { 65 // don't setup race-condition listener for AJAX forms 66 if (this.#form.getAttribute("data-powermail-ajax") === "true") { 67 return; 68 } 69 70 this.#form.addEventListener('submit', (event) => { 71 event.preventDefault(); 72 73 if (document.activeElement && document.activeElement.tagName) { 74 const activeElement = document.activeElement; 75 const tagName = activeElement.tagName.toLowerCase(); 76 if ((tagName === 'input' || tagName === 'textarea' || tagName === 'select') && 77 this.#form.contains(activeElement)) { 78 activeElement.blur(); 79 80 setTimeout(() => { 81 if (this.#hasValidationErrors()) { 82 return; 83 } 84 85 this.#form.submit(); 86 }, 50); 87 return; 88 } 89 } 90 91 if (this.#hasValidationErrors()) { 92 return; 93 } 94 95 this.#form.submit(); 96 }); 97 } 98 99 /** 100 * Check if the form has any validation errors, covering both native HTML5 101 * validation and powermail's JavaScript validation. 102 * 103 * Note: reportValidity() alone is not sufficient because it always returns 104 * true when the form has a "novalidate" attribute (i.e. when native 105 * validation is disabled via TypoScript validation.native = 0). 106 */ 107 #hasValidationErrors() { 108 if (!this.#form.reportValidity()) { 109 return true; 110 } 111 if (this.#form.classList.contains('powermail_form_error')) { 112 return true; 113 } 114 return false; 115 } 116 117 #fieldListener() { 118 const that = this; 119 const fields = this.#getFieldsFromForm(); 120 fields.forEach((field) => { 121 field.addEventListener('change', function(event) { 122 that.#sendFormValuesToPowermailCond(); 123 }) 124 }); 125 } 126 127 #sendFormValuesToPowermailCond () { 128 const that = this; 129 that.#enableAllFields(); 130 const dataToSend = new FormData(this.#form); 131 132 if (this.#form.hasAttribute(this.#excludedFieldsSelector)) { 133 // gather fields that should be excluded 134 const excludedFields = this.#form.querySelectorAll(this.#form.getAttribute(this.#excludedFieldsSelector)); 135 excludedFields.forEach(e => { 136 // and remove them from the payload being sent to the backend 137 if (e.hasAttribute('name')) { 138 dataToSend.delete(e.getAttribute('name')); 139 } 140 }); 141 } 142 143 fetch(this.#getAjaxUri(), {body: dataToSend, method: 'post'}) 144 .then((resp) => resp.json()) 145 .then(function(data) { 146 if (data.loops > data.loopLimit) { 147 console.log('Too much loops reached by parsing conditions and rules. Check for c
147onflicting conditions.'); 148 } else { 149 that.#processActions(data); 150 } 151 }) 152 .catch(function(error) { 153 console.log(error); 154 }); 155 }; 156 157 #processActions(data) { 158 if (data.todo !== undefined) { 159 for (let formUid in data.todo) { 160 for (let pageUid in data.todo[formUid]) { 161 162 // do actions with whole pages 163 if (data.todo[formUid][pageUid]['#action'] === 'hide') { 164 if (this.#isMoreStepForm) { 165 PowermailCondition.hideElement(this.#getMoreStepToggleByUid(pageUid)); 166 } 167 168 this.#hidePage(this.#getFieldsetByUid(pageUid)); 169 } 170 if (data.todo[formUid][pageUid]['#action'] === 'un_hide') { 171 if (this.#isMoreStepForm) { 172 PowermailCondition.showElement(this.#getMoreStepToggleByUid(pageUid)); 173 } 174 this.#showPage(this.#getFieldsetByUid(pageUid)); 175 } 176 177 // do actions with single fields 178 for (var fieldMarker in data.todo[formUid][pageUid]) { 179 if (data.todo[formUid][pageUid][fieldMarker]['#action'] === 'hide') { 180 this.#hideField(fieldMarker); 181 } 182 if (data.todo[formUid][pageUid][fieldMarker]['#action'] === 'un_hide') { 183 this.#showField(fieldMarker); 184 } 185 } 186 } 187 } 188 } 189 let fieldsets = this.#form.querySelectorAll('.powermail_fieldset'); 190 fieldsets.forEach(function(fieldset) { 191 if (window.getComputedStyle(fieldset).visibility === 'hidden') { 192 // Making initially invisible fieldset visible 193 fieldset.style.visibility = 'visible'; 194 fieldset.style.opacity = 1; 195 } 196 }); 197 }; 198 199 #enableAllFields() { 200 const fields = this.#form.querySelectorAll('[disabled="disabled"]'); 201 fields.forEach((field) => { 202 field.removeAttribute('disabled'); 203 }); 204 }; 205 206 #getFieldsFromForm() { 207 return this.#form.querySelectorAll( 208 'input:not([data-powermail-validation="disabled"]):not([type="hidden"]):not([type="submit"])' 209 + ', textarea:not([data-powermail-validation="disabled"])' 210 + ', select:not([data-powermail-validation="disabled"])' 211 ); 212 }; 213 214 #getAjaxUri() { 215 const container = document.querySelector('[data-condition-uri]'); 216 if (container === null) { 217 console.log('Tag with data-condition-uri not found. Maybe TypoScript was not included.'); 218 } 219 return container.getAttribute('data-condition-uri'); 220 }; 221 222 #showField(fieldMarker) { 223 let wrappingContainer = this.#getWrappingContainerByMarkerName(fieldMarker); 224 if (wrappingContainer !== null) { 225 PowermailCondition.showElement(wrappingContainer); 226 } 227 let field = this.#getFieldByMarker(fieldMarker); 228 if (field !== null) { 229 field.removeAttribute('disabled'); 230 this.#rerequireField(field); 231 } 232 }; 233 234 #hideField(fieldMarker) { 235 let wrappingContainer = this.#getWrappingContainerByMarkerName(fieldMarker); 236 if (wrappingContainer !== null) { 237 PowermailCondition.hideElement(wrappingContainer); 238 } 239 let field = this.#getFieldByMarker(fieldMarker); 240 if (field !== null) { 241 field.setAttribute('disabled', 'disabled'); 242 this.#derequireField(field); 243 } 244 }; 245 246 #showPage(page) { 247 page.classList.remove('powermail-cond-hidden'); 248 if (!this.#isMoreStepForm) { 249 PowermailCondition.showElement(page); 250 } 251 }; 252 253 #hidePage(page) { 254 if (this.#isMoreStepForm) { 255 page.classList.add('powermail-cond-hidden'); 256 } else { 257 PowermailCondition.hideElement(page); 258 } 259 }; 260 261 #derequireField(field) { 262 if (field.hasAttribute('required') || field.hasAttribute('data-powermail-required')) { 263 field.removeAttribute('required'); 264 field.removeAttribute('data-powermail-required'); 265 field.setAttribute('data-powermailcond-required', 'required'); 266 } 267 }; 268 269 #rerequireField(field) { 270 if (field.getAttribute('data-powermailcond-required') === 'required') { 271 if (this.#isHtml5ValidationActivated() || this.#isPowermailValidationActivated()) { 272 field.setAttribute('required', 'required') 273 } 274 } 275 field.removeAttribute('data-powermailcond-required'); 276 }; 277 278 #isPowermailValidationActivated() { 279 return this.#form.getAttribute('data-powermail-validate') === 'data-powermail-validate'; 280 }; 281 282 #isHtml5ValidationActivated() { 283 return this.#form.getAttribute('data-validate') === 'html5'; 284 }; 285 286 #getWrappingContainerByMarkerName(fieldMarker) { 287 let wrappingContainer = this.#getFieldwrappingContainerByMarker(fieldMarker); 288 if (wrappingContainer !== null) { 289 return wrappingContainer; 290 } 291 292 let field = this.#getFieldByMarker(fieldMarker); 293 if (field !== null) { 294 let wrappingContainer = field.closest('.powermail_fieldwrap'); 295 if (wrappingContainer !== null) { 296 return wrappingContainer; 297 } 298 } 299 300 console.log('Error: Could not find field by fieldMarker "' + fieldMarker + '"'); 301 return null; 302 }; 303 304 #getFieldByMarker(fieldMarker) { 305 let fieldName = 'tx_powermail_pi1[field][' + fieldMarker + ']'; 306 return this.#form.querySelector('[name="' + fieldName + '"]:not([type="hidden"])') || 307 this.#form.querySelector('[name="' + fieldName + '[]"]'); 308 }; 309 310 #getFieldsetByUid(pageUid) { 311 return this.#form.querySelector('.powermail_fieldset_' + pageUid); 312 }; 313 314 #getMoreStepToggleByUid(pageUid) { 315 return this.#form.querySelector(`.btn[data-powermail-fieldset="${pageUid}"]`) 316 } 317 318 #getFieldwrappingContainerByMarker(fieldMarker) { 319 return this.#form.querySelector('.powermail_fieldwrap_' + fieldMarker); 320 }; 321 322 static hideElement(element) { 323 if (element !== null) { 324 element.classList.add('powermail-cond-hidden'); 325 element.style.display = 'none'; 326 } 327 } 328 329 static showElement(element) { 330 if (element !== null) { 331 element.classList.remove('powermail-cond-hidden'); 332 element.style.display = ''; 333 } 334 } 335} 336 337// We use "pageshow" instead of ready/DOMContentLoaded because this event 338// specifically handles the backward/forward-navigation cache (bfcac
338he) 339// of browsers, so when someone returns to a already filled out form, 340// the values get checked properly instead of sendFormValuesToPowermailCond 341// receiving a practically empty initial form state. 342window.addEventListener('pageshow', () => { 343 const forms = document.querySelectorAll('.powermail_form'); 344 forms.forEach(function(form) { 345 let powermailConditions = new PowermailCondition(form); 346 powermailConditions.initialize(); 347 }); 348});
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.