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