/** * Page d'édition de post (post.php / post-new.php) : onglets FR/EN du corps, * réordonnancement des metaboxes, groupement des axes, visibilité * conditionnelle des boxes Pods, popovers d'aide, fixes TinyMCE/Gutenberg. * Dépend de admin-base.js (window.ThalimAdmin) — enqueue conditionnel. */ (function($) { 'use strict'; var TA = window.ThalimAdmin; var CONFIG = TA.CONFIG; var safeRun = TA.safeRun; // Phase 1: insert the tab bar and relocate #pods-meta-body-en. // The DOM move breaks TinyMCE's iframe (browsers reset iframe content on detach), // so we leave the container visible here and let Pods/TinyMCE initialise normally. // The broken iframe is repaired by reinitEditor() on first EN tab open. function setupBodyTabsDom() { var nativeEditor = document.getElementById('postdivrich') || document.getElementById('postdiv'); var bodyEnBox = document.querySelector(CONFIG.boxes.bodyEn); if (!nativeEditor || !bodyEnBox) return; var tabBar = document.createElement('div'); tabBar.className = 'body-lang-tabs'; tabBar.innerHTML = '' + ''; nativeEditor.parentNode.insertBefore(tabBar, nativeEditor); // Move EN metabox to sit right after the native editor for correct visual layout. // Do NOT hide it yet — Pods must init TinyMCE with the container visible so the // iframe can measure its dimensions. Page is still opacity:0 so no flash. nativeEditor.parentNode.insertBefore(bodyEnBox, nativeEditor.nextSibling); } // Phase 2: wire tab click handlers — runs at t=100ms after metabox reordering. function initBodyLanguageTabs() { var nativeEditor = document.getElementById('postdivrich') || document.getElementById('postdiv'); var bodyEnBox = document.querySelector(CONFIG.boxes.bodyEn); var tabBar = document.querySelector('.body-lang-tabs'); if (!nativeEditor || !bodyEnBox || !tabBar) { // body_en not available (e.g. contributor role) — still force visual mode on main editor if (nativeEditor) TA.ensureVisualMode('content'); return; } var enEditorId = CONFIG.editors.bodyEn; var enTmceReady = false; // Hide EN panel — page is still opacity:0, user won't see the switch bodyEnBox.style.display = 'none'; tabBar.querySelectorAll('.body-lang-tab').forEach(function(btn) { btn.addEventListener('click', function() { tabBar.querySelectorAll('.body-lang-tab').forEach(function(b) { b.classList.remove('is-active'); }); btn.classList.add('is-active'); var revealedPanel; if (btn.dataset.panel === 'fr') { bodyEnBox.style.display = 'none'; nativeEditor.style.opacity = '0'; nativeEditor.style.display = ''; revealedPanel = nativeEditor; } else { nativeEditor.style.display = 'none'; bodyEnBox.style.opacity = '0'; bodyEnBox.style.display = 'block'; revealedPanel = bodyEnBox; if (!enTmceReady) { enTmceReady = true; // Reinit while container is visible so TinyMCE can measure dimensions TA.reinitEditor(enEditorId); } } // Notify TinyMCE to reflow, then fade in once layout is correct setTimeout(function() { window.dispatchEvent(new Event('resize')); requestAnimationFrame(function() { requestAnimationFrame(function() { revealedPanel.style.opacity = ''; }); }); }, 50); }); }); // Ensure both editors start in Visual (not Code) mode TA.ensureVisualMode('content'); TA.ensureVisualMode(enEditorId); } function groupAxesCheckboxes() { if (!window.thalimAxesGroups || !thalimAxesGroups.length) return; var row = document.querySelector('.' + CONFIG.rows.axes); if (!row) return; var list = row.querySelector('ul'); if (!list) return; // Already grouped — nothing to do if (list.querySelector('.axes-group-label')) return; // Map existing
  • by checkbox value; preserve "add new" button var liMap = {}; var addNewItem = null; list.querySelectorAll('li').forEach(function(li) { if (li.classList.contains('pods-pick-add-new')) { addNewItem = li; return; } var cb = li.querySelector('input[type="checkbox"]'); if (cb) liMap[cb.value] = li; }); // Rebuild list in group order list.innerHTML = ''; thalimAxesGroups.forEach(function(group) { var labelLi = document.createElement('li'); labelLi.className = 'axes-group-label'; labelLi.textContent = group.label; list.appendChild(labelLi); group.terms.forEach(function(term) { var li = liMap[String(term.id)]; if (li) list.appendChild(li); }); }); if (addNewItem) list.appendChild(addNewItem); } var REF_BIB_EDITOR_ID = CONFIG.editors.refBib; var refBibReinited = false; // Reinit the référence bibliographique TinyMCE editor. // Called at page load (if the field is already visible) and by the // MutationObserver (when the field becomes visible after a category change). function initRefBibEditor() { if (refBibReinited) return; var row = document.querySelector('.' + CONFIG.rows.refBib); if (!row || row.style.display === 'none') return; refBibReinited = true; TA.reinitEditor(REF_BIB_EDITOR_ID); TA.ensureVisualMode(REF_BIB_EDITOR_ID); } function initAxesGroupObserver() { // Pods shows/hides conditional rows by removing inline style="display:none" // Watch the entire Pods meta form for style changes on the axes row var podsForm = document.querySelector('.pods-pick-values, ' + CONFIG.boxes.champsContextuels + ', form#post'); if (!podsForm) podsForm = document.body; var observer = new MutationObserver(function(mutations) { for (var i = 0; i < mutations.length; i++) { var target = mutations[i].target; if (target.classList && target.classList.contains(CONFIG.rows.axes)) { if (target.style.display !== 'none') { setTimeout(groupAxesCheckboxes, 50); } } // Reinit TinyMCE on the référence bibliographique field when its // row becomes visible — Pods hides it with display:none which breaks // the TinyMCE iframe. Only reinit once per page load. if (!refBibReinited && target.classList && target.classList.contains(CONFIG.rows.refBib)) { if (target.style.display !== 'none') { setTimeout(initRefBibEditor, 100); } } } }); observer.observe(podsForm, { attributes: true, attributeFilter: ['style'], subtree: true }); } // Gutenberg's Popover component closes on outside click via focusout detection. // But if focus never enters the popover, focusout never fires and clicking outside // does nothing. Fix: focus the popover container as soon as it appears in the DOM. function initDatePickerPopoverFix() { var observer = new MutationObserver(function(mutations) { for (var i = 0; i < mutations.length; i++) { var added = mutations[i].addedNodes; for (var j = 0; j < added.length; j++) { var node = added[j]; if (node.nodeType !== 1) continue; var content = node.classList.contains('components-popover__content') ? node : node.querySelector && node.querySelector('.components-popover__content'); if (content) { var c = content; requestAnimationFrame(function() { if (!c.hasAttribute('tabindex')) c.setAttribute('tabindex', '-1'); c.focus(); }); } } } }); observer.observe(document.body, { childList: true, subtree: true }); } function updateMembresGridSeparator() { var sep = document.querySelector(CONFIG.boxes.membres + ' .membres-grid-separator'); if (!sep) return; var autreRows = document.querySelectorAll(CONFIG.boxes.membres + ' [class*="pods-form-ui-row-name-autre-"]'); var anyVisible = Array.from(autreRows).some(function(row) { return row.style.display !== 'none'; }); sep.style.display = anyVisible ? '' : 'none'; } function initPostEditPage() { // Disable category options (CSS handles the color) const categorieSelect = document.querySelector(CONFIG.categorySelect); if (categorieSelect) { categorieSelect.querySelectorAll('option').forEach(option => { if (CONFIG.disabledCategoryIds.includes(option.value)) { option.disabled = true; } }); } // Reorder meta boxes const sideSortables = document.querySelector('#side-sortables'); if (sideSortables) { const typeDannonce = document.querySelector(CONFIG.boxes.typeDannonce); const affichageAccueil = document.querySelector(CONFIG.boxes.affichageAccueil); const thematique = document.querySelector(CONFIG.boxes.thematique); if (typeDannonce) sideSortables.prepend(typeDannonce); if (affichageAccueil) sideSortables.appendChild(affichageAccueil); if (thematique) sideSortables.appendChild(thematique); } const submitDiv = document.querySelector('#submitdiv'); if (submitDiv && submitDiv.parentNode) { submitDiv.parentNode.appendChild(submitDiv); } const champsContextuels = document.querySelector(CONFIG.boxes.champsContextuels); if (champsContextuels && champsContextuels.parentNode) { champsContextuels.parentNode.prepend(champsContextuels); } // Chaque sous-module est isolé : une exception dans l'un // n'empêche pas les suivants de s'initialiser. safeRun('initBodyLanguageTabs', initBodyLanguageTabs); safeRun('initRefBibEditor', initRefBibEditor); safeRun('groupAxesCheckboxes', groupAxesCheckboxes); safeRun('initAxesGroupObserver', initAxesGroupObserver); safeRun('updatePostboxVisibility', TA.updatePostboxVisibility); safeRun('initDatePickerPopoverFix', initDatePickerPopoverFix); safeRun('initInfoPopovers', TA.initInfoPopovers); // Place #pods-meta-documents-joints in #normal-sortables, right after // #pods-meta-champs-contextuels. This keeps it out of #post-body-content // (the body editor section) regardless of whether champsContextuels is // currently visible. When champsContextuels is hidden it takes no space, // so documentsJoints simply appears first in #normal-sortables. const documentsJoints = document.querySelector(CONFIG.boxes.documentsJoints); if (documentsJoints) { if (champsContextuels && champsContextuels.parentNode) { champsContextuels.parentNode.insertBefore(documentsJoints, champsContextuels.nextSibling); } else { const normalSortables = document.querySelector('#normal-sortables'); if (normalSortables) normalSortables.prepend(documentsJoints); } } // Inject separator row for the Membres grid layout var membresTbody = document.querySelector(CONFIG.boxes.membres + ' .form-table tbody'); if (membresTbody && !membresTbody.querySelector('.membres-grid-separator')) { var sep = document.createElement('tr'); sep.className = 'membres-grid-separator'; membresTbody.appendChild(sep); } updateMembresGridSeparator(); } $(document).ready(function() { if (!TA.isPostEditPage()) return; safeRun('setupBodyTabsDom', setupBodyTabsDom); setTimeout(function() { safeRun('initPostEditPage', initPostEditPage); TA.markReady(); }, 100); $(CONFIG.categorySelect).change(function() { setTimeout(function() { safeRun('updatePostboxVisibility', TA.updatePostboxVisibility); safeRun('updateMembresGridSeparator', updateMembresGridSeparator); }, 10); }); }); })(jQuery);