]> BookStack Code Mirror - bookstack/blob - resources/assets/js/code.js
Fixed code block selection and drag/drop issues
[bookstack] / resources / assets / js / code.js
1 require('codemirror/mode/css/css');
2 require('codemirror/mode/clike/clike');
3 require('codemirror/mode/go/go');
4 require('codemirror/mode/htmlmixed/htmlmixed');
5 require('codemirror/mode/javascript/javascript');
6 require('codemirror/mode/markdown/markdown');
7 require('codemirror/mode/nginx/nginx');
8 require('codemirror/mode/php/php');
9 require('codemirror/mode/powershell/powershell');
10 require('codemirror/mode/python/python');
11 require('codemirror/mode/ruby/ruby');
12 require('codemirror/mode/shell/shell');
13 require('codemirror/mode/sql/sql');
14 require('codemirror/mode/toml/toml');
15 require('codemirror/mode/xml/xml');
16 require('codemirror/mode/yaml/yaml');
17
18 const CodeMirror = require('codemirror');
19
20 const modeMap = {
21     css: 'css',
22     c: 'clike',
23     java: 'clike',
24     scala: 'clike',
25     kotlin: 'clike',
26     'c++': 'clike',
27     'c#': 'clike',
28     csharp: 'clike',
29     go: 'go',
30     html: 'htmlmixed',
31     javascript: 'javascript',
32     json: {name: 'javascript', json: true},
33     js: 'javascript',
34     php: 'php',
35     md: 'markdown',
36     mdown: 'markdown',
37     markdown: 'markdown',
38     nginx: 'nginx',
39     powershell: 'powershell',
40     py: 'python',
41     python: 'python',
42     ruby: 'ruby',
43     rb: 'ruby',
44     shell: 'shell',
45     bash: 'shell',
46     toml: 'toml',
47     sql: 'sql',
48     xml: 'xml',
49     yaml: 'yaml',
50     yml: 'yaml',
51 };
52
53 module.exports.highlight = function() {
54     let codeBlocks = document.querySelectorAll('.page-content pre');
55     for (let i = 0; i < codeBlocks.length; i++) {
56         highlightElem(codeBlocks[i]);
57     }
58 };
59
60 function highlightElem(elem) {
61     let innerCodeElem = elem.querySelector('code[class^=language-]');
62     let mode = '';
63     if (innerCodeElem !== null) {
64         let langName = innerCodeElem.className.replace('language-', '');
65         mode = getMode(langName);
66     }
67     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
68     let content = elem.textContent;
69
70     CodeMirror(function(elt) {
71         elem.parentNode.replaceChild(elt, elem);
72     }, {
73         value: content,
74         mode:  mode,
75         lineNumbers: true,
76         theme: 'base16-light',
77         readOnly: true
78     });
79 }
80
81 /**
82  * Search for a codemirror code based off a user suggestion
83  * @param suggestion
84  * @returns {string}
85  */
86 function getMode(suggestion) {
87     suggestion = suggestion.trim().replace(/^\./g, '').toLowerCase();
88     return (typeof modeMap[suggestion] !== 'undefined') ? modeMap[suggestion] : '';
89 }
90
91 module.exports.highlightElem = highlightElem;
92
93 module.exports.wysiwygView = function(elem) {
94     let doc = elem.ownerDocument;
95     let codeElem = elem.querySelector('code');
96
97     let lang = (elem.className || '').replace('language-', '');
98     if (lang === '' && codeElem) {
99         lang = (codeElem.className || '').replace('language-', '')
100     }
101
102     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
103     let content = elem.textContent;
104     let newWrap = doc.createElement('div');
105     let newTextArea = doc.createElement('textarea');
106
107     newWrap.className = 'CodeMirrorContainer';
108     newWrap.setAttribute('data-lang', lang);
109     newTextArea.style.display = 'none';
110     elem.parentNode.replaceChild(newWrap, elem);
111
112     newWrap.appendChild(newTextArea);
113     newWrap.contentEditable = false;
114     newTextArea.textContent = content;
115
116     let cm = CodeMirror(function(elt) {
117         newWrap.appendChild(elt);
118     }, {
119         value: content,
120         mode:  getMode(lang),
121         lineNumbers: true,
122         theme: 'base16-light',
123         readOnly: true
124     });
125     setTimeout(() => {
126         cm.refresh();
127     }, 300);
128     return {wrap: newWrap, editor: cm};
129 };
130
131 module.exports.popupEditor = function(elem, modeSuggestion) {
132     let content = elem.textContent;
133
134     return CodeMirror(function(elt) {
135         elem.parentNode.insertBefore(elt, elem);
136         elem.style.display = 'none';
137     }, {
138         value: content,
139         mode:  getMode(modeSuggestion),
140         lineNumbers: true,
141         theme: 'base16-light',
142         lineWrapping: true
143     });
144 };
145
146 module.exports.setMode = function(cmInstance, modeSuggestion) {
147       cmInstance.setOption('mode', getMode(modeSuggestion));
148 };
149 module.exports.setContent = function(cmInstance, codeContent) {
150     cmInstance.setValue(codeContent);
151     setTimeout(() => {
152         cmInstance.refresh();
153     }, 10);
154 };
155
156 module.exports.markdownEditor = function(elem) {
157     let content = elem.textContent;
158
159     return CodeMirror(function (elt) {
160         elem.parentNode.insertBefore(elt, elem);
161         elem.style.display = 'none';
162     }, {
163         value: content,
164         mode: "markdown",
165         lineNumbers: true,
166         theme: 'base16-light',
167         lineWrapping: true
168     });
169
170 };
171