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