/* ------------------------------------------------------------------------ */ /* CONSENTEMENT AU DEMARCHAGE - loi n 2025-594 du 30/06/2025 - DEV-4823 */ /* */ /* Pour les sites en generation LibSource (prototype + scriptaculous), qui */ /* ne chargent NI jQuery NI Load.js : l'injection du parc n'a aucune prise */ /* sur eux. Ce bloc est donc autonome, en JS natif, et se pose dans */ /* config.js, seul fichier charge par TOUTES les pages de ces sites. */ /* */ /* Cote serveur il n'y a RIEN a faire : lib_mail.php / GetConsentDemarchage()*/ /* lit deja $_REQUEST["consentement_demarchage" | "_tel" | "_email"], et */ /* RecMail() - appele par MailContact(), MailDeposer() et MailEnregistrer(), */ /* les handlers de ces formulaires - l'appelle. Les dates sont horodatees */ /* par le SERVEUR, jamais par le navigateur. */ /* ------------------------------------------------------------------------ */ (function() { var Reglages = { Responsable : "PROXIMMO - GR IMMOBILIER", UrlPpd : "http://www.ppd-rgpd.com/fr/34356/34356" }; var Textes = { Intro : "J'accepte que mes donn\u00e9es personnelles soient trait\u00e9es \u00e0 des fins de prospection commerciale :", LibTel : "Par t\u00e9l\u00e9phone", LibEmail : "Par courrier \u00e9lectronique", AriaTel : "J'accepte d'\u00eatre contact\u00e9(e) par t\u00e9l\u00e9phone \u00e0 des fins de prospection commerciale.", AriaEmail: "J'accepte d'\u00eatre contact\u00e9(e) par courrier \u00e9lectronique (e-mail) \u00e0 des fins de prospection commerciale.", Legal1 : "Les donn\u00e9es collect\u00e9es par le pr\u00e9sent formulaire sont trait\u00e9es par ", Legal2 : ", responsable du traitement, aux fins de gestion de votre demande. Vous disposez de droits d'acc\u00e8s, de rectification, d'effacement, de limitation, d'opposition et de portabilit\u00e9 sur vos donn\u00e9es, ainsi que du droit de d\u00e9finir des directives sur leur sort apr\u00e8s votre d\u00e9c\u00e8s. Pour exercer ces droits ou en savoir plus sur le traitement de vos donn\u00e9es, leur dur\u00e9e de conservation et leurs destinataires, consultez notre ", LibPpd : "politique de protection des donn\u00e9es", Legal3 : ". Vous pouvez \u00e9galement introduire une r\u00e9clamation aupr\u00e8s de la CNIL." }; // Hors perimetre : moteurs de recherche, simulateurs, points d'interet, // authentification, desinscription, rappel, et l'envoi a un ami que le cahier // des charges formulaires exclut explicitement du RGPD. Meme liste que Load.js. var HorsPerimetre = /Ami|Search|Calc|Poi|Login|Forget|Pass|Unsubscribe|Resa|DetailEnova|Newsletter|Rappel|MyaCreate|MiniMoteur|Dept/i; // Une case ne se pose que si le canal est REELLEMENT collecte. email2 est la // confirmation de saisie, pas une seconde coordonnee : elle n'est pas listee. var ChampsTel = ["tel", "telephone", "telport", "tel_portable", "rappel_tel"]; var ChampsEmail = ["email", "mail"]; function Echappe(T) { return String(T).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } function ChampPresent(Form, Noms) { for(var i = 0; i < Noms.length; i++) { var E = Form.elements[Noms[i]]; if(E && (!E.length || E.length > 0)) return true; } return false; } // Le bouton d'envoi de ces gabarits n'est PAS un input[type=submit] : c'est // une qui porte onclick="ValiderFormulaire('FormContact')". On cherche // donc d'abord les vrais boutons, puis tout element dont le onclick declenche // un envoi. Detection par le COMPORTEMENT, jamais par le nom du formulaire : // les 4 sites ne sont pas identiques - proximmo34 passe par Adapt.Form.Submit // et ne charge meme pas fonction.js. function TrouveEnvoi(Form) { var Vrai = Form.querySelector("input[type='submit'],button[type='submit'],input[type='image']"); if(Vrai) return Vrai; var Candidats = Form.querySelectorAll("[onclick]"); for(var i = 0; i < Candidats.length; i++) { var Code = Candidats[i].getAttribute("onclick") || ""; if(/ValiderFormulaire|Adapt\.Form\.Submit|ValiderAlertEmail|\.submit\s*\(/i.test(Code)) return Candidats[i]; } return null; } function NbColonnes(Ligne) { var N = 0; for(var i = 0; i < Ligne.cells.length; i++) N += (Ligne.cells[i].colSpan || 1); return N || 1; } function PoseStyle() { if(document.getElementById("consent-demarchage-css")) return; var S = document.createElement("style"); S.id = "consent-demarchage-css"; S.type = "text/css"; S.appendChild(document.createTextNode( ".form-ppd{margin:8px 0;text-align:left;line-height:1.4}" + ".form-ppd-intro{margin:0 0 4px 0;font-weight:bold}" + ".form-ppd-cases{display:block}" + ".form-ppd-consentement{display:inline-block;margin:0 18px 2px 0;font-weight:normal;cursor:pointer}" + ".form-ppd-consentement input{vertical-align:middle;margin:0 5px 0 0}" + ".form-ppd-legal{margin:8px 0;text-align:left;font-size:11px;line-height:1.4}" + ".form-ppd-legal a{text-decoration:underline}" )); (document.head || document.getElementsByTagName("head")[0] || document.body).appendChild(S); } function ConstruitBloc(ATel, AEmail) { var D = document.createElement("div"); D.className = "form-ppd form-critere"; var H = '

' + Echappe(Textes.Intro) + '

'; if(ATel) H += ''; if(AEmail) H += ''; D.innerHTML = H + ''; return D; } function ConstruitLegal() { var P = document.createElement("p"); P.className = "form-ppd-legal"; P.innerHTML = Echappe(Textes.Legal1) + Echappe(Reglages.Responsable) + Echappe(Textes.Legal2) + '' + Echappe(Textes.LibPpd) + '' + Echappe(Textes.Legal3); return P; } // Les cases AU-DESSUS du bouton d'envoi, le bloc juridique EN DESSOUS : c'est // la disposition validee sur le parc, et celle que verifie le banc. Sur ces // gabarits en , "au-dessus" veut dire une inseree avant la ligne // du bouton ; "en dessous", apres le tableau mais TOUJOURS dans le , // sinon les cases ne seraient pas serialisees. function Pose(Form, Bloc, Legal, Envoi) { var Ligne = null, N = Envoi; while(N && N !== Form) { if(N.tagName === "TR") { Ligne = N; break; } N = N.parentNode; } if(Ligne && Ligne.parentNode) { var Tr = document.createElement("tr"); var Td = document.createElement("td"); Td.colSpan = NbColonnes(Ligne); Td.appendChild(Bloc); Tr.appendChild(Td); Ligne.parentNode.insertBefore(Tr, Ligne); var Tableau = Ligne; while(Tableau && Tableau !== Form && Tableau.tagName !== "TABLE") Tableau = Tableau.parentNode; if(Tableau && Tableau.tagName === "TABLE" && Tableau.parentNode) Tableau.parentNode.insertBefore(Legal, Tableau.nextSibling); else Form.appendChild(Legal); return; } if(Envoi && Envoi.parentNode) { Envoi.parentNode.insertBefore(Bloc, Envoi); if(Envoi.nextSibling) Envoi.parentNode.insertBefore(Legal, Envoi.nextSibling); else Envoi.parentNode.appendChild(Legal); return; } Form.appendChild(Bloc); Form.appendChild(Legal); } /* -------------------------------------------------------------------- */ /* LISIBILITE */ /* Sur certains gabarits le bloc herite d'un gris clair : sur kylia-immo */ /* l'intro et la case sortaient a #999 sur blanc, soit 2,85 - sous le */ /* seuil WCAG AA de 4,5. On ne repeint QUE si la mesure le demande. */ /* */ /* Le piege connu du parc : un ancetre qui peint une IMAGE de fond rend */ /* la couleur de fond mesuree fausse (famille bg-white90, ratios a 1,19 */ /* partout alors que le texte est parfaitement lisible). On exige donc */ /* une CORROBORATION : un texte du site lui-meme, voisin du bloc, doit */ /* etre lisible sur ce meme fond. S'il ne l'est pas, c'est la mesure qui */ /* est fausse, pas la couleur - et on s'abstient. */ /* -------------------------------------------------------------------- */ function Rvba(C) { var M = String(C).match(/[\d.]+/g); if(!M || M.length < 3) return null; return [ +M[0], +M[1], +M[2], (M.length > 3) ? +M[3] : 1 ]; } function Luminance(C) { var A = [ C[0], C[1], C[2] ].map(function(V) { V = V / 255; return (V <= 0.03928) ? (V / 12.92) : Math.pow((V + 0.055) / 1.055, 2.4); }); return 0.2126 * A[0] + 0.7152 * A[1] + 0.0722 * A[2]; } function Ratio(A, B) { var a = Luminance(A) + 0.05, b = Luminance(B) + 0.05; return (a > b) ? (a / b) : (b / a); } // On remonte jusqu'au premier fond OPAQUE. Si une IMAGE de fond est rencontree en // chemin, la couleur trouvee ne dit plus ce qui est reellement peint : c'est le // faux positif classique du parc (famille bg-white90 : ratios a 1,19 partout alors // que le texte est parfaitement lisible). On rend alors null, et on s'abstient. function FondOpaque(El) { var N = El; while(N && N.nodeType === 1) { var S = window.getComputedStyle(N); if((S.backgroundImage || "none") !== "none") return null; var F = Rvba(S.backgroundColor); if(F && F[3] >= 1) return F; N = N.parentNode; } return null; } function AssureLisibilite(Bloc, Form) { var Fond = FondOpaque(Bloc); if(!Fond) return; // Corroboration : un texte du site, VISIBLE et voisin, doit etre lisible sur // ce fond. Deux pieges rencontres : // - le champ antispam ("N'entrez rien dans ce champ") est masque ET en #999 : // pris comme reference il faisait echouer la corroboration a tort, et le // bloc restait illisible. Constate le 17/09/2026 sur la beta de kylia-immo. // - un conteneur herite du texte de ses enfants : on n'accepte que les // elements qui portent EUX-MEMES du texte. // On cherche d'abord dans le formulaire, puis en remontant depuis le bloc. function TexteEnPropre(E) { for(var n = 0; n < E.childNodes.length; n++) if(E.childNodes[n].nodeType === 3 && E.childNodes[n].nodeValue.replace(/\s/g, "").length) return true; return false; } function Visible(E) { var S = window.getComputedStyle(E); if(S.visibility === "hidden" || S.display === "none") return false; return !!(E.offsetWidth || E.offsetHeight || E.getClientRects().length); } // Le repere doit etre sur LE MEME FOND que notre bloc, sinon on compare des // choses qui n'ont rien a voir. Sur kylia-immo, le formulaire d'alerte // s'etend sur un bandeau NOIR et sur le blanc de la page : la premiere // version prenait un texte gris du bandeau noir - parfaitement lisible la // ou il est - et en concluait que notre gris sur blanc etait acceptable. function MemeFond(E) { var F = FondOpaque(E); return !!(F && F[0] === Fond[0] && F[1] === Fond[1] && F[2] === Fond[2]); } // On retient le MEILLEUR contraste parmi les textes du site poses sur ce meme // fond, pas le premier venu : le premier peut etre un texte pale isole, alors // que le site prouve ailleurs qu'il sait ecrire lisiblement sur ce fond. function ChercheRef(Racine) { var L = Racine.querySelectorAll("td,label,p,span,div,a,h1,h2,h3,strong,li"), Best = null; for(var v = 0; v < L.length; v++) { if(Bloc.contains(L[v]) || !TexteEnPropre(L[v]) || !Visible(L[v])) continue; if(!MemeFond(L[v])) continue; var C = Rvba(window.getComputedStyle(L[v]).color); if(C && (!Best || Ratio(C, Fond) > Ratio(Best, Fond))) Best = C; } return Best; } var Ref = ChercheRef(Form), Amont = Bloc.parentNode, Niv = 0; while(!Ref && Amont && Amont.nodeType === 1 && Niv < 4) { Ref = ChercheRef(Amont); Amont = Amont.parentNode; Niv++; } if(!Ref || Ratio(Ref, Fond) < 3) return; // mesure non fiable : on ne touche a rien var Choix = (Ratio([0, 0, 0, 1], Fond) >= Ratio([255, 255, 255, 1], Fond)) ? "#000" : "#fff"; var Cibles = [ Bloc ].concat(Array.prototype.slice.call(Bloc.querySelectorAll("*"))); for(var i = 0; i < Cibles.length; i++) { var T = Rvba(window.getComputedStyle(Cibles[i]).color); if(!T || Ratio(T, Fond) >= 4.5) continue; Cibles[i].style.setProperty("color", Choix, "important"); } } function Traite(Form) { if(!Form || Form.getAttribute("data-consent-demarchage")) return; var Nom = Form.getAttribute("name") || Form.getAttribute("id") || ""; if(HorsPerimetre.test(Nom)) return; var ATel = ChampPresent(Form, ChampsTel), AEmail = ChampPresent(Form, ChampsEmail); if(!ATel && !AEmail) return; // aucune coordonnee : rien a demander // Deja pose (page regeneree avec le bloc, ou double execution) : on sort. if(Form.elements["consentement_demarchage"]) return; var Envoi = TrouveEnvoi(Form); Form.setAttribute("data-consent-demarchage", "1"); PoseStyle(); // Le marqueur. INDISPENSABLE : une case decochee n'est pas transmise par le // navigateur, sans lui le serveur ne distingue pas un refus explicite d'un // ancien formulaire qui n'a jamais pose la question. var Marqueur = document.createElement("input"); Marqueur.type = "hidden"; Marqueur.name = "consentement_demarchage"; Marqueur.value = "1"; Form.appendChild(Marqueur); var Bloc = ConstruitBloc(ATel, AEmail), Legal = ConstruitLegal(); Pose(Form, Bloc, Legal, Envoi); AssureLisibilite(Bloc, Form); AssureLisibilite(Legal, Form); } function Parcours() { var F = document.getElementsByTagName("form"); for(var i = 0; i < F.length; i++) Traite(F[i]); } if(document.readyState === "complete" || document.readyState === "interactive") Parcours(); else if(document.addEventListener) document.addEventListener("DOMContentLoaded", Parcours, false); else window.onload = Parcours; })(); // AJAX // // Mode Ajax ModeAjax = true; // Tableau des variables invisibles dans le hash var TabHiddenHashVar = Array("merci","reecrit","DomaineAgence","langue","titre","img1","img2","img3","img4","img5"); // FORMULAIRE // // FormSearchSubmit // Type de champs var FormSearchDataType = Array( { id:'prixmin', lib:'Prix min', type:'Numeric' }, { id:'prixmax', lib:'Prix max', type:'Numeric' }, { id:'ref', lib:'Référence', type:'Numeric' } ); // Paramétrage var FormSearchSubmit = {DataType:FormSearchDataType,DisableEmpty:true}; // FormSearchBigSubmit // Type de champs var FormSearchBigDataType = Array( { id:'prixmin', lib:'Budget min', type:'Numeric' }, { id:'prixmax', lib:'Budget max', type:'Numeric' }, { id:'termin', lib:'Surface terrain min', type:'Numeric' }, { id:'habmin', lib:'Surface habitable min', type:'Numeric' }, { id:'ref', lib:'Référence', type:'Numeric' } ); // Champs par défault var FormSearchBigDefault = Array( { id:'motscles', val:'Par exemple : Piscine, ascenseur, ...' }, { id:'ville1', val:'Par exemple : Montpellier, 34000, ...' } ); // Paramétrage var FormSearchBigSubmit = { DisableEmpty:true, DataType:FormSearchBigDataType, Default:FormSearchBigDefault }; // FormDeposerSubmit // Type de champs var FormDeposerDataType = Array( { id:'surfhab', lib:'Surface habitable', type:'Numeric' }, { id:'surfterrain', lib:'Surface terrain', type:'Numeric' }, { id:'budget', lib:'Budget', type:'Numeric' }, { id:'email', lib:'E-mail', type:'Email' } ); // Champs obligatoires var FormDeposerMandatory = Array( { id:'nom', lib:'Nom' }, { id:'tel', lib:'Téléphone' }, { id:'email', lib:'Votre e-mail' }, { id:'email2', lib:'Ressaisissez votre e-mail' } ); // Champs similaires var FormDeposerSimilar = Array( { id1:'email', lib1:'Votre e-mail', id2:'email2', lib2:'Ressaisissez votre e-mail' } ); // Paramétrage var FormDeposerSubmit = { AntiSpam:true, DisableEmpty:true, DataType:FormDeposerDataType, Mandatory:FormDeposerMandatory, Similar:FormDeposerSimilar }; // FormEnregistrerSubmit // Type de champs var FormEnregistrerDataType = Array( { id:'surfhab', lib:'Surface habitable', type:'Numeric' }, { id:'surfterrain', lib:'Surface terrain', type:'Numeric' }, { id:'budget', lib:'Budget', type:'Numeric' }, { id:'email', lib:'E-mail', type:'Email' } ); // Champs obligatoires var FormEnregistrerMandatory = Array( { id:'nom', lib:'Nom' }, { id:'tel', lib:'Téléphone' }, { id:'email', lib:'Votre e-mail' }, { id:'email2', lib:'Ressaisissez votre e-mail' } ); // Champs similaires var FormEnregistrerSimilar = Array( { id1:'email', lib1:'Votre e-mail', id2:'email2', lib2:'Ressaisissez votre e-mail' } ); // Paramétrage FormEnregistrerSubmit = { AntiSpam:true, DisableEmpty:true, DataType:FormEnregistrerDataType, Mandatory:FormEnregistrerMandatory, Similar:FormEnregistrerSimilar }; // FormContactSubmit // Type de champs var FormContactDataType = Array( { id:'email', lib:'E-mail', type:'Email' } ); // Champs obligatoires var FormContactMandatory = Array( { id:'nom', lib:'Nom' }, { id:'tel', lib:'Téléphone' }, { id:'email', lib:'Votre e-mail' }, { id:'email2', lib:'Ressaisissez votre e-mail' } ); // Champs similaires var FormContactSimilar = Array( { id1:'email', lib1:'Votre e-mail', id2:'email2', lib2:'Ressaisissez votre e-mail' } ); // Paramétrage var FormContactSubmit = { AntiSpam:true, DisableEmpty:true, DataType:FormContactDataType, Mandatory:FormContactMandatory, Similar:FormContactSimilar }; // FormAlertEmailSubmit // Type de champs var FormAlertEmailDataType = Array( { id:'email', lib:'E-mail', type:'Email' } ); // Champs obligatoires var FormAlertEmailMandatory = Array( { id:'email', lib:'Votre e-mail' } ); // Champs par défault var FormAlertEmailDefault = Array( { id:'email', val:'> Votre e-mail' } ); // Paramétrage var FormAlertEmailSubmit = { AntiSpam:true, DisableEmpty:true, Default:FormAlertEmailDefault, DataType:FormAlertEmailDataType, Mandatory:FormAlertEmailMandatory }; // FormMiniMoteurSubmit // Type de champs var FormMiniMoteurDataType = Array( { id:'habmin', lib:'Surface habitable min', type:'Numeric' }, { id:'habmax', lib:'Surface habitable max', type:'Numeric' }, { id:'termin', lib:'Surface terrain min', type:'Numeric' }, { id:'termax', lib:'Surface terrain max', type:'Numeric' }, { id:'ref', lib:'Référence', type:'Numeric' }, { id:'prixmin', lib:'Budget min', type:'Numeric' }, { id:'prixmax', lib:'Budget max', type:'Numeric' } ); // Champs par défault var FormMiniMoteurDefault = Array( { id:'motclef', val:'Par exemple : Piscine, ascenseur, ...' }, { id:'ville1', val:'Par exemple : Montpellier, 34000' } ); // Paramétrage var FormMiniMoteurSubmit = { DisableEmpty:true, Default:FormMiniMoteurDefault, DataType:FormMiniMoteurDataType }; // FormContactDetailSubmit // Type de champs var FormDetailContactDataType = Array( { id:'email', lib:'E-mail', type:'Email' } ); // Champs obligatoires var FormDetailContactMandatory = Array( { id:'nom', lib:'Nom' }, { id:'tel', lib:'Téléphone' }, { id:'email', lib:'Votre e-mail' }, { id:'email2', lib:'Ressaisissez votre e-mail' } ); // Champs similaires var FormDetailContactSimilar = Array( { id1:'email', lib1:'Votre e-mail', id2:'email2', lib2:'Ressaisissez votre e-mail' } ); // Paramétrage var FormDetailContactSubmit = { AntiSpam:true, DisableEmpty:true, DataType:FormDetailContactDataType, Mandatory:FormDetailContactMandatory, Similar:FormDetailContactSimilar }; // FormAmiSubmit // Type de champs var FormAmiDataType = Array( { id:'mail', lib:'Destinataire', type:'Email' }, { id:'expediteur', lib:'Expediteur', type:'Email' } ); // Champs obligatoires var FormAmiMandatory = Array( { id:'mail', lib:'Destinataire' }, { id:'expediteur', lib:'Expediteur' } ); // Champs par défault var FormAmiDefault = Array( { id:'mail', val:'> Adresse e-mail de votre ami' }, { id:'expediteur', val:'> Votre adresse e-mail' } ); // Paramétrage var FormAmiSubmit = { AntiSpam:false, DisableEmpty:false, DataType:FormAmiDataType, Mandatory:FormAmiMandatory, Default:FormAmiDefault }; // FormAnnuaireSubmit // Type de champs var FormAnnuaireDataType = Array( { id:'mail', lib:'Destinataire', type:'Email' }, { id:'expediteur', lib:'Expediteur', type:'Email' } ); // Champs obligatoires var FormAnnuaireMandatory = Array( { id:'nomSite', lib:'Nom Du site' }, { id:'email', lib:'Votre e-mail' }, { id:'email', lib:'Votre e-mail' }, { id:'email2', lib:'Ressaisissez votre e-mail' } ); // Champs similaires var FormAnnuaireSimilar = Array( { id1:'email', lib1:'Votre e-mail', id2:'email2', lib2:'Ressaisissez votre e-mail' } ); // Champs par défault var FormAnnuaireDefault = Array( { id:'urlPage', val:'http://' }, { id:'urlSite', val:'http://' } ); // Paramétrage var FormAnnuaireSubmit = { AntiSpam:false, DisableEmpty:false, DataType:FormAnnuaireDataType, Mandatory:FormAnnuaireMandatory, Similar:FormAnnuaireSimilar, Default:FormAnnuaireDefault };