]> BookStack Code Mirror - bookstack/blob - resources/js/editor/schema-nodes.js
Added table creation and insertion
[bookstack] / resources / js / editor / schema-nodes.js
1 import {orderedList, bulletList, listItem} from "prosemirror-schema-list";
2 import {tableNodes} from "prosemirror-tables";
3
4 /**
5  * @param {HTMLElement} node
6  * @return {string|null}
7  */
8 function getAlignAttrFromDomNode(node) {
9     const classList = node.classList;
10     const styles = node.style || {};
11     const alignments = ['right', 'left', 'center', 'justify'];
12     for (const alignment of alignments) {
13         if (classList.contains('align-' + alignment) || styles.textAlign === alignment) {
14             return alignment;
15         }
16     }
17     return null;
18 }
19
20 /**
21  * @param {String} className
22  * @param {Object} attrs
23  * @return {Object}
24  */
25 function addClassToAttrs(className, attrs) {
26     return Object.assign({}, attrs, {
27         class: attrs.class ? attrs.class + ' ' + className : className,
28     });
29 }
30
31 /**
32  * @param node
33  * @param {Object} attrs
34  * @return {Object}
35  */
36 function addAlignmentAttr(node, attrs) {
37     const positions = ['right', 'left', 'center', 'justify'];
38     for (const position of positions) {
39         if (node.attrs.align === position) {
40             return addClassToAttrs('align-' + position, attrs);
41         }
42     }
43     return attrs;
44 }
45
46 function getAttrsParserForAlignment(node) {
47     return {
48         align: getAlignAttrFromDomNode(node),
49     };
50 }
51
52 const doc = {
53     content: "block+",
54 };
55
56 const paragraph = {
57     content: "inline*",
58     group: "block",
59     parseDOM: [
60         {
61             tag: "p",
62             getAttrs: getAttrsParserForAlignment,
63         }
64     ],
65     attrs: {
66         align: {
67             default: null,
68         }
69     },
70     toDOM(node) {
71         return ["p", addAlignmentAttr(node, {}), 0];
72     }
73 };
74
75 const blockquote = {
76     content: "block+",
77     group: "block",
78     defining: true,
79     parseDOM: [{tag: "blockquote", getAttrs: getAttrsParserForAlignment}],
80     attrs: {
81         align: {
82             default: null,
83         }
84     },
85     toDOM(node) {
86         return ["blockquote", addAlignmentAttr(node, {}), 0];
87     }
88 };
89
90 const horizontal_rule = {
91     group: "block",
92     parseDOM: [{tag: "hr"}],
93     toDOM() {
94         return ["hr"];
95     }
96 };
97
98
99 const headingParseGetAttrs = (level) => {
100     return function (node) {
101         return {level, align: getAlignAttrFromDomNode(node)};
102     };
103 };
104 const heading = {
105     attrs: {level: {default: 1}, align: {default: null}},
106     content: "inline*",
107     group: "block",
108     defining: true,
109     parseDOM: [
110         {tag: "h1", getAttrs: headingParseGetAttrs(1)},
111         {tag: "h2", getAttrs: headingParseGetAttrs(2)},
112         {tag: "h3", getAttrs: headingParseGetAttrs(3)},
113         {tag: "h4", getAttrs: headingParseGetAttrs(4)},
114         {tag: "h5", getAttrs: headingParseGetAttrs(5)},
115         {tag: "h6", getAttrs: headingParseGetAttrs(6)},
116     ],
117     toDOM(node) {
118         return ["h" + node.attrs.level, addAlignmentAttr(node, {}), 0]
119     }
120 };
121
122 const code_block = {
123     content: "text*",
124     marks: "",
125     group: "block",
126     code: true,
127     defining: true,
128     parseDOM: [{tag: "pre", preserveWhitespace: "full"}],
129     toDOM() {
130         return ["pre", ["code", 0]];
131     }
132 };
133
134 const text = {
135     group: "inline"
136 };
137
138 const image = {
139     inline: true,
140     attrs: {
141         src: {},
142         alt: {default: null},
143         title: {default: null},
144         height: {default: null},
145         width: {default: null},
146     },
147     group: "inline",
148     draggable: true,
149     parseDOM: [{
150         tag: "img[src]", getAttrs: function getAttrs(dom) {
151             return {
152                 src: dom.getAttribute("src"),
153                 title: dom.getAttribute("title"),
154                 alt: dom.getAttribute("alt"),
155                 height: dom.getAttribute("height"),
156                 width: dom.getAttribute("width"),
157             }
158         }
159     }],
160     toDOM: function toDOM(node) {
161         const ref = node.attrs;
162         const src = ref.src;
163         const alt = ref.alt;
164         const title = ref.title;
165         const width = ref.width;
166         const height = ref.height;
167         return ["img", {src, alt, title, width, height}]
168     }
169 };
170
171 const hard_break = {
172     inline: true,
173     group: "inline",
174     selectable: false,
175     parseDOM: [{tag: "br"}],
176     toDOM() {
177         return ["br"];
178     }
179 };
180
181
182 const calloutParseGetAttrs = (type) => {
183     return function (node) {
184         return {type, align: getAlignAttrFromDomNode(node)};
185     };
186 };
187 const callout = {
188     attrs: {
189         type: {default: 'info'},
190         align: {default: null},
191     },
192     content: "inline*",
193     group: "block",
194     defining: true,
195     parseDOM: [
196         {tag: 'p.callout.info', getAttrs: calloutParseGetAttrs('info'), priority: 75},
197         {tag: 'p.callout.success', getAttrs: calloutParseGetAttrs('success'), priority: 75},
198         {tag: 'p.callout.danger', getAttrs: calloutParseGetAttrs('danger'), priority: 75},
199         {tag: 'p.callout.warning', getAttrs: calloutParseGetAttrs('warning'), priority: 75},
200         {tag: 'p.callout', getAttrs: calloutParseGetAttrs('info'), priority: 75},
201     ],
202     toDOM(node) {
203         const type = node.attrs.type || 'info';
204         return ['p', addAlignmentAttr(node, {class: 'callout ' + type}), 0];
205     }
206 };
207
208 const ordered_list = Object.assign({}, orderedList, {content: "list_item+", group: "block"});
209 const bullet_list = Object.assign({}, bulletList, {content: "list_item+", group: "block"});
210 const list_item = Object.assign({}, listItem, {content: 'paragraph block*'});
211
212 const {
213     table,
214     table_row,
215     table_cell,
216     table_header,
217 } = tableNodes({
218     tableGroup: "block",
219     cellContent: "block*"
220 });
221
222 const nodes = {
223     doc,
224     paragraph,
225     blockquote,
226     horizontal_rule,
227     heading,
228     code_block,
229     text,
230     image,
231     hard_break,
232     callout,
233     ordered_list,
234     bullet_list,
235     list_item,
236     table,
237     table_row,
238     table_cell,
239     table_header,
240 };
241
242 export default nodes;