{"id":13096,"date":"2022-08-25T21:32:23","date_gmt":"2022-08-26T01:32:23","guid":{"rendered":"https:\/\/allianceenjesus.com\/?page_id=13096"},"modified":"2026-08-04T17:51:33","modified_gmt":"2026-08-04T21:51:33","slug":"a-test","status":"publish","type":"page","link":"https:\/\/allianceenjesus.com\/?page_id=13096","title":{"rendered":"A TEST"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"13096\" class=\"elementor elementor-13096\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3df0466 e-flex e-con-boxed elementor-repeater-item-default_padding elementor-repeater-item-none elementor-repeater-item-none_hover wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-column-slider-no wpr-equal-height-no e-con e-parent\" data-id=\"3df0466\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;ang_container_spacing_size&quot;:&quot;default_padding&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-187f84e elementor-repeater-item-none elementor-repeater-item-none_hover elementor-widget elementor-widget-html\" data-id=\"187f84e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<p>Test<\/p>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-faa4a4b e-flex e-con-boxed elementor-repeater-item-default_padding elementor-repeater-item-none elementor-repeater-item-none_hover wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no wpr-column-slider-no wpr-equal-height-no e-con e-parent\" data-id=\"faa4a4b\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;ang_container_spacing_size&quot;:&quot;default_padding&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-50bd500 elementor-repeater-item-none elementor-repeater-item-none_hover elementor-widget elementor-widget-html\" data-id=\"50bd500\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!--\n    CATALOGUE FILMS \u2014 version WordPress \/ Elementor\n    =================================================\n    A coller tel quel dans un widget \"HTML\" (ou \"Code HTML\")\n    d'Elementor. Ne PAS coller <!DOCTYPE>, <html>, <head> ou\n    <body> \u2014 il n'y en a plus ici, uniquement le contenu du\n    widget.\n\n    Changements par rapport \u00e0 la version autonome :\n    - Toutes les r\u00e8gles CSS sont scop\u00e9es sous .wdfc-app pour\n      ne pas d\u00e9teindre sur le reste du th\u00e8me (les s\u00e9lecteurs\n      globaux comme \"button\", \"input, select\", \":root\" du\n      fichier d'origine s'appliquaient \u00e0 TOUT le site).\n    - Tous les id=\"\" sont pr\u00e9fix\u00e9s \"wdfc-\" pour \u00e9viter les\n      collisions avec d'autres widgets\/plugins de la page.\n    - Tout le JavaScript est encapsul\u00e9 dans une seule IIFE\n      (au lieu de variables globales comme currentPage,\n      currentResults, searchWikidata...) et d\u00e9marre apr\u00e8s le\n      DOM pr\u00eat.\n    - Le hack \"pleine largeur\" (width:100vw + translateX) a\n      \u00e9t\u00e9 retir\u00e9 car il casse la mise en page \u00e0 l'int\u00e9rieur\n      des conteneurs Elementor. Pour un rendu pleine largeur,\n      utilisez plut\u00f4t l'option native \"Stretch Section\" \/\n      \"\u00c9tirer la section\" d'Elementor sur la section qui\n      contient ce widget.\n    - Le reste (logique de recherche Wikidata, module TMDB,\n      export CSV, vue Cin\u00e9ma, etc.) est inchang\u00e9.\n\n    \u26a0\ufe0f La cl\u00e9 API TMDB reste visible dans le code source c\u00f4t\u00e9\n    navigateur (comme dans l'original) : n'importe qui peut la\n    lire en ouvrant l'inspecteur. Si c'est un probl\u00e8me pour\n    vous, il faudrait faire passer les appels TMDB par un\n    endpoint c\u00f4t\u00e9 serveur (ex. un shortcode PHP ou un plugin\n    \"code snippet\" qui masque la cl\u00e9) \u2014 dites-le moi si vous\n    voulez cette version.\n\n    Limite : ce widget utilise des id fixes, donc n'en placez\n    qu'UNE seule instance par page (deux copies sur la m\u00eame\n    page cr\u00e9eraient des id dupliqu\u00e9s et casseraient le script).\n-->\n\n<div class=\"wdfc-app\">\n\n<style>\n\n.wdfc-app, .wdfc-app * {\n    box-sizing: border-box;\n}\n\n.wdfc-app {\n    color-scheme: dark;\n    font-family: Arial, Helvetica, sans-serif !important;\n    background: #0b0b0d !important;\n    color: #fff !important;\n    margin: 0;\n    padding: 25px;\n}\n\n.wdfc-app .search-box {\n    background: #1d1d1d;\n    border-radius: 14px;\n    padding: 20px;\n    margin-bottom: 20px;\n}\n\n.wdfc-app .field-row {\n    display: grid;\n    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n    gap: 12px;\n    margin-bottom: 16px;\n}\n\n.wdfc-app .field-row:last-child { margin-bottom: 0; }\n\n.wdfc-app .field-row.search-row {\n    grid-template-columns: 3fr 1.4fr auto;\n}\n\n.wdfc-app .field label {\n    display: block;\n    font-size: 11.5px;\n    text-transform: uppercase;\n    letter-spacing: .4px;\n    opacity: .55;\n    margin-bottom: 5px;\n    color: #fff !important;\n}\n\n.wdfc-app .field.full-width { grid-column: 1 \/ -1; }\n\n.wdfc-app input,\n.wdfc-app select {\n    width: 100%;\n    padding: 12px;\n    border-radius: 8px;\n    border: 1px solid #444;\n    background: #111 !important;\n    color: #fff !important;\n    font-size: 14px;\n}\n\n.wdfc-app select[multiple] { padding: 8px; }\n\n.wdfc-app select:disabled,\n.wdfc-app input:disabled {\n    opacity: .4;\n    cursor: not-allowed;\n}\n\n.wdfc-app .field-hint {\n    font-size: 11.5px;\n    opacity: .55;\n    margin-top: 6px;\n    color: #fff !important;\n}\n\n.wdfc-app button {\n    padding: 12px 18px;\n    border: 0;\n    border-radius: 8px;\n    background: #f0c400 !important;\n    color: #111 !important;\n    font-weight: bold;\n    cursor: pointer;\n}\n\n.wdfc-app button:hover { opacity: .85; }\n.wdfc-app button:disabled { opacity: .4; cursor: not-allowed; }\n\n.wdfc-app .youtube-button { background: #ff0000 !important; color: #fff !important; }\n.wdfc-app .cinema-button { background: #2c2c30 !important; color: #fff !important; border: 1px solid #f0c400; }\n\n.wdfc-app .toggle-filter {\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    padding: 12px;\n    border-radius: 8px;\n    border: 1px solid #444;\n    background: #111;\n    cursor: pointer;\n    user-select: none;\n}\n\n.wdfc-app .toggle-filter input {\n    width: 18px;\n    height: 18px;\n    accent-color: #f0c400;\n    cursor: pointer;\n}\n\n.wdfc-app .toggle-filter span { font-size: 13.5px; color: #fff !important; }\n\n.wdfc-app .lexical-box {\n    border: 1px solid #444;\n    border-radius: 8px;\n    padding: 12px;\n    background: #111;\n}\n\n.wdfc-app .lexical-box legend {\n    font-size: 11.5px;\n    text-transform: uppercase;\n    letter-spacing: .4px;\n    opacity: .65;\n    padding: 0 6px;\n    color: #fff !important;\n}\n\n.wdfc-app .lexical-options {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 12px;\n    margin-top: 6px;\n}\n\n.wdfc-app .lexical-options label {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 13px;\n    cursor: pointer;\n    color: #fff !important;\n}\n\n.wdfc-app .lexical-options input {\n    width: 16px;\n    height: 16px;\n    accent-color: #f0c400;\n    cursor: pointer;\n}\n\n.wdfc-app .language-options {\n    max-height: 190px;\n    overflow-y: auto;\n    border: 1px solid #444;\n    border-radius: 8px;\n    padding: 12px;\n    background: #111;\n    display: flex;\n    flex-wrap: wrap;\n    gap: 10px 18px;\n}\n\n.wdfc-app .language-options.is-disabled {\n    opacity: .4;\n    pointer-events: none;\n}\n\n.wdfc-app .language-options label {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 13px;\n    cursor: pointer;\n    color: #fff !important;\n}\n\n.wdfc-app .language-options input {\n    width: 16px;\n    height: 16px;\n    accent-color: #f0c400;\n    cursor: pointer;\n}\n\n.wdfc-app .status { margin: 20px 0; text-align: center; min-height: 25px; color: #fff !important; }\n\n.wdfc-app .progress {\n    width: 100%;\n    height: 8px;\n    background: #333;\n    border-radius: 10px;\n    overflow: hidden;\n    margin-top: 15px;\n}\n\n.wdfc-app .progress-bar {\n    width: 0%;\n    height: 100%;\n    background: #f0c400;\n    transition: width .2s;\n}\n\n.wdfc-app .results {\n    display: grid;\n    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));\n    gap: 24px;\n}\n\n.wdfc-app .card {\n    position: relative;\n    background: #1a1a1d;\n    border-radius: 14px;\n    overflow: hidden;\n    box-shadow: 0 4px 14px rgba(0,0,0,.4);\n    transition: transform .25s ease, box-shadow .25s ease;\n    display: flex;\n    flex-direction: column;\n}\n\n.wdfc-app .card:hover {\n    transform: translateY(-6px);\n    box-shadow: 0 14px 30px rgba(0,0,0,.6);\n}\n\n.wdfc-app .poster-wrap {\n    position: relative;\n    width: 100%;\n    aspect-ratio: 2\/3;\n    background: #222;\n    overflow: hidden;\n}\n\n.wdfc-app .poster {\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    display: block;\n    transition: transform .35s ease;\n}\n\n.wdfc-app .card:hover .poster { transform: scale(1.06); }\n\n.wdfc-app .poster-fallback-title {\n    position: absolute;\n    left: 0;\n    right: 0;\n    top: 71%;\n    bottom: 6%;\n    display: flex;\n    align-items: flex-start;\n    justify-content: center;\n    text-align: center;\n    padding: 8px 14px 0;\n    font-weight: bold;\n    font-size: clamp(13px, 1.4vw + 8px, 18px);\n    line-height: 1.25;\n    color: var(--wdfc-title-color, #00e5ff) !important;\n    text-shadow: 0 2px 8px rgba(0,0,0,.9);\n    pointer-events: none;\n    overflow: hidden;\n}\n\n.wdfc-app .year-badge {\n    position: absolute;\n    top: 10px;\n    left: 10px;\n    background: rgba(0,0,0,.65);\n    backdrop-filter: blur(3px);\n    padding: 4px 10px;\n    border-radius: 20px;\n    font-size: 12px;\n    font-weight: bold;\n    color: #fff !important;\n}\n\n.wdfc-app .religious-badge {\n    position: absolute;\n    top: 10px;\n    right: 10px;\n    background: rgba(240,196,0,.9);\n    color: #111 !important;\n    padding: 4px 10px;\n    border-radius: 20px;\n    font-size: 11px;\n    font-weight: bold;\n}\n\n.wdfc-app .poster-overlay {\n    position: absolute;\n    inset: 0;\n    background: linear-gradient(to top, rgba(0,0,0,.92) 0%, rgba(0,0,0,.15) 55%, rgba(0,0,0,0) 100%);\n    opacity: 0;\n    transition: opacity .25s ease;\n    display: flex;\n    align-items: flex-end;\n    padding: 14px;\n}\n\n.wdfc-app .card:hover .poster-overlay { opacity: 1; }\n\n.wdfc-app .overlay-btn {\n    display: inline-block;\n    width: 100%;\n    text-align: center;\n    padding: 10px;\n    background: #f0c400;\n    color: #111 !important;\n    font-weight: bold;\n    text-decoration: none;\n    border-radius: 8px;\n    font-size: 13px;\n}\n\n.wdfc-app .card-content {\n    padding: 14px 16px 16px;\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    gap: 6px;\n}\n\n.wdfc-app .meta-line {\n    font-size: 12.5px;\n    color: rgba(255,255,255,.75) !important;\n    line-height: 1.5;\n    overflow-wrap: break-word;\n}\n\n.wdfc-app .meta-line b { color: #fff !important; }\n\n.wdfc-app .card-links {\n    margin-top: auto;\n    display: flex;\n    gap: 8px;\n    padding-top: 8px;\n}\n\n.wdfc-app .link-primary,\n.wdfc-app .link-secondary {\n    text-decoration: none;\n    text-align: center;\n    border-radius: 7px;\n    font-size: 12.5px;\n    font-weight: bold;\n    padding: 9px 6px;\n}\n\n.wdfc-app .link-primary { flex: 2; background: #f0c400; color: #111 !important; }\n.wdfc-app .link-secondary { flex: 1; background: #2c2c30; color: #fff !important; }\n.wdfc-app .link-primary:hover,\n.wdfc-app .link-secondary:hover { opacity: .85; }\n\n.wdfc-app #wdfc-titleColorPicker {\n    -webkit-appearance: none;\n    appearance: none;\n    width: 22px;\n    height: 22px;\n    padding: 0;\n    border: 2px solid #444;\n    border-radius: 50%;\n    cursor: pointer;\n    background: none;\n    overflow: hidden;\n}\n\n.wdfc-app #wdfc-titleColorPicker::-webkit-color-swatch-wrapper {\n    padding: 0;\n    border-radius: 50%;\n}\n\n.wdfc-app #wdfc-titleColorPicker::-webkit-color-swatch {\n    border: none;\n    border-radius: 50%;\n}\n\n.wdfc-app #wdfc-titleColorPicker::-moz-color-swatch {\n    border: none;\n    border-radius: 50%;\n}\n\n.wdfc-app .pagination {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    gap: 15px;\n    margin-top: 30px;\n    flex-wrap: wrap;\n    color: #fff !important;\n}\n\n@media (max-width: 700px) {\n    .wdfc-app .field-row.search-row { grid-template-columns: 1fr; }\n}\n\n\/* ===== Module TMDB \"Plus de films\" ===== *\/\n\n.wdfc-app #wdfc-tmdbPlusButton {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    margin: 8px 0;\n    padding: 10px 18px;\n    border: 0;\n    border-radius: 8px;\n    background: #01b4e4 !important;\n    color: #ffffff !important;\n    font-weight: 700;\n    cursor: pointer;\n    transition: opacity .2s, transform .2s;\n}\n\n.wdfc-app #wdfc-tmdbPlusButton:hover {\n    opacity: .88;\n    transform: translateY(-1px);\n}\n\n.wdfc-app #wdfc-tmdbPlusButton:disabled {\n    opacity: .5;\n    cursor: wait;\n    transform: none;\n}\n\n.wdfc-app #wdfc-tmdbPlusStatus {\n    display: none;\n    width: 100%;\n    box-sizing: border-box;\n    margin: 8px 0 14px;\n    padding: 10px 14px;\n    border-radius: 8px;\n    background: rgba(1,180,228,.12);\n    border: 1px solid rgba(1,180,228,.35);\n    color: #fff !important;\n    font-size: 13px;\n    line-height: 1.45;\n}\n\n<\/style>\n\n<section class=\"search-box\">\n\n<div class=\"field-row search-row\">\n\n<div class=\"field\">\n<label for=\"wdfc-search\">Recherche libre<\/label>\n<input id=\"wdfc-search\" type=\"search\" placeholder=\"Mot ou partie de mot...\">\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-searchCategory\">Rechercher dans<\/label>\n<select id=\"wdfc-searchCategory\">\n<option value=\"title\" selected>Titre<\/option>\n<option value=\"director\">R\u00e9alisateur<\/option>\n<option value=\"cast\">Acteur<\/option>\n<option value=\"all\">Titre + r\u00e9alisateur + acteur<\/option>\n<\/select>\n<\/div>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-searchButton\">\ud83d\udd0e Rechercher<\/button>\n<\/div>\n\n<\/div>\n\n\n<div class=\"field-row\">\n\n<div class=\"field\">\n<label for=\"wdfc-destinationSite\">Ouvrir les films sur<\/label>\n<select id=\"wdfc-destinationSite\">\n<option value=\"justwatch\" selected>JustWatch<\/option>\n<option value=\"allocine\">AlloCin\u00e9<\/option>\n<option value=\"imdb\">IMDb<\/option>\n<option value=\"custom\">URL personnalis\u00e9e<\/option>\n<\/select>\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-titleColorPicker\">Couleur des titres de films<\/label>\n<input id=\"wdfc-titleColorPicker\" type=\"color\" value=\"#00e5ff\">\n<\/div>\n\n<div class=\"field full-width\">\n<label for=\"wdfc-customSiteUrl\">URL personnalis\u00e9e (si s\u00e9lectionn\u00e9e ci-dessus)<\/label>\n<input\n    id=\"wdfc-customSiteUrl\"\n    type=\"text\"\n    placeholder=\"https:\/\/exemple.com\/recherche?q={titre}\"\n    disabled\n>\n<div class=\"field-hint\">\nUtilisez <code>{titre}<\/code> dans l'URL pour qu'il soit remplac\u00e9 par le titre du film \u00e0 chaque clic. Sans <code>{titre}<\/code>, le titre est ajout\u00e9 automatiquement en param\u00e8tre <code>q=<\/code> \u00e0 la fin de l'URL saisie. Les films venant de TMDB (bouton \"Plus de films\") s'ouvrent toujours directement sur TMDB, quel que soit ce choix \u2014 voir note plus bas.\n<\/div>\n<\/div>\n\n<\/div>\n\n\n<div class=\"field-row\">\n\n<div class=\"field\">\n<label for=\"wdfc-dateFilter\">Date de sortie<\/label>\n<select id=\"wdfc-dateFilter\">\n<option value=\"all\">Toutes les dates<\/option>\n<option value=\"with\">Avec date de sortie<\/option>\n<option value=\"without\">Sans date de sortie<\/option>\n<\/select>\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-day\">Derniers jours<\/label>\n<input id=\"wdfc-day\" type=\"number\" placeholder=\"Ex : 7\" min=\"1\">\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-month\">Derniers mois<\/label>\n<input id=\"wdfc-month\" type=\"number\" placeholder=\"Ex : 7\" min=\"1\">\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-year\">Ann\u00e9e (2026) ou N derni\u00e8res ann\u00e9es (3)<\/label>\n<input id=\"wdfc-year\" type=\"number\" placeholder=\"Ex : 2026 ou 3\" min=\"1\">\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-yearFrom\">Ann\u00e9e de (fourchette)<\/label>\n<input id=\"wdfc-yearFrom\" type=\"number\" placeholder=\"De\" min=\"1\">\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-yearTo\">Ann\u00e9e \u00e0 (fourchette)<\/label>\n<input id=\"wdfc-yearTo\" type=\"number\" placeholder=\"\u00c0\" min=\"1\">\n<\/div>\n\n<\/div>\n\n\n<div class=\"field-row\">\n\n<div class=\"field\">\n<label for=\"wdfc-dateFrom\">Date pr\u00e9cise de d\u00e9but<\/label>\n<input id=\"wdfc-dateFrom\" type=\"date\">\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-dateTo\">Date pr\u00e9cise de fin<\/label>\n<input id=\"wdfc-dateTo\" type=\"date\">\n<\/div>\n\n<\/div>\n\n\n<div class=\"field-row\">\n\n<div class=\"field\">\n<label for=\"wdfc-titleFilter\">Titre<\/label>\n<select id=\"wdfc-titleFilter\">\n<option value=\"all\">Tous les films<\/option>\n<option value=\"with\">Avec un titre<\/option>\n<option value=\"without\">Sans titre<\/option>\n<\/select>\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-typeFilter\">Type<\/label>\n<select id=\"wdfc-typeFilter\">\n<option value=\"film\">Films uniquement<\/option>\n<option value=\"animation\">Dessins anim\u00e9s uniquement<\/option>\n<option value=\"film_animation\" selected>Films + dessins anim\u00e9s<\/option>\n<option value=\"all\">Films et \u0153uvres audiovisuelles (large)<\/option>\n<\/select>\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-sort\">Tri<\/label>\n<select id=\"wdfc-sort\">\n<option value=\"newest\">Plus r\u00e9cents<\/option>\n<option value=\"oldest\">Plus anciens<\/option>\n<option value=\"title\">Titre A-Z<\/option>\n<\/select>\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-pageSize\">R\u00e9sultats par page<\/label>\n<input id=\"wdfc-pageSize\" type=\"number\" min=\"1\" max=\"500\" value=\"20\" list=\"wdfc-pageSizePresets\">\n<datalist id=\"wdfc-pageSizePresets\">\n<option value=\"20\">\n<option value=\"50\">\n<option value=\"100\">\n<option value=\"200\">\n<\/datalist>\n<\/div>\n\n<div class=\"field\">\n<label for=\"wdfc-maxResults\">Nombre maxi de r\u00e9sultats<\/label>\n<input id=\"wdfc-maxResults\" type=\"number\" placeholder=\"Illimit\u00e9\" min=\"1\">\n<\/div>\n\n<\/div>\n\n\n<div class=\"field-row\">\n\n<div class=\"field full-width\">\n<label>Langue(s) du titre <span id=\"wdfc-languageCount\" style=\"opacity:.5; text-transform:none;\"><\/span><\/label>\n<div id=\"wdfc-languageOptions\" class=\"language-options\">\n<span style=\"opacity:.6; font-size:13px;\">Chargement des langues disponibles dans la base...<\/span>\n<\/div>\n\n<label class=\"toggle-filter\" style=\"margin-top:8px;\" for=\"wdfc-allLanguages\">\n<input type=\"checkbox\" id=\"wdfc-allLanguages\">\n<span>\ud83c\udf10 Toutes les langues (ignore la liste ci-dessus)<\/span>\n<\/label>\n\n<div class=\"field-hint\">\nCochez une, plusieurs, ou toutes les langues souhait\u00e9es. Cochez \u00ab Toutes les langues \u00bb pour ne faire aucun tri par langue.\n<\/div>\n<\/div>\n\n<\/div>\n\n\n<div class=\"field-row\">\n\n<label class=\"toggle-filter\" for=\"wdfc-religiousFilter\">\n<input type=\"checkbox\" id=\"wdfc-religiousFilter\" checked>\n<span>\u271d\ufe0f Films chr\u00e9tiens \/ religieux (genre \/ sujet Wikidata)<\/span>\n<\/label>\n\n<fieldset class=\"lexical-box\">\n<legend>\u00c9largir la recherche (mots-cl\u00e9s dans le titre)<\/legend>\n<div class=\"lexical-options\">\n<label><input type=\"checkbox\" class=\"wdfc-lexicalKeyword\" value=\"chr\u00e9tien,christian\"> Chr\u00e9tien<\/label>\n<label><input type=\"checkbox\" class=\"wdfc-lexicalKeyword\" value=\"religieux,religious\"> Religieux<\/label>\n<label><input type=\"checkbox\" class=\"wdfc-lexicalKeyword\" value=\"biblique,biblical,bible\"> Biblique<\/label>\n<label><input type=\"checkbox\" class=\"wdfc-lexicalKeyword\" value=\"spirituel,spiritual\"> Spirituel<\/label>\n<label><input type=\"checkbox\" class=\"wdfc-lexicalKeyword\" value=\"croyance,belief,faith\"> Croyance<\/label>\n<label><input type=\"checkbox\" class=\"wdfc-lexicalKeyword\" value=\"\u00e9vangile,gospel\"> \u00c9vangile<\/label>\n<label><input type=\"checkbox\" class=\"wdfc-lexicalKeyword\" value=\"dieu,\u00e9ternel,god,eternal\"> Dieu \/ \u00c9ternel<\/label>\n<\/div>\n<input\n    id=\"wdfc-lexicalCustom\"\n    type=\"text\"\n    placeholder=\"Autres mots-cl\u00e9s, s\u00e9par\u00e9s par une virgule (ex : messe, pri\u00e8re)\"\n    style=\"margin-top:8px;\"\n>\n<\/fieldset>\n\n<fieldset class=\"lexical-box\">\n<legend>Exclure les films contenant ces mots (titre)<\/legend>\n<input\n    id=\"wdfc-excludeWords\"\n    type=\"text\"\n    placeholder=\"Mots \u00e0 exclure, s\u00e9par\u00e9s par une virgule (ex : horreur, zombie)\"\n>\n<\/fieldset>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-cancelButton\" disabled>\u23f9 Annuler<\/button>\n<\/div>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-resetButton\">\ud83e\uddf9 R\u00e9initialiser<\/button>\n<\/div>\n\n<\/div>\n\n\n<div class=\"field-row\">\n\n<div class=\"field\">\n<label for=\"wdfc-pageInput\">Aller \u00e0 la page<\/label>\n<input id=\"wdfc-pageInput\" type=\"number\" min=\"1\" value=\"1\">\n<\/div>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-pageButton\">\ud83d\udcc4 Aller \u00e0 la page<\/button>\n<\/div>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-exportButton\">\u2b07\ufe0f Exporter CSV<\/button>\n<\/div>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-reloadButton\">\ud83d\udd04 Rechercher<\/button>\n<\/div>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-youtubeButton\" class=\"youtube-button\" type=\"button\">\u25b6\ufe0f S\u00e9lection sur YouTube<\/button>\n<\/div>\n\n<div class=\"field\">\n<label>&nbsp;<\/label>\n<button id=\"wdfc-cinemaButton\" class=\"cinema-button\" type=\"button\">\ud83c\udfa6 Vue Cin\u00e9ma<\/button>\n<\/div>\n\n<\/div>\n\n\n<div class=\"progress\"><div id=\"wdfc-progressBar\" class=\"progress-bar\"><\/div><\/div>\n\n<\/section>\n\n<div id=\"wdfc-status\" class=\"status\">Pr\u00eat.<\/div>\n\n<div id=\"wdfc-totalCounter\" class=\"status\" style=\"display:none; font-weight:bold; color:#f0c400 !important;\"><\/div>\n\n<div id=\"wdfc-results\" class=\"results\"><\/div>\n\n<div class=\"pagination\">\n<button id=\"wdfc-previousButton\">\u25c0 Pr\u00e9c\u00e9dent<\/button>\n<span id=\"wdfc-pageInfo\">Page 1 \/ 1<\/span>\n<button id=\"wdfc-nextButton\">Suivant \u25b6<\/button>\n<\/div>\n\n<\/div>\n\n\n<script>\n(function () {\n\"use strict\";\n\nfunction init() {\n\n\/* =========================================================\n   CONFIGURATION\n========================================================= *\/\n\nconst WIKIDATA_ENDPOINT = \"https:\/\/query.wikidata.org\/sparql\";\n\nconst RELIGIOUS_GENRE_IDS = [\"wd:Q115122793\", \"wd:Q7717463\"];\n\nconst RELIGIOUS_SUBJECT_IDS = [\n    \"wd:Q5043\", \"wd:Q302\", \"wd:Q766677\", \"wd:Q190\", \"wd:Q194253\",\n    \"wd:Q5410500\", \"wd:Q1845\", \"wd:Q34274\", \"wd:Q34394\", \"wd:Q9174\",\n    \"wd:Q2728698\", \"wd:Q51628\", \"wd:Q51636\", \"wd:Q51624\",\n    \"wd:Q9077\", \"wd:Q81422\", \"wd:Q41370\", \"wd:Q9200\"\n];\n\nconst FILM_CLASS_ID = \"wd:Q11424\";\nconst ANIMATED_FILM_CLASS_ID = \"wd:Q202866\";\n\nconst COUNT_CAP = 3000;\nconst LANGUAGE_SAMPLE_SIZE = 20000;\nconst UPCOMING_LIMIT = 24;\n\nconst FALLBACK_LANGUAGE_CODES = [\n    \"fr\",\"en\",\"es\",\"de\",\"it\",\"pt\",\"nl\",\"ar\",\"ru\",\"zh\",\"ja\",\"ko\",\n    \"hi\",\"tr\",\"pl\",\"sv\",\"no\",\"da\",\"fi\",\"el\",\"he\",\"cs\",\"hu\",\"ro\",\"uk\"\n];\n\nconst DEFAULT_POSTER =\n    \"data:image\/svg+xml,\" +\n    encodeURIComponent(\n        '<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 400 600\">' +\n        '<defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">' +\n        '<stop offset=\"0\" stop-color=\"#34343e\"\/>' +\n        '<stop offset=\"1\" stop-color=\"#131316\"\/>' +\n        '<\/linearGradient><\/defs>' +\n        '<rect width=\"400\" height=\"600\" fill=\"url(#g)\"\/>' +\n        '<circle cx=\"200\" cy=\"248\" r=\"74\" fill=\"none\" stroke=\"#f0c400\" stroke-width=\"6\" opacity=\"0.9\"\/>' +\n        '<polygon points=\"184,210 184,286 250,248\" fill=\"#f0c400\" opacity=\"0.95\"\/>' +\n        '<text x=\"200\" y=\"412\" text-anchor=\"middle\" font-family=\"Arial, Helvetica, sans-serif\" ' +\n        'font-size=\"42\" font-weight=\"bold\" letter-spacing=\"10\" fill=\"#ffffff\" opacity=\"0.9\">FILM<\/text>' +\n        '<\/svg>'\n    );\n\n\/* =========================================================\n   VARIABLES\n========================================================= *\/\n\nlet currentPage = 1;\nlet totalPages = 1;\nlet totalKnown = true;\nlet hasNextPageUnknown = false;\nlet currentResults = [];\nlet abortController = null;\nlet isLoading = false;\n\n\/* =========================================================\n   ELEMENTS\n========================================================= *\/\n\nconst searchInput = document.getElementById(\"wdfc-search\");\nconst searchCategory = document.getElementById(\"wdfc-searchCategory\");\nconst destinationSiteSelect = document.getElementById(\"wdfc-destinationSite\");\nconst titleColorPicker = document.getElementById(\"wdfc-titleColorPicker\");\nconst customSiteUrlInput = document.getElementById(\"wdfc-customSiteUrl\");\nconst dayInput = document.getElementById(\"wdfc-day\");\nconst monthInput = document.getElementById(\"wdfc-month\");\nconst yearInput = document.getElementById(\"wdfc-year\");\nconst yearFromInput = document.getElementById(\"wdfc-yearFrom\");\nconst yearToInput = document.getElementById(\"wdfc-yearTo\");\nconst dateFromInput = document.getElementById(\"wdfc-dateFrom\");\nconst dateToInput = document.getElementById(\"wdfc-dateTo\");\nconst titleFilter = document.getElementById(\"wdfc-titleFilter\");\nconst dateFilter = document.getElementById(\"wdfc-dateFilter\");\nconst sortSelect = document.getElementById(\"wdfc-sort\");\nconst typeFilter = document.getElementById(\"wdfc-typeFilter\");\nconst religiousFilter = document.getElementById(\"wdfc-religiousFilter\");\nconst lexicalCustomInput = document.getElementById(\"wdfc-lexicalCustom\");\nconst excludeWordsInput = document.getElementById(\"wdfc-excludeWords\");\nconst languageOptionsContainer = document.getElementById(\"wdfc-languageOptions\");\nconst languageCountLabel = document.getElementById(\"wdfc-languageCount\");\nconst allLanguagesCheckbox = document.getElementById(\"wdfc-allLanguages\");\nconst pageSizeSelect = document.getElementById(\"wdfc-pageSize\");\nconst maxResultsInput = document.getElementById(\"wdfc-maxResults\");\nconst statusElement = document.getElementById(\"wdfc-status\");\nconst totalCounterElement = document.getElementById(\"wdfc-totalCounter\");\n\nlet wikidataTotalCount = null;\nlet tmdbTotalCount = null;\nlet wikidataCountToken = 0;\n\nfunction updateTotalCounter() {\n    if (wikidataTotalCount === null && tmdbTotalCount === null) {\n        totalCounterElement.style.display = \"none\";\n        return;\n    }\n    const parts = [];\n    if (wikidataTotalCount !== null) parts.push(\"Wikidata : \" + wikidataTotalCount.toLocaleString(\"fr-FR\"));\n    if (tmdbTotalCount !== null) parts.push(\"TMDB : \" + tmdbTotalCount.toLocaleString(\"fr-FR\"));\n    const total = (wikidataTotalCount || 0) + (tmdbTotalCount || 0);\n    totalCounterElement.textContent =\n        \"\ud83d\udd22 \" + parts.join(\" \u00b7 \") + \" \u00b7 Total cumul\u00e9 : \" + total.toLocaleString(\"fr-FR\") + \" film(s)\";\n    totalCounterElement.style.display = \"block\";\n}\nconst resultsElement = document.getElementById(\"wdfc-results\");\nconst progressBar = document.getElementById(\"wdfc-progressBar\");\nconst cancelButton = document.getElementById(\"wdfc-cancelButton\");\nconst pageInput = document.getElementById(\"wdfc-pageInput\");\nconst previousButton = document.getElementById(\"wdfc-previousButton\");\nconst nextButton = document.getElementById(\"wdfc-nextButton\");\nconst pageInfo = document.getElementById(\"wdfc-pageInfo\");\nconst youtubeButton = document.getElementById(\"wdfc-youtubeButton\");\nconst cinemaButton = document.getElementById(\"wdfc-cinemaButton\");\n\n\/* =========================================================\n   SITE D'OUVERTURE DES FILMS\n========================================================= *\/\n\nfunction getFilmDestinationUrl(film) {\n\n    if (film.isTMDB || film.source === \"TMDB\") {\n        return film.primaryLink;\n    }\n\n    const title = film.title || \"\";\n    const encodedTitle = encodeURIComponent(title);\n    const site = destinationSiteSelect.value;\n\n    if (site === \"allocine\") {\n        return title\n            ? `https:\/\/www.allocine.fr\/rechercher\/?q=${encodedTitle}`\n            : film.uri;\n    }\n\n    if (site === \"imdb\") {\n        if (film.imdb) return film.imdb;\n        return title\n            ? `https:\/\/www.imdb.com\/find\/?q=${encodedTitle}`\n            : film.uri;\n    }\n\n    if (site === \"custom\") {\n\n        const template = customSiteUrlInput.value.trim();\n\n        if (!template) return film.primaryLink;\n\n        if (template.includes(\"{titre}\")) {\n            return template.split(\"{titre}\").join(encodedTitle);\n        }\n\n        if (!title) return film.primaryLink;\n\n        const separator = template.includes(\"?\") ? \"&\" : \"?\";\n        return `${template}${separator}q=${encodedTitle}`;\n    }\n\n    if (!title) return film.uri;\n\n    return `https:\/\/www.justwatch.com\/fr\/recherche?q=${encodedTitle}`;\n}\n\n\/* =========================================================\n   UTILITAIRES DATE\n========================================================= *\/\n\nfunction daysAgo(n) {\n    const d = new Date();\n    d.setDate(d.getDate() - n);\n    return d;\n}\n\nfunction monthsAgo(n) {\n    const d = new Date();\n    d.setMonth(d.getMonth() - n);\n    return d;\n}\n\nfunction yearsAgo(n) {\n    const d = new Date();\n    d.setFullYear(d.getFullYear() - n);\n    return d;\n}\n\n\/* =========================================================\n   REQUETE SIMPLE (sans annulation), AVEC TENTATIVES AUTOMATIQUES\n========================================================= *\/\n\nasync function fetchWikidataOnce(query, attempt = 0) {\n\n    const response = await fetch(WIKIDATA_ENDPOINT, {\n        method: \"POST\",\n        headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded\",\n            \"Accept\": \"application\/sparql-results+json\"\n        },\n        body: \"query=\" + encodeURIComponent(query)\n    });\n\n    if ([429, 502, 503, 504].includes(response.status) && attempt < 3) {\n\n        const delay = (attempt + 1) * 2500;\n\n        await new Promise(resolve => setTimeout(resolve, delay));\n\n        return fetchWikidataOnce(query, attempt + 1);\n    }\n\n    if (!response.ok) {\n        throw new Error(\"Erreur HTTP \" + response.status);\n    }\n\n    return response.json();\n}\n\n\/* =========================================================\n   LANGUES DISPONIBLES DANS LA BASE\n========================================================= *\/\n\nfunction languageDisplayName(code) {\n    try {\n        const displayNames = new Intl.DisplayNames([\"fr\"], { type: \"language\" });\n        const name = displayNames.of(code);\n        return name && name.toLowerCase() !== code.toLowerCase() ? name : code;\n    }\n    catch (error) {\n        return code;\n    }\n}\n\nfunction populateLanguageSelect(codes) {\n\n    const uniqueCodes = Array.from(new Set(codes)).filter(Boolean);\n\n    uniqueCodes.sort((a, b) =>\n        languageDisplayName(a).localeCompare(languageDisplayName(b), \"fr\")\n    );\n\n    languageOptionsContainer.innerHTML = \"\";\n\n    uniqueCodes.forEach(code => {\n\n        const label = document.createElement(\"label\");\n\n        const checkbox = document.createElement(\"input\");\n        checkbox.type = \"checkbox\";\n        checkbox.className = \"wdfc-languageCheckbox\";\n        checkbox.value = code;\n        if (code === \"fr\") checkbox.checked = true;\n\n        const span = document.createElement(\"span\");\n        span.textContent = languageDisplayName(code) + \" (\" + code + \")\";\n\n        label.appendChild(checkbox);\n        label.appendChild(span);\n        languageOptionsContainer.appendChild(label);\n    });\n\n    if (!languageOptionsContainer.querySelector(\".wdfc-languageCheckbox:checked\") && uniqueCodes.length) {\n        languageOptionsContainer.querySelector(\".wdfc-languageCheckbox\").checked = true;\n    }\n\n    languageCountLabel.textContent = \"(\" + uniqueCodes.length + \" langues trouv\u00e9es)\";\n}\n\nasync function loadAvailableLanguages() {\n\n    try {\n\n        const query = `\n            SELECT DISTINCT ?lang WHERE {\n                {\n                    SELECT DISTINCT ?item WHERE {\n                        ?item wdt:P31 ${FILM_CLASS_ID} .\n                    }\n                    LIMIT ${LANGUAGE_SAMPLE_SIZE}\n                }\n                ?item rdfs:label ?itemLabel .\n                BIND(LANG(?itemLabel) AS ?lang)\n            }\n            ORDER BY ?lang\n        `;\n\n        const data = await fetchWikidataOnce(query);\n\n        const codes = data.results.bindings\n            .map(row => row.lang.value)\n            .filter(code => code && code.length <= 8);\n\n        if (codes.length === 0) {\n            throw new Error(\"Aucune langue retourn\u00e9e\");\n        }\n\n        populateLanguageSelect(codes);\n    }\n    catch (error) {\n        console.warn(\"Langues Wikidata indisponibles, repli sur une liste par d\u00e9faut :\", error);\n        populateLanguageSelect(FALLBACK_LANGUAGE_CODES);\n    }\n}\n\n\/* =========================================================\n   LANGUES SELECTIONNEES\n========================================================= *\/\n\nfunction getSelectedLanguages() {\n\n    if (allLanguagesCheckbox.checked) {\n        return null;\n    }\n\n    const selected = Array.from(document.querySelectorAll(\".wdfc-languageCheckbox:checked\"))\n        .map(cb => cb.value)\n        .filter(v => v);\n\n    return selected.length ? selected : [\"fr\"];\n}\n\n\/* =========================================================\n   CONDITIONS DE BASE\n========================================================= *\/\n\nfunction buildBaseConditions() {\n\n    const search = searchInput.value.trim();\n\n    const selectedLanguages = getSelectedLanguages();\n\n    let labelCondition;\n\n    if (selectedLanguages === null) {\n        labelCondition = `?item rdfs:label ?itemLabel .`;\n    }\n    else {\n        const langList = selectedLanguages.map(l => `\"${l}\"`).join(\",\");\n        labelCondition = `\n            ?item rdfs:label ?itemLabel .\n            FILTER(LANG(?itemLabel) IN (${langList}))\n        `;\n    }\n\n    let typeCondition = `?item wdt:P31 ${FILM_CLASS_ID} .`;\n\n    if (typeFilter.value === \"animation\") {\n        typeCondition = `\n            { ?item wdt:P31 ${ANIMATED_FILM_CLASS_ID} . }\n            UNION\n            { ?item wdt:P31 ?animType . ?animType wdt:P279* ${ANIMATED_FILM_CLASS_ID} . }\n        `;\n    }\n    else if (typeFilter.value === \"film_animation\") {\n        typeCondition = `\n            { ?item wdt:P31 ${FILM_CLASS_ID} . }\n            UNION\n            { ?item wdt:P31 ?filmType . ?filmType wdt:P279* ${FILM_CLASS_ID} . }\n            UNION\n            { ?item wdt:P31 ${ANIMATED_FILM_CLASS_ID} . }\n            UNION\n            { ?item wdt:P31 ?animType2 . ?animType2 wdt:P279* ${ANIMATED_FILM_CLASS_ID} . }\n        `;\n    }\n    else if (typeFilter.value === \"all\") {\n        typeCondition = `\n            { ?item wdt:P31 ${FILM_CLASS_ID} . }\n            UNION\n            { ?item wdt:P31 ?type . ?type wdt:P279* ${FILM_CLASS_ID} . }\n        `;\n    }\n\n    const lexicalKeywords =\n        Array.from(document.querySelectorAll(\".wdfc-lexicalKeyword:checked\"))\n            .flatMap(cb => cb.value.split(\",\"));\n\n    const customKeywordsRaw = lexicalCustomInput.value.trim();\n\n    if (customKeywordsRaw) {\n        customKeywordsRaw\n            .split(\",\")\n            .map(word => word.trim())\n            .filter(Boolean)\n            .forEach(word => lexicalKeywords.push(word));\n    }\n\n    const hasLexicalFilter = lexicalKeywords.length > 0;\n\n    let religiousCondition = \"\";\n\n    if (religiousFilter.checked || hasLexicalFilter) {\n\n        const branches = [];\n\n        if (religiousFilter.checked) {\n\n            branches.push(`\n                {\n                    ?item wdt:P136 ?religiousGenre .\n                    VALUES ?religiousGenre { ${RELIGIOUS_GENRE_IDS.join(\" \")} }\n                }\n            `);\n\n            branches.push(`\n                {\n                    ?item wdt:P921 ?religiousSubject .\n                    VALUES ?religiousSubject { ${RELIGIOUS_SUBJECT_IDS.join(\" \")} }\n                }\n            `);\n        }\n\n        if (hasLexicalFilter) {\n            const lexicalOr = lexicalKeywords\n                .map(word => `CONTAINS(LCASE(STR(?itemLabel)), LCASE(\"${word.replace(\/\"\/g, '\\\\\"')}\"))`)\n                .join(\" || \");\n\n            branches.push(`{ FILTER(${lexicalOr}) }`);\n        }\n\n        religiousCondition = branches.join(\" UNION \");\n    }\n\n    const excludeWordsRaw = excludeWordsInput.value.trim();\n    let excludeCondition = \"\";\n\n    if (excludeWordsRaw) {\n\n        const excludeWords = excludeWordsRaw\n            .split(\",\")\n            .map(word => word.trim())\n            .filter(Boolean);\n\n        if (excludeWords.length) {\n            const excludeOr = excludeWords\n                .map(word => `CONTAINS(LCASE(STR(?itemLabel)), LCASE(\"${word.replace(\/\"\/g, '\\\\\"')}\"))`)\n                .join(\" || \");\n\n            excludeCondition = `FILTER(!(${excludeOr}))`;\n        }\n    }\n\n    let searchCondition = \"\";\n\n    if (search) {\n\n        const safeSearch = search.replace(\/\"\/g, '\\\\\"');\n        const category = searchCategory.value || \"title\";\n\n        const titleMatch = `CONTAINS(LCASE(STR(?itemLabel)), LCASE(\"${safeSearch}\"))`;\n\n        const directorBlock = `\n            ?item wdt:P57 ?searchDirector .\n            ?searchDirector rdfs:label ?searchDirectorLabel .\n            FILTER(LANG(?searchDirectorLabel) IN (\"fr\",\"en\"))\n            FILTER(CONTAINS(LCASE(STR(?searchDirectorLabel)), LCASE(\"${safeSearch}\")))\n        `;\n\n        const castBlock = `\n            ?item wdt:P161 ?searchCast .\n            ?searchCast rdfs:label ?searchCastLabel .\n            FILTER(LANG(?searchCastLabel) IN (\"fr\",\"en\"))\n            FILTER(CONTAINS(LCASE(STR(?searchCastLabel)), LCASE(\"${safeSearch}\")))\n        `;\n\n        if (category === \"title\") {\n            searchCondition = `FILTER(${titleMatch})`;\n        }\n        else if (category === \"director\") {\n            searchCondition = directorBlock;\n        }\n        else if (category === \"cast\") {\n            searchCondition = castBlock;\n        }\n        else {\n            searchCondition = `\n                { FILTER(${titleMatch}) }\n                UNION\n                { ${directorBlock} }\n                UNION\n                { ${castBlock} }\n            `;\n        }\n    }\n\n    return `\n        ${typeCondition}\n        ${labelCondition}\n        ${religiousCondition}\n        ${excludeCondition}\n        ${searchCondition}\n    `;\n}\n\n\/* =========================================================\n   CONDITIONS DE DATE\n========================================================= *\/\n\nfunction buildWhereClause() {\n\n    const day = dayInput.value.trim();\n    const month = monthInput.value.trim();\n    const year = yearInput.value.trim();\n    const yearFrom = yearFromInput.value.trim();\n    const yearTo = yearToInput.value.trim();\n\n    const base = buildBaseConditions();\n\n    const dateFrom = dateFromInput.value;\n    const dateTo = dateToInput.value;\n\n    const hasFullDateRange = Boolean(dateFrom || dateTo);\n    const hasYearRange = Boolean(yearFrom || yearTo);\n    const hasRollingFilter = Boolean(day || month || year);\n\n    const hasDateFilter = hasFullDateRange || hasYearRange || hasRollingFilter;\n\n    let dateVariable = `OPTIONAL{ ?item wdt:P577 ?date . }`;\n\n    if (dateFilter.value === \"with\" || hasDateFilter) {\n        dateVariable = `?item wdt:P577 ?date .`;\n    }\n\n    if (dateFilter.value === \"without\") {\n        dateVariable = `FILTER NOT EXISTS{ ?item wdt:P577 ?anyDate . }`;\n    }\n\n    let dateFilterCondition = \"\";\n\n    if (hasDateFilter && dateFilter.value !== \"without\") {\n\n        const conditions = [];\n\n        if (hasFullDateRange) {\n            if (dateFrom) conditions.push(`?date >= \"${dateFrom}T00:00:00Z\"^^xsd:dateTime`);\n            if (dateTo) conditions.push(`?date <= \"${dateTo}T23:59:59Z\"^^xsd:dateTime`);\n        }\n        else if (hasYearRange) {\n            if (yearFrom) conditions.push(`YEAR(?date) >= ${Number(yearFrom)}`);\n            if (yearTo) conditions.push(`YEAR(?date) <= ${Number(yearTo)}`);\n        }\n        else {\n\n            const now = new Date();\n            const nowIso = now.toISOString();\n\n            if (day) {\n                conditions.push(`?date >= \"${daysAgo(Number(day)).toISOString()}\"^^xsd:dateTime`);\n                conditions.push(`?date <= \"${nowIso}\"^^xsd:dateTime`);\n            }\n\n            if (month) {\n                conditions.push(`?date >= \"${monthsAgo(Number(month)).toISOString()}\"^^xsd:dateTime`);\n                conditions.push(`?date <= \"${nowIso}\"^^xsd:dateTime`);\n            }\n\n            if (year) {\n\n                const yearNumber = Number(year);\n\n                if (yearNumber >= 100) {\n                    conditions.push(`YEAR(?date) = ${yearNumber}`);\n                }\n                else {\n                    conditions.push(`?date >= \"${yearsAgo(yearNumber).toISOString()}\"^^xsd:dateTime`);\n                    conditions.push(`?date <= \"${nowIso}\"^^xsd:dateTime`);\n                }\n            }\n        }\n\n        if (conditions.length) {\n            dateFilterCondition = `FILTER(${conditions.join(\" && \")})`;\n        }\n    }\n\n    return `\n        ${base}\n        ${dateVariable}\n        ${dateFilterCondition}\n    `;\n}\n\n\/* =========================================================\n   CONDITIONS \"A VENIR\" (vue Cin\u00e9ma)\n========================================================= *\/\n\nfunction buildUpcomingWhereClause() {\n\n    const base = buildBaseConditions();\n\n    return `\n        ${base}\n        ?item wdt:P577 ?date .\n        FILTER(?date > NOW())\n    `;\n}\n\n\/* =========================================================\n   REQUETE DE COMPTAGE\n========================================================= *\/\n\nfunction buildCountQuery() {\n\n    const where = buildWhereClause();\n\n    return `\n        PREFIX xsd: <http:\/\/www.w3.org\/2001\/XMLSchema#>\n        SELECT (COUNT(*) AS ?count) WHERE {\n            SELECT DISTINCT ?item WHERE{ ${where} }\n            LIMIT ${COUNT_CAP}\n        }\n    `;\n}\n\n\/* =========================================================\n   REQUETE DE LISTE (l\u00e9g\u00e8re, pagination r\u00e9elle)\n========================================================= *\/\n\nfunction buildListQuery(limit, offset) {\n\n    const where = buildWhereClause();\n\n    let order = \"DESC(?dateAgg)\";\n    if (sortSelect.value === \"oldest\") order = \"ASC(?dateAgg)\";\n    if (sortSelect.value === \"title\") order = \"LCASE(STR(?labelAgg))\";\n\n    return `\n        PREFIX xsd: <http:\/\/www.w3.org\/2001\/XMLSchema#>\n        SELECT ?item (SAMPLE(?itemLabel) AS ?labelAgg) (SAMPLE(?date) AS ?dateAgg)\n        WHERE{ ${where} }\n        GROUP BY ?item\n        ORDER BY ${order}\n        LIMIT ${limit}\n        OFFSET ${offset}\n    `;\n}\n\n\/* =========================================================\n   REQUETE DE LISTE \"A VENIR\" (vue Cin\u00e9ma)\n========================================================= *\/\n\nfunction buildUpcomingListQuery(limit) {\n\n    const where = buildUpcomingWhereClause();\n\n    return `\n        PREFIX xsd: <http:\/\/www.w3.org\/2001\/XMLSchema#>\n        SELECT ?item (SAMPLE(?itemLabel) AS ?labelAgg) (SAMPLE(?date) AS ?dateAgg)\n        WHERE{ ${where} }\n        GROUP BY ?item\n        ORDER BY ASC(?dateAgg)\n        LIMIT ${limit}\n    `;\n}\n\n\/* =========================================================\n   REQUETE D'ENRICHISSEMENT\n========================================================= *\/\n\nfunction buildDetailsQuery(itemUris) {\n\n    const values = itemUris.map(uri => `<${uri}>`).join(\" \");\n\n    return `\n        SELECT ?item\n               (GROUP_CONCAT(DISTINCT ?directorLabel; separator=\", \") AS ?directors)\n               (GROUP_CONCAT(DISTINCT ?castLabel; separator=\", \") AS ?cast)\n               (SAMPLE(?website) AS ?website)\n               (SAMPLE(?imdb) AS ?imdb)\n        WHERE{\n            VALUES ?item { ${values} }\n            OPTIONAL{\n                ?item wdt:P57 ?director .\n                ?director rdfs:label ?directorLabel .\n                FILTER(LANG(?directorLabel) IN (\"fr\",\"en\"))\n            }\n            OPTIONAL{\n                ?item wdt:P161 ?cast .\n                ?cast rdfs:label ?castLabel .\n                FILTER(LANG(?castLabel) IN (\"fr\",\"en\"))\n            }\n            OPTIONAL{ ?item wdt:P856 ?website . }\n            OPTIONAL{\n                ?item wdt:P345 ?imdbId .\n                BIND(CONCAT(\"https:\/\/www.imdb.com\/title\/\", ?imdbId) AS ?imdb)\n            }\n        }\n        GROUP BY ?item\n    `;\n}\n\n\/* =========================================================\n   REQUETE AVEC RETRY (POST, pour \u00e9viter les URL trop longues)\n========================================================= *\/\n\nasync function fetchWikidata(query, attempt = 0) {\n\n    const response = await fetch(WIKIDATA_ENDPOINT, {\n        method: \"POST\",\n        headers: {\n            \"Content-Type\": \"application\/x-www-form-urlencoded\",\n            \"Accept\": \"application\/sparql-results+json\"\n        },\n        body: \"query=\" + encodeURIComponent(query),\n        signal: abortController.signal\n    });\n\n    if ([429, 502, 503, 504].includes(response.status)) {\n\n        if (attempt < 3) {\n\n            const delay = (attempt + 1) * 2500;\n\n            statusElement.textContent =\n                \"\u23f3 Wikidata est temporairement charg\u00e9. Nouvelle tentative dans \"\n                + (delay \/ 1000) + \" secondes...\";\n\n            await new Promise(resolve => setTimeout(resolve, delay));\n\n            return fetchWikidata(query, attempt + 1);\n        }\n    }\n\n    if (!response.ok) {\n        throw new Error(\"Erreur HTTP \" + response.status);\n    }\n\n    return response.json();\n}\n\n\/* =========================================================\n   RECHERCHE (comptage -> liste -> enrichissement)\n========================================================= *\/\n\nasync function searchWikidata() {\n\n    if (isLoading) return;\n\n    isLoading = true;\n    abortController = new AbortController();\n    cancelButton.disabled = false;\n    progressBar.style.width = \"10%\";\n    resultsElement.innerHTML = \"\";\n    statusElement.textContent = \"\u23f3 Pr\u00e9paration de la recherche...\";\n\n    wikidataTotalCount = null;\n    tmdbTotalCount = null;\n    updateTotalCounter();\n    const countToken = ++wikidataCountToken;\n\n    try {\n\n        if (titleFilter.value === \"without\") {\n            currentResults = [];\n            totalPages = 1;\n            statusElement.textContent =\n                \"\u26a0\ufe0f Wikidata ne permet pas de r\u00e9cup\u00e9rer fiablement les films \"\n                + \"sans titre dans cette requ\u00eate l\u00e9g\u00e8re.\";\n            renderResults();\n            return;\n        }\n\n        const pageSize = Math.max(1, Number(pageSizeSelect.value) || 20);\n\n        const maxResultsRaw = maxResultsInput.value.trim();\n        const maxResults = maxResultsRaw ? Math.max(1, Number(maxResultsRaw)) : null;\n\n        \/\/ La pagination se fait \"\u00e0 l'aveugle\" (sans COUNT pr\u00e9alable) pour que\n        \/\/ la premi\u00e8re page s'affiche le plus vite possible : on sait s'il y a\n        \/\/ une page suivante selon qu'elle est pleine ou non. Le total exact,\n        \/\/ lui, est calcul\u00e9 en arri\u00e8re-plan (voir plus bas) et n'emp\u00eache donc\n        \/\/ plus l'affichage des r\u00e9sultats d'\u00eatre rapide.\n        totalKnown = false;\n        totalPages = Math.max(totalPages, currentPage);\n\n        \/\/ Comptage exact lanc\u00e9 en parall\u00e8le, sans \u00eatre attendu : il met \u00e0\n        \/\/ jour l'encart \"Total cumul\u00e9\" d\u00e8s qu'il r\u00e9pond, sans ralentir\n        \/\/ l'affichage des films eux-m\u00eames.\n        fetchWikidataOnce(buildCountQuery())\n            .then(function (countData) {\n                if (countToken !== wikidataCountToken) return; \/\/ une recherche plus r\u00e9cente a d\u00e9marr\u00e9 depuis\n                wikidataTotalCount = Number(countData.results.bindings[0]?.count?.value || 0);\n                updateTotalCounter();\n            })\n            .catch(function (error) {\n                console.warn(\"Comptage Wikidata (arri\u00e8re-plan) indisponible :\", error);\n            });\n\n        statusElement.textContent = \"\u23f3 Recherche des films...\";\n        progressBar.style.width = \"40%\";\n\n        const offset = (currentPage - 1) * pageSize;\n\n        let limit = pageSize;\n\n        if (maxResults) {\n            limit = Math.min(pageSize, Math.max(0, maxResults - offset));\n        }\n\n        const fetchLimit = totalKnown ? limit : limit + 1;\n\n        const listData = await fetchWikidata(buildListQuery(fetchLimit, offset));\n        let listRows = listData.results.bindings;\n\n        if (!totalKnown) {\n            hasNextPageUnknown = listRows.length > limit;\n            listRows = listRows.slice(0, limit);\n            totalPages = hasNextPageUnknown ? currentPage + 1 : currentPage;\n        }\n\n        progressBar.style.width = \"60%\";\n\n        if (listRows.length === 0) {\n            currentResults = [];\n            progressBar.style.width = \"100%\";\n            statusElement.textContent = \"\u274c Aucun r\u00e9sultat sur cette page.\";\n            renderResults();\n            return;\n        }\n\n        statusElement.textContent = \"\u23f3 R\u00e9cup\u00e9ration des affiches et informations...\";\n\n        const itemUris = listRows.map(row => row.item.value);\n        const detailsData = await fetchWikidata(buildDetailsQuery(itemUris));\n\n        const detailsByUri = {};\n        detailsData.results.bindings.forEach(row => {\n            detailsByUri[row.item.value] = row;\n        });\n\n        progressBar.style.width = \"90%\";\n\n        currentResults = listRows.map(row => convertResult(row, detailsByUri[row.item.value]));\n\n        progressBar.style.width = \"100%\";\n\n        statusElement.textContent =\n            \"\u2705 \" + listRows.length + \" r\u00e9sultat(s) affich\u00e9(s) sur cette page \"\n            + \"(le total exact s'affiche s\u00e9par\u00e9ment d\u00e8s qu'il est calcul\u00e9).\";\n\n        renderResults();\n\n    }\n    catch (error) {\n\n        if (error.name === \"AbortError\") {\n            statusElement.textContent = \"\u23f9 Recherche annul\u00e9e.\";\n        }\n        else {\n            console.error(\"Wikidata :\", error);\n            statusElement.textContent = \"\u274c Impossible d'interroger Wikidata : \" + error.message;\n        }\n    }\n    finally {\n        isLoading = false;\n        cancelButton.disabled = true;\n        abortController = null;\n    }\n}\n\n\/* =========================================================\n   CONVERSION (fusion ligne liste + ligne d\u00e9tails)\n========================================================= *\/\n\nfunction convertResult(listRow, detailsRow) {\n\n    const details = detailsRow || {};\n\n    const website = details.website ? details.website.value : \"\";\n    const imdb = details.imdb ? details.imdb.value : \"\";\n    const uri = listRow.item ? listRow.item.value : \"\";\n\n    const primaryLink = website || imdb || uri;\n\n    return {\n        uri,\n        title: listRow.labelAgg ? listRow.labelAgg.value : \"\",\n        poster: \"\",\n        directors: details.directors ? details.directors.value : \"\",\n        cast: details.cast ? details.cast.value : \"\",\n        date: listRow.dateAgg ? listRow.dateAgg.value : \"\",\n        website,\n        imdb,\n        primaryLink,\n        source: \"wikidata\"\n    };\n}\n\n\/* =========================================================\n   AFFICHAGE\n========================================================= *\/\n\nfunction renderResults() {\n\n    resultsElement.innerHTML = \"\";\n\n    const isReligiousSearch =\n        religiousFilter.checked\n        || document.querySelectorAll(\".wdfc-lexicalKeyword:checked\").length > 0;\n\n    currentResults.forEach(film => {\n\n        const card = document.createElement(\"article\");\n        card.className = \"card\";\n\n        if (film.tmdbId) {\n            card.dataset.tmdbId = film.tmdbId;\n        }\n\n        const year = film.date ? new Date(film.date).getFullYear() : \"\";\n\n        const castList = film.cast\n            ? film.cast.split(\", \").slice(0, 4).join(\", \")\n            : \"\";\n\n        const hasRealPoster = Boolean(film.poster);\n        const filmTitle = film.title || \"Titre non renseign\u00e9\";\n        const destinationUrl = getFilmDestinationUrl(film);\n\n        card.innerHTML = `\n\n            <div class=\"poster-wrap\">\n\n                <img decoding=\"async\" class=\"poster\" src=\"${film.poster || DEFAULT_POSTER}\"\n                     alt=\"${escapeHTML(filmTitle)}\" loading=\"lazy\">\n\n                ${!hasRealPoster ? `<div class=\"poster-fallback-title\">${escapeHTML(filmTitle)}<\/div>` : \"\"}\n\n                ${year ? `<div class=\"year-badge\">${year}<\/div>` : \"\"}\n\n                ${isReligiousSearch ? `<div class=\"religious-badge\">\u271d\ufe0f Religieux<\/div>` : \"\"}\n\n                <a class=\"poster-overlay\" href=\"${destinationUrl}\" target=\"_blank\" rel=\"noopener\">\n                    <span class=\"overlay-btn\">\u25b6 Voir le film<\/span>\n                <\/a>\n\n            <\/div>\n\n            <div class=\"card-content\">\n\n                ${film.directors ? `<div class=\"meta-line\"><b>R\u00e9alisation :<\/b> ${escapeHTML(film.directors)}<\/div>` : \"\"}\n\n                ${castList ? `<div class=\"meta-line\"><b>Avec :<\/b> ${escapeHTML(castList)}<\/div>` : \"\"}\n\n                <div class=\"meta-line\">\ud83d\udcc5 ${formatDate(film.date)}<\/div>\n\n                <div class=\"card-links\">\n                    <a class=\"link-primary\" href=\"${destinationUrl}\" target=\"_blank\" rel=\"noopener\">\u25b6 Voir le film<\/a>\n                    <a class=\"link-secondary\" href=\"${film.uri}\" target=\"_blank\" rel=\"noopener\">${film.isTMDB ? \"TMDB\" : \"Wikidata\"}<\/a>\n                <\/div>\n\n            <\/div>\n        `;\n\n        resultsElement.appendChild(card);\n    });\n\n    updatePagination();\n}\n\n\/* =========================================================\n   PAGINATION\n========================================================= *\/\n\nfunction updatePagination() {\n\n    if (totalKnown) {\n        pageInfo.textContent = \"Page \" + currentPage + \" \/ \" + totalPages;\n        previousButton.disabled = currentPage <= 1;\n        nextButton.disabled = currentPage >= totalPages;\n        pageInput.max = totalPages;\n    }\n    else {\n        pageInfo.textContent = \"Page \" + currentPage + \" (total non calcul\u00e9)\";\n        previousButton.disabled = currentPage <= 1;\n        nextButton.disabled = !hasNextPageUnknown;\n        pageInput.removeAttribute(\"max\");\n    }\n}\n\n\/* =========================================================\n   DATE \/ SECURITE\n========================================================= *\/\n\nfunction formatDate(date) {\n\n    if (!date) return \"Date non renseign\u00e9e\";\n\n    const d = new Date(date);\n\n    if (isNaN(d)) return date;\n\n    return d.toLocaleDateString(\"fr-FR\");\n}\n\nfunction escapeHTML(value) {\n    const div = document.createElement(\"div\");\n    div.textContent = value ? String(value) : \"\";\n    return div.innerHTML;\n}\n\n\/* =========================================================\n   VUE CINEMA\n========================================================= *\/\n\nfunction renderCinemaCard(film) {\n\n    const title = film.title || \"Titre non renseign\u00e9\";\n    const year = film.date ? new Date(film.date).getFullYear() : \"\";\n    const poster = film.poster || DEFAULT_POSTER;\n    const hasPoster = Boolean(film.poster);\n    const destinationUrl = getFilmDestinationUrl(film);\n\n    return `\n        <a class=\"cinema-card\" href=\"${destinationUrl}\" target=\"_blank\" rel=\"noopener\">\n            <div class=\"cinema-poster-wrap\">\n                <img decoding=\"async\" src=\"${poster}\" alt=\"${escapeHTML(title)}\" loading=\"lazy\">\n                ${!hasPoster ? `<div class=\"cinema-fallback-title\">${escapeHTML(title)}<\/div>` : \"\"}\n                ${year ? `<div class=\"cinema-year\">${year}<\/div>` : \"\"}\n            <\/div>\n            <div class=\"cinema-title\">${escapeHTML(title)}<\/div>\n        <\/a>\n    `;\n}\n\nfunction renderCinemaHtml(nowShowing, upcoming, loading) {\n\n    const nowCards = nowShowing.map(renderCinemaCard).join(\"\");\n\n    let upcomingSection = \"\";\n\n    if (loading) {\n        upcomingSection = `<p class=\"cinema-loading\">\u23f3 Recherche des films \u00e0 venir dans la base...<\/p>`;\n    }\n    else if (upcoming.length) {\n        upcomingSection = `\n            <h2 class=\"cinema-section-title\">\ud83d\udd1c Prochainement<\/h2>\n            <div class=\"cinema-grid\">${upcoming.map(renderCinemaCard).join(\"\")}<\/div>\n        `;\n    }\n\n    return `\n        <!DOCTYPE html>\n        <html lang=\"fr\">\n        <head>\n        <meta charset=\"UTF-8\">\n        <title>Au cin\u00e9ma \u2014 S\u00e9lection<\/title>\n        <style>\n            * { box-sizing: border-box; }\n            body { margin:0; font-family: Arial, Helvetica, sans-serif; background:#0b0b0d; color:#fff; padding:28px; }\n            h1 { text-align:center; margin-bottom: 6px; }\n            .cinema-subtitle { text-align:center; opacity:.6; margin-bottom:28px; font-size:13px; }\n            .cinema-section-title { margin: 34px 0 16px; font-size:18px; border-left:4px solid #f0c400; padding-left:10px; }\n            .cinema-loading { opacity:.6; margin-top:20px; }\n            .cinema-grid { display:grid; grid-template-columns: repeat(auto-fill, minmax(170px,1fr)); gap:20px; }\n            .cinema-card { color:#fff; text-decoration:none; display:block; background:#1a1a1d; border-radius:12px; overflow:hidden; transition: transform .2s ease; }\n            .cinema-card:hover { transform: translateY(-4px); }\n            .cinema-poster-wrap { position:relative; width:100%; aspect-ratio:2\/3; background:#222; overflow:hidden; }\n            .cinema-poster-wrap img { width:100%; height:100%; object-fit:cover; display:block; }\n            .cinema-fallback-title {\n                position:absolute; left:0; right:0; top:71%; bottom:6%;\n                display:flex; align-items:flex-start; justify-content:center;\n                text-align:center; padding:8px 12px 0; font-weight:bold;\n                font-size:13px; line-height:1.25; text-shadow:0 2px 6px rgba(0,0,0,.9);\n            }\n            .cinema-year { position:absolute; top:8px; left:8px; background:rgba(0,0,0,.65); padding:3px 8px; border-radius:14px; font-size:11px; font-weight:bold; }\n            .cinema-title { padding:10px 12px 14px; font-size:13.5px; font-weight:bold; line-height:1.3; }\n        <\/style>\n        <\/head>\n        <body>\n            <h1>\ud83c\udfac Au cin\u00e9ma<\/h1>\n            <div class=\"cinema-subtitle\">Cliquez sur une affiche pour acc\u00e9der directement \u00e0 la page de visionnage du film<\/div>\n            <div class=\"cinema-grid\">${nowCards || \"<p>Aucun film \u00e0 afficher.<\/p>\"}<\/div>\n            ${upcomingSection}\n        <\/body>\n        <\/html>\n    `;\n}\n\nasync function openCinemaView() {\n\n    if (currentResults.length === 0) {\n        alert(\"Aucun film \u00e0 afficher. Lancez d'abord une recherche.\");\n        return;\n    }\n\n    const win = window.open(\"\", \"_blank\");\n\n    if (!win) {\n        alert(\"Merci d'autoriser les fen\u00eatres pop-up pour ce site afin d'ouvrir la vue cin\u00e9ma.\");\n        return;\n    }\n\n    win.document.open();\n    win.document.write(renderCinemaHtml(currentResults, [], true));\n    win.document.close();\n\n    let upcomingFilms = [];\n\n    try {\n\n        const listData = await fetchWikidataOnce(buildUpcomingListQuery(UPCOMING_LIMIT));\n        const rows = listData.results.bindings;\n\n        if (rows.length) {\n\n            const uris = rows.map(row => row.item.value);\n            const detailsData = await fetchWikidataOnce(buildDetailsQuery(uris));\n\n            const detailsByUri = {};\n            detailsData.results.bindings.forEach(row => {\n                detailsByUri[row.item.value] = row;\n            });\n\n            upcomingFilms = rows.map(row => convertResult(row, detailsByUri[row.item.value]));\n        }\n    }\n    catch (error) {\n        console.warn(\"Films \u00e0 venir indisponibles :\", error);\n    }\n\n    if (!win.closed) {\n        win.document.open();\n        win.document.write(renderCinemaHtml(currentResults, upcomingFilms, false));\n        win.document.close();\n    }\n}\n\n\/* =========================================================\n   EVENEMENTS\n========================================================= *\/\n\ncancelButton.addEventListener(\"click\", function () {\n    if (abortController) abortController.abort();\n});\n\ndocument.getElementById(\"wdfc-searchButton\").addEventListener(\"click\", function () {\n    currentPage = 1;\n    searchWikidata();\n});\n\nsearchInput.addEventListener(\"keydown\", function (event) {\n    if (event.key === \"Enter\") {\n        currentPage = 1;\n        searchWikidata();\n    }\n});\n\nnextButton.addEventListener(\"click\", function () {\n    if (currentPage < totalPages) {\n        currentPage++;\n        searchWikidata();\n    }\n});\n\npreviousButton.addEventListener(\"click\", function () {\n    if (currentPage > 1) {\n        currentPage--;\n        searchWikidata();\n    }\n});\n\ndocument.getElementById(\"wdfc-pageButton\").addEventListener(\"click\", function () {\n    let page = Number(pageInput.value);\n    if (page < 1) page = 1;\n    if (totalKnown && page > totalPages) page = totalPages;\n    currentPage = page;\n    searchWikidata();\n});\n\ndocument.getElementById(\"wdfc-reloadButton\").addEventListener(\"click\", searchWikidata);\n\nallLanguagesCheckbox.addEventListener(\"change\", function () {\n    languageOptionsContainer.classList.toggle(\"is-disabled\", allLanguagesCheckbox.checked);\n});\n\ndestinationSiteSelect.addEventListener(\"change\", function () {\n    customSiteUrlInput.disabled = destinationSiteSelect.value !== \"custom\";\n    if (currentResults.length) renderResults();\n});\n\nconst TITLE_COLOR_STORAGE_KEY = \"wdfcFilmCatalogTitleColor\";\n\ntitleColorPicker.addEventListener(\"input\", function () {\n    document.querySelector(\".wdfc-app\").style.setProperty(\"--wdfc-title-color\", titleColorPicker.value);\n\n    try {\n        localStorage.setItem(TITLE_COLOR_STORAGE_KEY, titleColorPicker.value);\n    }\n    catch (error) {\n        console.warn(\"Impossible de sauvegarder la couleur choisie :\", error);\n    }\n});\n\ncustomSiteUrlInput.addEventListener(\"input\", function () {\n    if (destinationSiteSelect.value === \"custom\" && currentResults.length) {\n        renderResults();\n    }\n});\n\nyoutubeButton.addEventListener(\"click\", function () {\n\n    if (currentResults.length === 0) {\n        alert(\"Aucun film \u00e0 afficher pour le moment. Lancez d'abord une recherche.\");\n        return;\n    }\n\n    const win = window.open(\"\", \"_blank\");\n\n    if (!win) {\n        alert(\"Merci d'autoriser les fen\u00eatres pop-up pour ce site afin d'ouvrir la s\u00e9lection YouTube.\");\n        return;\n    }\n\n    const linksHtml = currentResults.map(film => {\n        const title = film.title || \"Titre non renseign\u00e9\";\n        const query = encodeURIComponent(title + \" film complet\");\n        const url = \"https:\/\/www.youtube.com\/results?search_query=\" + query;\n        return \"<li><a href=\\\"\" + url + \"\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\">\"\n            + escapeHTML(title) + \"<\/a><\/li>\";\n    }).join(\"\");\n\n    win.document.write(\n        \"<!DOCTYPE html><html lang=\\\"fr\\\"><head><meta charset=\\\"UTF-8\\\">\"\n        + \"<title>S\u00e9lection sur YouTube<\/title>\"\n        + \"<style>body{font-family:Arial,Helvetica,sans-serif;background:#0b0b0d;color:#fff;padding:24px;}\"\n        + \"h1{font-size:20px;} a{color:#f0c400;text-decoration:none;} a:hover{text-decoration:underline;}\"\n        + \"li{margin-bottom:10px;font-size:15px;}<\/style><\/head>\"\n        + \"<body><h1>\ud83c\udfac Recherche YouTube \u2014 s\u00e9lection actuelle (\" + currentResults.length + \" film(s))<\/h1>\"\n        + \"<ul>\" + linksHtml + \"<\/ul><\/body><\/html>\"\n    );\n\n    win.document.close();\n});\n\ncinemaButton.addEventListener(\"click\", openCinemaView);\n\ndocument.getElementById(\"wdfc-resetButton\").addEventListener(\"click\", function () {\n\n    if (isLoading && abortController) abortController.abort();\n\n    searchInput.value = \"\";\n    searchCategory.value = \"title\";\n    destinationSiteSelect.value = \"justwatch\";\n    customSiteUrlInput.value = \"\";\n    customSiteUrlInput.disabled = true;\n    dayInput.value = \"\";\n    monthInput.value = \"\";\n    yearInput.value = \"\";\n    yearFromInput.value = \"\";\n    yearToInput.value = \"\";\n    dateFromInput.value = \"\";\n    dateToInput.value = \"\";\n    titleFilter.value = \"all\";\n    dateFilter.value = \"all\";\n    typeFilter.value = \"film_animation\";\n\n    document.querySelectorAll(\".wdfc-languageCheckbox\").forEach(cb => {\n        cb.checked = (cb.value === \"fr\");\n    });\n    allLanguagesCheckbox.checked = false;\n    languageOptionsContainer.classList.remove(\"is-disabled\");\n\n    religiousFilter.checked = true;\n\n    document.querySelectorAll(\".wdfc-lexicalKeyword\").forEach(cb => { cb.checked = false; });\n    lexicalCustomInput.value = \"\";\n    excludeWordsInput.value = \"\";\n\n    sortSelect.value = \"newest\";\n    pageSizeSelect.value = \"20\";\n    maxResultsInput.value = \"\";\n\n    currentPage = 1;\n    totalPages = 1;\n    totalKnown = true;\n    hasNextPageUnknown = false;\n    currentResults = [];\n\n    wikidataTotalCount = null;\n    tmdbTotalCount = null;\n    wikidataCountToken++;\n    updateTotalCounter();\n\n    resultsElement.innerHTML = \"\";\n    progressBar.style.width = \"0%\";\n    pageInput.value = 1;\n    pageInfo.textContent = \"Page 1 \/ 1\";\n\n    statusElement.textContent = \"Filtres r\u00e9initialis\u00e9s.\";\n});\n\ndocument.getElementById(\"wdfc-exportButton\").addEventListener(\"click\", function () {\n\n    if (currentResults.length === 0) {\n        alert(\"Aucun r\u00e9sultat \u00e0 exporter.\");\n        return;\n    }\n\n    const header = [\"Titre\", \"R\u00e9alisateur(s)\", \"Casting\", \"Date\", \"Lien\", \"Wikidata\", \"Source\"];\n\n    const rows = currentResults.map(film => [\n        film.title, film.directors, film.cast, film.date, film.primaryLink, film.uri, film.source\n    ]);\n\n    const csv = [header, ...rows]\n        .map(row => row.map(value =>\n            '\"' + String(value || \"\").replace(\/\"\/g, '\"\"') + '\"'\n        ).join(\";\"))\n        .join(\"\\n\");\n\n    const blob = new Blob([\"\\ufeff\" + csv], { type: \"text\/csv;charset=utf-8\" });\n    const url = URL.createObjectURL(blob);\n    const link = document.createElement(\"a\");\n    link.href = url;\n    link.download = \"films-wikidata.csv\";\n    link.click();\n    URL.revokeObjectURL(url);\n});\n\n\/* =========================================================\n   INITIALISATION\n========================================================= *\/\n\nstatusElement.textContent = \"Pr\u00eat. Entrez un terme ou lancez une recherche.\";\nloadAvailableLanguages();\n\ntry {\n    const savedTitleColor = localStorage.getItem(TITLE_COLOR_STORAGE_KEY);\n    if (savedTitleColor) {\n        titleColorPicker.value = savedTitleColor;\n        document.querySelector(\".wdfc-app\").style.setProperty(\"--wdfc-title-color\", savedTitleColor);\n    }\n}\ncatch (error) {\n    console.warn(\"Impossible de relire la couleur sauvegard\u00e9e :\", error);\n}\n\n\/* =========================================================================================\n   MODULE TMDB - PLUS DE FILMS CHRETIENS\n   (imbriqu\u00e9 ici pour partager le scope avec le reste du script \u2014 currentResults,\n   renderResults, etc. restent accessibles sans passer par window global)\n========================================================================================= *\/\n\n(function () {\n\n    \"use strict\";\n\n    const TMDB_API_KEY = \"af5576c58fe6fd7fab447fbb4dd2423a\";\n    const TMDB_API = \"https:\/\/api.themoviedb.org\/3\";\n    const TMDB_LANGUAGE = \"fr-FR\";\n    const TMDB_REGION = \"FR\";\n    const TMDB_MAX_PAGES = 500; \/\/ limite r\u00e9elle de l'API \/discover\n    const TMDB_PAGE_BATCH_SIZE = 6; \/\/ pages r\u00e9cup\u00e9r\u00e9es en parall\u00e8le par lot\n\n    const CHRISTIAN_KEYWORDS = [\n        \"christianity\", \"christian\", \"christian faith\", \"christian film\",\n        \"religious film\", \"religion\", \"faith\", \"bible\", \"biblical\",\n        \"jesus christ\", \"jesus\", \"gospel\", \"resurrection\", \"crucifixion\",\n        \"church\", \"evangelical christianity\"\n    ];\n\n    const typeFilterEl = document.getElementById(\"wdfc-typeFilter\");\n    const pageSizeSelectEl = document.getElementById(\"wdfc-pageSize\");\n    const maxResultsInputEl = document.getElementById(\"wdfc-maxResults\");\n    const sortSelectEl = document.getElementById(\"wdfc-sort\");\n    const religiousFilterEl = document.getElementById(\"wdfc-religiousFilter\");\n    const allLanguagesCheckboxEl = document.getElementById(\"wdfc-allLanguages\");\n    const languageOptionsContainerEl = document.getElementById(\"wdfc-languageOptions\");\n    const statusElementEl = document.getElementById(\"wdfc-status\");\n    const progressBarEl = document.getElementById(\"wdfc-progressBar\");\n    const resultsElementEl = document.getElementById(\"wdfc-results\");\n\n    if (!typeFilterEl || !pageSizeSelectEl || !maxResultsInputEl || !sortSelectEl) {\n        console.warn(\"TMDB Plus : un ou plusieurs filtres n\u00e9cessaires sont absents.\");\n        return;\n    }\n\n    function createTMDBButton() {\n\n        if (document.getElementById(\"wdfc-tmdbPlusButton\")) return;\n\n        const button = document.createElement(\"button\");\n        button.id = \"wdfc-tmdbPlusButton\";\n        button.type = \"button\";\n        button.textContent = \"\ud83c\udfac Plus de films\";\n\n        const searchButtonEl = document.querySelector(\n            '#wdfc-searchButton, [data-action=\"search\"], button[type=\"submit\"]'\n        );\n\n        if (searchButtonEl && searchButtonEl.parentNode) {\n            searchButtonEl.parentNode.appendChild(button);\n        }\n        else if (resultsElementEl && resultsElementEl.parentNode) {\n            resultsElementEl.parentNode.insertBefore(button, resultsElementEl);\n        }\n        else {\n            document.body.appendChild(button);\n        }\n\n        const status = document.createElement(\"div\");\n        status.id = \"wdfc-tmdbPlusStatus\";\n\n        if (button.parentNode) {\n            button.parentNode.insertBefore(status, button.nextSibling);\n        }\n\n        button.addEventListener(\"click\", function () {\n            searchTMDB();\n        });\n    }\n\n    function setTMDBStatus(message) {\n        const element = document.getElementById(\"wdfc-tmdbPlusStatus\");\n        if (element) {\n            element.textContent = message;\n            element.style.display = \"block\";\n        }\n        if (statusElementEl) {\n            statusElementEl.textContent = message;\n        }\n    }\n\n    function normalizeTitle(value) {\n        return String(value || \"\")\n            .toLowerCase()\n            .normalize(\"NFD\")\n            .replace(\/[\\u0300-\\u036f]\/g, \"\")\n            .replace(\/[^\\p{L}\\p{N}]+\/gu, \" \")\n            .trim();\n    }\n\n    function getYear(value) {\n        if (!value) return \"\";\n        const match = String(value).match(\/\\b(18|19|20)\\d{2}\\b\/);\n        return match ? match[0] : \"\";\n    }\n\n    function getTMDBType() {\n        const value = typeFilterEl.value;\n        if (value === \"film\") return [\"movie\"];\n        if (value === \"animation\") return [\"animation\"];\n        if (value === \"film_animation\") return [\"movie\", \"animation\"];\n        if (value === \"all\") return [\"movie\", \"animation\"];\n        return [\"movie\"];\n    }\n\n    function getSelectedLanguagesTMDB() {\n        if (allLanguagesCheckboxEl && allLanguagesCheckboxEl.checked) return [];\n        if (!languageOptionsContainerEl) return [];\n        const checked = languageOptionsContainerEl.querySelectorAll('input[type=\"checkbox\"]:checked');\n        const languages = [];\n        checked.forEach(function (checkbox) {\n            const language = checkbox.value || checkbox.dataset.lang || checkbox.dataset.language || \"\";\n            if (language) languages.push(language.toLowerCase());\n        });\n        return [...new Set(languages)];\n    }\n\n    async function findKeywordId(keyword) {\n        const params = new URLSearchParams({ api_key: TMDB_API_KEY, query: keyword, page: \"1\" });\n        const response = await fetch(TMDB_API + \"\/search\/keyword?\" + params.toString());\n        if (!response.ok) throw new Error(\"Recherche de mot-cl\u00e9 TMDB : HTTP \" + response.status);\n        const data = await response.json();\n        if (!data.results || !data.results.length) return null;\n        const exact = data.results.find(function (item) {\n            return normalizeTitle(item.name) === normalizeTitle(keyword);\n        });\n        return (exact || data.results[0]).id;\n    }\n\n    async function getKeywordIds() {\n        const results = await Promise.all(\n            CHRISTIAN_KEYWORDS.map(function (keyword) {\n                return findKeywordId(keyword).catch(function (error) {\n                    console.warn(\"Mot-cl\u00e9 ignor\u00e9 :\", keyword, error);\n                    return null;\n                });\n            })\n        );\n        return [...new Set(results.filter(Boolean))];\n    }\n\n    async function fetchTMDBPage(keywordsParam, page, animationOnly, originalLanguage) {\n        const params = new URLSearchParams({\n            api_key: TMDB_API_KEY,\n            language: TMDB_LANGUAGE,\n            region: TMDB_REGION,\n            with_keywords: keywordsParam,\n            sort_by: \"primary_release_date.desc\",\n            include_adult: \"false\",\n            page: String(page)\n        });\n        if (animationOnly) params.set(\"with_genres\", \"16\");\n        if (originalLanguage) params.set(\"with_original_language\", originalLanguage);\n        const response = await fetch(TMDB_API + \"\/discover\/movie?\" + params.toString());\n        if (!response.ok) throw new Error(\"TMDB Discover : HTTP \" + response.status);\n        return await response.json();\n    }\n\n    function isExistingResult(movie) {\n        if (!Array.isArray(currentResults)) return false;\n        const tmdbId = String(movie.id);\n        const title = normalizeTitle(movie.title || movie.original_title);\n        const year = getYear(movie.release_date);\n\n        return currentResults.some(function (existing) {\n            if (existing.tmdbId && String(existing.tmdbId) === tmdbId) return true;\n            const existingTitle = normalizeTitle(existing.title);\n            const existingYear = getYear(existing.date || existing.year || existing.release_date);\n            if (title && existingTitle && title === existingTitle && year && existingYear && year === existingYear) return true;\n            if (title && existingTitle && title === existingTitle && (!year || !existingYear)) return true;\n            return false;\n        });\n    }\n\n    function convertTMDBResult(movie) {\n        const title = movie.title || movie.original_title || \"Titre non renseign\u00e9\";\n        return {\n            uri: \"tmdb:\" + movie.id,\n            title: title,\n            poster: \"\",\n            image: \"\",\n            directors: \"\",\n            cast: \"\",\n            actors: \"\",\n            date: movie.release_date || \"\",\n            year: getYear(movie.release_date),\n            description: movie.overview || \"\",\n            imdb: \"\",\n            website: \"\",\n            primaryLink: \"https:\/\/www.themoviedb.org\/movie\/\" + movie.id,\n            tmdbId: String(movie.id),\n            source: \"TMDB\",\n            isTMDB: true\n        };\n    }\n\n    function addTMDBResults(results, map) {\n        (results || []).forEach(function (movie) {\n            const title = normalizeTitle(movie.title || movie.original_title);\n            const year = getYear(movie.release_date);\n            const key = title + \"|\" + year;\n            if (!map.has(key)) map.set(key, movie);\n        });\n    }\n\n    \/\/ Ajoute au filtre de langue existant (celui utilis\u00e9 pour Wikidata) toute\n    \/\/ langue d'origine rencontr\u00e9e parmi les films TMDB r\u00e9cup\u00e9r\u00e9s et qui n'y\n    \/\/ figurait pas d\u00e9j\u00e0. N'entoche pas les cases d\u00e9j\u00e0 coch\u00e9es par\n    \/\/ l'utilisateur \u2014 les nouvelles langues sont ajout\u00e9es d\u00e9coch\u00e9es.\n    function addDiscoveredLanguages(movies) {\n\n        if (!languageOptionsContainerEl) return;\n\n        const existingCodes = new Set(\n            Array.from(languageOptionsContainerEl.querySelectorAll(\".wdfc-languageCheckbox\"))\n                .map(function (cb) { return cb.value; })\n        );\n\n        const newCodes = new Set();\n\n        movies.forEach(function (movie) {\n            const code = String(movie.original_language || \"\").toLowerCase();\n            if (code && !existingCodes.has(code)) newCodes.add(code);\n        });\n\n        if (!newCodes.size) return;\n\n        Array.from(newCodes).sort().forEach(function (code) {\n\n            const label = document.createElement(\"label\");\n\n            const checkbox = document.createElement(\"input\");\n            checkbox.type = \"checkbox\";\n            checkbox.className = \"wdfc-languageCheckbox\";\n            checkbox.value = code;\n\n            const span = document.createElement(\"span\");\n            span.textContent = (typeof languageDisplayName === \"function\" ? languageDisplayName(code) : code) + \" (\" + code + \")\";\n\n            label.appendChild(checkbox);\n            label.appendChild(span);\n            languageOptionsContainerEl.appendChild(label);\n\n            existingCodes.add(code);\n        });\n\n        if (languageCountLabel) {\n            languageCountLabel.textContent = \"(\" + existingCodes.size + \" langues trouv\u00e9es)\";\n        }\n    }\n\n    async function fetchTMDBPagesInBatches(keywordsParam, pageNumbers, animationOnly, language, unique, target, comboLabel) {\n\n        for (let i = 0; i < pageNumbers.length; i += TMDB_PAGE_BATCH_SIZE) {\n\n            if (unique.size >= target) return;\n\n            const batch = pageNumbers.slice(i, i + TMDB_PAGE_BATCH_SIZE);\n\n            setTMDBStatus(\n                \"\u23f3 TMDB : \" + comboLabel + \" \u2014 pages \" + batch[0] + \" \u00e0 \" + batch[batch.length - 1]\n                + \" \/ \" + pageNumbers[pageNumbers.length - 1]\n                + \" (\" + unique.size + \" film(s) trouv\u00e9(s) pour l'instant)\"\n            );\n\n            const pages = await Promise.all(\n                batch.map(function (p) {\n                    return fetchTMDBPage(keywordsParam, p, animationOnly, language)\n                        .catch(function () { return null; });\n                })\n            );\n\n            pages.forEach(function (data) {\n                if (data) addTMDBResults(data.results, unique);\n            });\n        }\n    }\n\n    async function collectTMDBResults() {\n\n        const selectedTypes = getTMDBType();\n        const languages = getSelectedLanguagesTMDB(); \/\/ [] = toutes les langues, sinon liste de codes ISO\n        const maxRaw = maxResultsInputEl.value.trim();\n        const maxResults = maxRaw ? Math.max(1, Number(maxRaw)) : null;\n        \/\/ Sans \"Nombre maxi de r\u00e9sultats\", on r\u00e9cup\u00e8re tout ce que TMDB peut\n        \/\/ renvoyer pour ces mots-cl\u00e9s (born\u00e9 uniquement par la vraie limite\n        \/\/ de l'API, TMDB_MAX_PAGES) plut\u00f4t qu'un plafond arbitraire.\n        const target = maxResults || Infinity;\n\n        const keywordIds = await getKeywordIds();\n        if (!keywordIds.length) {\n            throw new Error(\"Aucun mot-cl\u00e9 chr\u00e9tien reconnu par TMDB.\");\n        }\n\n        \/\/ Tous les mots-cl\u00e9s sont combin\u00e9s en UNE seule requ\u00eate (logique OU,\n        \/\/ s\u00e9parateur \"|\" c\u00f4t\u00e9 API TMDB) au lieu d'une requ\u00eate par mot-cl\u00e9 :\n        \/\/ \u00e7a divise le nombre d'appels r\u00e9seau par ~16 par rapport \u00e0 l'ancienne\n        \/\/ version.\n        const keywordsParam = keywordIds.join(\"|\");\n\n        const unique = new Map();\n\n        \/\/ Un \"seau\" par langue s\u00e9lectionn\u00e9e : si l'utilisateur a coch\u00e9 des\n        \/\/ langues pr\u00e9cises, on demande directement \u00e0 TMDB de filtrer par\n        \/\/ langue d'origine (with_original_language) au lieu de tout\n        \/\/ t\u00e9l\u00e9charger puis trier c\u00f4t\u00e9 navigateur.\n        const languageBuckets = languages.length ? languages : [null];\n\n        outer:\n        for (const type of selectedTypes) {\n\n            const animationOnly = type === \"animation\";\n\n            for (const language of languageBuckets) {\n\n                if (unique.size >= target) break outer;\n\n                const comboLabel = type + (language ? \" \/ \" + language : \"\");\n\n                setTMDBStatus(\"\u23f3 TMDB : recherche \" + comboLabel + \"...\");\n\n                try {\n\n                    const firstPage = await fetchTMDBPage(keywordsParam, 1, animationOnly, language);\n                    addTMDBResults(firstPage.results, unique);\n\n                    const totalPages = Math.min(Number(firstPage.total_pages || 1), TMDB_MAX_PAGES);\n\n                    if (totalPages > 1 && unique.size < target) {\n\n                        const pageNumbers = [];\n                        for (let p = 2; p <= totalPages; p++) pageNumbers.push(p);\n\n                        \/\/ Les pages sont r\u00e9cup\u00e9r\u00e9es par lots de plusieurs\n                        \/\/ requ\u00eates en parall\u00e8le (au lieu d'une boucle\n                        \/\/ s\u00e9quentielle page par page) : c'est ce qui acc\u00e9l\u00e8re\n                        \/\/ le plus la recherche, tout en restant raisonnable\n                        \/\/ pour ne pas se faire limiter par TMDB.\n                        await fetchTMDBPagesInBatches(\n                            keywordsParam, pageNumbers, animationOnly, language, unique, target, comboLabel\n                        );\n                    }\n                }\n                catch (error) {\n                    console.warn(\"TMDB : combo ignor\u00e9 (\" + comboLabel + \")\", error);\n                }\n            }\n        }\n\n        let movies = Array.from(unique.values());\n\n        addDiscoveredLanguages(movies);\n\n        if (sortSelectEl.value === \"oldest\") {\n            movies.sort(function (a, b) {\n                return String(a.release_date || \"\").localeCompare(String(b.release_date || \"\"));\n            });\n        }\n        else if (sortSelectEl.value === \"title\") {\n            movies.sort(function (a, b) {\n                return normalizeTitle(a.title).localeCompare(normalizeTitle(b.title));\n            });\n        }\n        else {\n            movies.sort(function (a, b) {\n                return String(b.release_date || \"\").localeCompare(String(a.release_date || \"\"));\n            });\n        }\n\n        if (maxResults) movies = movies.slice(0, maxResults);\n\n        return movies;\n    }\n\n    async function searchTMDB() {\n\n        const button = document.getElementById(\"wdfc-tmdbPlusButton\");\n        if (button.disabled) return;\n\n        if (religiousFilterEl && !religiousFilterEl.checked) {\n            const proceed = confirm(\n                \"Le filtre \u00ab Films chr\u00e9tiens \/ religieux \u00bb est d\u00e9sactiv\u00e9.\\n\\n\"\n                + \"La recherche TMDB de ce bouton est sp\u00e9cialement con\u00e7ue \"\n                + \"pour les films chr\u00e9tiens et religieux.\\n\\n\"\n                + \"Voulez-vous continuer ?\"\n            );\n            if (!proceed) return;\n        }\n\n        button.disabled = true;\n        if (progressBarEl) progressBarEl.style.width = \"10%\";\n\n        try {\n\n            setTMDBStatus(\"\u23f3 Pr\u00e9paration de la recherche TMDB...\");\n\n            const movies = await collectTMDBResults();\n\n            if (progressBarEl) progressBarEl.style.width = \"70%\";\n\n            let added = 0;\n            let duplicates = 0;\n\n            movies.forEach(function (movie) {\n                if (isExistingResult(movie)) {\n                    duplicates++;\n                    return;\n                }\n                const converted = convertTMDBResult(movie);\n                currentResults.push(converted);\n                added++;\n            });\n\n            if (sortSelectEl.value === \"title\") {\n                currentResults.sort(function (a, b) {\n                    return normalizeTitle(a.title).localeCompare(normalizeTitle(b.title));\n                });\n            }\n            else if (sortSelectEl.value === \"oldest\") {\n                currentResults.sort(function (a, b) {\n                    return String(a.date || \"\").localeCompare(String(b.date || \"\"));\n                });\n            }\n            else {\n                currentResults.sort(function (a, b) {\n                    return String(b.date || \"\").localeCompare(String(a.date || \"\"));\n                });\n            }\n\n            if (typeof renderResults === \"function\") renderResults();\n\n            tmdbTotalCount = currentResults.filter(function (f) { return f.isTMDB; }).length;\n            if (typeof updateTotalCounter === \"function\") updateTotalCounter();\n\n            if (progressBarEl) progressBarEl.style.width = \"100%\";\n\n            setTMDBStatus(\n                \"\u2705 TMDB termin\u00e9 : \" + added + \" nouveau(x) film(s) ajout\u00e9(s). \"\n                + duplicates + \" doublon(s) ignor\u00e9(s).\"\n            );\n\n        }\n        catch (error) {\n            console.error(\"TMDB Plus :\", error);\n            setTMDBStatus(\"\u274c Erreur TMDB : \" + error.message);\n            if (progressBarEl) progressBarEl.style.width = \"100%\";\n        }\n        finally {\n            button.disabled = false;\n        }\n    }\n\n    createTMDBButton();\n\n    console.log(\"\u2705 Module TMDB \u00ab Plus de films \u00bb charg\u00e9.\");\n\n})();\n\n} \/\/ fin de init()\n\nif (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", init);\n}\nelse {\n    init();\n}\n\n})();\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Test Recherche libre Rechercher dans TitreR\u00e9alisateurActeurTitre + r\u00e9alisateur + acteur &nbsp; \ud83d\udd0e Rechercher Ouvrir les films sur JustWatchAlloCin\u00e9IMDbURL personnalis\u00e9e Couleur des titres de films URL personnalis\u00e9e (si s\u00e9lectionn\u00e9e ci-dessus) Utilisez {titre} dans l&rsquo;URL pour qu&rsquo;il soit remplac\u00e9 par le titre du film \u00e0 chaque clic. Sans {titre}, le titre est ajout\u00e9 automatiquement en param\u00e8tre q= \u00e0 la fin de l&rsquo;URL saisie. Les films venant de TMDB (bouton \u00ab\u00a0Plus de films\u00a0\u00bb) s&rsquo;ouvrent toujours directement sur TMDB, quel que soit ce choix \u2014 voir note plus bas. Date de sortie Toutes les datesAvec date de sortieSans date de sortie Derniers jours Derniers mois Ann\u00e9e (2026) ou N derni\u00e8res ann\u00e9es (3) Ann\u00e9e de (fourchette) Ann\u00e9e \u00e0 (fourchette) Date pr\u00e9cise de d\u00e9but Date pr\u00e9cise de fin Titre Tous les filmsAvec un titreSans titre Type Films uniquementDessins anim\u00e9s uniquementFilms + dessins anim\u00e9sFilms et \u0153uvres audiovisuelles (large) Tri Plus r\u00e9centsPlus anciensTitre A-Z R\u00e9sultats par page Nombre maxi de r\u00e9sultats Langue(s) du titre Chargement des langues disponibles dans la base&#8230; \ud83c\udf10 Toutes les langues (ignore la liste ci-dessus) Cochez une, plusieurs, ou toutes les langues souhait\u00e9es. Cochez \u00ab Toutes les langues \u00bb pour ne faire aucun tri par langue. \u271d\ufe0f Films chr\u00e9tiens \/ religieux (genre \/ sujet Wikidata) \u00c9largir la recherche (mots-cl\u00e9s dans le titre) Chr\u00e9tien Religieux Biblique Spirituel Croyance \u00c9vangile Dieu \/ \u00c9ternel Exclure les films contenant ces mots (titre) &nbsp; \u23f9 Annuler &nbsp; \ud83e\uddf9 R\u00e9initialiser Aller \u00e0 la page &nbsp; \ud83d\udcc4 Aller \u00e0 la page &nbsp; \u2b07\ufe0f Exporter CSV &nbsp; \ud83d\udd04 Rechercher &nbsp; \u25b6\ufe0f S\u00e9lection sur YouTube &nbsp; \ud83c\udfa6 Vue Cin\u00e9ma Pr\u00eat. \u25c0 Pr\u00e9c\u00e9dent Page 1 \/ 1 Suivant \u25b6<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"page-builder","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":"","_members_access_role":[],"_members_access_error":""},"class_list":["post-13096","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=\/wp\/v2\/pages\/13096","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=13096"}],"version-history":[{"count":10,"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=\/wp\/v2\/pages\/13096\/revisions"}],"predecessor-version":[{"id":32032,"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=\/wp\/v2\/pages\/13096\/revisions\/32032"}],"wp:attachment":[{"href":"https:\/\/allianceenjesus.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=13096"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}