©2024 Traveltodo - Tous droits réservés.
") !== -1
);
}
function sanitizeBasic(v){
if(!v) return "";
return String(v)
.replace(//g,"")
.replace(/script/gi,"")
.replace(/javascript:/gi,"")
.trim();
}
$("#ctnQuoteForm").on("input", "input[type='text'], input[type='email'], input[type='tel']", function(){
if (hasDangerousPayload(this.value)) this.value = sanitizeBasic(this.value);
});
// Phone plugin (single geoIpLookup callback)
$("#phone").intlTelInput({
initialCountry: "auto",
preferredCountries: ["tn", "dz", "ma", "fr"],
autoPlaceholder: "polite",
utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/12.1.6/js/utils.js",
formatOnDisplay: true,
autoHideDialCode: true,
geoIpLookup: function (cb) {
fetch("https://ipinfo.io/json", { cache: "reload" })
.then(r => r.ok ? r.json() : Promise.reject(r.status))
.then(d => cb(d && d.country ? d.country : "tn"))
.catch(() => cb("tn"));
}
});
function syncPhoneHidden(){
try{
var e164 = $("#phone").intlTelInput("getNumber"); // ex: +216XXXXXXXX
var c = $("#phone").intlTelInput("getSelectedCountryData");
$("#phoneE164").val(e164 || "");
$("#isoCountry").val(c && c.iso2 ? c.iso2 : "");
}catch(e){
$("#phoneE164").val("");
$("#isoCountry").val("");
}
}
$("#phone").on("keyup change blur countrychange", function(){
this.value = this.value.replace(/[^0-9+\-\s()]/g, "");
syncPhoneHidden();
});
// TripType radio feedback
function validateTripTypeUI(){
var ok = $("input[name='tripType']:checked").length > 0;
$("#tripTypeError").toggle(!ok);
return ok;
}
$("input[name='tripType']").on("change", validateTripTypeUI);
// Passengers UX
function refreshPassengerUI() {
var $rows = $("#passengersContainer .passenger-row");
var count = $rows.length;
$("#passengersCount").val(count);
$("#btnRemovePassenger").prop("disabled", count <= 1);
$rows.each(function(i){
var idx = i + 1;
$(this).attr("data-index", idx);
$(this).find(".passenger-badge span").text(idx);
});
}
function addPassengerRow() {
var count = $("#passengersContainer .passenger-row").length;
if (count >= 9) {
Swal.fire({ title: "Maximum 9 passagers", icon: "info", timer: 2000 });
return;
}
var nextIndex = count + 1;
var html = `
`;
$("#passengersContainer").append(html);
refreshPassengerUI();
}
function removePassengerRow() {
var count = $("#passengersContainer .passenger-row").length;
if (count <= 1) return;
$("#passengersContainer .passenger-row").last().remove();
refreshPassengerUI();
}
$("#btnAddPassenger").on("click", addPassengerRow);
$("#btnRemovePassenger").on("click", removePassengerRow);
refreshPassengerUI();
syncPhoneHidden();
$("#passengersCount").on("change keyup", function () {
var desired = parseInt($(this).val(), 10);
if (isNaN(desired) || desired < 1) desired = 1;
if (desired > 9) desired = 9;
var current = $("#passengersContainer .passenger-row").length;
while (current < desired) { addPassengerRow(); current++; }
while (current > desired) { removePassengerRow(); current--; }
refreshPassengerUI();
});
// reCAPTCHA v3 token
grecaptcha.ready(function() {
grecaptcha.execute('6Lc3d08aAAAAAMGdTvOOeMb3fu91rOp62qJM-5hv', {action: 'ctn_devis'}).then(function(token) {
if ($("input[name='g-recaptcha-response']").length === 0) {
$('#ctnQuoteForm').prepend('
');
} else {
$("input[name='g-recaptcha-response']").val(token);
}
});
});
// Submit AJAX (force JSON)
$("#ctnQuoteForm").submit(function (event) {
var form = $("#ctnQuoteForm")[0];
var tripOk = validateTripTypeUI();
// ensure phone hidden synced before sending
syncPhoneHidden();
if (!form.checkValidity() || !tripOk) {
event.preventDefault();
event.stopPropagation();
setTimeout(function(){
var $firstInvalid = $("#ctnQuoteForm .form-control:invalid, #ctnQuoteForm .form-select:invalid").first();
if (!$firstInvalid.length && !tripOk) {
$('html, body').animate({ scrollTop: $("#trip_oneway").offset().top - 120 }, 300);
$("#trip_oneway").focus();
return;
}
if ($firstInvalid.length) {
$('html, body').animate({ scrollTop: $firstInvalid.offset().top - 120 }, 300);
$firstInvalid.focus();
}
}, 50);
} else {
event.preventDefault();
event.stopPropagation();
var $f = $("#ctnQuoteForm");
var url = $f.attr("action");
var $btn = $("#ctnQuoteForm button[type='submit']");
var originalBtnText = $btn.text();
$.ajax({
type: "POST",
url: url,
data: $f.serialize(),
// IMPORTANT: on force la réponse en texte puis on parse nous-mêmes
dataType: "text",
beforeSend: function () {
$btn.attr("disabled", "disabled").html("Envoi en cours...");
},
success: function (respText) {
let data = null;
// 1) Try parse JSON
try {
data = JSON.parse(respText);
} catch (e) {
// si respText contient du texte parasite avant/après JSON
// on tente d'extraire le JSON
try {
const start = respText.indexOf("{");
const end = respText.lastIndexOf("}");
if (start !== -1 && end !== -1 && end > start) {
data = JSON.parse(respText.substring(start, end + 1));
}
} catch (e2) {}
}
// 2) Si toujours pas JSON => erreur lisible
if (!data) {
Swal.fire({
title: "Réponse invalide",
html: "
" + respText + "
",
icon: "error"
});
$btn.removeAttr("disabled").html(originalBtnText);
return;
}
// 3) Normaliser status
const status = (data.status || "").toString().trim().toLowerCase();
if (status === "success") {
Swal.fire({ title: data.message || "Succès", icon: "success", timer: 4000 });
// optionnel: reset
// $("#ctnQuoteForm")[0].reset();
} else {
Swal.fire({ title: data.message || "Erreur", icon: "error", timer: 4000 });
}
$btn.removeAttr("disabled").html(originalBtnText);
},
error: function (xhr) {
Swal.fire({
title: "Erreur serveur",
html: "
" + (xhr.responseText || "") + "
",
icon: "error"
});
$btn.removeAttr("disabled").html(originalBtnText);
}
});
}
$("#ctnQuoteForm").addClass("was-validated");
});