@openleaf-editor/core 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/dist/commands.d.ts +82 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +327 -0
- package/dist/commands.js.map +1 -0
- package/dist/extensions.d.ts +91 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +281 -0
- package/dist/extensions.js.map +1 -0
- package/dist/html.d.ts +22 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +117 -0
- package/dist/html.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/keymap.d.ts +49 -0
- package/dist/keymap.d.ts.map +1 -0
- package/dist/keymap.js +106 -0
- package/dist/keymap.js.map +1 -0
- package/dist/plugins.d.ts +29 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +91 -0
- package/dist/plugins.js.map +1 -0
- package/dist/preserve.d.ts +55 -0
- package/dist/preserve.d.ts.map +1 -0
- package/dist/preserve.js +288 -0
- package/dist/preserve.js.map +1 -0
- package/dist/schema.d.ts +23 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +268 -0
- package/dist/schema.js.map +1 -0
- package/dist/tables.d.ts +43 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +172 -0
- package/dist/tables.js.map +1 -0
- package/dist/url.d.ts +26 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +78 -0
- package/dist/url.js.map +1 -0
- package/package.json +43 -0
package/dist/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"}
|
package/dist/plugins.js
ADDED
|
@@ -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"}
|
package/dist/preserve.js
ADDED
|
@@ -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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"}
|
package/dist/schema.d.ts
ADDED
|
@@ -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"}
|