@openleaf-editor/core 0.1.0-beta.2 → 0.1.0-beta.4

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.
Files changed (104) hide show
  1. package/README.md +127 -0
  2. package/dist/autolink.d.ts +7 -4
  3. package/dist/autolink.d.ts.map +1 -1
  4. package/dist/autolink.js +90 -11
  5. package/dist/autolink.js.map +1 -1
  6. package/dist/command-helpers.d.ts +8 -0
  7. package/dist/command-helpers.d.ts.map +1 -0
  8. package/dist/command-helpers.js +36 -0
  9. package/dist/command-helpers.js.map +1 -0
  10. package/dist/commands.d.ts +21 -63
  11. package/dist/commands.d.ts.map +1 -1
  12. package/dist/commands.js +430 -346
  13. package/dist/commands.js.map +1 -1
  14. package/dist/css.d.ts +1 -175
  15. package/dist/css.d.ts.map +1 -1
  16. package/dist/css.js +1 -516
  17. package/dist/css.js.map +1 -1
  18. package/dist/disclosure.d.ts +21 -0
  19. package/dist/disclosure.d.ts.map +1 -0
  20. package/dist/disclosure.js +144 -0
  21. package/dist/disclosure.js.map +1 -0
  22. package/dist/elements.d.ts +2 -0
  23. package/dist/elements.d.ts.map +1 -0
  24. package/dist/elements.js +2 -0
  25. package/dist/elements.js.map +1 -0
  26. package/dist/embed.d.ts +1 -51
  27. package/dist/embed.d.ts.map +1 -1
  28. package/dist/embed.js +1 -115
  29. package/dist/embed.js.map +1 -1
  30. package/dist/errors.d.ts +38 -0
  31. package/dist/errors.d.ts.map +1 -0
  32. package/dist/errors.js +40 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/extensions.d.ts +46 -5
  35. package/dist/extensions.d.ts.map +1 -1
  36. package/dist/extensions.js +272 -52
  37. package/dist/extensions.js.map +1 -1
  38. package/dist/figure-drag.d.ts +26 -0
  39. package/dist/figure-drag.d.ts.map +1 -0
  40. package/dist/figure-drag.js +75 -0
  41. package/dist/figure-drag.js.map +1 -0
  42. package/dist/formats.d.ts.map +1 -1
  43. package/dist/formats.js +19 -7
  44. package/dist/formats.js.map +1 -1
  45. package/dist/gapcursor.d.ts +14 -0
  46. package/dist/gapcursor.d.ts.map +1 -0
  47. package/dist/gapcursor.js +17 -0
  48. package/dist/gapcursor.js.map +1 -0
  49. package/dist/html.d.ts +32 -2
  50. package/dist/html.d.ts.map +1 -1
  51. package/dist/html.js +185 -31
  52. package/dist/html.js.map +1 -1
  53. package/dist/index.d.ts +23 -6
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +34 -6
  56. package/dist/index.js.map +1 -1
  57. package/dist/insert-commands.d.ts +172 -0
  58. package/dist/insert-commands.d.ts.map +1 -0
  59. package/dist/insert-commands.js +560 -0
  60. package/dist/insert-commands.js.map +1 -0
  61. package/dist/isolating-selection.d.ts +59 -0
  62. package/dist/isolating-selection.d.ts.map +1 -0
  63. package/dist/isolating-selection.js +251 -0
  64. package/dist/isolating-selection.js.map +1 -0
  65. package/dist/noneditable.d.ts.map +1 -1
  66. package/dist/noneditable.js +18 -4
  67. package/dist/noneditable.js.map +1 -1
  68. package/dist/plugins.d.ts +20 -2
  69. package/dist/plugins.d.ts.map +1 -1
  70. package/dist/plugins.js +27 -2
  71. package/dist/plugins.js.map +1 -1
  72. package/dist/preserve.d.ts +48 -17
  73. package/dist/preserve.d.ts.map +1 -1
  74. package/dist/preserve.js +214 -43
  75. package/dist/preserve.js.map +1 -1
  76. package/dist/schema.d.ts +15 -0
  77. package/dist/schema.d.ts.map +1 -1
  78. package/dist/schema.js +157 -28
  79. package/dist/schema.js.map +1 -1
  80. package/dist/structure.d.ts +32 -0
  81. package/dist/structure.d.ts.map +1 -1
  82. package/dist/structure.js +53 -10
  83. package/dist/structure.js.map +1 -1
  84. package/dist/tables.d.ts +47 -1
  85. package/dist/tables.d.ts.map +1 -1
  86. package/dist/tables.js +417 -40
  87. package/dist/tables.js.map +1 -1
  88. package/dist/testing.d.ts +3 -0
  89. package/dist/testing.d.ts.map +1 -0
  90. package/dist/testing.js +3 -0
  91. package/dist/testing.js.map +1 -0
  92. package/dist/tokens.d.ts +8 -0
  93. package/dist/tokens.d.ts.map +1 -1
  94. package/dist/tokens.js +54 -6
  95. package/dist/tokens.js.map +1 -1
  96. package/dist/url.d.ts +1 -25
  97. package/dist/url.d.ts.map +1 -1
  98. package/dist/url.js +1 -77
  99. package/dist/url.js.map +1 -1
  100. package/dist/visual-aids.d.ts +11 -1
  101. package/dist/visual-aids.d.ts.map +1 -1
  102. package/dist/visual-aids.js +124 -36
  103. package/dist/visual-aids.js.map +1 -1
  104. package/package.json +17 -1
