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