xref: /plugin/myshortcuts/script.js (revision 61ef61f6aef59d2b4dc796cb87289ce67202085d)
1/**
2 * DokuWiki Plugin myshortcuts (JavaScript Component)
3 *
4 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
5 * @author David Jiménez <davidjimenez75@gmail.com>
6 */
7
8var MyShortcutsPlugin = {
9
10    /**
11     * Language strings
12     */
13    lang: {
14        en: {
15            selectSnippet: 'Select a Snippet',
16            noSnippets: 'No snippets configured. Please add snippets in the plugin settings.',
17            snippetsEditOnly: 'Snippets can only be inserted in edit mode. Press {0} to edit first.',
18            cancel: 'Cancel (ESC)',
19            editorNotFound: 'Editor not found.',
20            saveNotInEdit: 'MyShortcuts: Save button not found. Are you in edit mode?'
21        },
22        es: {
23            selectSnippet: 'Seleccionar un Fragmento',
24            noSnippets: 'No hay fragmentos configurados. Por favor añade fragmentos en la configuración del plugin.',
25            snippetsEditOnly: 'Los fragmentos solo se pueden insertar en modo edición. Presiona {0} para editar primero.',
26            cancel: 'Cancelar (ESC)',
27            editorNotFound: 'Editor no encontrado.',
28            saveNotInEdit: 'MyShortcuts: Botón de guardar no encontrado. ¿Estás en modo edición?'
29        }
30    },
31
32    /**
33     * Get current language
34     */
35    getLang: function() {
36        // Try to detect DokuWiki language
37        if (typeof JSINFO !== 'undefined' && JSINFO.lang) {
38            return JSINFO.lang;
39        }
40        // Fallback to browser language
41        var browserLang = navigator.language || navigator.userLanguage;
42        return browserLang.split('-')[0];
43    },
44
45    /**
46     * Get translated string
47     */
48    getString: function(key) {
49        var currentLang = MyShortcutsPlugin.getLang();
50        var strings = MyShortcutsPlugin.lang[currentLang] || MyShortcutsPlugin.lang.en;
51        return strings[key] || MyShortcutsPlugin.lang.en[key] || key;
52    },
53
54    /**
55     * Initialize the plugin
56     */
57    init: function() {
58        // Check if config is available
59        if (typeof MYSHORTCUTS_CONFIG === 'undefined') {
60            return;
61        }
62
63        // Add keyboard event listener
64        document.addEventListener('keydown', function(e) {
65            MyShortcutsPlugin.handleKeyPress(e);
66        });
67    },
68
69    /**
70     * Parse keyboard shortcut string (e.g., "ctrl+e", "alt+s")
71     */
72    parseShortcut: function(shortcut) {
73        if (!shortcut) return null;
74
75        var parts = shortcut.toLowerCase().split('+');
76        var result = {
77            ctrl: false,
78            alt: false,
79            shift: false,
80            meta: false,
81            key: ''
82        };
83
84        parts.forEach(function(part) {
85            part = part.trim();
86            if (part === 'ctrl' || part === 'control') {
87                result.ctrl = true;
88            } else if (part === 'alt') {
89                result.alt = true;
90            } else if (part === 'shift') {
91                result.shift = true;
92            } else if (part === 'meta' || part === 'cmd') {
93                result.meta = true;
94            } else {
95                result.key = part;
96            }
97        });
98
99        return result;
100    },
101
102    /**
103     * Check if pressed keys match a shortcut
104     */
105    matchesShortcut: function(event, shortcut) {
106        var parsed = MyShortcutsPlugin.parseShortcut(shortcut);
107        if (!parsed) return false;
108
109        var key = event.key.toLowerCase();
110
111        return (
112            event.ctrlKey === parsed.ctrl &&
113            event.altKey === parsed.alt &&
114            event.shiftKey === parsed.shift &&
115            event.metaKey === parsed.meta &&
116            key === parsed.key
117        );
118    },
119
120    /**
121     * Handle keyboard shortcuts
122     */
123    handleKeyPress: function(e) {
124        var config = MYSHORTCUTS_CONFIG;
125
126        // Edit shortcut
127        if (MyShortcutsPlugin.matchesShortcut(e, config.shortcutEdit)) {
128            e.preventDefault();
129            MyShortcutsPlugin.triggerEdit();
130            return;
131        }
132
133        // Save shortcut
134        if (MyShortcutsPlugin.matchesShortcut(e, config.shortcutSave)) {
135            e.preventDefault();
136            MyShortcutsPlugin.triggerSave();
137            return;
138        }
139
140        // Snippet shortcut
141        if (MyShortcutsPlugin.matchesShortcut(e, config.shortcutSnippet)) {
142            e.preventDefault();
143            MyShortcutsPlugin.showSnippetDialog();
144            return;
145        }
146    },
147
148    /**
149     * Trigger edit action
150     */
151    triggerEdit: function() {
152        // Look for edit button in DokuWiki
153        var editBtn = document.querySelector('button[name="do"][value="edit"]') ||
154                     document.querySelector('a.action.edit') ||
155                     document.querySelector('a[href*="do=edit"]');
156
157        if (editBtn) {
158            editBtn.click();
159        } else {
160            // Construct edit URL manually
161            var url = window.location.href;
162            if (url.indexOf('?') > -1) {
163                url += '&do=edit';
164            } else {
165                url += '?do=edit';
166            }
167            window.location.href = url;
168        }
169    },
170
171    /**
172     * Trigger save action
173     */
174    triggerSave: function() {
175        // Only works in edit mode
176        var saveBtn = document.querySelector('#edbtn__save') ||
177                     document.getElementById('edbtn__save') ||
178                     document.querySelector('button[name="do"][value="save"]');
179
180        if (saveBtn) {
181            saveBtn.click();
182        } else {
183            console.log(MyShortcutsPlugin.getString('saveNotInEdit'));
184        }
185    },
186
187    /**
188     * Show snippet selection dialog
189     */
190    showSnippetDialog: function() {
191        var config = MYSHORTCUTS_CONFIG;
192
193        if (!config.snippets || config.snippets.length === 0) {
194            alert(MyShortcutsPlugin.getString('noSnippets'));
195            return;
196        }
197
198        // Check if we're in edit mode
199        var editor = document.getElementById('wiki__text');
200        if (!editor) {
201            var msg = MyShortcutsPlugin.getString('snippetsEditOnly').replace('{0}', config.shortcutEdit);
202            alert(msg);
203            return;
204        }
205
206        // Remove any existing dialogs first
207        var existingOverlays = document.querySelectorAll('.myshortcuts-overlay');
208        existingOverlays.forEach(function(overlay) {
209            overlay.parentNode.removeChild(overlay);
210        });
211
212        // Create dialog
213        var dialog = MyShortcutsPlugin.createSnippetDialog(config.snippets);
214        document.body.appendChild(dialog);
215
216        // Focus first item
217        var firstItem = dialog.querySelector('.myshortcuts-snippet-item');
218        if (firstItem) {
219            firstItem.focus();
220        }
221    },
222
223    /**
224     * Create snippet selection dialog
225     */
226    createSnippetDialog: function(snippets) {
227        var overlay = document.createElement('div');
228        overlay.className = 'myshortcuts-overlay';
229
230        // Store snippets map for keyboard access
231        overlay._snippetsMap = {};
232
233        var dialog = document.createElement('div');
234        dialog.className = 'myshortcuts-dialog';
235
236        var header = document.createElement('h3');
237        header.textContent = MyShortcutsPlugin.getString('selectSnippet');
238        dialog.appendChild(header);
239
240        var list = document.createElement('div');
241        list.className = 'myshortcuts-snippet-list';
242
243        snippets.forEach(function(snippet, index) {
244            var item = document.createElement('button');
245            item.className = 'myshortcuts-snippet-item';
246            item.setAttribute('tabindex', '0');
247            item.setAttribute('data-index', index);
248            item.setAttribute('data-number', snippet.number || snippet.label);
249
250            // Store snippet in map for keyboard access
251            overlay._snippetsMap[snippet.number || snippet.label] = snippet.text;
252
253            var keyIndicator = document.createElement('span');
254            keyIndicator.className = 'myshortcuts-key-indicator';
255            keyIndicator.textContent = snippet.number || snippet.label;
256            item.appendChild(keyIndicator);
257
258            var content = document.createElement('div');
259            content.className = 'myshortcuts-snippet-content';
260
261            var text = document.createElement('div');
262            text.className = 'myshortcuts-snippet-preview';
263            text.textContent = snippet.text.substring(0, 80) + (snippet.text.length > 80 ? '...' : '');
264            content.appendChild(text);
265
266            item.appendChild(content);
267
268            item.addEventListener('click', function(e) {
269                e.stopPropagation();
270                e.preventDefault();
271                MyShortcutsPlugin.insertSnippet(snippet.text);
272                MyShortcutsPlugin.closeDialog(overlay);
273            });
274
275            list.appendChild(item);
276        });
277
278        dialog.appendChild(list);
279
280        var closeBtn = document.createElement('button');
281        closeBtn.className = 'myshortcuts-close-btn';
282        closeBtn.textContent = MyShortcutsPlugin.getString('cancel');
283        closeBtn.addEventListener('click', function(e) {
284            e.stopPropagation();
285            e.preventDefault();
286            MyShortcutsPlugin.closeDialog(overlay);
287        });
288        dialog.appendChild(closeBtn);
289
290        overlay.appendChild(dialog);
291
292        // Handle keyboard shortcuts
293        overlay.addEventListener('keydown', function(e) {
294            if (e.key === 'Escape') {
295                e.preventDefault();
296                MyShortcutsPlugin.closeDialog(overlay);
297                return;
298            }
299
300            // Handle number keys (1-9, 0)
301            var key = e.key;
302            if (/^[0-9]$/.test(key)) {
303                e.preventDefault();
304                var snippetText = overlay._snippetsMap[key];
305                if (snippetText) {
306                    MyShortcutsPlugin.insertSnippet(snippetText);
307                    MyShortcutsPlugin.closeDialog(overlay);
308                }
309            }
310        });
311
312        // Close on overlay click
313        overlay.addEventListener('click', function(e) {
314            if (e.target === overlay) {
315                MyShortcutsPlugin.closeDialog(overlay);
316            }
317        });
318
319        return overlay;
320    },
321
322    /**
323     * Close dialog
324     */
325    closeDialog: function(overlay) {
326        // Remove all overlays as a safety measure
327        var allOverlays = document.querySelectorAll('.myshortcuts-overlay');
328        allOverlays.forEach(function(o) {
329            if (o.parentNode) {
330                o.parentNode.removeChild(o);
331            }
332        });
333    },
334
335    /**
336     * Insert snippet into editor
337     */
338    insertSnippet: function(text) {
339        var editor = document.getElementById('wiki__text');
340        if (!editor) {
341            alert(MyShortcutsPlugin.getString('editorNotFound'));
342            return;
343        }
344
345        // Get cursor position
346        var startPos = editor.selectionStart;
347        var endPos = editor.selectionEnd;
348
349        // Insert text at cursor position
350        var beforeText = editor.value.substring(0, startPos);
351        var afterText = editor.value.substring(endPos, editor.value.length);
352        editor.value = beforeText + text + afterText;
353
354        // Set cursor position after inserted text
355        var newPos = startPos + text.length;
356        editor.selectionStart = newPos;
357        editor.selectionEnd = newPos;
358
359        // Focus editor
360        editor.focus();
361    }
362};
363
364// Initialize when DOM is ready
365if (document.readyState === 'loading') {
366    document.addEventListener('DOMContentLoaded', function() {
367        MyShortcutsPlugin.init();
368    });
369} else {
370    MyShortcutsPlugin.init();
371}
372