package/dist/preserve.js CHANGED
@@ -27,7 +27,8 @@
27
27
  * and is preserved intact.
28
28
  */
29
29
  import { isFullyModelledStyle, safeLang } from './css.js';
30
- import { URL_ATTRIBUTES, isEventHandlerAttribute, isSafeUrl } from './url.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,27 +46,36 @@ 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
- 'script',
51
- 'style',
52
- 'frame',
53
- 'frameset',
54
- 'object',
55
- 'embed',
56
- 'applet',
57
- 'form',
58
- 'input',
59
- 'button',
60
- 'select',
61
- 'textarea',
62
- 'option',
63
- 'link',
64
- 'meta',
65
- 'base',
66
- 'noscript',
67
- 'template',
68
- ];
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']);
69
79
  /** Parse rules that drop dangerous elements before any other rule sees them. */
70
80
  const dropRules = NEVER_PRESERVE.map((tag) => ({ tag, ignore: true, priority: 100 }));
71
81
  /**
@@ -140,7 +150,7 @@ export function scrub(el) {
140
150
  const clone = el.cloneNode(true);
141
151
  const visit = (node) => {
142
152
  for (const child of Array.from(node.children)) {
143
- if (NEVER_PRESERVE.includes(child.nodeName.toLowerCase())) {
153
+ if (NEVER_INSIDE_PRESERVED.has(child.nodeName.toLowerCase())) {
144
154
  child.remove();
145
155
  continue;
146
156
  }
@@ -151,6 +161,13 @@ export function scrub(el) {
151
161
  node.removeAttribute(attr.name);
152
162
  continue;
153
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
+ }
154
171
  if (URL_ATTRIBUTES.has(attr.name.toLowerCase()) && !isSafeUrl(attr.value)) {
155
172
  node.removeAttribute(attr.name);
156
173
  }
@@ -231,13 +248,63 @@ export function isLosslesslyUnwrappable(el) {
231
248
  }
232
249
  return true;
233
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();
234
283
  /** Rebuild a DOM element from stored markup. `<template>` is used because
235
284
  * its parsing context permits otherwise-illegal fragments such as a bare
236
285
  * `<tr>`, which a `<div>` container would silently discard. */
237
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);
238
291
  const tpl = doc.createElement('template');
239
292
  tpl.innerHTML = html;
240
- return tpl.content.firstElementChild;
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;
241
308
  }
