@openleaf-editor/core 0.1.0-beta.1 → 0.1.0-beta.3
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/README.md +115 -0
- package/dist/autolink.d.ts +12 -0
- package/dist/autolink.d.ts.map +1 -0
- package/dist/autolink.js +136 -0
- package/dist/autolink.js.map +1 -0
- package/dist/command-helpers.d.ts +8 -0
- package/dist/command-helpers.d.ts.map +1 -0
- package/dist/command-helpers.js +36 -0
- package/dist/command-helpers.js.map +1 -0
- package/dist/commands.d.ts +43 -22
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +513 -115
- package/dist/commands.js.map +1 -1
- package/dist/css.d.ts +1 -123
- package/dist/css.d.ts.map +1 -1
- package/dist/css.js +1 -214
- package/dist/css.js.map +1 -1
- package/dist/disclosure.d.ts +21 -0
- package/dist/disclosure.d.ts.map +1 -0
- package/dist/disclosure.js +144 -0
- package/dist/disclosure.js.map +1 -0
- package/dist/elements.d.ts +2 -0
- package/dist/elements.d.ts.map +1 -0
- package/dist/elements.js +2 -0
- package/dist/elements.js.map +1 -0
- package/dist/embed.d.ts +2 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +2 -0
- package/dist/embed.js.map +1 -0
- package/dist/errors.d.ts +38 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/extensions.d.ts +46 -5
- package/dist/extensions.d.ts.map +1 -1
- package/dist/extensions.js +292 -65
- package/dist/extensions.js.map +1 -1
- package/dist/formats.d.ts +44 -0
- package/dist/formats.d.ts.map +1 -0
- package/dist/formats.js +123 -0
- package/dist/formats.js.map +1 -0
- package/dist/html.d.ts +32 -2
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +185 -31
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +27 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -10
- package/dist/index.js.map +1 -1
- package/dist/insert-commands.d.ts +172 -0
- package/dist/insert-commands.d.ts.map +1 -0
- package/dist/insert-commands.js +560 -0
- package/dist/insert-commands.js.map +1 -0
- package/dist/isolating-selection.d.ts +41 -0
- package/dist/isolating-selection.d.ts.map +1 -0
- package/dist/isolating-selection.js +162 -0
- package/dist/isolating-selection.js.map +1 -0
- package/dist/keymap.d.ts.map +1 -1
- package/dist/keymap.js +6 -3
- package/dist/keymap.js.map +1 -1
- package/dist/noneditable.d.ts +14 -0
- package/dist/noneditable.d.ts.map +1 -0
- package/dist/noneditable.js +107 -0
- package/dist/noneditable.js.map +1 -0
- package/dist/plugins.d.ts +20 -2
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +27 -2
- package/dist/plugins.js.map +1 -1
- package/dist/preserve.d.ts +76 -0
- package/dist/preserve.d.ts.map +1 -1
- package/dist/preserve.js +308 -58
- package/dist/preserve.js.map +1 -1
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +356 -76
- package/dist/schema.js.map +1 -1
- package/dist/structure.d.ts +76 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +422 -0
- package/dist/structure.js.map +1 -0
- package/dist/tables.d.ts +60 -1
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +617 -35
- package/dist/tables.js.map +1 -1
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +3 -0
- package/dist/testing.js.map +1 -0
- package/dist/tokens.d.ts +36 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +116 -0
- package/dist/tokens.js.map +1 -0
- package/dist/url.d.ts +1 -25
- package/dist/url.d.ts.map +1 -1
- package/dist/url.js +1 -77
- package/dist/url.js.map +1 -1
- package/dist/visual-aids.d.ts +20 -0
- package/dist/visual-aids.d.ts.map +1 -0
- package/dist/visual-aids.js +149 -0
- package/dist/visual-aids.js.map +1 -0
- package/package.json +16 -1
package/dist/preserve.js
CHANGED
|
@@ -26,8 +26,9 @@
|
|
|
26
26
|
* element carries an attribute we cannot represent, it becomes opaque
|
|
27
27
|
* and is preserved intact.
|
|
28
28
|
*/
|
|
29
|
-
import { isFullyModelledStyle } from './css.js';
|
|
30
|
-
import {
|
|
29
|
+
import { isFullyModelledStyle, safeLang } from './css.js';
|
|
30
|
+
import { DROP_WITH_CONTENT } from './elements.js';
|
|
31
|
+
import { URL_ATTRIBUTES, isEventHandlerAttribute, isNeverCarriedAttribute, isSafeUrl, } from './url.js';
|
|
31
32
|
/**
|
|
32
33
|
* Elements that are never preserved, and whose contents are discarded with them.
|
|
33
34
|
*
|
|
@@ -45,42 +46,111 @@ import { URL_ATTRIBUTES, isEventHandlerAttribute, isSafeUrl } from './url.js';
|
|
|
45
46
|
* `ignore` rather than `skip`: the element AND its contents go. Skipping would
|
|
46
47
|
* unwrap `<script>alert(1)</script>` into the literal text "alert(1)" appearing
|
|
47
48
|
* in the document, which is a different kind of wrong.
|
|
49
|
+
*
|
|
50
|
+
* Spread from `@openleaf-editor/content-policy` rather than written out here.
|
|
51
|
+
* This list and the sanitize policy's `dropWithContent` are the same decision
|
|
52
|
+
* made in two packages, they were maintained by hand, and they drifted: `<svg>`
|
|
53
|
+
* and `<math>` were on the sanitizer's list and missing from this one, so the
|
|
54
|
+
* editor stored exactly the markup the server was configured to delete. Sharing
|
|
55
|
+
* the constant is what makes that drift impossible rather than merely unlikely.
|
|
56
|
+
*
|
|
57
|
+
* Exported so the divergence can be asserted in a test as well as prevented by
|
|
58
|
+
* construction. Not re-exported from the package index: it is an internal
|
|
59
|
+
* invariant, and the shared list itself is public in content-policy.
|
|
48
60
|
*/
|
|
49
|
-
const NEVER_PRESERVE = [
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
'noscript',
|
|
68
|
-
'template',
|
|
69
|
-
];
|
|
61
|
+
export const NEVER_PRESERVE = [...DROP_WITH_CONTENT];
|
|
62
|
+
/**
|
|
63
|
+
* Tags that must not survive *inside* a preserved subtree either.
|
|
64
|
+
*
|
|
65
|
+
* `iframe` is here and not in `NEVER_PRESERVE` because the two answer different
|
|
66
|
+
* questions. At the top level an iframe is claimed by the modelled embed node
|
|
67
|
+
* when its `src` is an allowlisted player, and ignored otherwise -- a
|
|
68
|
+
* priority-100 drop rule would outrank the embed node and delete legitimate
|
|
69
|
+
* players. Inside preserved markup there is no such question: the subtree is
|
|
70
|
+
* stored as an opaque string and re-emitted verbatim, so nothing re-checks the
|
|
71
|
+
* frame on the way out.
|
|
72
|
+
*
|
|
73
|
+
* That gap was the bypass. `<iframe src="https://evil.example/">` on its own was
|
|
74
|
+
* dropped; wrapped in a `<div class="c">` it round-tripped byte-identical, with
|
|
75
|
+
* `allow="camera; microphone; geolocation"` intact. One attribute on a wrapper
|
|
76
|
+
* defeated both the host allowlist and the permissions filter.
|
|
77
|
+
*/
|
|
78
|
+
const NEVER_INSIDE_PRESERVED = new Set([...NEVER_PRESERVE, 'iframe']);
|
|
70
79
|
/** Parse rules that drop dangerous elements before any other rule sees them. */
|
|
71
80
|
const dropRules = NEVER_PRESERVE.map((tag) => ({ tag, ignore: true, priority: 100 }));
|
|
81
|
+
/**
|
|
82
|
+
* `<source>` and `<track>`: media children the schema stores as markup rather
|
|
83
|
+
* than as nodes, so they do not count as content when deciding what to keep.
|
|
84
|
+
*
|
|
85
|
+
* Lives here because this module owns the "keep it whole or claim it" decision,
|
|
86
|
+
* and `structure.ts` asks the same question when it reads a media element.
|
|
87
|
+
*/
|
|
88
|
+
export const MEDIA_FURNITURE_TAGS = new Set(['source', 'track']);
|
|
89
|
+
/**
|
|
90
|
+
* True when a media element holds content its node cannot carry.
|
|
91
|
+
*
|
|
92
|
+
* Video and audio are atoms whose only modelled children are `<source>` and
|
|
93
|
+
* `<track>`, so fallback content -- "Download <a href=...>the video</a>", shown
|
|
94
|
+
* by a browser that cannot play the file -- has nowhere to live on the node.
|
|
95
|
+
*/
|
|
96
|
+
export function hasMediaFallback(el) {
|
|
97
|
+
for (const child of Array.from(el.childNodes)) {
|
|
98
|
+
if (child.nodeType === 1) {
|
|
99
|
+
if (!MEDIA_FURNITURE_TAGS.has(child.nodeName.toLowerCase()))
|
|
100
|
+
return true;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
// Whitespace between the <source> children is layout, not fallback: the
|
|
104
|
+
// element still models cleanly and still round-trips.
|
|
105
|
+
if (child.nodeType === 3 && (child.textContent ?? '').trim() !== '')
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Media whose fallback content the node cannot hold, kept whole.
|
|
112
|
+
*
|
|
113
|
+
* Priority 45 sits below the schema's own rules (50) and above the drop rule
|
|
114
|
+
* (40): a video with a safe `src` is still claimed as a node first, and an
|
|
115
|
+
* unsafe iframe is still dropped rather than preserved, but a `<video>` carrying
|
|
116
|
+
* a download link is preserved instead of being deleted along with it.
|
|
117
|
+
*/
|
|
118
|
+
const preserveMediaWithFallback = ['video', 'audio'].map((tag) => ({
|
|
119
|
+
tag,
|
|
120
|
+
priority: 45,
|
|
121
|
+
getAttrs(dom) {
|
|
122
|
+
if (!hasMediaFallback(dom))
|
|
123
|
+
return false;
|
|
124
|
+
return { html: scrub(dom), tag: dom.nodeName.toLowerCase() };
|
|
125
|
+
},
|
|
126
|
+
}));
|
|
127
|
+
/**
|
|
128
|
+
* Media the schema declined. Priority 40 is below the schema's default (50),
|
|
129
|
+
* so an allowlisted iframe or a video with a safe `src` is claimed first;
|
|
130
|
+
* anything left is ignored rather than preserved as an atom. Preserving an
|
|
131
|
+
* arbitrary iframe would be a nested page the author never asked to keep.
|
|
132
|
+
*/
|
|
133
|
+
const dropDeclinedMedia = ['iframe', 'video', 'audio'].map((tag) => ({
|
|
134
|
+
tag,
|
|
135
|
+
ignore: true,
|
|
136
|
+
priority: 40,
|
|
137
|
+
}));
|
|
72
138
|
/**
|
|
73
139
|
* Scrub markup before it is stored for preservation.
|
|
74
140
|
*
|
|
75
141
|
* Preserving an element verbatim means preserving its attributes verbatim, and
|
|
76
142
|
* `<div class="callout" onclick="steal()">` is not something an author needs
|
|
77
143
|
* kept. Works on a clone so the live parse tree is untouched.
|
|
144
|
+
*
|
|
145
|
+
* Exported because the table schema stores `<caption>` and `<colgroup>` the same
|
|
146
|
+
* way, for the same reason, and a second scrubber that drifted from this one
|
|
147
|
+
* would be a hole in exactly the code path that exists to close holes.
|
|
78
148
|
*/
|
|
79
|
-
function scrub(el) {
|
|
149
|
+
export function scrub(el) {
|
|
80
150
|
const clone = el.cloneNode(true);
|
|
81
151
|
const visit = (node) => {
|
|
82
152
|
for (const child of Array.from(node.children)) {
|
|
83
|
-
if (
|
|
153
|
+
if (NEVER_INSIDE_PRESERVED.has(child.nodeName.toLowerCase())) {
|
|
84
154
|
child.remove();
|
|
85
155
|
continue;
|
|
86
156
|
}
|
|
@@ -91,6 +161,13 @@ function scrub(el) {
|
|
|
91
161
|
node.removeAttribute(attr.name);
|
|
92
162
|
continue;
|
|
93
163
|
}
|
|
164
|
+
// Before the URL question, not as a case of it: `srcdoc` is a document
|
|
165
|
+
// rather than a URL, and asking a scheme checker about a document gets
|
|
166
|
+
// "no scheme, therefore relative, therefore safe".
|
|
167
|
+
if (isNeverCarriedAttribute(attr.name)) {
|
|
168
|
+
node.removeAttribute(attr.name);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
94
171
|
if (URL_ATTRIBUTES.has(attr.name.toLowerCase()) && !isSafeUrl(attr.value)) {
|
|
95
172
|
node.removeAttribute(attr.name);
|
|
96
173
|
}
|
|
@@ -137,51 +214,97 @@ export function isLosslesslyUnwrappable(el) {
|
|
|
137
214
|
return false;
|
|
138
215
|
if (el.attributes.length === 0)
|
|
139
216
|
return true;
|
|
140
|
-
|
|
141
|
-
* One exception, and it is deliberately the narrowest one that works: a
|
|
142
|
-
* `<span>` whose only attribute is a style the colour marks fully model.
|
|
143
|
-
*
|
|
144
|
-
* Without it, colour is a fidelity regression dressed as a feature. Every
|
|
145
|
-
* `<span style="color:#c00">` in an inherited archive is preserved as an
|
|
146
|
-
* opaque atom -- a grey card where a sentence used to be, its text
|
|
147
|
-
* unselectable and unspellcheckable. Declining the rule here lets the span
|
|
148
|
-
* unwrap, and ProseMirror's style rules then re-apply the colour as a mark on
|
|
149
|
-
* the text inside, which is both editable and byte-identical on the way out.
|
|
150
|
-
*
|
|
151
|
-
* `text-align` is not in the accepted set, because unwrapping the element that
|
|
152
|
-
* carries it would drop it: it is a property of the block, and the block here
|
|
153
|
-
* is somebody else. Two attributes, or one attribute the marks cannot fully
|
|
154
|
-
* express, and the element stays preserved exactly as it does today.
|
|
155
|
-
*/
|
|
156
|
-
if (el.attributes.length !== 1 || name !== 'span')
|
|
217
|
+
if (name !== 'span')
|
|
157
218
|
return false;
|
|
158
219
|
/*
|
|
159
|
-
*
|
|
220
|
+
* A `<span>` whose attributes are fully modelled as marks: colour, font, and
|
|
221
|
+
* `lang`. Without this, those runs become opaque atoms -- a grey card where a
|
|
222
|
+
* sentence used to be. Declining the catch-all lets the span unwrap so the
|
|
223
|
+
* mark rules can re-apply formatting on the text inside.
|
|
160
224
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* plainly there: the declaration is invalid, and -- the one that matters -- a
|
|
164
|
-
* Content-Security-Policy with no `unsafe-inline` in `style-src`, under which
|
|
165
|
-
* the browser leaves the attribute in the DOM and refuses to parse it.
|
|
225
|
+
* Any other attribute, or a style the marks cannot fully express, and the
|
|
226
|
+
* element stays preserved exactly as it does today.
|
|
166
227
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* is not.
|
|
228
|
+
* ProseMirror matches mark style rules through the CSSOM. Under a CSP with
|
|
229
|
+
* no `unsafe-inline` in `style-src`, the browser leaves the attribute in the
|
|
230
|
+
* DOM and refuses to parse it, so unwrapping would destroy the formatting.
|
|
231
|
+
* An empty CSSOM is therefore a reason to leave the element to the
|
|
232
|
+
* preservation layer.
|
|
173
233
|
*/
|
|
174
|
-
|
|
234
|
+
for (const attr of Array.from(el.attributes)) {
|
|
235
|
+
if (attr.name === 'lang') {
|
|
236
|
+
if (safeLang(attr.value) === null)
|
|
237
|
+
return false;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (attr.name === 'style') {
|
|
241
|
+
if (el.style?.length === 0)
|
|
242
|
+
return false;
|
|
243
|
+
if (!isFullyModelledStyle(attr.value))
|
|
244
|
+
return false;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
175
247
|
return false;
|
|
176
|
-
|
|
248
|
+
}
|
|
249
|
+
return true;
|
|
177
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Parsed preserved markup, per Document, keyed on the exact stored string.
|
|
253
|
+
*
|
|
254
|
+
* `toDOM` runs on every render of every preserved atom, and an HTML parse is the
|
|
255
|
+
* most expensive thing in it: a document that is half preserved markup spent
|
|
256
|
+
* roughly 4x as long serializing as the same document in plain paragraphs.
|
|
257
|
+
*
|
|
258
|
+
* Caching one is only sound because the key is the whole input. `html` is a node
|
|
259
|
+
* attribute, ProseMirror nodes are immutable, and the string was produced by
|
|
260
|
+
* `scrub` before it was ever stored -- so two calls with the same string must
|
|
261
|
+
* produce the same element, and there is no edit that can invalidate an entry
|
|
262
|
+
* without changing the key.
|
|
263
|
+
*
|
|
264
|
+
* Three properties this must keep, none of them optional:
|
|
265
|
+
*
|
|
266
|
+
* A CLONE every time. Handing out the cached node would let one caller's
|
|
267
|
+
* mutation -- ProseMirror appending it, a decoration setting an attribute --
|
|
268
|
+
* poison every later render of that markup.
|
|
269
|
+
*
|
|
270
|
+
* PER DOCUMENT. Serialization can target a Document that is not the global
|
|
271
|
+
* one, and a node has to be owned by the document it is going into. Scoping by
|
|
272
|
+
* Document also means the entries die with it, so a throwaway serialization
|
|
273
|
+
* document is still collectable.
|
|
274
|
+
*
|
|
275
|
+
* BOUNDED. The keys are document content, so an unbounded map is a leak that
|
|
276
|
+
* grows with what the user pastes. A cap with clear-on-overflow keeps it flat;
|
|
277
|
+
* very large strings are not cached at all, because they are both the least
|
|
278
|
+
* likely to repeat and the most expensive to hold.
|
|
279
|
+
*/
|
|
280
|
+
const PARSE_CACHE_ENTRIES = 256;
|
|
281
|
+
const PARSE_CACHE_MAX_LENGTH = 4096;
|
|
282
|
+
const parseCaches = new WeakMap();
|
|
178
283
|
/** Rebuild a DOM element from stored markup. `<template>` is used because
|
|
179
284
|
* its parsing context permits otherwise-illegal fragments such as a bare
|
|
180
285
|
* `<tr>`, which a `<div>` container would silently discard. */
|
|
181
286
|
function elementFromHtml(html, doc) {
|
|
287
|
+
let cache = parseCaches.get(doc);
|
|
288
|
+
const hit = cache?.get(html);
|
|
289
|
+
if (hit)
|
|
290
|
+
return hit.cloneNode(true);
|
|
182
291
|
const tpl = doc.createElement('template');
|
|
183
292
|
tpl.innerHTML = html;
|
|
184
|
-
|
|
293
|
+
const parsed = tpl.content.firstElementChild;
|
|
294
|
+
if (!parsed)
|
|
295
|
+
return null;
|
|
296
|
+
if (html.length > PARSE_CACHE_MAX_LENGTH)
|
|
297
|
+
return parsed;
|
|
298
|
+
if (!cache) {
|
|
299
|
+
cache = new Map();
|
|
300
|
+
parseCaches.set(doc, cache);
|
|
301
|
+
}
|
|
302
|
+
if (cache.size >= PARSE_CACHE_ENTRIES)
|
|
303
|
+
cache.clear();
|
|
304
|
+
// The master copy is the clone, so the element handed back is the freshly
|
|
305
|
+
// parsed one and the cache holds something no caller has ever touched.
|
|
306
|
+
cache.set(html, parsed.cloneNode(true));
|
|
307
|
+
return parsed;
|
|
185
308
|
}
|
|
186
309
|
/**
|
|
187
310
|
* ProseMirror's `toDOM` does not receive the `document` passed to
|
|
@@ -281,6 +404,8 @@ export const unknownBlock = {
|
|
|
281
404
|
},
|
|
282
405
|
parseDOM: [
|
|
283
406
|
...dropRules,
|
|
407
|
+
...preserveMediaWithFallback,
|
|
408
|
+
...dropDeclinedMedia,
|
|
284
409
|
{
|
|
285
410
|
tag: '*',
|
|
286
411
|
// Lowest priority: every real rule in the schema gets first refusal.
|
|
@@ -300,6 +425,114 @@ export const unknownBlock = {
|
|
|
300
425
|
return rebuildOrCarry(node.attrs['html'], 'div');
|
|
301
426
|
},
|
|
302
427
|
};
|
|
428
|
+
/**
|
|
429
|
+
* Where the inline catch-all is allowed to fire.
|
|
430
|
+
*
|
|
431
|
+
* ProseMirror matches `context` against the parent node the content is being
|
|
432
|
+
* parsed INTO, and the naive list -- paragraph and heading -- described the
|
|
433
|
+
* wrong thing. It named the nodes that hold inline content, when what the rule
|
|
434
|
+
* needs to name is every node that can END UP holding it.
|
|
435
|
+
*
|
|
436
|
+
* `list_item` is `paragraph block*`, so its implicit paragraph does not exist
|
|
437
|
+
* until some inline content arrives to force it. An unknown element that is the
|
|
438
|
+
* FIRST child of an `<li>` therefore sees a context of `.../list_item/`, the
|
|
439
|
+
* inline rule declines, and `unknownBlock` claims a block-level atom inside a
|
|
440
|
+
* container whose content expression cannot hold one. ProseMirror's recovery is
|
|
441
|
+
* to close the list and emit the atom as a sibling, so
|
|
442
|
+
* `<ol><li><ins>a</ins></li><li>b</li></ol>` came back as an emptied `<ol>`, an
|
|
443
|
+
* escaped `<ins>`, and a `<ul>` -- the list's contents gone and its very type
|
|
444
|
+
* changed. The identical element with a single character of text before it
|
|
445
|
+
* round-tripped perfectly, which is what kept this invisible: position was the
|
|
446
|
+
* whole trigger.
|
|
447
|
+
*
|
|
448
|
+
* `summary` and `figcaption` fail the same way for the same reason, and
|
|
449
|
+
* `blockquote`, `table_cell` and `table_header` are listed so that a first-child
|
|
450
|
+
* unknown *inline* element is wrapped in a paragraph exactly as it is when text
|
|
451
|
+
* precedes it. Those three can also hold a block atom legally. Wrapping a
|
|
452
|
+
* p-closing element (`div`, `section`, …) in that paragraph is the other
|
|
453
|
+
* failure: the HTML parser closes the `<p>` on the next parse, leaves empty
|
|
454
|
+
* paragraphs on both sides, and the document grows by two blanks on every save.
|
|
455
|
+
* `unknownInline` declines those tags so `unknownBlock` claims them instead.
|
|
456
|
+
*
|
|
457
|
+
* Naming containers explicitly rather than dropping the context altogether:
|
|
458
|
+
* without it the inline rule would outrank `unknownBlock` everywhere, and a
|
|
459
|
+
* genuinely block-level unknown element at the top of the document would become
|
|
460
|
+
* an inline atom inside a paragraph the author never had.
|
|
461
|
+
*/
|
|
462
|
+
const INLINE_CONTEXT = [
|
|
463
|
+
'paragraph/',
|
|
464
|
+
'heading/',
|
|
465
|
+
'summary/',
|
|
466
|
+
'figcaption/',
|
|
467
|
+
'list_item/',
|
|
468
|
+
'table_cell/',
|
|
469
|
+
'table_header/',
|
|
470
|
+
'blockquote/',
|
|
471
|
+
].join('|');
|
|
472
|
+
/**
|
|
473
|
+
* Start tags that close an open `<p>` in the HTML "in body" insertion mode.
|
|
474
|
+
*
|
|
475
|
+
* Serializing one of these as an inline atom wraps it in `<p>…</p>`. The next
|
|
476
|
+
* parse cannot keep it there, so it splits into an empty paragraph, the
|
|
477
|
+
* element, and another empty paragraph -- and the following save wraps it
|
|
478
|
+
* again. The same unbounded growth `<plaintext>` caused by having no end tag.
|
|
479
|
+
*
|
|
480
|
+
* Shared with anything else that must not claim a p-closing element as inline
|
|
481
|
+
* (`figcaption` being inline is the other door into this). Custom elements are
|
|
482
|
+
* not on the list: they do not close a `<p>`, so `<drupal-media>` inside a
|
|
483
|
+
* blockquote is correctly an inline atom.
|
|
484
|
+
*
|
|
485
|
+
* Source: WHATWG HTML parsing, "in body" -- every start tag that runs
|
|
486
|
+
* "if the stack of open elements has a p element in button scope, then close
|
|
487
|
+
* a p element" before inserting.
|
|
488
|
+
*/
|
|
489
|
+
export const CLOSES_OPEN_P = new Set([
|
|
490
|
+
'address',
|
|
491
|
+
'article',
|
|
492
|
+
'aside',
|
|
493
|
+
'blockquote',
|
|
494
|
+
'center',
|
|
495
|
+
'details',
|
|
496
|
+
'dialog',
|
|
497
|
+
'dir',
|
|
498
|
+
'div',
|
|
499
|
+
'dl',
|
|
500
|
+
'dt',
|
|
501
|
+
'dd',
|
|
502
|
+
'fieldset',
|
|
503
|
+
'figcaption',
|
|
504
|
+
'figure',
|
|
505
|
+
'footer',
|
|
506
|
+
'header',
|
|
507
|
+
'hgroup',
|
|
508
|
+
'main',
|
|
509
|
+
'menu',
|
|
510
|
+
'nav',
|
|
511
|
+
'ol',
|
|
512
|
+
'p',
|
|
513
|
+
'search',
|
|
514
|
+
'section',
|
|
515
|
+
'summary',
|
|
516
|
+
'ul',
|
|
517
|
+
'h1',
|
|
518
|
+
'h2',
|
|
519
|
+
'h3',
|
|
520
|
+
'h4',
|
|
521
|
+
'h5',
|
|
522
|
+
'h6',
|
|
523
|
+
'listing',
|
|
524
|
+
'plaintext',
|
|
525
|
+
'pre',
|
|
526
|
+
'form',
|
|
527
|
+
'li',
|
|
528
|
+
'button',
|
|
529
|
+
'table',
|
|
530
|
+
'hr',
|
|
531
|
+
'xmp',
|
|
532
|
+
'applet',
|
|
533
|
+
'marquee',
|
|
534
|
+
'object',
|
|
535
|
+
]);
|
|
303
536
|
/**
|
|
304
537
|
* Inline preserved content, for unrecognised markup appearing inside a
|
|
305
538
|
* paragraph -- the `<o:p>` and `<w:sdt>` debris of a Word paste, custom
|
|
@@ -316,17 +549,34 @@ export const unknownInline = {
|
|
|
316
549
|
},
|
|
317
550
|
parseDOM: [
|
|
318
551
|
...dropRules,
|
|
552
|
+
...preserveMediaWithFallback,
|
|
553
|
+
...dropDeclinedMedia,
|
|
319
554
|
{
|
|
320
555
|
tag: '*',
|
|
321
556
|
// Higher than unknownBlock's catch-all: inline gets first refusal so
|
|
322
557
|
// the block rule cannot claim inline debris and split the paragraph
|
|
323
558
|
// that contained it.
|
|
324
559
|
priority: 1,
|
|
325
|
-
context:
|
|
560
|
+
context: INLINE_CONTEXT,
|
|
326
561
|
getAttrs(dom) {
|
|
327
562
|
const el = dom;
|
|
328
563
|
if (isLosslesslyUnwrappable(el))
|
|
329
564
|
return false;
|
|
565
|
+
// A block-level element inside a paragraph-holding container must not
|
|
566
|
+
// become an inline atom: emitting it inside `<p>` is markup the HTML
|
|
567
|
+
// parser will not re-parse as itself. Declining lets unknownBlock
|
|
568
|
+
// claim it, which blockquote (`block+`) and list_item (`paragraph
|
|
569
|
+
// block*`) can hold. `<ins>` and custom elements stay on this path.
|
|
570
|
+
// `figcaption` is on CLOSES_OPEN_P, but declining it in summary/heading
|
|
571
|
+
// sends it to unknownBlock, which those containers cannot hold.
|
|
572
|
+
const tag = el.nodeName.toLowerCase();
|
|
573
|
+
if (tag === 'figcaption') {
|
|
574
|
+
if (el.parentElement?.nodeName.toLowerCase() === 'p')
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
else if (CLOSES_OPEN_P.has(tag)) {
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
330
580
|
return { html: scrub(el), tag: el.nodeName.toLowerCase() };
|
|
331
581
|
},
|
|
332
582
|
},
|
package/dist/preserve.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preserve.js","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"preserve.js","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,uBAAuB,EACvB,SAAS,GACV,MAAM,UAAU,CAAA;AAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,cAAc,GAAsB,CAAC,GAAG,iBAAiB,CAAC,CAAA;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC,CAAC,GAAG,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAA;AAE1F,gFAAgF;AAChF,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;AAErF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAwB,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;AAErF;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAW;IAC1C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9C,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAA;YACxE,SAAQ;QACV,CAAC;QACD,wEAAwE;QACxE,sDAAsD;QACtD,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,IAAI,CAAA;IAClF,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,yBAAyB,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACjE,GAAG;IACH,QAAQ,EAAE,EAAE;IACZ,QAAQ,CAAC,GAAgB;QACvB,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAA;QACxC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAA;IAC9D,CAAC;CACF,CAAC,CAAC,CAAA;AAEH;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnE,GAAG;IACH,MAAM,EAAE,IAAa;IACrB,QAAQ,EAAE,EAAE;CACb,CAAC,CAAC,CAAA;AAEH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CAAC,EAAW;IAC/B,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAY,CAAA;IAE3C,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;QACpC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,IAAI,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBAC7D,KAAK,CAAC,MAAM,EAAE,CAAA;gBACd,SAAQ;YACV,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,CAAA;QACd,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC/C,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC/B,SAAQ;YACV,CAAC;YACD,uEAAuE;YACvE,uEAAuE;YACvE,mDAAmD;YACnD,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC/B,SAAQ;YACV,CAAC;YACD,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1E,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACjC,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,KAAK,CAAC,KAAK,CAAC,CAAA;IACZ,OAAO,KAAK,CAAC,SAAS,CAAA;AACxB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IAC1D,KAAK;IACL,SAAS;IACT,SAAS;IACT,MAAM;IACN,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,KAAK;IACL,MAAM;IACN,QAAQ;CACT,CAAC,CAAA;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,EAAW;IACjD,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAA;IACtC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IACnD,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAC3C,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,KAAK,CAAA;IAEjC;;;;;;;;;;;;;;OAcG;IACH,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAA;YAC/C,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,IAAK,EAAkB,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;YACzD,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;YACnD,SAAQ;QACV,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAC/B,MAAM,sBAAsB,GAAG,IAAI,CAAA;AACnC,MAAM,WAAW,GAAG,IAAI,OAAO,EAAkC,CAAA;AAEjE;;gEAEgE;AAChE,SAAS,eAAe,CAAC,IAAY,EAAE,GAAa;IAClD,IAAI,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAChC,MAAM,GAAG,GAAG,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;IAC5B,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC,SAAS,CAAC,IAAI,CAAY,CAAA;IAE9C,MAAM,GAAG,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;IACzC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAA;IACpB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAA;IAC5C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACxB,IAAI,IAAI,CAAC,MAAM,GAAG,sBAAsB;QAAE,OAAO,MAAM,CAAA;IAEvD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,IAAI,GAAG,EAAE,CAAA;QACjB,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,IAAI,mBAAmB;QAAE,KAAK,CAAC,KAAK,EAAE,CAAA;IACpD,0EAA0E;IAC1E,uEAAuE;IACvE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,IAAI,CAAY,CAAC,CAAA;IAClD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;GAMG;AACH,IAAI,qBAA2C,CAAA;AAE/C,0EAA0E;AAC1E,MAAM,UAAU,yBAAyB,CAAI,GAAa,EAAE,EAAW;IACrE,MAAM,QAAQ,GAAG,qBAAqB,CAAA;IACtC,qBAAqB,GAAG,GAAG,CAAA;IAC3B,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAA;IACb,CAAC;YAAS,CAAC;QACT,qBAAqB,GAAG,QAAQ,CAAA;IAClC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,aAAa,EAAE,CAAA;AACxB,CAAC;AAED,SAAS,aAAa;IACpB,IAAI,qBAAqB;QAAE,OAAO,qBAAqB,CAAA;IACvD,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACb,2EAA2E;YACzE,+DAA+D;YAC/D,sCAAsC,CACzC,CAAA;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAGD;;;;;;;;GAQG;AACH;;;;;;;;;;;;;GAaG;AACH,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAAW,CAAA;AAEhD,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,IAAoB;IACpD,KAAK,IAAI,OAAO,GAAG,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;QAClE,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAA;IACjD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,WAA2B;IAC/D,MAAM,GAAG,GAAG,aAAa,EAAE,CAAA;IAC3B,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC1C,IAAI,OAAO,EAAE,CAAC;QACZ,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QAC9B,OAAO,OAAO,CAAA;IAChB,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,WAAW,CAAC,CAAA;IAC9C,OAAO,CAAC,YAAY,CAAC,0BAA0B,EAAE,IAAI,CAAC,CAAA;IACtD,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9B,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAa;IACpC,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,IAAI;IACV,UAAU,EAAE,IAAI;IAChB,SAAS,EAAE,IAAI;IACf,KAAK,EAAE;QACL,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACrB,GAAG,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;KACxB;IACD,QAAQ,EAAE;QACR,GAAG,SAAS;QACZ,GAAG,yBAAyB;QAC5B,GAAG,iBAAiB;QACpB;YACE,GAAG,EAAE,GAAG;YACR,qEAAqE;YACrE,wDAAwD;YACxD,QAAQ,EAAE,CAAC;YACX,QAAQ,CAAC,GAAG;gBACV,MAAM,EAAE,GAAG,GAAc,CAAA;gBACzB,kEAAkE;gBAClE,kEAAkE;gBAClE,IAAI,uBAAuB,CAAC,EAAE,CAAC;oBAAE,OAAO,KAAK,CAAA;gBAC7C,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAA;YAC5D,CAAC;SACF;KACF;IACD,KAAK,CAAC,IAAI;QACR,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAW,EAAE,KAAK,CAAC,CAAA;IAC5D,CAAC;CACF,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,cAAc,GAAG;IACrB,YAAY;IACZ,UAAU;IACV,UAAU;IACV,aAAa;IACb,YAAY;IACZ,aAAa;IACb,eAAe;IACf,aAAa;CACd,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAEX;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC;IACxD,SAAS;IACT,SAAS;IACT,OAAO;IACP,YAAY;IACZ,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,KAAK;IACL,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,UAAU;IACV,YAAY;IACZ,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,MAAM;IACN,KAAK;IACL,IAAI;IACJ,GAAG;IACH,QAAQ;IACR,SAAS;IACT,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,SAAS;IACT,WAAW;IACX,KAAK;IACL,MAAM;IACN,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,IAAI;IACJ,KAAK;IACL,QAAQ;IACR,SAAS;IACT,QAAQ;CACT,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAa;IACrC,KAAK,EAAE,QAAQ;IACf,MAAM,EAAE,IAAI;IACZ,IAAI,EAAE,IAAI;IACV,UAAU,EAAE,IAAI;IAChB,KAAK,EAAE;QACL,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACrB,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;KACzB;IACD,QAAQ,EAAE;QACR,GAAG,SAAS;QACZ,GAAG,yBAAyB;QAC5B,GAAG,iBAAiB;QACpB;YACE,GAAG,EAAE,GAAG;YACR,qEAAqE;YACrE,oEAAoE;YACpE,qBAAqB;YACrB,QAAQ,EAAE,CAAC;YACX,OAAO,EAAE,cAAc;YACvB,QAAQ,CAAC,GAAG;gBACV,MAAM,EAAE,GAAG,GAAc,CAAA;gBACzB,IAAI,uBAAuB,CAAC,EAAE,CAAC;oBAAE,OAAO,KAAK,CAAA;gBAC7C,sEAAsE;gBACtE,qEAAqE;gBACrE,kEAAkE;gBAClE,kEAAkE;gBAClE,oEAAoE;gBACpE,wEAAwE;gBACxE,gEAAgE;gBAChE,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAA;gBACrC,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;oBACzB,IAAI,EAAE,CAAC,aAAa,EAAE,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG;wBAAE,OAAO,KAAK,CAAA;gBACpE,CAAC;qBAAM,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAClC,OAAO,KAAK,CAAA;gBACd,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAA;YAC5D,CAAC;SACF;KACF;IACD,KAAK,CAAC,IAAI;QACR,OAAO,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAW,EAAE,MAAM,CAAC,CAAA;IAC7D,CAAC;CACF,CAAA"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
* there rather than at the package boundary.
|
|
9
9
|
*/
|
|
10
10
|
import { Schema, type MarkSpec, type NodeSpec } from 'prosemirror-model';
|
|
11
|
+
/**
|
|
12
|
+
* The `start` of an ordered list, or null when the attribute is not a number.
|
|
13
|
+
*
|
|
14
|
+
* `Number('abc')` is `NaN`, and `NaN` is a perfectly good value to store on a
|
|
15
|
+
* node -- so `<ol start="abc">` was stored as `NaN` and serialized as the string
|
|
16
|
+
* `start="NaN"`, turning a value the browser ignores into one it still ignores
|
|
17
|
+
* but that is now in the customer's database with our fingerprints on it.
|
|
18
|
+
* Returning null instead lets extensions.ts carry the original spelling as
|
|
19
|
+
* residue, so the attribute survives without being interpreted.
|
|
20
|
+
*
|
|
21
|
+
* Exported so that the carry mechanism asks the same question this does. Two
|
|
22
|
+
* copies of "is this a valid start" is how they would disagree and emit the
|
|
23
|
+
* attribute twice, or neither time.
|
|
24
|
+
*/
|
|
25
|
+
export declare function listStart(value: string | null | undefined): number | null;
|
|
11
26
|
/** The base node specs. Extensions are appended to these. */
|
|
12
27
|
export declare const coreNodes: Record<string, NodeSpec>;
|
|
13
28
|
/** The base mark specs. */
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAkC,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAkC,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAqHxG;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAIzE;AA8BD,6DAA6D;AAC7D,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA4L9C,CAAA;AAgDD,2BAA2B;AAC3B,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAwQ9C,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,wBAAqD,CAAA"}
|