1*6983cdfdSLORTET(function loadCatmenuProsemirrorAddon() { 2*6983cdfdSLORTET if (window.__catmenuPmAddonRequested) return; 3*6983cdfdSLORTET window.__catmenuPmAddonRequested = true; 4*6983cdfdSLORTET 5*6983cdfdSLORTET var base = (typeof DOKU_BASE !== 'undefined' && DOKU_BASE) ? DOKU_BASE : '/'; 6*6983cdfdSLORTET var src = base + 'lib/plugins/catmenu/script/prosemirror.js'; 7*6983cdfdSLORTET var script = document.createElement('script'); 8*6983cdfdSLORTET script.src = src; 9*6983cdfdSLORTET script.defer = true; 10*6983cdfdSLORTET document.head.appendChild(script); 11*6983cdfdSLORTET})(); 12*6983cdfdSLORTET 13*6983cdfdSLORTETfunction catmenu_adjustSubmenuHeight(submenu, menuContainer) { 14*6983cdfdSLORTET var allTitles = menuContainer.querySelectorAll(`.menu-item .header:not(.menu-item .menu-item .header)`); 15*6983cdfdSLORTET 16*6983cdfdSLORTET // Hauteur totale du conteneur 17*6983cdfdSLORTET var containerHeight = menuContainer.clientHeight; 18*6983cdfdSLORTET 19*6983cdfdSLORTET // Calculer la hauteur des titres en incluant bordures, padding et margin-top 20*6983cdfdSLORTET var titlesHeight = Array.from(allTitles).reduce((sum, title) => { 21*6983cdfdSLORTET var computedStyle = window.getComputedStyle(title.parentElement); 22*6983cdfdSLORTET var marginTop = parseInt(computedStyle.marginTop) || 0; 23*6983cdfdSLORTET return sum + title.offsetHeight + marginTop; 24*6983cdfdSLORTET }, 0); 25*6983cdfdSLORTET 26*6983cdfdSLORTET // Définir la hauteur maximale en prenant en compte le padding et le margin-top 27*6983cdfdSLORTET var availableHeight = Math.max(Math.max(containerHeight, window.innerHeight) - titlesHeight - menuContainer.getBoundingClientRect().top, 500); 28*6983cdfdSLORTET 29*6983cdfdSLORTET submenu.style.maxHeight = availableHeight + "px"; 30*6983cdfdSLORTET 31*6983cdfdSLORTET return availableHeight; 32*6983cdfdSLORTET} 33*6983cdfdSLORTET 34*6983cdfdSLORTETfunction copyToClipboard(text) { 35*6983cdfdSLORTET if (!text) return; 36*6983cdfdSLORTET 37*6983cdfdSLORTET // Méthode moderne (HTTPS requis) 38*6983cdfdSLORTET if (navigator.clipboard && window.isSecureContext) { 39*6983cdfdSLORTET navigator.clipboard.writeText(text) 40*6983cdfdSLORTET .then(() => { 41*6983cdfdSLORTET alert("URL copiée dans le presse-papiers !"); 42*6983cdfdSLORTET }) 43*6983cdfdSLORTET .catch(err => { 44*6983cdfdSLORTET console.error("Erreur lors de la copie :", err); 45*6983cdfdSLORTET }); 46*6983cdfdSLORTET } 47*6983cdfdSLORTET // Fallback pour anciens navigateurs 48*6983cdfdSLORTET else { 49*6983cdfdSLORTET const textarea = document.createElement("textarea"); 50*6983cdfdSLORTET textarea.value = text; 51*6983cdfdSLORTET textarea.style.position = "fixed"; 52*6983cdfdSLORTET textarea.style.opacity = "0"; 53*6983cdfdSLORTET document.body.appendChild(textarea); 54*6983cdfdSLORTET textarea.focus(); 55*6983cdfdSLORTET textarea.select(); 56*6983cdfdSLORTET 57*6983cdfdSLORTET try { 58*6983cdfdSLORTET document.execCommand("copy"); 59*6983cdfdSLORTET alert("URL copiée dans le presse-papiers !"); 60*6983cdfdSLORTET } catch (err) { 61*6983cdfdSLORTET console.error("Erreur lors de la copie :", err); 62*6983cdfdSLORTET } 63*6983cdfdSLORTET 64*6983cdfdSLORTET document.body.removeChild(textarea); 65*6983cdfdSLORTET } 66*6983cdfdSLORTET} 67*6983cdfdSLORTET 68*6983cdfdSLORTET// Appeler cette fonction à l'ouverture d'un sous-menu 69*6983cdfdSLORTETfunction catmenu_toggleSectionMenu(menuItem, menuContainer) { 70*6983cdfdSLORTET let isOpen = !menuItem.classList.contains('open'); 71*6983cdfdSLORTET 72*6983cdfdSLORTET let topMenuItems = Array.from(menuContainer.querySelectorAll('.menu-item:not(.menu-item .menu-item)')); 73*6983cdfdSLORTET 74*6983cdfdSLORTET let currentTopMenu = topMenuItems.find(item => item.contains(menuItem)); 75*6983cdfdSLORTET let othersTopMenu = Array.from(topMenuItems).filter(item => item !== currentTopMenu); 76*6983cdfdSLORTET 77*6983cdfdSLORTET othersTopMenu.forEach(item => { 78*6983cdfdSLORTET item.classList.remove("open"); 79*6983cdfdSLORTET let submenu = item.getElementsByClassName('submenu')[0]; 80*6983cdfdSLORTET if(submenu) { 81*6983cdfdSLORTET submenu.style.maxHeight = null; 82*6983cdfdSLORTET } 83*6983cdfdSLORTET }); 84*6983cdfdSLORTET 85*6983cdfdSLORTET let currentTopSubmenu = currentTopMenu.getElementsByClassName('submenu')[0]; 86*6983cdfdSLORTET if(isOpen) { 87*6983cdfdSLORTET let newHeight = catmenu_adjustSubmenuHeight(currentTopSubmenu, menuContainer); 88*6983cdfdSLORTET 89*6983cdfdSLORTET setTimeout(() => { 90*6983cdfdSLORTET let hasOverflow = currentTopSubmenu.scrollHeight > newHeight; 91*6983cdfdSLORTET if(hasOverflow) { 92*6983cdfdSLORTET console.log(menuItem.offsetTop-currentTopSubmenu.offsetTop, currentTopSubmenu, currentTopSubmenu.offsetTop, menuItem, menuItem.offsetTop); 93*6983cdfdSLORTET currentTopSubmenu.scrollTo({ 94*6983cdfdSLORTET top: menuItem.offsetTop-currentTopSubmenu.offsetTop-5-10 95*6983cdfdSLORTET }); 96*6983cdfdSLORTET } 97*6983cdfdSLORTET }, 0); 98*6983cdfdSLORTET } 99*6983cdfdSLORTET else { 100*6983cdfdSLORTET currentTopSubmenu.style.maxHeight = null; 101*6983cdfdSLORTET } 102*6983cdfdSLORTET menuItem.classList.toggle('open'); 103*6983cdfdSLORTET} 104*6983cdfdSLORTET 105*6983cdfdSLORTETfunction catmenu_generateSectionMenu(conf, menuData, parentElement, menuContainer = null) { 106*6983cdfdSLORTET const URL_SEPARATOR = conf.userewrite == 1? '/' : ':'; 107*6983cdfdSLORTET 108*6983cdfdSLORTET const AUTH_READ = 1; 109*6983cdfdSLORTET const AUTH_EDIT = 2; 110*6983cdfdSLORTET const AUTH_CREATE = 4; 111*6983cdfdSLORTET const AUTH_UPLOAD = 8; 112*6983cdfdSLORTET const AUTH_DELETE = 16; 113*6983cdfdSLORTET 114*6983cdfdSLORTET menuData.forEach(item => { // Pour tous les éléments 115*6983cdfdSLORTET let menuItem = document.createElement("div"); 116*6983cdfdSLORTET menuItem.classList.add("menu-item"); 117*6983cdfdSLORTET 118*6983cdfdSLORTET let header = document.createElement("div"); 119*6983cdfdSLORTET header.classList.add("header"); 120*6983cdfdSLORTET header.dataset.folderNamespace = item.folderNamespace; 121*6983cdfdSLORTET header.dataset.permission = item.permission; 122*6983cdfdSLORTET header.dataset.pagesiconUploadUrl = item.pagesiconUploadUrl || ''; 123*6983cdfdSLORTET 124*6983cdfdSLORTET if (item.icon) { 125*6983cdfdSLORTET let icon = document.createElement("img"); 126*6983cdfdSLORTET icon.classList.add("icon"); 127*6983cdfdSLORTET icon.loading = "lazy"; 128*6983cdfdSLORTET icon.src = item.icon; 129*6983cdfdSLORTET header.appendChild(icon); 130*6983cdfdSLORTET } 131*6983cdfdSLORTET 132*6983cdfdSLORTET let title; 133*6983cdfdSLORTET if(item.url) { 134*6983cdfdSLORTET title = document.createElement("a"); 135*6983cdfdSLORTET title.href = item.url; 136*6983cdfdSLORTET header.dataset.href = item.url; 137*6983cdfdSLORTET } 138*6983cdfdSLORTET else { 139*6983cdfdSLORTET title = document.createElement("span"); 140*6983cdfdSLORTET } 141*6983cdfdSLORTET title.textContent = item.title; 142*6983cdfdSLORTET header.title = item.title; 143*6983cdfdSLORTET header.appendChild(title); 144*6983cdfdSLORTET 145*6983cdfdSLORTET let isCurrent = (':' + JSINFO.id + ':').indexOf(':' + item.namespace + ':') >= 0; 146*6983cdfdSLORTET if(isCurrent) { 147*6983cdfdSLORTET header.classList.add("current"); 148*6983cdfdSLORTET menuItem.classList.add("open"); 149*6983cdfdSLORTET } 150*6983cdfdSLORTET menuItem.appendChild(header); 151*6983cdfdSLORTET 152*6983cdfdSLORTET parentElement.appendChild(menuItem); 153*6983cdfdSLORTET 154*6983cdfdSLORTET if (item.subtree && item.subtree.length > 0) { 155*6983cdfdSLORTET header.classList.add("arrow"); 156*6983cdfdSLORTET 157*6983cdfdSLORTET let submenu = document.createElement("div"); 158*6983cdfdSLORTET submenu.classList.add("submenu"); 159*6983cdfdSLORTET 160*6983cdfdSLORTET catmenu_generateSectionMenu(conf, item.subtree, submenu, menuContainer ?? parentElement); 161*6983cdfdSLORTET menuItem.appendChild(submenu); 162*6983cdfdSLORTET 163*6983cdfdSLORTET header.addEventListener("click", (event) => { 164*6983cdfdSLORTET const isLinkClick = !!event.target.closest("a"); 165*6983cdfdSLORTET if (isLinkClick) { 166*6983cdfdSLORTET // Clicking section link should never close it. 167*6983cdfdSLORTET if (!menuItem.classList.contains("open")) { 168*6983cdfdSLORTET catmenu_toggleSectionMenu(menuItem, menuContainer ?? parentElement); 169*6983cdfdSLORTET } 170*6983cdfdSLORTET return; 171*6983cdfdSLORTET } 172*6983cdfdSLORTET catmenu_toggleSectionMenu(menuItem, menuContainer ?? parentElement); 173*6983cdfdSLORTET }); 174*6983cdfdSLORTET } 175*6983cdfdSLORTET }); 176*6983cdfdSLORTET 177*6983cdfdSLORTET 178*6983cdfdSLORTET if(!menuContainer) { 179*6983cdfdSLORTET window.addEventListener('resize', () => { 180*6983cdfdSLORTET let openedTopSubmenu = parentElement.querySelector('.menu-item:not(.menu-item .menu-item).open > .submenu'); 181*6983cdfdSLORTET if(openedTopSubmenu) { 182*6983cdfdSLORTET catmenu_adjustSubmenuHeight(openedTopSubmenu, parentElement); 183*6983cdfdSLORTET } 184*6983cdfdSLORTET }, false); 185*6983cdfdSLORTET 186*6983cdfdSLORTET let contextMenu = document.getElementById('catmenu_contextMenu'); 187*6983cdfdSLORTET if(!contextMenu) { 188*6983cdfdSLORTET contextMenu = document.createElement('div'); 189*6983cdfdSLORTET contextMenu.id = 'catmenu_contextMenu'; 190*6983cdfdSLORTET document.body.appendChild(contextMenu); 191*6983cdfdSLORTET document.addEventListener('click', function() { 192*6983cdfdSLORTET contextMenu.style.display = 'none'; 193*6983cdfdSLORTET }); 194*6983cdfdSLORTET 195*6983cdfdSLORTET contextMenu.addEventListener('click', function() { 196*6983cdfdSLORTET console.log(`Click`, contextMenu.dataset.namespace); 197*6983cdfdSLORTET }); 198*6983cdfdSLORTET } 199*6983cdfdSLORTET 200*6983cdfdSLORTET function appendToUrl(base, params) { 201*6983cdfdSLORTET return base + (base.indexOf('?') >= 0? '&' : '?') + params; 202*6983cdfdSLORTET } 203*6983cdfdSLORTET 204*6983cdfdSLORTET document.addEventListener('contextmenu', function(event) { 205*6983cdfdSLORTET let header = event.target.closest(".header"); // Vérifie si clic sur un menu 206*6983cdfdSLORTET if (!header || !header.closest('.catmenu')) { 207*6983cdfdSLORTET contextMenu.style.display = 'none'; 208*6983cdfdSLORTET return; 209*6983cdfdSLORTET } 210*6983cdfdSLORTET 211*6983cdfdSLORTET if(!parentElement.contains(header)) { 212*6983cdfdSLORTET return; 213*6983cdfdSLORTET } 214*6983cdfdSLORTET event.preventDefault(); // Empêche le menu par défaut 215*6983cdfdSLORTET 216*6983cdfdSLORTET let permission = header.dataset.permission; 217*6983cdfdSLORTET 218*6983cdfdSLORTET let htmlActions = ''; 219*6983cdfdSLORTET if(permission >= AUTH_CREATE) { 220*6983cdfdSLORTET let baseHref = header.dataset.href; 221*6983cdfdSLORTET if(baseHref.endsWith(URL_SEPARATOR + conf.start)) { 222*6983cdfdSLORTET baseHref = baseHref.slice(0, -(URL_SEPARATOR + conf.start).length); 223*6983cdfdSLORTET } 224*6983cdfdSLORTET htmlActions += '<div class="button" data-action="newPage" onclick="let nomPage = prompt(\'Identifiant de la page à créer ?\'); if(nomPage) window.location.href = \'' + appendToUrl(baseHref + URL_SEPARATOR + '\' + encodeURIComponent(nomPage) + \'' + URL_SEPARATOR + conf.start, 'do=edit') + '\';"> Créer une nouvelle page</div>'; 225*6983cdfdSLORTET } 226*6983cdfdSLORTET if(header.dataset.href && permission >= AUTH_EDIT) { 227*6983cdfdSLORTET htmlActions += '<a class="button" data-action="reload" href="' + appendToUrl(header.dataset.href, 'purge=true') + '"> Recharger le cache</a>'; 228*6983cdfdSLORTET } 229*6983cdfdSLORTET if(permission >= AUTH_UPLOAD) { 230*6983cdfdSLORTET htmlActions += '<a class="button" data-action="medias" target="_blank" href="' + appendToUrl('/lib/exe/mediamanager.php', 'ns=' + header.dataset.folderNamespace) + '" onclick="event.preventDefault();window.open(\'' + appendToUrl('/lib/exe/mediamanager.php', 'ns=' + header.dataset.folderNamespace) + '\', \'MediasPopup\', \'width=800,height=600,resizable=yes,scrollbars=yes\');">️ Gérer les médias</a>'; 231*6983cdfdSLORTET if (header.dataset.pagesiconUploadUrl) { 232*6983cdfdSLORTET htmlActions += '<a class="button" data-action="icon" target="_blank" href="' + header.dataset.pagesiconUploadUrl + '">️ Gérer l\'icône</a>'; 233*6983cdfdSLORTET } 234*6983cdfdSLORTET } 235*6983cdfdSLORTET htmlActions += '<div class="button" data-action="copy" onclick="copyToClipboard(\'' + header.dataset.href + '\')"> Copier l\'url</div>'; 236*6983cdfdSLORTET 237*6983cdfdSLORTET if(htmlActions) { 238*6983cdfdSLORTET let html = '<p><b>' + header.innerText + '</b></p>' + htmlActions; 239*6983cdfdSLORTET contextMenu.style.left = `${event.clientX}px`; 240*6983cdfdSLORTET contextMenu.style.top = `${event.clientY}px`; 241*6983cdfdSLORTET contextMenu.style.display = "block"; 242*6983cdfdSLORTET contextMenu.innerHTML = html; 243*6983cdfdSLORTET 244*6983cdfdSLORTET // Stocke l'élément cliqué pour utilisation 245*6983cdfdSLORTET contextMenu.dataset.namespace = header.dataset.namespace; 246*6983cdfdSLORTET contextMenu.dataset.folderNamespace = header.dataset.folderNamespace; 247*6983cdfdSLORTET } 248*6983cdfdSLORTET }, false); 249*6983cdfdSLORTET } 250*6983cdfdSLORTET} 251*6983cdfdSLORTET 252*6983cdfdSLORTET// The syntax renderer calls this function from inline <script>. 253*6983cdfdSLORTET// Expose it explicitly on window because bundled scopes may hide declarations. 254*6983cdfdSLORTETif (typeof window !== 'undefined') { 255*6983cdfdSLORTET window.catmenu_generateSectionMenu = catmenu_generateSectionMenu; 256*6983cdfdSLORTET if (typeof document !== 'undefined') { 257*6983cdfdSLORTET document.dispatchEvent(new Event('catmenu:ready')); 258*6983cdfdSLORTET } 259*6983cdfdSLORTET} 260