@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.
Files changed (272) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +2 -2
  3. package/dist/packages/core/src/CommandManager.d.ts +12 -5
  4. package/dist/packages/core/src/Editor.d.ts +19 -20
  5. package/dist/packages/core/src/EventEmitter.d.ts +8 -4
  6. package/dist/packages/core/src/Extension.d.ts +102 -26
  7. package/dist/packages/core/src/ExtensionManager.d.ts +6 -11
  8. package/dist/packages/core/src/InputRule.d.ts +42 -0
  9. package/dist/packages/core/src/Mark.d.ts +134 -30
  10. package/dist/packages/core/src/Node.d.ts +151 -32
  11. package/dist/packages/core/src/NodeView.d.ts +5 -9
  12. package/dist/packages/core/src/PasteRule.d.ts +42 -0
  13. package/dist/packages/core/src/Tracker.d.ts +11 -0
  14. package/dist/packages/core/src/commands/blur.d.ts +3 -3
  15. package/dist/packages/core/src/commands/clearContent.d.ts +3 -3
  16. package/dist/packages/core/src/commands/clearNodes.d.ts +3 -3
  17. package/dist/packages/core/src/commands/command.d.ts +2 -2
  18. package/dist/packages/core/src/commands/createParagraphNear.d.ts +3 -3
  19. package/dist/packages/core/src/commands/deleteNode.d.ts +13 -0
  20. package/dist/packages/core/src/commands/deleteRange.d.ts +3 -3
  21. package/dist/packages/core/src/commands/deleteSelection.d.ts +3 -3
  22. package/dist/packages/core/src/commands/enter.d.ts +3 -3
  23. package/dist/packages/core/src/commands/exitCode.d.ts +3 -3
  24. package/dist/packages/core/src/commands/extendMarkRange.d.ts +3 -3
  25. package/dist/packages/core/src/commands/first.d.ts +3 -3
  26. package/dist/packages/core/src/commands/focus.d.ts +3 -3
  27. package/dist/packages/core/src/commands/forEach.d.ts +14 -0
  28. package/dist/packages/core/src/commands/insertContent.d.ts +16 -0
  29. package/dist/packages/core/src/commands/insertContentAt.d.ts +16 -0
  30. package/dist/packages/core/src/commands/joinBackward.d.ts +3 -3
  31. package/dist/packages/core/src/commands/joinForward.d.ts +3 -3
  32. package/dist/packages/core/src/commands/keyboardShortcut.d.ts +3 -3
  33. package/dist/packages/core/src/commands/lift.d.ts +3 -3
  34. package/dist/packages/core/src/commands/liftEmptyBlock.d.ts +3 -3
  35. package/dist/packages/core/src/commands/liftListItem.d.ts +3 -3
  36. package/dist/packages/core/src/commands/newlineInCode.d.ts +3 -3
  37. package/dist/packages/core/src/commands/resetAttributes.d.ts +13 -0
  38. package/dist/packages/core/src/commands/scrollIntoView.d.ts +3 -3
  39. package/dist/packages/core/src/commands/selectAll.d.ts +3 -3
  40. package/dist/packages/core/src/commands/selectNodeBackward.d.ts +3 -3
  41. package/dist/packages/core/src/commands/selectNodeForward.d.ts +3 -3
  42. package/dist/packages/core/src/commands/selectParentNode.d.ts +3 -3
  43. package/dist/packages/core/src/commands/setContent.d.ts +4 -3
  44. package/dist/packages/core/src/commands/setMark.d.ts +3 -3
  45. package/dist/packages/core/src/commands/setMeta.d.ts +12 -0
  46. package/dist/packages/core/src/commands/setNode.d.ts +3 -3
  47. package/dist/packages/core/src/commands/setNodeSelection.d.ts +12 -0
  48. package/dist/packages/core/src/commands/setTextSelection.d.ts +12 -0
  49. package/dist/packages/core/src/commands/sinkListItem.d.ts +3 -3
  50. package/dist/packages/core/src/commands/splitBlock.d.ts +3 -3
  51. package/dist/packages/core/src/commands/splitListItem.d.ts +3 -3
  52. package/dist/packages/core/src/commands/toggleList.d.ts +3 -3
  53. package/dist/packages/core/src/commands/toggleMark.d.ts +8 -3
  54. package/dist/packages/core/src/commands/toggleNode.d.ts +3 -3
  55. package/dist/packages/core/src/commands/toggleWrap.d.ts +3 -3
  56. package/dist/packages/core/src/commands/undoInputRule.d.ts +3 -3
  57. package/dist/packages/core/src/commands/unsetAllMarks.d.ts +3 -3
  58. package/dist/packages/core/src/commands/unsetMark.d.ts +8 -3
  59. package/dist/packages/core/src/commands/updateAttributes.d.ts +13 -0
  60. package/dist/packages/core/src/commands/wrapIn.d.ts +3 -3
  61. package/dist/packages/core/src/commands/wrapInList.d.ts +3 -3
  62. package/dist/packages/core/src/extensions/clipboardTextSerializer.d.ts +1 -1
  63. package/dist/packages/core/src/extensions/commands.d.ts +19 -15
  64. package/dist/packages/core/src/extensions/editable.d.ts +1 -1
  65. package/dist/packages/core/src/extensions/focusEvents.d.ts +1 -1
  66. package/dist/packages/core/src/extensions/index.d.ts +1 -0
  67. package/dist/packages/core/src/extensions/keymap.d.ts +1 -1
  68. package/dist/packages/core/src/extensions/tabindex.d.ts +2 -0
  69. package/dist/packages/core/src/helpers/createChainableState.d.ts +5 -0
  70. package/dist/packages/core/src/helpers/createDocument.d.ts +3 -0
  71. package/dist/packages/core/src/helpers/createNodeFromContent.d.ts +7 -0
  72. package/dist/packages/core/src/helpers/findChildren.d.ts +3 -0
  73. package/dist/packages/core/src/helpers/findChildrenInRange.d.ts +6 -0
  74. package/dist/packages/core/src/helpers/generateHTML.d.ts +2 -2
  75. package/dist/packages/core/src/helpers/generateJSON.d.ts +2 -0
  76. package/dist/packages/core/src/helpers/generateText.d.ts +5 -0
  77. package/dist/packages/core/src/helpers/getAttributes.d.ts +3 -0
  78. package/dist/packages/core/src/helpers/getDebugJSON.d.ts +8 -0
  79. package/dist/packages/core/src/helpers/getExtensionField.d.ts +2 -0
  80. package/dist/packages/core/src/helpers/getHTMLFromFragment.d.ts +2 -2
  81. package/dist/packages/core/src/helpers/getMarkAttributes.d.ts +1 -2
  82. package/dist/packages/core/src/helpers/getMarkRange.d.ts +1 -1
  83. package/dist/packages/core/src/helpers/getNodeAttributes.d.ts +1 -2
  84. package/dist/packages/core/src/helpers/getRenderedAttributes.d.ts +2 -2
  85. package/dist/packages/core/src/helpers/getSchemaByResolvedExtensions.d.ts +3 -0
  86. package/dist/packages/core/src/helpers/getSplittedAttributes.d.ts +2 -2
  87. package/dist/packages/core/src/helpers/getText.d.ts +6 -0
  88. package/dist/packages/core/src/helpers/getTextBetween.d.ts +6 -0
  89. package/dist/packages/core/src/helpers/getTextSeralizersFromSchema.d.ts +3 -0
  90. package/dist/packages/core/src/helpers/isActive.d.ts +1 -2
  91. package/dist/packages/core/src/helpers/isExtensionRulesEnabled.d.ts +2 -0
  92. package/dist/packages/core/src/helpers/isMarkActive.d.ts +1 -2
  93. package/dist/packages/core/src/helpers/isNodeActive.d.ts +1 -2
  94. package/dist/packages/core/src/helpers/posToDOMRect.d.ts +2 -0
  95. package/dist/packages/core/src/helpers/splitExtensions.d.ts +3 -3
  96. package/dist/packages/core/src/index.d.ts +32 -5
  97. package/dist/packages/core/src/inputRules/markInputRule.d.ts +11 -2
  98. package/dist/packages/core/src/inputRules/nodeInputRule.d.ts +11 -2
  99. package/dist/packages/core/src/inputRules/textInputRule.d.ts +9 -0
  100. package/dist/packages/core/src/inputRules/textblockTypeInputRule.d.ts +14 -0
  101. package/dist/packages/core/src/inputRules/wrappingInputRule.d.ts +23 -0
  102. package/dist/packages/core/src/pasteRules/markPasteRule.d.ts +11 -2
  103. package/dist/packages/core/src/pasteRules/textPasteRule.d.ts +9 -0
  104. package/dist/packages/core/src/style.d.ts +1 -1
  105. package/dist/packages/core/src/types.d.ts +104 -51
  106. package/dist/packages/core/src/utilities/callOrReturn.d.ts +2 -1
  107. package/dist/packages/core/src/utilities/deleteProps.d.ts +1 -2
  108. package/dist/packages/core/src/utilities/findDuplicates.d.ts +1 -0
  109. package/dist/packages/core/src/utilities/isClass.d.ts +1 -1
  110. package/dist/packages/core/src/utilities/isEmptyObject.d.ts +1 -1
  111. package/dist/packages/core/src/utilities/isFunction.d.ts +1 -0
  112. package/dist/packages/core/src/utilities/isNumber.d.ts +1 -0
  113. package/dist/packages/core/src/utilities/isObject.d.ts +1 -1
  114. package/dist/packages/core/src/utilities/isPlainObject.d.ts +1 -1
  115. package/dist/packages/core/src/utilities/isRegExp.d.ts +1 -0
  116. package/dist/packages/core/src/utilities/isiOS.d.ts +1 -0
  117. package/dist/packages/core/src/utilities/mergeAttributes.d.ts +1 -2
  118. package/dist/packages/core/src/utilities/mergeDeep.d.ts +1 -2
  119. package/dist/packages/core/src/utilities/objectIncludes.d.ts +3 -2
  120. package/dist/tiptap-core.cjs.js +2695 -1451
  121. package/dist/tiptap-core.cjs.js.map +1 -1
  122. package/dist/tiptap-core.esm.js +3244 -2026
  123. package/dist/tiptap-core.esm.js.map +1 -1
  124. package/dist/tiptap-core.umd.js +3182 -1937
  125. package/dist/tiptap-core.umd.js.map +1 -1
  126. package/package.json +19 -16
  127. package/src/CommandManager.ts +59 -61
  128. package/src/Editor.ts +91 -95
  129. package/src/EventEmitter.ts +14 -4
  130. package/src/Extension.ts +202 -40
  131. package/src/ExtensionManager.ts +234 -70
  132. package/src/InputRule.ts +268 -0
  133. package/src/Mark.ts +248 -46
  134. package/src/Node.ts +275 -51
  135. package/src/NodeView.ts +105 -42
  136. package/src/PasteRule.ts +215 -0
  137. package/src/Tracker.ts +42 -0
  138. package/src/commands/blur.ts +9 -7
  139. package/src/commands/clearContent.ts +3 -3
  140. package/src/commands/clearNodes.ts +25 -19
  141. package/src/commands/command.ts +2 -2
  142. package/src/commands/createParagraphNear.ts +3 -3
  143. package/src/commands/deleteNode.ts +36 -0
  144. package/src/commands/deleteRange.ts +3 -3
  145. package/src/commands/deleteSelection.ts +3 -3
  146. package/src/commands/enter.ts +3 -3
  147. package/src/commands/exitCode.ts +3 -3
  148. package/src/commands/extendMarkRange.ts +8 -8
  149. package/src/commands/first.ts +3 -3
  150. package/src/commands/focus.ts +31 -9
  151. package/src/commands/forEach.ts +24 -0
  152. package/src/commands/insertContent.ts +23 -0
  153. package/src/commands/insertContentAt.ts +93 -0
  154. package/src/commands/joinBackward.ts +3 -3
  155. package/src/commands/joinForward.ts +3 -3
  156. package/src/commands/keyboardShortcut.ts +3 -3
  157. package/src/commands/lift.ts +3 -3
  158. package/src/commands/liftEmptyBlock.ts +3 -3
  159. package/src/commands/liftListItem.ts +3 -3
  160. package/src/commands/newlineInCode.ts +3 -3
  161. package/src/commands/resetAttributes.ts +61 -0
  162. package/src/commands/scrollIntoView.ts +3 -3
  163. package/src/commands/selectAll.ts +8 -6
  164. package/src/commands/selectNodeBackward.ts +3 -3
  165. package/src/commands/selectNodeForward.ts +3 -3
  166. package/src/commands/selectParentNode.ts +3 -3
  167. package/src/commands/setContent.ts +10 -5
  168. package/src/commands/setMark.ts +33 -10
  169. package/src/commands/setMeta.ts +18 -0
  170. package/src/commands/setNode.ts +3 -3
  171. package/src/commands/setNodeSelection.ts +28 -0
  172. package/src/commands/setTextSelection.ts +32 -0
  173. package/src/commands/sinkListItem.ts +3 -3
  174. package/src/commands/splitBlock.ts +13 -8
  175. package/src/commands/splitListItem.ts +39 -13
  176. package/src/commands/toggleList.ts +4 -4
  177. package/src/commands/toggleMark.ts +15 -5
  178. package/src/commands/toggleNode.ts +3 -3
  179. package/src/commands/toggleWrap.ts +3 -3
  180. package/src/commands/undoInputRule.ts +33 -5
  181. package/src/commands/unsetAllMarks.ts +3 -3
  182. package/src/commands/unsetMark.ts +32 -20
  183. package/src/commands/updateAttributes.ts +72 -0
  184. package/src/commands/wrapIn.ts +3 -3
  185. package/src/commands/wrapInList.ts +3 -3
  186. package/src/extensions/clipboardTextSerializer.ts +11 -35
  187. package/src/extensions/commands.ts +27 -21
  188. package/src/extensions/focusEvents.ts +0 -3
  189. package/src/extensions/index.ts +1 -0
  190. package/src/extensions/keymap.ts +6 -6
  191. package/src/extensions/tabindex.ts +19 -0
  192. package/src/helpers/createChainableState.ts +37 -0
  193. package/src/helpers/createDocument.ts +11 -0
  194. package/src/helpers/createNodeFromContent.ts +56 -0
  195. package/src/helpers/findChildren.ts +17 -0
  196. package/src/helpers/findChildrenInRange.ts +31 -0
  197. package/src/helpers/generateHTML.ts +3 -3
  198. package/src/helpers/generateJSON.ts +13 -0
  199. package/src/helpers/generateText.ts +29 -0
  200. package/src/helpers/getAttributes.ts +27 -0
  201. package/src/helpers/getAttributesFromExtensions.ts +26 -5
  202. package/src/helpers/getDebugJSON.ts +46 -0
  203. package/src/helpers/getExtensionField.ts +25 -0
  204. package/src/helpers/getHTMLFromFragment.ts +5 -5
  205. package/src/helpers/getMarkAttributes.ts +11 -8
  206. package/src/helpers/getMarkRange.ts +34 -6
  207. package/src/helpers/getMarkType.ts +4 -0
  208. package/src/helpers/getMarksBetween.ts +3 -3
  209. package/src/helpers/getNodeAttributes.ts +6 -7
  210. package/src/helpers/getNodeType.ts +4 -0
  211. package/src/helpers/getRenderedAttributes.ts +3 -5
  212. package/src/helpers/getSchema.ts +5 -132
  213. package/src/helpers/getSchemaByResolvedExtensions.ts +147 -0
  214. package/src/helpers/getSchemaTypeByName.ts +1 -9
  215. package/src/helpers/getSplittedAttributes.ts +3 -3
  216. package/src/helpers/getText.ts +18 -0
  217. package/src/helpers/getTextBetween.ts +45 -0
  218. package/src/helpers/getTextSeralizersFromSchema.ts +9 -0
  219. package/src/helpers/injectExtensionAttributesToParseRule.ts +12 -8
  220. package/src/helpers/isActive.ts +1 -2
  221. package/src/helpers/isExtensionRulesEnabled.ts +15 -0
  222. package/src/helpers/isList.ts +11 -4
  223. package/src/helpers/isMarkActive.ts +40 -16
  224. package/src/helpers/isNodeActive.ts +23 -31
  225. package/src/helpers/posToDOMRect.ts +34 -0
  226. package/src/index.ts +34 -5
  227. package/src/inputRules/markInputRule.ts +57 -38
  228. package/src/inputRules/nodeInputRule.ts +43 -11
  229. package/src/inputRules/textInputRule.ts +35 -0
  230. package/src/inputRules/textblockTypeInputRule.ts +37 -0
  231. package/src/inputRules/wrappingInputRule.ts +59 -0
  232. package/src/pasteRules/markPasteRule.ts +61 -53
  233. package/src/pasteRules/textPasteRule.ts +35 -0
  234. package/src/style.ts +14 -0
  235. package/src/types.ts +118 -36
  236. package/src/utilities/callOrReturn.ts +6 -3
  237. package/src/utilities/createStyleTag.ts +7 -0
  238. package/src/utilities/deleteProps.ts +2 -4
  239. package/src/utilities/elementFromString.ts +3 -4
  240. package/src/utilities/findDuplicates.ts +5 -0
  241. package/src/utilities/fromString.ts +1 -1
  242. package/src/utilities/isClass.ts +2 -2
  243. package/src/utilities/isEmptyObject.ts +2 -2
  244. package/src/utilities/isFunction.ts +3 -0
  245. package/src/utilities/isNumber.ts +3 -0
  246. package/src/utilities/isObject.ts +5 -5
  247. package/src/utilities/isPlainObject.ts +5 -5
  248. package/src/utilities/isRegExp.ts +3 -0
  249. package/src/utilities/isString.ts +3 -0
  250. package/src/utilities/isiOS.ts +12 -0
  251. package/src/utilities/mergeAttributes.ts +2 -3
  252. package/src/utilities/mergeDeep.ts +1 -2
  253. package/src/utilities/objectIncludes.ts +17 -5
  254. package/CHANGELOG.md +0 -294
  255. package/dist/packages/core/src/commands/insertHTML.d.ts +0 -12
  256. package/dist/packages/core/src/commands/insertNode.d.ts +0 -13
  257. package/dist/packages/core/src/commands/insertText.d.ts +0 -12
  258. package/dist/packages/core/src/commands/replace.d.ts +0 -13
  259. package/dist/packages/core/src/commands/replaceRange.d.ts +0 -13
  260. package/dist/packages/core/src/commands/resetNodeAttributes.d.ts +0 -13
  261. package/dist/packages/core/src/commands/updateNodeAttributes.d.ts +0 -13
  262. package/dist/packages/core/src/utilities/removeElement.d.ts +0 -1
  263. package/dist/tiptap-core.bundle.umd.min.js +0 -17
  264. package/dist/tiptap-core.bundle.umd.min.js.map +0 -1
  265. package/src/commands/insertHTML.ts +0 -28
  266. package/src/commands/insertNode.ts +0 -31
  267. package/src/commands/insertText.ts +0 -20
  268. package/src/commands/replace.ts +0 -20
  269. package/src/commands/replaceRange.ts +0 -36
  270. package/src/commands/resetNodeAttributes.ts +0 -31
  271. package/src/commands/updateNodeAttributes.ts +0 -33
  272. package/src/utilities/removeElement.ts +0 -5
