[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 /** 2 * source.js 3 * 4 * Original code by Arjan Haverkamp 5 * Copyright 2013-2015 Arjan Haverkamp ([email protected]) 6 */ 7 if (!window.parent.Joomla || typeof window.parent.Joomla.getOptions !== 'function') { 8 throw new Error('Joomla API not found'); 9 } // Get the base path for CodeMirror 10 11 12 const rootPath = window.parent.Joomla.getOptions('system.paths').rootFull; 13 const cmPath = `$rootPath}/media/vendor/codemirror`; // CodeMirror settings 14 15 let CMsettings = { 16 indentOnInit: true, 17 config: { 18 mode: 'htmlmixed', 19 theme: 'default', 20 lineNumbers: true, 21 lineWrapping: true, 22 indentUnit: 2, 23 tabSize: 2, 24 indentWithTabs: true, 25 matchBrackets: true, 26 saveCursorPosition: true, 27 styleActiveLine: true 28 }, 29 jsFiles: [// Default JS files 30 `$cmPath}/lib/codemirror.min.js`, `$cmPath}/addon/edit/matchbrackets.min.js`, `$cmPath}/mode/xml/xml.min.js`, `$cmPath}/mode/javascript/javascript.min.js`, `$cmPath}/mode/css/css.min.js`, `$cmPath}/mode/htmlmixed/htmlmixed.min.js`, `$cmPath}/addon/dialog/dialog.min.js`, `$cmPath}/addon/search/searchcursor.min.js`, `$cmPath}/addon/search/search.min.js`, `$cmPath}/addon/selection/active-line.min.js`], 31 cssFiles: [// Default CSS files 32 `$cmPath}/lib/codemirror.css`, `$cmPath}/addon/dialog/dialog.css`] 33 }; // Declare some variables: 34 35 let tinymce; // Reference to TinyMCE 36 37 let editor; // Reference to TinyMCE editor 38 39 let codemirror; // CodeMirror instance 40 41 const chr = 0; // Unused utf-8 character, placeholder for cursor 42 43 const isMac = /macintosh|mac os/i.test(navigator.userAgent); // Utility function to load CodeMirror script files 44 45 const loadScript = url => new Promise((resolve, reject) => { 46 const script = document.createElement('script'); 47 script.src = url; 48 49 script.onload = () => resolve(); 50 51 script.onerror = () => reject(new Error(`Failed to load the script $url}`)); 52 53 document.head.appendChild(script); 54 }); 55 /** 56 * Find the depth level 57 */ 58 59 60 const findDepth = (haystack, needle) => { 61 const idx = haystack.indexOf(needle); 62 let depth = 0; 63 64 for (let x = idx - 1; x >= 0; x -= 1) { 65 switch (haystack.charAt(x)) { 66 case '<': 67 depth -= 1; 68 break; 69 70 case '>': 71 depth += 1; 72 break; 73 74 case '&': 75 depth += 1; 76 break; 77 } 78 } 79 80 return depth; 81 }; 82 /** 83 * This function is called by plugin.js, when user clicks 'Ok' button 84 */ 85 86 87 window.tinymceHighlighterSubmit = () => { 88 const cc = '�'; 89 const { 90 isDirty 91 } = codemirror; 92 const { 93 doc 94 } = codemirror; 95 96 if (doc.somethingSelected()) { 97 // Clear selection: 98 doc.setCursor(doc.getCursor()); 99 } // Insert cursor placeholder (�) 100 101 102 doc.replaceSelection(cc); 103 const pos = codemirror.getCursor(); 104 let curLineHTML = doc.getLine(pos.line); 105 106 if (findDepth(curLineHTML, cc) !== 0) { 107 // Cursor is inside a <tag>, don't set cursor: 108 curLineHTML = curLineHTML.replace(cc, ''); 109 doc.replaceRange(curLineHTML, window.CodeMirror.Pos(pos.line, 0), window.CodeMirror.Pos(pos.line)); 110 } // Submit HTML to TinyMCE: 111 // [FIX] Cursor position inside JS, style or &nbps; 112 // Workaround to fix cursor position if inside script tag 113 114 115 const code = codemirror.getValue(); 116 /* Regex to check if inside script or style tags */ 117 118 const ccScript = new RegExp(`<script(.*?)>(.*?)$cc}(.*?)</script>`, 'ms'); 119 const ccStyle = new RegExp(`<style(.*?)>(.*?)$cc}(.*?)</style>`, 'ms'); 120 /* Regex to check if in beginning or end or if between < & > */ 121 122 const ccLocationCheck = new RegExp(`<[^>]*($cc}).*>|^($cc})|($cc})$`); 123 124 if (code.search(ccScript) !== -1 || code.search(ccStyle) !== -1 || code.search(ccLocationCheck) !== -1) { 125 editor.setContent(code.replace(cc, '')); 126 } else { 127 editor.setContent(code.replace(cc, '<span id="CmCaReT"></span>')); 128 } 129 130 editor.isNotDirty = !isDirty; 131 132 if (isDirty) { 133 editor.nodeChanged(); 134 } // Set cursor: 135 136 137 const el = editor.dom.select('span#CmCaReT')[0]; 138 139 if (el) { 140 editor.selection.scrollIntoView(el); 141 editor.selection.setCursorLocation(el, 0); 142 editor.dom.remove(el); 143 } 144 }; 145 /** 146 * Listen for the escape key and close the modal 147 * 148 * @param {Event} evt 149 */ 150 151 152 document.addEventListener('keydown', evt => { 153 const event = evt || window.event; 154 let isEscape = false; 155 if ('key' in event) isEscape = event.key === 'Escape' || event.key === 'Esc';else isEscape = event.keyCode === 27; 156 if (isEscape) tinymce.activeEditor.windowManager.close(); 157 }); 158 /** 159 * Append some help text in the modal footer 160 */ 161 162 const start = () => { 163 // Initialise (on load) 164 if (typeof window.CodeMirror !== 'function') { 165 throw new Error(`CodeMirror not found in "$CMsettings.path}", aborting...`); 166 } // Create legend for keyboard shortcuts for find & replace: 167 168 169 const head = window.parent.document.querySelectorAll('.tox-dialog__footer')[0]; 170 const div = window.parent.document.createElement('div'); 171 const td1 = '<td style="font-size:11px;background:#777;color:#fff;padding:0 4px">'; 172 const td2 = '<td style="font-size:11px;padding-right:5px">'; 173 div.innerHTML = ` 174 <table cellspacing="0" cellpadding="0" style="border-spacing:4px"> 175 <tr> 176 $td1}$isMac ? '⌘-F' : 'Ctrl-F</td>'}$td2}$tinymce.translate('Start search')}</td> 177 $td1}$isMac ? '⌘-G' : 'Ctrl-G'}</td> 178 $td2}$tinymce.translate('Find next')}</td> 179 $td1}$isMac ? '⌘-Alt-F' : 'Shift-Ctrl-F'}</td> 180 $td2}$tinymce.translate('Find previous')}</td> 181 </tr> 182 <tr> 183 $td1}$isMac ? '⌘-Alt-F' : 'Shift-Ctrl-F'}</td> 184 $td2}$tinymce.translate('Replace')}</td> 185 $td1}$isMac ? 'Shift-⌘-Alt-F' : 'Shift-Ctrl-R'}</td> 186 $td2}$tinymce.translate('Replace all')}</td> 187 </tr> 188 </table>`; 189 div.style.position = 'absolute'; 190 div.style.left = '5px'; 191 div.style.bottom = '5px'; 192 head.appendChild(div); // Set CodeMirror cursor and bookmark to same position as cursor was in TinyMCE: 193 194 let html = editor.getContent({ 195 source_view: true 196 }); // [FIX] #6 z-index issue with table panel and source code dialog 197 // editor.selection.getBookmark(); 198 199 html = html.replace(/<span\s+style="display: none;"\s+class="CmCaReT"([^>]*)>([^<]*)<\/span>/gm, String.fromCharCode(chr)); 200 editor.dom.remove(editor.dom.select('.CmCaReT')); // Hide TinyMCE toolbar panels, [FIX] #6 z-index issue with table panel and source code dialog 201 // https://github.com/christiaan/tinymce-codemirror/issues/6 202 203 tinymce.each(editor.contextToolbars, toolbar => { 204 if (toolbar.panel) { 205 toolbar.panel.hide(); 206 } 207 }); 208 window.CodeMirror.defineInitHook(inst => { 209 // Move cursor to correct position: 210 inst.focus(); 211 const cursor = inst.getSearchCursor(String.fromCharCode(chr), false); 212 213 if (cursor.findNext()) { 214 inst.setCursor(cursor.to()); 215 cursor.replace(''); 216 } // Indent all code, if so requested: 217 218 219 if (editor.settings.codemirror.indentOnInit) { 220 const last = inst.lineCount(); 221 inst.operation(() => { 222 // eslint-disable-next-line no-plusplus 223 for (let i = 0; i < last; ++i) { 224 inst.indentLine(i); 225 } 226 }); 227 } 228 }); 229 CMsettings.config.value = html; // Instantiate CodeMirror: 230 231 codemirror = window.CodeMirror(document.body, CMsettings.config); 232 codemirror.isDirty = false; 233 codemirror.on('change', inst => { 234 inst.isDirty = true; 235 }); 236 codemirror.setSize('100%', '100%'); 237 codemirror.refresh(); 238 }; // Initialise 239 240 241 tinymce = window.parent.tinymce; 242 243 if (!tinymce) { 244 throw new Error('tinyMCE not found'); 245 } 246 247 editor = tinymce.activeEditor; 248 const userSettings = editor.settings.codemirror; 249 250 if (userSettings.fullscreen) { 251 CMsettings.jsFiles.push(`$cmPath}/addon/display/fullscreen.min.js`); 252 CMsettings.cssFiles.push(`$cmPath}/addon/display/fullscreen.css`); 253 } // Merge config 254 255 256 CMsettings = { ...CMsettings, 257 ...userSettings 258 }; // Append the stylesheets 259 260 CMsettings.cssFiles.forEach(css => { 261 const link = document.createElement('link'); 262 link.rel = 'stylesheet'; 263 link.href = css; 264 document.head.appendChild(link); 265 }); 266 /** 267 * Append javascript files ensuring the order of execution. 268 * Then execute the start function. 269 */ 270 271 CMsettings.jsFiles.reduce((p, item) => p.then(() => loadScript(item)), Promise.resolve(true)).then(() => { 272 // Borrowed from codemirror.js themeChanged function. Sets the theme's class names to the html element. 273 // Without this, the background color outside of the codemirror wrapper element remains white. 274 // [TMP] commented temporary, cause JS error: Uncaught TypeError: Cannot read property 'replace' of undefined 275 if (CMsettings.config.theme) { 276 document.documentElement.className += CMsettings.config.theme.replace(/(^|\s)\s*/g, ' cm-s-'); 277 } 278 279 start(); 280 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |