@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/dist/keymap.js ADDED
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Keyboard shortcuts.
3
+ *
4
+ * Bindings follow whatever convention the largest number of users already have
5
+ * in their fingers, which usually means Word and Google Docs rather than
6
+ * anything invented here. Where those disagree, the web convention wins,
7
+ * because this editor lives in a browser.
8
+ *
9
+ * `Mod` resolves to Cmd on macOS and Ctrl elsewhere.
10
+ *
11
+ * ## On Tab
12
+ *
13
+ * Tab is deliberately NOT bound to list indentation, even though Word does it
14
+ * and users ask for it. Inside a `contenteditable`, capturing Tab removes the
15
+ * only way a keyboard user has to leave the editor, which is a WCAG 2.1.2
16
+ * keyboard-trap failure -- and for the institutional users who most need a free
17
+ * editor, that is a procurement blocker rather than a rough edge.
18
+ *
19
+ * Indentation uses `Mod-[` and `Mod-]` instead, matching Google Docs and
20
+ * VS Code. If Tab is ever added it must come with a documented escape (Escape
21
+ * then Tab, or a first-Tab-escapes heuristic) and real screen reader testing.
22
+ */
23
+ import { baseKeymap, chainCommands, exitCode } from 'prosemirror-commands';
24
+ import { redo, undo } from 'prosemirror-history';
25
+ import { indentListItem, insertHorizontalRule, outdentListItem, setParagraph, splitListItemCommand, toggleBlockquote, toggleBold, toggleBulletList, toggleCodeBlock, toggleHeading, toggleInlineCode, toggleItalic, toggleOrderedList, toggleStrike, toggleUnderline, } from './commands.js';
26
+ /**
27
+ * The default shortcut table.
28
+ *
29
+ * Exported as data rather than as a finished keymap so that an integrator can
30
+ * remove a binding that collides with their own application, and so the help
31
+ * dialog and the toolbar tooltips can render the real bindings instead of a
32
+ * hand-maintained duplicate list that drifts.
33
+ */
34
+ export const shortcuts = [
35
+ // Marks. Mod-b/i/u are universal; Mod-Shift-x and Mod-e follow GitHub.
36
+ { keys: 'Mod-b', command: toggleBold, label: 'Bold' },
37
+ { keys: 'Mod-i', command: toggleItalic, label: 'Italic' },
38
+ // Note: Mod-u is a View Source accelerator in some browsers. It has not been
39
+ // a problem in testing, but if it becomes one the workaround belongs here.
40
+ { keys: 'Mod-u', command: toggleUnderline, label: 'Underline' },
41
+ { keys: 'Mod-Shift-x', command: toggleStrike, label: 'Strikethrough' },
42
+ { keys: 'Mod-e', command: toggleInlineCode, label: 'Inline code' },
43
+ // Blocks. Mod-Alt-N for headings matches Google Docs.
44
+ { keys: 'Mod-Alt-0', command: setParagraph, label: 'Paragraph' },
45
+ ...[1, 2, 3, 4, 5, 6].map((level) => ({
46
+ keys: `Mod-Alt-${level}`,
47
+ command: toggleHeading(level),
48
+ label: `Heading ${level}`,
49
+ })),
50
+ { keys: 'Mod-Shift-.', command: toggleBlockquote, label: 'Blockquote' },
51
+ { keys: 'Mod-Alt-c', command: toggleCodeBlock, label: 'Code block' },
52
+ { keys: 'Mod-Shift-Enter', command: insertHorizontalRule, label: 'Horizontal rule' },
53
+ // Lists. Mod-Shift-7/8 matches Word and Google Docs.
54
+ { keys: 'Mod-Shift-7', command: toggleOrderedList, label: 'Numbered list' },
55
+ { keys: 'Mod-Shift-8', command: toggleBulletList, label: 'Bulleted list' },
56
+ { keys: 'Mod-]', command: indentListItem, label: 'Indent list item' },
57
+ { keys: 'Mod-[', command: outdentListItem, label: 'Outdent list item' },
58
+ // History.
59
+ { keys: 'Mod-z', command: undo, label: 'Undo' },
60
+ { keys: 'Mod-Shift-z', command: redo, label: 'Redo' },
61
+ { keys: 'Mod-y', command: redo, label: 'Redo' },
62
+ ];
63
+ /**
64
+ * Build the keymap bindings object.
65
+ *
66
+ * `Enter` chains: splitting a list item must be tried before ProseMirror's
67
+ * default paragraph split, or pressing Enter in a list creates a paragraph
68
+ * instead of the next bullet.
69
+ */
70
+ export function buildKeymap(custom = {}) {
71
+ const bindings = {};
72
+ for (const { keys, command } of shortcuts) {
73
+ // Later entries chain behind earlier ones so two shortcuts can share a key
74
+ // and the first that applies wins -- which is how Mod-y and Mod-Shift-z
75
+ // both mean redo without clobbering each other.
76
+ const existing = bindings[keys];
77
+ bindings[keys] = existing ? chainCommands(existing, command) : command;
78
+ }
79
+ bindings['Enter'] = chainCommands(splitListItemCommand, baseKeymap['Enter']);
80
+ bindings['Shift-Enter'] = chainCommands(exitCode, (state, dispatch) => {
81
+ const br = state.schema.nodes['hard_break'];
82
+ if (!br)
83
+ return false;
84
+ if (dispatch)
85
+ dispatch(state.tr.replaceSelectionWith(br.create()).scrollIntoView());
86
+ return true;
87
+ });
88
+ return { ...bindings, ...custom };
89
+ }
90
+ /** Human-readable shortcut for a label, with the platform's modifier symbol. */
91
+ export function shortcutFor(label, isMac = detectMac()) {
92
+ const found = shortcuts.find((s) => s.label === label);
93
+ if (!found)
94
+ return null;
95
+ return found.keys
96
+ .replace(/Mod/g, isMac ? '⌘' : 'Ctrl')
97
+ .replace(/Alt/g, isMac ? '⌥' : 'Alt')
98
+ .replace(/Shift/g, isMac ? '⇧' : 'Shift')
99
+ .replace(/-/g, isMac ? '' : '+');
100
+ }
101
+ function detectMac() {
102
+ if (typeof navigator === 'undefined')
103
+ return false;
104
+ return /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent);
105
+ }
106
+ //# sourceMappingURL=keymap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keymap.js","sourceRoot":"","sources":["../src/keymap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The editor plugin registry.
3
+ *
4
+ * A ProseMirror plugin cannot be added to a running editor, so anything that
5
+ * contributes one has to do it before the view is constructed. This registry is
6
+ * how an opt-in bundle -- loaded by a second `<script>` tag, after the core
7
+ * bundle -- gets its plugins into editors that have not been created yet.
8
+ *
9
+ * Factories rather than plugin instances: a ProseMirror plugin instance carries
10
+ * per-editor state and cannot be shared between two editors on the same page.
11
+ * Calling the factory once per editor is the difference between two working
12
+ * editors and two editors fighting over one plugin's state.
13
+ */
14
+ import type { Plugin } from 'prosemirror-state';
15
+ import type { Schema } from 'prosemirror-model';
16
+ export type EditorPluginFactory = (schema: Schema) => Plugin[];
17
+ /** Register plugins to be installed in every editor created from now on. */
18
+ export declare function registerEditorPlugin(factory: EditorPluginFactory): () => void;
19
+ /** Build plugin instances for an editor, reusing cached instances when given. */
20
+ export declare function createRegisteredPlugins(schema: Schema, cache?: Map<EditorPluginFactory, Plugin[]>): Plugin[];
21
+ /**
22
+ * Notified when a plugin registers.
23
+ *
24
+ * An editor already on the page when a deferred bundle finishes loading would
25
+ * otherwise never receive its plugins, and the author would find table controls
26
+ * that do nothing.
27
+ */
28
+ export declare function onEditorPluginsChange(listener: () => void): () => void;
29
+ //# sourceMappingURL=plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE/C,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAA;AAK9D,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAU7E;AAoBD,iFAAiF;AACjF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,GAAG,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC,GACzC,MAAM,EAAE,CA2BV;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAKtE"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The editor plugin registry.
3
+ *
4
+ * A ProseMirror plugin cannot be added to a running editor, so anything that
5
+ * contributes one has to do it before the view is constructed. This registry is
6
+ * how an opt-in bundle -- loaded by a second `<script>` tag, after the core
7
+ * bundle -- gets its plugins into editors that have not been created yet.
8
+ *
9
+ * Factories rather than plugin instances: a ProseMirror plugin instance carries
10
+ * per-editor state and cannot be shared between two editors on the same page.
11
+ * Calling the factory once per editor is the difference between two working
12
+ * editors and two editors fighting over one plugin's state.
13
+ */
14
+ const factories = new Set();
15
+ const listeners = new Set();
16
+ /** Register plugins to be installed in every editor created from now on. */
17
+ export function registerEditorPlugin(factory) {
18
+ factories.add(factory);
19
+ notify();
20
+ return () => {
21
+ // Notify on removal too. Without it the disposer deleted the factory and
22
+ // told nobody, so every editor already on the page kept the plugin running
23
+ // -- a disposer that is observably a no-op in the common case is worse than
24
+ // none, because callers believe it worked.
25
+ if (factories.delete(factory))
26
+ notify();
27
+ };
28
+ }
29
+ /**
30
+ * Notify listeners, isolating each one.
31
+ *
32
+ * These listeners are editors. On a page with three of them, an exception from
33
+ * the second must not stop the third from ever seeing the plugin -- and it must
34
+ * not propagate back out of `registerEditorPlugin` into the calling plugin,
35
+ * where a broken editor would look like a broken install.
36
+ */
37
+ function notify() {
38
+ for (const listener of listeners) {
39
+ try {
40
+ listener();
41
+ }
42
+ catch (error) {
43
+ console.error('@openleaf-editor/core: an editor failed to apply a plugin change', error);
44
+ }
45
+ }
46
+ }
47
+ /** Build plugin instances for an editor, reusing cached instances when given. */
48
+ export function createRegisteredPlugins(schema, cache) {
49
+ const plugins = [];
50
+ const seen = new Set();
51
+ for (const factory of factories) {
52
+ seen.add(factory);
53
+ const cached = cache?.get(factory);
54
+ if (cached) {
55
+ plugins.push(...cached);
56
+ continue;
57
+ }
58
+ try {
59
+ const created = factory(schema);
60
+ cache?.set(factory, created);
61
+ plugins.push(...created);
62
+ }
63
+ catch (error) {
64
+ // A throwing factory used to take EditorState.create with it, so one bad
65
+ // script tag produced a blank editor. Contributing nothing is the right
66
+ // failure: the editor comes up without that plugin.
67
+ console.error('@openleaf-editor/core: a plugin factory threw; skipping it', error);
68
+ }
69
+ }
70
+ if (cache) {
71
+ for (const factory of [...cache.keys()]) {
72
+ if (!seen.has(factory))
73
+ cache.delete(factory);
74
+ }
75
+ }
76
+ return plugins;
77
+ }
78
+ /**
79
+ * Notified when a plugin registers.
80
+ *
81
+ * An editor already on the page when a deferred bundle finishes loading would
82
+ * otherwise never receive its plugins, and the author would find table controls
83
+ * that do nothing.
84
+ */
85
+ export function onEditorPluginsChange(listener) {
86
+ listeners.add(listener);
87
+ return () => {
88
+ listeners.delete(listener);
89
+ };
90
+ }
91
+ //# sourceMappingURL=plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAA;AAChD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAA;AAEvC,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,OAA4B;IAC/D,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,MAAM,EAAE,CAAA;IACR,OAAO,GAAG,EAAE;QACV,yEAAyE;QACzE,2EAA2E;QAC3E,4EAA4E;QAC5E,2CAA2C;QAC3C,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,MAAM,EAAE,CAAA;IACzC,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,MAAM;IACb,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,QAAQ,EAAE,CAAA;QACZ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,kEAAkE,EAAE,KAAK,CAAC,CAAA;QAC1F,CAAC;IACH,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,uBAAuB,CACrC,MAAc,EACd,KAA0C;IAE1C,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAuB,CAAA;IAC3C,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACjB,MAAM,MAAM,GAAG,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;QAClC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAA;YACvB,SAAQ;QACV,CAAC;QACD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;YAC/B,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5B,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,yEAAyE;YACzE,wEAAwE;YACxE,oDAAoD;YACpD,OAAO,CAAC,KAAK,CAAC,4DAA4D,EAAE,KAAK,CAAC,CAAA;QACpF,CAAC;IACH,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAoB;IACxD,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,GAAG,EAAE;QACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC5B,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The content-preservation layer.
3
+ *
4
+ * ProseMirror is schema-strict: anything its schema does not recognise is
5
+ * silently discarded. TinyMCE is permissive: it round-trips almost
6
+ * anything. That difference is the single largest risk in replacing
7
+ * TinyMCE with a ProseMirror-based editor, because the failure mode is
8
+ * not an error -- it is a customer opening a ten-year-old blog post,
9
+ * pressing Save, and losing a section of it with no warning.
10
+ *
11
+ * This module makes that failure impossible by construction. Unrecognised
12
+ * markup is captured verbatim into an atom node rather than dropped, and
13
+ * re-emitted byte-identical on serialization.
14
+ *
15
+ * The governing distinction:
16
+ *
17
+ * NORMALIZATION is allowed. `<div>hi</div>` becoming `<p>hi</p>` is
18
+ * fine -- no information is lost, the markup is merely made canonical.
19
+ *
20
+ * INFORMATION LOSS is not. `<div class="callout">hi</div>` becoming
21
+ * `<p>hi</p>` silently destroys the author's intent. The class was
22
+ * load-bearing and we had no way to know it wasn't.
23
+ *
24
+ * So the rule is not "is this tag known?" but "would unwrapping this
25
+ * lose information?" A bare structural wrapper unwraps. The moment an
26
+ * element carries an attribute we cannot represent, it becomes opaque
27
+ * and is preserved intact.
28
+ */
29
+ import type { NodeSpec } from 'prosemirror-model';
30
+ /**
31
+ * True when this element can be unwrapped without losing information.
32
+ *
33
+ * Conservative on purpose: ANY attribute makes an element opaque, even a
34
+ * seemingly harmless one. We would rather preserve a redundant `id` than
35
+ * guess wrong about a `data-` attribute some integration depends on.
36
+ * Over-preserving is visible and correctable by the user; under-
37
+ * preserving is invisible and permanent.
38
+ */
39
+ export declare function isLosslesslyUnwrappable(el: Element): boolean;
40
+ /** Run `fn` with preserved-node serialization targeting this Document. */
41
+ export declare function withSerializationDocument<T>(doc: Document, fn: () => T): T;
42
+ /** True when this element, or an ancestor, was rendered from preserved markup. */
43
+ export declare function isInsidePreserved(node: Element | null): boolean;
44
+ /**
45
+ * Block-level preserved content. An atom: the editor can select, move and
46
+ * delete it, but never edits its interior, so its markup cannot drift.
47
+ */
48
+ export declare const unknownBlock: NodeSpec;
49
+ /**
50
+ * Inline preserved content, for unrecognised markup appearing inside a
51
+ * paragraph -- the `<o:p>` and `<w:sdt>` debris of a Word paste, custom
52
+ * inline web components, legacy `<font>` runs.
53
+ */
54
+ export declare const unknownInline: NodeSpec;
55
+ //# sourceMappingURL=preserve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preserve.d.ts","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAuGjD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAG5D;AAoBD,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ1E;AAwCD,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAK/D;AAeD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,QA4B1B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QA4B3B,CAAA"}
@@ -0,0 +1,288 @@
1
+ /**
2
+ * The content-preservation layer.
3
+ *
4
+ * ProseMirror is schema-strict: anything its schema does not recognise is
5
+ * silently discarded. TinyMCE is permissive: it round-trips almost
6
+ * anything. That difference is the single largest risk in replacing
7
+ * TinyMCE with a ProseMirror-based editor, because the failure mode is
8
+ * not an error -- it is a customer opening a ten-year-old blog post,
9
+ * pressing Save, and losing a section of it with no warning.
10
+ *
11
+ * This module makes that failure impossible by construction. Unrecognised
12
+ * markup is captured verbatim into an atom node rather than dropped, and
13
+ * re-emitted byte-identical on serialization.
14
+ *
15
+ * The governing distinction:
16
+ *
17
+ * NORMALIZATION is allowed. `<div>hi</div>` becoming `<p>hi</p>` is
18
+ * fine -- no information is lost, the markup is merely made canonical.
19
+ *
20
+ * INFORMATION LOSS is not. `<div class="callout">hi</div>` becoming
21
+ * `<p>hi</p>` silently destroys the author's intent. The class was
22
+ * load-bearing and we had no way to know it wasn't.
23
+ *
24
+ * So the rule is not "is this tag known?" but "would unwrapping this
25
+ * lose information?" A bare structural wrapper unwraps. The moment an
26
+ * element carries an attribute we cannot represent, it becomes opaque
27
+ * and is preserved intact.
28
+ */
29
+ import { URL_ATTRIBUTES, isEventHandlerAttribute, isSafeUrl } from './url.js';
30
+ /**
31
+ * Elements that are never preserved, and whose contents are discarded with them.
32
+ *
33
+ * This is where the project's two strongest instincts collide. The preservation
34
+ * layer exists because silently deleting a customer's markup is the failure
35
+ * OpenLeaf was built to prevent -- but "markup the schema does not recognise"
36
+ * includes `<script>`.
37
+ *
38
+ * Preserving an author's `<div class="callout">` is the product working.
39
+ * Preserving a `<script>` is a vulnerability with extra steps: the editor would
40
+ * hand it back on save, the server would store it, and the next reader would
41
+ * execute it. The content-safety promise is about *authorial content*, and a
42
+ * script tag is not that.
43
+ *
44
+ * `ignore` rather than `skip`: the element AND its contents go. Skipping would
45
+ * unwrap `<script>alert(1)</script>` into the literal text "alert(1)" appearing
46
+ * in the document, which is a different kind of wrong.
47
+ */
48
+ const NEVER_PRESERVE = [
49
+ 'script',
50
+ 'style',
51
+ 'iframe',
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
+ ];
69
+ /** Parse rules that drop dangerous elements before any other rule sees them. */
70
+ const dropRules = NEVER_PRESERVE.map((tag) => ({ tag, ignore: true, priority: 100 }));
71
+ /**
72
+ * Scrub markup before it is stored for preservation.
73
+ *
74
+ * Preserving an element verbatim means preserving its attributes verbatim, and
75
+ * `<div class="callout" onclick="steal()">` is not something an author needs
76
+ * kept. Works on a clone so the live parse tree is untouched.
77
+ */
78
+ function scrub(el) {
79
+ const clone = el.cloneNode(true);
80
+ const visit = (node) => {
81
+ for (const child of Array.from(node.children)) {
82
+ if (NEVER_PRESERVE.includes(child.nodeName.toLowerCase())) {
83
+ child.remove();
84
+ continue;
85
+ }
86
+ visit(child);
87
+ }
88
+ for (const attr of Array.from(node.attributes)) {
89
+ if (isEventHandlerAttribute(attr.name)) {
90
+ node.removeAttribute(attr.name);
91
+ continue;
92
+ }
93
+ if (URL_ATTRIBUTES.has(attr.name.toLowerCase()) && !isSafeUrl(attr.value)) {
94
+ node.removeAttribute(attr.name);
95
+ }
96
+ }
97
+ };
98
+ visit(clone);
99
+ return clone.outerHTML;
100
+ }
101
+ /**
102
+ * Elements that contribute no meaning of their own -- pure structural
103
+ * wrappers. Unwrapping one of these loses nothing, PROVIDED it carries no
104
+ * attributes.
105
+ *
106
+ * Deliberately excluded, because they do carry meaning we would lose:
107
+ * figure/figcaption (image semantics -- belongs to a real node type)
108
+ * center, font (presentational intent)
109
+ * ins, del (revision semantics)
110
+ * details, summary (interaction semantics)
111
+ */
112
+ const TRANSPARENT_CONTAINERS = new Set([
113
+ 'div',
114
+ 'section',
115
+ 'article',
116
+ 'main',
117
+ 'aside',
118
+ 'header',
119
+ 'footer',
120
+ 'nav',
121
+ 'span',
122
+ 'hgroup',
123
+ ]);
124
+ /**
125
+ * True when this element can be unwrapped without losing information.
126
+ *
127
+ * Conservative on purpose: ANY attribute makes an element opaque, even a
128
+ * seemingly harmless one. We would rather preserve a redundant `id` than
129
+ * guess wrong about a `data-` attribute some integration depends on.
130
+ * Over-preserving is visible and correctable by the user; under-
131
+ * preserving is invisible and permanent.
132
+ */
133
+ export function isLosslesslyUnwrappable(el) {
134
+ if (!TRANSPARENT_CONTAINERS.has(el.nodeName.toLowerCase()))
135
+ return false;
136
+ return el.attributes.length === 0;
137
+ }
138
+ /** Rebuild a DOM element from stored markup. `<template>` is used because
139
+ * its parsing context permits otherwise-illegal fragments such as a bare
140
+ * `<tr>`, which a `<div>` container would silently discard. */
141
+ function elementFromHtml(html, doc) {
142
+ const tpl = doc.createElement('template');
143
+ tpl.innerHTML = html;
144
+ return tpl.content.firstElementChild;
145
+ }
146
+ /**
147
+ * ProseMirror's `toDOM` does not receive the `document` passed to
148
+ * `serializeFragment`. Preserved nodes rebuild markup inside `toDOM`, so the
149
+ * explicit Document has to travel out of band for the duration of one
150
+ * serialize. Nested calls restore the previous value so a re-entrant serialize
151
+ * cannot leak a document across documents.
152
+ */
153
+ let serializationDocument;
154
+ /** Run `fn` with preserved-node serialization targeting this Document. */
155
+ export function withSerializationDocument(doc, fn) {
156
+ const previous = serializationDocument;
157
+ serializationDocument = doc;
158
+ try {
159
+ return fn();
160
+ }
161
+ finally {
162
+ serializationDocument = previous;
163
+ }
164
+ }
165
+ function ownerDocument() {
166
+ if (serializationDocument)
167
+ return serializationDocument;
168
+ if (typeof document === 'undefined') {
169
+ throw new Error('@openleaf-editor/core: no global `document` available. Preserved content ' +
170
+ 'needs a DOM to re-serialize. On the server, pass an explicit ' +
171
+ 'document to parseHtml/serializeHtml.');
172
+ }
173
+ return document;
174
+ }
175
+ /**
176
+ * Rebuild preserved markup, or -- if it somehow will not re-parse -- carry it
177
+ * out on a data attribute rather than dropping it.
178
+ *
179
+ * This fallback should be unreachable, because the stored string came from
180
+ * `outerHTML` of an element the browser had already parsed. It exists anyway:
181
+ * emitting something slightly odd is always preferable to destroying a user's
182
+ * content, and an unreachable branch that preserves data costs nothing.
183
+ */
184
+ /**
185
+ * Elements rendered from preserved markup, identified out of band.
186
+ *
187
+ * Normalization passes running over the serialized output need to tell "markup
188
+ * we own" from "markup we promised not to touch". The first attempt marked
189
+ * preserved output with a real DOM attribute and stripped it afterwards -- which
190
+ * could not distinguish the attribute it had just added from the same attribute
191
+ * occurring in somebody's document, so a customer using `data-ol-preserved` had
192
+ * it silently deleted. Destroying an attribute inside preserved content is the
193
+ * exact failure the marker existed to prevent.
194
+ *
195
+ * A WeakSet cannot collide with content, needs no cleanup pass, and holds its
196
+ * entries weakly so a serialization's throwaway DOM is still collectable.
197
+ */
198
+ const preservedElements = new WeakSet();
199
+ /** True when this element, or an ancestor, was rendered from preserved markup. */
200
+ export function isInsidePreserved(node) {
201
+ for (let current = node; current; current = current.parentElement) {
202
+ if (preservedElements.has(current))
203
+ return true;
204
+ }
205
+ return false;
206
+ }
207
+ function rebuildOrCarry(html, fallbackTag) {
208
+ const doc = ownerDocument();
209
+ const rebuilt = elementFromHtml(html, doc);
210
+ if (rebuilt) {
211
+ preservedElements.add(rebuilt);
212
+ return rebuilt;
213
+ }
214
+ const carrier = doc.createElement(fallbackTag);
215
+ carrier.setAttribute('data-openleaf-unparsable', html);
216
+ preservedElements.add(carrier);
217
+ return carrier;
218
+ }
219
+ /**
220
+ * Block-level preserved content. An atom: the editor can select, move and
221
+ * delete it, but never edits its interior, so its markup cannot drift.
222
+ */
223
+ export const unknownBlock = {
224
+ group: 'block',
225
+ atom: true,
226
+ selectable: true,
227
+ isolating: true,
228
+ attrs: {
229
+ html: { default: '' },
230
+ tag: { default: 'div' },
231
+ },
232
+ parseDOM: [
233
+ ...dropRules,
234
+ {
235
+ tag: '*',
236
+ // Lowest priority: every real rule in the schema gets first refusal.
237
+ // This only ever fires for markup nothing else claimed.
238
+ priority: 0,
239
+ getAttrs(dom) {
240
+ const el = dom;
241
+ // Returning false declines the rule, so ProseMirror falls through
242
+ // to its default behaviour -- unwrap, keep the children editable.
243
+ if (isLosslesslyUnwrappable(el))
244
+ return false;
245
+ return { html: scrub(el), tag: el.nodeName.toLowerCase() };
246
+ },
247
+ },
248
+ ],
249
+ toDOM(node) {
250
+ return rebuildOrCarry(node.attrs['html'], 'div');
251
+ },
252
+ };
253
+ /**
254
+ * Inline preserved content, for unrecognised markup appearing inside a
255
+ * paragraph -- the `<o:p>` and `<w:sdt>` debris of a Word paste, custom
256
+ * inline web components, legacy `<font>` runs.
257
+ */
258
+ export const unknownInline = {
259
+ group: 'inline',
260
+ inline: true,
261
+ atom: true,
262
+ selectable: true,
263
+ attrs: {
264
+ html: { default: '' },
265
+ tag: { default: 'span' },
266
+ },
267
+ parseDOM: [
268
+ ...dropRules,
269
+ {
270
+ tag: '*',
271
+ // Higher than unknownBlock's catch-all: inline gets first refusal so
272
+ // the block rule cannot claim inline debris and split the paragraph
273
+ // that contained it.
274
+ priority: 1,
275
+ context: 'paragraph/|heading/',
276
+ getAttrs(dom) {
277
+ const el = dom;
278
+ if (isLosslesslyUnwrappable(el))
279
+ return false;
280
+ return { html: scrub(el), tag: el.nodeName.toLowerCase() };
281
+ },
282
+ },
283
+ ],
284
+ toDOM(node) {
285
+ return rebuildOrCarry(node.attrs['html'], 'span');
286
+ },
287
+ };
288
+ //# sourceMappingURL=preserve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preserve.js","sourceRoot":"","sources":["../src/preserve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
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, type MarkSpec, type NodeSpec } from 'prosemirror-model';
11
+ /** The base node specs. Extensions are appended to these. */
12
+ export declare const coreNodes: Record<string, NodeSpec>;
13
+ /** The base mark specs. */
14
+ export declare const coreMarks: Record<string, MarkSpec>;
15
+ /**
16
+ * The base schema, with no extensions.
17
+ *
18
+ * Kept for the many places that only ever need the built-in types. Anything that
19
+ * must honour plugin-contributed node types uses `createSchema` or reads
20
+ * `state.schema` instead -- see extensions.ts.
21
+ */
22
+ export declare const baseSchema: Schema<string, string>;
23
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAKxE,6DAA6D;AAC7D,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAyL9C,CAAA;AAED,2BAA2B;AAC3B,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAkE9C,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,wBAAqD,CAAA"}