]> BookStack Code Mirror - bookstack/blob - resources/js/markdown/codemirror.ts
a3b81418f9f5128aebf6779fbae5da8291427fce
[bookstack] / resources / js / markdown / codemirror.ts
1 import {provideKeyBindings} from './shortcuts';
2 import {debounce} from '../services/util';
3 import {Clipboard} from '../services/clipboard';
4 import {EditorView, ViewUpdate} from "@codemirror/view";
5 import {MarkdownEditor} from "./index.mjs";
6
7 /**
8  * Initiate the codemirror instance for the markdown editor.
9  */
10 export async function init(editor: MarkdownEditor): Promise<EditorView> {
11     const Code = await window.importVersioned('code') as (typeof import('../code/index.mjs'));
12
13     function onViewUpdate(v: ViewUpdate) {
14         if (v.docChanged) {
15             editor.actions.updateAndRender();
16         }
17     }
18
19     const onScrollDebounced = debounce(editor.actions.syncDisplayPosition.bind(editor.actions), 100, false);
20     let syncActive = editor.settings.get('scrollSync');
21     editor.settings.onChange('scrollSync', val => {
22         syncActive = val;
23     });
24
25     const domEventHandlers = {
26         // Handle scroll to sync display view
27         scroll: (event: Event) => syncActive && onScrollDebounced(event),
28         // Handle image & content drag n drop
29         drop: (event: DragEvent) => {
30             if (!event.dataTransfer) {
31                 return;
32             }
33
34             const templateId = event.dataTransfer.getData('bookstack/template');
35             if (templateId) {
36                 event.preventDefault();
37                 editor.actions.insertTemplate(templateId, event.pageX, event.pageY);
38             }
39
40             const clipboard = new Clipboard(event.dataTransfer);
41             const clipboardImages = clipboard.getImages();
42             if (clipboardImages.length > 0) {
43                 event.stopPropagation();
44                 event.preventDefault();
45                 editor.actions.insertClipboardImages(clipboardImages, event.pageX, event.pageY);
46             }
47         },
48         // Handle dragover event to allow as drop-target in chrome
49         dragover: (event: DragEvent) => {
50             event.preventDefault();
51         },
52         // Handle image paste
53         paste: (event: ClipboardEvent) => {
54             if (!event.clipboardData) {
55                 return;
56             }
57
58             const clipboard = new Clipboard(event.clipboardData);
59
60             // Don't handle the event ourselves if no items exist of contains table-looking data
61             if (!clipboard.hasItems() || clipboard.containsTabularData()) {
62                 return;
63             }
64
65             const images = clipboard.getImages();
66             for (const image of images) {
67                 editor.actions.uploadImage(image);
68             }
69         },
70     };
71
72     const cm = Code.markdownEditor(
73         editor.config.inputEl,
74         onViewUpdate,
75         domEventHandlers,
76         provideKeyBindings(editor),
77     );
78
79     // Add editor view to the window for easy access/debugging.
80     // Not part of official API/Docs
81     // @ts-ignore
82     window.mdEditorView = cm;
83
84     return cm;
85 }