Repository navigation
Expand file tree
/
Copy pathscript.js
More file actions
163 lines (142 loc) · 7.94 KB
/
Copy pathscript.js
File metadata and controls
163 lines (142 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
jQuery(function() {
// Check if user has edit permission
if (!JSINFO.codeblockedit_canedit) {
return; // Don't show edit buttons if user can't edit
}
// Check if on a page with content (not in edit mode)
if (document.querySelector('#dw__editform')) {
return; // Don't add edit buttons while in edit mode
}
// Function to handle edit click
var handleEdit = function(event) {
event.preventDefault();
event.stopPropagation();
var $btn = jQuery(this);
var index = $btn.data('index');
var hid = 'codeblock_' + index;
// Redirect to standard DokuWiki editor with codeblockindex and hid parameters
// hid is used by DokuWiki to redirect back to this section after saving
// target=codeblock ensures EDIT_FORM_ADDTEXTAREA event fires (Editor.php:83
// only emits the event when target !== 'section'), so our handleAddTextarea
// hook runs to add hidden fields and the container-prefix notice.
var url = DOKU_BASE + 'doku.php?id=' + encodeURIComponent(JSINFO.id) + '&do=edit&codeblockindex=' + index + '&hid=' + hid + '&target=codeblock';
window.location.href = url;
};
let sup = 'desktop';
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
sup = 'mobile';
}
var blockIndex = 0;
// Mask from PHP: one entry per rendered <pre> in DOM order (1:1 with
// code/file/preformatted instructions from walkPreTags — includes blocks
// nested inside blockquotes/lists). All entries are now `true` (preformatted
// is editable via computePreformattedRange), so the mask's primary purpose
// is a count check rather than a per-block filter.
//
// COUNT SANITY CHECK (Alt P, fixes #9 + #11):
// - Empty mask (older DW, parse failure, !canedit) → no buttons (safe —
// no broken edit buttons, no silent wrong-block edits)
// - Mask length ≠ DOM <pre> count (third-party plugin injected/removed/reordered
// <pre> elements) → no buttons (safe — prevents mask/DOM index drift)
// Both failure modes are strictly safer than the old "attach to all" fallback
// which produced broken buttons on preformatted and silently edited wrong
// blocks on mixed pages.
var mask = JSINFO.codeblockedit_mask || [];
var preElements = document.querySelectorAll('pre.code, pre.file');
if (mask.length === 0 || mask.length !== preElements.length) {
// Plugin disabled for this page view — fail safe (no buttons)
return;
}
// Process all code and file blocks - wrap the <pre> element directly like copy2clipboard does
preElements.forEach(function(preElem, domIndex) {
// Check if already processed
if (preElem.dataset.codeblockeditProcessed) return;
preElem.dataset.codeblockeditProcessed = 'true';
// Defensive: skip blocks the mask marked non-editable (future-proof —
// currently mask is all true after Alt P, but a future block kind
// might be marked false if it shouldn't be editable).
if (mask[domIndex] === false) return;
var currentIndex = blockIndex++;
// Create the edit button
var editBtn = document.createElement('button');
editBtn.setAttribute('title', JSINFO.codeblockedit_btn_title || 'Edit this code block');
editBtn.classList.add('codeblockedit-btn');
editBtn.dataset.index = currentIndex;
editBtn.addEventListener('click', handleEdit);
// Check if already wrapped by copy2clipboard
var existingWrapper = preElem.parentNode;
var useExistingWrapper = existingWrapper &&
existingWrapper.classList &&
existingWrapper.classList.contains('cp2clipcont');
var btnWrapper;
if (useExistingWrapper) {
// Reuse existing wrapper from copy2clipboard
btnWrapper = existingWrapper;
btnWrapper.classList.add('codeblockedit-wrapper', sup);
} else {
// Create our own wrapper around the <pre> element (same as copy2clipboard)
btnWrapper = document.createElement('div');
btnWrapper.classList.add('codeblockedit-wrapper', sup);
// Wrap the pre element
preElem.parentNode.insertBefore(btnWrapper, preElem);
btnWrapper.appendChild(preElem);
// Transfer margin from pre to wrapper for proper alignment
var marginTop = window.getComputedStyle(preElem)['margin-top'];
if (marginTop !== '0px') {
btnWrapper.style['margin-top'] = marginTop;
preElem.style['margin-top'] = '0';
}
var marginBottom = window.getComputedStyle(preElem)['margin-bottom'];
if (marginBottom !== '0px') {
btnWrapper.style['margin-bottom'] = marginBottom;
preElem.style['margin-bottom'] = '0';
}
}
// Add anchor ID to wrapper for redirect back after edit
btnWrapper.id = 'codeblock_' + currentIndex;
btnWrapper.appendChild(editBtn);
});
// After adding all IDs, check if we need to scroll to a codeblock anchor
// This handles the case where DokuWiki redirects with #codeblock_N after save
// but the ID didn't exist in the initial HTML (it's added by JavaScript)
if (window.location.hash && window.location.hash.match(/^#codeblock_\d+$/)) {
var targetId = window.location.hash.substring(1);
var targetElement = document.getElementById(targetId);
if (targetElement) {
// Use setTimeout to ensure the DOM is fully ready and other plugins have run
setTimeout(function() {
// Expand any collapsed sections (sectiontoggle plugin compatibility)
// Collect all hidden ancestors first, then expand from outermost to innermost
var hiddenAncestors = [];
var parent = targetElement.parentElement;
while (parent && parent !== document.body) {
// Check if this element is hidden (collapsed by sectiontoggle)
var computedDisplay = window.getComputedStyle(parent).display;
if (computedDisplay === 'none' || parent.style.display === 'none') {
hiddenAncestors.push(parent);
}
parent = parent.parentElement;
}
// Expand from outermost (last in array) to innermost (first in array)
// This ensures parent sections are expanded before child sections
for (var i = hiddenAncestors.length - 1; i >= 0; i--) {
var hiddenElem = hiddenAncestors[i];
// Find the header that controls this section
// sectiontoggle hides the nextElementSibling of the header
var header = hiddenElem.previousElementSibling;
if (header && /^H[1-6]$/.test(header.tagName) && header.classList.contains('st_closed')) {
// Expand this section by toggling classes
header.classList.remove('st_closed');
header.classList.add('st_opened');
hiddenElem.style.display = '';
} else {
// Fallback: just show the element if we can't find the controlling header
hiddenElem.style.display = '';
}
}
// Now scroll to the target
targetElement.scrollIntoView({ behavior: 'auto', block: 'start' });
}, 100); // Slightly longer delay to ensure sectiontoggle has finished
}
}
});