242
309
  /**
243
310
  * ProseMirror's `toDOM` does not receive the `document` passed to
@@ -258,25 +325,6 @@ export function withSerializationDocument(doc, fn) {
258
325
  serializationDocument = previous;
259
326
  }
260
327
  }
261
- /**
262
- * True while `serializeHtml` is running.
263
- *
264
- * A node whose `toDOM` needs to render differently for the editor than for the
265
- * saved HTML has no other way to tell which one it is building. The table spec
266
- * needs exactly that: a preserved `<caption>` must be `contenteditable="false"`
267
- * on screen, because it sits inside the editable area but outside the node's
268
- * `contentDOM`, and letting a caret into it means typing that ProseMirror will
269
- * silently revert. That attribute must NOT reach the saved HTML, where it would
270
- * be our editor scribbling on the author's markup.
271
- *
272
- * Stripping it afterwards was the other option and is worse: it cannot tell the
273
- * attribute it just added from the same attribute in somebody's document -- the
274
- * collision the preserved-element WeakSet above exists to avoid. Not emitting it
275
- * at all has no such failure mode.
276
- */
277
- export function isSerializing() {
278
- return serializationDocument !== undefined;
279
- }
280
328
  /**
281
329
  * The Document that serialization should build into.
282
330
  *
@@ -377,6 +425,114 @@ export const unknownBlock = {
377
425
  return rebuildOrCarry(node.attrs['html'], 'div');
378
426
  },
379
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
+ ]);
380
536
  /**
381
537
  * Inline preserved content, for unrecognised markup appearing inside a
382
538
  * paragraph -- the `<o:p>` and `<w:sdt>` debris of a Word paste, custom
@@ -401,11 +557,26 @@ export const unknownInline = {
401
557
  // the block rule cannot claim inline debris and split the paragraph
402
558
  // that contained it.
403
559
  priority: 1,
404
- context: 'paragraph/|heading/',
560
+ context: INLINE_CONTEXT,
405
561
  getAttrs(dom) {
406
562
  const el = dom;
407
563
  if (isLosslesslyUnwrappable(el))
408
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
+ }
409
580
  return { html: scrub(el), tag: el.nodeName.toLowerCase() };
410
581
  },
411
582
  },
@@ -1 +1 @@
1
- {"version":3,"file":"preserve.js","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"preserve.js","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"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"}
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. */
@@ -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;AAiJxG,6DAA6D;AAC7D,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA4L9C,CAAA;AAED,2BAA2B;AAC3B,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAsN9C,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,wBAAqD,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"}
package/dist/schema.js CHANGED
@@ -8,9 +8,9 @@
8
8
  * there rather than at the package boundary.
9
9
  */
10
10
  import { Schema } from 'prosemirror-model';
11
- import { applyStyleAttribute, indentCss, indentLevels, isFullyModelledStyle, parseDeclarations, safeAlign, safeColor, safeDir, safeFontFamily, safeFontSize, safeLang, safeLineHeight, safeListStyle, serializeDeclarations } from './css.js';
12
- import { serializationTarget, unknownBlock, unknownInline } from './preserve.js';
13
- import { audio, details, figcaption, figure, iframe, imageDomAttrs, imageParseAttrs, named_anchor, page_break, summary, video, } from './structure.js';
11
+ import { INLINE_STYLE_PROPERTIES, applyStyleAttribute, indentCss, indentLevels, isFullyModelledStyle, modelledValue, parseDeclarations, safeAlign, safeColor, safeDir, safeFontFamily, safeFontSize, safeLang, safeLineHeight, safeListStyle, serializeDeclarations } from './css.js';
12
+ import { isLosslesslyUnwrappable, serializationTarget, unknownBlock, unknownInline } from './preserve.js';
13
+ import { audio, details, figcaption, figure, iframe, imageDomAttrs, imageParseAttrs, isEmptyNamedAnchorElement, named_anchor, page_break, summary, video, } from './structure.js';
14
14
  import { table, table_cell, table_header, table_row } from './tables.js';
