]> BookStack Code Mirror - bookstack/blobdiff - resources/js/editor/markdown-serializer.js
Started work on details/summary blocks
[bookstack] / resources / js / editor / markdown-serializer.js
index 5e1dfb33bdfa5672633d788c761a6f2d548f1c08..57d8484dc21c39f1f65f0adae2949ff28b8e03fc 100644 (file)
@@ -1,14 +1,61 @@
-import {MarkdownSerializer, defaultMarkdownSerializer} from "prosemirror-markdown";
+import {MarkdownSerializer, defaultMarkdownSerializer, MarkdownSerializerState} from "prosemirror-markdown";
 import {docToHtml} from "./util";
 
 const nodes = defaultMarkdownSerializer.nodes;
 const marks = defaultMarkdownSerializer.marks;
 
 
-nodes.callout = function(state, node) {
+nodes.callout = function (state, node) {
     writeNodeAsHtml(state, node);
 };
 
+nodes.table = function (state, node) {
+    writeNodeAsHtml(state, node);
+};
+
+nodes.iframe = function (state, node) {
+    writeNodeAsHtml(state, node);
+};
+
+nodes.details = function (state, node) {
+    wrapNodeWithHtml(state, node, '<details>', '</details>');
+};
+
+nodes.details_summary = function(state, node) {
+    writeNodeAsHtml(state, node);
+};
+
+function isPlainURL(link, parent, index, side) {
+    if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) {
+        return false
+    }
+    const content = parent.child(index + (side < 0 ? -1 : 0));
+    if (!content.isText || content.text != link.attrs.href || content.marks[content.marks.length - 1] != link) {
+        return false
+    }
+    if (index == (side < 0 ? 1 : parent.childCount - 1)) {
+        return true
+    }
+    const next = parent.child(index + (side < 0 ? -2 : 1));
+    return !link.isInSet(next.marks)
+}
+
+marks.link = {
+    open(state, mark, parent, index) {
+        const attrs = mark.attrs;
+        if (attrs.target) {
+            return `<a href="${attrs.target}" ${attrs.title ? `title="${attrs.title}"` : ''} target="${attrs.target}">`
+        }
+        return isPlainURL(mark, parent, index, 1) ? "<" : "["
+    },
+    close(state, mark, parent, index) {
+        if (mark.attrs.target) {
+            return `</a>`;
+        }
+        return isPlainURL(mark, parent, index, -1) ? ">"
+            : "](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")"
+    }
+};
 
 marks.underline = {
     open: '<span style="text-decoration: underline;">',
@@ -44,21 +91,40 @@ marks.background_color = {
     close: '</span>',
 };
 
-
+/**
+ * @param {MarkdownSerializerState} state
+ * @param {PmNode} node
+ */
 function writeNodeAsHtml(state, node) {
-    const html = docToHtml({ content: [node] });
+    const html = docToHtml({content: [node]});
     state.write(html);
     state.ensureNewLine();
     state.write('\n');
     state.closeBlock();
 }
 
+/**
+ * @param {MarkdownSerializerState} state
+ * @param {PmNode} node
+ * @param {String} openTag
+ * @param {String} closeTag
+ */
+function wrapNodeWithHtml(state, node, openTag, closeTag) {
+    state.write(openTag);
+    state.ensureNewLine();
+    state.renderContent(node);
+    state.write(closeTag);
+    state.closeBlock();
+    state.ensureNewLine();
+    state.write('\n');
+}
+
 // Update serializers to just write out as HTML if we have an attribute
 // or element that cannot be represented in commonmark without losing
 // formatting or content.
 for (const [nodeType, serializerFunction] of Object.entries(nodes)) {
-    nodes[nodeType] = function(state, node, parent, index) {
-        if (node.attrs.align) {
+    nodes[nodeType] = function (state, node, parent, index) {
+        if (node.attrs.align || node.attrs.height || node.attrs.width) {
             writeNodeAsHtml(state, node);
         } else {
             serializerFunction(state, node, parent, index);