mirror of
				https://github.com/BookStackApp/BookStack.git
				synced 2025-11-03 02:13:16 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			115 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			115 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import { patchDomFromHtmlString } from '../services/vdom';
 | 
						|
import {MarkdownEditor} from "./index.mjs";
 | 
						|
 | 
						|
export class Display {
 | 
						|
    protected editor: MarkdownEditor;
 | 
						|
    protected container: HTMLIFrameElement;
 | 
						|
    protected doc: Document | null = null;
 | 
						|
    protected lastDisplayClick: number = 0;
 | 
						|
 | 
						|
    constructor(editor: MarkdownEditor) {
 | 
						|
        this.editor = editor;
 | 
						|
        this.container = editor.config.displayEl;
 | 
						|
 | 
						|
        if (this.container.contentDocument?.readyState === 'complete') {
 | 
						|
            this.onLoad();
 | 
						|
        } else {
 | 
						|
            this.container.addEventListener('load', this.onLoad.bind(this));
 | 
						|
        }
 | 
						|
 | 
						|
        this.updateVisibility(Boolean(editor.settings.get('showPreview')));
 | 
						|
        editor.settings.onChange('showPreview', (show) => this.updateVisibility(Boolean(show)));
 | 
						|
    }
 | 
						|
 | 
						|
    protected updateVisibility(show: boolean): void {
 | 
						|
        const wrap = this.container.closest('.markdown-editor-wrap') as HTMLElement;
 | 
						|
        wrap.style.display = show ? '' : 'none';
 | 
						|
    }
 | 
						|
 | 
						|
    protected onLoad(): void {
 | 
						|
        this.doc = this.container.contentDocument;
 | 
						|
 | 
						|
        if (!this.doc) return;
 | 
						|
 | 
						|
        this.loadStylesIntoDisplay();
 | 
						|
        this.doc.body.className = 'page-content';
 | 
						|
 | 
						|
        // Prevent markdown display link click redirect
 | 
						|
        this.doc.addEventListener('click', this.onDisplayClick.bind(this));
 | 
						|
    }
 | 
						|
 | 
						|
    protected onDisplayClick(event: MouseEvent): void {
 | 
						|
        const isDblClick = Date.now() - this.lastDisplayClick < 300;
 | 
						|
 | 
						|
        const link = (event.target as Element).closest('a');
 | 
						|
        if (link !== null) {
 | 
						|
            event.preventDefault();
 | 
						|
            const href = link.getAttribute('href');
 | 
						|
            if (href) {
 | 
						|
                window.open(href);
 | 
						|
            }
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        const drawing = (event.target as Element).closest('[drawio-diagram]') as HTMLElement;
 | 
						|
        if (drawing !== null && isDblClick) {
 | 
						|
            this.editor.actions.editDrawing(drawing);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        this.lastDisplayClick = Date.now();
 | 
						|
    }
 | 
						|
 | 
						|
    protected loadStylesIntoDisplay(): void {
 | 
						|
        if (!this.doc) return;
 | 
						|
 | 
						|
        this.doc.documentElement.classList.add('markdown-editor-display');
 | 
						|
 | 
						|
        // Set display to be dark mode if the parent is
 | 
						|
        if (document.documentElement.classList.contains('dark-mode')) {
 | 
						|
            this.doc.documentElement.style.backgroundColor = '#222';
 | 
						|
            this.doc.documentElement.classList.add('dark-mode');
 | 
						|
        }
 | 
						|
 | 
						|
        this.doc.head.innerHTML = '';
 | 
						|
        const styles = document.head.querySelectorAll('style,link[rel=stylesheet]');
 | 
						|
        for (const style of styles) {
 | 
						|
            const copy = style.cloneNode(true) as HTMLElement;
 | 
						|
            this.doc.head.appendChild(copy);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Patch the display DOM with the given HTML content.
 | 
						|
     */
 | 
						|
    public patchWithHtml(html: string): void {
 | 
						|
        if (!this.doc) return;
 | 
						|
 | 
						|
        const { body } = this.doc;
 | 
						|
 | 
						|
        if (body.children.length === 0) {
 | 
						|
            const wrap = document.createElement('div');
 | 
						|
            this.doc.body.append(wrap);
 | 
						|
        }
 | 
						|
 | 
						|
        const target = body.children[0] as HTMLElement;
 | 
						|
 | 
						|
        patchDomFromHtmlString(target, html);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Scroll to the given block index within the display content.
 | 
						|
     * Will scroll to the end if the index is -1.
 | 
						|
     */
 | 
						|
    public scrollToIndex(index: number): void {
 | 
						|
        const elems = this.doc?.body?.children[0]?.children;
 | 
						|
        if (!elems || elems.length <= index) return;
 | 
						|
 | 
						|
        const topElem = (index === -1) ? elems[elems.length - 1] : elems[index];
 | 
						|
        (topElem as Element).scrollIntoView({
 | 
						|
            block: 'start',
 | 
						|
            inline: 'nearest',
 | 
						|
            behavior: 'smooth'
 | 
						|
        });
 | 
						|
    }
 | 
						|
} |