]> BookStack Code Mirror - bookstack/blob - resources/js/markdown/display.js
Merge pull request #5626 from BookStackApp/rubentalstra-development
[bookstack] / resources / js / markdown / display.js
1 import {patchDomFromHtmlString} from '../services/vdom.ts';
2
3 export class Display {
4
5     /**
6      * @param {MarkdownEditor} editor
7      */
8     constructor(editor) {
9         this.editor = editor;
10         this.container = editor.config.displayEl;
11
12         this.doc = null;
13         this.lastDisplayClick = 0;
14
15         if (this.container.contentDocument.readyState === 'complete') {
16             this.onLoad();
17         } else {
18             this.container.addEventListener('load', this.onLoad.bind(this));
19         }
20
21         this.updateVisibility(editor.settings.get('showPreview'));
22         editor.settings.onChange('showPreview', show => this.updateVisibility(show));
23     }
24
25     updateVisibility(show) {
26         const wrap = this.container.closest('.markdown-editor-wrap');
27         wrap.style.display = show ? null : 'none';
28     }
29
30     onLoad() {
31         this.doc = this.container.contentDocument;
32
33         this.loadStylesIntoDisplay();
34         this.doc.body.className = 'page-content';
35
36         // Prevent markdown display link click redirect
37         this.doc.addEventListener('click', this.onDisplayClick.bind(this));
38     }
39
40     /**
41      * @param {MouseEvent} event
42      */
43     onDisplayClick(event) {
44         const isDblClick = Date.now() - this.lastDisplayClick < 300;
45
46         const link = event.target.closest('a');
47         if (link !== null) {
48             event.preventDefault();
49             window.open(link.getAttribute('href'));
50             return;
51         }
52
53         const drawing = event.target.closest('[drawio-diagram]');
54         if (drawing !== null && isDblClick) {
55             this.editor.actions.editDrawing(drawing);
56             return;
57         }
58
59         this.lastDisplayClick = Date.now();
60     }
61
62     loadStylesIntoDisplay() {
63         this.doc.documentElement.classList.add('markdown-editor-display');
64
65         // Set display to be dark mode if parent is
66         if (document.documentElement.classList.contains('dark-mode')) {
67             this.doc.documentElement.style.backgroundColor = '#222';
68             this.doc.documentElement.classList.add('dark-mode');
69         }
70
71         this.doc.head.innerHTML = '';
72         const styles = document.head.querySelectorAll('style,link[rel=stylesheet]');
73         for (const style of styles) {
74             const copy = style.cloneNode(true);
75             this.doc.head.appendChild(copy);
76         }
77     }
78
79     /**
80      * Patch the display DOM with the given HTML content.
81      * @param {String} html
82      */
83     patchWithHtml(html) {
84         const {body} = this.doc;
85
86         if (body.children.length === 0) {
87             const wrap = document.createElement('div');
88             this.doc.body.append(wrap);
89         }
90
91         const target = body.children[0];
92
93         patchDomFromHtmlString(target, html);
94     }
95
96     /**
97      * Scroll to the given block index within the display content.
98      * Will scroll to the end if the index is -1.
99      * @param {Number} index
100      */
101     scrollToIndex(index) {
102         const elems = this.doc.body?.children[0]?.children;
103         if (elems && elems.length <= index) return;
104
105         const topElem = (index === -1) ? elems[elems.length - 1] : elems[index];
106         topElem.scrollIntoView({block: 'start', inline: 'nearest', behavior: 'smooth'});
107     }
108
109 }
Morty Proxy This is a proxified and sanitized view of the page, visit original site.