@tiptap/core 2.0.0-beta.14 → 2.0.0-beta.140
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.md +1 -1
- package/README.md +2 -2
- package/dist/packages/core/src/CommandManager.d.ts +12 -5
- package/dist/packages/core/src/Editor.d.ts +19 -20
- package/dist/packages/core/src/EventEmitter.d.ts +8 -4
- package/dist/packages/core/src/Extension.d.ts +102 -26
- package/dist/packages/core/src/ExtensionManager.d.ts +6 -11
- package/dist/packages/core/src/InputRule.d.ts +42 -0
- package/dist/packages/core/src/Mark.d.ts +134 -30
- package/dist/packages/core/src/Node.d.ts +151 -32
- package/dist/packages/core/src/NodeView.d.ts +5 -9
- package/dist/packages/core/src/PasteRule.d.ts +42 -0
- package/dist/packages/core/src/Tracker.d.ts +11 -0
- package/dist/packages/core/src/commands/blur.d.ts +3 -3
- package/dist/packages/core/src/commands/clearContent.d.ts +3 -3
- package/dist/packages/core/src/commands/clearNodes.d.ts +3 -3
- package/dist/packages/core/src/commands/command.d.ts +2 -2
- package/dist/packages/core/src/commands/createParagraphNear.d.ts +3 -3
- package/dist/packages/core/src/commands/deleteNode.d.ts +13 -0
- package/dist/packages/core/src/commands/deleteRange.d.ts +3 -3
- package/dist/packages/core/src/commands/deleteSelection.d.ts +3 -3
- package/dist/packages/core/src/commands/enter.d.ts +3 -3
- package/dist/packages/core/src/commands/exitCode.d.ts +3 -3
- package/dist/packages/core/src/commands/extendMarkRange.d.ts +3 -3
- package/dist/packages/core/src/commands/first.d.ts +3 -3
- package/dist/packages/core/src/commands/focus.d.ts +3 -3
- package/dist/packages/core/src/commands/forEach.d.ts +14 -0
- package/dist/packages/core/src/commands/insertContent.d.ts +16 -0
- package/dist/packages/core/src/commands/insertContentAt.d.ts +16 -0
- package/dist/packages/core/src/commands/joinBackward.d.ts +3 -3
- package/dist/packages/core/src/commands/joinForward.d.ts +3 -3
- package/dist/packages/core/src/commands/keyboardShortcut.d.ts +3 -3
- package/dist/packages/core/src/commands/lift.d.ts +3 -3
- package/dist/packages/core/src/commands/liftEmptyBlock.d.ts +3 -3
- package/dist/packages/core/src/commands/liftListItem.d.ts +3 -3
- package/dist/packages/core/src/commands/newlineInCode.d.ts +3 -3
- package/dist/packages/core/src/commands/resetAttributes.d.ts +13 -0
- package/dist/packages/core/src/commands/scrollIntoView.d.ts +3 -3
- package/dist/packages/core/src/commands/selectAll.d.ts +3 -3
- package/dist/packages/core/src/commands/selectNodeBackward.d.ts +3 -3
- package/dist/packages/core/src/commands/selectNodeForward.d.ts +3 -3
- package/dist/packages/core/src/commands/selectParentNode.d.ts +3 -3
- package/dist/packages/core/src/commands/setContent.d.ts +4 -3
- package/dist/packages/core/src/commands/setMark.d.ts +3 -3
- package/dist/packages/core/src/commands/setMeta.d.ts +12 -0
- package/dist/packages/core/src/commands/setNode.d.ts +3 -3
- package/dist/packages/core/src/commands/setNodeSelection.d.ts +12 -0
- package/dist/packages/core/src/commands/setTextSelection.d.ts +12 -0
- package/dist/packages/core/src/commands/sinkListItem.d.ts +3 -3
- package/dist/packages/core/src/commands/splitBlock.d.ts +3 -3
- package/dist/packages/core/src/commands/splitListItem.d.ts +3 -3
- package/dist/packages/core/src/commands/toggleList.d.ts +3 -3
- package/dist/packages/core/src/commands/toggleMark.d.ts +8 -3
- package/dist/packages/core/src/commands/toggleNode.d.ts +3 -3
- package/dist/packages/core/src/commands/toggleWrap.d.ts +3 -3
- package/dist/packages/core/src/commands/undoInputRule.d.ts +3 -3
- package/dist/packages/core/src/commands/unsetAllMarks.d.ts +3 -3
- package/dist/packages/core/src/commands/unsetMark.d.ts +8 -3
- package/dist/packages/core/src/commands/updateAttributes.d.ts +13 -0
- package/dist/packages/core/src/commands/wrapIn.d.ts +3 -3
- package/dist/packages/core/src/commands/wrapInList.d.ts +3 -3
- package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +1 -1
- package/dist/packages/core/src/extensions/commands.d.ts +19 -15
- package/dist/packages/core/src/extensions/editable.d.ts +1 -1
- package/dist/packages/core/src/extensions/focusEvents.d.ts +1 -1
- package/dist/packages/core/src/extensions/index.d.ts +1 -0
- package/dist/packages/core/src/extensions/keymap.d.ts +1 -1
- package/dist/packages/core/src/extensions/tabindex.d.ts +2 -0
- package/dist/packages/core/src/helpers/createChainableState.d.ts +5 -0
- package/dist/packages/core/src/helpers/createDocument.d.ts +3 -0
- package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +7 -0
- package/dist/packages/core/src/helpers/findChildren.d.ts +3 -0
- package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +6 -0
- package/dist/packages/core/src/helpers/generateHTML.d.ts +2 -2
- package/dist/packages/core/src/helpers/generateJSON.d.ts +2 -0
- package/dist/packages/core/src/helpers/generateText.d.ts +5 -0
- package/dist/packages/core/src/helpers/getAttributes.d.ts +3 -0
- package/dist/packages/core/src/helpers/getDebugJSON.d.ts +8 -0
- package/dist/packages/core/src/helpers/getExtensionField.d.ts +2 -0
- package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +2 -2
- package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +1 -2
- package/dist/packages/core/src/helpers/getMarkRange.d.ts +1 -1
- package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +1 -2
- package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +2 -2
- package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +3 -0
- package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +2 -2
- package/dist/packages/core/src/helpers/getText.d.ts +6 -0
- package/dist/packages/core/src/helpers/getTextBetween.d.ts +6 -0
- package/dist/packages/core/src/helpers/getTextSeralizersFromSchema.d.ts +3 -0
- package/dist/packages/core/src/helpers/isActive.d.ts +1 -2
- package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +2 -0
- package/dist/packages/core/src/helpers/isMarkActive.d.ts +1 -2
- package/dist/packages/core/src/helpers/isNodeActive.d.ts +1 -2
- package/dist/packages/core/src/helpers/posToDOMRect.d.ts +2 -0
- package/dist/packages/core/src/helpers/splitExtensions.d.ts +3 -3
- package/dist/packages/core/src/index.d.ts +32 -5
- package/dist/packages/core/src/inputRules/markInputRule.d.ts +11 -2
- package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +11 -2
- package/dist/packages/core/src/inputRules/textInputRule.d.ts +9 -0
- package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +14 -0
- package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +23 -0
- package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +11 -2
- package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +9 -0
- package/dist/packages/core/src/style.d.ts +1 -1
- package/dist/packages/core/src/types.d.ts +104 -51
- package/dist/packages/core/src/utilities/callOrReturn.d.ts +2 -1
- package/dist/packages/core/src/utilities/deleteProps.d.ts +1 -2
- package/dist/packages/core/src/utilities/findDuplicates.d.ts +1 -0
- package/dist/packages/core/src/utilities/isClass.d.ts +1 -1
- package/dist/packages/core/src/utilities/isEmptyObject.d.ts +1 -1
- package/dist/packages/core/src/utilities/isFunction.d.ts +1 -0
- package/dist/packages/core/src/utilities/isNumber.d.ts +1 -0
- package/dist/packages/core/src/utilities/isObject.d.ts +1 -1
- package/dist/packages/core/src/utilities/isPlainObject.d.ts +1 -1
- package/dist/packages/core/src/utilities/isRegExp.d.ts +1 -0
- package/dist/packages/core/src/utilities/isiOS.d.ts +1 -0
- package/dist/packages/core/src/utilities/mergeAttributes.d.ts +1 -2
- package/dist/packages/core/src/utilities/mergeDeep.d.ts +1 -2
- package/dist/packages/core/src/utilities/objectIncludes.d.ts +3 -2
- package/dist/tiptap-core.cjs.js +2695 -1451
- package/dist/tiptap-core.cjs.js.map +1 -1
- package/dist/tiptap-core.esm.js +3244 -2026
- package/dist/tiptap-core.esm.js.map +1 -1
- package/dist/tiptap-core.umd.js +3182 -1937
- package/dist/tiptap-core.umd.js.map +1 -1
- package/package.json +19 -16
- package/src/CommandManager.ts +59 -61
- package/src/Editor.ts +91 -95
- package/src/EventEmitter.ts +14 -4
- package/src/Extension.ts +202 -40
- package/src/ExtensionManager.ts +234 -70
- package/src/InputRule.ts +268 -0
- package/src/Mark.ts +248 -46
- package/src/Node.ts +275 -51
- package/src/NodeView.ts +105 -42
- package/src/PasteRule.ts +215 -0
- package/src/Tracker.ts +42 -0
- package/src/commands/blur.ts +9 -7
- package/src/commands/clearContent.ts +3 -3
- package/src/commands/clearNodes.ts +25 -19
- package/src/commands/command.ts +2 -2
- package/src/commands/createParagraphNear.ts +3 -3
- package/src/commands/deleteNode.ts +36 -0
- package/src/commands/deleteRange.ts +3 -3
- package/src/commands/deleteSelection.ts +3 -3
- package/src/commands/enter.ts +3 -3
- package/src/commands/exitCode.ts +3 -3
- package/src/commands/extendMarkRange.ts +8 -8
- package/src/commands/first.ts +3 -3
- package/src/commands/focus.ts +31 -9
- package/src/commands/forEach.ts +24 -0
- package/src/commands/insertContent.ts +23 -0
- package/src/commands/insertContentAt.ts +93 -0
- package/src/commands/joinBackward.ts +3 -3
- package/src/commands/joinForward.ts +3 -3
- package/src/commands/keyboardShortcut.ts +3 -3
- package/src/commands/lift.ts +3 -3
- package/src/commands/liftEmptyBlock.ts +3 -3
- package/src/commands/liftListItem.ts +3 -3
- package/src/commands/newlineInCode.ts +3 -3
- package/src/commands/resetAttributes.ts +61 -0
- package/src/commands/scrollIntoView.ts +3 -3
- package/src/commands/selectAll.ts +8 -6
- package/src/commands/selectNodeBackward.ts +3 -3
- package/src/commands/selectNodeForward.ts +3 -3
- package/src/commands/selectParentNode.ts +3 -3
- package/src/commands/setContent.ts +10 -5
- package/src/commands/setMark.ts +33 -10
- package/src/commands/setMeta.ts +18 -0
- package/src/commands/setNode.ts +3 -3
- package/src/commands/setNodeSelection.ts +28 -0
- package/src/commands/setTextSelection.ts +32 -0
- package/src/commands/sinkListItem.ts +3 -3
- package/src/commands/splitBlock.ts +13 -8
- package/src/commands/splitListItem.ts +39 -13
- package/src/commands/toggleList.ts +4 -4
- package/src/commands/toggleMark.ts +15 -5
- package/src/commands/toggleNode.ts +3 -3
- package/src/commands/toggleWrap.ts +3 -3
- package/src/commands/undoInputRule.ts +33 -5
- package/src/commands/unsetAllMarks.ts +3 -3
- package/src/commands/unsetMark.ts +32 -20
- package/src/commands/updateAttributes.ts +72 -0
- package/src/commands/wrapIn.ts +3 -3
- package/src/commands/wrapInList.ts +3 -3
- package/src/extensions/clipboardTextSerializer.ts +11 -35
- package/src/extensions/commands.ts +27 -21
- package/src/extensions/focusEvents.ts +0 -3
- package/src/extensions/index.ts +1 -0
- package/src/extensions/keymap.ts +6 -6
- package/src/extensions/tabindex.ts +19 -0
- package/src/helpers/createChainableState.ts +37 -0
- package/src/helpers/createDocument.ts +11 -0
- package/src/helpers/createNodeFromContent.ts +56 -0
- package/src/helpers/findChildren.ts +17 -0
- package/src/helpers/findChildrenInRange.ts +31 -0
- package/src/helpers/generateHTML.ts +3 -3
- package/src/helpers/generateJSON.ts +13 -0
- package/src/helpers/generateText.ts +29 -0
- package/src/helpers/getAttributes.ts +27 -0
- package/src/helpers/getAttributesFromExtensions.ts +26 -5
- package/src/helpers/getDebugJSON.ts +46 -0
- package/src/helpers/getExtensionField.ts +25 -0
- package/src/helpers/getHTMLFromFragment.ts +5 -5
- package/src/helpers/getMarkAttributes.ts +11 -8
- package/src/helpers/getMarkRange.ts +34 -6
- package/src/helpers/getMarkType.ts +4 -0
- package/src/helpers/getMarksBetween.ts +3 -3
- package/src/helpers/getNodeAttributes.ts +6 -7
- package/src/helpers/getNodeType.ts +4 -0
- package/src/helpers/getRenderedAttributes.ts +3 -5
- package/src/helpers/getSchema.ts +5 -132
- package/src/helpers/getSchemaByResolvedExtensions.ts +147 -0
- package/src/helpers/getSchemaTypeByName.ts +1 -9
- package/src/helpers/getSplittedAttributes.ts +3 -3
- package/src/helpers/getText.ts +18 -0
- package/src/helpers/getTextBetween.ts +45 -0
- package/src/helpers/getTextSeralizersFromSchema.ts +9 -0
- package/src/helpers/injectExtensionAttributesToParseRule.ts +12 -8
- package/src/helpers/isActive.ts +1 -2
- package/src/helpers/isExtensionRulesEnabled.ts +15 -0
- package/src/helpers/isList.ts +11 -4
- package/src/helpers/isMarkActive.ts +40 -16
- package/src/helpers/isNodeActive.ts +23 -31
- package/src/helpers/posToDOMRect.ts +34 -0
- package/src/index.ts +34 -5
- package/src/inputRules/markInputRule.ts +57 -38
- package/src/inputRules/nodeInputRule.ts +43 -11
- package/src/inputRules/textInputRule.ts +35 -0
- package/src/inputRules/textblockTypeInputRule.ts +37 -0
- package/src/inputRules/wrappingInputRule.ts +59 -0
- package/src/pasteRules/markPasteRule.ts +61 -53
- package/src/pasteRules/textPasteRule.ts +35 -0
- package/src/style.ts +14 -0
- package/src/types.ts +118 -36
- package/src/utilities/callOrReturn.ts +6 -3
- package/src/utilities/createStyleTag.ts +7 -0
- package/src/utilities/deleteProps.ts +2 -4
- package/src/utilities/elementFromString.ts +3 -4
- package/src/utilities/findDuplicates.ts +5 -0
- package/src/utilities/fromString.ts +1 -1
- package/src/utilities/isClass.ts +2 -2
- package/src/utilities/isEmptyObject.ts +2 -2
- package/src/utilities/isFunction.ts +3 -0
- package/src/utilities/isNumber.ts +3 -0
- package/src/utilities/isObject.ts +5 -5
- package/src/utilities/isPlainObject.ts +5 -5
- package/src/utilities/isRegExp.ts +3 -0
- package/src/utilities/isString.ts +3 -0
- package/src/utilities/isiOS.ts +12 -0
- package/src/utilities/mergeAttributes.ts +2 -3
- package/src/utilities/mergeDeep.ts +1 -2
- package/src/utilities/objectIncludes.ts +17 -5
- package/CHANGELOG.md +0 -294
- package/dist/packages/core/src/commands/insertHTML.d.ts +0 -12
- package/dist/packages/core/src/commands/insertNode.d.ts +0 -13
- package/dist/packages/core/src/commands/insertText.d.ts +0 -12
- package/dist/packages/core/src/commands/replace.d.ts +0 -13
- package/dist/packages/core/src/commands/replaceRange.d.ts +0 -13
- package/dist/packages/core/src/commands/resetNodeAttributes.d.ts +0 -13
- package/dist/packages/core/src/commands/updateNodeAttributes.d.ts +0 -13
- package/dist/packages/core/src/utilities/removeElement.d.ts +0 -1
- package/dist/tiptap-core.bundle.umd.min.js +0 -17
- package/dist/tiptap-core.bundle.umd.min.js.map +0 -1
- package/src/commands/insertHTML.ts +0 -28
- package/src/commands/insertNode.ts +0 -31
- package/src/commands/insertText.ts +0 -20
- package/src/commands/replace.ts +0 -20
- package/src/commands/replaceRange.ts +0 -36
- package/src/commands/resetNodeAttributes.ts +0 -31
- package/src/commands/updateNodeAttributes.ts +0 -33
- package/src/utilities/removeElement.ts +0 -5
package/dist/tiptap-core.cjs.js
CHANGED
|
@@ -3,1117 +3,443 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var prosemirrorState = require('prosemirror-state');
|
|
6
|
-
var prosemirrorView = require('prosemirror-view');
|
|
7
|
-
var prosemirrorModel = require('prosemirror-model');
|
|
8
|
-
var prosemirrorKeymap = require('prosemirror-keymap');
|
|
9
|
-
var prosemirrorInputrules = require('prosemirror-inputrules');
|
|
10
6
|
var prosemirrorTransform = require('prosemirror-transform');
|
|
11
7
|
var prosemirrorCommands = require('prosemirror-commands');
|
|
8
|
+
var prosemirrorModel = require('prosemirror-model');
|
|
12
9
|
var prosemirrorSchemaList = require('prosemirror-schema-list');
|
|
10
|
+
var prosemirrorView = require('prosemirror-view');
|
|
11
|
+
var prosemirrorKeymap = require('prosemirror-keymap');
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
// see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts
|
|
14
|
+
function getType(value) {
|
|
15
|
+
return Object.prototype.toString.call(value).slice(8, -1);
|
|
16
|
+
}
|
|
17
|
+
function isPlainObject(value) {
|
|
18
|
+
if (getType(value) !== 'Object')
|
|
19
|
+
return false;
|
|
20
|
+
return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
function mergeDeep(target, source) {
|
|
24
|
+
const output = { ...target };
|
|
25
|
+
if (isPlainObject(target) && isPlainObject(source)) {
|
|
26
|
+
Object.keys(source).forEach(key => {
|
|
27
|
+
if (isPlainObject(source[key])) {
|
|
28
|
+
if (!(key in target)) {
|
|
29
|
+
Object.assign(output, { [key]: source[key] });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
output[key] = mergeDeep(target[key], source[key]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
Object.assign(output, { [key]: source[key] });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
24
39
|
}
|
|
25
|
-
return
|
|
40
|
+
return output;
|
|
26
41
|
}
|
|
27
42
|
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
const { from, to } = state.selection;
|
|
31
|
-
let nodes = [];
|
|
32
|
-
state.doc.nodesBetween(from, to, node => {
|
|
33
|
-
nodes = [...nodes, node];
|
|
34
|
-
});
|
|
35
|
-
const node = nodes
|
|
36
|
-
.reverse()
|
|
37
|
-
.find(nodeItem => nodeItem.type.name === type.name);
|
|
38
|
-
if (node) {
|
|
39
|
-
return { ...node.attrs };
|
|
40
|
-
}
|
|
41
|
-
return {};
|
|
43
|
+
function isObject$1(value) {
|
|
44
|
+
return typeof value === 'function';
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Optionally calls `value` as a function.
|
|
49
|
+
* Otherwise it is returned directly.
|
|
50
|
+
* @param value Function or any value.
|
|
51
|
+
* @param context Optional context to bind to function.
|
|
52
|
+
* @param props Optional props to pass to function.
|
|
53
|
+
*/
|
|
54
|
+
function callOrReturn(value, context = undefined, ...props) {
|
|
55
|
+
if (isObject$1(value)) {
|
|
56
|
+
if (context) {
|
|
57
|
+
return value.bind(context)(...props);
|
|
58
|
+
}
|
|
59
|
+
return value(...props);
|
|
47
60
|
}
|
|
48
|
-
return
|
|
61
|
+
return value;
|
|
49
62
|
}
|
|
50
63
|
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
let marks = [];
|
|
55
|
-
if (empty) {
|
|
56
|
-
marks = state.selection.$head.marks();
|
|
64
|
+
function getExtensionField(extension, field, context) {
|
|
65
|
+
if (extension.config[field] === undefined && extension.parent) {
|
|
66
|
+
return getExtensionField(extension.parent, field, context);
|
|
57
67
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
if (typeof extension.config[field] === 'function') {
|
|
69
|
+
const value = extension.config[field].bind({
|
|
70
|
+
...context,
|
|
71
|
+
parent: extension.parent
|
|
72
|
+
? getExtensionField(extension.parent, field, context)
|
|
73
|
+
: null,
|
|
61
74
|
});
|
|
75
|
+
return value;
|
|
62
76
|
}
|
|
63
|
-
|
|
64
|
-
if (mark) {
|
|
65
|
-
return { ...mark.attrs };
|
|
66
|
-
}
|
|
67
|
-
return {};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Check if object1 includes object2
|
|
72
|
-
* @param object1 Object
|
|
73
|
-
* @param object2 Object
|
|
74
|
-
*/
|
|
75
|
-
function objectIncludes(object1, object2) {
|
|
76
|
-
const keys = Object.keys(object2);
|
|
77
|
-
if (!keys.length) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
return !!keys
|
|
81
|
-
.filter(key => object2[key] === object1[key])
|
|
82
|
-
.length;
|
|
77
|
+
return extension.config[field];
|
|
83
78
|
}
|
|
84
79
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
80
|
+
class Extension {
|
|
81
|
+
constructor(config = {}) {
|
|
82
|
+
this.type = 'extension';
|
|
83
|
+
this.name = 'extension';
|
|
84
|
+
this.parent = null;
|
|
85
|
+
this.child = null;
|
|
86
|
+
this.config = {
|
|
87
|
+
name: this.name,
|
|
88
|
+
defaultOptions: {},
|
|
89
|
+
};
|
|
90
|
+
this.config = {
|
|
91
|
+
...this.config,
|
|
92
|
+
...config,
|
|
93
|
+
};
|
|
94
|
+
this.name = this.config.name;
|
|
95
|
+
if (config.defaultOptions) {
|
|
96
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
100
97
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
// TODO: remove `addOptions` fallback
|
|
99
|
+
this.options = this.config.defaultOptions;
|
|
100
|
+
if (this.config.addOptions) {
|
|
101
|
+
this.options = callOrReturn(getExtensionField(this, 'addOptions', {
|
|
102
|
+
name: this.name,
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
|
|
106
|
+
name: this.name,
|
|
107
|
+
options: this.options,
|
|
108
|
+
})) || {};
|
|
111
109
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
static create(config = {}) {
|
|
111
|
+
return new Extension(config);
|
|
112
|
+
}
|
|
113
|
+
configure(options = {}) {
|
|
114
|
+
// return a new instance so we can use the same extension
|
|
115
|
+
// with different calls of `configure`
|
|
116
|
+
const extension = this.extend();
|
|
117
|
+
extension.options = mergeDeep(this.options, options);
|
|
118
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
119
|
+
name: extension.name,
|
|
120
|
+
options: extension.options,
|
|
121
|
+
}));
|
|
122
|
+
return extension;
|
|
123
|
+
}
|
|
124
|
+
extend(extendedConfig = {}) {
|
|
125
|
+
const extension = new Extension(extendedConfig);
|
|
126
|
+
extension.parent = this;
|
|
127
|
+
this.child = extension;
|
|
128
|
+
extension.name = extendedConfig.name
|
|
129
|
+
? extendedConfig.name
|
|
130
|
+
: extension.parent.name;
|
|
131
|
+
if (extendedConfig.defaultOptions) {
|
|
132
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
117
133
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
134
|
+
// TODO: remove `addOptions` fallback
|
|
135
|
+
extension.options = extendedConfig.defaultOptions
|
|
136
|
+
? extendedConfig.defaultOptions
|
|
137
|
+
: extension.parent.options;
|
|
138
|
+
if (extendedConfig.addOptions) {
|
|
139
|
+
extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
|
|
140
|
+
name: extension.name,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
144
|
+
name: extension.name,
|
|
145
|
+
options: extension.options,
|
|
146
|
+
}));
|
|
147
|
+
return extension;
|
|
148
|
+
}
|
|
126
149
|
}
|
|
127
150
|
|
|
128
|
-
function
|
|
129
|
-
const { from, to
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
function getTextBetween(startNode, range, options) {
|
|
152
|
+
const { from, to } = range;
|
|
153
|
+
const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
|
|
154
|
+
let text = '';
|
|
155
|
+
let separated = true;
|
|
156
|
+
startNode.nodesBetween(from, to, (node, pos, parent, index) => {
|
|
157
|
+
var _a;
|
|
158
|
+
const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
|
|
159
|
+
if (textSerializer) {
|
|
160
|
+
if (node.isBlock && !separated) {
|
|
161
|
+
text += blockSeparator;
|
|
162
|
+
separated = true;
|
|
138
163
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
146
|
-
if (node.isText) {
|
|
147
|
-
const relativeFrom = Math.max(from, pos);
|
|
148
|
-
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
149
|
-
const range = relativeTo - relativeFrom;
|
|
150
|
-
selectionRange += range;
|
|
151
|
-
markRanges = [...markRanges, ...node.marks.map(mark => ({
|
|
152
|
-
mark,
|
|
153
|
-
from: relativeFrom,
|
|
154
|
-
to: relativeTo,
|
|
155
|
-
}))];
|
|
164
|
+
text += textSerializer({
|
|
165
|
+
node,
|
|
166
|
+
pos,
|
|
167
|
+
parent,
|
|
168
|
+
index,
|
|
169
|
+
});
|
|
156
170
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
const range = markRanges
|
|
162
|
-
.filter(markRange => {
|
|
163
|
-
if (!type) {
|
|
164
|
-
return true;
|
|
171
|
+
else if (node.isText) {
|
|
172
|
+
text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
|
|
173
|
+
separated = false;
|
|
165
174
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}, 0);
|
|
173
|
-
return range >= selectionRange;
|
|
175
|
+
else if (node.isBlock && !separated) {
|
|
176
|
+
text += blockSeparator;
|
|
177
|
+
separated = true;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return text;
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
function
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return 'mark';
|
|
182
|
-
}
|
|
183
|
-
return null;
|
|
183
|
+
function getTextSeralizersFromSchema(schema) {
|
|
184
|
+
return Object.fromEntries(Object
|
|
185
|
+
.entries(schema.nodes)
|
|
186
|
+
.filter(([, node]) => node.spec.toText)
|
|
187
|
+
.map(([name, node]) => [name, node.spec.toText]));
|
|
184
188
|
}
|
|
185
189
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
190
|
+
const ClipboardTextSerializer = Extension.create({
|
|
191
|
+
name: 'clipboardTextSerializer',
|
|
192
|
+
addProseMirrorPlugins() {
|
|
193
|
+
return [
|
|
194
|
+
new prosemirrorState.Plugin({
|
|
195
|
+
key: new prosemirrorState.PluginKey('clipboardTextSerializer'),
|
|
196
|
+
props: {
|
|
197
|
+
clipboardTextSerializer: () => {
|
|
198
|
+
const { editor } = this;
|
|
199
|
+
const { state, schema } = editor;
|
|
200
|
+
const { doc, selection } = state;
|
|
201
|
+
const { from, to } = selection;
|
|
202
|
+
const textSerializers = getTextSeralizersFromSchema(schema);
|
|
203
|
+
const range = { from, to };
|
|
204
|
+
return getTextBetween(doc, range, {
|
|
205
|
+
textSerializers,
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
}),
|
|
210
|
+
];
|
|
211
|
+
},
|
|
212
|
+
});
|
|
199
213
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
214
|
+
const blur = () => ({ editor, view }) => {
|
|
215
|
+
requestAnimationFrame(() => {
|
|
216
|
+
if (!editor.isDestroyed) {
|
|
217
|
+
view.dom.blur();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
return true;
|
|
221
|
+
};
|
|
205
222
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const temporaryDocument = document.implementation.createHTMLDocument();
|
|
211
|
-
const container = temporaryDocument.createElement('div');
|
|
212
|
-
container.appendChild(fragment);
|
|
213
|
-
return container.innerHTML;
|
|
214
|
-
}
|
|
223
|
+
var blur$1 = /*#__PURE__*/Object.freeze({
|
|
224
|
+
__proto__: null,
|
|
225
|
+
blur: blur
|
|
226
|
+
});
|
|
215
227
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const content = node.toJSON();
|
|
220
|
-
return JSON.stringify(defaultContent) === JSON.stringify(content);
|
|
221
|
-
}
|
|
228
|
+
const clearContent = (emitUpdate = false) => ({ commands }) => {
|
|
229
|
+
return commands.setContent('', emitUpdate);
|
|
230
|
+
};
|
|
222
231
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return styleNode;
|
|
228
|
-
}
|
|
232
|
+
var clearContent$1 = /*#__PURE__*/Object.freeze({
|
|
233
|
+
__proto__: null,
|
|
234
|
+
clearContent: clearContent
|
|
235
|
+
});
|
|
229
236
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const { commands, editor } = this;
|
|
237
|
-
const { state, view } = editor;
|
|
238
|
-
const { tr } = state;
|
|
239
|
-
const props = this.buildProps(tr);
|
|
240
|
-
return Object.fromEntries(Object
|
|
241
|
-
.entries(commands)
|
|
242
|
-
.map(([name, command]) => {
|
|
243
|
-
const method = (...args) => {
|
|
244
|
-
const callback = command(...args)(props);
|
|
245
|
-
if (!tr.getMeta('preventDispatch')) {
|
|
246
|
-
view.dispatch(tr);
|
|
247
|
-
}
|
|
248
|
-
return callback;
|
|
249
|
-
};
|
|
250
|
-
return [name, method];
|
|
251
|
-
}));
|
|
252
|
-
}
|
|
253
|
-
createChain(startTr, shouldDispatch = true) {
|
|
254
|
-
const { commands, editor } = this;
|
|
255
|
-
const { state, view } = editor;
|
|
256
|
-
const callbacks = [];
|
|
257
|
-
const hasStartTransaction = !!startTr;
|
|
258
|
-
const tr = startTr || state.tr;
|
|
259
|
-
const run = () => {
|
|
260
|
-
if (!hasStartTransaction && shouldDispatch && !tr.getMeta('preventDispatch')) {
|
|
261
|
-
view.dispatch(tr);
|
|
262
|
-
}
|
|
263
|
-
return () => callbacks.every(callback => callback === true);
|
|
264
|
-
};
|
|
265
|
-
const chain = {
|
|
266
|
-
...Object.fromEntries(Object.entries(commands).map(([name, command]) => {
|
|
267
|
-
const chainedCommand = (...args) => {
|
|
268
|
-
const props = this.buildProps(tr, shouldDispatch);
|
|
269
|
-
const callback = command(...args)(props);
|
|
270
|
-
callbacks.push(callback);
|
|
271
|
-
return chain;
|
|
272
|
-
};
|
|
273
|
-
return [name, chainedCommand];
|
|
274
|
-
})),
|
|
275
|
-
run,
|
|
276
|
-
};
|
|
277
|
-
return chain;
|
|
278
|
-
}
|
|
279
|
-
createCan(startTr) {
|
|
280
|
-
const { commands, editor } = this;
|
|
281
|
-
const { state } = editor;
|
|
282
|
-
const dispatch = undefined;
|
|
283
|
-
const tr = startTr || state.tr;
|
|
284
|
-
const props = this.buildProps(tr, dispatch);
|
|
285
|
-
const formattedCommands = Object.fromEntries(Object
|
|
286
|
-
.entries(commands)
|
|
287
|
-
.map(([name, command]) => {
|
|
288
|
-
return [name, (...args) => command(...args)({ ...props, dispatch })];
|
|
289
|
-
}));
|
|
290
|
-
return {
|
|
291
|
-
...formattedCommands,
|
|
292
|
-
chain: () => this.createChain(tr, dispatch),
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
buildProps(tr, shouldDispatch = true) {
|
|
296
|
-
const { editor, commands } = this;
|
|
297
|
-
const { state, view } = editor;
|
|
298
|
-
if (state.storedMarks) {
|
|
299
|
-
tr.setStoredMarks(state.storedMarks);
|
|
300
|
-
}
|
|
301
|
-
const props = {
|
|
302
|
-
tr,
|
|
303
|
-
editor,
|
|
304
|
-
view,
|
|
305
|
-
state: this.chainableState(tr, state),
|
|
306
|
-
dispatch: shouldDispatch
|
|
307
|
-
? () => undefined
|
|
308
|
-
: undefined,
|
|
309
|
-
chain: () => this.createChain(tr),
|
|
310
|
-
can: () => this.createCan(tr),
|
|
311
|
-
get commands() {
|
|
312
|
-
return Object.fromEntries(Object
|
|
313
|
-
.entries(commands)
|
|
314
|
-
.map(([name, command]) => {
|
|
315
|
-
return [name, (...args) => command(...args)(props)];
|
|
316
|
-
}));
|
|
317
|
-
},
|
|
318
|
-
};
|
|
319
|
-
return props;
|
|
320
|
-
}
|
|
321
|
-
chainableState(tr, state) {
|
|
322
|
-
let { selection } = tr;
|
|
323
|
-
let { doc } = tr;
|
|
324
|
-
let { storedMarks } = tr;
|
|
325
|
-
return {
|
|
326
|
-
...state,
|
|
327
|
-
schema: state.schema,
|
|
328
|
-
plugins: state.plugins,
|
|
329
|
-
apply: state.apply.bind(state),
|
|
330
|
-
applyTransaction: state.applyTransaction.bind(state),
|
|
331
|
-
reconfigure: state.reconfigure.bind(state),
|
|
332
|
-
toJSON: state.toJSON.bind(state),
|
|
333
|
-
get storedMarks() {
|
|
334
|
-
return storedMarks;
|
|
335
|
-
},
|
|
336
|
-
get selection() {
|
|
337
|
-
return selection;
|
|
338
|
-
},
|
|
339
|
-
get doc() {
|
|
340
|
-
return doc;
|
|
341
|
-
},
|
|
342
|
-
get tr() {
|
|
343
|
-
selection = tr.selection;
|
|
344
|
-
doc = tr.doc;
|
|
345
|
-
storedMarks = tr.storedMarks;
|
|
346
|
-
return tr;
|
|
347
|
-
},
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function splitExtensions(extensions) {
|
|
353
|
-
const baseExtensions = extensions.filter(extension => extension.type === 'extension');
|
|
354
|
-
const nodeExtensions = extensions.filter(extension => extension.type === 'node');
|
|
355
|
-
const markExtensions = extensions.filter(extension => extension.type === 'mark');
|
|
356
|
-
return {
|
|
357
|
-
baseExtensions,
|
|
358
|
-
nodeExtensions,
|
|
359
|
-
markExtensions,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Get a list of all extension attributes defined in `addAttribute` and `addGlobalAttribute`.
|
|
365
|
-
* @param extensions List of extensions
|
|
366
|
-
*/
|
|
367
|
-
function getAttributesFromExtensions(extensions) {
|
|
368
|
-
const extensionAttributes = [];
|
|
369
|
-
const { nodeExtensions, markExtensions } = splitExtensions(extensions);
|
|
370
|
-
const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions];
|
|
371
|
-
const defaultAttribute = {
|
|
372
|
-
default: null,
|
|
373
|
-
rendered: true,
|
|
374
|
-
renderHTML: null,
|
|
375
|
-
parseHTML: null,
|
|
376
|
-
keepOnSplit: true,
|
|
377
|
-
};
|
|
378
|
-
extensions.forEach(extension => {
|
|
379
|
-
const context = {
|
|
380
|
-
options: extension.options,
|
|
381
|
-
};
|
|
382
|
-
if (!extension.config.addGlobalAttributes) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
const globalAttributes = extension.config.addGlobalAttributes.bind(context)();
|
|
386
|
-
globalAttributes.forEach(globalAttribute => {
|
|
387
|
-
globalAttribute.types.forEach(type => {
|
|
388
|
-
Object
|
|
389
|
-
.entries(globalAttribute.attributes)
|
|
390
|
-
.forEach(([name, attribute]) => {
|
|
391
|
-
extensionAttributes.push({
|
|
392
|
-
type,
|
|
393
|
-
name,
|
|
394
|
-
attribute: {
|
|
395
|
-
...defaultAttribute,
|
|
396
|
-
...attribute,
|
|
397
|
-
},
|
|
398
|
-
});
|
|
399
|
-
});
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
nodeAndMarkExtensions.forEach(extension => {
|
|
404
|
-
const context = {
|
|
405
|
-
options: extension.options,
|
|
406
|
-
};
|
|
407
|
-
if (!extension.config.addAttributes) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
const attributes = extension.config.addAttributes.bind(context)();
|
|
411
|
-
Object
|
|
412
|
-
.entries(attributes)
|
|
413
|
-
.forEach(([name, attribute]) => {
|
|
414
|
-
extensionAttributes.push({
|
|
415
|
-
type: extension.config.name,
|
|
416
|
-
name,
|
|
417
|
-
attribute: {
|
|
418
|
-
...defaultAttribute,
|
|
419
|
-
...attribute,
|
|
420
|
-
},
|
|
421
|
-
});
|
|
422
|
-
});
|
|
423
|
-
});
|
|
424
|
-
return extensionAttributes;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function mergeAttributes(...objects) {
|
|
428
|
-
return objects
|
|
429
|
-
.filter(item => !!item)
|
|
430
|
-
.reduce((items, item) => {
|
|
431
|
-
const mergedAttributes = { ...items };
|
|
432
|
-
Object.entries(item).forEach(([key, value]) => {
|
|
433
|
-
const exists = mergedAttributes[key];
|
|
434
|
-
if (!exists) {
|
|
435
|
-
mergedAttributes[key] = value;
|
|
237
|
+
const clearNodes = () => ({ state, tr, dispatch }) => {
|
|
238
|
+
const { selection } = tr;
|
|
239
|
+
const { ranges } = selection;
|
|
240
|
+
ranges.forEach(range => {
|
|
241
|
+
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
|
|
242
|
+
if (node.type.isText) {
|
|
436
243
|
return;
|
|
437
244
|
}
|
|
438
|
-
|
|
439
|
-
|
|
245
|
+
const $fromPos = tr.doc.resolve(tr.mapping.map(pos));
|
|
246
|
+
const $toPos = tr.doc.resolve(tr.mapping.map(pos + node.nodeSize));
|
|
247
|
+
const nodeRange = $fromPos.blockRange($toPos);
|
|
248
|
+
if (!nodeRange) {
|
|
249
|
+
return;
|
|
440
250
|
}
|
|
441
|
-
|
|
442
|
-
|
|
251
|
+
const targetLiftDepth = prosemirrorTransform.liftTarget(nodeRange);
|
|
252
|
+
if (node.type.isTextblock && dispatch) {
|
|
253
|
+
const { defaultType } = $fromPos.parent.contentMatchAt($fromPos.index());
|
|
254
|
+
tr.setNodeMarkup(nodeRange.start, defaultType);
|
|
443
255
|
}
|
|
444
|
-
|
|
445
|
-
|
|
256
|
+
if ((targetLiftDepth || targetLiftDepth === 0) && dispatch) {
|
|
257
|
+
tr.lift(nodeRange, targetLiftDepth);
|
|
446
258
|
}
|
|
447
259
|
});
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
260
|
+
});
|
|
261
|
+
return true;
|
|
262
|
+
};
|
|
451
263
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if (!item.attribute.renderHTML) {
|
|
457
|
-
return {
|
|
458
|
-
[item.name]: nodeOrMark.attrs[item.name],
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
return item.attribute.renderHTML(nodeOrMark.attrs) || {};
|
|
462
|
-
})
|
|
463
|
-
.reduce((attributes, attribute) => {
|
|
464
|
-
return mergeAttributes(attributes, attribute);
|
|
465
|
-
}, {});
|
|
466
|
-
}
|
|
264
|
+
var clearNodes$1 = /*#__PURE__*/Object.freeze({
|
|
265
|
+
__proto__: null,
|
|
266
|
+
clearNodes: clearNodes
|
|
267
|
+
});
|
|
467
268
|
|
|
468
|
-
|
|
469
|
-
return
|
|
470
|
-
}
|
|
269
|
+
const command = fn => props => {
|
|
270
|
+
return fn(props);
|
|
271
|
+
};
|
|
471
272
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
if (value.match(/^\d*(\.\d+)?$/)) {
|
|
477
|
-
return Number(value);
|
|
478
|
-
}
|
|
479
|
-
if (value === 'true') {
|
|
480
|
-
return true;
|
|
481
|
-
}
|
|
482
|
-
if (value === 'false') {
|
|
483
|
-
return false;
|
|
484
|
-
}
|
|
485
|
-
return value;
|
|
486
|
-
}
|
|
273
|
+
var command$1 = /*#__PURE__*/Object.freeze({
|
|
274
|
+
__proto__: null,
|
|
275
|
+
command: command
|
|
276
|
+
});
|
|
487
277
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
278
|
+
const createParagraphNear = () => ({ state, dispatch }) => {
|
|
279
|
+
return prosemirrorCommands.createParagraphNear(state, dispatch);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
var createParagraphNear$1 = /*#__PURE__*/Object.freeze({
|
|
283
|
+
__proto__: null,
|
|
284
|
+
createParagraphNear: createParagraphNear
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
function getNodeType(nameOrType, schema) {
|
|
288
|
+
if (typeof nameOrType === 'string') {
|
|
289
|
+
if (!schema.nodes[nameOrType]) {
|
|
290
|
+
throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
291
|
+
}
|
|
292
|
+
return schema.nodes[nameOrType];
|
|
497
293
|
}
|
|
498
|
-
return
|
|
499
|
-
...parseRule,
|
|
500
|
-
getAttrs: node => {
|
|
501
|
-
const oldAttributes = parseRule.getAttrs
|
|
502
|
-
? parseRule.getAttrs(node)
|
|
503
|
-
: parseRule.attrs;
|
|
504
|
-
if (oldAttributes === false) {
|
|
505
|
-
return false;
|
|
506
|
-
}
|
|
507
|
-
const newAttributes = extensionAttributes
|
|
508
|
-
.filter(item => item.attribute.rendered)
|
|
509
|
-
.reduce((items, item) => {
|
|
510
|
-
const attributes = item.attribute.parseHTML
|
|
511
|
-
? item.attribute.parseHTML(node) || {}
|
|
512
|
-
: {
|
|
513
|
-
[item.name]: fromString(node.getAttribute(item.name)),
|
|
514
|
-
};
|
|
515
|
-
const filteredAttributes = Object.fromEntries(Object.entries(attributes)
|
|
516
|
-
.filter(([, value]) => value !== undefined && value !== null));
|
|
517
|
-
return {
|
|
518
|
-
...items,
|
|
519
|
-
...filteredAttributes,
|
|
520
|
-
};
|
|
521
|
-
}, {});
|
|
522
|
-
return { ...oldAttributes, ...newAttributes };
|
|
523
|
-
},
|
|
524
|
-
};
|
|
294
|
+
return nameOrType;
|
|
525
295
|
}
|
|
526
296
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
297
|
+
const deleteNode = typeOrName => ({ tr, state, dispatch }) => {
|
|
298
|
+
const type = getNodeType(typeOrName, state.schema);
|
|
299
|
+
const $pos = tr.selection.$anchor;
|
|
300
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
301
|
+
const node = $pos.node(depth);
|
|
302
|
+
if (node.type === type) {
|
|
303
|
+
if (dispatch) {
|
|
304
|
+
const from = $pos.before(depth);
|
|
305
|
+
const to = $pos.after(depth);
|
|
306
|
+
tr.delete(from, to).scrollIntoView();
|
|
307
|
+
}
|
|
308
|
+
return true;
|
|
538
309
|
}
|
|
539
|
-
return value(...props);
|
|
540
310
|
}
|
|
541
|
-
return
|
|
542
|
-
}
|
|
311
|
+
return false;
|
|
312
|
+
};
|
|
543
313
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
return value !== null && value !== undefined;
|
|
550
|
-
}));
|
|
551
|
-
}
|
|
552
|
-
function getSchema(extensions) {
|
|
553
|
-
var _a;
|
|
554
|
-
const allAttributes = getAttributesFromExtensions(extensions);
|
|
555
|
-
const { nodeExtensions, markExtensions } = splitExtensions(extensions);
|
|
556
|
-
const topNode = (_a = nodeExtensions.find(extension => extension.config.topNode)) === null || _a === void 0 ? void 0 : _a.config.name;
|
|
557
|
-
const nodeSchemaExtenders = [];
|
|
558
|
-
const markSchemaExtenders = [];
|
|
559
|
-
extensions.forEach(extension => {
|
|
560
|
-
if (typeof extension.config.extendNodeSchema === 'function') {
|
|
561
|
-
nodeSchemaExtenders.push(extension.config.extendNodeSchema);
|
|
562
|
-
}
|
|
563
|
-
if (typeof extension.config.extendMarkSchema === 'function') {
|
|
564
|
-
markSchemaExtenders.push(extension.config.extendMarkSchema);
|
|
565
|
-
}
|
|
566
|
-
});
|
|
567
|
-
const nodes = Object.fromEntries(nodeExtensions.map(extension => {
|
|
568
|
-
var _a;
|
|
569
|
-
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name);
|
|
570
|
-
const context = { options: extension.options };
|
|
571
|
-
const extraNodeFields = nodeSchemaExtenders.reduce((fields, nodeSchemaExtender) => {
|
|
572
|
-
const extraFields = callOrReturn(nodeSchemaExtender, context, extension);
|
|
573
|
-
return {
|
|
574
|
-
...fields,
|
|
575
|
-
...extraFields,
|
|
576
|
-
};
|
|
577
|
-
}, {});
|
|
578
|
-
const schema = cleanUpSchemaItem({
|
|
579
|
-
...extraNodeFields,
|
|
580
|
-
content: callOrReturn(extension.config.content, context),
|
|
581
|
-
marks: callOrReturn(extension.config.marks, context),
|
|
582
|
-
group: callOrReturn(extension.config.group, context),
|
|
583
|
-
inline: callOrReturn(extension.config.inline, context),
|
|
584
|
-
atom: callOrReturn(extension.config.atom, context),
|
|
585
|
-
selectable: callOrReturn(extension.config.selectable, context),
|
|
586
|
-
draggable: callOrReturn(extension.config.draggable, context),
|
|
587
|
-
code: callOrReturn(extension.config.code, context),
|
|
588
|
-
defining: callOrReturn(extension.config.defining, context),
|
|
589
|
-
isolating: callOrReturn(extension.config.isolating, context),
|
|
590
|
-
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
|
|
591
|
-
var _a;
|
|
592
|
-
return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
|
|
593
|
-
})),
|
|
594
|
-
});
|
|
595
|
-
if (extension.config.parseHTML) {
|
|
596
|
-
schema.parseDOM = (_a = extension.config.parseHTML
|
|
597
|
-
.bind(context)()) === null || _a === void 0 ? void 0 : _a.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
598
|
-
}
|
|
599
|
-
if (extension.config.renderHTML) {
|
|
600
|
-
schema.toDOM = node => {
|
|
601
|
-
var _a;
|
|
602
|
-
return (_a = extension.config.renderHTML) === null || _a === void 0 ? void 0 : _a.bind(context)({
|
|
603
|
-
node,
|
|
604
|
-
HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
|
|
605
|
-
});
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
return [extension.config.name, schema];
|
|
609
|
-
}));
|
|
610
|
-
const marks = Object.fromEntries(markExtensions.map(extension => {
|
|
611
|
-
var _a;
|
|
612
|
-
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.config.name);
|
|
613
|
-
const context = { options: extension.options };
|
|
614
|
-
const extraMarkFields = markSchemaExtenders.reduce((fields, markSchemaExtender) => {
|
|
615
|
-
const extraFields = callOrReturn(markSchemaExtender, context, extension);
|
|
616
|
-
return {
|
|
617
|
-
...fields,
|
|
618
|
-
...extraFields,
|
|
619
|
-
};
|
|
620
|
-
}, {});
|
|
621
|
-
const schema = cleanUpSchemaItem({
|
|
622
|
-
...extraMarkFields,
|
|
623
|
-
inclusive: callOrReturn(extension.config.inclusive, context),
|
|
624
|
-
excludes: callOrReturn(extension.config.excludes, context),
|
|
625
|
-
group: callOrReturn(extension.config.group, context),
|
|
626
|
-
spanning: callOrReturn(extension.config.spanning, context),
|
|
627
|
-
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
|
|
628
|
-
var _a;
|
|
629
|
-
return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
|
|
630
|
-
})),
|
|
631
|
-
});
|
|
632
|
-
if (extension.config.parseHTML) {
|
|
633
|
-
schema.parseDOM = (_a = extension.config.parseHTML
|
|
634
|
-
.bind(context)()) === null || _a === void 0 ? void 0 : _a.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
635
|
-
}
|
|
636
|
-
if (extension.config.renderHTML) {
|
|
637
|
-
schema.toDOM = mark => {
|
|
638
|
-
var _a;
|
|
639
|
-
return (_a = extension.config.renderHTML) === null || _a === void 0 ? void 0 : _a.bind(context)({
|
|
640
|
-
mark,
|
|
641
|
-
HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
|
|
642
|
-
});
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
return [extension.config.name, schema];
|
|
646
|
-
}));
|
|
647
|
-
return new prosemirrorModel.Schema({
|
|
648
|
-
topNode,
|
|
649
|
-
nodes,
|
|
650
|
-
marks,
|
|
651
|
-
});
|
|
652
|
-
}
|
|
314
|
+
var deleteNode$1 = /*#__PURE__*/Object.freeze({
|
|
315
|
+
__proto__: null,
|
|
316
|
+
deleteNode: deleteNode
|
|
317
|
+
});
|
|
653
318
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
319
|
+
const deleteRange = range => ({ tr, dispatch }) => {
|
|
320
|
+
const { from, to } = range;
|
|
321
|
+
if (dispatch) {
|
|
322
|
+
tr.delete(from, to);
|
|
657
323
|
}
|
|
658
|
-
|
|
659
|
-
|
|
324
|
+
return true;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
var deleteRange$1 = /*#__PURE__*/Object.freeze({
|
|
328
|
+
__proto__: null,
|
|
329
|
+
deleteRange: deleteRange
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const deleteSelection = () => ({ state, dispatch }) => {
|
|
333
|
+
return prosemirrorCommands.deleteSelection(state, dispatch);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
var deleteSelection$1 = /*#__PURE__*/Object.freeze({
|
|
337
|
+
__proto__: null,
|
|
338
|
+
deleteSelection: deleteSelection
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const enter = () => ({ commands }) => {
|
|
342
|
+
return commands.keyboardShortcut('Enter');
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
var enter$1 = /*#__PURE__*/Object.freeze({
|
|
346
|
+
__proto__: null,
|
|
347
|
+
enter: enter
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const exitCode = () => ({ state, dispatch }) => {
|
|
351
|
+
return prosemirrorCommands.exitCode(state, dispatch);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
var exitCode$1 = /*#__PURE__*/Object.freeze({
|
|
355
|
+
__proto__: null,
|
|
356
|
+
exitCode: exitCode
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
function getMarkType(nameOrType, schema) {
|
|
360
|
+
if (typeof nameOrType === 'string') {
|
|
361
|
+
if (!schema.marks[nameOrType]) {
|
|
362
|
+
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
363
|
+
}
|
|
364
|
+
return schema.marks[nameOrType];
|
|
660
365
|
}
|
|
661
|
-
return
|
|
366
|
+
return nameOrType;
|
|
662
367
|
}
|
|
663
368
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (extension.type === 'mark') {
|
|
678
|
-
const keepOnSplit = (_a = callOrReturn(extension.config.keepOnSplit, context)) !== null && _a !== void 0 ? _a : true;
|
|
679
|
-
if (keepOnSplit) {
|
|
680
|
-
this.splittableMarks.push(extension.config.name);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
if (typeof extension.config.onCreate === 'function') {
|
|
684
|
-
this.editor.on('create', extension.config.onCreate.bind(context));
|
|
685
|
-
}
|
|
686
|
-
if (typeof extension.config.onUpdate === 'function') {
|
|
687
|
-
this.editor.on('update', extension.config.onUpdate.bind(context));
|
|
688
|
-
}
|
|
689
|
-
if (typeof extension.config.onSelectionUpdate === 'function') {
|
|
690
|
-
this.editor.on('selectionUpdate', extension.config.onSelectionUpdate.bind(context));
|
|
691
|
-
}
|
|
692
|
-
if (typeof extension.config.onViewUpdate === 'function') {
|
|
693
|
-
this.editor.on('viewUpdate', extension.config.onViewUpdate.bind(context));
|
|
694
|
-
}
|
|
695
|
-
if (typeof extension.config.onTransaction === 'function') {
|
|
696
|
-
this.editor.on('transaction', extension.config.onTransaction.bind(context));
|
|
697
|
-
}
|
|
698
|
-
if (typeof extension.config.onFocus === 'function') {
|
|
699
|
-
this.editor.on('focus', extension.config.onFocus.bind(context));
|
|
700
|
-
}
|
|
701
|
-
if (typeof extension.config.onBlur === 'function') {
|
|
702
|
-
this.editor.on('blur', extension.config.onBlur.bind(context));
|
|
703
|
-
}
|
|
704
|
-
if (typeof extension.config.onDestroy === 'function') {
|
|
705
|
-
this.editor.on('destroy', extension.config.onDestroy.bind(context));
|
|
706
|
-
}
|
|
707
|
-
});
|
|
369
|
+
function isRegExp(value) {
|
|
370
|
+
return Object.prototype.toString.call(value) === '[object RegExp]';
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Check if object1 includes object2
|
|
375
|
+
* @param object1 Object
|
|
376
|
+
* @param object2 Object
|
|
377
|
+
*/
|
|
378
|
+
function objectIncludes(object1, object2, options = { strict: true }) {
|
|
379
|
+
const keys = Object.keys(object2);
|
|
380
|
+
if (!keys.length) {
|
|
381
|
+
return true;
|
|
708
382
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
383
|
+
return keys.every(key => {
|
|
384
|
+
if (options.strict) {
|
|
385
|
+
return object2[key] === object1[key];
|
|
386
|
+
}
|
|
387
|
+
if (isRegExp(object2[key])) {
|
|
388
|
+
return object2[key].test(object1[key]);
|
|
389
|
+
}
|
|
390
|
+
return object2[key] === object1[key];
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function findMarkInSet(marks, type, attributes = {}) {
|
|
395
|
+
return marks.find(item => {
|
|
396
|
+
return item.type === type && objectIncludes(item.attrs, attributes);
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function isMarkInSet(marks, type, attributes = {}) {
|
|
400
|
+
return !!findMarkInSet(marks, type, attributes);
|
|
401
|
+
}
|
|
402
|
+
function getMarkRange($pos, type, attributes = {}) {
|
|
403
|
+
if (!$pos || !type) {
|
|
404
|
+
return;
|
|
724
405
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
.map(extension => {
|
|
729
|
-
const context = {
|
|
730
|
-
options: extension.options,
|
|
731
|
-
editor: this.editor,
|
|
732
|
-
type: getSchemaTypeByName(extension.config.name, this.schema),
|
|
733
|
-
};
|
|
734
|
-
const plugins = [];
|
|
735
|
-
if (extension.config.addKeyboardShortcuts) {
|
|
736
|
-
const keyMapPlugin = prosemirrorKeymap.keymap(extension.config.addKeyboardShortcuts.bind(context)());
|
|
737
|
-
plugins.push(keyMapPlugin);
|
|
738
|
-
}
|
|
739
|
-
if (this.editor.options.enableInputRules && extension.config.addInputRules) {
|
|
740
|
-
const inputRules = extension.config.addInputRules.bind(context)();
|
|
741
|
-
const inputRulePlugins = inputRules.length
|
|
742
|
-
? [prosemirrorInputrules.inputRules({ rules: inputRules })]
|
|
743
|
-
: [];
|
|
744
|
-
plugins.push(...inputRulePlugins);
|
|
745
|
-
}
|
|
746
|
-
if (this.editor.options.enablePasteRules && extension.config.addPasteRules) {
|
|
747
|
-
const pasteRulePlugins = extension.config.addPasteRules.bind(context)();
|
|
748
|
-
plugins.push(...pasteRulePlugins);
|
|
749
|
-
}
|
|
750
|
-
if (extension.config.addProseMirrorPlugins) {
|
|
751
|
-
const proseMirrorPlugins = extension.config.addProseMirrorPlugins.bind(context)();
|
|
752
|
-
plugins.push(...proseMirrorPlugins);
|
|
753
|
-
}
|
|
754
|
-
return plugins;
|
|
755
|
-
})
|
|
756
|
-
.flat();
|
|
406
|
+
const start = $pos.parent.childAfter($pos.parentOffset);
|
|
407
|
+
if (!start.node) {
|
|
408
|
+
return;
|
|
757
409
|
}
|
|
758
|
-
|
|
759
|
-
|
|
410
|
+
const mark = findMarkInSet(start.node.marks, type, attributes);
|
|
411
|
+
if (!mark) {
|
|
412
|
+
return;
|
|
760
413
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const context = {
|
|
770
|
-
options: extension.options,
|
|
771
|
-
editor,
|
|
772
|
-
type: getNodeType(extension.config.name, this.schema),
|
|
773
|
-
};
|
|
774
|
-
const renderer = (_a = extension.config.addNodeView) === null || _a === void 0 ? void 0 : _a.call(context);
|
|
775
|
-
const nodeview = (node, view, getPos, decorations) => {
|
|
776
|
-
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes);
|
|
777
|
-
return renderer({
|
|
778
|
-
editor,
|
|
779
|
-
node,
|
|
780
|
-
getPos,
|
|
781
|
-
decorations,
|
|
782
|
-
HTMLAttributes,
|
|
783
|
-
extension,
|
|
784
|
-
});
|
|
785
|
-
};
|
|
786
|
-
return [extension.config.name, nodeview];
|
|
787
|
-
}));
|
|
414
|
+
let startIndex = $pos.index();
|
|
415
|
+
let startPos = $pos.start() + start.offset;
|
|
416
|
+
let endIndex = startIndex + 1;
|
|
417
|
+
let endPos = startPos + start.node.nodeSize;
|
|
418
|
+
findMarkInSet(start.node.marks, type, attributes);
|
|
419
|
+
while (startIndex > 0 && mark.isInSet($pos.parent.child(startIndex - 1).marks)) {
|
|
420
|
+
startIndex -= 1;
|
|
421
|
+
startPos -= $pos.parent.child(startIndex).nodeSize;
|
|
788
422
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
.filter(extension => !!extension.config.renderText)
|
|
794
|
-
.map(extension => {
|
|
795
|
-
const context = {
|
|
796
|
-
options: extension.options,
|
|
797
|
-
editor,
|
|
798
|
-
type: getNodeType(extension.config.name, this.schema),
|
|
799
|
-
};
|
|
800
|
-
const textSerializer = (props) => { var _a; return (_a = extension.config.renderText) === null || _a === void 0 ? void 0 : _a.call(context, props); };
|
|
801
|
-
return [extension.config.name, textSerializer];
|
|
802
|
-
}));
|
|
423
|
+
while (endIndex < $pos.parent.childCount
|
|
424
|
+
&& isMarkInSet($pos.parent.child(endIndex).marks, type, attributes)) {
|
|
425
|
+
endPos += $pos.parent.child(endIndex).nodeSize;
|
|
426
|
+
endIndex += 1;
|
|
803
427
|
}
|
|
428
|
+
return {
|
|
429
|
+
from: startPos,
|
|
430
|
+
to: endPos,
|
|
431
|
+
};
|
|
804
432
|
}
|
|
805
433
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
return this;
|
|
816
|
-
}
|
|
817
|
-
emit(event, ...args) {
|
|
818
|
-
const callbacks = this.callbacks[event];
|
|
819
|
-
if (callbacks) {
|
|
820
|
-
callbacks.forEach(callback => callback.apply(this, args));
|
|
821
|
-
}
|
|
822
|
-
return this;
|
|
823
|
-
}
|
|
824
|
-
off(event, fn) {
|
|
825
|
-
const callbacks = this.callbacks[event];
|
|
826
|
-
if (callbacks) {
|
|
827
|
-
if (fn) {
|
|
828
|
-
this.callbacks[event] = callbacks.filter(callback => callback !== fn);
|
|
829
|
-
}
|
|
830
|
-
else {
|
|
831
|
-
delete this.callbacks[event];
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
return this;
|
|
835
|
-
}
|
|
836
|
-
removeAllListeners() {
|
|
837
|
-
this.callbacks = {};
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/*! *****************************************************************************
|
|
842
|
-
Copyright (c) Microsoft Corporation.
|
|
843
|
-
|
|
844
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
845
|
-
purpose with or without fee is hereby granted.
|
|
846
|
-
|
|
847
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
848
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
849
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
850
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
851
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
852
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
853
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
854
|
-
***************************************************************************** */
|
|
855
|
-
|
|
856
|
-
function __classPrivateFieldGet(receiver, privateMap) {
|
|
857
|
-
if (!privateMap.has(receiver)) {
|
|
858
|
-
throw new TypeError("attempted to get private field on non-instance");
|
|
859
|
-
}
|
|
860
|
-
return privateMap.get(receiver);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
// see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts
|
|
864
|
-
function getType(payload) {
|
|
865
|
-
return Object.prototype.toString.call(payload).slice(8, -1);
|
|
866
|
-
}
|
|
867
|
-
function isPlainObject(payload) {
|
|
868
|
-
if (getType(payload) !== 'Object')
|
|
869
|
-
return false;
|
|
870
|
-
return payload.constructor === Object && Object.getPrototypeOf(payload) === Object.prototype;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
function mergeDeep(target, source) {
|
|
874
|
-
const output = { ...target };
|
|
875
|
-
if (isPlainObject(target) && isPlainObject(source)) {
|
|
876
|
-
Object.keys(source).forEach(key => {
|
|
877
|
-
if (isPlainObject(source[key])) {
|
|
878
|
-
if (!(key in target)) {
|
|
879
|
-
Object.assign(output, { [key]: source[key] });
|
|
880
|
-
}
|
|
881
|
-
else {
|
|
882
|
-
output[key] = mergeDeep(target[key], source[key]);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
else {
|
|
886
|
-
Object.assign(output, { [key]: source[key] });
|
|
887
|
-
}
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
return output;
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
var _configure$2;
|
|
894
|
-
class Extension {
|
|
895
|
-
constructor(config) {
|
|
896
|
-
this.type = 'extension';
|
|
897
|
-
this.config = {
|
|
898
|
-
name: 'extension',
|
|
899
|
-
defaultOptions: {},
|
|
900
|
-
};
|
|
901
|
-
_configure$2.set(this, (options) => {
|
|
902
|
-
this.options = mergeDeep(this.config.defaultOptions, options);
|
|
903
|
-
return this;
|
|
904
|
-
});
|
|
905
|
-
this.config = {
|
|
906
|
-
...this.config,
|
|
907
|
-
...config,
|
|
908
|
-
};
|
|
909
|
-
this.options = this.config.defaultOptions;
|
|
910
|
-
}
|
|
911
|
-
static create(config) {
|
|
912
|
-
return new Extension(config);
|
|
913
|
-
}
|
|
914
|
-
configure(options = {}) {
|
|
915
|
-
var _a;
|
|
916
|
-
return __classPrivateFieldGet((_a = Extension
|
|
917
|
-
.create(this.config)), _configure$2).call(_a, options);
|
|
918
|
-
}
|
|
919
|
-
extend(extendedConfig) {
|
|
920
|
-
return new Extension({
|
|
921
|
-
...this.config,
|
|
922
|
-
...extendedConfig,
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
_configure$2 = new WeakMap();
|
|
927
|
-
|
|
928
|
-
const textBetween = (editor, from, to, blockSeparator, leafText) => {
|
|
929
|
-
let text = '';
|
|
930
|
-
let separated = true;
|
|
931
|
-
editor.state.doc.nodesBetween(from, to, (node, pos) => {
|
|
932
|
-
var _a;
|
|
933
|
-
const textSerializer = editor.extensionManager.textSerializers[node.type.name];
|
|
934
|
-
if (textSerializer) {
|
|
935
|
-
text += textSerializer({ node });
|
|
936
|
-
separated = !blockSeparator;
|
|
937
|
-
}
|
|
938
|
-
else if (node.isText) {
|
|
939
|
-
text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
|
|
940
|
-
separated = !blockSeparator;
|
|
941
|
-
}
|
|
942
|
-
else if (node.isLeaf && leafText) {
|
|
943
|
-
text += leafText;
|
|
944
|
-
separated = !blockSeparator;
|
|
945
|
-
}
|
|
946
|
-
else if (!separated && node.isBlock) {
|
|
947
|
-
text += blockSeparator;
|
|
948
|
-
separated = true;
|
|
949
|
-
}
|
|
950
|
-
}, 0);
|
|
951
|
-
return text;
|
|
952
|
-
};
|
|
953
|
-
const ClipboardTextSerializer = Extension.create({
|
|
954
|
-
name: 'editable',
|
|
955
|
-
addProseMirrorPlugins() {
|
|
956
|
-
return [
|
|
957
|
-
new prosemirrorState.Plugin({
|
|
958
|
-
key: new prosemirrorState.PluginKey('clipboardTextSerializer'),
|
|
959
|
-
props: {
|
|
960
|
-
clipboardTextSerializer: () => {
|
|
961
|
-
const { editor } = this;
|
|
962
|
-
const { from, to } = editor.state.selection;
|
|
963
|
-
return textBetween(editor, from, to, '\n');
|
|
964
|
-
},
|
|
965
|
-
},
|
|
966
|
-
}),
|
|
967
|
-
];
|
|
968
|
-
},
|
|
969
|
-
});
|
|
970
|
-
|
|
971
|
-
const blur = () => ({ view }) => {
|
|
972
|
-
const element = view.dom;
|
|
973
|
-
element.blur();
|
|
974
|
-
return true;
|
|
975
|
-
};
|
|
976
|
-
|
|
977
|
-
var blur$1 = /*#__PURE__*/Object.freeze({
|
|
978
|
-
__proto__: null,
|
|
979
|
-
blur: blur
|
|
980
|
-
});
|
|
981
|
-
|
|
982
|
-
const clearContent = (emitUpdate = false) => ({ commands }) => {
|
|
983
|
-
return commands.setContent('', emitUpdate);
|
|
984
|
-
};
|
|
985
|
-
|
|
986
|
-
var clearContent$1 = /*#__PURE__*/Object.freeze({
|
|
987
|
-
__proto__: null,
|
|
988
|
-
clearContent: clearContent
|
|
989
|
-
});
|
|
990
|
-
|
|
991
|
-
const clearNodes = () => ({ state, tr, dispatch }) => {
|
|
992
|
-
const { selection } = tr;
|
|
993
|
-
const { ranges } = selection;
|
|
994
|
-
ranges.forEach(range => {
|
|
995
|
-
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
|
|
996
|
-
if (!node.type.isText) {
|
|
997
|
-
const fromPos = tr.doc.resolve(tr.mapping.map(pos + 1));
|
|
998
|
-
const toPos = tr.doc.resolve(tr.mapping.map(pos + node.nodeSize - 1));
|
|
999
|
-
const nodeRange = fromPos.blockRange(toPos);
|
|
1000
|
-
if (nodeRange) {
|
|
1001
|
-
const targetLiftDepth = prosemirrorTransform.liftTarget(nodeRange);
|
|
1002
|
-
if (node.type.isTextblock && dispatch) {
|
|
1003
|
-
tr.setNodeMarkup(nodeRange.start, state.doc.type.contentMatch.defaultType);
|
|
1004
|
-
}
|
|
1005
|
-
if ((targetLiftDepth || targetLiftDepth === 0) && dispatch) {
|
|
1006
|
-
tr.lift(nodeRange, targetLiftDepth);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
});
|
|
1011
|
-
});
|
|
1012
|
-
return true;
|
|
1013
|
-
};
|
|
1014
|
-
|
|
1015
|
-
var clearNodes$1 = /*#__PURE__*/Object.freeze({
|
|
1016
|
-
__proto__: null,
|
|
1017
|
-
clearNodes: clearNodes
|
|
1018
|
-
});
|
|
1019
|
-
|
|
1020
|
-
const command = fn => props => {
|
|
1021
|
-
return fn(props);
|
|
1022
|
-
};
|
|
1023
|
-
|
|
1024
|
-
var command$1 = /*#__PURE__*/Object.freeze({
|
|
1025
|
-
__proto__: null,
|
|
1026
|
-
command: command
|
|
1027
|
-
});
|
|
1028
|
-
|
|
1029
|
-
const createParagraphNear = () => ({ state, dispatch }) => {
|
|
1030
|
-
return prosemirrorCommands.createParagraphNear(state, dispatch);
|
|
1031
|
-
};
|
|
1032
|
-
|
|
1033
|
-
var createParagraphNear$1 = /*#__PURE__*/Object.freeze({
|
|
1034
|
-
__proto__: null,
|
|
1035
|
-
createParagraphNear: createParagraphNear
|
|
1036
|
-
});
|
|
1037
|
-
|
|
1038
|
-
const deleteRange = range => ({ tr, dispatch }) => {
|
|
1039
|
-
const { from, to } = range;
|
|
1040
|
-
if (dispatch) {
|
|
1041
|
-
tr.delete(from, to);
|
|
1042
|
-
}
|
|
1043
|
-
return true;
|
|
1044
|
-
};
|
|
1045
|
-
|
|
1046
|
-
var deleteRange$1 = /*#__PURE__*/Object.freeze({
|
|
1047
|
-
__proto__: null,
|
|
1048
|
-
deleteRange: deleteRange
|
|
1049
|
-
});
|
|
1050
|
-
|
|
1051
|
-
const deleteSelection = () => ({ state, dispatch }) => {
|
|
1052
|
-
return prosemirrorCommands.deleteSelection(state, dispatch);
|
|
1053
|
-
};
|
|
1054
|
-
|
|
1055
|
-
var deleteSelection$1 = /*#__PURE__*/Object.freeze({
|
|
1056
|
-
__proto__: null,
|
|
1057
|
-
deleteSelection: deleteSelection
|
|
1058
|
-
});
|
|
1059
|
-
|
|
1060
|
-
const enter = () => ({ commands }) => {
|
|
1061
|
-
return commands.keyboardShortcut('Enter');
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
var enter$1 = /*#__PURE__*/Object.freeze({
|
|
1065
|
-
__proto__: null,
|
|
1066
|
-
enter: enter
|
|
1067
|
-
});
|
|
1068
|
-
|
|
1069
|
-
const exitCode = () => ({ state, dispatch }) => {
|
|
1070
|
-
return prosemirrorCommands.exitCode(state, dispatch);
|
|
1071
|
-
};
|
|
1072
|
-
|
|
1073
|
-
var exitCode$1 = /*#__PURE__*/Object.freeze({
|
|
1074
|
-
__proto__: null,
|
|
1075
|
-
exitCode: exitCode
|
|
1076
|
-
});
|
|
1077
|
-
|
|
1078
|
-
function getMarkRange($pos, type) {
|
|
1079
|
-
if (!$pos || !type) {
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
const start = $pos.parent.childAfter($pos.parentOffset);
|
|
1083
|
-
if (!start.node) {
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
const link = start.node.marks.find(mark => mark.type === type);
|
|
1087
|
-
if (!link) {
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
let startIndex = $pos.index();
|
|
1091
|
-
let startPos = $pos.start() + start.offset;
|
|
1092
|
-
let endIndex = startIndex + 1;
|
|
1093
|
-
let endPos = startPos + start.node.nodeSize;
|
|
1094
|
-
while (startIndex > 0 && link.isInSet($pos.parent.child(startIndex - 1).marks)) {
|
|
1095
|
-
startIndex -= 1;
|
|
1096
|
-
startPos -= $pos.parent.child(startIndex).nodeSize;
|
|
1097
|
-
}
|
|
1098
|
-
while (endIndex < $pos.parent.childCount && link.isInSet($pos.parent.child(endIndex).marks)) {
|
|
1099
|
-
endPos += $pos.parent.child(endIndex).nodeSize;
|
|
1100
|
-
endIndex += 1;
|
|
1101
|
-
}
|
|
1102
|
-
return {
|
|
1103
|
-
from: startPos,
|
|
1104
|
-
to: endPos,
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
const extendMarkRange = typeOrName => ({ tr, state, dispatch }) => {
|
|
1109
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1110
|
-
const { doc, selection } = tr;
|
|
1111
|
-
const { $from, empty } = selection;
|
|
1112
|
-
if (empty && dispatch) {
|
|
1113
|
-
const range = getMarkRange($from, type);
|
|
1114
|
-
if (range) {
|
|
1115
|
-
const newSelection = prosemirrorState.TextSelection.create(doc, range.from, range.to);
|
|
1116
|
-
tr.setSelection(newSelection);
|
|
434
|
+
const extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
435
|
+
const type = getMarkType(typeOrName, state.schema);
|
|
436
|
+
const { doc, selection } = tr;
|
|
437
|
+
const { $from, from, to } = selection;
|
|
438
|
+
if (dispatch) {
|
|
439
|
+
const range = getMarkRange($from, type, attributes);
|
|
440
|
+
if (range && range.from <= from && range.to >= to) {
|
|
441
|
+
const newSelection = prosemirrorState.TextSelection.create(doc, range.from, range.to);
|
|
442
|
+
tr.setSelection(newSelection);
|
|
1117
443
|
}
|
|
1118
444
|
}
|
|
1119
445
|
return true;
|
|
@@ -1145,25 +471,38 @@ function minMax(value = 0, min = 0, max = 0) {
|
|
|
1145
471
|
return Math.min(Math.max(value, min), max);
|
|
1146
472
|
}
|
|
1147
473
|
|
|
1148
|
-
function isClass(
|
|
474
|
+
function isClass(value) {
|
|
1149
475
|
var _a;
|
|
1150
|
-
if (((_a =
|
|
476
|
+
if (((_a = value.constructor) === null || _a === void 0 ? void 0 : _a.toString().substring(0, 5)) !== 'class') {
|
|
1151
477
|
return false;
|
|
1152
478
|
}
|
|
1153
479
|
return true;
|
|
1154
480
|
}
|
|
1155
481
|
|
|
1156
|
-
function isObject(
|
|
1157
|
-
return (
|
|
1158
|
-
&& typeof
|
|
1159
|
-
&& !Array.isArray(
|
|
1160
|
-
&& !isClass(
|
|
482
|
+
function isObject(value) {
|
|
483
|
+
return (value
|
|
484
|
+
&& typeof value === 'object'
|
|
485
|
+
&& !Array.isArray(value)
|
|
486
|
+
&& !isClass(value));
|
|
1161
487
|
}
|
|
1162
488
|
|
|
1163
489
|
function isTextSelection(value) {
|
|
1164
490
|
return isObject(value) && value instanceof prosemirrorState.TextSelection;
|
|
1165
491
|
}
|
|
1166
492
|
|
|
493
|
+
function isiOS() {
|
|
494
|
+
return [
|
|
495
|
+
'iPad Simulator',
|
|
496
|
+
'iPhone Simulator',
|
|
497
|
+
'iPod Simulator',
|
|
498
|
+
'iPad',
|
|
499
|
+
'iPhone',
|
|
500
|
+
'iPod',
|
|
501
|
+
].includes(navigator.platform)
|
|
502
|
+
// iPad on iOS 13 detection
|
|
503
|
+
|| (navigator.userAgent.includes('Mac') && 'ontouchend' in document);
|
|
504
|
+
}
|
|
505
|
+
|
|
1167
506
|
function resolveSelection(state, position = null) {
|
|
1168
507
|
if (!position) {
|
|
1169
508
|
return null;
|
|
@@ -1187,28 +526,47 @@ function resolveSelection(state, position = null) {
|
|
|
1187
526
|
};
|
|
1188
527
|
}
|
|
1189
528
|
const focus = (position = null) => ({ editor, view, tr, dispatch, }) => {
|
|
529
|
+
const delayedFocus = () => {
|
|
530
|
+
// focus within `requestAnimationFrame` breaks focus on iOS
|
|
531
|
+
// so we have to call this
|
|
532
|
+
if (isiOS()) {
|
|
533
|
+
view.dom.focus();
|
|
534
|
+
}
|
|
535
|
+
// For React we have to focus asynchronously. Otherwise wild things happen.
|
|
536
|
+
// see: https://github.com/ueberdosis/tiptap/issues/1520
|
|
537
|
+
requestAnimationFrame(() => {
|
|
538
|
+
if (!editor.isDestroyed) {
|
|
539
|
+
view.focus();
|
|
540
|
+
editor.commands.scrollIntoView();
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
};
|
|
1190
544
|
if ((view.hasFocus() && position === null) || position === false) {
|
|
1191
545
|
return true;
|
|
1192
546
|
}
|
|
1193
547
|
// we don’t try to resolve a NodeSelection or CellSelection
|
|
1194
548
|
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
|
|
1195
|
-
|
|
549
|
+
delayedFocus();
|
|
1196
550
|
return true;
|
|
1197
551
|
}
|
|
1198
552
|
const { from, to } = resolveSelection(editor.state, position) || editor.state.selection;
|
|
1199
553
|
const { doc, storedMarks } = tr;
|
|
1200
|
-
const
|
|
1201
|
-
const
|
|
554
|
+
const minPos = prosemirrorState.Selection.atStart(doc).from;
|
|
555
|
+
const maxPos = prosemirrorState.Selection.atEnd(doc).to;
|
|
556
|
+
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
557
|
+
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
1202
558
|
const selection = prosemirrorState.TextSelection.create(doc, resolvedFrom, resolvedEnd);
|
|
1203
559
|
const isSameSelection = editor.state.selection.eq(selection);
|
|
1204
560
|
if (dispatch) {
|
|
1205
|
-
|
|
561
|
+
if (!isSameSelection) {
|
|
562
|
+
tr.setSelection(selection);
|
|
563
|
+
}
|
|
1206
564
|
// `tr.setSelection` resets the stored marks
|
|
1207
565
|
// so we’ll restore them if the selection is the same as before
|
|
1208
566
|
if (isSameSelection && storedMarks) {
|
|
1209
567
|
tr.setStoredMarks(storedMarks);
|
|
1210
568
|
}
|
|
1211
|
-
|
|
569
|
+
delayedFocus();
|
|
1212
570
|
}
|
|
1213
571
|
return true;
|
|
1214
572
|
};
|
|
@@ -1218,6 +576,57 @@ var focus$1 = /*#__PURE__*/Object.freeze({
|
|
|
1218
576
|
focus: focus
|
|
1219
577
|
});
|
|
1220
578
|
|
|
579
|
+
const forEach = (items, fn) => props => {
|
|
580
|
+
return items.every((item, index) => fn(item, { ...props, index }));
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
var forEach$1 = /*#__PURE__*/Object.freeze({
|
|
584
|
+
__proto__: null,
|
|
585
|
+
forEach: forEach
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
const insertContent = (value, options) => ({ tr, commands }) => {
|
|
589
|
+
return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
var insertContent$1 = /*#__PURE__*/Object.freeze({
|
|
593
|
+
__proto__: null,
|
|
594
|
+
insertContent: insertContent
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
function elementFromString(value) {
|
|
598
|
+
// add a wrapper to preserve leading and trailing whitespace
|
|
599
|
+
const wrappedValue = `<body>${value}</body>`;
|
|
600
|
+
return new window.DOMParser().parseFromString(wrappedValue, 'text/html').body;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function createNodeFromContent(content, schema, options) {
|
|
604
|
+
options = {
|
|
605
|
+
slice: true,
|
|
606
|
+
parseOptions: {},
|
|
607
|
+
...options,
|
|
608
|
+
};
|
|
609
|
+
if (typeof content === 'object' && content !== null) {
|
|
610
|
+
try {
|
|
611
|
+
if (Array.isArray(content)) {
|
|
612
|
+
return prosemirrorModel.Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)));
|
|
613
|
+
}
|
|
614
|
+
return schema.nodeFromJSON(content);
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
|
|
618
|
+
return createNodeFromContent('', schema, options);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (typeof content === 'string') {
|
|
622
|
+
const parser = prosemirrorModel.DOMParser.fromSchema(schema);
|
|
623
|
+
return options.slice
|
|
624
|
+
? parser.parseSlice(elementFromString(content), options.parseOptions).content
|
|
625
|
+
: parser.parse(elementFromString(content), options.parseOptions);
|
|
626
|
+
}
|
|
627
|
+
return createNodeFromContent('', schema, options);
|
|
628
|
+
}
|
|
629
|
+
|
|
1221
630
|
// source: https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466
|
|
1222
631
|
function selectionToInsertionEnd(tr, startLen, bias) {
|
|
1223
632
|
const last = tr.steps.length - 1;
|
|
@@ -1238,50 +647,65 @@ function selectionToInsertionEnd(tr, startLen, bias) {
|
|
|
1238
647
|
tr.setSelection(prosemirrorState.Selection.near(tr.doc.resolve(end), bias));
|
|
1239
648
|
}
|
|
1240
649
|
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
650
|
+
const isFragment = (nodeOrFragment) => {
|
|
651
|
+
return nodeOrFragment.toString().startsWith('<');
|
|
652
|
+
};
|
|
653
|
+
const insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
|
|
1245
654
|
if (dispatch) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
};
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
655
|
+
options = {
|
|
656
|
+
parseOptions: {},
|
|
657
|
+
updateSelection: true,
|
|
658
|
+
...options,
|
|
659
|
+
};
|
|
660
|
+
const content = createNodeFromContent(value, editor.schema, {
|
|
661
|
+
parseOptions: {
|
|
662
|
+
preserveWhitespace: 'full',
|
|
663
|
+
...options.parseOptions,
|
|
664
|
+
},
|
|
665
|
+
});
|
|
666
|
+
// don’t dispatch an empty fragment because this can lead to strange errors
|
|
667
|
+
if (content.toString() === '<>') {
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
let { from, to } = typeof position === 'number'
|
|
671
|
+
? { from: position, to: position }
|
|
672
|
+
: position;
|
|
673
|
+
let isOnlyBlockContent = true;
|
|
674
|
+
const nodes = isFragment(content)
|
|
675
|
+
? content
|
|
676
|
+
: [content];
|
|
677
|
+
nodes.forEach(node => {
|
|
678
|
+
isOnlyBlockContent = isOnlyBlockContent
|
|
679
|
+
? node.isBlock
|
|
680
|
+
: false;
|
|
681
|
+
});
|
|
682
|
+
// check if we can replace the wrapping node by
|
|
683
|
+
// the newly inserted content
|
|
684
|
+
// example:
|
|
685
|
+
// replace an empty paragraph by an inserted image
|
|
686
|
+
// instead of inserting the image below the paragraph
|
|
687
|
+
if (from === to && isOnlyBlockContent) {
|
|
688
|
+
const { parent } = tr.doc.resolve(from);
|
|
689
|
+
const isEmptyTextBlock = parent.isTextblock
|
|
690
|
+
&& !parent.type.spec.code
|
|
691
|
+
&& !parent.childCount;
|
|
692
|
+
if (isEmptyTextBlock) {
|
|
693
|
+
from -= 1;
|
|
694
|
+
to += 1;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
tr.replaceWith(from, to, content);
|
|
698
|
+
// set cursor at end of inserted content
|
|
699
|
+
if (options.updateSelection) {
|
|
700
|
+
selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
|
|
701
|
+
}
|
|
1278
702
|
}
|
|
1279
703
|
return true;
|
|
1280
704
|
};
|
|
1281
705
|
|
|
1282
|
-
var
|
|
706
|
+
var insertContentAt$1 = /*#__PURE__*/Object.freeze({
|
|
1283
707
|
__proto__: null,
|
|
1284
|
-
|
|
708
|
+
insertContentAt: insertContentAt
|
|
1285
709
|
});
|
|
1286
710
|
|
|
1287
711
|
const joinBackward = () => ({ state, dispatch }) => {
|
|
@@ -1384,6 +808,41 @@ var keyboardShortcut$1 = /*#__PURE__*/Object.freeze({
|
|
|
1384
808
|
keyboardShortcut: keyboardShortcut
|
|
1385
809
|
});
|
|
1386
810
|
|
|
811
|
+
function isNodeActive(state, typeOrName, attributes = {}) {
|
|
812
|
+
const { from, to, empty } = state.selection;
|
|
813
|
+
const type = typeOrName
|
|
814
|
+
? getNodeType(typeOrName, state.schema)
|
|
815
|
+
: null;
|
|
816
|
+
const nodeRanges = [];
|
|
817
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
818
|
+
if (node.isText) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const relativeFrom = Math.max(from, pos);
|
|
822
|
+
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
823
|
+
nodeRanges.push({
|
|
824
|
+
node,
|
|
825
|
+
from: relativeFrom,
|
|
826
|
+
to: relativeTo,
|
|
827
|
+
});
|
|
828
|
+
});
|
|
829
|
+
const selectionRange = to - from;
|
|
830
|
+
const matchedNodeRanges = nodeRanges
|
|
831
|
+
.filter(nodeRange => {
|
|
832
|
+
if (!type) {
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
return type.name === nodeRange.node.type.name;
|
|
836
|
+
})
|
|
837
|
+
.filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
|
|
838
|
+
if (empty) {
|
|
839
|
+
return !!matchedNodeRanges.length;
|
|
840
|
+
}
|
|
841
|
+
const range = matchedNodeRanges
|
|
842
|
+
.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
|
|
843
|
+
return range >= selectionRange;
|
|
844
|
+
}
|
|
845
|
+
|
|
1387
846
|
const lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
1388
847
|
const type = getNodeType(typeOrName, state.schema);
|
|
1389
848
|
const isActive = isNodeActive(state, type, attributes);
|
|
@@ -1426,35 +885,15 @@ var newlineInCode$1 = /*#__PURE__*/Object.freeze({
|
|
|
1426
885
|
newlineInCode: newlineInCode
|
|
1427
886
|
});
|
|
1428
887
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
return commands.replaceRange(range, typeOrName, attributes);
|
|
1433
|
-
};
|
|
1434
|
-
|
|
1435
|
-
var replace$1 = /*#__PURE__*/Object.freeze({
|
|
1436
|
-
__proto__: null,
|
|
1437
|
-
replace: replace
|
|
1438
|
-
});
|
|
1439
|
-
|
|
1440
|
-
const replaceRange = (range, typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1441
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1442
|
-
const { from, to } = range;
|
|
1443
|
-
const $from = tr.doc.resolve(from);
|
|
1444
|
-
const index = $from.index();
|
|
1445
|
-
if (!$from.parent.canReplaceWith(index, index, type)) {
|
|
1446
|
-
return false;
|
|
888
|
+
function getSchemaTypeNameByName(name, schema) {
|
|
889
|
+
if (schema.nodes[name]) {
|
|
890
|
+
return 'node';
|
|
1447
891
|
}
|
|
1448
|
-
if (
|
|
1449
|
-
|
|
892
|
+
if (schema.marks[name]) {
|
|
893
|
+
return 'mark';
|
|
1450
894
|
}
|
|
1451
|
-
return
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
var replaceRange$1 = /*#__PURE__*/Object.freeze({
|
|
1455
|
-
__proto__: null,
|
|
1456
|
-
replaceRange: replaceRange
|
|
1457
|
-
});
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
1458
897
|
|
|
1459
898
|
/**
|
|
1460
899
|
* Remove a property or an array of properties from an object
|
|
@@ -1475,23 +914,43 @@ function deleteProps(obj, propOrProps) {
|
|
|
1475
914
|
}, {});
|
|
1476
915
|
}
|
|
1477
916
|
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
917
|
+
const resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
|
|
918
|
+
let nodeType = null;
|
|
919
|
+
let markType = null;
|
|
920
|
+
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
|
|
921
|
+
? typeOrName
|
|
922
|
+
: typeOrName.name, state.schema);
|
|
923
|
+
if (!schemaType) {
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
if (schemaType === 'node') {
|
|
927
|
+
nodeType = getNodeType(typeOrName, state.schema);
|
|
928
|
+
}
|
|
929
|
+
if (schemaType === 'mark') {
|
|
930
|
+
markType = getMarkType(typeOrName, state.schema);
|
|
931
|
+
}
|
|
932
|
+
if (dispatch) {
|
|
933
|
+
tr.selection.ranges.forEach(range => {
|
|
934
|
+
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
|
|
935
|
+
if (nodeType && nodeType === node.type) {
|
|
936
|
+
tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes));
|
|
937
|
+
}
|
|
938
|
+
if (markType && node.marks.length) {
|
|
939
|
+
node.marks.forEach(mark => {
|
|
940
|
+
if (markType === mark.type) {
|
|
941
|
+
tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
});
|
|
1487
946
|
});
|
|
1488
|
-
}
|
|
947
|
+
}
|
|
1489
948
|
return true;
|
|
1490
949
|
};
|
|
1491
950
|
|
|
1492
|
-
var
|
|
951
|
+
var resetAttributes$1 = /*#__PURE__*/Object.freeze({
|
|
1493
952
|
__proto__: null,
|
|
1494
|
-
|
|
953
|
+
resetAttributes: resetAttributes
|
|
1495
954
|
});
|
|
1496
955
|
|
|
1497
956
|
const scrollIntoView = () => ({ tr, dispatch }) => {
|
|
@@ -1506,8 +965,11 @@ var scrollIntoView$1 = /*#__PURE__*/Object.freeze({
|
|
|
1506
965
|
scrollIntoView: scrollIntoView
|
|
1507
966
|
});
|
|
1508
967
|
|
|
1509
|
-
const selectAll = () => ({
|
|
1510
|
-
return
|
|
968
|
+
const selectAll = () => ({ tr, commands }) => {
|
|
969
|
+
return commands.setTextSelection({
|
|
970
|
+
from: 0,
|
|
971
|
+
to: tr.doc.content.size,
|
|
972
|
+
});
|
|
1511
973
|
};
|
|
1512
974
|
|
|
1513
975
|
var selectAll$1 = /*#__PURE__*/Object.freeze({
|
|
@@ -1542,10 +1004,13 @@ var selectParentNode$1 = /*#__PURE__*/Object.freeze({
|
|
|
1542
1004
|
selectParentNode: selectParentNode
|
|
1543
1005
|
});
|
|
1544
1006
|
|
|
1007
|
+
function createDocument(content, schema, parseOptions = {}) {
|
|
1008
|
+
return createNodeFromContent(content, schema, { slice: false, parseOptions });
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1545
1011
|
const setContent = (content, emitUpdate = false, parseOptions = {}) => ({ tr, editor, dispatch }) => {
|
|
1546
|
-
const { createDocument } = editor;
|
|
1547
1012
|
const { doc } = tr;
|
|
1548
|
-
const document = createDocument(content, parseOptions);
|
|
1013
|
+
const document = createDocument(content, editor.schema, parseOptions);
|
|
1549
1014
|
const selection = prosemirrorState.TextSelection.create(doc, 0, doc.content.size);
|
|
1550
1015
|
if (dispatch) {
|
|
1551
1016
|
tr.setSelection(selection)
|
|
@@ -1560,22 +1025,65 @@ var setContent$1 = /*#__PURE__*/Object.freeze({
|
|
|
1560
1025
|
setContent: setContent
|
|
1561
1026
|
});
|
|
1562
1027
|
|
|
1028
|
+
function getMarkAttributes(state, typeOrName) {
|
|
1029
|
+
const type = getMarkType(typeOrName, state.schema);
|
|
1030
|
+
const { from, to, empty } = state.selection;
|
|
1031
|
+
const marks = [];
|
|
1032
|
+
if (empty) {
|
|
1033
|
+
if (state.storedMarks) {
|
|
1034
|
+
marks.push(...state.storedMarks);
|
|
1035
|
+
}
|
|
1036
|
+
marks.push(...state.selection.$head.marks());
|
|
1037
|
+
}
|
|
1038
|
+
else {
|
|
1039
|
+
state.doc.nodesBetween(from, to, node => {
|
|
1040
|
+
marks.push(...node.marks);
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
const mark = marks.find(markItem => markItem.type.name === type.name);
|
|
1044
|
+
if (!mark) {
|
|
1045
|
+
return {};
|
|
1046
|
+
}
|
|
1047
|
+
return { ...mark.attrs };
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1563
1050
|
const setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1564
1051
|
const { selection } = tr;
|
|
1565
1052
|
const { empty, ranges } = selection;
|
|
1566
1053
|
const type = getMarkType(typeOrName, state.schema);
|
|
1567
|
-
const oldAttributes = getMarkAttributes(state, type);
|
|
1568
|
-
const newAttributes = {
|
|
1569
|
-
...oldAttributes,
|
|
1570
|
-
...attributes,
|
|
1571
|
-
};
|
|
1572
1054
|
if (dispatch) {
|
|
1573
1055
|
if (empty) {
|
|
1574
|
-
|
|
1056
|
+
const oldAttributes = getMarkAttributes(state, type);
|
|
1057
|
+
tr.addStoredMark(type.create({
|
|
1058
|
+
...oldAttributes,
|
|
1059
|
+
...attributes,
|
|
1060
|
+
}));
|
|
1575
1061
|
}
|
|
1576
1062
|
else {
|
|
1577
1063
|
ranges.forEach(range => {
|
|
1578
|
-
|
|
1064
|
+
const from = range.$from.pos;
|
|
1065
|
+
const to = range.$to.pos;
|
|
1066
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1067
|
+
const trimmedFrom = Math.max(pos, from);
|
|
1068
|
+
const trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
1069
|
+
const someHasMark = node.marks.find(mark => mark.type === type);
|
|
1070
|
+
// if there is already a mark of this type
|
|
1071
|
+
// we know that we have to merge its attributes
|
|
1072
|
+
// otherwise we add a fresh new mark
|
|
1073
|
+
if (someHasMark) {
|
|
1074
|
+
node.marks.forEach(mark => {
|
|
1075
|
+
if (type === mark.type) {
|
|
1076
|
+
tr.addMark(trimmedFrom, trimmedTo, type.create({
|
|
1077
|
+
...mark.attrs,
|
|
1078
|
+
...attributes,
|
|
1079
|
+
}));
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1579
1087
|
});
|
|
1580
1088
|
}
|
|
1581
1089
|
}
|
|
@@ -1587,6 +1095,16 @@ var setMark$1 = /*#__PURE__*/Object.freeze({
|
|
|
1587
1095
|
setMark: setMark
|
|
1588
1096
|
});
|
|
1589
1097
|
|
|
1098
|
+
const setMeta = (key, value) => ({ tr }) => {
|
|
1099
|
+
tr.setMeta(key, value);
|
|
1100
|
+
return true;
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
var setMeta$1 = /*#__PURE__*/Object.freeze({
|
|
1104
|
+
__proto__: null,
|
|
1105
|
+
setMeta: setMeta
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1590
1108
|
const setNode = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
1591
1109
|
const type = getNodeType(typeOrName, state.schema);
|
|
1592
1110
|
return prosemirrorCommands.setBlockType(type, attributes)(state, dispatch);
|
|
@@ -1597,6 +1115,44 @@ var setNode$1 = /*#__PURE__*/Object.freeze({
|
|
|
1597
1115
|
setNode: setNode
|
|
1598
1116
|
});
|
|
1599
1117
|
|
|
1118
|
+
const setNodeSelection = position => ({ tr, dispatch }) => {
|
|
1119
|
+
if (dispatch) {
|
|
1120
|
+
const { doc } = tr;
|
|
1121
|
+
const minPos = prosemirrorState.Selection.atStart(doc).from;
|
|
1122
|
+
const maxPos = prosemirrorState.Selection.atEnd(doc).to;
|
|
1123
|
+
const resolvedPos = minMax(position, minPos, maxPos);
|
|
1124
|
+
const selection = prosemirrorState.NodeSelection.create(doc, resolvedPos);
|
|
1125
|
+
tr.setSelection(selection);
|
|
1126
|
+
}
|
|
1127
|
+
return true;
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
var setNodeSelection$1 = /*#__PURE__*/Object.freeze({
|
|
1131
|
+
__proto__: null,
|
|
1132
|
+
setNodeSelection: setNodeSelection
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
const setTextSelection = position => ({ tr, dispatch }) => {
|
|
1136
|
+
if (dispatch) {
|
|
1137
|
+
const { doc } = tr;
|
|
1138
|
+
const { from, to } = typeof position === 'number'
|
|
1139
|
+
? { from: position, to: position }
|
|
1140
|
+
: position;
|
|
1141
|
+
const minPos = prosemirrorState.Selection.atStart(doc).from;
|
|
1142
|
+
const maxPos = prosemirrorState.Selection.atEnd(doc).to;
|
|
1143
|
+
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
1144
|
+
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
1145
|
+
const selection = prosemirrorState.TextSelection.create(doc, resolvedFrom, resolvedEnd);
|
|
1146
|
+
tr.setSelection(selection);
|
|
1147
|
+
}
|
|
1148
|
+
return true;
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
var setTextSelection$1 = /*#__PURE__*/Object.freeze({
|
|
1152
|
+
__proto__: null,
|
|
1153
|
+
setTextSelection: setTextSelection
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1600
1156
|
const sinkListItem = typeOrName => ({ state, dispatch }) => {
|
|
1601
1157
|
const type = getNodeType(typeOrName, state.schema);
|
|
1602
1158
|
return prosemirrorSchemaList.sinkListItem(type)(state, dispatch);
|
|
@@ -1622,7 +1178,7 @@ function getSplittedAttributes(extensionAttributes, typeName, attributes) {
|
|
|
1622
1178
|
}
|
|
1623
1179
|
|
|
1624
1180
|
function defaultBlockAt(match) {
|
|
1625
|
-
for (let i = 0; i < match.edgeCount; i
|
|
1181
|
+
for (let i = 0; i < match.edgeCount; i += 1) {
|
|
1626
1182
|
const { type } = match.edge(i);
|
|
1627
1183
|
if (type.isTextblock && !type.hasRequiredAttrs()) {
|
|
1628
1184
|
return type;
|
|
@@ -1686,11 +1242,15 @@ const splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor
|
|
|
1686
1242
|
}
|
|
1687
1243
|
if (can) {
|
|
1688
1244
|
tr.split(tr.mapping.map($from.pos), 1, types);
|
|
1689
|
-
if (
|
|
1245
|
+
if (deflt
|
|
1246
|
+
&& !atEnd
|
|
1690
1247
|
&& !$from.parentOffset
|
|
1691
|
-
&& $from.parent.type !== deflt
|
|
1692
|
-
|
|
1693
|
-
tr.
|
|
1248
|
+
&& $from.parent.type !== deflt) {
|
|
1249
|
+
const first = tr.mapping.map($from.before());
|
|
1250
|
+
const $first = tr.doc.resolve(first);
|
|
1251
|
+
if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
|
|
1252
|
+
tr.setNodeMarkup(tr.mapping.map($from.before()), deflt);
|
|
1253
|
+
}
|
|
1694
1254
|
}
|
|
1695
1255
|
}
|
|
1696
1256
|
if (keepMarks) {
|
|
@@ -1732,20 +1292,42 @@ const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
|
|
|
1732
1292
|
}
|
|
1733
1293
|
if (dispatch) {
|
|
1734
1294
|
let wrap = prosemirrorModel.Fragment.empty;
|
|
1735
|
-
|
|
1295
|
+
// eslint-disable-next-line
|
|
1296
|
+
const depthBefore = $from.index(-1)
|
|
1297
|
+
? 1
|
|
1298
|
+
: $from.index(-2)
|
|
1299
|
+
? 2
|
|
1300
|
+
: 3;
|
|
1736
1301
|
// Build a fragment containing empty versions of the structure
|
|
1737
1302
|
// from the outer list item to the parent node of the cursor
|
|
1738
|
-
for (let d = $from.depth -
|
|
1303
|
+
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
|
|
1739
1304
|
wrap = prosemirrorModel.Fragment.from($from.node(d).copy(wrap));
|
|
1740
1305
|
}
|
|
1306
|
+
// eslint-disable-next-line
|
|
1307
|
+
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount
|
|
1308
|
+
? 1
|
|
1309
|
+
: $from.indexAfter(-2) < $from.node(-3).childCount
|
|
1310
|
+
? 2
|
|
1311
|
+
: 3;
|
|
1741
1312
|
// Add a second list item with an empty default start node
|
|
1742
1313
|
const newNextTypeAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
|
|
1743
1314
|
const nextType = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes)) || undefined;
|
|
1744
1315
|
wrap = wrap.append(prosemirrorModel.Fragment.from(type.createAndFill(null, nextType) || undefined));
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1316
|
+
const start = $from.before($from.depth - (depthBefore - 1));
|
|
1317
|
+
tr.replace(start, $from.after(-depthAfter), new prosemirrorModel.Slice(wrap, 4 - depthBefore, 0));
|
|
1318
|
+
let sel = -1;
|
|
1319
|
+
tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {
|
|
1320
|
+
if (sel > -1) {
|
|
1321
|
+
return false;
|
|
1322
|
+
}
|
|
1323
|
+
if (n.isTextblock && n.content.size === 0) {
|
|
1324
|
+
sel = pos + 1;
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
if (sel > -1) {
|
|
1328
|
+
tr.setSelection(prosemirrorState.TextSelection.near(tr.doc.resolve(sel)));
|
|
1329
|
+
}
|
|
1330
|
+
tr.scrollIntoView();
|
|
1749
1331
|
}
|
|
1750
1332
|
return true;
|
|
1751
1333
|
}
|
|
@@ -1790,21 +1372,37 @@ function findParentNode(predicate) {
|
|
|
1790
1372
|
return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
|
|
1791
1373
|
}
|
|
1792
1374
|
|
|
1375
|
+
function splitExtensions(extensions) {
|
|
1376
|
+
const baseExtensions = extensions.filter(extension => extension.type === 'extension');
|
|
1377
|
+
const nodeExtensions = extensions.filter(extension => extension.type === 'node');
|
|
1378
|
+
const markExtensions = extensions.filter(extension => extension.type === 'mark');
|
|
1379
|
+
return {
|
|
1380
|
+
baseExtensions,
|
|
1381
|
+
nodeExtensions,
|
|
1382
|
+
markExtensions,
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1793
1386
|
function isList(name, extensions) {
|
|
1794
1387
|
const { nodeExtensions } = splitExtensions(extensions);
|
|
1795
|
-
const extension = nodeExtensions.find(item => item.
|
|
1388
|
+
const extension = nodeExtensions.find(item => item.name === name);
|
|
1796
1389
|
if (!extension) {
|
|
1797
1390
|
return false;
|
|
1798
1391
|
}
|
|
1799
|
-
const
|
|
1800
|
-
|
|
1392
|
+
const context = {
|
|
1393
|
+
name: extension.name,
|
|
1394
|
+
options: extension.options,
|
|
1395
|
+
storage: extension.storage,
|
|
1396
|
+
};
|
|
1397
|
+
const group = callOrReturn(getExtensionField(extension, 'group', context));
|
|
1398
|
+
if (typeof group !== 'string') {
|
|
1801
1399
|
return false;
|
|
1802
1400
|
}
|
|
1803
|
-
return
|
|
1401
|
+
return group.split(' ').includes('list');
|
|
1804
1402
|
}
|
|
1805
1403
|
|
|
1806
1404
|
const toggleList = (listTypeOrName, itemTypeOrName) => ({ editor, tr, state, dispatch, chain, commands, can, }) => {
|
|
1807
|
-
const { extensions } = editor.
|
|
1405
|
+
const { extensions } = editor.extensionManager;
|
|
1808
1406
|
const listType = getNodeType(listTypeOrName, state.schema);
|
|
1809
1407
|
const itemType = getNodeType(itemTypeOrName, state.schema);
|
|
1810
1408
|
const { selection } = state;
|
|
@@ -1843,11 +1441,79 @@ var toggleList$1 = /*#__PURE__*/Object.freeze({
|
|
|
1843
1441
|
toggleList: toggleList
|
|
1844
1442
|
});
|
|
1845
1443
|
|
|
1846
|
-
|
|
1444
|
+
function isMarkActive(state, typeOrName, attributes = {}) {
|
|
1445
|
+
const { empty, ranges } = state.selection;
|
|
1446
|
+
const type = typeOrName
|
|
1447
|
+
? getMarkType(typeOrName, state.schema)
|
|
1448
|
+
: null;
|
|
1449
|
+
if (empty) {
|
|
1450
|
+
return !!(state.storedMarks || state.selection.$from.marks())
|
|
1451
|
+
.filter(mark => {
|
|
1452
|
+
if (!type) {
|
|
1453
|
+
return true;
|
|
1454
|
+
}
|
|
1455
|
+
return type.name === mark.type.name;
|
|
1456
|
+
})
|
|
1457
|
+
.find(mark => objectIncludes(mark.attrs, attributes, { strict: false }));
|
|
1458
|
+
}
|
|
1459
|
+
let selectionRange = 0;
|
|
1460
|
+
const markRanges = [];
|
|
1461
|
+
ranges.forEach(({ $from, $to }) => {
|
|
1462
|
+
const from = $from.pos;
|
|
1463
|
+
const to = $to.pos;
|
|
1464
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1465
|
+
if (!node.isText && !node.marks.length) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
const relativeFrom = Math.max(from, pos);
|
|
1469
|
+
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
1470
|
+
const range = relativeTo - relativeFrom;
|
|
1471
|
+
selectionRange += range;
|
|
1472
|
+
markRanges.push(...node.marks.map(mark => ({
|
|
1473
|
+
mark,
|
|
1474
|
+
from: relativeFrom,
|
|
1475
|
+
to: relativeTo,
|
|
1476
|
+
})));
|
|
1477
|
+
});
|
|
1478
|
+
});
|
|
1479
|
+
if (selectionRange === 0) {
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
// calculate range of matched mark
|
|
1483
|
+
const matchedRange = markRanges
|
|
1484
|
+
.filter(markRange => {
|
|
1485
|
+
if (!type) {
|
|
1486
|
+
return true;
|
|
1487
|
+
}
|
|
1488
|
+
return type.name === markRange.mark.type.name;
|
|
1489
|
+
})
|
|
1490
|
+
.filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))
|
|
1491
|
+
.reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
1492
|
+
// calculate range of marks that excludes the searched mark
|
|
1493
|
+
// for example `code` doesn’t allow any other marks
|
|
1494
|
+
const excludedRange = markRanges
|
|
1495
|
+
.filter(markRange => {
|
|
1496
|
+
if (!type) {
|
|
1497
|
+
return true;
|
|
1498
|
+
}
|
|
1499
|
+
return markRange.mark.type !== type
|
|
1500
|
+
&& markRange.mark.type.excludes(type);
|
|
1501
|
+
})
|
|
1502
|
+
.reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
1503
|
+
// we only include the result of `excludedRange`
|
|
1504
|
+
// if there is a match at all
|
|
1505
|
+
const range = matchedRange > 0
|
|
1506
|
+
? matchedRange + excludedRange
|
|
1507
|
+
: matchedRange;
|
|
1508
|
+
return range >= selectionRange;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
const toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands }) => {
|
|
1512
|
+
const { extendEmptyMarkRange = false } = options;
|
|
1847
1513
|
const type = getMarkType(typeOrName, state.schema);
|
|
1848
1514
|
const isActive = isMarkActive(state, type, attributes);
|
|
1849
1515
|
if (isActive) {
|
|
1850
|
-
return commands.unsetMark(type);
|
|
1516
|
+
return commands.unsetMark(type, { extendEmptyMarkRange });
|
|
1851
1517
|
}
|
|
1852
1518
|
return commands.setMark(type, attributes);
|
|
1853
1519
|
};
|
|
@@ -1887,7 +1553,31 @@ var toggleWrap$1 = /*#__PURE__*/Object.freeze({
|
|
|
1887
1553
|
});
|
|
1888
1554
|
|
|
1889
1555
|
const undoInputRule = () => ({ state, dispatch }) => {
|
|
1890
|
-
|
|
1556
|
+
const plugins = state.plugins;
|
|
1557
|
+
for (let i = 0; i < plugins.length; i += 1) {
|
|
1558
|
+
const plugin = plugins[i];
|
|
1559
|
+
let undoable;
|
|
1560
|
+
// @ts-ignore
|
|
1561
|
+
// eslint-disable-next-line
|
|
1562
|
+
if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
|
|
1563
|
+
if (dispatch) {
|
|
1564
|
+
const tr = state.tr;
|
|
1565
|
+
const toUndo = undoable.transform;
|
|
1566
|
+
for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
|
|
1567
|
+
tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
|
|
1568
|
+
}
|
|
1569
|
+
if (undoable.text) {
|
|
1570
|
+
const marks = tr.doc.resolve(undoable.from).marks();
|
|
1571
|
+
tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
|
|
1572
|
+
}
|
|
1573
|
+
else {
|
|
1574
|
+
tr.delete(undoable.from, undoable.to);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
return true;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
return false;
|
|
1891
1581
|
};
|
|
1892
1582
|
|
|
1893
1583
|
var undoInputRule$1 = /*#__PURE__*/Object.freeze({
|
|
@@ -1918,27 +1608,31 @@ var unsetAllMarks$1 = /*#__PURE__*/Object.freeze({
|
|
|
1918
1608
|
unsetAllMarks: unsetAllMarks
|
|
1919
1609
|
});
|
|
1920
1610
|
|
|
1921
|
-
const unsetMark = typeOrName => ({ tr, state, dispatch }) => {
|
|
1611
|
+
const unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
|
|
1612
|
+
var _a;
|
|
1613
|
+
const { extendEmptyMarkRange = false } = options;
|
|
1922
1614
|
const { selection } = tr;
|
|
1923
1615
|
const type = getMarkType(typeOrName, state.schema);
|
|
1924
1616
|
const { $from, empty, ranges } = selection;
|
|
1925
|
-
if (dispatch) {
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
else {
|
|
1936
|
-
ranges.forEach(range => {
|
|
1937
|
-
tr.removeMark(range.$from.pos, range.$to.pos, type);
|
|
1938
|
-
});
|
|
1617
|
+
if (!dispatch) {
|
|
1618
|
+
return true;
|
|
1619
|
+
}
|
|
1620
|
+
if (empty && extendEmptyMarkRange) {
|
|
1621
|
+
let { from, to } = selection;
|
|
1622
|
+
const attrs = (_a = $from.marks().find(mark => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
|
|
1623
|
+
const range = getMarkRange($from, type, attrs);
|
|
1624
|
+
if (range) {
|
|
1625
|
+
from = range.from;
|
|
1626
|
+
to = range.to;
|
|
1939
1627
|
}
|
|
1940
|
-
tr.
|
|
1628
|
+
tr.removeMark(from, to, type);
|
|
1629
|
+
}
|
|
1630
|
+
else {
|
|
1631
|
+
ranges.forEach(range => {
|
|
1632
|
+
tr.removeMark(range.$from.pos, range.$to.pos, type);
|
|
1633
|
+
});
|
|
1941
1634
|
}
|
|
1635
|
+
tr.removeStoredMark(type);
|
|
1942
1636
|
return true;
|
|
1943
1637
|
};
|
|
1944
1638
|
|
|
@@ -1947,26 +1641,53 @@ var unsetMark$1 = /*#__PURE__*/Object.freeze({
|
|
|
1947
1641
|
unsetMark: unsetMark
|
|
1948
1642
|
});
|
|
1949
1643
|
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
const
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1644
|
+
const updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1645
|
+
let nodeType = null;
|
|
1646
|
+
let markType = null;
|
|
1647
|
+
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
|
|
1648
|
+
? typeOrName
|
|
1649
|
+
: typeOrName.name, state.schema);
|
|
1650
|
+
if (!schemaType) {
|
|
1651
|
+
return false;
|
|
1652
|
+
}
|
|
1653
|
+
if (schemaType === 'node') {
|
|
1654
|
+
nodeType = getNodeType(typeOrName, state.schema);
|
|
1655
|
+
}
|
|
1656
|
+
if (schemaType === 'mark') {
|
|
1657
|
+
markType = getMarkType(typeOrName, state.schema);
|
|
1658
|
+
}
|
|
1659
|
+
if (dispatch) {
|
|
1660
|
+
tr.selection.ranges.forEach(range => {
|
|
1661
|
+
const from = range.$from.pos;
|
|
1662
|
+
const to = range.$to.pos;
|
|
1663
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1664
|
+
if (nodeType && nodeType === node.type) {
|
|
1665
|
+
tr.setNodeMarkup(pos, undefined, {
|
|
1666
|
+
...node.attrs,
|
|
1667
|
+
...attributes,
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
if (markType && node.marks.length) {
|
|
1671
|
+
node.marks.forEach(mark => {
|
|
1672
|
+
if (markType === mark.type) {
|
|
1673
|
+
const trimmedFrom = Math.max(pos, from);
|
|
1674
|
+
const trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
1675
|
+
tr.addMark(trimmedFrom, trimmedTo, markType.create({
|
|
1676
|
+
...mark.attrs,
|
|
1677
|
+
...attributes,
|
|
1678
|
+
}));
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
});
|
|
1962
1683
|
});
|
|
1963
|
-
}
|
|
1684
|
+
}
|
|
1964
1685
|
return true;
|
|
1965
1686
|
};
|
|
1966
1687
|
|
|
1967
|
-
var
|
|
1688
|
+
var updateAttributes$1 = /*#__PURE__*/Object.freeze({
|
|
1968
1689
|
__proto__: null,
|
|
1969
|
-
|
|
1690
|
+
updateAttributes: updateAttributes
|
|
1970
1691
|
});
|
|
1971
1692
|
|
|
1972
1693
|
const wrapIn = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
@@ -2002,6 +1723,7 @@ const Commands = Extension.create({
|
|
|
2002
1723
|
...clearNodes$1,
|
|
2003
1724
|
...command$1,
|
|
2004
1725
|
...createParagraphNear$1,
|
|
1726
|
+
...deleteNode$1,
|
|
2005
1727
|
...deleteRange$1,
|
|
2006
1728
|
...deleteSelection$1,
|
|
2007
1729
|
...enter$1,
|
|
@@ -2009,9 +1731,9 @@ const Commands = Extension.create({
|
|
|
2009
1731
|
...extendMarkRange$1,
|
|
2010
1732
|
...first$1,
|
|
2011
1733
|
...focus$1,
|
|
2012
|
-
...
|
|
2013
|
-
...
|
|
2014
|
-
...
|
|
1734
|
+
...forEach$1,
|
|
1735
|
+
...insertContent$1,
|
|
1736
|
+
...insertContentAt$1,
|
|
2015
1737
|
...joinBackward$1,
|
|
2016
1738
|
...joinForward$1,
|
|
2017
1739
|
...keyboardShortcut$1,
|
|
@@ -2019,9 +1741,7 @@ const Commands = Extension.create({
|
|
|
2019
1741
|
...liftEmptyBlock$1,
|
|
2020
1742
|
...liftListItem$1,
|
|
2021
1743
|
...newlineInCode$1,
|
|
2022
|
-
...
|
|
2023
|
-
...replaceRange$1,
|
|
2024
|
-
...resetNodeAttributes$1,
|
|
1744
|
+
...resetAttributes$1,
|
|
2025
1745
|
...scrollIntoView$1,
|
|
2026
1746
|
...selectAll$1,
|
|
2027
1747
|
...selectNodeBackward$1,
|
|
@@ -2029,7 +1749,10 @@ const Commands = Extension.create({
|
|
|
2029
1749
|
...selectParentNode$1,
|
|
2030
1750
|
...setContent$1,
|
|
2031
1751
|
...setMark$1,
|
|
1752
|
+
...setMeta$1,
|
|
2032
1753
|
...setNode$1,
|
|
1754
|
+
...setNodeSelection$1,
|
|
1755
|
+
...setTextSelection$1,
|
|
2033
1756
|
...sinkListItem$1,
|
|
2034
1757
|
...splitBlock$1,
|
|
2035
1758
|
...splitListItem$1,
|
|
@@ -2040,7 +1763,7 @@ const Commands = Extension.create({
|
|
|
2040
1763
|
...undoInputRule$1,
|
|
2041
1764
|
...unsetAllMarks$1,
|
|
2042
1765
|
...unsetMark$1,
|
|
2043
|
-
...
|
|
1766
|
+
...updateAttributes$1,
|
|
2044
1767
|
...wrapIn$1,
|
|
2045
1768
|
...wrapInList$1,
|
|
2046
1769
|
};
|
|
@@ -2069,9 +1792,6 @@ const FocusEvents = Extension.create({
|
|
|
2069
1792
|
new prosemirrorState.Plugin({
|
|
2070
1793
|
key: new prosemirrorState.PluginKey('focusEvents'),
|
|
2071
1794
|
props: {
|
|
2072
|
-
attributes: {
|
|
2073
|
-
tabindex: '0',
|
|
2074
|
-
},
|
|
2075
1795
|
handleDOMEvents: {
|
|
2076
1796
|
focus: (view, event) => {
|
|
2077
1797
|
editor.isFocused = true;
|
|
@@ -2118,24 +1838,1108 @@ const Keymap = Extension.create({
|
|
|
2118
1838
|
() => commands.splitBlock(),
|
|
2119
1839
|
]),
|
|
2120
1840
|
'Mod-Enter': () => this.editor.commands.exitCode(),
|
|
2121
|
-
Backspace:
|
|
2122
|
-
'Mod-Backspace':
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
1841
|
+
Backspace: handleBackspace,
|
|
1842
|
+
'Mod-Backspace': handleBackspace,
|
|
1843
|
+
'Shift-Backspace': handleBackspace,
|
|
1844
|
+
Delete: handleDelete,
|
|
1845
|
+
'Mod-Delete': handleDelete,
|
|
1846
|
+
'Mod-a': () => this.editor.commands.selectAll(),
|
|
2127
1847
|
};
|
|
2128
1848
|
},
|
|
2129
1849
|
});
|
|
2130
1850
|
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
1851
|
+
const Tabindex = Extension.create({
|
|
1852
|
+
name: 'tabindex',
|
|
1853
|
+
addProseMirrorPlugins() {
|
|
1854
|
+
return [
|
|
1855
|
+
new prosemirrorState.Plugin({
|
|
1856
|
+
key: new prosemirrorState.PluginKey('tabindex'),
|
|
1857
|
+
props: {
|
|
1858
|
+
attributes: {
|
|
1859
|
+
tabindex: '0',
|
|
1860
|
+
},
|
|
1861
|
+
},
|
|
1862
|
+
}),
|
|
1863
|
+
];
|
|
1864
|
+
},
|
|
1865
|
+
});
|
|
1866
|
+
|
|
1867
|
+
var extensions = /*#__PURE__*/Object.freeze({
|
|
1868
|
+
__proto__: null,
|
|
1869
|
+
ClipboardTextSerializer: ClipboardTextSerializer,
|
|
1870
|
+
Commands: Commands,
|
|
1871
|
+
Editable: Editable,
|
|
1872
|
+
FocusEvents: FocusEvents,
|
|
1873
|
+
Keymap: Keymap,
|
|
1874
|
+
Tabindex: Tabindex
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1877
|
+
function getNodeAttributes(state, typeOrName) {
|
|
1878
|
+
const type = getNodeType(typeOrName, state.schema);
|
|
1879
|
+
const { from, to } = state.selection;
|
|
1880
|
+
const nodes = [];
|
|
1881
|
+
state.doc.nodesBetween(from, to, node => {
|
|
1882
|
+
nodes.push(node);
|
|
1883
|
+
});
|
|
1884
|
+
const node = nodes
|
|
1885
|
+
.reverse()
|
|
1886
|
+
.find(nodeItem => nodeItem.type.name === type.name);
|
|
1887
|
+
if (!node) {
|
|
1888
|
+
return {};
|
|
1889
|
+
}
|
|
1890
|
+
return { ...node.attrs };
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function getAttributes(state, typeOrName) {
|
|
1894
|
+
const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
|
|
1895
|
+
? typeOrName
|
|
1896
|
+
: typeOrName.name, state.schema);
|
|
1897
|
+
if (schemaType === 'node') {
|
|
1898
|
+
return getNodeAttributes(state, typeOrName);
|
|
1899
|
+
}
|
|
1900
|
+
if (schemaType === 'mark') {
|
|
1901
|
+
return getMarkAttributes(state, typeOrName);
|
|
1902
|
+
}
|
|
1903
|
+
return {};
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
function isActive(state, name, attributes = {}) {
|
|
1907
|
+
if (!name) {
|
|
1908
|
+
return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
|
|
1909
|
+
}
|
|
1910
|
+
const schemaType = getSchemaTypeNameByName(name, state.schema);
|
|
1911
|
+
if (schemaType === 'node') {
|
|
1912
|
+
return isNodeActive(state, name, attributes);
|
|
1913
|
+
}
|
|
1914
|
+
if (schemaType === 'mark') {
|
|
1915
|
+
return isMarkActive(state, name, attributes);
|
|
1916
|
+
}
|
|
1917
|
+
return false;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
function getHTMLFromFragment(fragment, schema) {
|
|
1921
|
+
const documentFragment = prosemirrorModel.DOMSerializer
|
|
1922
|
+
.fromSchema(schema)
|
|
1923
|
+
.serializeFragment(fragment);
|
|
1924
|
+
const temporaryDocument = document.implementation.createHTMLDocument();
|
|
1925
|
+
const container = temporaryDocument.createElement('div');
|
|
1926
|
+
container.appendChild(documentFragment);
|
|
1927
|
+
return container.innerHTML;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
function getText(node, options) {
|
|
1931
|
+
const range = {
|
|
1932
|
+
from: 0,
|
|
1933
|
+
to: node.content.size,
|
|
1934
|
+
};
|
|
1935
|
+
return getTextBetween(node, range, options);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
function isNodeEmpty(node) {
|
|
1939
|
+
var _a;
|
|
1940
|
+
const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
|
|
1941
|
+
const content = node.toJSON();
|
|
1942
|
+
return JSON.stringify(defaultContent) === JSON.stringify(content);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
function createStyleTag(style) {
|
|
1946
|
+
const tipTapStyleTag = document.querySelector('style[data-tiptap-style]');
|
|
1947
|
+
if (tipTapStyleTag !== null) {
|
|
1948
|
+
return tipTapStyleTag;
|
|
1949
|
+
}
|
|
1950
|
+
const styleNode = document.createElement('style');
|
|
1951
|
+
styleNode.setAttribute('data-tiptap-style', '');
|
|
1952
|
+
styleNode.innerHTML = style;
|
|
1953
|
+
document.getElementsByTagName('head')[0].appendChild(styleNode);
|
|
1954
|
+
return styleNode;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
function createChainableState(config) {
|
|
1958
|
+
const { state, transaction } = config;
|
|
1959
|
+
let { selection } = transaction;
|
|
1960
|
+
let { doc } = transaction;
|
|
1961
|
+
let { storedMarks } = transaction;
|
|
1962
|
+
return {
|
|
1963
|
+
...state,
|
|
1964
|
+
schema: state.schema,
|
|
1965
|
+
plugins: state.plugins,
|
|
1966
|
+
apply: state.apply.bind(state),
|
|
1967
|
+
applyTransaction: state.applyTransaction.bind(state),
|
|
1968
|
+
reconfigure: state.reconfigure.bind(state),
|
|
1969
|
+
toJSON: state.toJSON.bind(state),
|
|
1970
|
+
get storedMarks() {
|
|
1971
|
+
return storedMarks;
|
|
1972
|
+
},
|
|
1973
|
+
get selection() {
|
|
1974
|
+
return selection;
|
|
1975
|
+
},
|
|
1976
|
+
get doc() {
|
|
1977
|
+
return doc;
|
|
1978
|
+
},
|
|
1979
|
+
get tr() {
|
|
1980
|
+
selection = transaction.selection;
|
|
1981
|
+
doc = transaction.doc;
|
|
1982
|
+
storedMarks = transaction.storedMarks;
|
|
1983
|
+
return transaction;
|
|
1984
|
+
},
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
class CommandManager {
|
|
1989
|
+
constructor(props) {
|
|
1990
|
+
this.editor = props.editor;
|
|
1991
|
+
this.rawCommands = this.editor.extensionManager.commands;
|
|
1992
|
+
this.customState = props.state;
|
|
1993
|
+
}
|
|
1994
|
+
get hasCustomState() {
|
|
1995
|
+
return !!this.customState;
|
|
1996
|
+
}
|
|
1997
|
+
get state() {
|
|
1998
|
+
return this.customState || this.editor.state;
|
|
1999
|
+
}
|
|
2000
|
+
get commands() {
|
|
2001
|
+
const { rawCommands, editor, state } = this;
|
|
2002
|
+
const { view } = editor;
|
|
2003
|
+
const { tr } = state;
|
|
2004
|
+
const props = this.buildProps(tr);
|
|
2005
|
+
return Object.fromEntries(Object
|
|
2006
|
+
.entries(rawCommands)
|
|
2007
|
+
.map(([name, command]) => {
|
|
2008
|
+
const method = (...args) => {
|
|
2009
|
+
const callback = command(...args)(props);
|
|
2010
|
+
if (!tr.getMeta('preventDispatch') && !this.hasCustomState) {
|
|
2011
|
+
view.dispatch(tr);
|
|
2012
|
+
}
|
|
2013
|
+
return callback;
|
|
2014
|
+
};
|
|
2015
|
+
return [name, method];
|
|
2016
|
+
}));
|
|
2017
|
+
}
|
|
2018
|
+
get chain() {
|
|
2019
|
+
return () => this.createChain();
|
|
2020
|
+
}
|
|
2021
|
+
get can() {
|
|
2022
|
+
return () => this.createCan();
|
|
2023
|
+
}
|
|
2024
|
+
createChain(startTr, shouldDispatch = true) {
|
|
2025
|
+
const { rawCommands, editor, state } = this;
|
|
2026
|
+
const { view } = editor;
|
|
2027
|
+
const callbacks = [];
|
|
2028
|
+
const hasStartTransaction = !!startTr;
|
|
2029
|
+
const tr = startTr || state.tr;
|
|
2030
|
+
const run = () => {
|
|
2031
|
+
if (!hasStartTransaction
|
|
2032
|
+
&& shouldDispatch
|
|
2033
|
+
&& !tr.getMeta('preventDispatch')
|
|
2034
|
+
&& !this.hasCustomState) {
|
|
2035
|
+
view.dispatch(tr);
|
|
2036
|
+
}
|
|
2037
|
+
return callbacks.every(callback => callback === true);
|
|
2038
|
+
};
|
|
2039
|
+
const chain = {
|
|
2040
|
+
...Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
|
|
2041
|
+
const chainedCommand = (...args) => {
|
|
2042
|
+
const props = this.buildProps(tr, shouldDispatch);
|
|
2043
|
+
const callback = command(...args)(props);
|
|
2044
|
+
callbacks.push(callback);
|
|
2045
|
+
return chain;
|
|
2046
|
+
};
|
|
2047
|
+
return [name, chainedCommand];
|
|
2048
|
+
})),
|
|
2049
|
+
run,
|
|
2050
|
+
};
|
|
2051
|
+
return chain;
|
|
2052
|
+
}
|
|
2053
|
+
createCan(startTr) {
|
|
2054
|
+
const { rawCommands, state } = this;
|
|
2055
|
+
const dispatch = undefined;
|
|
2056
|
+
const tr = startTr || state.tr;
|
|
2057
|
+
const props = this.buildProps(tr, dispatch);
|
|
2058
|
+
const formattedCommands = Object.fromEntries(Object
|
|
2059
|
+
.entries(rawCommands)
|
|
2060
|
+
.map(([name, command]) => {
|
|
2061
|
+
return [name, (...args) => command(...args)({ ...props, dispatch })];
|
|
2062
|
+
}));
|
|
2063
|
+
return {
|
|
2064
|
+
...formattedCommands,
|
|
2065
|
+
chain: () => this.createChain(tr, dispatch),
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
buildProps(tr, shouldDispatch = true) {
|
|
2069
|
+
const { rawCommands, editor, state } = this;
|
|
2070
|
+
const { view } = editor;
|
|
2071
|
+
if (state.storedMarks) {
|
|
2072
|
+
tr.setStoredMarks(state.storedMarks);
|
|
2073
|
+
}
|
|
2074
|
+
const props = {
|
|
2075
|
+
tr,
|
|
2076
|
+
editor,
|
|
2077
|
+
view,
|
|
2078
|
+
state: createChainableState({
|
|
2079
|
+
state,
|
|
2080
|
+
transaction: tr,
|
|
2081
|
+
}),
|
|
2082
|
+
dispatch: shouldDispatch
|
|
2083
|
+
? () => undefined
|
|
2084
|
+
: undefined,
|
|
2085
|
+
chain: () => this.createChain(tr),
|
|
2086
|
+
can: () => this.createCan(tr),
|
|
2087
|
+
get commands() {
|
|
2088
|
+
return Object.fromEntries(Object
|
|
2089
|
+
.entries(rawCommands)
|
|
2090
|
+
.map(([name, command]) => {
|
|
2091
|
+
return [name, (...args) => command(...args)(props)];
|
|
2092
|
+
}));
|
|
2093
|
+
},
|
|
2094
|
+
};
|
|
2095
|
+
return props;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
class InputRule {
|
|
2100
|
+
constructor(config) {
|
|
2101
|
+
this.find = config.find;
|
|
2102
|
+
this.handler = config.handler;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
const inputRuleMatcherHandler = (text, find) => {
|
|
2106
|
+
if (isRegExp(find)) {
|
|
2107
|
+
return find.exec(text);
|
|
2108
|
+
}
|
|
2109
|
+
const inputRuleMatch = find(text);
|
|
2110
|
+
if (!inputRuleMatch) {
|
|
2111
|
+
return null;
|
|
2112
|
+
}
|
|
2113
|
+
const result = [];
|
|
2114
|
+
result.push(inputRuleMatch.text);
|
|
2115
|
+
result.index = inputRuleMatch.index;
|
|
2116
|
+
result.input = text;
|
|
2117
|
+
result.data = inputRuleMatch.data;
|
|
2118
|
+
if (inputRuleMatch.replaceWith) {
|
|
2119
|
+
if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {
|
|
2120
|
+
console.warn('[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".');
|
|
2121
|
+
}
|
|
2122
|
+
result.push(inputRuleMatch.replaceWith);
|
|
2123
|
+
}
|
|
2124
|
+
return result;
|
|
2125
|
+
};
|
|
2126
|
+
function run$1(config) {
|
|
2127
|
+
var _a;
|
|
2128
|
+
const { editor, from, to, text, rules, plugin, } = config;
|
|
2129
|
+
const { view } = editor;
|
|
2130
|
+
if (view.composing) {
|
|
2131
|
+
return false;
|
|
2132
|
+
}
|
|
2133
|
+
const $from = view.state.doc.resolve(from);
|
|
2134
|
+
if (
|
|
2135
|
+
// check for code node
|
|
2136
|
+
$from.parent.type.spec.code
|
|
2137
|
+
// check for code mark
|
|
2138
|
+
|| !!((_a = ($from.nodeBefore || $from.nodeAfter)) === null || _a === void 0 ? void 0 : _a.marks.find(mark => mark.type.spec.code))) {
|
|
2139
|
+
return false;
|
|
2140
|
+
}
|
|
2141
|
+
let matched = false;
|
|
2142
|
+
const maxMatch = 500;
|
|
2143
|
+
const textBefore = $from.parent.textBetween(Math.max(0, $from.parentOffset - maxMatch), $from.parentOffset, undefined, '\ufffc') + text;
|
|
2144
|
+
rules.forEach(rule => {
|
|
2145
|
+
if (matched) {
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
const match = inputRuleMatcherHandler(textBefore, rule.find);
|
|
2149
|
+
if (!match) {
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
const tr = view.state.tr;
|
|
2153
|
+
const state = createChainableState({
|
|
2154
|
+
state: view.state,
|
|
2155
|
+
transaction: tr,
|
|
2156
|
+
});
|
|
2157
|
+
const range = {
|
|
2158
|
+
from: from - (match[0].length - text.length),
|
|
2159
|
+
to,
|
|
2160
|
+
};
|
|
2161
|
+
const { commands, chain, can } = new CommandManager({
|
|
2162
|
+
editor,
|
|
2163
|
+
state,
|
|
2164
|
+
});
|
|
2165
|
+
rule.handler({
|
|
2166
|
+
state,
|
|
2167
|
+
range,
|
|
2168
|
+
match,
|
|
2169
|
+
commands,
|
|
2170
|
+
chain,
|
|
2171
|
+
can,
|
|
2172
|
+
});
|
|
2173
|
+
// stop if there are no changes
|
|
2174
|
+
if (!tr.steps.length) {
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
// store transform as meta data
|
|
2178
|
+
// so we can undo input rules within the `undoInputRules` command
|
|
2179
|
+
tr.setMeta(plugin, {
|
|
2180
|
+
transform: tr,
|
|
2181
|
+
from,
|
|
2182
|
+
to,
|
|
2183
|
+
text,
|
|
2184
|
+
});
|
|
2185
|
+
view.dispatch(tr);
|
|
2186
|
+
matched = true;
|
|
2187
|
+
});
|
|
2188
|
+
return matched;
|
|
2189
|
+
}
|
|
2190
|
+
/**
|
|
2191
|
+
* Create an input rules plugin. When enabled, it will cause text
|
|
2192
|
+
* input that matches any of the given rules to trigger the rule’s
|
|
2193
|
+
* action.
|
|
2194
|
+
*/
|
|
2195
|
+
function inputRulesPlugin(props) {
|
|
2196
|
+
const { editor, rules } = props;
|
|
2197
|
+
const plugin = new prosemirrorState.Plugin({
|
|
2198
|
+
state: {
|
|
2199
|
+
init() {
|
|
2200
|
+
return null;
|
|
2201
|
+
},
|
|
2202
|
+
apply(tr, prev) {
|
|
2203
|
+
const stored = tr.getMeta(this);
|
|
2204
|
+
if (stored) {
|
|
2205
|
+
return stored;
|
|
2206
|
+
}
|
|
2207
|
+
return tr.selectionSet || tr.docChanged
|
|
2208
|
+
? null
|
|
2209
|
+
: prev;
|
|
2210
|
+
},
|
|
2211
|
+
},
|
|
2212
|
+
props: {
|
|
2213
|
+
handleTextInput(view, from, to, text) {
|
|
2214
|
+
return run$1({
|
|
2215
|
+
editor,
|
|
2216
|
+
from,
|
|
2217
|
+
to,
|
|
2218
|
+
text,
|
|
2219
|
+
rules,
|
|
2220
|
+
plugin,
|
|
2221
|
+
});
|
|
2222
|
+
},
|
|
2223
|
+
handleDOMEvents: {
|
|
2224
|
+
compositionend: view => {
|
|
2225
|
+
setTimeout(() => {
|
|
2226
|
+
const { $cursor } = view.state.selection;
|
|
2227
|
+
if ($cursor) {
|
|
2228
|
+
run$1({
|
|
2229
|
+
editor,
|
|
2230
|
+
from: $cursor.pos,
|
|
2231
|
+
to: $cursor.pos,
|
|
2232
|
+
text: '',
|
|
2233
|
+
rules,
|
|
2234
|
+
plugin,
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
});
|
|
2238
|
+
return false;
|
|
2239
|
+
},
|
|
2240
|
+
},
|
|
2241
|
+
// add support for input rules to trigger on enter
|
|
2242
|
+
// this is useful for example for code blocks
|
|
2243
|
+
handleKeyDown(view, event) {
|
|
2244
|
+
if (event.key !== 'Enter') {
|
|
2245
|
+
return false;
|
|
2246
|
+
}
|
|
2247
|
+
const { $cursor } = view.state.selection;
|
|
2248
|
+
if ($cursor) {
|
|
2249
|
+
return run$1({
|
|
2250
|
+
editor,
|
|
2251
|
+
from: $cursor.pos,
|
|
2252
|
+
to: $cursor.pos,
|
|
2253
|
+
text: '\n',
|
|
2254
|
+
rules,
|
|
2255
|
+
plugin,
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
return false;
|
|
2259
|
+
},
|
|
2260
|
+
},
|
|
2261
|
+
// @ts-ignore
|
|
2262
|
+
isInputRules: true,
|
|
2263
|
+
});
|
|
2264
|
+
return plugin;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
function isNumber(value) {
|
|
2268
|
+
return typeof value === 'number';
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
class PasteRule {
|
|
2272
|
+
constructor(config) {
|
|
2273
|
+
this.find = config.find;
|
|
2274
|
+
this.handler = config.handler;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
const pasteRuleMatcherHandler = (text, find) => {
|
|
2278
|
+
if (isRegExp(find)) {
|
|
2279
|
+
return [...text.matchAll(find)];
|
|
2280
|
+
}
|
|
2281
|
+
const matches = find(text);
|
|
2282
|
+
if (!matches) {
|
|
2283
|
+
return [];
|
|
2284
|
+
}
|
|
2285
|
+
return matches.map(pasteRuleMatch => {
|
|
2286
|
+
const result = [];
|
|
2287
|
+
result.push(pasteRuleMatch.text);
|
|
2288
|
+
result.index = pasteRuleMatch.index;
|
|
2289
|
+
result.input = text;
|
|
2290
|
+
result.data = pasteRuleMatch.data;
|
|
2291
|
+
if (pasteRuleMatch.replaceWith) {
|
|
2292
|
+
if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {
|
|
2293
|
+
console.warn('[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".');
|
|
2294
|
+
}
|
|
2295
|
+
result.push(pasteRuleMatch.replaceWith);
|
|
2296
|
+
}
|
|
2297
|
+
return result;
|
|
2298
|
+
});
|
|
2299
|
+
};
|
|
2300
|
+
function run(config) {
|
|
2301
|
+
const { editor, state, from, to, rules, } = config;
|
|
2302
|
+
const { commands, chain, can } = new CommandManager({
|
|
2303
|
+
editor,
|
|
2304
|
+
state,
|
|
2305
|
+
});
|
|
2306
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
2307
|
+
if (!node.isTextblock || node.type.spec.code) {
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
2310
|
+
const resolvedFrom = Math.max(from, pos);
|
|
2311
|
+
const resolvedTo = Math.min(to, pos + node.content.size);
|
|
2312
|
+
const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\ufffc');
|
|
2313
|
+
rules.forEach(rule => {
|
|
2314
|
+
const matches = pasteRuleMatcherHandler(textToMatch, rule.find);
|
|
2315
|
+
matches.forEach(match => {
|
|
2316
|
+
if (match.index === undefined) {
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
const start = resolvedFrom + match.index + 1;
|
|
2320
|
+
const end = start + match[0].length;
|
|
2321
|
+
const range = {
|
|
2322
|
+
from: state.tr.mapping.map(start),
|
|
2323
|
+
to: state.tr.mapping.map(end),
|
|
2324
|
+
};
|
|
2325
|
+
rule.handler({
|
|
2326
|
+
state,
|
|
2327
|
+
range,
|
|
2328
|
+
match,
|
|
2329
|
+
commands,
|
|
2330
|
+
chain,
|
|
2331
|
+
can,
|
|
2332
|
+
});
|
|
2333
|
+
});
|
|
2334
|
+
});
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Create an paste rules plugin. When enabled, it will cause pasted
|
|
2339
|
+
* text that matches any of the given rules to trigger the rule’s
|
|
2340
|
+
* action.
|
|
2341
|
+
*/
|
|
2342
|
+
function pasteRulesPlugin(props) {
|
|
2343
|
+
const { editor, rules } = props;
|
|
2344
|
+
let isProseMirrorHTML = false;
|
|
2345
|
+
const plugin = new prosemirrorState.Plugin({
|
|
2346
|
+
props: {
|
|
2347
|
+
handlePaste: (view, event) => {
|
|
2348
|
+
var _a;
|
|
2349
|
+
const html = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text/html');
|
|
2350
|
+
isProseMirrorHTML = !!(html === null || html === void 0 ? void 0 : html.includes('data-pm-slice'));
|
|
2351
|
+
return false;
|
|
2352
|
+
},
|
|
2353
|
+
},
|
|
2354
|
+
appendTransaction: (transactions, oldState, state) => {
|
|
2355
|
+
const transaction = transactions[0];
|
|
2356
|
+
// stop if there is not a paste event
|
|
2357
|
+
if (!transaction.getMeta('paste') || isProseMirrorHTML) {
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
// stop if there is no changed range
|
|
2361
|
+
const { doc, before } = transaction;
|
|
2362
|
+
const from = before.content.findDiffStart(doc.content);
|
|
2363
|
+
const to = before.content.findDiffEnd(doc.content);
|
|
2364
|
+
if (!isNumber(from) || !to || from === to.b) {
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
// build a chainable state
|
|
2368
|
+
// so we can use a single transaction for all paste rules
|
|
2369
|
+
const tr = state.tr;
|
|
2370
|
+
const chainableState = createChainableState({
|
|
2371
|
+
state,
|
|
2372
|
+
transaction: tr,
|
|
2373
|
+
});
|
|
2374
|
+
run({
|
|
2375
|
+
editor,
|
|
2376
|
+
state: chainableState,
|
|
2377
|
+
from: Math.max(from - 1, 0),
|
|
2378
|
+
to: to.b,
|
|
2379
|
+
rules,
|
|
2380
|
+
plugin,
|
|
2381
|
+
});
|
|
2382
|
+
// stop if there are no changes
|
|
2383
|
+
if (!tr.steps.length) {
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
return tr;
|
|
2387
|
+
},
|
|
2388
|
+
// @ts-ignore
|
|
2389
|
+
isPasteRules: true,
|
|
2390
|
+
});
|
|
2391
|
+
return plugin;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
/**
|
|
2395
|
+
* Get a list of all extension attributes defined in `addAttribute` and `addGlobalAttribute`.
|
|
2396
|
+
* @param extensions List of extensions
|
|
2397
|
+
*/
|
|
2398
|
+
function getAttributesFromExtensions(extensions) {
|
|
2399
|
+
const extensionAttributes = [];
|
|
2400
|
+
const { nodeExtensions, markExtensions } = splitExtensions(extensions);
|
|
2401
|
+
const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions];
|
|
2402
|
+
const defaultAttribute = {
|
|
2403
|
+
default: null,
|
|
2404
|
+
rendered: true,
|
|
2405
|
+
renderHTML: null,
|
|
2406
|
+
parseHTML: null,
|
|
2407
|
+
keepOnSplit: true,
|
|
2408
|
+
};
|
|
2409
|
+
extensions.forEach(extension => {
|
|
2410
|
+
const context = {
|
|
2411
|
+
name: extension.name,
|
|
2412
|
+
options: extension.options,
|
|
2413
|
+
storage: extension.storage,
|
|
2414
|
+
};
|
|
2415
|
+
const addGlobalAttributes = getExtensionField(extension, 'addGlobalAttributes', context);
|
|
2416
|
+
if (!addGlobalAttributes) {
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
// TODO: remove `as GlobalAttributes`
|
|
2420
|
+
const globalAttributes = addGlobalAttributes();
|
|
2421
|
+
globalAttributes.forEach(globalAttribute => {
|
|
2422
|
+
globalAttribute.types.forEach(type => {
|
|
2423
|
+
Object
|
|
2424
|
+
.entries(globalAttribute.attributes)
|
|
2425
|
+
.forEach(([name, attribute]) => {
|
|
2426
|
+
extensionAttributes.push({
|
|
2427
|
+
type,
|
|
2428
|
+
name,
|
|
2429
|
+
attribute: {
|
|
2430
|
+
...defaultAttribute,
|
|
2431
|
+
...attribute,
|
|
2432
|
+
},
|
|
2433
|
+
});
|
|
2434
|
+
});
|
|
2435
|
+
});
|
|
2436
|
+
});
|
|
2437
|
+
});
|
|
2438
|
+
nodeAndMarkExtensions.forEach(extension => {
|
|
2439
|
+
const context = {
|
|
2440
|
+
name: extension.name,
|
|
2441
|
+
options: extension.options,
|
|
2442
|
+
storage: extension.storage,
|
|
2443
|
+
};
|
|
2444
|
+
const addAttributes = getExtensionField(extension, 'addAttributes', context);
|
|
2445
|
+
if (!addAttributes) {
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
// TODO: remove `as Attributes`
|
|
2449
|
+
const attributes = addAttributes();
|
|
2450
|
+
Object
|
|
2451
|
+
.entries(attributes)
|
|
2452
|
+
.forEach(([name, attribute]) => {
|
|
2453
|
+
extensionAttributes.push({
|
|
2454
|
+
type: extension.name,
|
|
2455
|
+
name,
|
|
2456
|
+
attribute: {
|
|
2457
|
+
...defaultAttribute,
|
|
2458
|
+
...attribute,
|
|
2459
|
+
},
|
|
2460
|
+
});
|
|
2461
|
+
});
|
|
2462
|
+
});
|
|
2463
|
+
return extensionAttributes;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
function mergeAttributes(...objects) {
|
|
2467
|
+
return objects
|
|
2468
|
+
.filter(item => !!item)
|
|
2469
|
+
.reduce((items, item) => {
|
|
2470
|
+
const mergedAttributes = { ...items };
|
|
2471
|
+
Object.entries(item).forEach(([key, value]) => {
|
|
2472
|
+
const exists = mergedAttributes[key];
|
|
2473
|
+
if (!exists) {
|
|
2474
|
+
mergedAttributes[key] = value;
|
|
2475
|
+
return;
|
|
2476
|
+
}
|
|
2477
|
+
if (key === 'class') {
|
|
2478
|
+
mergedAttributes[key] = [mergedAttributes[key], value].join(' ');
|
|
2479
|
+
}
|
|
2480
|
+
else if (key === 'style') {
|
|
2481
|
+
mergedAttributes[key] = [mergedAttributes[key], value].join('; ');
|
|
2482
|
+
}
|
|
2483
|
+
else {
|
|
2484
|
+
mergedAttributes[key] = value;
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2487
|
+
return mergedAttributes;
|
|
2488
|
+
}, {});
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
function getRenderedAttributes(nodeOrMark, extensionAttributes) {
|
|
2492
|
+
return extensionAttributes
|
|
2493
|
+
.filter(item => item.attribute.rendered)
|
|
2494
|
+
.map(item => {
|
|
2495
|
+
if (!item.attribute.renderHTML) {
|
|
2496
|
+
return {
|
|
2497
|
+
[item.name]: nodeOrMark.attrs[item.name],
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
return item.attribute.renderHTML(nodeOrMark.attrs) || {};
|
|
2501
|
+
})
|
|
2502
|
+
.reduce((attributes, attribute) => mergeAttributes(attributes, attribute), {});
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
function isEmptyObject(value = {}) {
|
|
2506
|
+
return Object.keys(value).length === 0 && value.constructor === Object;
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
function fromString(value) {
|
|
2510
|
+
if (typeof value !== 'string') {
|
|
2511
|
+
return value;
|
|
2512
|
+
}
|
|
2513
|
+
if (value.match(/^[+-]?(?:\d*\.)?\d+$/)) {
|
|
2514
|
+
return Number(value);
|
|
2515
|
+
}
|
|
2516
|
+
if (value === 'true') {
|
|
2517
|
+
return true;
|
|
2518
|
+
}
|
|
2519
|
+
if (value === 'false') {
|
|
2520
|
+
return false;
|
|
2521
|
+
}
|
|
2522
|
+
return value;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
/**
|
|
2526
|
+
* This function merges extension attributes into parserule attributes (`attrs` or `getAttrs`).
|
|
2527
|
+
* Cancels when `getAttrs` returned `false`.
|
|
2528
|
+
* @param parseRule ProseMirror ParseRule
|
|
2529
|
+
* @param extensionAttributes List of attributes to inject
|
|
2530
|
+
*/
|
|
2531
|
+
function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
|
|
2532
|
+
if (parseRule.style) {
|
|
2533
|
+
return parseRule;
|
|
2534
|
+
}
|
|
2535
|
+
return {
|
|
2536
|
+
...parseRule,
|
|
2537
|
+
getAttrs: node => {
|
|
2538
|
+
const oldAttributes = parseRule.getAttrs
|
|
2539
|
+
? parseRule.getAttrs(node)
|
|
2540
|
+
: parseRule.attrs;
|
|
2541
|
+
if (oldAttributes === false) {
|
|
2542
|
+
return false;
|
|
2543
|
+
}
|
|
2544
|
+
const newAttributes = extensionAttributes
|
|
2545
|
+
.filter(item => item.attribute.rendered)
|
|
2546
|
+
.reduce((items, item) => {
|
|
2547
|
+
const value = item.attribute.parseHTML
|
|
2548
|
+
? item.attribute.parseHTML(node)
|
|
2549
|
+
: fromString(node.getAttribute(item.name));
|
|
2550
|
+
if (isObject(value)) {
|
|
2551
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "parseHTML" for your attribute "${item.name}" returns an object but should return the value itself. If this is expected you can ignore this message. This warning will be removed in one of the next releases. Further information: https://github.com/ueberdosis/tiptap/issues/1863`);
|
|
2552
|
+
}
|
|
2553
|
+
if (value === null || value === undefined) {
|
|
2554
|
+
return items;
|
|
2555
|
+
}
|
|
2556
|
+
return {
|
|
2557
|
+
...items,
|
|
2558
|
+
[item.name]: value,
|
|
2559
|
+
};
|
|
2560
|
+
}, {});
|
|
2561
|
+
return { ...oldAttributes, ...newAttributes };
|
|
2562
|
+
},
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
function cleanUpSchemaItem(data) {
|
|
2567
|
+
return Object.fromEntries(Object.entries(data).filter(([key, value]) => {
|
|
2568
|
+
if (key === 'attrs' && isEmptyObject(value)) {
|
|
2569
|
+
return false;
|
|
2570
|
+
}
|
|
2571
|
+
return value !== null && value !== undefined;
|
|
2572
|
+
}));
|
|
2573
|
+
}
|
|
2574
|
+
function getSchemaByResolvedExtensions(extensions) {
|
|
2575
|
+
var _a;
|
|
2576
|
+
const allAttributes = getAttributesFromExtensions(extensions);
|
|
2577
|
+
const { nodeExtensions, markExtensions } = splitExtensions(extensions);
|
|
2578
|
+
const topNode = (_a = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))) === null || _a === void 0 ? void 0 : _a.name;
|
|
2579
|
+
const nodes = Object.fromEntries(nodeExtensions.map(extension => {
|
|
2580
|
+
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name);
|
|
2581
|
+
const context = {
|
|
2582
|
+
name: extension.name,
|
|
2583
|
+
options: extension.options,
|
|
2584
|
+
storage: extension.storage,
|
|
2585
|
+
};
|
|
2586
|
+
const extraNodeFields = extensions.reduce((fields, e) => {
|
|
2587
|
+
const extendNodeSchema = getExtensionField(e, 'extendNodeSchema', context);
|
|
2588
|
+
return {
|
|
2589
|
+
...fields,
|
|
2590
|
+
...(extendNodeSchema ? extendNodeSchema(extension) : {}),
|
|
2591
|
+
};
|
|
2592
|
+
}, {});
|
|
2593
|
+
const schema = cleanUpSchemaItem({
|
|
2594
|
+
...extraNodeFields,
|
|
2595
|
+
content: callOrReturn(getExtensionField(extension, 'content', context)),
|
|
2596
|
+
marks: callOrReturn(getExtensionField(extension, 'marks', context)),
|
|
2597
|
+
group: callOrReturn(getExtensionField(extension, 'group', context)),
|
|
2598
|
+
inline: callOrReturn(getExtensionField(extension, 'inline', context)),
|
|
2599
|
+
atom: callOrReturn(getExtensionField(extension, 'atom', context)),
|
|
2600
|
+
selectable: callOrReturn(getExtensionField(extension, 'selectable', context)),
|
|
2601
|
+
draggable: callOrReturn(getExtensionField(extension, 'draggable', context)),
|
|
2602
|
+
code: callOrReturn(getExtensionField(extension, 'code', context)),
|
|
2603
|
+
defining: callOrReturn(getExtensionField(extension, 'defining', context)),
|
|
2604
|
+
isolating: callOrReturn(getExtensionField(extension, 'isolating', context)),
|
|
2605
|
+
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
|
|
2606
|
+
var _a;
|
|
2607
|
+
return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
|
|
2608
|
+
})),
|
|
2609
|
+
});
|
|
2610
|
+
const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
|
|
2611
|
+
if (parseHTML) {
|
|
2612
|
+
schema.parseDOM = parseHTML
|
|
2613
|
+
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
2614
|
+
}
|
|
2615
|
+
const renderHTML = getExtensionField(extension, 'renderHTML', context);
|
|
2616
|
+
if (renderHTML) {
|
|
2617
|
+
schema.toDOM = node => renderHTML({
|
|
2618
|
+
node,
|
|
2619
|
+
HTMLAttributes: getRenderedAttributes(node, extensionAttributes),
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
const renderText = getExtensionField(extension, 'renderText', context);
|
|
2623
|
+
if (renderText) {
|
|
2624
|
+
schema.toText = renderText;
|
|
2625
|
+
}
|
|
2626
|
+
return [extension.name, schema];
|
|
2627
|
+
}));
|
|
2628
|
+
const marks = Object.fromEntries(markExtensions.map(extension => {
|
|
2629
|
+
const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name);
|
|
2630
|
+
const context = {
|
|
2631
|
+
name: extension.name,
|
|
2632
|
+
options: extension.options,
|
|
2633
|
+
storage: extension.storage,
|
|
2634
|
+
};
|
|
2635
|
+
const extraMarkFields = extensions.reduce((fields, e) => {
|
|
2636
|
+
const extendMarkSchema = getExtensionField(e, 'extendMarkSchema', context);
|
|
2637
|
+
return {
|
|
2638
|
+
...fields,
|
|
2639
|
+
...(extendMarkSchema ? extendMarkSchema(extension) : {}),
|
|
2640
|
+
};
|
|
2641
|
+
}, {});
|
|
2642
|
+
const schema = cleanUpSchemaItem({
|
|
2643
|
+
...extraMarkFields,
|
|
2644
|
+
inclusive: callOrReturn(getExtensionField(extension, 'inclusive', context)),
|
|
2645
|
+
excludes: callOrReturn(getExtensionField(extension, 'excludes', context)),
|
|
2646
|
+
group: callOrReturn(getExtensionField(extension, 'group', context)),
|
|
2647
|
+
spanning: callOrReturn(getExtensionField(extension, 'spanning', context)),
|
|
2648
|
+
code: callOrReturn(getExtensionField(extension, 'code', context)),
|
|
2649
|
+
attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => {
|
|
2650
|
+
var _a;
|
|
2651
|
+
return [extensionAttribute.name, { default: (_a = extensionAttribute === null || extensionAttribute === void 0 ? void 0 : extensionAttribute.attribute) === null || _a === void 0 ? void 0 : _a.default }];
|
|
2652
|
+
})),
|
|
2653
|
+
});
|
|
2654
|
+
const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context));
|
|
2655
|
+
if (parseHTML) {
|
|
2656
|
+
schema.parseDOM = parseHTML
|
|
2657
|
+
.map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes));
|
|
2658
|
+
}
|
|
2659
|
+
const renderHTML = getExtensionField(extension, 'renderHTML', context);
|
|
2660
|
+
if (renderHTML) {
|
|
2661
|
+
schema.toDOM = mark => renderHTML({
|
|
2662
|
+
mark,
|
|
2663
|
+
HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),
|
|
2664
|
+
});
|
|
2665
|
+
}
|
|
2666
|
+
return [extension.name, schema];
|
|
2667
|
+
}));
|
|
2668
|
+
return new prosemirrorModel.Schema({
|
|
2669
|
+
topNode,
|
|
2670
|
+
nodes,
|
|
2671
|
+
marks,
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
function getSchemaTypeByName(name, schema) {
|
|
2676
|
+
return schema.nodes[name] || schema.marks[name] || null;
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
function isExtensionRulesEnabled(extension, enabled) {
|
|
2680
|
+
if (Array.isArray(enabled)) {
|
|
2681
|
+
return enabled.some(enabledExtension => {
|
|
2682
|
+
const name = typeof enabledExtension === 'string'
|
|
2683
|
+
? enabledExtension
|
|
2684
|
+
: enabledExtension.name;
|
|
2685
|
+
return name === extension.name;
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
return enabled;
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
function findDuplicates(items) {
|
|
2692
|
+
const filtered = items.filter((el, index) => items.indexOf(el) !== index);
|
|
2693
|
+
return [...new Set(filtered)];
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
class ExtensionManager {
|
|
2697
|
+
constructor(extensions, editor) {
|
|
2698
|
+
this.splittableMarks = [];
|
|
2699
|
+
this.editor = editor;
|
|
2700
|
+
this.extensions = ExtensionManager.resolve(extensions);
|
|
2701
|
+
this.schema = getSchemaByResolvedExtensions(this.extensions);
|
|
2702
|
+
this.extensions.forEach(extension => {
|
|
2703
|
+
var _a;
|
|
2704
|
+
// store extension storage in editor
|
|
2705
|
+
this.editor.extensionStorage[extension.name] = extension.storage;
|
|
2706
|
+
const context = {
|
|
2707
|
+
name: extension.name,
|
|
2708
|
+
options: extension.options,
|
|
2709
|
+
storage: extension.storage,
|
|
2710
|
+
editor: this.editor,
|
|
2711
|
+
type: getSchemaTypeByName(extension.name, this.schema),
|
|
2712
|
+
};
|
|
2713
|
+
if (extension.type === 'mark') {
|
|
2714
|
+
const keepOnSplit = (_a = callOrReturn(getExtensionField(extension, 'keepOnSplit', context))) !== null && _a !== void 0 ? _a : true;
|
|
2715
|
+
if (keepOnSplit) {
|
|
2716
|
+
this.splittableMarks.push(extension.name);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
const onBeforeCreate = getExtensionField(extension, 'onBeforeCreate', context);
|
|
2720
|
+
if (onBeforeCreate) {
|
|
2721
|
+
this.editor.on('beforeCreate', onBeforeCreate);
|
|
2722
|
+
}
|
|
2723
|
+
const onCreate = getExtensionField(extension, 'onCreate', context);
|
|
2724
|
+
if (onCreate) {
|
|
2725
|
+
this.editor.on('create', onCreate);
|
|
2726
|
+
}
|
|
2727
|
+
const onUpdate = getExtensionField(extension, 'onUpdate', context);
|
|
2728
|
+
if (onUpdate) {
|
|
2729
|
+
this.editor.on('update', onUpdate);
|
|
2730
|
+
}
|
|
2731
|
+
const onSelectionUpdate = getExtensionField(extension, 'onSelectionUpdate', context);
|
|
2732
|
+
if (onSelectionUpdate) {
|
|
2733
|
+
this.editor.on('selectionUpdate', onSelectionUpdate);
|
|
2734
|
+
}
|
|
2735
|
+
const onTransaction = getExtensionField(extension, 'onTransaction', context);
|
|
2736
|
+
if (onTransaction) {
|
|
2737
|
+
this.editor.on('transaction', onTransaction);
|
|
2738
|
+
}
|
|
2739
|
+
const onFocus = getExtensionField(extension, 'onFocus', context);
|
|
2740
|
+
if (onFocus) {
|
|
2741
|
+
this.editor.on('focus', onFocus);
|
|
2742
|
+
}
|
|
2743
|
+
const onBlur = getExtensionField(extension, 'onBlur', context);
|
|
2744
|
+
if (onBlur) {
|
|
2745
|
+
this.editor.on('blur', onBlur);
|
|
2746
|
+
}
|
|
2747
|
+
const onDestroy = getExtensionField(extension, 'onDestroy', context);
|
|
2748
|
+
if (onDestroy) {
|
|
2749
|
+
this.editor.on('destroy', onDestroy);
|
|
2750
|
+
}
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
static resolve(extensions) {
|
|
2754
|
+
const resolvedExtensions = ExtensionManager.sort(ExtensionManager.flatten(extensions));
|
|
2755
|
+
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name));
|
|
2756
|
+
if (duplicatedNames.length) {
|
|
2757
|
+
console.warn(`[tiptap warn]: Duplicate extension names found: [${duplicatedNames.map(item => `'${item}'`).join(', ')}]. This can lead to issues.`);
|
|
2758
|
+
}
|
|
2759
|
+
return resolvedExtensions;
|
|
2760
|
+
}
|
|
2761
|
+
static flatten(extensions) {
|
|
2762
|
+
return extensions
|
|
2763
|
+
.map(extension => {
|
|
2764
|
+
const context = {
|
|
2765
|
+
name: extension.name,
|
|
2766
|
+
options: extension.options,
|
|
2767
|
+
storage: extension.storage,
|
|
2768
|
+
};
|
|
2769
|
+
const addExtensions = getExtensionField(extension, 'addExtensions', context);
|
|
2770
|
+
if (addExtensions) {
|
|
2771
|
+
return [
|
|
2772
|
+
extension,
|
|
2773
|
+
...this.flatten(addExtensions()),
|
|
2774
|
+
];
|
|
2775
|
+
}
|
|
2776
|
+
return extension;
|
|
2777
|
+
})
|
|
2778
|
+
// `Infinity` will break TypeScript so we set a number that is probably high enough
|
|
2779
|
+
.flat(10);
|
|
2780
|
+
}
|
|
2781
|
+
static sort(extensions) {
|
|
2782
|
+
const defaultPriority = 100;
|
|
2783
|
+
return extensions.sort((a, b) => {
|
|
2784
|
+
const priorityA = getExtensionField(a, 'priority') || defaultPriority;
|
|
2785
|
+
const priorityB = getExtensionField(b, 'priority') || defaultPriority;
|
|
2786
|
+
if (priorityA > priorityB) {
|
|
2787
|
+
return -1;
|
|
2788
|
+
}
|
|
2789
|
+
if (priorityA < priorityB) {
|
|
2790
|
+
return 1;
|
|
2791
|
+
}
|
|
2792
|
+
return 0;
|
|
2793
|
+
});
|
|
2794
|
+
}
|
|
2795
|
+
get commands() {
|
|
2796
|
+
return this.extensions.reduce((commands, extension) => {
|
|
2797
|
+
const context = {
|
|
2798
|
+
name: extension.name,
|
|
2799
|
+
options: extension.options,
|
|
2800
|
+
storage: extension.storage,
|
|
2801
|
+
editor: this.editor,
|
|
2802
|
+
type: getSchemaTypeByName(extension.name, this.schema),
|
|
2803
|
+
};
|
|
2804
|
+
const addCommands = getExtensionField(extension, 'addCommands', context);
|
|
2805
|
+
if (!addCommands) {
|
|
2806
|
+
return commands;
|
|
2807
|
+
}
|
|
2808
|
+
return {
|
|
2809
|
+
...commands,
|
|
2810
|
+
...addCommands(),
|
|
2811
|
+
};
|
|
2812
|
+
}, {});
|
|
2813
|
+
}
|
|
2814
|
+
get plugins() {
|
|
2815
|
+
const { editor } = this;
|
|
2816
|
+
// With ProseMirror, first plugins within an array are executed first.
|
|
2817
|
+
// In tiptap, we provide the ability to override plugins,
|
|
2818
|
+
// so it feels more natural to run plugins at the end of an array first.
|
|
2819
|
+
// That’s why we have to reverse the `extensions` array and sort again
|
|
2820
|
+
// based on the `priority` option.
|
|
2821
|
+
const extensions = ExtensionManager.sort([...this.extensions].reverse());
|
|
2822
|
+
const inputRules = [];
|
|
2823
|
+
const pasteRules = [];
|
|
2824
|
+
const allPlugins = extensions
|
|
2825
|
+
.map(extension => {
|
|
2826
|
+
const context = {
|
|
2827
|
+
name: extension.name,
|
|
2828
|
+
options: extension.options,
|
|
2829
|
+
storage: extension.storage,
|
|
2830
|
+
editor,
|
|
2831
|
+
type: getSchemaTypeByName(extension.name, this.schema),
|
|
2832
|
+
};
|
|
2833
|
+
const plugins = [];
|
|
2834
|
+
const addKeyboardShortcuts = getExtensionField(extension, 'addKeyboardShortcuts', context);
|
|
2835
|
+
if (addKeyboardShortcuts) {
|
|
2836
|
+
const bindings = Object.fromEntries(Object
|
|
2837
|
+
.entries(addKeyboardShortcuts())
|
|
2838
|
+
.map(([shortcut, method]) => {
|
|
2839
|
+
return [shortcut, () => method({ editor })];
|
|
2840
|
+
}));
|
|
2841
|
+
const keyMapPlugin = prosemirrorKeymap.keymap(bindings);
|
|
2842
|
+
plugins.push(keyMapPlugin);
|
|
2843
|
+
}
|
|
2844
|
+
const addInputRules = getExtensionField(extension, 'addInputRules', context);
|
|
2845
|
+
if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {
|
|
2846
|
+
inputRules.push(...addInputRules());
|
|
2847
|
+
}
|
|
2848
|
+
const addPasteRules = getExtensionField(extension, 'addPasteRules', context);
|
|
2849
|
+
if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {
|
|
2850
|
+
pasteRules.push(...addPasteRules());
|
|
2851
|
+
}
|
|
2852
|
+
const addProseMirrorPlugins = getExtensionField(extension, 'addProseMirrorPlugins', context);
|
|
2853
|
+
if (addProseMirrorPlugins) {
|
|
2854
|
+
const proseMirrorPlugins = addProseMirrorPlugins();
|
|
2855
|
+
plugins.push(...proseMirrorPlugins);
|
|
2856
|
+
}
|
|
2857
|
+
return plugins;
|
|
2858
|
+
})
|
|
2859
|
+
.flat();
|
|
2860
|
+
return [
|
|
2861
|
+
inputRulesPlugin({
|
|
2862
|
+
editor,
|
|
2863
|
+
rules: inputRules,
|
|
2864
|
+
}),
|
|
2865
|
+
pasteRulesPlugin({
|
|
2866
|
+
editor,
|
|
2867
|
+
rules: pasteRules,
|
|
2868
|
+
}),
|
|
2869
|
+
...allPlugins,
|
|
2870
|
+
];
|
|
2871
|
+
}
|
|
2872
|
+
get attributes() {
|
|
2873
|
+
return getAttributesFromExtensions(this.extensions);
|
|
2874
|
+
}
|
|
2875
|
+
get nodeViews() {
|
|
2876
|
+
const { editor } = this;
|
|
2877
|
+
const { nodeExtensions } = splitExtensions(this.extensions);
|
|
2878
|
+
return Object.fromEntries(nodeExtensions
|
|
2879
|
+
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
|
|
2880
|
+
.map(extension => {
|
|
2881
|
+
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name);
|
|
2882
|
+
const context = {
|
|
2883
|
+
name: extension.name,
|
|
2884
|
+
options: extension.options,
|
|
2885
|
+
storage: extension.storage,
|
|
2886
|
+
editor,
|
|
2887
|
+
type: getNodeType(extension.name, this.schema),
|
|
2888
|
+
};
|
|
2889
|
+
const addNodeView = getExtensionField(extension, 'addNodeView', context);
|
|
2890
|
+
if (!addNodeView) {
|
|
2891
|
+
return [];
|
|
2892
|
+
}
|
|
2893
|
+
const nodeview = (node, view, getPos, decorations) => {
|
|
2894
|
+
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes);
|
|
2895
|
+
return addNodeView()({
|
|
2896
|
+
editor,
|
|
2897
|
+
node,
|
|
2898
|
+
getPos,
|
|
2899
|
+
decorations,
|
|
2900
|
+
HTMLAttributes,
|
|
2901
|
+
extension,
|
|
2902
|
+
});
|
|
2903
|
+
};
|
|
2904
|
+
return [extension.name, nodeview];
|
|
2905
|
+
}));
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
class EventEmitter {
|
|
2910
|
+
constructor() {
|
|
2911
|
+
this.callbacks = {};
|
|
2912
|
+
}
|
|
2913
|
+
on(event, fn) {
|
|
2914
|
+
if (!this.callbacks[event]) {
|
|
2915
|
+
this.callbacks[event] = [];
|
|
2916
|
+
}
|
|
2917
|
+
this.callbacks[event].push(fn);
|
|
2918
|
+
return this;
|
|
2919
|
+
}
|
|
2920
|
+
emit(event, ...args) {
|
|
2921
|
+
const callbacks = this.callbacks[event];
|
|
2922
|
+
if (callbacks) {
|
|
2923
|
+
callbacks.forEach(callback => callback.apply(this, args));
|
|
2924
|
+
}
|
|
2925
|
+
return this;
|
|
2926
|
+
}
|
|
2927
|
+
off(event, fn) {
|
|
2928
|
+
const callbacks = this.callbacks[event];
|
|
2929
|
+
if (callbacks) {
|
|
2930
|
+
if (fn) {
|
|
2931
|
+
this.callbacks[event] = callbacks.filter(callback => callback !== fn);
|
|
2932
|
+
}
|
|
2933
|
+
else {
|
|
2934
|
+
delete this.callbacks[event];
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
return this;
|
|
2938
|
+
}
|
|
2939
|
+
removeAllListeners() {
|
|
2940
|
+
this.callbacks = {};
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2139
2943
|
|
|
2140
2944
|
const style = `.ProseMirror {
|
|
2141
2945
|
position: relative;
|
|
@@ -2144,8 +2948,10 @@ const style = `.ProseMirror {
|
|
|
2144
2948
|
.ProseMirror {
|
|
2145
2949
|
word-wrap: break-word;
|
|
2146
2950
|
white-space: pre-wrap;
|
|
2951
|
+
white-space: break-spaces;
|
|
2147
2952
|
-webkit-font-variant-ligatures: none;
|
|
2148
2953
|
font-variant-ligatures: none;
|
|
2954
|
+
font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
|
|
2149
2955
|
}
|
|
2150
2956
|
|
|
2151
2957
|
.ProseMirror [contenteditable="false"] {
|
|
@@ -2160,6 +2966,14 @@ const style = `.ProseMirror {
|
|
|
2160
2966
|
white-space: pre-wrap;
|
|
2161
2967
|
}
|
|
2162
2968
|
|
|
2969
|
+
img.ProseMirror-separator {
|
|
2970
|
+
display: inline !important;
|
|
2971
|
+
border: none !important;
|
|
2972
|
+
margin: 0 !important;
|
|
2973
|
+
width: 1px !important;
|
|
2974
|
+
height: 1px !important;
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2163
2977
|
.ProseMirror-gapcursor {
|
|
2164
2978
|
display: none;
|
|
2165
2979
|
pointer-events: none;
|
|
@@ -2196,12 +3010,17 @@ const style = `.ProseMirror {
|
|
|
2196
3010
|
|
|
2197
3011
|
.ProseMirror-focused .ProseMirror-gapcursor {
|
|
2198
3012
|
display: block;
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
.tippy-box[data-animation=fade][data-state=hidden] {
|
|
3016
|
+
opacity: 0
|
|
2199
3017
|
}`;
|
|
2200
3018
|
|
|
2201
3019
|
class Editor extends EventEmitter {
|
|
2202
3020
|
constructor(options = {}) {
|
|
2203
3021
|
super();
|
|
2204
3022
|
this.isFocused = false;
|
|
3023
|
+
this.extensionStorage = {};
|
|
2205
3024
|
this.options = {
|
|
2206
3025
|
element: document.createElement('div'),
|
|
2207
3026
|
content: '',
|
|
@@ -2213,80 +3032,64 @@ class Editor extends EventEmitter {
|
|
|
2213
3032
|
parseOptions: {},
|
|
2214
3033
|
enableInputRules: true,
|
|
2215
3034
|
enablePasteRules: true,
|
|
3035
|
+
enableCoreExtensions: true,
|
|
3036
|
+
onBeforeCreate: () => null,
|
|
2216
3037
|
onCreate: () => null,
|
|
2217
3038
|
onUpdate: () => null,
|
|
2218
3039
|
onSelectionUpdate: () => null,
|
|
2219
|
-
onViewUpdate: () => null,
|
|
2220
3040
|
onTransaction: () => null,
|
|
2221
3041
|
onFocus: () => null,
|
|
2222
3042
|
onBlur: () => null,
|
|
2223
|
-
onResize: () => null,
|
|
2224
3043
|
onDestroy: () => null,
|
|
2225
3044
|
};
|
|
2226
|
-
/**
|
|
2227
|
-
* Creates a ProseMirror document.
|
|
2228
|
-
*/
|
|
2229
|
-
this.createDocument = (content, parseOptions = this.options.parseOptions) => {
|
|
2230
|
-
if (content && typeof content === 'object') {
|
|
2231
|
-
try {
|
|
2232
|
-
return this.schema.nodeFromJSON(content);
|
|
2233
|
-
}
|
|
2234
|
-
catch (error) {
|
|
2235
|
-
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error);
|
|
2236
|
-
return this.createDocument('');
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
if (typeof content === 'string') {
|
|
2240
|
-
return prosemirrorModel.DOMParser
|
|
2241
|
-
.fromSchema(this.schema)
|
|
2242
|
-
.parse(elementFromString(content), parseOptions);
|
|
2243
|
-
}
|
|
2244
|
-
return this.createDocument('');
|
|
2245
|
-
};
|
|
2246
3045
|
this.isCapturingTransaction = false;
|
|
2247
3046
|
this.capturedTransaction = null;
|
|
2248
3047
|
this.setOptions(options);
|
|
2249
3048
|
this.createExtensionManager();
|
|
2250
3049
|
this.createCommandManager();
|
|
2251
3050
|
this.createSchema();
|
|
3051
|
+
this.on('beforeCreate', this.options.onBeforeCreate);
|
|
3052
|
+
this.emit('beforeCreate', { editor: this });
|
|
2252
3053
|
this.createView();
|
|
2253
3054
|
this.injectCSS();
|
|
2254
3055
|
this.on('create', this.options.onCreate);
|
|
2255
3056
|
this.on('update', this.options.onUpdate);
|
|
2256
3057
|
this.on('selectionUpdate', this.options.onSelectionUpdate);
|
|
2257
|
-
this.on('viewUpdate', this.options.onViewUpdate);
|
|
2258
3058
|
this.on('transaction', this.options.onTransaction);
|
|
2259
3059
|
this.on('focus', this.options.onFocus);
|
|
2260
3060
|
this.on('blur', this.options.onBlur);
|
|
2261
3061
|
this.on('destroy', this.options.onDestroy);
|
|
2262
3062
|
window.setTimeout(() => {
|
|
3063
|
+
if (this.isDestroyed) {
|
|
3064
|
+
return;
|
|
3065
|
+
}
|
|
2263
3066
|
this.commands.focus(this.options.autofocus);
|
|
2264
3067
|
this.emit('create', { editor: this });
|
|
2265
|
-
if (window.ResizeObserver) {
|
|
2266
|
-
this.resizeObserver = new ResizeObserver(() => {
|
|
2267
|
-
this.emit('resize', { editor: this });
|
|
2268
|
-
});
|
|
2269
|
-
this.resizeObserver.observe(this.view.dom);
|
|
2270
|
-
}
|
|
2271
3068
|
}, 0);
|
|
2272
3069
|
}
|
|
3070
|
+
/**
|
|
3071
|
+
* Returns the editor storage.
|
|
3072
|
+
*/
|
|
3073
|
+
get storage() {
|
|
3074
|
+
return this.extensionStorage;
|
|
3075
|
+
}
|
|
2273
3076
|
/**
|
|
2274
3077
|
* An object of all registered commands.
|
|
2275
3078
|
*/
|
|
2276
3079
|
get commands() {
|
|
2277
|
-
return this.commandManager.
|
|
3080
|
+
return this.commandManager.commands;
|
|
2278
3081
|
}
|
|
2279
3082
|
/**
|
|
2280
3083
|
* Create a command chain to call multiple commands at once.
|
|
2281
3084
|
*/
|
|
2282
3085
|
chain() {
|
|
2283
|
-
return this.commandManager.
|
|
3086
|
+
return this.commandManager.chain();
|
|
2284
3087
|
}
|
|
2285
3088
|
/**
|
|
2286
3089
|
* Check if a command or a command chain can be executed. Without executing it.
|
|
2287
3090
|
*/
|
|
2288
3091
|
can() {
|
|
2289
|
-
return this.commandManager.
|
|
3092
|
+
return this.commandManager.can();
|
|
2290
3093
|
}
|
|
2291
3094
|
/**
|
|
2292
3095
|
* Inject CSS styles.
|
|
@@ -2302,22 +3105,34 @@ class Editor extends EventEmitter {
|
|
|
2302
3105
|
* @param options A list of options
|
|
2303
3106
|
*/
|
|
2304
3107
|
setOptions(options = {}) {
|
|
2305
|
-
this.options = {
|
|
3108
|
+
this.options = {
|
|
3109
|
+
...this.options,
|
|
3110
|
+
...options,
|
|
3111
|
+
};
|
|
3112
|
+
if (!this.view || !this.state || this.isDestroyed) {
|
|
3113
|
+
return;
|
|
3114
|
+
}
|
|
3115
|
+
if (this.options.editorProps) {
|
|
3116
|
+
this.view.setProps(this.options.editorProps);
|
|
3117
|
+
}
|
|
3118
|
+
this.view.updateState(this.state);
|
|
2306
3119
|
}
|
|
2307
3120
|
/**
|
|
2308
3121
|
* Update editable state of the editor.
|
|
2309
3122
|
*/
|
|
2310
3123
|
setEditable(editable) {
|
|
2311
3124
|
this.setOptions({ editable });
|
|
2312
|
-
if (this.view && this.state && !this.isDestroyed) {
|
|
2313
|
-
this.view.updateState(this.state);
|
|
2314
|
-
}
|
|
2315
3125
|
}
|
|
2316
3126
|
/**
|
|
2317
3127
|
* Returns whether the editor is editable.
|
|
2318
3128
|
*/
|
|
2319
3129
|
get isEditable() {
|
|
2320
|
-
|
|
3130
|
+
// since plugins are applied after creating the view
|
|
3131
|
+
// `editable` is always `true` for one tick.
|
|
3132
|
+
// that’s why we also have to check for `options.editable`
|
|
3133
|
+
return this.options.editable
|
|
3134
|
+
&& this.view
|
|
3135
|
+
&& this.view.editable;
|
|
2321
3136
|
}
|
|
2322
3137
|
/**
|
|
2323
3138
|
* Returns the editor state.
|
|
@@ -2332,7 +3147,7 @@ class Editor extends EventEmitter {
|
|
|
2332
3147
|
* @param handlePlugins Control how to merge the plugin into the existing plugins.
|
|
2333
3148
|
*/
|
|
2334
3149
|
registerPlugin(plugin, handlePlugins) {
|
|
2335
|
-
const plugins =
|
|
3150
|
+
const plugins = isObject$1(handlePlugins)
|
|
2336
3151
|
? handlePlugins(plugin, this.state.plugins)
|
|
2337
3152
|
: [...this.state.plugins, plugin];
|
|
2338
3153
|
const state = this.state.reconfigure({ plugins });
|
|
@@ -2341,7 +3156,7 @@ class Editor extends EventEmitter {
|
|
|
2341
3156
|
/**
|
|
2342
3157
|
* Unregister a ProseMirror plugin.
|
|
2343
3158
|
*
|
|
2344
|
-
* @param
|
|
3159
|
+
* @param nameOrPluginKey The plugins name
|
|
2345
3160
|
*/
|
|
2346
3161
|
unregisterPlugin(nameOrPluginKey) {
|
|
2347
3162
|
if (this.isDestroyed) {
|
|
@@ -2361,7 +3176,9 @@ class Editor extends EventEmitter {
|
|
|
2361
3176
|
* Creates an extension manager.
|
|
2362
3177
|
*/
|
|
2363
3178
|
createExtensionManager() {
|
|
2364
|
-
const coreExtensions =
|
|
3179
|
+
const coreExtensions = this.options.enableCoreExtensions
|
|
3180
|
+
? Object.values(extensions)
|
|
3181
|
+
: [];
|
|
2365
3182
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
|
2366
3183
|
return ['extension', 'node', 'mark'].includes(extension === null || extension === void 0 ? void 0 : extension.type);
|
|
2367
3184
|
});
|
|
@@ -2371,7 +3188,9 @@ class Editor extends EventEmitter {
|
|
|
2371
3188
|
* Creates an command manager.
|
|
2372
3189
|
*/
|
|
2373
3190
|
createCommandManager() {
|
|
2374
|
-
this.commandManager = new CommandManager(
|
|
3191
|
+
this.commandManager = new CommandManager({
|
|
3192
|
+
editor: this,
|
|
3193
|
+
});
|
|
2375
3194
|
}
|
|
2376
3195
|
/**
|
|
2377
3196
|
* Creates a ProseMirror schema.
|
|
@@ -2387,22 +3206,13 @@ class Editor extends EventEmitter {
|
|
|
2387
3206
|
...this.options.editorProps,
|
|
2388
3207
|
dispatchTransaction: this.dispatchTransaction.bind(this),
|
|
2389
3208
|
state: prosemirrorState.EditorState.create({
|
|
2390
|
-
doc:
|
|
3209
|
+
doc: createDocument(this.options.content, this.schema, this.options.parseOptions),
|
|
2391
3210
|
}),
|
|
2392
3211
|
});
|
|
2393
3212
|
// `editor.view` is not yet available at this time.
|
|
2394
3213
|
// Therefore we will add all plugins and node views directly afterwards.
|
|
2395
3214
|
const newState = this.state.reconfigure({
|
|
2396
|
-
plugins:
|
|
2397
|
-
new prosemirrorState.Plugin({
|
|
2398
|
-
view: () => ({
|
|
2399
|
-
update: () => this.emit('viewUpdate', {
|
|
2400
|
-
editor: this,
|
|
2401
|
-
}),
|
|
2402
|
-
}),
|
|
2403
|
-
}),
|
|
2404
|
-
...this.extensionManager.plugins,
|
|
2405
|
-
],
|
|
3215
|
+
plugins: this.extensionManager.plugins,
|
|
2406
3216
|
});
|
|
2407
3217
|
this.view.updateState(newState);
|
|
2408
3218
|
this.createNodeViews();
|
|
@@ -2433,9 +3243,6 @@ class Editor extends EventEmitter {
|
|
|
2433
3243
|
* @param transaction An editor state transaction
|
|
2434
3244
|
*/
|
|
2435
3245
|
dispatchTransaction(transaction) {
|
|
2436
|
-
if (transaction.docChanged && !this.isEditable) {
|
|
2437
|
-
return;
|
|
2438
|
-
}
|
|
2439
3246
|
if (this.isCapturingTransaction) {
|
|
2440
3247
|
if (!this.capturedTransaction) {
|
|
2441
3248
|
this.capturedTransaction = transaction;
|
|
@@ -2454,6 +3261,7 @@ class Editor extends EventEmitter {
|
|
|
2454
3261
|
if (selectionHasChanged) {
|
|
2455
3262
|
this.emit('selectionUpdate', {
|
|
2456
3263
|
editor: this,
|
|
3264
|
+
transaction,
|
|
2457
3265
|
});
|
|
2458
3266
|
}
|
|
2459
3267
|
const focus = transaction.getMeta('focus');
|
|
@@ -2462,12 +3270,14 @@ class Editor extends EventEmitter {
|
|
|
2462
3270
|
this.emit('focus', {
|
|
2463
3271
|
editor: this,
|
|
2464
3272
|
event: focus.event,
|
|
3273
|
+
transaction,
|
|
2465
3274
|
});
|
|
2466
3275
|
}
|
|
2467
3276
|
if (blur) {
|
|
2468
3277
|
this.emit('blur', {
|
|
2469
3278
|
editor: this,
|
|
2470
3279
|
event: blur.event,
|
|
3280
|
+
transaction,
|
|
2471
3281
|
});
|
|
2472
3282
|
}
|
|
2473
3283
|
if (!transaction.docChanged || transaction.getMeta('preventUpdate')) {
|
|
@@ -2479,20 +3289,10 @@ class Editor extends EventEmitter {
|
|
|
2479
3289
|
});
|
|
2480
3290
|
}
|
|
2481
3291
|
/**
|
|
2482
|
-
* Get attributes of the currently selected node.
|
|
2483
|
-
*
|
|
2484
|
-
* @param name Name of the node
|
|
2485
|
-
*/
|
|
2486
|
-
getNodeAttributes(name) {
|
|
2487
|
-
return getNodeAttributes(this.state, name);
|
|
2488
|
-
}
|
|
2489
|
-
/**
|
|
2490
|
-
* Get attributes of the currently selected mark.
|
|
2491
|
-
*
|
|
2492
|
-
* @param name Name of the mark
|
|
3292
|
+
* Get attributes of the currently selected node or mark.
|
|
2493
3293
|
*/
|
|
2494
|
-
|
|
2495
|
-
return
|
|
3294
|
+
getAttributes(nameOrType) {
|
|
3295
|
+
return getAttributes(this.state, nameOrType);
|
|
2496
3296
|
}
|
|
2497
3297
|
isActive(nameOrAttributes, attributesOrUndefined) {
|
|
2498
3298
|
const name = typeof nameOrAttributes === 'string'
|
|
@@ -2513,7 +3313,20 @@ class Editor extends EventEmitter {
|
|
|
2513
3313
|
* Get the document as HTML.
|
|
2514
3314
|
*/
|
|
2515
3315
|
getHTML() {
|
|
2516
|
-
return getHTMLFromFragment(this.state.doc, this.schema);
|
|
3316
|
+
return getHTMLFromFragment(this.state.doc.content, this.schema);
|
|
3317
|
+
}
|
|
3318
|
+
/**
|
|
3319
|
+
* Get the document as text.
|
|
3320
|
+
*/
|
|
3321
|
+
getText(options) {
|
|
3322
|
+
const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
|
|
3323
|
+
return getText(this.state.doc, {
|
|
3324
|
+
blockSeparator,
|
|
3325
|
+
textSerializers: {
|
|
3326
|
+
...textSerializers,
|
|
3327
|
+
...getTextSeralizersFromSchema(this.schema),
|
|
3328
|
+
},
|
|
3329
|
+
});
|
|
2517
3330
|
}
|
|
2518
3331
|
/**
|
|
2519
3332
|
* Check if there is no content.
|
|
@@ -2531,14 +3344,11 @@ class Editor extends EventEmitter {
|
|
|
2531
3344
|
* Destroy the editor.
|
|
2532
3345
|
*/
|
|
2533
3346
|
destroy() {
|
|
2534
|
-
var _a;
|
|
2535
|
-
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.unobserve(this.view.dom);
|
|
2536
3347
|
this.emit('destroy');
|
|
2537
3348
|
if (this.view) {
|
|
2538
3349
|
this.view.destroy();
|
|
2539
3350
|
}
|
|
2540
3351
|
this.removeAllListeners();
|
|
2541
|
-
removeElement(this.css);
|
|
2542
3352
|
}
|
|
2543
3353
|
/**
|
|
2544
3354
|
* Check if the editor is already destroyed.
|
|
@@ -2550,86 +3360,158 @@ class Editor extends EventEmitter {
|
|
|
2550
3360
|
}
|
|
2551
3361
|
}
|
|
2552
3362
|
|
|
2553
|
-
var _configure$1;
|
|
2554
3363
|
class Node {
|
|
2555
|
-
constructor(config) {
|
|
3364
|
+
constructor(config = {}) {
|
|
2556
3365
|
this.type = 'node';
|
|
3366
|
+
this.name = 'node';
|
|
3367
|
+
this.parent = null;
|
|
3368
|
+
this.child = null;
|
|
2557
3369
|
this.config = {
|
|
2558
|
-
name:
|
|
3370
|
+
name: this.name,
|
|
2559
3371
|
defaultOptions: {},
|
|
2560
3372
|
};
|
|
2561
|
-
_configure$1.set(this, (options) => {
|
|
2562
|
-
this.options = mergeDeep(this.config.defaultOptions, options);
|
|
2563
|
-
return this;
|
|
2564
|
-
});
|
|
2565
3373
|
this.config = {
|
|
2566
3374
|
...this.config,
|
|
2567
3375
|
...config,
|
|
2568
3376
|
};
|
|
3377
|
+
this.name = this.config.name;
|
|
3378
|
+
if (config.defaultOptions) {
|
|
3379
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
3380
|
+
}
|
|
3381
|
+
// TODO: remove `addOptions` fallback
|
|
2569
3382
|
this.options = this.config.defaultOptions;
|
|
3383
|
+
if (this.config.addOptions) {
|
|
3384
|
+
this.options = callOrReturn(getExtensionField(this, 'addOptions', {
|
|
3385
|
+
name: this.name,
|
|
3386
|
+
}));
|
|
3387
|
+
}
|
|
3388
|
+
this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
|
|
3389
|
+
name: this.name,
|
|
3390
|
+
options: this.options,
|
|
3391
|
+
})) || {};
|
|
2570
3392
|
}
|
|
2571
|
-
static create(config) {
|
|
3393
|
+
static create(config = {}) {
|
|
2572
3394
|
return new Node(config);
|
|
2573
3395
|
}
|
|
2574
3396
|
configure(options = {}) {
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
3397
|
+
// return a new instance so we can use the same extension
|
|
3398
|
+
// with different calls of `configure`
|
|
3399
|
+
const extension = this.extend();
|
|
3400
|
+
extension.options = mergeDeep(this.options, options);
|
|
3401
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3402
|
+
name: extension.name,
|
|
3403
|
+
options: extension.options,
|
|
3404
|
+
}));
|
|
3405
|
+
return extension;
|
|
3406
|
+
}
|
|
3407
|
+
extend(extendedConfig = {}) {
|
|
3408
|
+
const extension = new Node(extendedConfig);
|
|
3409
|
+
extension.parent = this;
|
|
3410
|
+
this.child = extension;
|
|
3411
|
+
extension.name = extendedConfig.name
|
|
3412
|
+
? extendedConfig.name
|
|
3413
|
+
: extension.parent.name;
|
|
3414
|
+
if (extendedConfig.defaultOptions) {
|
|
3415
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
3416
|
+
}
|
|
3417
|
+
// TODO: remove `addOptions` fallback
|
|
3418
|
+
extension.options = extendedConfig.defaultOptions
|
|
3419
|
+
? extendedConfig.defaultOptions
|
|
3420
|
+
: extension.parent.options;
|
|
3421
|
+
if (extendedConfig.addOptions) {
|
|
3422
|
+
extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
|
|
3423
|
+
name: extension.name,
|
|
3424
|
+
}));
|
|
3425
|
+
}
|
|
3426
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3427
|
+
name: extension.name,
|
|
3428
|
+
options: extension.options,
|
|
3429
|
+
}));
|
|
3430
|
+
return extension;
|
|
2584
3431
|
}
|
|
2585
|
-
}
|
|
2586
|
-
_configure$1 = new WeakMap();
|
|
3432
|
+
}
|
|
2587
3433
|
|
|
2588
|
-
var _configure;
|
|
2589
3434
|
class Mark {
|
|
2590
|
-
constructor(config) {
|
|
3435
|
+
constructor(config = {}) {
|
|
2591
3436
|
this.type = 'mark';
|
|
3437
|
+
this.name = 'mark';
|
|
3438
|
+
this.parent = null;
|
|
3439
|
+
this.child = null;
|
|
2592
3440
|
this.config = {
|
|
2593
|
-
name:
|
|
3441
|
+
name: this.name,
|
|
2594
3442
|
defaultOptions: {},
|
|
2595
3443
|
};
|
|
2596
|
-
_configure.set(this, (options) => {
|
|
2597
|
-
this.options = mergeDeep(this.config.defaultOptions, options);
|
|
2598
|
-
return this;
|
|
2599
|
-
});
|
|
2600
3444
|
this.config = {
|
|
2601
3445
|
...this.config,
|
|
2602
3446
|
...config,
|
|
2603
3447
|
};
|
|
3448
|
+
this.name = this.config.name;
|
|
3449
|
+
if (config.defaultOptions) {
|
|
3450
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
3451
|
+
}
|
|
3452
|
+
// TODO: remove `addOptions` fallback
|
|
2604
3453
|
this.options = this.config.defaultOptions;
|
|
3454
|
+
if (this.config.addOptions) {
|
|
3455
|
+
this.options = callOrReturn(getExtensionField(this, 'addOptions', {
|
|
3456
|
+
name: this.name,
|
|
3457
|
+
}));
|
|
3458
|
+
}
|
|
3459
|
+
this.storage = callOrReturn(getExtensionField(this, 'addStorage', {
|
|
3460
|
+
name: this.name,
|
|
3461
|
+
options: this.options,
|
|
3462
|
+
})) || {};
|
|
2605
3463
|
}
|
|
2606
|
-
static create(config) {
|
|
3464
|
+
static create(config = {}) {
|
|
2607
3465
|
return new Mark(config);
|
|
2608
3466
|
}
|
|
2609
3467
|
configure(options = {}) {
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
3468
|
+
// return a new instance so we can use the same extension
|
|
3469
|
+
// with different calls of `configure`
|
|
3470
|
+
const extension = this.extend();
|
|
3471
|
+
extension.options = mergeDeep(this.options, options);
|
|
3472
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3473
|
+
name: extension.name,
|
|
3474
|
+
options: extension.options,
|
|
3475
|
+
}));
|
|
3476
|
+
return extension;
|
|
3477
|
+
}
|
|
3478
|
+
extend(extendedConfig = {}) {
|
|
3479
|
+
const extension = new Mark(extendedConfig);
|
|
3480
|
+
extension.parent = this;
|
|
3481
|
+
this.child = extension;
|
|
3482
|
+
extension.name = extendedConfig.name
|
|
3483
|
+
? extendedConfig.name
|
|
3484
|
+
: extension.parent.name;
|
|
3485
|
+
if (extendedConfig.defaultOptions) {
|
|
3486
|
+
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
3487
|
+
}
|
|
3488
|
+
// TODO: remove `addOptions` fallback
|
|
3489
|
+
extension.options = extendedConfig.defaultOptions
|
|
3490
|
+
? extendedConfig.defaultOptions
|
|
3491
|
+
: extension.parent.options;
|
|
3492
|
+
if (extendedConfig.addOptions) {
|
|
3493
|
+
extension.options = callOrReturn(getExtensionField(extension, 'addOptions', {
|
|
3494
|
+
name: extension.name,
|
|
3495
|
+
}));
|
|
3496
|
+
}
|
|
3497
|
+
extension.storage = callOrReturn(getExtensionField(extension, 'addStorage', {
|
|
3498
|
+
name: extension.name,
|
|
3499
|
+
options: extension.options,
|
|
3500
|
+
}));
|
|
3501
|
+
return extension;
|
|
2619
3502
|
}
|
|
2620
|
-
}
|
|
2621
|
-
_configure = new WeakMap();
|
|
3503
|
+
}
|
|
2622
3504
|
|
|
2623
3505
|
class NodeView {
|
|
2624
3506
|
constructor(component, props, options) {
|
|
2625
3507
|
this.isDragging = false;
|
|
3508
|
+
this.component = component;
|
|
3509
|
+
this.editor = props.editor;
|
|
2626
3510
|
this.options = {
|
|
2627
3511
|
stopEvent: null,
|
|
2628
|
-
|
|
3512
|
+
ignoreMutation: null,
|
|
3513
|
+
...options,
|
|
2629
3514
|
};
|
|
2630
|
-
this.component = component;
|
|
2631
|
-
this.options = { ...this.options, ...options };
|
|
2632
|
-
this.editor = props.editor;
|
|
2633
3515
|
this.extension = props.extension;
|
|
2634
3516
|
this.node = props.node;
|
|
2635
3517
|
this.decorations = props.decorations;
|
|
@@ -2647,17 +3529,31 @@ class NodeView {
|
|
|
2647
3529
|
return null;
|
|
2648
3530
|
}
|
|
2649
3531
|
onDragStart(event) {
|
|
2650
|
-
var _a, _b;
|
|
2651
|
-
if (!this.dom) {
|
|
2652
|
-
return;
|
|
2653
|
-
}
|
|
3532
|
+
var _a, _b, _c;
|
|
2654
3533
|
const { view } = this.editor;
|
|
2655
3534
|
const target = event.target;
|
|
2656
|
-
|
|
3535
|
+
// get the drag handle element
|
|
3536
|
+
// `closest` is not available for text nodes so we may have to use its parent
|
|
3537
|
+
const dragHandle = target.nodeType === 3
|
|
3538
|
+
? (_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.closest('[data-drag-handle]')
|
|
3539
|
+
: target.closest('[data-drag-handle]');
|
|
3540
|
+
if (!this.dom
|
|
3541
|
+
|| ((_b = this.contentDOM) === null || _b === void 0 ? void 0 : _b.contains(target))
|
|
3542
|
+
|| !dragHandle) {
|
|
2657
3543
|
return;
|
|
2658
3544
|
}
|
|
2659
|
-
|
|
2660
|
-
|
|
3545
|
+
let x = 0;
|
|
3546
|
+
let y = 0;
|
|
3547
|
+
// calculate offset for drag element if we use a different drag handle element
|
|
3548
|
+
if (this.dom !== dragHandle) {
|
|
3549
|
+
const domBox = this.dom.getBoundingClientRect();
|
|
3550
|
+
const handleBox = dragHandle.getBoundingClientRect();
|
|
3551
|
+
x = handleBox.x - domBox.x + event.offsetX;
|
|
3552
|
+
y = handleBox.y - domBox.y + event.offsetY;
|
|
3553
|
+
}
|
|
3554
|
+
(_c = event.dataTransfer) === null || _c === void 0 ? void 0 : _c.setDragImage(this.dom, x, y);
|
|
3555
|
+
// we need to tell ProseMirror that we want to move the whole node
|
|
3556
|
+
// so we create a NodeSelection
|
|
2661
3557
|
const selection = prosemirrorState.NodeSelection.create(view.state.doc, this.getPos());
|
|
2662
3558
|
const transaction = view.state.tr.setSelection(selection);
|
|
2663
3559
|
view.dispatch(transaction);
|
|
@@ -2668,7 +3564,7 @@ class NodeView {
|
|
|
2668
3564
|
return false;
|
|
2669
3565
|
}
|
|
2670
3566
|
if (typeof this.options.stopEvent === 'function') {
|
|
2671
|
-
return this.options.stopEvent(event);
|
|
3567
|
+
return this.options.stopEvent({ event });
|
|
2672
3568
|
}
|
|
2673
3569
|
const target = event.target;
|
|
2674
3570
|
const isInElement = this.dom.contains(target) && !((_a = this.contentDOM) === null || _a === void 0 ? void 0 : _a.contains(target));
|
|
@@ -2676,10 +3572,11 @@ class NodeView {
|
|
|
2676
3572
|
if (!isInElement) {
|
|
2677
3573
|
return false;
|
|
2678
3574
|
}
|
|
3575
|
+
const isDropEvent = event.type === 'drop';
|
|
2679
3576
|
const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName)
|
|
2680
3577
|
|| target.isContentEditable;
|
|
2681
3578
|
// any input event within node views should be ignored by ProseMirror
|
|
2682
|
-
if (isInput) {
|
|
3579
|
+
if (isInput && !isDropEvent) {
|
|
2683
3580
|
return true;
|
|
2684
3581
|
}
|
|
2685
3582
|
const { isEditable } = this.editor;
|
|
@@ -2690,7 +3587,7 @@ class NodeView {
|
|
|
2690
3587
|
const isPasteEvent = event.type === 'paste';
|
|
2691
3588
|
const isCutEvent = event.type === 'cut';
|
|
2692
3589
|
const isClickEvent = event.type === 'mousedown';
|
|
2693
|
-
const isDragEvent = event.type.startsWith('drag')
|
|
3590
|
+
const isDragEvent = event.type.startsWith('drag');
|
|
2694
3591
|
// ProseMirror tries to drag selectable nodes
|
|
2695
3592
|
// even if `draggable` is set to `false`
|
|
2696
3593
|
// this fix prevents that
|
|
@@ -2718,6 +3615,7 @@ class NodeView {
|
|
|
2718
3615
|
}
|
|
2719
3616
|
// these events are handled by prosemirror
|
|
2720
3617
|
if (isDragging
|
|
3618
|
+
|| isDropEvent
|
|
2721
3619
|
|| isCopyEvent
|
|
2722
3620
|
|| isPasteEvent
|
|
2723
3621
|
|| isCutEvent
|
|
@@ -2727,170 +3625,510 @@ class NodeView {
|
|
|
2727
3625
|
return true;
|
|
2728
3626
|
}
|
|
2729
3627
|
ignoreMutation(mutation) {
|
|
3628
|
+
if (!this.dom || !this.contentDOM) {
|
|
3629
|
+
return true;
|
|
3630
|
+
}
|
|
3631
|
+
if (typeof this.options.ignoreMutation === 'function') {
|
|
3632
|
+
return this.options.ignoreMutation({ mutation });
|
|
3633
|
+
}
|
|
3634
|
+
// a leaf/atom node is like a black box for ProseMirror
|
|
3635
|
+
// and should be fully handled by the node view
|
|
3636
|
+
if (this.node.isLeaf || this.node.isAtom) {
|
|
3637
|
+
return true;
|
|
3638
|
+
}
|
|
3639
|
+
// ProseMirror should handle any selections
|
|
2730
3640
|
if (mutation.type === 'selection') {
|
|
2731
|
-
if (this.node.isLeaf) {
|
|
2732
|
-
return true;
|
|
2733
|
-
}
|
|
2734
3641
|
return false;
|
|
2735
3642
|
}
|
|
2736
|
-
|
|
3643
|
+
// try to prevent a bug on iOS that will break node views on enter
|
|
3644
|
+
// this is because ProseMirror can’t preventDispatch on enter
|
|
3645
|
+
// this will lead to a re-render of the node view on enter
|
|
3646
|
+
// see: https://github.com/ueberdosis/tiptap/issues/1214
|
|
3647
|
+
if (this.dom.contains(mutation.target)
|
|
3648
|
+
&& mutation.type === 'childList'
|
|
3649
|
+
&& isiOS()
|
|
3650
|
+
&& this.editor.isFocused) {
|
|
3651
|
+
const changedNodes = [
|
|
3652
|
+
...Array.from(mutation.addedNodes),
|
|
3653
|
+
...Array.from(mutation.removedNodes),
|
|
3654
|
+
];
|
|
3655
|
+
// we’ll check if every changed node is contentEditable
|
|
3656
|
+
// to make sure it’s probably mutated by ProseMirror
|
|
3657
|
+
if (changedNodes.every(node => node.isContentEditable)) {
|
|
3658
|
+
return false;
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
// we will allow mutation contentDOM with attributes
|
|
3662
|
+
// so we can for example adding classes within our node view
|
|
3663
|
+
if (this.contentDOM === mutation.target && mutation.type === 'attributes') {
|
|
2737
3664
|
return true;
|
|
2738
3665
|
}
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
3666
|
+
// ProseMirror should handle any changes within contentDOM
|
|
3667
|
+
if (this.contentDOM.contains(mutation.target)) {
|
|
3668
|
+
return false;
|
|
3669
|
+
}
|
|
3670
|
+
return true;
|
|
2742
3671
|
}
|
|
2743
3672
|
updateAttributes(attributes) {
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
...attributes,
|
|
3673
|
+
this.editor.commands.command(({ tr }) => {
|
|
3674
|
+
const pos = this.getPos();
|
|
3675
|
+
tr.setNodeMarkup(pos, undefined, {
|
|
3676
|
+
...this.node.attrs,
|
|
3677
|
+
...attributes,
|
|
3678
|
+
});
|
|
3679
|
+
return true;
|
|
2752
3680
|
});
|
|
2753
|
-
|
|
3681
|
+
}
|
|
3682
|
+
deleteNode() {
|
|
3683
|
+
const from = this.getPos();
|
|
3684
|
+
const to = from + this.node.nodeSize;
|
|
3685
|
+
this.editor.commands.deleteRange({ from, to });
|
|
2754
3686
|
}
|
|
2755
3687
|
}
|
|
2756
3688
|
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
3689
|
+
class Tracker {
|
|
3690
|
+
constructor(transaction) {
|
|
3691
|
+
this.transaction = transaction;
|
|
3692
|
+
this.currentStep = this.transaction.steps.length;
|
|
3693
|
+
}
|
|
3694
|
+
map(position) {
|
|
3695
|
+
let deleted = false;
|
|
3696
|
+
const mappedPosition = this.transaction.steps
|
|
3697
|
+
.slice(this.currentStep)
|
|
3698
|
+
.reduce((newPosition, step) => {
|
|
3699
|
+
const mapResult = step
|
|
3700
|
+
.getMap()
|
|
3701
|
+
.mapResult(newPosition);
|
|
3702
|
+
if (mapResult.deleted) {
|
|
3703
|
+
deleted = true;
|
|
3704
|
+
}
|
|
3705
|
+
return mapResult.pos;
|
|
3706
|
+
}, position);
|
|
3707
|
+
return {
|
|
3708
|
+
position: mappedPosition,
|
|
3709
|
+
deleted,
|
|
3710
|
+
};
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
/**
|
|
3715
|
+
* Build an input rule that adds a node when the
|
|
3716
|
+
* matched text is typed into it.
|
|
3717
|
+
*/
|
|
3718
|
+
function nodeInputRule(config) {
|
|
3719
|
+
return new InputRule({
|
|
3720
|
+
find: config.find,
|
|
3721
|
+
handler: ({ state, range, match }) => {
|
|
3722
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
|
|
3723
|
+
const { tr } = state;
|
|
3724
|
+
const start = range.from;
|
|
3725
|
+
let end = range.to;
|
|
3726
|
+
if (match[1]) {
|
|
3727
|
+
const offset = match[0].lastIndexOf(match[1]);
|
|
3728
|
+
let matchStart = start + offset;
|
|
3729
|
+
if (matchStart > end) {
|
|
3730
|
+
matchStart = end;
|
|
3731
|
+
}
|
|
3732
|
+
else {
|
|
3733
|
+
end = matchStart + match[1].length;
|
|
3734
|
+
}
|
|
3735
|
+
// insert last typed character
|
|
3736
|
+
const lastChar = match[0][match[0].length - 1];
|
|
3737
|
+
tr.insertText(lastChar, start + match[0].length - 1);
|
|
3738
|
+
// insert node from input rule
|
|
3739
|
+
tr.replaceWith(matchStart, end, config.type.create(attributes));
|
|
3740
|
+
}
|
|
3741
|
+
else if (match[0]) {
|
|
3742
|
+
tr.replaceWith(start, end, config.type.create(attributes));
|
|
3743
|
+
}
|
|
3744
|
+
},
|
|
2767
3745
|
});
|
|
2768
3746
|
}
|
|
2769
3747
|
|
|
2770
3748
|
function getMarksBetween(from, to, state) {
|
|
2771
|
-
|
|
3749
|
+
const marks = [];
|
|
2772
3750
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
2773
|
-
marks
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
3751
|
+
marks.push(...node.marks.map(mark => ({
|
|
3752
|
+
from: pos,
|
|
3753
|
+
to: pos + node.nodeSize,
|
|
3754
|
+
mark,
|
|
3755
|
+
})));
|
|
2778
3756
|
});
|
|
2779
3757
|
return marks;
|
|
2780
3758
|
}
|
|
2781
3759
|
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
const
|
|
2795
|
-
const
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
const
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
3760
|
+
/**
|
|
3761
|
+
* Build an input rule that adds a mark when the
|
|
3762
|
+
* matched text is typed into it.
|
|
3763
|
+
*/
|
|
3764
|
+
function markInputRule(config) {
|
|
3765
|
+
return new InputRule({
|
|
3766
|
+
find: config.find,
|
|
3767
|
+
handler: ({ state, range, match }) => {
|
|
3768
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match);
|
|
3769
|
+
if (attributes === false || attributes === null) {
|
|
3770
|
+
return;
|
|
3771
|
+
}
|
|
3772
|
+
const { tr } = state;
|
|
3773
|
+
const captureGroup = match[match.length - 1];
|
|
3774
|
+
const fullMatch = match[0];
|
|
3775
|
+
let markEnd = range.to;
|
|
3776
|
+
if (captureGroup) {
|
|
3777
|
+
const startSpaces = fullMatch.search(/\S/);
|
|
3778
|
+
const textStart = range.from + fullMatch.indexOf(captureGroup);
|
|
3779
|
+
const textEnd = textStart + captureGroup.length;
|
|
3780
|
+
const excludedMarks = getMarksBetween(range.from, range.to, state)
|
|
3781
|
+
.filter(item => {
|
|
3782
|
+
// @ts-ignore
|
|
3783
|
+
const excluded = item.mark.type.excluded;
|
|
3784
|
+
return excluded.find(type => type === config.type && type !== item.mark.type);
|
|
3785
|
+
})
|
|
3786
|
+
.filter(item => item.to > textStart);
|
|
3787
|
+
if (excludedMarks.length) {
|
|
3788
|
+
return null;
|
|
3789
|
+
}
|
|
3790
|
+
if (textEnd < range.to) {
|
|
3791
|
+
tr.delete(textEnd, range.to);
|
|
3792
|
+
}
|
|
3793
|
+
if (textStart > range.from) {
|
|
3794
|
+
tr.delete(range.from + startSpaces, textStart);
|
|
3795
|
+
}
|
|
3796
|
+
markEnd = range.from + startSpaces + captureGroup.length;
|
|
3797
|
+
tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
|
|
3798
|
+
tr.removeStoredMark(config.type);
|
|
3799
|
+
}
|
|
3800
|
+
},
|
|
3801
|
+
});
|
|
3802
|
+
}
|
|
3803
|
+
|
|
3804
|
+
/**
|
|
3805
|
+
* Build an input rule that changes the type of a textblock when the
|
|
3806
|
+
* matched text is typed into it. When using a regular expresion you’ll
|
|
3807
|
+
* probably want the regexp to start with `^`, so that the pattern can
|
|
3808
|
+
* only occur at the start of a textblock.
|
|
3809
|
+
*/
|
|
3810
|
+
function textblockTypeInputRule(config) {
|
|
3811
|
+
return new InputRule({
|
|
3812
|
+
find: config.find,
|
|
3813
|
+
handler: ({ state, range, match }) => {
|
|
3814
|
+
const $start = state.doc.resolve(range.from);
|
|
3815
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
|
|
3816
|
+
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {
|
|
2804
3817
|
return null;
|
|
2805
3818
|
}
|
|
2806
|
-
|
|
2807
|
-
|
|
3819
|
+
state.tr
|
|
3820
|
+
.delete(range.from, range.to)
|
|
3821
|
+
.setBlockType(range.from, range.from, config.type, attributes);
|
|
3822
|
+
},
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
|
|
3826
|
+
/**
|
|
3827
|
+
* Build an input rule that replaces text when the
|
|
3828
|
+
* matched text is typed into it.
|
|
3829
|
+
*/
|
|
3830
|
+
function textInputRule(config) {
|
|
3831
|
+
return new InputRule({
|
|
3832
|
+
find: config.find,
|
|
3833
|
+
handler: ({ state, range, match }) => {
|
|
3834
|
+
let insert = config.replace;
|
|
3835
|
+
let start = range.from;
|
|
3836
|
+
const end = range.to;
|
|
3837
|
+
if (match[1]) {
|
|
3838
|
+
const offset = match[0].lastIndexOf(match[1]);
|
|
3839
|
+
insert += match[0].slice(offset + match[1].length);
|
|
3840
|
+
start += offset;
|
|
3841
|
+
const cutOff = start - end;
|
|
3842
|
+
if (cutOff > 0) {
|
|
3843
|
+
insert = match[0].slice(offset - cutOff, offset) + insert;
|
|
3844
|
+
start = end;
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
state.tr.insertText(insert, start, end);
|
|
3848
|
+
},
|
|
3849
|
+
});
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3852
|
+
/**
|
|
3853
|
+
* Build an input rule for automatically wrapping a textblock when a
|
|
3854
|
+
* given string is typed. When using a regular expresion you’ll
|
|
3855
|
+
* probably want the regexp to start with `^`, so that the pattern can
|
|
3856
|
+
* only occur at the start of a textblock.
|
|
3857
|
+
*
|
|
3858
|
+
* `type` is the type of node to wrap in.
|
|
3859
|
+
*
|
|
3860
|
+
* By default, if there’s a node with the same type above the newly
|
|
3861
|
+
* wrapped node, the rule will try to join those
|
|
3862
|
+
* two nodes. You can pass a join predicate, which takes a regular
|
|
3863
|
+
* expression match and the node before the wrapped node, and can
|
|
3864
|
+
* return a boolean to indicate whether a join should happen.
|
|
3865
|
+
*/
|
|
3866
|
+
function wrappingInputRule(config) {
|
|
3867
|
+
return new InputRule({
|
|
3868
|
+
find: config.find,
|
|
3869
|
+
handler: ({ state, range, match }) => {
|
|
3870
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match) || {};
|
|
3871
|
+
const tr = state.tr.delete(range.from, range.to);
|
|
3872
|
+
const $start = tr.doc.resolve(range.from);
|
|
3873
|
+
const blockRange = $start.blockRange();
|
|
3874
|
+
const wrapping = blockRange && prosemirrorTransform.findWrapping(blockRange, config.type, attributes);
|
|
3875
|
+
if (!wrapping) {
|
|
3876
|
+
return null;
|
|
2808
3877
|
}
|
|
2809
|
-
|
|
2810
|
-
|
|
3878
|
+
tr.wrap(blockRange, wrapping);
|
|
3879
|
+
const before = tr.doc.resolve(range.from - 1).nodeBefore;
|
|
3880
|
+
if (before
|
|
3881
|
+
&& before.type === config.type
|
|
3882
|
+
&& prosemirrorTransform.canJoin(tr.doc, range.from - 1)
|
|
3883
|
+
&& (!config.joinPredicate || config.joinPredicate(match, before))) {
|
|
3884
|
+
tr.join(range.from - 1);
|
|
2811
3885
|
}
|
|
2812
|
-
|
|
2813
|
-
tr.addMark(start + startSpaces, markEnd, markType.create(attributes));
|
|
2814
|
-
tr.removeStoredMark(markType);
|
|
2815
|
-
}
|
|
2816
|
-
return tr;
|
|
3886
|
+
},
|
|
2817
3887
|
});
|
|
2818
3888
|
}
|
|
2819
3889
|
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
3890
|
+
/**
|
|
3891
|
+
* Build an paste rule that adds a mark when the
|
|
3892
|
+
* matched text is pasted into it.
|
|
3893
|
+
*/
|
|
3894
|
+
function markPasteRule(config) {
|
|
3895
|
+
return new PasteRule({
|
|
3896
|
+
find: config.find,
|
|
3897
|
+
handler: ({ state, range, match }) => {
|
|
3898
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match);
|
|
3899
|
+
if (attributes === false || attributes === null) {
|
|
3900
|
+
return;
|
|
3901
|
+
}
|
|
3902
|
+
const { tr } = state;
|
|
3903
|
+
const captureGroup = match[match.length - 1];
|
|
3904
|
+
const fullMatch = match[0];
|
|
3905
|
+
let markEnd = range.to;
|
|
3906
|
+
if (captureGroup) {
|
|
3907
|
+
const startSpaces = fullMatch.search(/\S/);
|
|
3908
|
+
const textStart = range.from + fullMatch.indexOf(captureGroup);
|
|
3909
|
+
const textEnd = textStart + captureGroup.length;
|
|
3910
|
+
const excludedMarks = getMarksBetween(range.from, range.to, state)
|
|
3911
|
+
.filter(item => {
|
|
3912
|
+
// @ts-ignore
|
|
3913
|
+
const excluded = item.mark.type.excluded;
|
|
3914
|
+
return excluded.find(type => type === config.type && type !== item.mark.type);
|
|
3915
|
+
})
|
|
3916
|
+
.filter(item => item.to > textStart);
|
|
3917
|
+
if (excludedMarks.length) {
|
|
3918
|
+
return null;
|
|
3919
|
+
}
|
|
3920
|
+
if (textEnd < range.to) {
|
|
3921
|
+
tr.delete(textEnd, range.to);
|
|
2849
3922
|
}
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
nodes.push(child.cut(pos));
|
|
3923
|
+
if (textStart > range.from) {
|
|
3924
|
+
tr.delete(range.from + startSpaces, textStart);
|
|
2853
3925
|
}
|
|
3926
|
+
markEnd = range.from + startSpaces + captureGroup.length;
|
|
3927
|
+
tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
|
|
3928
|
+
tr.removeStoredMark(config.type);
|
|
2854
3929
|
}
|
|
2855
|
-
|
|
2856
|
-
|
|
3930
|
+
},
|
|
3931
|
+
});
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
/**
|
|
3935
|
+
* Build an paste rule that replaces text when the
|
|
3936
|
+
* matched text is pasted into it.
|
|
3937
|
+
*/
|
|
3938
|
+
function textPasteRule(config) {
|
|
3939
|
+
return new PasteRule({
|
|
3940
|
+
find: config.find,
|
|
3941
|
+
handler: ({ state, range, match }) => {
|
|
3942
|
+
let insert = config.replace;
|
|
3943
|
+
let start = range.from;
|
|
3944
|
+
const end = range.to;
|
|
3945
|
+
if (match[1]) {
|
|
3946
|
+
const offset = match[0].lastIndexOf(match[1]);
|
|
3947
|
+
insert += match[0].slice(offset + match[1].length);
|
|
3948
|
+
start += offset;
|
|
3949
|
+
const cutOff = start - end;
|
|
3950
|
+
if (cutOff > 0) {
|
|
3951
|
+
insert = match[0].slice(offset - cutOff, offset) + insert;
|
|
3952
|
+
start = end;
|
|
3953
|
+
}
|
|
2857
3954
|
}
|
|
2858
|
-
|
|
2859
|
-
return prosemirrorModel.Fragment.fromArray(nodes);
|
|
2860
|
-
};
|
|
2861
|
-
return new prosemirrorState.Plugin({
|
|
2862
|
-
key: new prosemirrorState.PluginKey('markPasteRule'),
|
|
2863
|
-
props: {
|
|
2864
|
-
transformPasted: slice => {
|
|
2865
|
-
return new prosemirrorModel.Slice(handler(slice.content), slice.openStart, slice.openEnd);
|
|
2866
|
-
},
|
|
3955
|
+
state.tr.insertText(insert, start, end);
|
|
2867
3956
|
},
|
|
2868
3957
|
});
|
|
2869
3958
|
}
|
|
2870
3959
|
|
|
3960
|
+
function findChildren(node, predicate) {
|
|
3961
|
+
const nodesWithPos = [];
|
|
3962
|
+
node.descendants((child, pos) => {
|
|
3963
|
+
if (predicate(child)) {
|
|
3964
|
+
nodesWithPos.push({
|
|
3965
|
+
node: child,
|
|
3966
|
+
pos,
|
|
3967
|
+
});
|
|
3968
|
+
}
|
|
3969
|
+
});
|
|
3970
|
+
return nodesWithPos;
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
/**
|
|
3974
|
+
* Same as `findChildren` but searches only within a `range`.
|
|
3975
|
+
*/
|
|
3976
|
+
function findChildrenInRange(node, range, predicate) {
|
|
3977
|
+
const nodesWithPos = [];
|
|
3978
|
+
// if (range.from === range.to) {
|
|
3979
|
+
// const nodeAt = node.nodeAt(range.from)
|
|
3980
|
+
// if (nodeAt) {
|
|
3981
|
+
// nodesWithPos.push({
|
|
3982
|
+
// node: nodeAt,
|
|
3983
|
+
// pos: range.from,
|
|
3984
|
+
// })
|
|
3985
|
+
// }
|
|
3986
|
+
// }
|
|
3987
|
+
node.nodesBetween(range.from, range.to, (child, pos) => {
|
|
3988
|
+
if (predicate(child)) {
|
|
3989
|
+
nodesWithPos.push({
|
|
3990
|
+
node: child,
|
|
3991
|
+
pos,
|
|
3992
|
+
});
|
|
3993
|
+
}
|
|
3994
|
+
});
|
|
3995
|
+
return nodesWithPos;
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3998
|
+
function getSchema(extensions) {
|
|
3999
|
+
const resolvedExtensions = ExtensionManager.resolve(extensions);
|
|
4000
|
+
return getSchemaByResolvedExtensions(resolvedExtensions);
|
|
4001
|
+
}
|
|
4002
|
+
|
|
2871
4003
|
function generateHTML(doc, extensions) {
|
|
2872
4004
|
const schema = getSchema(extensions);
|
|
2873
4005
|
const contentNode = prosemirrorModel.Node.fromJSON(schema, doc);
|
|
2874
|
-
return getHTMLFromFragment(contentNode, schema);
|
|
4006
|
+
return getHTMLFromFragment(contentNode.content, schema);
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
function generateJSON(html, extensions) {
|
|
4010
|
+
const schema = getSchema(extensions);
|
|
4011
|
+
const dom = elementFromString(html);
|
|
4012
|
+
return prosemirrorModel.DOMParser.fromSchema(schema)
|
|
4013
|
+
.parse(dom)
|
|
4014
|
+
.toJSON();
|
|
4015
|
+
}
|
|
4016
|
+
|
|
4017
|
+
function generateText(doc, extensions, options) {
|
|
4018
|
+
const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
|
|
4019
|
+
const schema = getSchema(extensions);
|
|
4020
|
+
const contentNode = prosemirrorModel.Node.fromJSON(schema, doc);
|
|
4021
|
+
return getText(contentNode, {
|
|
4022
|
+
blockSeparator,
|
|
4023
|
+
textSerializers: {
|
|
4024
|
+
...textSerializers,
|
|
4025
|
+
...getTextSeralizersFromSchema(schema),
|
|
4026
|
+
},
|
|
4027
|
+
});
|
|
4028
|
+
}
|
|
4029
|
+
|
|
4030
|
+
function getDebugJSON(node, startOffset = 0) {
|
|
4031
|
+
const isTopNode = node.type === node.type.schema.topNodeType;
|
|
4032
|
+
const increment = isTopNode ? 0 : 1;
|
|
4033
|
+
const from = startOffset; // + offset
|
|
4034
|
+
const to = from + node.nodeSize;
|
|
4035
|
+
const marks = node.marks.map(mark => ({
|
|
4036
|
+
type: mark.type.name,
|
|
4037
|
+
attrs: { ...mark.attrs },
|
|
4038
|
+
}));
|
|
4039
|
+
const attrs = { ...node.attrs };
|
|
4040
|
+
const output = {
|
|
4041
|
+
type: node.type.name,
|
|
4042
|
+
from,
|
|
4043
|
+
to,
|
|
4044
|
+
};
|
|
4045
|
+
if (Object.keys(attrs).length) {
|
|
4046
|
+
output.attrs = attrs;
|
|
4047
|
+
}
|
|
4048
|
+
if (marks.length) {
|
|
4049
|
+
output.marks = marks;
|
|
4050
|
+
}
|
|
4051
|
+
if (node.content.childCount) {
|
|
4052
|
+
output.content = [];
|
|
4053
|
+
node.forEach((child, offset) => {
|
|
4054
|
+
var _a;
|
|
4055
|
+
(_a = output.content) === null || _a === void 0 ? void 0 : _a.push(getDebugJSON(child, startOffset + offset + increment));
|
|
4056
|
+
});
|
|
4057
|
+
}
|
|
4058
|
+
if (node.text) {
|
|
4059
|
+
output.text = node.text;
|
|
4060
|
+
}
|
|
4061
|
+
return output;
|
|
2875
4062
|
}
|
|
2876
4063
|
|
|
2877
4064
|
function isNodeSelection(value) {
|
|
2878
4065
|
return isObject(value) && value instanceof prosemirrorState.NodeSelection;
|
|
2879
4066
|
}
|
|
2880
4067
|
|
|
4068
|
+
function posToDOMRect(view, from, to) {
|
|
4069
|
+
const minPos = 0;
|
|
4070
|
+
const maxPos = view.state.doc.content.size;
|
|
4071
|
+
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
4072
|
+
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
4073
|
+
const start = view.coordsAtPos(resolvedFrom);
|
|
4074
|
+
const end = view.coordsAtPos(resolvedEnd, -1);
|
|
4075
|
+
const top = Math.min(start.top, end.top);
|
|
4076
|
+
const bottom = Math.max(start.bottom, end.bottom);
|
|
4077
|
+
const left = Math.min(start.left, end.left);
|
|
4078
|
+
const right = Math.max(start.right, end.right);
|
|
4079
|
+
const width = right - left;
|
|
4080
|
+
const height = bottom - top;
|
|
4081
|
+
const x = left;
|
|
4082
|
+
const y = top;
|
|
4083
|
+
const data = {
|
|
4084
|
+
top,
|
|
4085
|
+
bottom,
|
|
4086
|
+
left,
|
|
4087
|
+
right,
|
|
4088
|
+
width,
|
|
4089
|
+
height,
|
|
4090
|
+
x,
|
|
4091
|
+
y,
|
|
4092
|
+
};
|
|
4093
|
+
return {
|
|
4094
|
+
...data,
|
|
4095
|
+
toJSON: () => data,
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
4098
|
+
|
|
2881
4099
|
exports.Editor = Editor;
|
|
2882
4100
|
exports.Extension = Extension;
|
|
4101
|
+
exports.InputRule = InputRule;
|
|
2883
4102
|
exports.Mark = Mark;
|
|
2884
4103
|
exports.Node = Node;
|
|
2885
4104
|
exports.NodeView = NodeView;
|
|
4105
|
+
exports.PasteRule = PasteRule;
|
|
4106
|
+
exports.Tracker = Tracker;
|
|
2886
4107
|
exports.callOrReturn = callOrReturn;
|
|
2887
4108
|
exports.extensions = extensions;
|
|
4109
|
+
exports.findChildren = findChildren;
|
|
4110
|
+
exports.findChildrenInRange = findChildrenInRange;
|
|
4111
|
+
exports.findParentNode = findParentNode;
|
|
2888
4112
|
exports.findParentNodeClosestToPos = findParentNodeClosestToPos;
|
|
2889
4113
|
exports.generateHTML = generateHTML;
|
|
4114
|
+
exports.generateJSON = generateJSON;
|
|
4115
|
+
exports.generateText = generateText;
|
|
4116
|
+
exports.getAttributes = getAttributes;
|
|
4117
|
+
exports.getDebugJSON = getDebugJSON;
|
|
4118
|
+
exports.getExtensionField = getExtensionField;
|
|
2890
4119
|
exports.getHTMLFromFragment = getHTMLFromFragment;
|
|
2891
4120
|
exports.getMarkAttributes = getMarkAttributes;
|
|
4121
|
+
exports.getMarkRange = getMarkRange;
|
|
4122
|
+
exports.getMarkType = getMarkType;
|
|
4123
|
+
exports.getMarksBetween = getMarksBetween;
|
|
4124
|
+
exports.getNodeAttributes = getNodeAttributes;
|
|
4125
|
+
exports.getNodeType = getNodeType;
|
|
2892
4126
|
exports.getSchema = getSchema;
|
|
4127
|
+
exports.getText = getText;
|
|
4128
|
+
exports.getTextBetween = getTextBetween;
|
|
4129
|
+
exports.inputRulesPlugin = inputRulesPlugin;
|
|
2893
4130
|
exports.isActive = isActive;
|
|
4131
|
+
exports.isList = isList;
|
|
2894
4132
|
exports.isMarkActive = isMarkActive;
|
|
2895
4133
|
exports.isNodeActive = isNodeActive;
|
|
2896
4134
|
exports.isNodeEmpty = isNodeEmpty;
|
|
@@ -2900,4 +4138,10 @@ exports.markInputRule = markInputRule;
|
|
|
2900
4138
|
exports.markPasteRule = markPasteRule;
|
|
2901
4139
|
exports.mergeAttributes = mergeAttributes;
|
|
2902
4140
|
exports.nodeInputRule = nodeInputRule;
|
|
4141
|
+
exports.pasteRulesPlugin = pasteRulesPlugin;
|
|
4142
|
+
exports.posToDOMRect = posToDOMRect;
|
|
4143
|
+
exports.textInputRule = textInputRule;
|
|
4144
|
+
exports.textPasteRule = textPasteRule;
|
|
4145
|
+
exports.textblockTypeInputRule = textblockTypeInputRule;
|
|
4146
|
+
exports.wrappingInputRule = wrappingInputRule;
|
|
2903
4147
|
//# sourceMappingURL=tiptap-core.cjs.js.map
|