@openleaf-editor/core 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/dist/commands.d.ts +82 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +327 -0
- package/dist/commands.js.map +1 -0
- package/dist/extensions.d.ts +91 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +281 -0
- package/dist/extensions.js.map +1 -0
- package/dist/html.d.ts +22 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +117 -0
- package/dist/html.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/keymap.d.ts +49 -0
- package/dist/keymap.d.ts.map +1 -0
- package/dist/keymap.js +106 -0
- package/dist/keymap.js.map +1 -0
- package/dist/plugins.d.ts +29 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +91 -0
- package/dist/plugins.js.map +1 -0
- package/dist/preserve.d.ts +55 -0
- package/dist/preserve.d.ts.map +1 -0
- package/dist/preserve.js +288 -0
- package/dist/preserve.js.map +1 -0
- package/dist/schema.d.ts +23 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +268 -0
- package/dist/schema.js.map +1 -0
- package/dist/tables.d.ts +43 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +172 -0
- package/dist/tables.js.map +1 -0
- package/dist/url.d.ts +26 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +78 -0
- package/dist/url.js.map +1 -0
- package/package.json +43 -0
package/dist/schema.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OpenLeaf document schema.
|
|
3
|
+
*
|
|
4
|
+
* Scope note: table NODES are here, in the base schema, so that every
|
|
5
|
+
* deployment reads and writes tables faithfully. Table EDITING -- cell
|
|
6
|
+
* selection, column resizing, the row and column commands and toolbar -- is
|
|
7
|
+
* the opt-in @openleaf-editor/plugins-table. See tables.ts for why the split falls
|
|
8
|
+
* there rather than at the package boundary.
|
|
9
|
+
*/
|
|
10
|
+
import { Schema } from 'prosemirror-model';
|
|
11
|
+
import { unknownBlock, unknownInline } from './preserve.js';
|
|
12
|
+
import { table, table_cell, table_header, table_row } from './tables.js';
|
|
13
|
+
import { isSafeUrl } from './url.js';
|
|
14
|
+
/** The base node specs. Extensions are appended to these. */
|
|
15
|
+
export const coreNodes = {
|
|
16
|
+
doc: { content: 'block+' },
|
|
17
|
+
paragraph: {
|
|
18
|
+
// `dir` is bidirectional text direction, not styling. Dropping it
|
|
19
|
+
// silently breaks Arabic, Hebrew and Persian content, so it is a
|
|
20
|
+
// first-class schema attribute rather than something the preservation
|
|
21
|
+
// layer has to rescue.
|
|
22
|
+
attrs: { dir: { default: null } },
|
|
23
|
+
content: 'inline*',
|
|
24
|
+
group: 'block',
|
|
25
|
+
parseDOM: [
|
|
26
|
+
{
|
|
27
|
+
tag: 'p',
|
|
28
|
+
getAttrs: (dom) => ({ dir: dom.getAttribute('dir') }),
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
toDOM(node) {
|
|
32
|
+
const dir = node.attrs['dir'];
|
|
33
|
+
return dir ? ['p', { dir }, 0] : ['p', 0];
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
heading: {
|
|
37
|
+
attrs: { level: { default: 1 }, dir: { default: null } },
|
|
38
|
+
content: 'inline*',
|
|
39
|
+
group: 'block',
|
|
40
|
+
defining: true,
|
|
41
|
+
parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({
|
|
42
|
+
tag: `h${level}`,
|
|
43
|
+
getAttrs: (dom) => ({ level, dir: dom.getAttribute('dir') }),
|
|
44
|
+
})),
|
|
45
|
+
toDOM(node) {
|
|
46
|
+
const tag = `h${node.attrs['level']}`;
|
|
47
|
+
const dir = node.attrs['dir'];
|
|
48
|
+
return dir ? [tag, { dir }, 0] : [tag, 0];
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
blockquote: {
|
|
52
|
+
content: 'block+',
|
|
53
|
+
group: 'block',
|
|
54
|
+
defining: true,
|
|
55
|
+
parseDOM: [{ tag: 'blockquote' }],
|
|
56
|
+
toDOM: () => ['blockquote', 0],
|
|
57
|
+
},
|
|
58
|
+
code_block: {
|
|
59
|
+
// `language` is content, not decoration. Without it a fenced block loses
|
|
60
|
+
// `class="language-js"` on the first save -- which is both an attribute-loss
|
|
61
|
+
// bug and the reason a highlighter has nothing to work from.
|
|
62
|
+
attrs: { language: { default: null } },
|
|
63
|
+
content: 'text*',
|
|
64
|
+
marks: '',
|
|
65
|
+
group: 'block',
|
|
66
|
+
code: true,
|
|
67
|
+
defining: true,
|
|
68
|
+
parseDOM: [
|
|
69
|
+
{
|
|
70
|
+
tag: 'pre',
|
|
71
|
+
preserveWhitespace: 'full',
|
|
72
|
+
getAttrs(dom) {
|
|
73
|
+
const pre = dom;
|
|
74
|
+
// The class may sit on either element in the wild. CommonMark, Prism
|
|
75
|
+
// and highlight.js all put it on <code>; older CMS output often puts
|
|
76
|
+
// it on <pre>. Read both, write one.
|
|
77
|
+
const code = pre.querySelector('code');
|
|
78
|
+
const source = `${pre.getAttribute('class') ?? ''} ${code?.getAttribute('class') ?? ''}`;
|
|
79
|
+
const match = /(?:^|\s)(?:language|lang)-([a-z0-9+#.-]+)/i.exec(source);
|
|
80
|
+
return { language: match ? match[1].toLowerCase() : null };
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
toDOM(node) {
|
|
85
|
+
const language = node.attrs['language'];
|
|
86
|
+
// Normalized onto <code>, which is where every downstream highlighter
|
|
87
|
+
// looks. A block authored with the class on <pre> moves it; that is a
|
|
88
|
+
// one-time normalization and it is stable thereafter.
|
|
89
|
+
return language ? ['pre', ['code', { class: `language-${language}` }, 0]] : ['pre', ['code', 0]];
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
horizontal_rule: {
|
|
93
|
+
group: 'block',
|
|
94
|
+
parseDOM: [{ tag: 'hr' }],
|
|
95
|
+
toDOM: () => ['hr'],
|
|
96
|
+
},
|
|
97
|
+
bullet_list: {
|
|
98
|
+
content: 'list_item+',
|
|
99
|
+
group: 'block',
|
|
100
|
+
parseDOM: [{ tag: 'ul' }],
|
|
101
|
+
toDOM: () => ['ul', 0],
|
|
102
|
+
},
|
|
103
|
+
ordered_list: {
|
|
104
|
+
attrs: { start: { default: 1 } },
|
|
105
|
+
content: 'list_item+',
|
|
106
|
+
group: 'block',
|
|
107
|
+
parseDOM: [
|
|
108
|
+
{
|
|
109
|
+
tag: 'ol',
|
|
110
|
+
getAttrs(dom) {
|
|
111
|
+
const start = dom.getAttribute('start');
|
|
112
|
+
return { start: start ? Number(start) : 1 };
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
toDOM(node) {
|
|
117
|
+
const start = node.attrs['start'];
|
|
118
|
+
return start === 1 ? ['ol', 0] : ['ol', { start: String(start) }, 0];
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
list_item: {
|
|
122
|
+
content: 'paragraph block*',
|
|
123
|
+
defining: true,
|
|
124
|
+
parseDOM: [{ tag: 'li' }],
|
|
125
|
+
toDOM: () => ['li', 0],
|
|
126
|
+
},
|
|
127
|
+
image: {
|
|
128
|
+
inline: true,
|
|
129
|
+
group: 'inline',
|
|
130
|
+
draggable: true,
|
|
131
|
+
attrs: {
|
|
132
|
+
src: {},
|
|
133
|
+
alt: { default: null },
|
|
134
|
+
title: { default: null },
|
|
135
|
+
width: { default: null },
|
|
136
|
+
height: { default: null },
|
|
137
|
+
},
|
|
138
|
+
parseDOM: [
|
|
139
|
+
{
|
|
140
|
+
tag: 'img[src]',
|
|
141
|
+
getAttrs(dom) {
|
|
142
|
+
const el = dom;
|
|
143
|
+
// Returning false declines the rule, so an image with a
|
|
144
|
+
// `javascript:` src is dropped rather than carried through.
|
|
145
|
+
if (!isSafeUrl(el.getAttribute('src')))
|
|
146
|
+
return false;
|
|
147
|
+
return {
|
|
148
|
+
src: el.getAttribute('src'),
|
|
149
|
+
// An absent alt and alt="" mean different things to a screen
|
|
150
|
+
// reader: "undescribed" versus "decorative". Never conflate them.
|
|
151
|
+
alt: el.getAttribute('alt'),
|
|
152
|
+
title: el.getAttribute('title'),
|
|
153
|
+
width: el.getAttribute('width'),
|
|
154
|
+
height: el.getAttribute('height'),
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
toDOM(node) {
|
|
160
|
+
const { src, alt, title, width, height } = node.attrs;
|
|
161
|
+
const attrs = { src: src };
|
|
162
|
+
if (alt !== null)
|
|
163
|
+
attrs['alt'] = alt;
|
|
164
|
+
if (title !== null)
|
|
165
|
+
attrs['title'] = title;
|
|
166
|
+
if (width !== null)
|
|
167
|
+
attrs['width'] = width;
|
|
168
|
+
if (height !== null)
|
|
169
|
+
attrs['height'] = height;
|
|
170
|
+
return ['img', attrs];
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
hard_break: {
|
|
174
|
+
inline: true,
|
|
175
|
+
group: 'inline',
|
|
176
|
+
selectable: false,
|
|
177
|
+
parseDOM: [{ tag: 'br' }],
|
|
178
|
+
toDOM: () => ['br'],
|
|
179
|
+
},
|
|
180
|
+
text: { group: 'inline' },
|
|
181
|
+
// Tables are in the BASE schema deliberately. Without them a <table> in
|
|
182
|
+
// stored content becomes an opaque preserved atom -- faithful but uneditable,
|
|
183
|
+
// which is not something you can tell a CMS. The heavy part (cell selection,
|
|
184
|
+
// column resizing, the row and column commands) is the opt-in
|
|
185
|
+
// @openleaf-editor/plugins-table.
|
|
186
|
+
table,
|
|
187
|
+
table_row,
|
|
188
|
+
table_cell,
|
|
189
|
+
table_header,
|
|
190
|
+
unknown_block: unknownBlock,
|
|
191
|
+
unknown_inline: unknownInline,
|
|
192
|
+
};
|
|
193
|
+
/** The base mark specs. */
|
|
194
|
+
export const coreMarks = {
|
|
195
|
+
strong: {
|
|
196
|
+
parseDOM: [
|
|
197
|
+
{ tag: 'strong' },
|
|
198
|
+
{ tag: 'b' },
|
|
199
|
+
{ style: 'font-weight=bold' },
|
|
200
|
+
{ style: 'font-weight=700' },
|
|
201
|
+
],
|
|
202
|
+
toDOM: () => ['strong', 0],
|
|
203
|
+
},
|
|
204
|
+
em: {
|
|
205
|
+
parseDOM: [{ tag: 'em' }, { tag: 'i' }, { style: 'font-style=italic' }],
|
|
206
|
+
toDOM: () => ['em', 0],
|
|
207
|
+
},
|
|
208
|
+
underline: {
|
|
209
|
+
parseDOM: [{ tag: 'u' }, { style: 'text-decoration=underline' }],
|
|
210
|
+
toDOM: () => ['u', 0],
|
|
211
|
+
},
|
|
212
|
+
strike: {
|
|
213
|
+
parseDOM: [{ tag: 's' }, { tag: 'del' }, { style: 'text-decoration=line-through' }],
|
|
214
|
+
toDOM: () => ['s', 0],
|
|
215
|
+
},
|
|
216
|
+
code: {
|
|
217
|
+
parseDOM: [{ tag: 'code' }],
|
|
218
|
+
toDOM: () => ['code', 0],
|
|
219
|
+
},
|
|
220
|
+
link: {
|
|
221
|
+
attrs: {
|
|
222
|
+
href: {},
|
|
223
|
+
title: { default: null },
|
|
224
|
+
target: { default: null },
|
|
225
|
+
rel: { default: null },
|
|
226
|
+
},
|
|
227
|
+
inclusive: false,
|
|
228
|
+
parseDOM: [
|
|
229
|
+
{
|
|
230
|
+
tag: 'a[href]',
|
|
231
|
+
getAttrs(dom) {
|
|
232
|
+
const el = dom;
|
|
233
|
+
// A `javascript:` href is the cheapest XSS vector there is: no
|
|
234
|
+
// injected element, no script tag, just a reader who clicks.
|
|
235
|
+
// Declining the rule keeps the link TEXT and drops the mark.
|
|
236
|
+
if (!isSafeUrl(el.getAttribute('href')))
|
|
237
|
+
return false;
|
|
238
|
+
return {
|
|
239
|
+
href: el.getAttribute('href'),
|
|
240
|
+
title: el.getAttribute('title'),
|
|
241
|
+
target: el.getAttribute('target'),
|
|
242
|
+
rel: el.getAttribute('rel'),
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
toDOM(node) {
|
|
248
|
+
const { href, title, target, rel } = node.attrs;
|
|
249
|
+
const attrs = { href: href };
|
|
250
|
+
if (title !== null)
|
|
251
|
+
attrs['title'] = title;
|
|
252
|
+
if (target !== null)
|
|
253
|
+
attrs['target'] = target;
|
|
254
|
+
if (rel !== null)
|
|
255
|
+
attrs['rel'] = rel;
|
|
256
|
+
return ['a', attrs, 0];
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* The base schema, with no extensions.
|
|
262
|
+
*
|
|
263
|
+
* Kept for the many places that only ever need the built-in types. Anything that
|
|
264
|
+
* must honour plugin-contributed node types uses `createSchema` or reads
|
|
265
|
+
* `state.schema` instead -- see extensions.ts.
|
|
266
|
+
*/
|
|
267
|
+
export const baseSchema = new Schema({ nodes: coreNodes, marks: coreMarks });
|
|
268
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/tables.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table node specifications.
|
|
3
|
+
*
|
|
4
|
+
* ## Why these live in core rather than in the opt-in plugin
|
|
5
|
+
*
|
|
6
|
+
* The obvious design is to put tables entirely in `@openleaf-editor/plugins-table`, so
|
|
7
|
+
* a CMS that forbids tables ships none of the code. That is wrong, and the
|
|
8
|
+
* fidelity harness is what shows why: without these node types, a `<table>` in
|
|
9
|
+
* stored content is claimed by the preservation layer and becomes a single
|
|
10
|
+
* opaque atom. It round-trips faithfully -- but it is *uneditable*. An author
|
|
11
|
+
* opening a decade-old post finds a grey card where their table used to be.
|
|
12
|
+
*
|
|
13
|
+
* "We read your tables but you may not touch them" is not a thing you can tell a
|
|
14
|
+
* CMS. So the schema is always present, costing about a kilobyte, and what is
|
|
15
|
+
* genuinely opt-in is the *editing machinery*: cell selection, column resizing,
|
|
16
|
+
* the row and column commands, the toolbar controls. That is the part with real
|
|
17
|
+
* weight, and it lives in the plugin.
|
|
18
|
+
*
|
|
19
|
+
* ## Compatibility with prosemirror-tables
|
|
20
|
+
*
|
|
21
|
+
* Each spec carries a `tableRole`, which is how `prosemirror-tables` recognises
|
|
22
|
+
* these nodes. Cell attributes are named `colspan`, `rowspan` and `colwidth`
|
|
23
|
+
* exactly because that library requires those names. Deviating would mean
|
|
24
|
+
* forking it.
|
|
25
|
+
*
|
|
26
|
+
* ## Legacy presentational attributes
|
|
27
|
+
*
|
|
28
|
+
* `border`, `cellpadding`, `cellspacing`, `align` and friends are kept, which a
|
|
29
|
+
* clean-slate schema would not do. They are how HTML expressed table styling for
|
|
30
|
+
* fifteen years, they are all over the content this editor is meant to inherit,
|
|
31
|
+
* and dropping them changes how a page renders. Preserving them is the same
|
|
32
|
+
* decision the preservation layer makes, applied to attributes.
|
|
33
|
+
*
|
|
34
|
+
* `scope` on a header cell is kept for a different and more important reason: it
|
|
35
|
+
* is what tells a screen reader which cells a header governs. Dropping it turns
|
|
36
|
+
* a navigable table into a grid of unrelated values.
|
|
37
|
+
*/
|
|
38
|
+
import type { NodeSpec } from 'prosemirror-model';
|
|
39
|
+
export declare const table: NodeSpec;
|
|
40
|
+
export declare const table_row: NodeSpec;
|
|
41
|
+
export declare const table_cell: NodeSpec;
|
|
42
|
+
export declare const table_header: NodeSpec;
|
|
43
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AA2EjD,eAAO,MAAM,KAAK,EAAE,QA0CnB,CAAA;AAED,eAAO,MAAM,SAAS,EAAE,QAQvB,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,QAOxB,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,QAO1B,CAAA"}
|
package/dist/tables.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table node specifications.
|
|
3
|
+
*
|
|
4
|
+
* ## Why these live in core rather than in the opt-in plugin
|
|
5
|
+
*
|
|
6
|
+
* The obvious design is to put tables entirely in `@openleaf-editor/plugins-table`, so
|
|
7
|
+
* a CMS that forbids tables ships none of the code. That is wrong, and the
|
|
8
|
+
* fidelity harness is what shows why: without these node types, a `<table>` in
|
|
9
|
+
* stored content is claimed by the preservation layer and becomes a single
|
|
10
|
+
* opaque atom. It round-trips faithfully -- but it is *uneditable*. An author
|
|
11
|
+
* opening a decade-old post finds a grey card where their table used to be.
|
|
12
|
+
*
|
|
13
|
+
* "We read your tables but you may not touch them" is not a thing you can tell a
|
|
14
|
+
* CMS. So the schema is always present, costing about a kilobyte, and what is
|
|
15
|
+
* genuinely opt-in is the *editing machinery*: cell selection, column resizing,
|
|
16
|
+
* the row and column commands, the toolbar controls. That is the part with real
|
|
17
|
+
* weight, and it lives in the plugin.
|
|
18
|
+
*
|
|
19
|
+
* ## Compatibility with prosemirror-tables
|
|
20
|
+
*
|
|
21
|
+
* Each spec carries a `tableRole`, which is how `prosemirror-tables` recognises
|
|
22
|
+
* these nodes. Cell attributes are named `colspan`, `rowspan` and `colwidth`
|
|
23
|
+
* exactly because that library requires those names. Deviating would mean
|
|
24
|
+
* forking it.
|
|
25
|
+
*
|
|
26
|
+
* ## Legacy presentational attributes
|
|
27
|
+
*
|
|
28
|
+
* `border`, `cellpadding`, `cellspacing`, `align` and friends are kept, which a
|
|
29
|
+
* clean-slate schema would not do. They are how HTML expressed table styling for
|
|
30
|
+
* fifteen years, they are all over the content this editor is meant to inherit,
|
|
31
|
+
* and dropping them changes how a page renders. Preserving them is the same
|
|
32
|
+
* decision the preservation layer makes, applied to attributes.
|
|
33
|
+
*
|
|
34
|
+
* `scope` on a header cell is kept for a different and more important reason: it
|
|
35
|
+
* is what tells a screen reader which cells a header governs. Dropping it turns
|
|
36
|
+
* a navigable table into a grid of unrelated values.
|
|
37
|
+
*/
|
|
38
|
+
/** Presentational attributes legacy CMS content puts on `<table>`. */
|
|
39
|
+
const TABLE_LEGACY_ATTRS = ['border', 'cellpadding', 'cellspacing', 'width', 'align', 'summary', 'class'];
|
|
40
|
+
/** Attributes legacy content puts on cells, plus the accessibility-critical ones. */
|
|
41
|
+
const CELL_LEGACY_ATTRS = ['align', 'valign', 'width', 'height', 'class', 'scope', 'headers', 'abbr'];
|
|
42
|
+
function readAttrs(el, names) {
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const name of names)
|
|
45
|
+
out[name] = el.getAttribute(name);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
function writeAttrs(attrs, names) {
|
|
49
|
+
const out = {};
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
const value = attrs[name];
|
|
52
|
+
if (value !== null && value !== undefined && value !== '')
|
|
53
|
+
out[name] = String(value);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
const legacyDefaults = (names) => Object.fromEntries(names.map((name) => [name, { default: null }]));
|
|
58
|
+
/**
|
|
59
|
+
* Parse `colwidth` from an inline width style or attribute.
|
|
60
|
+
*
|
|
61
|
+
* `prosemirror-tables` stores column widths as an array of numbers on the cell
|
|
62
|
+
* that starts the column, which is how its resizing plugin reads them.
|
|
63
|
+
*/
|
|
64
|
+
function readColwidth(el) {
|
|
65
|
+
const widthAttr = el.getAttribute('data-colwidth');
|
|
66
|
+
if (widthAttr) {
|
|
67
|
+
const parsed = widthAttr.split(',').map((n) => Number.parseInt(n, 10));
|
|
68
|
+
if (parsed.every((n) => Number.isFinite(n)))
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const cellAttrs = {
|
|
74
|
+
// These three names are required by prosemirror-tables.
|
|
75
|
+
colspan: { default: 1 },
|
|
76
|
+
rowspan: { default: 1 },
|
|
77
|
+
colwidth: { default: null },
|
|
78
|
+
...legacyDefaults(CELL_LEGACY_ATTRS),
|
|
79
|
+
};
|
|
80
|
+
function cellGetAttrs(dom) {
|
|
81
|
+
const el = dom;
|
|
82
|
+
return {
|
|
83
|
+
colspan: Number.parseInt(el.getAttribute('colspan') ?? '1', 10) || 1,
|
|
84
|
+
rowspan: Number.parseInt(el.getAttribute('rowspan') ?? '1', 10) || 1,
|
|
85
|
+
colwidth: readColwidth(el),
|
|
86
|
+
...readAttrs(el, CELL_LEGACY_ATTRS),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function cellToDOM(tag) {
|
|
90
|
+
return (node) => {
|
|
91
|
+
const attrs = writeAttrs(node.attrs, CELL_LEGACY_ATTRS);
|
|
92
|
+
const colspan = node.attrs['colspan'];
|
|
93
|
+
const rowspan = node.attrs['rowspan'];
|
|
94
|
+
if (colspan !== 1)
|
|
95
|
+
attrs['colspan'] = String(colspan);
|
|
96
|
+
if (rowspan !== 1)
|
|
97
|
+
attrs['rowspan'] = String(rowspan);
|
|
98
|
+
const colwidth = node.attrs['colwidth'];
|
|
99
|
+
if (colwidth)
|
|
100
|
+
attrs['data-colwidth'] = colwidth.join(',');
|
|
101
|
+
return [tag, attrs, 0];
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export const table = {
|
|
105
|
+
content: 'table_row+',
|
|
106
|
+
tableRole: 'table',
|
|
107
|
+
isolating: true,
|
|
108
|
+
group: 'block',
|
|
109
|
+
attrs: legacyDefaults(TABLE_LEGACY_ATTRS),
|
|
110
|
+
parseDOM: [
|
|
111
|
+
{ tag: 'table', getAttrs: (dom) => readAttrs(dom, TABLE_LEGACY_ATTRS) },
|
|
112
|
+
/*
|
|
113
|
+
* `tbody`, `thead` and `tfoot` are SKIPPED rather than ignored: the wrapper
|
|
114
|
+
* itself carries nothing, but its rows are the entire content. `ignore`
|
|
115
|
+
* would delete the table's contents along with the wrapper.
|
|
116
|
+
*
|
|
117
|
+
* These rules also have to exist because the preservation layer's catch-all
|
|
118
|
+
* would otherwise claim a `<tbody>` as unrecognised markup and produce an
|
|
119
|
+
* opaque atom that `table_row+` refuses to accept.
|
|
120
|
+
*/
|
|
121
|
+
{ tag: 'tbody', skip: true },
|
|
122
|
+
{ tag: 'thead', skip: true },
|
|
123
|
+
{ tag: 'tfoot', skip: true },
|
|
124
|
+
/*
|
|
125
|
+
* KNOWN LIMITATION, declared rather than hidden.
|
|
126
|
+
*
|
|
127
|
+
* `<colgroup>`/`<col>` are dropped, and `<caption>` is dropped with its
|
|
128
|
+
* text. Both should be preserved and neither can be today: a caption node
|
|
129
|
+
* would have to be the table's first child, and `prosemirror-tables`
|
|
130
|
+
* computes its cell map by treating every child of a table as a row, so a
|
|
131
|
+
* leading caption breaks its indexing.
|
|
132
|
+
*
|
|
133
|
+
* Dropping a caption is a real accessibility regression -- a caption is a
|
|
134
|
+
* table's accessible name -- so this is tracked as a bug to fix by adding a
|
|
135
|
+
* caption node and contributing the indexing fix upstream, not as a design
|
|
136
|
+
* decision. There is a fixture asserting the current behaviour so it cannot
|
|
137
|
+
* regress further without someone noticing.
|
|
138
|
+
*/
|
|
139
|
+
{ tag: 'colgroup', ignore: true },
|
|
140
|
+
{ tag: 'col', ignore: true },
|
|
141
|
+
{ tag: 'caption', ignore: true },
|
|
142
|
+
],
|
|
143
|
+
toDOM(node) {
|
|
144
|
+
return ['table', writeAttrs(node.attrs, TABLE_LEGACY_ATTRS), ['tbody', 0]];
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
export const table_row = {
|
|
148
|
+
content: '(table_cell | table_header)*',
|
|
149
|
+
tableRole: 'row',
|
|
150
|
+
attrs: { class: { default: null }, align: { default: null } },
|
|
151
|
+
parseDOM: [{ tag: 'tr', getAttrs: (dom) => readAttrs(dom, ['class', 'align']) }],
|
|
152
|
+
toDOM(node) {
|
|
153
|
+
return ['tr', writeAttrs(node.attrs, ['class', 'align']), 0];
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
export const table_cell = {
|
|
157
|
+
content: 'block+',
|
|
158
|
+
attrs: cellAttrs,
|
|
159
|
+
tableRole: 'cell',
|
|
160
|
+
isolating: true,
|
|
161
|
+
parseDOM: [{ tag: 'td', getAttrs: cellGetAttrs }],
|
|
162
|
+
toDOM: cellToDOM('td'),
|
|
163
|
+
};
|
|
164
|
+
export const table_header = {
|
|
165
|
+
content: 'block+',
|
|
166
|
+
attrs: cellAttrs,
|
|
167
|
+
tableRole: 'header_cell',
|
|
168
|
+
isolating: true,
|
|
169
|
+
parseDOM: [{ tag: 'th', getAttrs: cellGetAttrs }],
|
|
170
|
+
toDOM: cellToDOM('th'),
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"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"}
|
package/dist/url.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL safety.
|
|
3
|
+
*
|
|
4
|
+
* A link is the cheapest XSS vector there is: `<a href="javascript:...">` needs
|
|
5
|
+
* no injected element, no script tag, and no clever parsing trick -- just a user
|
|
6
|
+
* who clicks. OpenLeaf's schema previously accepted any `href` at all.
|
|
7
|
+
*
|
|
8
|
+
* The checks here are deliberately a small allowlist of schemes rather than a
|
|
9
|
+
* blocklist of dangerous ones. A blocklist is a promise to have thought of every
|
|
10
|
+
* scheme, including the ones a browser will invent later.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Is this URL safe to put in an `href` or `src`?
|
|
14
|
+
*
|
|
15
|
+
* Relative URLs, fragments and query-only URLs are allowed: they cannot carry a
|
|
16
|
+
* scheme, so they cannot execute. Protocol-relative `//host/path` is allowed
|
|
17
|
+
* because it inherits the page's scheme.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isSafeUrl(value: string | null | undefined): boolean;
|
|
20
|
+
/** The safe URL, or null when it should be dropped. */
|
|
21
|
+
export declare function safeUrlOrNull(value: string | null | undefined): string | null;
|
|
22
|
+
/** Attributes whose values are URLs and therefore need checking. */
|
|
23
|
+
export declare const URL_ATTRIBUTES: ReadonlySet<string>;
|
|
24
|
+
/** True for `onclick`, `onmouseover` and every other inline event handler. */
|
|
25
|
+
export declare function isEventHandlerAttribute(name: string): boolean;
|
|
26
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA6BH;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAUnE;AAED,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAE7E;AAED,oEAAoE;AACpE,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAa7C,CAAA;AAEF,8EAA8E;AAC9E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D"}
|
package/dist/url.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL safety.
|
|
3
|
+
*
|
|
4
|
+
* A link is the cheapest XSS vector there is: `<a href="javascript:...">` needs
|
|
5
|
+
* no injected element, no script tag, and no clever parsing trick -- just a user
|
|
6
|
+
* who clicks. OpenLeaf's schema previously accepted any `href` at all.
|
|
7
|
+
*
|
|
8
|
+
* The checks here are deliberately a small allowlist of schemes rather than a
|
|
9
|
+
* blocklist of dangerous ones. A blocklist is a promise to have thought of every
|
|
10
|
+
* scheme, including the ones a browser will invent later.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Schemes an author may link to.
|
|
14
|
+
*
|
|
15
|
+
* `data:` is absent on purpose. `data:text/html` is a full XSS vector, and
|
|
16
|
+
* distinguishing safe data URLs from dangerous ones by sniffing the media type
|
|
17
|
+
* is exactly the kind of parsing that gets defeated. Integrators who genuinely
|
|
18
|
+
* need inline images can permit them in their server-side policy, where the
|
|
19
|
+
* decision is explicit and reviewable.
|
|
20
|
+
*/
|
|
21
|
+
const SAFE_SCHEMES = new Set(['http', 'https', 'mailto', 'tel', 'ftp', 'ftps']);
|
|
22
|
+
/** Matches a leading URL scheme, per RFC 3986. */
|
|
23
|
+
const SCHEME = /^([a-z][a-z0-9+.-]*):/i;
|
|
24
|
+
/**
|
|
25
|
+
* Strip the characters browsers ignore when resolving a scheme.
|
|
26
|
+
*
|
|
27
|
+
* `java\tscript:alert(1)` and `java\nscript:alert(1)` both navigate. Browsers
|
|
28
|
+
* discard ASCII whitespace and control characters while parsing the scheme, so
|
|
29
|
+
* any check that does not do the same is trivially bypassed. Attribute values
|
|
30
|
+
* arrive already entity-decoded from `getAttribute`, so `javascript:` is
|
|
31
|
+
* covered by the same normalization.
|
|
32
|
+
*/
|
|
33
|
+
function normalize(value) {
|
|
34
|
+
return value.replace(/[\u0000-\u0020\u007f-\u00a0]/g, '').toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Is this URL safe to put in an `href` or `src`?
|
|
38
|
+
*
|
|
39
|
+
* Relative URLs, fragments and query-only URLs are allowed: they cannot carry a
|
|
40
|
+
* scheme, so they cannot execute. Protocol-relative `//host/path` is allowed
|
|
41
|
+
* because it inherits the page's scheme.
|
|
42
|
+
*/
|
|
43
|
+
export function isSafeUrl(value) {
|
|
44
|
+
if (value === null || value === undefined)
|
|
45
|
+
return false;
|
|
46
|
+
const candidate = normalize(value);
|
|
47
|
+
if (candidate === '')
|
|
48
|
+
return false;
|
|
49
|
+
const match = SCHEME.exec(candidate);
|
|
50
|
+
// No scheme means relative, fragment or protocol-relative. All are safe.
|
|
51
|
+
if (!match)
|
|
52
|
+
return true;
|
|
53
|
+
return SAFE_SCHEMES.has(match[1]);
|
|
54
|
+
}
|
|
55
|
+
/** The safe URL, or null when it should be dropped. */
|
|
56
|
+
export function safeUrlOrNull(value) {
|
|
57
|
+
return isSafeUrl(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
/** Attributes whose values are URLs and therefore need checking. */
|
|
60
|
+
export const URL_ATTRIBUTES = new Set([
|
|
61
|
+
'href',
|
|
62
|
+
'src',
|
|
63
|
+
'action',
|
|
64
|
+
'formaction',
|
|
65
|
+
'data',
|
|
66
|
+
'poster',
|
|
67
|
+
'background',
|
|
68
|
+
'cite',
|
|
69
|
+
'longdesc',
|
|
70
|
+
'srcdoc',
|
|
71
|
+
'xlink:href',
|
|
72
|
+
'ping',
|
|
73
|
+
]);
|
|
74
|
+
/** True for `onclick`, `onmouseover` and every other inline event handler. */
|
|
75
|
+
export function isEventHandlerAttribute(name) {
|
|
76
|
+
return /^on/i.test(name);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=url.js.map
|
package/dist/url.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;GAQG;AACH,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;AAEpG,kDAAkD;AAClD,MAAM,MAAM,GAAG,wBAAwB,CAAA;AAEvC;;;;;;;;GAQG;AACH,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;AACzE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAgC;IACxD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACvD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;IAClC,IAAI,SAAS,KAAK,EAAE;QAAE,OAAO,KAAK,CAAA;IAElC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACpC,yEAAyE;IACzE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,OAAO,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAA;AAC7C,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,aAAa,CAAC,KAAgC;IAC5D,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAgB,CAAC,CAAC,CAAC,IAAI,CAAA;AACpD,CAAC;AAED,oEAAoE;AACpE,MAAM,CAAC,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC;IACzD,MAAM;IACN,KAAK;IACL,QAAQ;IACR,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,YAAY;IACZ,MAAM;IACN,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,MAAM;CACP,CAAC,CAAA;AAEF,8EAA8E;AAC9E,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC1B,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openleaf-editor/core",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "OpenLeaf editing core: ProseMirror schema, HTML I/O, and the content-preservation layer. No UI, no framework dependencies.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/PeytonNowlin/openleaf.git",
|
|
12
|
+
"directory": "packages/core"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://peytonnowlin.github.io/openleaf/",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/PeytonNowlin/openleaf/issues"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"orderedmap": "^2.1.1",
|
|
33
|
+
"prosemirror-commands": "^1.6.2",
|
|
34
|
+
"prosemirror-history": "^1.4.1",
|
|
35
|
+
"prosemirror-model": "^1.24.1",
|
|
36
|
+
"prosemirror-schema-list": "^1.5.1",
|
|
37
|
+
"prosemirror-state": "^1.4.3",
|
|
38
|
+
"prosemirror-transform": "^1.10.2"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json"
|
|
42
|
+
}
|
|
43
|
+
}
|