1document.addEventListener("DOMContentLoaded", () => { 2 // --- Trouve tous les formulaires GRAND et MINI, et instancie chacun séparément --- 3 const miniForms = Array.from(document.querySelectorAll("#mini-calculateur form")) 4 .filter(f => f.querySelector("#demarche")); 5 const grandForms = Array.from(document.querySelectorAll("form")) 6 .filter(f => !f.closest("#mini-calculateur") && f.querySelector("#demarche")); 7 8 grandForms.forEach(f => initCalc({ form: f, kind: "grand" })); 9 miniForms.forEach(f => initCalc({ form: f, kind: "mini" })); 10 11 function initCalc({ form, kind }) { 12 // Ãvite double binding si le script est inclus plusieurs fois 13 if (!form || form.dataset.cg_bound === "1") return; 14 form.dataset.cg_bound = "1"; 15 16 const IN_MINI = kind === "mini"; 17 const CONTAINER = IN_MINI ? (form.closest("#mini-calculateur") || form) : form; 18 19 // === Helpers SCOPÃS AU FORM === 20 const $id = (id) => form.querySelector(`#${id}`); 21 const $qs = (sel, base = form) => (base || form).querySelector(sel); 22 const $all = (sel, base = form) => Array.from((base || form).querySelectorAll(sel)); 23 const getNear = (id) => 24 CONTAINER.querySelector(`#${id}`) || form.querySelector(`#${id}`) || document.getElementById(id); 25 26 const setReq = (el, on) => 27 el?.querySelectorAll("input,select,textarea") 28 .forEach(i => on ? i.setAttribute("required","") : i.removeAttribute("required")); 29 30 const show = (id) => { const el = $id(id); if (!el) return; el.style.display = ""; setReq(el, true); }; 31 const hide = (id) => { const el = $id(id); if (!el) return; el.style.display = "none"; setReq(el, false); }; 32 33 // === Références (dans CE form uniquement) === 34 const details = $id("div_champs_sivin"); 35 const demarche = $id("demarche"); 36 const trigger = $id("details"); // <- toggle "immat inconnue" 37 const genreSel = $id("type_vehicule"); 38 const typeSel = $id("type_vehicule"); 39 const ptacSel = $id("ptac"); 40 const immatInput = $id("immatriculation"); 41 const toggleWrapper = $id("toggle-wrapper"); 42 const puissanceInput = form.querySelector('#puissance_fiscale, input[name="puissance_fiscale"]'); 43 if (puissanceInput && puissanceInput.type !== "hidden") { 44 puissanceInput.type = "number"; 45 puissanceInput.inputMode = "numeric"; 46 puissanceInput.min = "0"; 47 puissanceInput.step = "1"; 48 } 49 const co2Input = form.querySelector('#co2, input[name="co2"]'); 50 if (co2Input && co2Input.type !== "hidden") { 51 co2Input.type = "number"; 52 co2Input.inputMode = "numeric"; 53 co2Input.min = "0"; 54 co2Input.step = "1"; 55 } 56 57 if (!document.getElementById("cg-number-input-style")) { 58 const style = document.createElement("style"); 59 style.id = "cg-number-input-style"; 60 style.textContent = ` 61.form-details input[type="number"] { 62 width: 100%; 63 padding: 17px 12px; 64 border: 1px solid #ccc; 65 border-radius: 5px; 66 font-family: 'Roboto', sans-serif; 67 font-size: 16px; 68 margin-bottom: 15px; 69 transition: border-color 0.3s ease; 70 height: 53px; 71 box-sizing: border-box; 72 text-align: center; 73 color: #004399; 74} 75.form-details input[type="number"]::-webkit-outer-spin-button, 76.form-details input[type="number"]::-webkit-inner-spin-button { 77 -webkit-appearance: none; 78 margin: 0; 79} 80.form-details input[type="number"] { 81 -moz-appearance: textfield; 82} 83#bloc_masse_ordre_marche .detail-label { 84 width: 65%; 85} 86#bloc_masse_ordre_marche .indication { 87 width: 35%; 88} 89`; 90 document.head.appendChild(style); 91 } 92 93 // === Règles === 94 const ALL = [ 95 "bloc_neuf_occasion","bloc_genre","bloc_energie","bloc_puissance","bloc_date","bloc_co2", 96 "bloc_masse_ordre_marche","bloc_date_vente","bloc_heure_vente","bloc_ptac","bloc_j3", 97 "bloc_collection","bloc_adr4", 98 ]; 99 const RULES = { 100 1: ["bloc_genre","bloc_neuf_occasion","bloc_energie","bloc_puissance","bloc_date"], 101 "1_import": ["bloc_genre","bloc_energie","bloc_puissance","bloc_date","bloc_co2","bloc_masse_ordre_marche"], 102 "-1": ["bloc_genre","bloc_energie","bloc_puissance","bloc_co2","bloc_masse_ordre_marche"], 103 2: [], 3: [], 6: [], 7: [], 8: [], 104 4: [], 105 10:["bloc_genre","bloc_energie","bloc_puissance","bloc_date"], 106 9: ["bloc_date_vente","bloc_heure_vente"], 107 }; 108 const HIDE_TRIGGER_FOR = new Set(["-1","2","3","4","6","7","8","9","10","1_import"]); 109 110 const normalizeTarifDemarcheValue = (value) => 111 value === "1_import" || value === "1_succession" ? "1" : value; 112 113 // === UI helpers === 114 const isOpen = () => details && getComputedStyle(details).display !== "none"; 115 const openDetails = () =>
115 { if (!details) return; details.style.display = "flex"; details.setAttribute("aria-hidden","false"); }; 116 const closeDetails = () => { if (!details) return; details.style.display = "none"; details.setAttribute("aria-hidden","true"); }; 117 const hideAll = () => ALL.forEach(hide); 118 119 // === Métier === 120 const J3_GENRES = new Set(["7","10","11"]); // QM, TM, CYCL 121 const shouldShowJ3 = (val) => J3_GENRES.has(String(val)); 122 123 function applyJ3() { 124 if (!isOpen()) return; 125 const val = genreSel?.value || ""; 126 if (shouldShowJ3(val)) { show("bloc_j3"); show("bloc_collection"); } 127 else { 128 hide("bloc_j3"); hide("bloc_collection"); 129 const j3 = $id("j3_marchandise"); if (j3) j3.value = ""; 130 $all('input[name="collection"]').forEach(input => { 131 input.checked = false; 132 const label = form.querySelector(`label[for="${input.id}"]`); 133 if (label) label.classList.remove("active"); 134 }); 135 } 136 } 137 138 function applyImmatDisable() { 139 const d = demarche?.value || ""; 140 const isOccasionImportee = $qs('input[name="neuf_occasion"][value="occasion_importee"]')?.checked; 141 const nocHidden = $id("noc_hidden")?.value; 142 let mustDisable = false; 143 144 if (d === "1" || d === "1_succession") { 145 mustDisable = trigger?.checked === true || isOccasionImportee === true || nocHidden === "3"; 146 } 147 else mustDisable = d === "-1" || d === "10" || d === "1_import" || isOccasionImportee; 148 149 if (immatInput) { 150 if (mustDisable) { immatInput.value = ""; immatInput.disabled = true; } 151 else immatInput.disabled = false; 152 } 153 } 154 155 function updatePtac() { 156 const d = demarche?.value || ""; 157 const needGenre = !!RULES[d]?.includes("bloc_genre"); 158 const val = typeSel?.value; 159 const opt1 = form.querySelector('#ptac option[value="1"]'); 160 if (isOpen() && needGenre && (val === "12" || val === "4")) { 161 show("bloc_ptac"); 162 if (opt1) opt1.disabled = val === "12"; // CAM >3.5t 163 if (val === "12" && ptacSel?.value === "1") ptacSel.value = ""; 164 } else { 165 hide("bloc_ptac"); 166 if (ptacSel) ptacSel.value = ""; 167 } 168 } 169 170 function resetDetailsValues() { 171 $all("#div_champs_sivin input, #div_champs_sivin select, #div_champs_sivin textarea").forEach(el => { 172 if (["departement","immatriculation","demarche"].includes(el.id)) return; 173 if (el.type === "radio" || el.type === "checkbox") el.checked = false; 174 else el.value = ""; 175 el.classList?.remove("error"); 176 }); 177 $all("#div_champs_sivin label.active").forEach(l => l.classList.remove("active")); 178 const optLe35 = form.querySelector('#ptac option[value="1"]'); if (optLe35) optLe35.disabled = false; 179 } 180 181 function isSIV(immat) { 182 if (!immat) return false; 183 const s = String(immat).toUpperCase().replace(/[^A-Z0-9]/g,""); 184 return /^[A-Z]{2}\d{3}[A-Z]{2}$/.test(s) || /^WW\d{3,4}[A-Z]{2}$/.test(s); 185 } 186 187 function ensureDefaultDateIfNeuf() { 188 const d = demarche?.value || ""; 189 const dateInput = $id("mise_en_circulation"); 190 if (d === "-1" && dateInput && !dateInput.value) { 191 const t = new Date(); const yyyy=t.getFullYear(); const mm=String(t.getMonth()+1).padStart(2,"0"); const dd=String(t.getDate()).padStart(2,"0"); 192 dateInput.value = `${yyyy}-${mm}-${dd}`; 193 } 194 } 195 196 function applyAdr4() { 197 const d = demarche?.value || ""; 198 if (d !== "2") { hide("bloc_adr4"); return; } 199 const siv = isSIV(immatInput?.value || ""); 200 if (siv) { if (!isOpen()) openDetails(); show("bloc_adr4"); setReq($id("bloc_adr4"), false); } 201 else { 202 hide("bloc_adr4"); const cb = $id("adr4"); if (cb) cb.checked = false;
203 if ($all(".row-container").every(w => getComputedStyle(w).display === "none")) closeDetails(); 204 } 205 } 206 207 function applyForCurrent() { 208 const d = demarche?.value || ""; 209 const rulesKey = d === "1_import" || d === "1_succession" ? "1" : d; 210 211 if (toggleWrapper) { 212 toggleWrapper.style.display = (!d || d === "0") ? "none" : (HIDE_TRIGGER_FOR.has(d) ? "none" : "inline-flex"); 213 if (!d || HIDE_TRIGGER_FOR.has(d)) { const chk = $id("details"); if (chk) chk.checked = false; } 214 } 215 216 hideAll(); 217 const hasBlocks = !!(RULES[rulesKey] && RULES[rulesKey].length); 218 if (!hasBlocks) { closeDetails(); if (d === "2") applyAdr4(); return; } 219 220 if (d === "1_import") { if (!isOpen()) openDetails(); } 221 else if (d !== "1" && d !== "1_succession" && !isOpen()) { openDetails(); } 222 if (!isOpen()) return; 223 224 const rules = RULES[rulesKey] || []; 225 rules.forEac
225h(show); 226 227 if (d === "1_import") hide("bloc_neuf_occasion"); 228 if (d === "1" || d === "1_succession") { 229 hide("bloc_neuf_occasion"); 230 const fr = $qs('input[name="neuf_occasion"][value="occasion_fr"]'); 231 const imp = $qs('input[name="neuf_occasion"][value="occasion_importee"]'); 232 if (fr) fr.value = "0"; if (imp) imp.value = "0"; 233 hide("bloc_co2"); 234 hide("bloc_masse_ordre_marche"); 235 } 236 if (d === "1_import") { 237 show("bloc_co2"); 238 show("bloc_masse_ordre_marche"); 239 } 240 241 applyJ3(); updatePtac(); applyAdr4(); 242 } 243 244 function forceUnknownImmat() { 245 if (toggleWrapper) toggleWrapper.style.display = "inline-flex"; 246 if (trigger) { 247 trigger.checked = true; 248 trigger.dispatchEvent(new Event("change", { bubbles: true })); 249 return; 250 } 251 if (!isOpen()) openDetails(); 252 applyForCurrent(); 253 } 254 255 // === AAA & util === 256 const ensureHidden = (name) => { 257 let el = form.querySelector(`input[name="${name}"]`); 258 if (!el) { el = document.createElement("input"); el.type = "hidden"; el.name = name; form.appendChild(el); } 259 return el; 260 }; 261 const hf_chevaux = ensureHidden("chevaux_fiscaux"); 262 const hf_date = ensureHidden("date_immatriculation"); 263 const hf_marque = ensureHidden("marque"); 264 const hf_modele = ensureHidden("modele"); 265 const hf_siv_id = ensureHidden("siv_id"); 266 267 const isWWPlate = (s) => /^WW[\s-]*\d{3,4}[\s-]*[A-Z]{2}$/i.test(String(s || "").trim()); 268 const shouldUseAAA = () => { 269 const d = demarche?.value; 270 const immat = immatInput?.value?.trim(); 271 if (!immat) return false; 272 if (isWWPlate(immat)) return false; 273 if (d === "-1" || d === "10") return false; 274 if (form.dataset.aaaFetched === "1") return false; 275 return true; 276 }; 277 278 // Variable pour stocker le jeton de sécurité 279 let securityToken = ""; 280 let securityTokenExpiresAt = 0; 281 let securityTokenPromise = null; 282 const TOKEN_REFRESH_MARGIN = 120; 283 284 function loadSecurityToken(forceRefresh = false) { 285 const now = Math.floor(Date.now() / 1000); 286 if (!forceRefresh && securityToken && securityTokenExpiresAt > now + TOKEN_REFRESH_MARGIN) { 287 return Promise.resolve(securityToken); 288 } 289 if (!forceRefresh && securityTokenPromise) { 290 return securityTokenPromise; 291 } 292 293 securityTokenPromise = fetch((typeof url_site !== "undefined" ? url_site : "/") + "js/jQuery/init_session.php", { 294 method: "GET", 295 headers: { "Accept": "application/json" }, 296 credentials: "include" 297 }) 298 .then(res => res.json()) 299 .then(response => { 300 securityToken = response.token || ""; 301 securityTokenExpiresAt = Number(response.expires_at) || now + 3600; 302 console.log("Session initialisée."); 303 return securityToken; 304 }) 305 .catch(err => { 306 console.error("Impossible d'initialiser la session de sécurité.", err); 307 return securityToken; 308 }) 309 .finally(() => { 310 securityTokenPromise = null; 311 }); 312 313 return securityTokenPromise; 314 } 315 316 // On initialise la session au chargement, puis on rafraîchit le token si besoin avant chaque appel protégé. 317 loadSecurityToken(true); 318 319 /*function fetchAAA(immat) { 320 return new Promise((resolve, reject) => { 321 $.ajax({ 322 dataType: "jsonp", 323 url: (typeof url_site !== "undefined" ? url_site : "/") + "js/jQuery/getDonneesImmatriculation.php", 324 data: { immatriculation: immat }, 325 success: (response) => resolve(response || {}), 326 error: (xhr) => reject(xhr), 327 }); 328 }); 329 }*/ 330 /* STOP jsonp. C'est une vieille technique; passer en POST rend l'appel impossible via la barre d'adresse du navigateur */ 331 async function fetchAAA(immat) { 332 const token = await loadSecurityToken(); 333 334 return new Promise((resolve, reject) => { 335 336 // Si le token n'est pas encore arrivé (page chargée trop vite), on attend ou on bloque 337 if (!token) { 338 console.warn("Token de sécurité manquant"); 339 // Optionnel : retenter de le chercher ou rejeter 340 } 341 342 $.ajax({ 343 type: "POST", 344 dataType: "json", 345 url: (typeof url_site !== "undefined" ? url_site : "/") + "js/jQuery/getDonneesImmatriculation_v2.php", 346 data: { 347 immatriculation: immat, 348 token: token 349 }, 350 xhrFields: { 351 withCredentials: true // Important pour passer les cookies de session (Token) 352 }, 353 success: (response) => resolve(response || {}), 354 error: (xhr) => reject(xhr), 355 }); 356 }); 357 } 358 359 function ensureVNDefaults() { 360 const d = demarche?.value || ""; 361 let noc = form.querySelector("#noc_hidden"); 362 if (!noc) { noc = document.createElement("input"); noc.type="hidden"; noc.name="neuf_occasion"; noc.id="noc_hidden"; form.appendChild(noc); } 363 364 if (d === "-1") { 365 noc.value = "1"; 366 ensureHidden("ptac").value = "1"; 367 ensureHidden("reception").value = "1"; 368 const cp = ensureHidden("codepostal"); 369 if (!cp.value) cp.value = $id("departement")?.value || ""; 370 } 371 if (d === "1" || d === "1_succession") noc.value = "2"; 372 if (d === "1_import") noc.value = "3"; 373 } 374 375 function setCollectionChoice(value) {
376 $all('input[name="collection"]').forEach(input => { 377 const checked = input.value === value; 378 input.checked = checked; 379 const label = form.querySelector(`label[for="${input.id}"]`); 380 if (label) label.classList.toggle("active", checked); 381 }); 382 } 383 384 function syncSpecialDemarcheFlags() { 385 ensureHidden("succession").value = demarche?.value === "1_succession" ? "oui" : "non"; 386 if (demarche?.value === "7") setCollectionChoice("oui"); 387 } 388 389 function syncDateForBack() { 390 const dateInput = $id("mise_en_circulation"); 391 const hidden = ensureHidden("date_immatriculation"); 392 if (!dateInput || !hidden || !dateInput.value) return; 393 const parts = dateInput.value.split("-"); 394 if (parts.length === 3) hidden.value = `${parts[0]}-${parts[1]}-${parts[2]}`; 395 } 396 397 function syncCO2ForBack() { 398 const visible = $id("co2"); 399 const hidden = ensureHidden("co2"); 400 hidden.type = "text"; 401 hidden.value = visible && visible.value ? visible.value : ""; 402 } 403 404 function ensureReception() { const r = ensureHidden("reception"); if (!r.value) r.value = "1"; } 405 406 function applyImmatName() { 407 const d = demarche?.value || ""; 408 if (!immatInput) return; 409 immatInput.setAttribute("name", d === "9" ? "immatriculation_declaration_cession" : "immatriculation"); 410 } 411 412 function applyAAA(resp) { 413 const sivId = resp?.siv_id ?? resp?.id ?? ""; 414 hf_siv_id.value = sivId ? String(sivId) : ""; 415 } 416 417 function needsMoreUserInputAfterAAA() { 418 const g = typeSel?.value || ""; 419 const blocPtac = $id("bloc_ptac"); 420 const blocJ3 = $id("bloc_j3"); 421 const blocCollection = $id("bloc_collection"); 422 423 const ptacVisible = blocPtac && getComputedStyle(blocPtac).display !== "none"; 424 const j3Visible = blocJ3 && getComputedStyle(blocJ3).display !== "none"; 425 const collVisible = blocCollection && getComputedStyle(blocCollection).display !== "none"; 426 427 const needPTAC = (g === "12" || g === "4") && ptacVisible && !ptacSel?.value; 428 const needJ3 = j3Visible && !$id("j3_marchandise")?.value; 429 const needCollection = collVisible && !$all('input[name="collection"]:checked').length; 430 return needPTAC || needJ3 || needCollection; 431 } 432 433 /*function loadCryptoJS() { 434 return new Promise((resolve, reject) => { 435 if (window.CryptoJS) { 436 resolve(); // Déjà chargé 437 return; 438 } 439 const script = document.createElement('script'); 440 script.src = "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"; 441 script.onload = () => resolve(); 442 script.onerror = () => reject(new Error("Erreur chargement CryptoJS")); 443 document.head.appendChild(script); 444 }); 445 }*/ 446 447 async function calculateTarifAndRedirect() { 448 ensureVNDefaults(); ensureReception(); syncDateForBack(); syncCO2ForBack(); syncSpecialDemarcheFlags(); 449 450 const loader = getNear("loader"); if (loader) loader.style.display = "flex"; 451 452 const originalDemarcheValue = demarche?.value || ""; 453 const originalDemarcheLabel = demarche?.options?.[demarche.selectedIndex]?.text?.trim() || ""; 454 const serializedDemarcheValue = normalizeTarifDemarcheValue(originalDemarcheValue); 455 let s = ""; 456 457 if (demarche && serializedDemarcheValue !== originalDemarcheValue) demarche.value = serializedDemarcheValue; 458 $all('input[name="neuf_occasion"]:not(#noc_hidden)').forEach(r => (r.disabled = true)); 459 try { 460 s = $(form).serialize(); 461 } finally { 462 $all('input[name="neuf_occasion"]:not(#noc_hidden)').forEach(r => (r.disabled = false)); 463 if (demarche && serializedDemarcheValue !== originalDemarcheValue) demarche.value = originalDemarcheValue; 464 } 465 466 const failSafe = setTimeout(() => { 467 if (loader && loader.style.display === "flex") { 468 loader.style.display = "none"; 469 const box = getNear("resultat"); 470 if (box) { box.innerHTML = "Le serveur ne répond pas ou a retourné une erreur. Merci de réessayer."; box.style.display = "block"; } 471 } 472 }, 6000); 473 474 // OPTIMISATION : On s'assure que le token est chargé et encore valide. 475 const token = await loadSecurityToken(); 476 477 if (!token) { 478 console.warn("Token de sécurité manquant"); 479 } 480 481 $.ajax({ 482 type: "POST", 483 dataType: "json", 484 url: (typeof url_site !== "undefined" ? url_site : "/") + "js/jQuery/getTarifCalculateur_v3.php", 485 data: { 486 param: s, 487 token: token // Le jeton CSRF en clair suffit 488 }, 489 xhrFields: { 490 withCredentials: true 491 }, 492 success: function (response) { 493 clearTimeout(failSafe); 494 if (loader) loader.style.display = "none"; 495 496 if (!response || response.erreur) { 497 const box = getNear("resultat"); 498 const msg = " " + (response?.erreur || "Erreur inconnue du serveur") + " "; 499 if (box) { box.innerHTML = msg; box.style.display = "block"; } else { alert(msg); } 500 return; 501 } 502 503 const action = typeof url_site_form_result !== "undefined" ? url_site_form_result : 504 typeof url_site !== "undefined" ? url_site : "/"; 505 506 const f = document.createElement("form"); 507 f.method = "post"; f.action = action; f.style.display = "none"; document.body.appendChild(f); 508 509 const add = (n, v) => { const i = document.createElement("input"); i.type="hidden"; i.name=n; i.value = v ?? ""; f.appendChild(i); }; 510 add("warning", response.message); 511 add("total", response.tarif);
512 add("y1", response.y1); add("y2", response.y2); add("y3", response.y3); add("y4", response.y4); add("y5", response.y5); 513 add("id", response.id); 514 add("cg_data_cgm", s); // Tu envoies bien 's' brut ici 515 add("demarche_source", originalDemarcheValue); 516 add("demarche_label", originalDemarcheLabel); 517 add("referer", form.querySelector('input[name="referer"]')?.value || ""); 518 add("interface", form.querySelector('input[name="interface"]')?.value || ""); 519 f.submit(); 520 }, 521 error: function (xhr) { 522 clearTimeout(failSafe); 523 if (loader) loader.style.display = "none"; 524 const box = getNear("resultat"); 525 const msg = "ERREUR réseau\nCode " + xhr.status; 526 if (box) { box.innerHTML = msg; box.style.display = "block"; } else { alert(msg); } 527 }, 528 }); 529 } 530 531 async function handleSmartSubmit(e) { 532 if (form.dataset.autosubmit === "1") return; // 2e clic 533 e.preventDefault(); 534 535 const loader = getNear("loader"); if (loader) loader.style.display = "flex"; 536 537 const d = demarche?.value || ""; 538 const immat = immatInput?.value?.trim() || ""; 539 540 // Déclaration de cession 541 if (d === "9") { 542 const action = "https://www.declaration-cession-vehicule.fr/enregistrement-cession.php"; 543 const f = document.createElement("form"); 544 f.method = "post"; f.action = action; f.style.display = "none"; document.body.appendChild(f); 545 const add = (n,v) => { const i=document.createElement("input"); i.type="hidden"; i.name=n; i.value=v||""; f.appendChild(i); }; 546 add("immatriculation_declaration_cession", form.querySelector('[name="immatriculation_declaration_cession"]')?.value || ""); 547 add("date_declaration_cession", form.querySelector('[name="date_declaration_cession"]')?.value || ""); 548 add("heure_declaration_cession", form.querySelector('[name="heure_declaration_cession"]')?.value || ""); 549 add("interface", form.querySelector('[name="interface"]')?.value || ""); 550 add("marque", form.querySelector('[name="marque"]')?.value || ""); 551 add("modele", form.querySelector('[name="modele"]')?.value || ""); 552 f.submit(); 553 return; 554 } 555 556 ensureDefaultDateIfNeuf(); 557 ensureVNDefaults(); 558 559 // Domicile sans immat => direct 560 if (d === "2" && !immat) { calculateTarifAndRedirect(); return; } 561 562 // Pas d'appel VIN => direct 563 if (!shouldUseAAA()) { calculateTarifAndRedirect(); return; } 564 565 try { 566 const resp = await fetchAAA(immat); 567 if (resp.erreur && d !== "2") { 568 if (loader) loader.style.display = "none"; 569 const errMsg = String(resp.erreur || ""); 570 const box = getNear("resultat"); 571 if (box) { box.innerHTML = errMsg; box.style.display = "block"; } else { alert(errMsg); } 572 form.dataset.aaaFetched = "1"; 573 const isUnknown = /inconnue|inconnu/i.test(errMsg); 574 if (isUnknown) forceUnknownImmat(); 575 return; 576 } 577 applyAAA(resp); 578 form.dataset.aaaFetched = "1"; 579 580 applyForCurrent(); 581 if (!needsMoreUserInputAfterAAA()) { calculateTarifAndRedirect(); return; } 582 if (!isOpen()) openDetails(); 583 } catch (err) { 584 if (loader) loader.style.display = "none"; 585 if (d === "2") calculateTarifAndRedirect(); 586 else { if (!isOpen()) openDetails(); } 587 } 588 } 589 590 // === EVENTS (scopés au form) === 591 form.addEventListener("submit", handleSmartSubmit, true); 592 593 demarche?.addEventListener("change", () => { 594 resetDetailsValues(); 595 if (demarche.value === "2" && immatInput) { immatInput.value = ""; immatInput.classList.remove("error"); } 596 if (trigger) trigger.checked = false; // on remet le toggle à 0 597 closeDetails(); applyForCurrent(); applyImmatDisable(); applyImmatName(); syncSpecialDemarcheFlags(); 598 }); 599
600 $all('input[name="neuf_occasion"]').forEach(r => 601 r.addEventListener("change", () => { 602 if (["1","1_succession"].includes(demarche?.value) && isOpen()) applyForCurrent(); 603 applyImmatDisable(); 604 }) 605 ); 606 607 immatInput?.addEventListener("input", applyAdr4); 608 immatInput?.addEventListener("blur", applyAdr4); 609 610 immatInput?.addEventListener("input", () => { 611 form.dataset.aaaFetched = "0"; 612 hf_siv_id.value = ""; 613 const box = getNear("resultat"); if (box) box.style.display = "none"; 614 }); 615 616 $all('input[name="neuf_ou_occasion"]').forEach(r => 617 r.addEventListener("change", () => { if (["1","1_succession"].includes(demarche?.value) && isOpen()) applyForCurrent(); }) 618 ); 619 620 form.addEventListener("submit", () => { 621 $all(".row-container").forEach(w => { if (getComputedStyle(w).display === "none") setReq(w, false); }); 622 }); 623 624 $all('.form-details input[type="radio"]').forEach(radio => { 625 radio.addEventListener("change", function () { 626 const g = this.name; 627 $all(`input[name="${g}"]`).forEach(i => form.querySelector(`label[for="${i.id}"]`)?.classList.remove("active")); 628 form.querySelector(`label[for="${this.id}"]`)?.classList.add("active"); 629 }); 630 }); 631 632 genreSel?.addEventListener("change", () => { applyJ3(); updatePtac(); }); 633 typeSel?.addEventListener("change", updatePtac); 634 635 // ð Toggle "Immatriculation inconnue" (MANQUANT AVANT) 636 trigger?.addEventListener("change", () => { 637 const d = demarche?.value || ""; 638 if (!toggleWrapper || toggleWrapper.style.display === "none") return; 639 640 if (trigger.checked) { 641 resetDetailsValues(); 642 openDetails(); 643 applyForCurrent(); // affiche les blocs dynamiques selon la démarche 644 details?.scrollIntoView({ behavior: "smooth", block: "nearest" }); 645 applyImmatDisable(); // désactive l'input immat 646 } else { 647 closeDetails(); 648 hideAll(); 649 if (immatInput) immatInput.value = ""; // on vide lâimmat si on repasse en mode connu 650 applyImmatDisable(); // réactive si besoin 651 } 652 }); 653 654 // === INITS === 655 closeDetails(); 656 if (toggleWrapper) toggleWrapper.style.display = "none"; 657 658 // Remplit les départements (dans CE form seulement) 659 (function populateDepartements() { 660 const sel = $id("departement"); if (!sel) return; 661 const data = [ 662 { code: "01", nom: "Ain" }, { code: "02", nom: "Aisne" }, { code: "03", nom: "Allier" }, 663 { code: "04", nom: "Alpes-de-Haute-Provence" }, { code: "05", nom: "Hautes-Alpes" }, 664 { code: "06", nom: "Alpes-Maritimes" }, { code: "07", nom: "Ardèche" }, { code: "08", nom: "Ardennes" }, 665 { code: "09", nom: "Ariège" }, { code: "10", nom: "Aube" }, { code: "11", nom: "Aude" }, 666 { code: "12", nom: "Aveyron" }, { code: "13", nom: "Bouches-du-Rhône" }, { code: "14", nom: "Calvados" }, 667 { code: "15", nom: "Cantal" }, { code: "16", nom: "Charente" }, { code: "17", nom: "Charente-Maritime" }, 668 { code: "18", nom: "Cher" }, { code: "19", nom: "Corrèze" }, { code: "2A", nom: "Corse-du-Sud" }, 669 { code: "2B", nom: "Haute-Corse" }, { code: "21", nom: "Côte-d'Or" }, { code: "22", nom: "Côtes-d'Armor" }, 670 { code: "23", nom: "Creuse" }, { code: "24", nom: "Dordogne" }, { code: "25", nom: "Doubs" }, 671 { code: "26", nom: "Drôme" }, { code: "27", nom: "Eure" }, { code: "28", nom: "Eure-et-Loir" }, 672 { code: "29", nom: "Finistère" }, { code: "30", nom: "Gard" }, { code: "31", nom: "Haute-Garonne" }, 673 { code: "32", nom: "Gers" }, { code: "33", nom: "Gironde" }, { code: "34", nom: "Hérault" }, 674 { code: "35", nom: "Ille-et-Vilaine" }, { code: "36", nom: "Indre" }, { code: "37", nom: "Indre-et-Loire" }, 675 { code: "38", nom: "Isère" }, { code: "39", nom: "Jura" }, { code: "40", nom: "Landes" }, 676 { code: "41", nom: "Loir-et-Cher" }, { code: "42", nom: "Loire" }, { code: "43", nom: "Haute-Loire" }, 677 { code: "44", nom: "Loire-Atlantique" }, { code: "45", nom: "Loiret" }, { code: "46", nom: "Lot" }, 678 { code: "47", nom: "Lot-et-Garonne" }, { code: "48", nom: "Lozère" }, { code: "49", nom: "Maine-et-Loire" }, 679 { code: "50", nom: "Manche" }, { code: "51", nom: "Marne" }, { code: "52", nom: "Haute-Marne" }, 680 { code: "53", nom: "Mayenne" }, { code: "54", nom: "Meurthe-et-Moselle" }, { code: "55", nom: "Meuse" }, 681 { code: "56", nom: "Morbihan" }, { code: "57", nom: "Moselle" }, { code: "58", nom: "Nièvre" }, 682 { code: "59", nom: "Nord" }, { code: "60", nom: "Oise" }, { code: "61", nom: "Orne" }, 683 { code: "62", nom: "Pas-de-Calais" }, { code: "63", nom: "Puy-de-Dôme" }, { code: "64", nom: "Pyrénées-Atlantiques" }, 684 { code: "65", nom: "Hautes-Pyrénées" }, { code: "66", nom: "Pyrénées-Orientales" }, { code: "67", nom: "Bas-Rhin" }, 685 { code: "68", nom: "Haut-Rhin" }, { code: "69", nom: "Rhône" }, { code: "70", nom: "Haute-Saône" }, 686 { code: "71", nom: "Saône-et-Loire" }, { code: "72", nom: "Sarthe" }, { code: "73", nom: "Savoie" }, 687 { code: "74", nom: "Haute-Savoie" }, { code: "75", nom: "Paris" }, { code: "76", nom: "Seine-Maritime" }, 688 { code: "77", nom: "Seine-et-Marne" }, { code: "78", nom: "Yvelines" }, { code: "79", nom: "Deux-Sèvres" }, 689 { code: "80", nom: "Somme" }, { code: "81", nom: "Tarn" }, { code: "82", nom: "Tarn-et-Garonne" }, 690 { code: "83", nom: "Var" }, { code: "84", nom: "Vaucluse" }, { code: "85", nom: "Vendée" }, 691 { code: "86", nom: "Vienne" }, { code: "87", nom: "Haute-Vienne" }, { code: "88", nom: "Vosges" }, 692 { code: "89", nom: "Yonne" }, { code: "90", nom: "Territoire de Belfort" }, { code: "91", nom: "Essonne" }, 693 { code: "92", nom: "Hauts-de-Seine" }, { code: "93", nom: "Seine-Saint-Denis" }
693, { code: "94", nom: "Val-de-Marne" }, 694 { code: "95", nom: "Val-d'Oise" }, { code: "971", nom: "Guadeloupe" }, { code: "972", nom: "Martinique" }, 695 { code: "973", nom: "Guyane" }, { code: "974", nom: "La Réunion" }, { code: "976", nom: "Mayotte" }, 696 ]; 697 sel.innerHTML = '<option value="" selected disabled hidden>Sélectionner votre département</option>'; 698 data.forEach(d => sel.appendChild(new Option(`${d.code} - ${d.nom}`, d.code))); 699 })(); 700 701 (function populateGenreOptions() { 702 const sel = $id("type_vehicule"); if (!sel) return; 703 const prev = sel.value; 704 const GENRE = [ 705 { v: "1", t: "Voiture (VP)" }, 706 { v: "2", t: "Véhicule société / utilitaire (CTTE / Deriv-VP)" }, 707 { sep: true }, 708 { v: "5", t: "Motocyclette (MTL, MTT1, MTT2, MTTE, MOTO)" }, 709 { v: "6", t: "Vélomoteur et Cyclomoteur <= 50cm3 (CL)" }, 710 { sep: true }, 711 { v: "12", t: "Camion (CAM) > 3,5t" }, 712 { v: "13", t: "Bus(TCP) > 3,5t" }, 713 { v: "14", t: "Tracteur Routier (TRR) > 3,5t" }, 714 { v: "4", t: "Véhicule spécialisé (VASP)" }, 715 { sep: true }, 716 { v: "15", t: "Remorque (REM) ou semi-remorque (SREM)" }, 717 { v: "16", t: "Caravane (RESP)" }, 718 { sep: true }, 719 { v: "10", t: "Tricycles à moteur (TM)" }, 720 { v: "11", t: "Cyclomoteur carrossé à 3 roues (voiturette) (CYCL)" }, 721 { v: "7", t: "Quadricycles à moteur: quad, voiturette (QM)" }, 722 { sep: true }, 723 { v: "8", t: "Engin agricole (TRA, Quad, MAGA)" }, 724 ]; 725 sel.innerHTML = '<option value="" selected disabled hidden>Sélectionner</option>'; 726 GENRE.forEach(x => { 727 const o = document.createElement("option"); 728 if (x.sep) { o.value="0"; o.textContent="ââââââââââ"; o.disabled=true; o.hidden=true; } 729 else { o.value=x.v; o.textContent=x.t; } 730 sel.appendChild(o); 731 }); 732 if (prev && sel.querySelector(`option[value="${prev}"]`)) sel.value = prev; 733 })(); 734 735 // GRAND uniquement : auto sélection de la démarche par URL (CGM + CGO) 736 (function autoSelectDemarcheByURL() { 737 if (!demarche) return; 738 try { 739 const params = new URLSearchParams(window.location.search); 740 const forced = params.get("demarche"); 741 if (forced) { 742 const opt = demarche.querySelector(`option[value="${forced}"]`); 743 if (opt) { demarche.value = forced; demarche.dispatchEvent(new Event("change")); return; } 744 } 745 } catch (e) {} 746 const url = window.location.href.toLowerCase(); 747 const mappingCGM = { 748 "vos-demarches-changement-de-titulaire-carte-grise": "1", 749 "vos-demarches-carte-grise-heritage": "1_succession", 750 "vos-demarches-carte-grise-vehicule-neuf": "-1", 751 "vos-demarches-changement-d-adresse-carte-grise": "2", 752 "vos-demarches-changement-statut-matrimonial": "3", 753 "vos-demarches-duplicata-carte-grise": "4", 754 "vos-demarches-carte-grise-import-etranger": "1_import", 755 "vos-demarches-carte-grise-collection": "7", 756 "vos-demarches-immatriculation-voiture-location": "8", 757 }; 758 const mappingCGO = { 759 "mode_emploi_changement_proprietaire": "1", 760 "mode_emploi_changement_domicile": "2", 761 "mode_emploi_duplicata": "4", 762 "mode_emploi_achat_vehicule_neuf": "-1", 763 "importation_vehicule_etranger": "1_import", 764 "certificat-cession": "9", 765 }; 766 const mappings = [mappingCGM, mappingCGO]; 767 let selectedValue = null; 768 for (const m of mappings) { 769 for (const key in m) { if (url.includes(key)) { selectedValue = m[key]; break; } } 770 if (selectedValue) break; 771 } 772 if (selectedValue) { 773 const opt = demarche.querySelector(`option[value="${selectedValue}"]`); 774 if (opt) { demarche.value = selectedValue; demarche.dispatchEvent(new Event("change")); } 775 } 776 })(); 777 778 // MINI uniquement : pré-sélection du département via ?dept=XX 779 if (IN_MINI) {
780 (function preselectDepartementFromURLForMini() { 781 try { 782 const params = new URLSearchParams(window.location.search); 783 const dept = params.get("dept"); 784 const sel = $id("departement"); 785 if (!dept || !sel) return; 786 const opt = sel.querySelector(`option[value="${dept}"]`); 787 if (opt) { sel.value = dept; sel.dispatchEvent(new Event("change")); } 788 } catch(e) { /* silencieux */ } 789 })(); 790 } 791 792 // Si le toggle est déjà coché au chargement (rare), on aligne l'UI 793 syncSpecialDemarcheFlags(); 794 if (trigger?.checked) { openDetails(); applyForCurrent(); applyImmatDisable(); } 795 } // fin initCalc 796});
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.