X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/6917ea088f905ec57e16d8795ad0bcb191c38b52..refs/pull/2022/head:/resources/js/services/code.js diff --git a/resources/js/services/code.js b/resources/js/services/code.js index 1e0e48289..a7dfa587f 100644 --- a/resources/js/services/code.js +++ b/resources/js/services/code.js @@ -5,17 +5,21 @@ import Clipboard from "clipboard/dist/clipboard.min"; import 'codemirror/mode/css/css'; import 'codemirror/mode/clike/clike'; import 'codemirror/mode/diff/diff'; +import 'codemirror/mode/fortran/fortran'; import 'codemirror/mode/go/go'; +import 'codemirror/mode/haskell/haskell'; import 'codemirror/mode/htmlmixed/htmlmixed'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/julia/julia'; import 'codemirror/mode/lua/lua'; -import 'codemirror/mode/haskell/haskell'; import 'codemirror/mode/markdown/markdown'; import 'codemirror/mode/mllike/mllike'; import 'codemirror/mode/nginx/nginx'; +import 'codemirror/mode/perl/perl'; +import 'codemirror/mode/pascal/pascal'; import 'codemirror/mode/php/php'; import 'codemirror/mode/powershell/powershell'; +import 'codemirror/mode/properties/properties'; import 'codemirror/mode/python/python'; import 'codemirror/mode/ruby/ruby'; import 'codemirror/mode/rust/rust'; @@ -28,6 +32,9 @@ import 'codemirror/mode/yaml/yaml'; // Addons import 'codemirror/addon/scroll/scrollpastend'; +// Mapping of possible languages or formats from user input to their codemirror modes. +// Value can be a mode string or a function that will receive the code content & return the mode string. +// The function option is used in the event the exact mode could be dynamic depending on the code. const modeMap = { css: 'css', c: 'text/x-csrc', @@ -38,10 +45,13 @@ const modeMap = { 'c#': 'text/x-csharp', csharp: 'text/x-csharp', diff: 'diff', + for: 'fortran', + fortran: 'fortran', go: 'go', haskell: 'haskell', hs: 'haskell', html: 'htmlmixed', + ini: 'properties', javascript: 'javascript', json: {name: 'javascript', json: true}, js: 'javascript', @@ -53,9 +63,16 @@ const modeMap = { markdown: 'markdown', ml: 'mllike', nginx: 'nginx', + perl: 'perl', + pl: 'perl', powershell: 'powershell', + properties: 'properties', ocaml: 'mllike', - php: 'php', + pascal: 'text/x-pascal', + pas: 'text/x-pascal', + php: (content) => { + return content.includes('/gi ,'\n'); + const content = elem.textContent.trimEnd(); + let mode = ''; if (innerCodeElem !== null) { - let langName = innerCodeElem.className.replace('language-', ''); - mode = getMode(langName); + const langName = innerCodeElem.className.replace('language-', ''); + mode = getMode(langName, content); } - elem.innerHTML = elem.innerHTML.replace(//gi ,'\n'); - let content = elem.textContent.trim(); - let cm = CodeMirror(function(elt) { + const cm = CodeMirror(function(elt) { elem.parentNode.replaceChild(elt, elem); }, { value: content, mode: mode, lineNumbers: true, + lineWrapping: false, theme: getTheme(), readOnly: true }); @@ -136,12 +166,24 @@ function addCopyIcon(cmInstance) { /** * Search for a codemirror code based off a user suggestion - * @param suggestion + * @param {String} suggestion + * @param {String} content * @returns {string} */ -function getMode(suggestion) { +function getMode(suggestion, content) { suggestion = suggestion.trim().replace(/^\./g, '').toLowerCase(); - return (typeof modeMap[suggestion] !== 'undefined') ? modeMap[suggestion] : ''; + + const modeMapType = typeof modeMap[suggestion]; + + if (modeMapType === 'undefined') { + return ''; + } + + if (modeMapType === 'function') { + return modeMap[suggestion](content); + } + + return modeMap[suggestion]; } /** @@ -149,7 +191,8 @@ function getMode(suggestion) { * @returns {*|string} */ function getTheme() { - return window.codeTheme || 'base16-light'; + const darkMode = document.documentElement.classList.contains('dark-mode'); + return window.codeTheme || (darkMode ? 'darcula' : 'default'); } /** @@ -159,8 +202,8 @@ function getTheme() { * @returns {{wrap: Element, editor: *}} */ function wysiwygView(elem) { - let doc = elem.ownerDocument; - let codeElem = elem.querySelector('code'); + const doc = elem.ownerDocument; + const codeElem = elem.querySelector('code'); let lang = (elem.className || '').replace('language-', ''); if (lang === '' && codeElem) { @@ -168,9 +211,9 @@ function wysiwygView(elem) { } elem.innerHTML = elem.innerHTML.replace(//gi ,'\n'); - let content = elem.textContent; - let newWrap = doc.createElement('div'); - let newTextArea = doc.createElement('textarea'); + const content = elem.textContent; + const newWrap = doc.createElement('div'); + const newTextArea = doc.createElement('textarea'); newWrap.className = 'CodeMirrorContainer'; newWrap.setAttribute('data-lang', lang); @@ -186,8 +229,9 @@ function wysiwygView(elem) { newWrap.appendChild(elt); }, { value: content, - mode: getMode(lang), + mode: getMode(lang, content), lineNumbers: true, + lineWrapping: false, theme: getTheme(), readOnly: true }); @@ -204,17 +248,17 @@ function wysiwygView(elem) { * @returns {*} */ function popupEditor(elem, modeSuggestion) { - let content = elem.textContent; + const content = elem.textContent; return CodeMirror(function(elt) { elem.parentNode.insertBefore(elt, elem); elem.style.display = 'none'; }, { value: content, - mode: getMode(modeSuggestion), + mode: getMode(modeSuggestion, content), lineNumbers: true, - theme: getTheme(), - lineWrapping: true + lineWrapping: false, + theme: getTheme() }); } @@ -223,8 +267,8 @@ function popupEditor(elem, modeSuggestion) { * @param cmInstance * @param modeSuggestion */ -function setMode(cmInstance, modeSuggestion) { - cmInstance.setOption('mode', getMode(modeSuggestion)); +function setMode(cmInstance, modeSuggestion, content) { + cmInstance.setOption('mode', getMode(modeSuggestion, content)); } /** @@ -235,29 +279,40 @@ function setMode(cmInstance, modeSuggestion) { function setContent(cmInstance, codeContent) { cmInstance.setValue(codeContent); setTimeout(() => { - cmInstance.refresh(); + updateLayout(cmInstance); }, 10); } /** - * Get a CodeMirror instace to use for the markdown editor. + * Update the layout (codemirror refresh) of a cm instance. + * @param cmInstance + */ +function updateLayout(cmInstance) { + cmInstance.refresh(); +} + +/** + * Get a CodeMirror instance to use for the markdown editor. * @param {HTMLElement} elem * @returns {*} */ function markdownEditor(elem) { - let content = elem.textContent; - - return CodeMirror(function (elt) { - elem.parentNode.insertBefore(elt, elem); - elem.style.display = 'none'; - }, { + const content = elem.textContent; + const config = { value: content, mode: "markdown", lineNumbers: true, - theme: getTheme(), lineWrapping: true, + theme: getTheme(), scrollPastEnd: true, - }); + }; + + window.$events.emitPublic(elem, 'editor-markdown-cm::pre-init', {config}); + + return CodeMirror(function (elt) { + elem.parentNode.insertBefore(elt, elem); + elem.style.display = 'none'; + }, config); } /** @@ -271,10 +326,12 @@ function getMetaKey() { export default { highlight: highlight, + highlightWithin: highlightWithin, wysiwygView: wysiwygView, popupEditor: popupEditor, setMode: setMode, setContent: setContent, + updateLayout: updateLayout, markdownEditor: markdownEditor, getMetaKey: getMetaKey, };