15
15
  import { safeId } from './tokens.js';
16
16
  import { isSafeUrl } from './url.js';
@@ -108,6 +108,26 @@ function textBlockToDOM(tag, attrs) {
108
108
  return elementWithStyle(tag, domAttrs);
109
109
  return Object.keys(domAttrs).length > 0 ? [tag, domAttrs, 0] : [tag, 0];
110
110
  }
111
+ /**
112
+ * The `start` of an ordered list, or null when the attribute is not a number.
113
+ *
114
+ * `Number('abc')` is `NaN`, and `NaN` is a perfectly good value to store on a
115
+ * node -- so `<ol start="abc">` was stored as `NaN` and serialized as the string
116
+ * `start="NaN"`, turning a value the browser ignores into one it still ignores
117
+ * but that is now in the customer's database with our fingerprints on it.
118
+ * Returning null instead lets extensions.ts carry the original spelling as
119
+ * residue, so the attribute survives without being interpreted.
120
+ *
121
+ * Exported so that the carry mechanism asks the same question this does. Two
122
+ * copies of "is this a valid start" is how they would disagree and emit the
123
+ * attribute twice, or neither time.
124
+ */
125
+ export function listStart(value) {
126
+ if (value === null || value === undefined)
127
+ return null;
128
+ const parsed = Number.parseInt(value.trim(), 10);
129
+ return Number.isFinite(parsed) ? parsed : null;
130
+ }
111
131
  function listAttrs(el, ordered) {
112
132
  const declarations = parseDeclarations(el.getAttribute('style'));
113
133
  const fromStyle = safeListStyle(declarations.get('list-style-type') ?? null);
@@ -115,8 +135,7 @@ function listAttrs(el, ordered) {
115
135
  const listStyle = fromStyle ?? fromType;
116
136
  if (!ordered)
117
137
  return { listStyle };
118
- const start = el.getAttribute('start');
119
- return { start: start ? Number(start) : 1, listStyle };
138
+ return { start: listStart(el.getAttribute('start')) ?? 1, listStyle };
120
139
  }
121
140
  function listToDOM(tag, attrs) {
122
141
  const out = {};
@@ -316,6 +335,55 @@ export const coreNodes = {
316
335
  unknown_block: unknownBlock,
317
336
  unknown_inline: unknownInline,
318
337
  };
338
+ /**
339
+ * True when a `<span>` carries nothing but formatting the mark set can express.
340
+ *
341
+ * The question a TAG rule on a mark has to ask, and the reason it is not
342
+ * `isFullyModelledStyle` alone: `background` is the shorthand Word writes, and
343
+ * the rule below reads it through `el.style.backgroundColor`, so the CSSOM has
344
+ * already expanded it and it is genuinely modelled even though the declaration
345
+ * name is not in the modelled list. Refusing it would turn every highlighted
346
+ * Word paste into an opaque atom.
347
+ *
348
+ * Any other attribute, or any declaration a mark cannot hold, and the answer is
349
+ * no -- because a mark that claims the element has nowhere to keep the rest.
350
+ */
351
+ function isSpanOnlyStyling(el) {
352
+ for (const attr of Array.from(el.attributes)) {
353
+ if (attr.name !== 'style')
354
+ return false;
355
+ }
356
+ const declarations = parseDeclarations(el.getAttribute('style'));
357
+ if (declarations.size === 0)
358
+ return false;
359
+ for (const [name, value] of declarations) {
360
+ if (name === 'background')
361
+ continue;
362
+ if (!INLINE_STYLE_PROPERTIES.includes(name))
363
+ return false;
364
+ if (modelledValue(name, value) === null)
365
+ return false;
366
+ }
367
+ return true;
368
+ }
369
+ /**
370
+ * True when a colour/font mark must not fire on this element.
371
+ *
372
+ * Those marks used to be `style:` rules. A style rule does not consume the
373
+ * element, which is why `<p style="color:red">` and a `<div>` wrapper still
374
+ * yield a mark -- and why a `<span style="color:red" class="hl">` yielded
375
+ * BOTH the mark and a preserved atom, nested. The preservation catch-all
376
+ * keeps any span `isLosslesslyUnwrappable` refuses, so the mark has to
377
+ * refuse the same elements.
378
+ *
379
+ * The replacement is a `tag: '*'` rule with `consuming: false`: same
380
+ * "applies to whoever carries the declaration" behaviour, but `getAttrs`
381
+ * receives the element and can decline. Hosts the schema models are not
382
+ * spans the catch-all would keep, so they still produce the mark.
383
+ */
384
+ function declineStyleMarkOnPreservedAtom(el) {
385
+ return el.nodeName.toLowerCase() === 'span' && !isLosslesslyUnwrappable(el);
386
+ }
319
387
  /** The base mark specs. */
320
388
  export const coreMarks = {
321
389
  strong: {
@@ -351,26 +419,36 @@ export const coreMarks = {
351
419
  * would have the second command overwrite the first's value with a default.
352
420
  * Two marks compose the way the toolbar's two controls do.
353
421
  *
354
- * Both are parsed through a `style` rule, not a tag rule, and that choice is
355
- * load-bearing. ProseMirror applies style rules to whatever element carries
356
- * the declaration, so `<span style="color:red">`, `<p style="color:red">` and
357
- * a `<div>` wrapper all yield the same mark -- whereas a `span[style]` tag rule
358
- * would claim the span, drop it, and quietly lose any other declaration on it.
422
+ * Parsed through a `tag: '*'` rule that does not consume the element, not a
423
+ * `span[style]` tag rule. The un-consuming match is load-bearing: it is how
424
+ * `<span style="color:red">`, `<p style="color:red">` and a `<div>` wrapper
425
+ * all yield the same mark -- whereas a `span[style]` rule would claim the
426
+ * span, drop it, and quietly lose any other declaration on it.
427
+ *
428
+ * It is not a `style:` rule either. Those also do not consume, but they
429
+ * never see the element, so they cannot decline when preservation is going
430
+ * to keep it. `declineStyleMarkOnPreservedAtom` is that decline.
359
431
  *
360
432
  * The value is normalized by `safeColor`, which folds `rgb(255, 0, 0)` back to
361
- * `#ff0000`. That is not cosmetic: ProseMirror reads style rules through the
362
- * CSSOM, which returns the functional form for an authored hex colour, so
363
- * without the fold every hex colour in an archive would be rewritten longer on
364
- * the first save.
433
+ * `#ff0000`. That is not cosmetic: the rule still reads through the CSSOM
434
+ * (`el.style.color`), which returns the functional form for an authored hex
435
+ * colour, so without the fold every hex colour in an archive would be
436
+ * rewritten longer on the first save.
365
437
  */
366
438
  text_color: {
367
439
  attrs: { color: {} },
368
440
  parseDOM: [
369
441
  {
370
- style: 'color',
371
- getAttrs(value) {
372
- const color = safeColor(value);
373
- return color ? { color } : false;
442
+ tag: '*',
443
+ consuming: false,
444
+ getAttrs(dom) {
445
+ const el = dom;
446
+ const color = safeColor(el.style.color);
447
+ if (!color)
448
+ return false;
449
+ if (declineStyleMarkOnPreservedAtom(el))
450
+ return false;
451
+ return { color };
374
452
  },
375
453
  },
376
454
  {
@@ -403,6 +481,17 @@ export const coreMarks = {
403
481
  tag: 'span',
404
482
  getAttrs(dom) {
405
483
  const el = dom;
484
+ // Only when the span is nothing but formatting this mark can hold.
485
+ // A tag rule CONSUMES the element. Character marks now carry leftover
486
+ // attributes the way nodes do, but this rule still declines a span
487
+ // that is more than a highlight: claiming
488
+ // `<span class="hl" style="background-color:#ffff00;letter-spacing:1px">`
489
+ // would swallow declarations other style-marks and the preservation
490
+ // layer are meant to see. Declining leaves the element whole. This is
491
+ // the same bargain `font[color]` and `span[lang]` already strike a
492
+ // few lines away.
493
+ if (!isSpanOnlyStyling(el))
494
+ return false;
406
495
  const fromAttr = parseDeclarations(el.getAttribute('style')).get('background-color');
407
496
  const color = safeColor(el.style.backgroundColor) ?? safeColor(fromAttr);
408
497
  return color ? { color } : false;
@@ -415,10 +504,16 @@ export const coreMarks = {
415
504
  attrs: { family: {} },
416
505
  parseDOM: [
417
506
  {
418
- style: 'font-family',
419
- getAttrs(value) {
420
- const family = safeFontFamily(value);
421
- return family ? { family } : false;
507
+ tag: '*',
508
+ consuming: false,
509
+ getAttrs(dom) {
510
+ const el = dom;
511
+ const family = safeFontFamily(el.style.fontFamily);
512
+ if (!family)
513
+ return false;
514
+ if (declineStyleMarkOnPreservedAtom(el))
515
+ return false;
516
+ return { family };
422
517
  },
423
518
  },
424
519
  {
@@ -440,10 +535,16 @@ export const coreMarks = {
440
535
  attrs: { size: {} },
441
536
  parseDOM: [
442
537
  {
443
- style: 'font-size',
444
- getAttrs(value) {
445
- const size = safeFontSize(value);
446
- return size ? { size } : false;
538
+ tag: '*',
539
+ consuming: false,
540
+ getAttrs(dom) {
541
+ const el = dom;
542
+ const size = safeFontSize(el.style.fontSize);
543
+ if (!size)
544
+ return false;
545
+ if (declineStyleMarkOnPreservedAtom(el))
546
+ return false;
547
+ return { size };
447
548
  },
448
549
  },
449
550
  ],
@@ -460,6 +561,8 @@ export const coreMarks = {
460
561
  toDOM: () => ['sup', 0],
461
562
  },
462
563
  language: {
564
+ // Content, not decoration -- the same claim `dir` makes as a node
565
+ // attribute. `clearFormatting` keeps this mark for that reason.
463
566
  attrs: { lang: {} },
464
567
  inclusive: false,
465
568
  parseDOM: [
@@ -485,7 +588,9 @@ export const coreMarks = {
485
588
  },
486
589
  link: {
487
590
  attrs: {
488
- href: {},
591
+ // Null when the mark is a wrapped named destination (`<a id>` with
592
+ // text, no href). Empty `<a id></a>` is the `named_anchor` atom.
593
+ href: { default: null },
489
594
  title: { default: null },
490
595
  target: { default: null },
491
596
  rel: { default: null },
@@ -511,10 +616,34 @@ export const coreMarks = {
511
616
  };
512
617
  },
513
618
  },
619
+ {
620
+ tag: 'a[id]',
621
+ getAttrs(dom) {
622
+ const el = dom;
623
+ if (el.hasAttribute('href'))
624
+ return false;
625
+ const id = safeId(el.getAttribute('id'));
626
+ if (!id)
627
+ return false;
628
+ // Empty `<a id>` belongs to `named_anchor`. This rule is the
629
+ // wrapped-text spelling, so the heading stays in the document.
630
+ if (isEmptyNamedAnchorElement(el))
631
+ return false;
632
+ return {
633
+ href: null,
634
+ title: el.getAttribute('title'),
635
+ target: el.getAttribute('target'),
636
+ rel: el.getAttribute('rel'),
637
+ id,
638
+ };
639
+ },
640
+ },
514
641
  ],
515
642
  toDOM(node) {
516
643
  const { href, title, target, rel, id } = node.attrs;
517
- const attrs = { href: href };
644
+ const attrs = {};
645
+ if (href !== null)
646
+ attrs['href'] = href;
518
647
  if (title !== null)
519
648
  attrs['title'] = title;
520
649
  if (target !== null)