xref: /plugin/codeblockedit/script.js (revision 9949c4fd50c301573218268366f7763bb5f6864a)
1jQuery(function() {
2    // Check if user has edit permission
3    if (!JSINFO.codeblockedit_canedit) {
4        return; // Don't show edit buttons if user can't edit
5    }
6
7    // Check if on a page with content (not in edit mode)
8    if (document.querySelector('#dw__editform')) {
9        return; // Don't add edit buttons while in edit mode
10    }
11
12    // Function to handle edit click
13    var handleEdit = function(event) {
14        event.preventDefault();
15        event.stopPropagation();
16
17        var $btn = jQuery(this);
18        var index = $btn.data('index');
19        var hid = 'codeblock_' + index;
20
21        // Redirect to standard DokuWiki editor with codeblockindex and hid parameters
22        // hid is used by DokuWiki to redirect back to this section after saving
23        var url = DOKU_BASE + 'doku.php?id=' + encodeURIComponent(JSINFO.id) + '&do=edit&codeblockindex=' + index + '&hid=' + hid;
24        window.location.href = url;
25    };
26
27    let sup = 'desktop';
28    if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
29        sup = 'mobile';
30    }
31
32    var blockIndex = 0;
33
34    // Process all code and file blocks - wrap the <pre> element directly like copy2clipboard does
35    document.querySelectorAll('pre.code, pre.file').forEach(function(preElem) {
36        // Check if already processed
37        if (preElem.dataset.codeblockeditProcessed) return;
38        preElem.dataset.codeblockeditProcessed = 'true';
39
40        var currentIndex = blockIndex++;
41
42        // Create the edit button
43        var editBtn = document.createElement('button');
44        editBtn.setAttribute('title', 'Edit this code block');
45        editBtn.classList.add('codeblockedit-btn');
46        editBtn.dataset.index = currentIndex;
47        editBtn.addEventListener('click', handleEdit);
48
49        // Check if already wrapped by copy2clipboard
50        var existingWrapper = preElem.parentNode;
51        var useExistingWrapper = existingWrapper &&
52            existingWrapper.classList &&
53            existingWrapper.classList.contains('cp2clipcont');
54
55        var btnWrapper;
56        if (useExistingWrapper) {
57            // Reuse existing wrapper from copy2clipboard
58            btnWrapper = existingWrapper;
59            btnWrapper.classList.add('codeblockedit-wrapper', sup);
60        } else {
61            // Create our own wrapper around the <pre> element (same as copy2clipboard)
62            btnWrapper = document.createElement('div');
63            btnWrapper.classList.add('codeblockedit-wrapper', sup);
64
65            // Wrap the pre element
66            preElem.parentNode.insertBefore(btnWrapper, preElem);
67            btnWrapper.appendChild(preElem);
68
69            // Transfer margin from pre to wrapper for proper alignment
70            var marginTop = window.getComputedStyle(preElem)['margin-top'];
71            if (marginTop !== '0px') {
72                btnWrapper.style['margin-top'] = marginTop;
73                preElem.style['margin-top'] = '0';
74            }
75            var marginBottom = window.getComputedStyle(preElem)['margin-bottom'];
76            if (marginBottom !== '0px') {
77                btnWrapper.style['margin-bottom'] = marginBottom;
78                preElem.style['margin-bottom'] = '0';
79            }
80        }
81
82        // Add anchor ID to wrapper for redirect back after edit
83        btnWrapper.id = 'codeblock_' + currentIndex;
84
85        btnWrapper.appendChild(editBtn);
86    });
87
88    // After adding all IDs, check if we need to scroll to a codeblock anchor
89    // This handles the case where DokuWiki redirects with #codeblock_N after save
90    // but the ID didn't exist in the initial HTML (it's added by JavaScript)
91    if (window.location.hash && window.location.hash.match(/^#codeblock_\d+$/)) {
92        var targetId = window.location.hash.substring(1);
93        var targetElement = document.getElementById(targetId);
94        if (targetElement) {
95            // Use setTimeout to ensure the DOM is fully ready and other plugins have run
96            setTimeout(function() {
97                // Expand any collapsed sections (sectiontoggle plugin compatibility)
98                // Collect all hidden ancestors first, then expand from outermost to innermost
99                var hiddenAncestors = [];
100                var parent = targetElement.parentElement;
101
102                while (parent && parent !== document.body) {
103                    // Check if this element is hidden (collapsed by sectiontoggle)
104                    var computedDisplay = window.getComputedStyle(parent).display;
105                    if (computedDisplay === 'none' || parent.style.display === 'none') {
106                        hiddenAncestors.push(parent);
107                    }
108                    parent = parent.parentElement;
109                }
110
111                // Expand from outermost (last in array) to innermost (first in array)
112                // This ensures parent sections are expanded before child sections
113                for (var i = hiddenAncestors.length - 1; i >= 0; i--) {
114                    var hiddenElem = hiddenAncestors[i];
115                    // Find the header that controls this section
116                    // sectiontoggle hides the nextElementSibling of the header
117                    var header = hiddenElem.previousElementSibling;
118                    if (header && /^H[1-6]$/.test(header.tagName) && header.classList.contains('st_closed')) {
119                        // Expand this section by toggling classes
120                        header.classList.remove('st_closed');
121                        header.classList.add('st_opened');
122                        hiddenElem.style.display = '';
123                    } else {
124                        // Fallback: just show the element if we can't find the controlling header
125                        hiddenElem.style.display = '';
126                    }
127                }
128
129                // Now scroll to the target
130                targetElement.scrollIntoView({ behavior: 'auto', block: 'start' });
131            }, 100); // Slightly longer delay to ensure sectiontoggle has finished
132        }
133    }
134});
135