xref: /plugin/catmenu/script.js (revision 6983cdfd4483215ff5a1e573925c9c612964e790)
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