]> BookStack Code Mirror - bookstack/blob - resources/js/editor/markdown-serializer.js
Added support for iframe node blocks
[bookstack] / resources / js / editor / markdown-serializer.js
1 import {MarkdownSerializer, defaultMarkdownSerializer, MarkdownSerializerState} from "prosemirror-markdown";
2 import {docToHtml} from "./util";
3
4 const nodes = defaultMarkdownSerializer.nodes;
5 const marks = defaultMarkdownSerializer.marks;
6
7
8 nodes.callout = function (state, node) {
9     writeNodeAsHtml(state, node);
10 };
11
12 nodes.table = function (state, node) {
13     writeNodeAsHtml(state, node);
14 };
15
16 nodes.iframe = function (state, node) {
17     writeNodeAsHtml(state, node);
18 };
19
20 function isPlainURL(link, parent, index, side) {
21     if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) {
22         return false
23     }
24     const content = parent.child(index + (side < 0 ? -1 : 0));
25     if (!content.isText || content.text != link.attrs.href || content.marks[content.marks.length - 1] != link) {
26         return false
27     }
28     if (index == (side < 0 ? 1 : parent.childCount - 1)) {
29         return true
30     }
31     const next = parent.child(index + (side < 0 ? -2 : 1));
32     return !link.isInSet(next.marks)
33 }
34
35 marks.link = {
36     open(state, mark, parent, index) {
37         const attrs = mark.attrs;
38         if (attrs.target) {
39             return `<a href="${attrs.target}" ${attrs.title ? `title="${attrs.title}"` : ''} target="${attrs.target}">`
40         }
41         return isPlainURL(mark, parent, index, 1) ? "<" : "["
42     },
43     close(state, mark, parent, index) {
44         if (mark.attrs.target) {
45             return `</a>`;
46         }
47         return isPlainURL(mark, parent, index, -1) ? ">"
48             : "](" + state.esc(mark.attrs.href) + (mark.attrs.title ? " " + state.quote(mark.attrs.title) : "") + ")"
49     }
50 };
51
52 marks.underline = {
53     open: '<span style="text-decoration: underline;">',
54     close: '</span>',
55 };
56
57 marks.strike = {
58     open: '<span style="text-decoration: line-through;">',
59     close: '</span>',
60 };
61
62 marks.superscript = {
63     open: '<sup>',
64     close: '</sup>',
65 };
66
67 marks.subscript = {
68     open: '<sub>',
69     close: '</sub>',
70 };
71
72 marks.text_color = {
73     open(state, mark, parent, index) {
74         return `<span style="color: ${mark.attrs.color};">`
75     },
76     close: '</span>',
77 };
78
79 marks.background_color = {
80     open(state, mark, parent, index) {
81         return `<span style="background-color: ${mark.attrs.color};">`
82     },
83     close: '</span>',
84 };
85
86 /**
87  * @param {MarkdownSerializerState} state
88  * @param node
89  */
90 function writeNodeAsHtml(state, node) {
91     const html = docToHtml({content: [node]});
92     state.write(html);
93     state.ensureNewLine();
94     state.write('\n');
95     state.closeBlock();
96 }
97
98 // Update serializers to just write out as HTML if we have an attribute
99 // or element that cannot be represented in commonmark without losing
100 // formatting or content.
101 for (const [nodeType, serializerFunction] of Object.entries(nodes)) {
102     nodes[nodeType] = function (state, node, parent, index) {
103         if (node.attrs.align || node.attrs.height || node.attrs.width) {
104             writeNodeAsHtml(state, node);
105         } else {
106             serializerFunction(state, node, parent, index);
107         }
108     }
109 }
110
111
112 const serializer = new MarkdownSerializer(nodes, marks);
113
114 export default serializer;