@@ -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
- function elementFromString(value) {
15
- const htmlString = `<div>${value}</div>`;
16
- const parser = new window.DOMParser();
17
- const element = parser.parseFromString(htmlString, 'text/html').body;
18
- return element;
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 getNodeType(nameOrType, schema) {
22
- if (typeof nameOrType === 'string') {
23
- return schema.nodes[nameOrType];
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 nameOrType;
40
+ return output;
26
41
  }
27
42
 
28
- function getNodeAttributes(state, typeOrName) {
29
- const type = getNodeType(typeOrName, state.schema);
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
- function getMarkType(nameOrType, schema) {
45
- if (typeof nameOrType === 'string') {
46
- return schema.marks[nameOrType];
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 nameOrType;
61
+ return value;
49
62
  }
50
63
 
51
- function getMarkAttributes(state, typeOrName) {
52
- const type = getMarkType(typeOrName, state.schema);
53
- const { from, to, empty } = state.selection;
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
- else {
59
- state.doc.nodesBetween(from, to, node => {
60
- marks = [...marks, ...node.marks];
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
- const mark = marks.find(markItem => markItem.type.name === type.name);
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
- function isNodeActive(state, typeOrName, attributes = {}) {
86
- const { from, to, empty } = state.selection;
87
- const type = typeOrName
88
- ? getNodeType(typeOrName, state.schema)
89
- : null;
90
- let nodeRanges = [];
91
- state.doc.nodesBetween(from, to, (node, pos) => {
92
- if (!node.isText) {
93
- const relativeFrom = Math.max(from, pos);
94
- const relativeTo = Math.min(to, pos + node.nodeSize);
95
- nodeRanges = [...nodeRanges, {
96
- node,
97
- from: relativeFrom,
98
- to: relativeTo,
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
- if (empty) {
103
- return !!nodeRanges
104
- .filter(nodeRange => {
105
- if (!type) {
106
- return true;
107
- }
108
- return type.name === nodeRange.node.type.name;
109
- })
110
- .find(nodeRange => objectIncludes(nodeRange.node.attrs, attributes));
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
- const selectionRange = to - from;
113
- const range = nodeRanges
114
- .filter(nodeRange => {
115
- if (!type) {
116
- return true;
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
- return type.name === nodeRange.node.type.name;
119
- })
120
- .filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes))
121
- .reduce((sum, nodeRange) => {
122
- const size = nodeRange.to - nodeRange.from;
123
- return sum + size;
124
- }, 0);
125
- return range >= selectionRange;
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 isMarkActive(state, typeOrName, attributes = {}) {
129
- const { from, to, empty } = state.selection;
130
- const type = typeOrName
131
- ? getMarkType(typeOrName, state.schema)
132
- : null;
133
- if (empty) {
134
- return !!(state.storedMarks || state.selection.$from.marks())
135
- .filter(mark => {
136
- if (!type) {
137
- return true;
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
- return type.name === mark.type.name;
140
- })
141
- .find(mark => objectIncludes(mark.attrs, attributes));
142
- }
143
- let selectionRange = 0;
144
- let markRanges = [];
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
- if (selectionRange === 0) {
159
- return false;
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
- return type.name === markRange.mark.type.name;
167
- })
168
- .filter(markRange => objectIncludes(markRange.mark.attrs, attributes))
169
- .reduce((sum, markRange) => {
170
- const size = markRange.to - markRange.from;
171
- return sum + size;
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 getSchemaTypeNameByName(name, schema) {
177
- if (schema.nodes[name]) {
178
- return 'node';
179
- }
180
- if (schema.marks[name]) {
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
- function isActive(state, name, attributes = {}) {
187
- if (!name) {
188
- return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
189
- }
190
- const schemaType = getSchemaTypeNameByName(name, state.schema);
191
- if (schemaType === 'node') {
192
- return isNodeActive(state, name, attributes);
193
- }
194
- if (schemaType === 'mark') {
195
- return isMarkActive(state, name, attributes);
196
- }
197
- return false;
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
- function removeElement(element) {
201
- if (element && element.parentNode) {
202
- element.parentNode.removeChild(element);
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
- function getHTMLFromFragment(doc, schema) {
207
- const fragment = prosemirrorModel.DOMSerializer
208
- .fromSchema(schema)
209
- .serializeFragment(doc.content);
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
- function isNodeEmpty(node) {
217
- var _a;
218
- const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
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
- function createStyleTag(style) {
224
- const styleNode = document.createElement('style');
225
- styleNode.innerHTML = style;
226
- document.getElementsByTagName('head')[0].appendChild(styleNode);
227
- return styleNode;
228
- }
232
+ var clearContent$1 = /*#__PURE__*/Object.freeze({
233
+ __proto__: null,
234
+ clearContent: clearContent
235
+ });
229
236
 
230
- class CommandManager {
231
- constructor(editor, commands) {
232
- this.editor = editor;
233
- this.commands = commands;
234
- }
235
- createCommands() {
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
- if (key === 'class') {
439
- mergedAttributes[key] = [mergedAttributes[key], value].join(' ');
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
- else if (key === 'style') {
442
- mergedAttributes[key] = [mergedAttributes[key], value].join('; ');
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
- else {
445
- mergedAttributes[key] = value;
256
+ if ((targetLiftDepth || targetLiftDepth === 0) && dispatch) {
257
+ tr.lift(nodeRange, targetLiftDepth);
446
258
  }
447
259
  });
448
- return mergedAttributes;
449
- }, {});
450
- }
260
+ });
261
+ return true;
262
+ };
451
263
 
452
- function getRenderedAttributes(nodeOrMark, extensionAttributes) {
453
- return extensionAttributes
454
- .filter(item => item.attribute.rendered)
455
- .map(item => {
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
- function isEmptyObject(object = {}) {
469
- return Object.keys(object).length === 0 && object.constructor === Object;
470
- }
269
+ const command = fn => props => {
270
+ return fn(props);
271
+ };
471
272
 
472
- function fromString(value) {
473
- if (typeof value !== 'string') {
474
- return value;
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
- * This function merges extension attributes into parserule attributes (`attrs` or `getAttrs`).
490
- * Cancels when `getAttrs` returned `false`.
491
- * @param parseRule ProseMirror ParseRule
492
- * @param extensionAttributes List of attributes to inject
493
- */
494
- function injectExtensionAttributesToParseRule(parseRule, extensionAttributes) {
495
- if (parseRule.style) {
496
- return parseRule;
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
- * Optionally calls `value` as a function.
529
- * Otherwise it is returned directly.
530
- * @param value Function or any value.
531
- * @param context Optional context to bind to function.
532
- * @param props Optional props to pass to function.
533
- */
534
- function callOrReturn(value, context = undefined, ...props) {
535
- if (typeof value === 'function') {
536
- if (context) {
537
- return value.bind(context)(...props);
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 value;
542
- }
311
+ return false;
312
+ };
543
313
 
544
- function cleanUpSchemaItem(data) {
545
- return Object.fromEntries(Object.entries(data).filter(([key, value]) => {
546
- if (key === 'attrs' && isEmptyObject(value)) {
547
- return false;
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
- function getSchemaTypeByName(name, schema) {
655
- if (schema.nodes[name]) {
656
- return schema.nodes[name];
319
+ const deleteRange = range => ({ tr, dispatch }) => {
320
+ const { from, to } = range;
321
+ if (dispatch) {
322
+ tr.delete(from, to);
657
323
  }
658
- if (schema.marks[name]) {
659
- return schema.marks[name];
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 null;
366
+ return nameOrType;
662
367
  }
663
368
 
664
- class ExtensionManager {
665
- constructor(extensions, editor) {
666
- this.splittableMarks = [];
667
- this.editor = editor;
668
- this.extensions = extensions;
669
- this.schema = getSchema(this.extensions);
670
- this.extensions.forEach(extension => {
671
- var _a;
672
- const context = {
673
- options: extension.options,
674
- editor: this.editor,
675
- type: getSchemaTypeByName(extension.config.name, this.schema),
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
- get commands() {
710
- return this.extensions.reduce((commands, extension) => {
711
- const context = {
712
- options: extension.options,
713
- editor: this.editor,
714
- type: getSchemaTypeByName(extension.config.name, this.schema),
715
- };
716
- if (!extension.config.addCommands) {
717
- return commands;
718
- }
719
- return {
720
- ...commands,
721
- ...extension.config.addCommands.bind(context)(),
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
- get plugins() {
726
- return [...this.extensions]
727
- .reverse()
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
- get attributes() {
759
- return getAttributesFromExtensions(this.extensions);
410
+ const mark = findMarkInSet(start.node.marks, type, attributes);
411
+ if (!mark) {
412
+ return;
760
413
  }
761
- get nodeViews() {
762
- const { editor } = this;
763
- const { nodeExtensions } = splitExtensions(this.extensions);
764
- return Object.fromEntries(nodeExtensions
765
- .filter(extension => !!extension.config.addNodeView)
766
- .map(extension => {
767
- var _a;
768
- const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.config.name);
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
- get textSerializers() {
790
- const { editor } = this;
791
- const { nodeExtensions } = splitExtensions(this.extensions);
792
- return Object.fromEntries(nodeExtensions
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
- class EventEmitter {
807
- constructor() {
808
- this.callbacks = {};
809
- }
810
- on(event, fn) {
811
- if (!this.callbacks[event]) {
812
- this.callbacks[event] = [];
813
- }
814
- this.callbacks[event].push(fn);
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(item) {
474
+ function isClass(value) {
1149
475
  var _a;
1150
- if (((_a = item.constructor) === null || _a === void 0 ? void 0 : _a.toString().substring(0, 5)) !== 'class') {
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(item) {
1157
- return (item
1158
- && typeof item === 'object'
1159
- && !Array.isArray(item)
1160
- && !isClass(item));
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
- view.focus();
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 resolvedFrom = minMax(from, 0, doc.content.size);
1201
- const resolvedEnd = minMax(to, 0, doc.content.size);
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
- tr.setSelection(selection);
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
- view.focus();
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 insertHTML = value => ({ tr, state, dispatch }) => {
1242
- const { selection } = tr;
1243
- const element = elementFromString(value);
1244
- const slice = prosemirrorModel.DOMParser.fromSchema(state.schema).parseSlice(element);
650
+ const isFragment = (nodeOrFragment) => {
651
+ return nodeOrFragment.toString().startsWith('<');
652
+ };
653
+ const insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
1245
654
  if (dispatch) {
1246
- tr.insert(selection.anchor, slice.content);
1247
- selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
1248
- }
1249
- return true;
1250
- };
1251
-
1252
- var insertHTML$1 = /*#__PURE__*/Object.freeze({
1253
- __proto__: null,
1254
- insertHTML: insertHTML
1255
- });
1256
-
1257
- const insertNode = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1258
- const { selection } = tr;
1259
- const type = getNodeType(typeOrName, state.schema);
1260
- if (!type) {
1261
- return false;
1262
- }
1263
- const node = type.create(attributes);
1264
- if (dispatch) {
1265
- tr.insert(selection.anchor, node);
1266
- }
1267
- return true;
1268
- };
1269
-
1270
- var insertNode$1 = /*#__PURE__*/Object.freeze({
1271
- __proto__: null,
1272
- insertNode: insertNode
1273
- });
1274
-
1275
- const insertText = value => ({ tr, dispatch }) => {
1276
- if (dispatch) {
1277
- tr.insertText(value);
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 insertText$1 = /*#__PURE__*/Object.freeze({
706
+ var insertContentAt$1 = /*#__PURE__*/Object.freeze({
1283
707
  __proto__: null,
1284
- insertText: insertText
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
- const replace = (typeOrName, attributes = {}) => ({ state, commands }) => {
1430
- const { from, to } = state.selection;
1431
- const range = { from, to };
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 (dispatch) {
1449
- tr.replaceWith(from, to, type.create(attributes));
892
+ if (schema.marks[name]) {
893
+ return 'mark';
1450
894
  }
1451
- return true;
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 resetNodeAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
1479
- const type = getNodeType(typeOrName, state.schema);
1480
- const { selection } = tr;
1481
- const { ranges } = selection;
1482
- ranges.forEach(range => {
1483
- state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
1484
- if (node.type === type && dispatch) {
1485
- tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes));
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 resetNodeAttributes$1 = /*#__PURE__*/Object.freeze({
951
+ var resetAttributes$1 = /*#__PURE__*/Object.freeze({
1493
952
  __proto__: null,
1494
- resetNodeAttributes: resetNodeAttributes
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 = () => ({ state, dispatch }) => {
1510
- return prosemirrorCommands.selectAll(state, dispatch);
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
- tr.addStoredMark(type.create(newAttributes));
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
- tr.addMark(range.$from.pos, range.$to.pos, type.create(newAttributes));
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 + 1) {
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 (!atEnd
1245
+ if (deflt
1246
+ && !atEnd
1690
1247
  && !$from.parentOffset
1691
- && $from.parent.type !== deflt
1692
- && $from.node(-1).canReplace($from.index(-1), $from.indexAfter(-1), prosemirrorModel.Fragment.from(deflt === null || deflt === void 0 ? void 0 : deflt.create()))) {
1693
- tr.setNodeMarkup(tr.mapping.map($from.before()), deflt || undefined);
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
- const keepItem = $from.index(-1) > 0;
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 - (keepItem ? 1 : 2); d >= $from.depth - 3; d -= 1) {
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
- tr
1746
- .replace($from.before(keepItem ? undefined : -1), $from.after(-3), new prosemirrorModel.Slice(wrap, keepItem ? 3 : 2, 2))
1747
- .setSelection(prosemirrorState.TextSelection.near(tr.doc.resolve($from.pos + (keepItem ? 3 : 2))))
1748
- .scrollIntoView();
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.config.name === name);
1388
+ const extension = nodeExtensions.find(item => item.name === name);
1796
1389
  if (!extension) {
1797
1390
  return false;
1798
1391
  }
1799
- const groups = callOrReturn(extension.config.group, { options: extension.options });
1800
- if (typeof groups !== 'string') {
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 groups.split(' ').includes('list');
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.options;
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
- const toggleMark = (typeOrName, attributes = {}) => ({ state, commands }) => {
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
- return prosemirrorInputrules.undoInputRule(state, dispatch);
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
- if (empty) {
1927
- let { from, to } = selection;
1928
- const range = getMarkRange($from, type);
1929
- if (range) {
1930
- from = range.from;
1931
- to = range.to;
1932
- }
1933
- tr.removeMark(from, to, type);
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.removeStoredMark(type);
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 updateNodeAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1951
- const type = getNodeType(typeOrName, state.schema);
1952
- const { selection } = tr;
1953
- const { ranges } = selection;
1954
- ranges.forEach(range => {
1955
- state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
1956
- if (node.type === type && dispatch) {
1957
- tr.setNodeMarkup(pos, undefined, {
1958
- ...node.attrs,
1959
- ...attributes,
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 updateNodeAttributes$1 = /*#__PURE__*/Object.freeze({
1688
+ var updateAttributes$1 = /*#__PURE__*/Object.freeze({
1968
1689
  __proto__: null,
1969
- updateNodeAttributes: updateNodeAttributes
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
- ...insertHTML$1,
2013
- ...insertNode$1,
2014
- ...insertText$1,
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
- ...replace$1,
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
- ...updateNodeAttributes$1,
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: () => handleBackspace(),
2122
- 'Mod-Backspace': () => handleBackspace(),
2123
- Delete: () => handleDelete(),
2124
- 'Mod-Delete': () => handleDelete(),
2125
- // we don’t need a custom `selectAll` for now
2126
- // 'Mod-a': () => this.editor.commands.selectAll(),
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
- var extensions = /*#__PURE__*/Object.freeze({
2132
- __proto__: null,
2133
- ClipboardTextSerializer: ClipboardTextSerializer,
2134
- Commands: Commands,
2135
- Editable: Editable,
2136
- FocusEvents: FocusEvents,
2137
- Keymap: Keymap
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.createCommands();
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.createChain();
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.createCan();
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 = { ...this.options, ...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
- return this.view && this.view.editable;
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 = typeof handlePlugins === 'function'
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 name The plugins name
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 = Object.entries(extensions).map(([, extension]) => extension);
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(this, this.extensionManager.commands);
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: this.createDocument(this.options.content),
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
- getMarkAttributes(name) {
2495
- return getMarkAttributes(this.state, name);
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: 'node',
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
- var _a;
2576
- return __classPrivateFieldGet((_a = Node
2577
- .create(this.config)), _configure$1).call(_a, options);
2578
- }
2579
- extend(extendedConfig) {
2580
- return new Node({
2581
- ...this.config,
2582
- ...extendedConfig,
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: 'mark',
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
- var _a;
2611
- return __classPrivateFieldGet((_a = Mark
2612
- .create(this.config)), _configure).call(_a, options);
2613
- }
2614
- extend(extendedConfig) {
2615
- return new Mark({
2616
- ...this.config,
2617
- ...extendedConfig,
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
- update: null,
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
- if ((_a = this.contentDOM) === null || _a === void 0 ? void 0 : _a.contains(target)) {
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
- // sometimes `event.target` is not the `dom` element
2660
- (_b = event.dataTransfer) === null || _b === void 0 ? void 0 : _b.setDragImage(this.dom, 0, 0);
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') || event.type === 'drop';
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
- if (!this.contentDOM) {
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
- const contentDOMHasChanged = !this.contentDOM.contains(mutation.target)
2740
- || this.contentDOM === mutation.target;
2741
- return contentDOMHasChanged;
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
- if (!this.editor.view.editable) {
2745
- return;
2746
- }
2747
- const { state } = this.editor.view;
2748
- const pos = this.getPos();
2749
- const transaction = state.tr.setNodeMarkup(pos, undefined, {
2750
- ...this.node.attrs,
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
- this.editor.view.dispatch(transaction);
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
- function nodeInputRule (regexp, type, getAttributes) {
2758
- return new prosemirrorInputrules.InputRule(regexp, (state, match, start, end) => {
2759
- const attributes = getAttributes instanceof Function
2760
- ? getAttributes(match)
2761
- : getAttributes;
2762
- const { tr } = state;
2763
- if (match[0]) {
2764
- tr.replaceWith(start - 1, end, type.create(attributes));
2765
- }
2766
- return tr;
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
- let marks = [];
3749
+ const marks = [];
2772
3750
  state.doc.nodesBetween(from, to, (node, pos) => {
2773
- marks = [...marks, ...node.marks.map(mark => ({
2774
- from: pos,
2775
- to: pos + node.nodeSize,
2776
- mark,
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
- function markInputRule (regexp, markType, getAttributes) {
2783
- return new prosemirrorInputrules.InputRule(regexp, (state, match, start, end) => {
2784
- const attributes = getAttributes instanceof Function
2785
- ? getAttributes(match)
2786
- : getAttributes;
2787
- const { tr } = state;
2788
- const captureGroup = match[match.length - 1];
2789
- const fullMatch = match[0];
2790
- let markEnd = end;
2791
- if (captureGroup) {
2792
- const startSpaces = fullMatch.search(/\S/);
2793
- const textStart = start + fullMatch.indexOf(captureGroup);
2794
- const textEnd = textStart + captureGroup.length;
2795
- const excludedMarks = getMarksBetween(start, end, state)
2796
- .filter(item => {
2797
- // TODO: PR to add excluded to MarkType
2798
- // @ts-ignore
2799
- const { excluded } = item.mark.type;
2800
- return excluded.find((type) => type.name === markType.name);
2801
- })
2802
- .filter(item => item.to > textStart);
2803
- if (excludedMarks.length) {
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
- if (textEnd < end) {
2807
- tr.delete(textEnd, end);
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
- if (textStart > start) {
2810
- tr.delete(start + startSpaces, textStart);
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
- markEnd = start + startSpaces + captureGroup.length;
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
- function markPasteRule (regexp, type, getAttrs) {
2821
- const handler = (fragment, parent) => {
2822
- const nodes = [];
2823
- fragment.forEach(child => {
2824
- if (child.isText && child.text) {
2825
- const { text } = child;
2826
- let pos = 0;
2827
- let match;
2828
- // eslint-disable-next-line
2829
- while ((match = regexp.exec(text)) !== null) {
2830
- const outerMatch = Math.max(match.length - 2, 0);
2831
- const innerMatch = Math.max(match.length - 1, 0);
2832
- if (parent === null || parent === void 0 ? void 0 : parent.type.allowsMarkType(type)) {
2833
- const start = match.index;
2834
- const matchStart = start + match[0].indexOf(match[outerMatch]);
2835
- const matchEnd = matchStart + match[outerMatch].length;
2836
- const textStart = matchStart + match[outerMatch].lastIndexOf(match[innerMatch]);
2837
- const textEnd = textStart + match[innerMatch].length;
2838
- const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
2839
- // adding text before markdown to nodes
2840
- if (matchStart > 0) {
2841
- nodes.push(child.cut(pos, matchStart));
2842
- }
2843
- // adding the markdown part to nodes
2844
- nodes.push(child
2845
- .cut(textStart, textEnd)
2846
- .mark(type.create(attrs).addToSet(child.marks)));
2847
- pos = matchEnd;
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
- // adding rest of text to nodes
2851
- if (pos < text.length) {
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
- else {
2856
- nodes.push(child.copy(handler(child.content, child)));
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