]> BookStack Code Mirror - bookstack/blob - resources/assets/js/code.js
Finished off main functionality of custom tinymce code editor
[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         console.log(codeElem.className);
100         lang = (codeElem.className || '').replace('language-', '')
101     }
102
103     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
104     let content = elem.textContent;
105     let newWrap = doc.createElement('div');
106     let newTextArea = doc.createElement('textarea');
107
108     newWrap.className = 'CodeMirrorContainer';
109     newWrap.setAttribute('data-lang', lang);
110     newTextArea.style.display = 'none';
111     elem.parentNode.replaceChild(newWrap, elem);
112
113     newWrap.appendChild(newTextArea);
114     newWrap.contentEditable = false;
115     newTextArea.textContent = content;
116
117     let cm = CodeMirror(function(elt) {
118         newWrap.appendChild(elt);
119     }, {
120         value: content,
121         mode:  getMode(lang),
122         lineNumbers: true,
123         theme: 'base16-light',
124         readOnly: true
125     });
126     setTimeout(() => {
127         cm.refresh();
128     }, 300);
129     return {wrap: newWrap, editor: cm};
130 };
131
132 module.exports.popupEditor = function(elem, modeSuggestion) {
133     let content = elem.textContent;
134
135     return CodeMirror(function(elt) {
136         elem.parentNode.insertBefore(elt, elem);
137         elem.style.display = 'none';
138     }, {
139         value: content,
140         mode:  getMode(modeSuggestion),
141         lineNumbers: true,
142         theme: 'base16-light',
143         lineWrapping: true
144     });
145 };
146
147 module.exports.setMode = function(cmInstance, modeSuggestion) {
148       cmInstance.setOption('mode', getMode(modeSuggestion));
149 };
150 module.exports.setContent = function(cmInstance, codeContent) {
151     cmInstance.setValue(codeContent);
152     setTimeout(() => {
153         cmInstance.refresh();
154     }, 10);
155 };
156
157 module.exports.markdownEditor = function(elem) {
158     let content = elem.textContent;
159
160     return CodeMirror(function (elt) {
161         elem.parentNode.insertBefore(elt, elem);
162         elem.style.display = 'none';
163     }, {
164         value: content,
165         mode: "markdown",
166         lineNumbers: true,
167         theme: 'base16-light',
168         lineWrapping: true
169     });
170
171 };
172