]> BookStack Code Mirror - bookstack/blobdiff - resources/js/markdown/actions.js
Got markdown editor barely functional
[bookstack] / resources / js / markdown / actions.js
index 96f9b263c9a7db4be02bc08273c32043e9696b0a..666998723ef16236b5f462bef293a244112d6fe0 100644 (file)
@@ -6,18 +6,28 @@ export class Actions {
      */
     constructor(editor) {
         this.editor = editor;
+        this.lastContent = {
+            html: '',
+            markdown: '',
+        };
     }
 
     updateAndRender() {
-        const content = this.editor.cm.getValue();
+        const content = this.editor.cm.state.doc.toString();
         this.editor.config.inputEl.value = content;
 
         const html = this.editor.markdown.render(content);
-        window.$events.emit('editor-html-change', html);
-        window.$events.emit('editor-markdown-change', content);
+        window.$events.emit('editor-html-change', '');
+        window.$events.emit('editor-markdown-change', '');
+        this.lastContent.html = html;
+        this.lastContent.markdown = content;
         this.editor.display.patchWithHtml(html);
     }
 
+    getContent() {
+        return this.lastContent;
+    }
+
     insertImage() {
         const cursorPos = this.editor.cm.getCursor('from');
         /** @type {ImageManager} **/
@@ -326,6 +336,44 @@ export class Actions {
         return this.replaceLineStart(prefix);
     }
 
+    /**
+     * Cycles through the type of callout block within the selection.
+     * Creates a callout block if none existing, and removes it if cycling past the danger type.
+     */
+    cycleCalloutTypeAtSelection() {
+        const selectionRange = this.editor.cm.listSelections()[0];
+        const lineContent = this.editor.cm.getLine(selectionRange.anchor.line);
+        const lineLength = lineContent.length;
+        const contentRange = {
+            anchor: {line: selectionRange.anchor.line, ch: 0},
+            head: {line: selectionRange.anchor.line, ch: lineLength},
+        };
+
+        const formats = ['info', 'success', 'warning', 'danger'];
+        const joint = formats.join('|');
+        const regex = new RegExp(`class="((${joint})\\s+callout|callout\\s+(${joint}))"`, 'i');
+        const matches = regex.exec(lineContent);
+        const format = (matches ? (matches[2] || matches[3]) : '').toLowerCase();
+
+        if (format === formats[formats.length - 1]) {
+            this.wrapLine(`<p class="callout ${formats[formats.length - 1]}">`, '</p>');
+        } else if (format === '') {
+            this.wrapLine('<p class="callout info">', '</p>');
+        } else {
+            const newFormatIndex = formats.indexOf(format) + 1;
+            const newFormat = formats[newFormatIndex];
+            const newContent = lineContent.replace(matches[0], matches[0].replace(format, newFormat));
+            this.editor.cm.replaceRange(newContent, contentRange.anchor, contentRange.head);
+
+            const chDiff = newContent.length - lineContent.length;
+            selectionRange.anchor.ch += chDiff;
+            if (selectionRange.anchor !== selectionRange.head) {
+                selectionRange.head.ch += chDiff;
+            }
+            this.editor.cm.setSelection(selectionRange.anchor, selectionRange.head);
+        }
+    }
+
     /**
      * Handle image upload and add image into markdown content
      * @param {File} file
@@ -363,17 +411,17 @@ export class Actions {
         });
     }
 
-    syncDisplayPosition() {
+    syncDisplayPosition(event) {
         // Thanks to http://liuhao.im/english/2015/11/10/the-sync-scroll-of-markdown-editor-in-javascript.html
-        const scroll = this.editor.cm.getScrollInfo();
-        const atEnd = scroll.top + scroll.clientHeight === scroll.height;
+        const scrollEl = event.target;
+        const atEnd = Math.abs(scrollEl.scrollHeight - scrollEl.clientHeight - scrollEl.scrollTop) < 1;
         if (atEnd) {
             this.editor.display.scrollToIndex(-1);
             return;
         }
 
-        const lineNum = this.editor.cm.lineAtHeight(scroll.top, 'local');
-        const range = this.editor.cm.getRange({line: 0, ch: null}, {line: lineNum, ch: null});
+        const blockInfo = this.editor.cm.lineBlockAtHeight(scrollEl.scrollTop);
+        const range = this.editor.cm.state.sliceDoc(0, blockInfo.from);
         const parser = new DOMParser();
         const doc = parser.parseFromString(this.editor.markdown.render(range), 'text/html');
         const totalLines = doc.documentElement.querySelectorAll('body > *');
@@ -404,7 +452,7 @@ export class Actions {
         const cursorPos = this.editor.cm.coordsChar({left: event.pageX, top: event.pageY});
         this.editor.cm.setCursor(cursorPos);
         for (const image of images) {
-            this.editor.actions.uploadImage(image);
+            this.uploadImage(image);
         }
     }
 }
\ No newline at end of file