1class Formulaire { 2 /** 3 * Génère un formulaire à partir de sa représentation JSON 4 * @param {number} id - id du formulaire 5 * @param {HTMLElement} parent - élément englobant le formulaire 6 */ 7 constructor(id,parent) { 8 this.id = id 9 this.premiere_soumission = true 10 11 this.#charger(id).then(r => { 12 this.sructForm = r 13 this.form = document.createElement('form') 14 // this.form.setAttribute('novalidate', "") 15 16 // positionnement 17 r['blocs'].sort((a, b) => a.position - b.position) 18 19 r['blocs'].forEach(b => { 20 let champ = document.createElement('div') 21 champ.classList.add('champ') 22 23 if(b['type'] == 'form_texte' || b['type'] == 'form_email'){ 24 champ.classList.add(b['type']) 25 26 //required 27 let required = false // required par défaut 28 if(b.hasOwnProperty("required")) required = b["required"] 29 let required_etoile = required ? '<abbr class="requis" title="Ce champ est obligatoire">*</abbr>' : '' 30 31 let type = { 32 "form_texte" : "text", 33 "form_email" : "email" 34 } 35 champ.innerHTML = ` 36 <label 37 for="form_input_${id}-${b['id']}" 38 class="label-principal" 39 > 40 ${b['label']} 41 ${required_etoile} 42 </label> 43 <input 44 id="form_input_${id}-${b['id']}" 45 type="${type[b['type']]}" 46 name="form_input_${id}-${b['id']}" 47 ${required ? "required" : ""} 48 ${b.hasOwnProperty("placeholder") ? "placeholder=" + b["placeholder"] : ""} 49 ${b.hasOwnProperty("minlength") ? "minlength=" + b["minlength"] : ""} 50 ${b.hasOwnProperty("maxlength") ? "maxlength=" + b["maxlength"] : ""} 51 > 52 <div class="erreur" style="display: none"></div> 53 ` 54 55 // validation du champ 56 champ.querySelector('input').addEventListener("input", () => { 57 this.#validation_champ(champ) 58 }) 59 60 } 61 62 if(b['type'] == "form_radio"){ 63 champ.classList.add('form_radio') 64 65 //required 66 let required = false // required par défaut 67 if(b.hasOwnProperty("required")) required = b["required"] 68 let required_etoile = required ? '<abbr class="requis" title="Ce champ est obligatoire">*</abbr>' : '' 69 70 //options 71 let options = "" 72 b['options'].forEach(o => { 73 options += ` 74 <div class="option ${o['checked'] ? 'checked' : ""}"> 75 <input 76 type="radio" 77 id="form_input_${id}-${b['id']}-${o['id']}" 78 name="form_input_${id}-${b['id']}" 79 value="${o['id']}" 80 ${o['checked'] ? "checked" : ""} 81 ${required ? "required" : ""} 82 > 83 <label for="form_input_${id}-${b['id']}-${o['id']}"> 84 ${o['label']} 85 </label> 86 </div> 87 ` 88 }) 89 90 champ.innerHTML = ` 91 <label class="label-principal"> 92 ${b['label']} 93 ${required_etoile} 94 </label> 95 <div class="options"> 96 ${options} 97 </div> 98 <div class="erreur" style="display: none"></div> 99 ` 100 champ.querySelectorAll('.form_radio .options .option').forEach(o => { 101 o.addEventListener('change', () => { 102 if(champ.querySelector('.form_radio .options .option.checked') != undefined) champ.querySelector('.form_radio .options .option.checked').classList.remove('checked') 103 o.classList.add('checked') 104 champ.querySelector('div.erreur').style.display = "none" 105 }) 106 o.addEventListener('click', e => { 107 if(o.classList.contains("checked")){ 108 e.preventDefault() 109 o.classList.remove("checked") 110 o.querySelector('input').checked = false 111 } 112 this.#validation_champ(champ) 113 }) 114 }) 115 } 116 117 if(b['type'] == "form_select"){ 118 champ.classList.add('form_select') 119 120 //required 121 let required = false // required par défaut 122 if(b.hasOwnProperty("required")) required = b["required"] 123 let required_etoile = required ? '<abbr class="requis" title="Ce champ est obligatoire">*</abbr>' : '' 124 125 //options 126 127 function creer_option(o){ 128 return ` 129 <option 130 value="${o.id}" 131 ${o.disabled ? "disabled" : ""} 132 ${o.selected ? "selected" : ""} 133 >${o.label}</option> 134 ` 135 } 136 137 let options = `<option value="">${b.hasOwnProperty('optionNeutre') ? b['optionNeutre'] : " -- Sélectionnez une option -- "}</option>` 138 let position = 0 139 b['optgroups'].sort((a, b) => a.position - b.position) 140 b['options'].sort((a, b) => a.position - b.position) 141 b['optgroups'].forEach(optgroup => { 142 b['options'].forEach(o => { 143 if(o.position < optgroup.position && o.optgroup === undefined){ 144 options += creer_option(o) 145 } 146 }) 147 options += `<optgroup 148 label="${optgroup.label}" 149 ${optgroup.disabled ? "disabled" : ""} 150 >` 151 b['options'].forEach(o => { 152 if(o.optgroup == optgroup.label){ 153 options += creer_option(o) 154 } 155 }) 156 options += `</optgroup>` 157 position = optgroup.position 158 }) 159 b['options'].forEach(o => { 160 if(o.optgroup === undefined && position < o.position){ 161 options += creer_option(o) 162 } 163 }) 164 165 champ.innerHTML = ` 166 <label 167 class="label-principal" 168 for="form_input_${id}-${b['id']}" 169 > 170 ${b['label']} 171 ${required_etoile} 172 </label> 173 <select 174 id="form_input_${id}-${b['id']}" 175 name="form_input_${id}-${b['id']}" 176 ${b["required"] ? "required" : ""} 177 > 178 ${options} 179 </select> 180 <div class="erreur" style="display: none"></div> 181 ` 182 champ.querySelector('select').addEventListener("input", () => { 183 this.#validation_champ(champ) 184 }) 185 } 186 187 if(b['type'] == "form_bouton"){ 188 champ.classList.add('form_bouton') 189 let bouton = document.createElement('button'); 190 bouton.innerHTML = b['label']; 191 bouton.addEventListener('click', e => { 192 e.preventDefault() 193 this.soumettreFormulaire() 194 }) 195 champ.insertBefore(bouton, null); 196 } 197 this.form.insertBefore(champ, null) 198 }) 199 200 // message resultat à la fin du formulaire, succès ou erreur 201 let msgResultat = document.createElement('div') 202 msgResultat.classList.add('msg-resultat') 203 msgResultat.style.display = "none" 204 this.form.appendChild(msgResultat) 205 206 parent.appendChild(this.form) 207 }) 208 209 } 210 211 /** 212 * Vérifie si tous les champs requis sont remplis 213 * @return {Boolean} 214 */ 215 216 verifierChamps() { 217 let estValide = true; 218 // itérer à travers tous les champs de formulaire 219 this.form.querySelectorAll('.champ').forEach(champ => { 220 if(!this.#validation_champ(champ)){ 221 estValide = false 222 } 223 }); 224 return estValide; 225 } 226 227 soumettreFormulaire() { 228 this.premiere_soumission = false 229 if (this.verifierChamps()) { 230 let formData = new FormData(this.form); 231 formData.append('action', 'envoyer') 232 formData.append('id', this.id) 233 console.log(JSON.stringify(Object.fromEntries(formData))) 234 fetch("/script/page/formulaire.php", { 235 method: "POST", 236 headers: { 237 'Content-Type': 'application/json', 238 }, 239 body: JSON.stringify(Object.fromEntries(formData)) 240 }).then(reponse => reponse.json()).then(reponse => { 241 console.log(reponse) 242 let msgResultat = this.form.querySelector('.msg-resultat') 243 if(reponse.erreur){ 244 msgResultat.innerHTML = "Une erreur est survenue. Si elle perciste contactez l'administrateur. Code d'erreur : \"" + reponse.erreurMsg + "\"" 245 msgResultat.style.display = "block" 246 msgResultat.classList.add('erreur') 247 } else { 248 let msg = "Vos informations ont été enregistrées avec succès. Nous vous remercions." 249 let afficherFormulaire = false 250 if(this.sructForm.hasOwnProperty('succes')){ 251 if(this.sructForm['succes'].hasOwnProperty('msg')) msg = this.sructForm['succes']['msg'] 252 if(this.sructForm['succes'].hasOwnProperty('msg')) msg = this.sructForm['succes']['msg'] 253 } 254 msgResultat.innerHTML = msg 255 msgResultat.style.display = "block" 256 msgResultat.classList.add('succes') 257 if(!afficherFormulaire) this.form.querySelectorAll('.champ').forEach(c => c.style.display = "none") 258 } 259 }) 260 // .catch((error) => { 261 // // gérez les erreurs 262 // }); 263 } 264 } 265 266 async #charger(id){ 267 return fetch("/script/page/formulaire.php",{ 268 method: 'POST', 269 headers: { 270 'Content-Type': 'application/json', 271 }, 272 body: JSON.stringify({ 273 "action": "charger", 274 "id": id 275 }) 276 }).then((reponse) => { 277 return reponse.json() 278 }).then(reponse => { 279 return reponse 280 }) 281 } 282 283 284 #validation_champ(champ){ 285 if(champ.classList.contains('form_texte') 286 || champ.classList.contains('form_email') 287 || champ.classList.contains('form_select') 288 ){ 289 let input = champ.querySelector('input') 290 if(champ.classList.contains('form_select')) input = champ.querySelector('select') 291 let erreur = champ.querySelector('div.erreur') 292 293 if(!this.premiere_soumission){ 294 input.classList.add('erreur') 295 erreur.style.display = "block"; 296 } 297 298 // required 299 if (input.validity.valueMissing) { 300 erreur.innerHTML = "Ce champ doit être complété." 301 return false 302 } 303 304 // minlength 305 if (input.validity.tooShort){ 306 erreur.innerHTML = "Ce champ doit contenir au minimum " + input.getAttribute("minlength") + " caractères." 307 return false 308 } 309 310 // maxlength inutile html le bloc déjà 311 312 // typeMismatch (pattern) 313 if (input.validity.typeMismatch){ 314 if(champ.classList.contains('form_email')){ 315 erreur.innerHTML = "Ce champ ne contient pas une adresse mail valide." 316 }else{ 317 //faire la gestion ici pour le pattern des champs texte une fois implémenté 318 erreur.innerHTML = "Une erreur impossible est survenue, contactez l'administrateur." 319 } 320 321 return false 322 } 323 324 // si jamais pour une raison wtf l'erreur a pas été gérée avant 325 if (!input.validity.valid){ 326 erreur.innerHTML = "Ce champ comporte une erreur." 327 return false 328 } 329 330 // s'il y a pas de problème 331 input.classList.remove('erreur') 332 erreur.style.display = "none"; 333 } 334 if(champ.classList.contains('form_radio')){ 335 let erreur = champ.querySelector('div.erreur') 336 let radioBtn = champ.querySelectorAll('input') 337 let estCoche = false;
338 for (let i = 0; i < radioBtn.length; i++) { 339 if (radioBtn[i].checked) { 340 estCoche = true; 341 break; 342 } 343 } 344 345 if(radioBtn[0].hasAttribute("required")){ 346 if(!estCoche){ 347 erreur.innerHTML = "Vous devez sélectionner au moins un choix." 348 if(!this.premiere_soumission){ 349 erreur.style.display = "block"; 350 } 351 return false 352 } 353 } 354 } 355 return true 356 } 357} 358 359document.querySelectorAll('.contenu .bloc').forEach(b => { 360 if(b.innerHTML.split('-')[0] == "formulaire"){ 361 let id = b.innerHTML.split('-')[1] 362 b.innerHTML = "" 363 new Formulaire(id,b) 364 } 365})
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.