@tiptap/core 2.0.0-beta.196 → 2.0.0-beta.198

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.
@@ -1,8 +1,8 @@
1
1
  import { Plugin, PluginKey, TextSelection, Selection, NodeSelection, EditorState } from 'prosemirror-state';
2
2
  import { EditorView } from 'prosemirror-view';
3
3
  import { keymap } from 'prosemirror-keymap';
4
- import { Schema, Fragment, DOMParser, Slice, DOMSerializer, Node as Node$1 } from 'prosemirror-model';
5
- import { liftTarget, ReplaceStep, ReplaceAroundStep, canSplit, canJoin, Transform, findWrapping } from 'prosemirror-transform';
4
+ import { Schema, Fragment, DOMParser, DOMSerializer, Node as Node$1, Slice } from 'prosemirror-model';
5
+ import { liftTarget, ReplaceStep, ReplaceAroundStep, Transform, canSplit, canJoin, findWrapping } from 'prosemirror-transform';
6
6
  import { createParagraphNear as createParagraphNear$1, deleteSelection as deleteSelection$1, exitCode as exitCode$1, joinBackward as joinBackward$1, joinForward as joinForward$1, lift as lift$1, liftEmptyBlock as liftEmptyBlock$1, newlineInCode as newlineInCode$1, selectNodeBackward as selectNodeBackward$1, selectNodeForward as selectNodeForward$1, selectParentNode as selectParentNode$1, selectTextblockEnd as selectTextblockEnd$1, selectTextblockStart as selectTextblockStart$1, setBlockType, wrapIn as wrapIn$1 } from 'prosemirror-commands';
7
7
  import { liftListItem as liftListItem$1, sinkListItem as sinkListItem$1, wrapInList as wrapInList$1 } from 'prosemirror-schema-list';
8
8
 
@@ -275,7 +275,7 @@ function getAttributesFromExtensions(extensions) {
275
275
  ...defaultAttribute,
276
276
  ...attribute,
277
277
  };
278
- if (attribute.isRequired && attribute.default === undefined) {
278
+ if ((attribute === null || attribute === void 0 ? void 0 : attribute.isRequired) && (attribute === null || attribute === void 0 ? void 0 : attribute.default) === undefined) {
279
279
  delete mergedAttr.default;
280
280
  }
281
281
  extensionAttributes.push({
@@ -541,11 +541,13 @@ function isExtensionRulesEnabled(extension, enabled) {
541
541
 
542
542
  const getTextContentFromNodes = ($from, maxMatch = 500) => {
543
543
  let textBefore = '';
544
- $from.parent.nodesBetween(Math.max(0, $from.parentOffset - maxMatch), $from.parentOffset, (node, pos, parent, index) => {
545
- var _a, _b, _c;
546
- textBefore += ((_b = (_a = node.type.spec).toText) === null || _b === void 0 ? void 0 : _b.call(_a, {
544
+ const sliceEndPos = $from.parentOffset;
545
+ $from.parent.nodesBetween(Math.max(0, sliceEndPos - maxMatch), sliceEndPos, (node, pos, parent, index) => {
546
+ var _a, _b;
547
+ const chunk = ((_b = (_a = node.type.spec).toText) === null || _b === void 0 ? void 0 : _b.call(_a, {
547
548
  node, pos, parent, index,
548
- })) || ((_c = $from.nodeBefore) === null || _c === void 0 ? void 0 : _c.text) || '%leaf%';
549
+ })) || node.textContent || '%leaf%';
550
+ textBefore += chunk.slice(0, Math.max(0, sliceEndPos - pos));
549
551
  });
550
552
  return textBefore;
551
553
  };
@@ -1932,6 +1934,135 @@ const setContent = (content, emitUpdate = false, parseOptions = {}) => ({ tr, ed
1932
1934
  return true;
1933
1935
  };
1934
1936
 
1937
+ /**
1938
+ * Returns a new `Transform` based on all steps of the passed transactions.
1939
+ */
1940
+ function combineTransactionSteps(oldDoc, transactions) {
1941
+ const transform = new Transform(oldDoc);
1942
+ transactions.forEach(transaction => {
1943
+ transaction.steps.forEach(step => {
1944
+ transform.step(step);
1945
+ });
1946
+ });
1947
+ return transform;
1948
+ }
1949
+
1950
+ function defaultBlockAt(match) {
1951
+ for (let i = 0; i < match.edgeCount; i += 1) {
1952
+ const { type } = match.edge(i);
1953
+ if (type.isTextblock && !type.hasRequiredAttrs()) {
1954
+ return type;
1955
+ }
1956
+ }
1957
+ return null;
1958
+ }
1959
+
1960
+ function findChildren(node, predicate) {
1961
+ const nodesWithPos = [];
1962
+ node.descendants((child, pos) => {
1963
+ if (predicate(child)) {
1964
+ nodesWithPos.push({
1965
+ node: child,
1966
+ pos,
1967
+ });
1968
+ }
1969
+ });
1970
+ return nodesWithPos;
1971
+ }
1972
+
1973
+ /**
1974
+ * Same as `findChildren` but searches only within a `range`.
1975
+ */
1976
+ function findChildrenInRange(node, range, predicate) {
1977
+ const nodesWithPos = [];
1978
+ // if (range.from === range.to) {
1979
+ // const nodeAt = node.nodeAt(range.from)
1980
+ // if (nodeAt) {
1981
+ // nodesWithPos.push({
1982
+ // node: nodeAt,
1983
+ // pos: range.from,
1984
+ // })
1985
+ // }
1986
+ // }
1987
+ node.nodesBetween(range.from, range.to, (child, pos) => {
1988
+ if (predicate(child)) {
1989
+ nodesWithPos.push({
1990
+ node: child,
1991
+ pos,
1992
+ });
1993
+ }
1994
+ });
1995
+ return nodesWithPos;
1996
+ }
1997
+
1998
+ function findParentNodeClosestToPos($pos, predicate) {
1999
+ for (let i = $pos.depth; i > 0; i -= 1) {
2000
+ const node = $pos.node(i);
2001
+ if (predicate(node)) {
2002
+ return {
2003
+ pos: i > 0 ? $pos.before(i) : 0,
2004
+ start: $pos.start(i),
2005
+ depth: i,
2006
+ node,
2007
+ };
2008
+ }
2009
+ }
2010
+ }
2011
+
2012
+ function findParentNode(predicate) {
2013
+ return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
2014
+ }
2015
+
2016
+ function getHTMLFromFragment(fragment, schema) {
2017
+ const documentFragment = DOMSerializer
2018
+ .fromSchema(schema)
2019
+ .serializeFragment(fragment);
2020
+ const temporaryDocument = document.implementation.createHTMLDocument();
2021
+ const container = temporaryDocument.createElement('div');
2022
+ container.appendChild(documentFragment);
2023
+ return container.innerHTML;
2024
+ }
2025
+
2026
+ function getSchema(extensions) {
2027
+ const resolvedExtensions = ExtensionManager.resolve(extensions);
2028
+ return getSchemaByResolvedExtensions(resolvedExtensions);
2029
+ }
2030
+
2031
+ function generateHTML(doc, extensions) {
2032
+ const schema = getSchema(extensions);
2033
+ const contentNode = Node$1.fromJSON(schema, doc);
2034
+ return getHTMLFromFragment(contentNode.content, schema);
2035
+ }
2036
+
2037
+ function generateJSON(html, extensions) {
2038
+ const schema = getSchema(extensions);
2039
+ const dom = elementFromString(html);
2040
+ return DOMParser.fromSchema(schema)
2041
+ .parse(dom)
2042
+ .toJSON();
2043
+ }
2044
+
2045
+ function getText(node, options) {
2046
+ const range = {
2047
+ from: 0,
2048
+ to: node.content.size,
2049
+ };
2050
+ return getTextBetween(node, range, options);
2051
+ }
2052
+
2053
+ function generateText(doc, extensions, options) {
2054
+ const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
2055
+ const schema = getSchema(extensions);
2056
+ const contentNode = Node$1.fromJSON(schema, doc);
2057
+ return getText(contentNode, {
2058
+ blockSeparator,
2059
+ textSerializers: {
2060
+ ...textSerializers,
2061
+ ...getTextSerializersFromSchema(schema),
2062
+ },
2063
+ });
2064
+ }
2065
+
1935
2066
  function getMarkAttributes(state, typeOrName) {
1936
2067
  const type = getMarkType(typeOrName, state.schema);
1937
2068
  const { from, to, empty } = state.selection;
@@ -1954,118 +2085,454 @@ function getMarkAttributes(state, typeOrName) {
1954
2085
  return { ...mark.attrs };
1955
2086
  }
1956
2087
 
1957
- const setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
1958
- const { selection } = tr;
1959
- const { empty, ranges } = selection;
1960
- const type = getMarkType(typeOrName, state.schema);
1961
- if (dispatch) {
1962
- if (empty) {
1963
- const oldAttributes = getMarkAttributes(state, type);
1964
- tr.addStoredMark(type.create({
1965
- ...oldAttributes,
1966
- ...attributes,
1967
- }));
1968
- }
1969
- else {
1970
- ranges.forEach(range => {
1971
- const from = range.$from.pos;
1972
- const to = range.$to.pos;
1973
- state.doc.nodesBetween(from, to, (node, pos) => {
1974
- const trimmedFrom = Math.max(pos, from);
1975
- const trimmedTo = Math.min(pos + node.nodeSize, to);
1976
- const someHasMark = node.marks.find(mark => mark.type === type);
1977
- // if there is already a mark of this type
1978
- // we know that we have to merge its attributes
1979
- // otherwise we add a fresh new mark
1980
- if (someHasMark) {
1981
- node.marks.forEach(mark => {
1982
- if (type === mark.type) {
1983
- tr.addMark(trimmedFrom, trimmedTo, type.create({
1984
- ...mark.attrs,
1985
- ...attributes,
1986
- }));
1987
- }
1988
- });
1989
- }
1990
- else {
1991
- tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
1992
- }
1993
- });
1994
- });
1995
- }
1996
- }
1997
- return true;
1998
- };
1999
-
2000
- const setMeta = (key, value) => ({ tr }) => {
2001
- tr.setMeta(key, value);
2002
- return true;
2003
- };
2004
-
2005
- const setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
2088
+ function getNodeAttributes(state, typeOrName) {
2006
2089
  const type = getNodeType(typeOrName, state.schema);
2007
- // TODO: use a fallback like insertContent?
2008
- if (!type.isTextblock) {
2009
- console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
2010
- return false;
2011
- }
2012
- return chain()
2013
- // try to convert node to default node if needed
2014
- .command(({ commands }) => {
2015
- const canSetBlock = setBlockType(type, attributes)(state);
2016
- if (canSetBlock) {
2017
- return true;
2018
- }
2019
- return commands.clearNodes();
2020
- })
2021
- .command(({ state: updatedState }) => {
2022
- return setBlockType(type, attributes)(updatedState, dispatch);
2023
- })
2024
- .run();
2025
- };
2026
-
2027
- const setNodeSelection = position => ({ tr, dispatch }) => {
2028
- if (dispatch) {
2029
- const { doc } = tr;
2030
- const from = minMax(position, 0, doc.content.size);
2031
- const selection = NodeSelection.create(doc, from);
2032
- tr.setSelection(selection);
2090
+ const { from, to } = state.selection;
2091
+ const nodes = [];
2092
+ state.doc.nodesBetween(from, to, node => {
2093
+ nodes.push(node);
2094
+ });
2095
+ const node = nodes
2096
+ .reverse()
2097
+ .find(nodeItem => nodeItem.type.name === type.name);
2098
+ if (!node) {
2099
+ return {};
2033
2100
  }
2034
- return true;
2035
- };
2101
+ return { ...node.attrs };
2102
+ }
2036
2103
 
2037
- const setTextSelection = position => ({ tr, dispatch }) => {
2038
- if (dispatch) {
2039
- const { doc } = tr;
2040
- const { from, to } = typeof position === 'number'
2041
- ? { from: position, to: position }
2042
- : position;
2043
- const minPos = TextSelection.atStart(doc).from;
2044
- const maxPos = TextSelection.atEnd(doc).to;
2045
- const resolvedFrom = minMax(from, minPos, maxPos);
2046
- const resolvedEnd = minMax(to, minPos, maxPos);
2047
- const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
2048
- tr.setSelection(selection);
2104
+ function getAttributes(state, typeOrName) {
2105
+ const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
2106
+ ? typeOrName
2107
+ : typeOrName.name, state.schema);
2108
+ if (schemaType === 'node') {
2109
+ return getNodeAttributes(state, typeOrName);
2049
2110
  }
2050
- return true;
2051
- };
2052
-
2053
- const sinkListItem = typeOrName => ({ state, dispatch }) => {
2054
- const type = getNodeType(typeOrName, state.schema);
2055
- return sinkListItem$1(type)(state, dispatch);
2056
- };
2057
-
2058
- function defaultBlockAt(match) {
2059
- for (let i = 0; i < match.edgeCount; i += 1) {
2060
- const { type } = match.edge(i);
2061
- if (type.isTextblock && !type.hasRequiredAttrs()) {
2062
- return type;
2063
- }
2111
+ if (schemaType === 'mark') {
2112
+ return getMarkAttributes(state, typeOrName);
2064
2113
  }
2065
- return null;
2114
+ return {};
2066
2115
  }
2067
2116
 
2068
- function getSplittedAttributes(extensionAttributes, typeName, attributes) {
2117
+ /**
2118
+ * Removes duplicated values within an array.
2119
+ * Supports numbers, strings and objects.
2120
+ */
2121
+ function removeDuplicates(array, by = JSON.stringify) {
2122
+ const seen = {};
2123
+ return array.filter(item => {
2124
+ const key = by(item);
2125
+ return Object.prototype.hasOwnProperty.call(seen, key)
2126
+ ? false
2127
+ : (seen[key] = true);
2128
+ });
2129
+ }
2130
+
2131
+ /**
2132
+ * Removes duplicated ranges and ranges that are
2133
+ * fully captured by other ranges.
2134
+ */
2135
+ function simplifyChangedRanges(changes) {
2136
+ const uniqueChanges = removeDuplicates(changes);
2137
+ return uniqueChanges.length === 1
2138
+ ? uniqueChanges
2139
+ : uniqueChanges.filter((change, index) => {
2140
+ const rest = uniqueChanges.filter((_, i) => i !== index);
2141
+ return !rest.some(otherChange => {
2142
+ return change.oldRange.from >= otherChange.oldRange.from
2143
+ && change.oldRange.to <= otherChange.oldRange.to
2144
+ && change.newRange.from >= otherChange.newRange.from
2145
+ && change.newRange.to <= otherChange.newRange.to;
2146
+ });
2147
+ });
2148
+ }
2149
+ /**
2150
+ * Returns a list of changed ranges
2151
+ * based on the first and last state of all steps.
2152
+ */
2153
+ function getChangedRanges(transform) {
2154
+ const { mapping, steps } = transform;
2155
+ const changes = [];
2156
+ mapping.maps.forEach((stepMap, index) => {
2157
+ const ranges = [];
2158
+ // This accounts for step changes where no range was actually altered
2159
+ // e.g. when setting a mark, node attribute, etc.
2160
+ // @ts-ignore
2161
+ if (!stepMap.ranges.length) {
2162
+ const { from, to } = steps[index];
2163
+ if (from === undefined || to === undefined) {
2164
+ return;
2165
+ }
2166
+ ranges.push({ from, to });
2167
+ }
2168
+ else {
2169
+ stepMap.forEach((from, to) => {
2170
+ ranges.push({ from, to });
2171
+ });
2172
+ }
2173
+ ranges.forEach(({ from, to }) => {
2174
+ const newStart = mapping.slice(index).map(from, -1);
2175
+ const newEnd = mapping.slice(index).map(to);
2176
+ const oldStart = mapping.invert().map(newStart, -1);
2177
+ const oldEnd = mapping.invert().map(newEnd);
2178
+ changes.push({
2179
+ oldRange: {
2180
+ from: oldStart,
2181
+ to: oldEnd,
2182
+ },
2183
+ newRange: {
2184
+ from: newStart,
2185
+ to: newEnd,
2186
+ },
2187
+ });
2188
+ });
2189
+ });
2190
+ return simplifyChangedRanges(changes);
2191
+ }
2192
+
2193
+ function getDebugJSON(node, startOffset = 0) {
2194
+ const isTopNode = node.type === node.type.schema.topNodeType;
2195
+ const increment = isTopNode ? 0 : 1;
2196
+ const from = startOffset;
2197
+ const to = from + node.nodeSize;
2198
+ const marks = node.marks.map(mark => {
2199
+ const output = {
2200
+ type: mark.type.name,
2201
+ };
2202
+ if (Object.keys(mark.attrs).length) {
2203
+ output.attrs = { ...mark.attrs };
2204
+ }
2205
+ return output;
2206
+ });
2207
+ const attrs = { ...node.attrs };
2208
+ const output = {
2209
+ type: node.type.name,
2210
+ from,
2211
+ to,
2212
+ };
2213
+ if (Object.keys(attrs).length) {
2214
+ output.attrs = attrs;
2215
+ }
2216
+ if (marks.length) {
2217
+ output.marks = marks;
2218
+ }
2219
+ if (node.content.childCount) {
2220
+ output.content = [];
2221
+ node.forEach((child, offset) => {
2222
+ var _a;
2223
+ (_a = output.content) === null || _a === void 0 ? void 0 : _a.push(getDebugJSON(child, startOffset + offset + increment));
2224
+ });
2225
+ }
2226
+ if (node.text) {
2227
+ output.text = node.text;
2228
+ }
2229
+ return output;
2230
+ }
2231
+
2232
+ function getMarksBetween(from, to, doc) {
2233
+ const marks = [];
2234
+ // get all inclusive marks on empty selection
2235
+ if (from === to) {
2236
+ doc
2237
+ .resolve(from)
2238
+ .marks()
2239
+ .forEach(mark => {
2240
+ const $pos = doc.resolve(from - 1);
2241
+ const range = getMarkRange($pos, mark.type);
2242
+ if (!range) {
2243
+ return;
2244
+ }
2245
+ marks.push({
2246
+ mark,
2247
+ ...range,
2248
+ });
2249
+ });
2250
+ }
2251
+ else {
2252
+ doc.nodesBetween(from, to, (node, pos) => {
2253
+ marks.push(...node.marks.map(mark => ({
2254
+ from: pos,
2255
+ to: pos + node.nodeSize,
2256
+ mark,
2257
+ })));
2258
+ });
2259
+ }
2260
+ return marks;
2261
+ }
2262
+
2263
+ function isMarkActive(state, typeOrName, attributes = {}) {
2264
+ const { empty, ranges } = state.selection;
2265
+ const type = typeOrName
2266
+ ? getMarkType(typeOrName, state.schema)
2267
+ : null;
2268
+ if (empty) {
2269
+ return !!(state.storedMarks || state.selection.$from.marks())
2270
+ .filter(mark => {
2271
+ if (!type) {
2272
+ return true;
2273
+ }
2274
+ return type.name === mark.type.name;
2275
+ })
2276
+ .find(mark => objectIncludes(mark.attrs, attributes, { strict: false }));
2277
+ }
2278
+ let selectionRange = 0;
2279
+ const markRanges = [];
2280
+ ranges.forEach(({ $from, $to }) => {
2281
+ const from = $from.pos;
2282
+ const to = $to.pos;
2283
+ state.doc.nodesBetween(from, to, (node, pos) => {
2284
+ if (!node.isText && !node.marks.length) {
2285
+ return;
2286
+ }
2287
+ const relativeFrom = Math.max(from, pos);
2288
+ const relativeTo = Math.min(to, pos + node.nodeSize);
2289
+ const range = relativeTo - relativeFrom;
2290
+ selectionRange += range;
2291
+ markRanges.push(...node.marks.map(mark => ({
2292
+ mark,
2293
+ from: relativeFrom,
2294
+ to: relativeTo,
2295
+ })));
2296
+ });
2297
+ });
2298
+ if (selectionRange === 0) {
2299
+ return false;
2300
+ }
2301
+ // calculate range of matched mark
2302
+ const matchedRange = markRanges
2303
+ .filter(markRange => {
2304
+ if (!type) {
2305
+ return true;
2306
+ }
2307
+ return type.name === markRange.mark.type.name;
2308
+ })
2309
+ .filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))
2310
+ .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
2311
+ // calculate range of marks that excludes the searched mark
2312
+ // for example `code` doesn’t allow any other marks
2313
+ const excludedRange = markRanges
2314
+ .filter(markRange => {
2315
+ if (!type) {
2316
+ return true;
2317
+ }
2318
+ return markRange.mark.type !== type
2319
+ && markRange.mark.type.excludes(type);
2320
+ })
2321
+ .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
2322
+ // we only include the result of `excludedRange`
2323
+ // if there is a match at all
2324
+ const range = matchedRange > 0
2325
+ ? matchedRange + excludedRange
2326
+ : matchedRange;
2327
+ return range >= selectionRange;
2328
+ }
2329
+
2330
+ function isActive(state, name, attributes = {}) {
2331
+ if (!name) {
2332
+ return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
2333
+ }
2334
+ const schemaType = getSchemaTypeNameByName(name, state.schema);
2335
+ if (schemaType === 'node') {
2336
+ return isNodeActive(state, name, attributes);
2337
+ }
2338
+ if (schemaType === 'mark') {
2339
+ return isMarkActive(state, name, attributes);
2340
+ }
2341
+ return false;
2342
+ }
2343
+
2344
+ function isList(name, extensions) {
2345
+ const { nodeExtensions } = splitExtensions(extensions);
2346
+ const extension = nodeExtensions.find(item => item.name === name);
2347
+ if (!extension) {
2348
+ return false;
2349
+ }
2350
+ const context = {
2351
+ name: extension.name,
2352
+ options: extension.options,
2353
+ storage: extension.storage,
2354
+ };
2355
+ const group = callOrReturn(getExtensionField(extension, 'group', context));
2356
+ if (typeof group !== 'string') {
2357
+ return false;
2358
+ }
2359
+ return group.split(' ').includes('list');
2360
+ }
2361
+
2362
+ function isNodeEmpty(node) {
2363
+ var _a;
2364
+ const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
2365
+ const content = node.toJSON();
2366
+ return JSON.stringify(defaultContent) === JSON.stringify(content);
2367
+ }
2368
+
2369
+ function isNodeSelection(value) {
2370
+ return value instanceof NodeSelection;
2371
+ }
2372
+
2373
+ function posToDOMRect(view, from, to) {
2374
+ const minPos = 0;
2375
+ const maxPos = view.state.doc.content.size;
2376
+ const resolvedFrom = minMax(from, minPos, maxPos);
2377
+ const resolvedEnd = minMax(to, minPos, maxPos);
2378
+ const start = view.coordsAtPos(resolvedFrom);
2379
+ const end = view.coordsAtPos(resolvedEnd, -1);
2380
+ const top = Math.min(start.top, end.top);
2381
+ const bottom = Math.max(start.bottom, end.bottom);
2382
+ const left = Math.min(start.left, end.left);
2383
+ const right = Math.max(start.right, end.right);
2384
+ const width = right - left;
2385
+ const height = bottom - top;
2386
+ const x = left;
2387
+ const y = top;
2388
+ const data = {
2389
+ top,
2390
+ bottom,
2391
+ left,
2392
+ right,
2393
+ width,
2394
+ height,
2395
+ x,
2396
+ y,
2397
+ };
2398
+ return {
2399
+ ...data,
2400
+ toJSON: () => data,
2401
+ };
2402
+ }
2403
+
2404
+ function canSetMark(state, tr, newMarkType) {
2405
+ var _a;
2406
+ const { selection } = tr;
2407
+ let cursor = null;
2408
+ if (isTextSelection(selection)) {
2409
+ cursor = selection.$cursor;
2410
+ }
2411
+ if (cursor) {
2412
+ const currentMarks = (_a = state.storedMarks) !== null && _a !== void 0 ? _a : cursor.marks();
2413
+ // There can be no current marks that exclude the new mark
2414
+ return !!newMarkType.isInSet(currentMarks) || !currentMarks.some(mark => mark.type.excludes(newMarkType));
2415
+ }
2416
+ const { ranges } = selection;
2417
+ return ranges.some(({ $from, $to }) => {
2418
+ let someNodeSupportsMark = $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false;
2419
+ state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
2420
+ // If we already found a mark that we can enable, return false to bypass the remaining search
2421
+ if (someNodeSupportsMark) {
2422
+ return false;
2423
+ }
2424
+ if (node.isInline) {
2425
+ const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType);
2426
+ const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks) || !node.marks.some(otherMark => otherMark.type.excludes(newMarkType));
2427
+ someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType;
2428
+ }
2429
+ return !someNodeSupportsMark;
2430
+ });
2431
+ return someNodeSupportsMark;
2432
+ });
2433
+ }
2434
+ const setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
2435
+ const { selection } = tr;
2436
+ const { empty, ranges } = selection;
2437
+ const type = getMarkType(typeOrName, state.schema);
2438
+ if (dispatch) {
2439
+ if (empty) {
2440
+ const oldAttributes = getMarkAttributes(state, type);
2441
+ tr.addStoredMark(type.create({
2442
+ ...oldAttributes,
2443
+ ...attributes,
2444
+ }));
2445
+ }
2446
+ else {
2447
+ ranges.forEach(range => {
2448
+ const from = range.$from.pos;
2449
+ const to = range.$to.pos;
2450
+ state.doc.nodesBetween(from, to, (node, pos) => {
2451
+ const trimmedFrom = Math.max(pos, from);
2452
+ const trimmedTo = Math.min(pos + node.nodeSize, to);
2453
+ const someHasMark = node.marks.find(mark => mark.type === type);
2454
+ // if there is already a mark of this type
2455
+ // we know that we have to merge its attributes
2456
+ // otherwise we add a fresh new mark
2457
+ if (someHasMark) {
2458
+ node.marks.forEach(mark => {
2459
+ if (type === mark.type) {
2460
+ tr.addMark(trimmedFrom, trimmedTo, type.create({
2461
+ ...mark.attrs,
2462
+ ...attributes,
2463
+ }));
2464
+ }
2465
+ });
2466
+ }
2467
+ else {
2468
+ tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
2469
+ }
2470
+ });
2471
+ });
2472
+ }
2473
+ }
2474
+ return canSetMark(state, tr, type);
2475
+ };
2476
+
2477
+ const setMeta = (key, value) => ({ tr }) => {
2478
+ tr.setMeta(key, value);
2479
+ return true;
2480
+ };
2481
+
2482
+ const setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
2483
+ const type = getNodeType(typeOrName, state.schema);
2484
+ // TODO: use a fallback like insertContent?
2485
+ if (!type.isTextblock) {
2486
+ console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
2487
+ return false;
2488
+ }
2489
+ return chain()
2490
+ // try to convert node to default node if needed
2491
+ .command(({ commands }) => {
2492
+ const canSetBlock = setBlockType(type, attributes)(state);
2493
+ if (canSetBlock) {
2494
+ return true;
2495
+ }
2496
+ return commands.clearNodes();
2497
+ })
2498
+ .command(({ state: updatedState }) => {
2499
+ return setBlockType(type, attributes)(updatedState, dispatch);
2500
+ })
2501
+ .run();
2502
+ };
2503
+
2504
+ const setNodeSelection = position => ({ tr, dispatch }) => {
2505
+ if (dispatch) {
2506
+ const { doc } = tr;
2507
+ const from = minMax(position, 0, doc.content.size);
2508
+ const selection = NodeSelection.create(doc, from);
2509
+ tr.setSelection(selection);
2510
+ }
2511
+ return true;
2512
+ };
2513
+
2514
+ const setTextSelection = position => ({ tr, dispatch }) => {
2515
+ if (dispatch) {
2516
+ const { doc } = tr;
2517
+ const { from, to } = typeof position === 'number'
2518
+ ? { from: position, to: position }
2519
+ : position;
2520
+ const minPos = TextSelection.atStart(doc).from;
2521
+ const maxPos = TextSelection.atEnd(doc).to;
2522
+ const resolvedFrom = minMax(from, minPos, maxPos);
2523
+ const resolvedEnd = minMax(to, minPos, maxPos);
2524
+ const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
2525
+ tr.setSelection(selection);
2526
+ }
2527
+ return true;
2528
+ };
2529
+
2530
+ const sinkListItem = typeOrName => ({ state, dispatch }) => {
2531
+ const type = getNodeType(typeOrName, state.schema);
2532
+ return sinkListItem$1(type)(state, dispatch);
2533
+ };
2534
+
2535
+ function getSplittedAttributes(extensionAttributes, typeName, attributes) {
2069
2536
  return Object.fromEntries(Object
2070
2537
  .entries(attributes)
2071
2538
  .filter(([name]) => {
@@ -2237,42 +2704,6 @@ const splitListItem = typeOrName => ({ tr, state, dispatch, editor, }) => {
2237
2704
  return true;
2238
2705
  };
2239
2706
 
2240
- function findParentNodeClosestToPos($pos, predicate) {
2241
- for (let i = $pos.depth; i > 0; i -= 1) {
2242
- const node = $pos.node(i);
2243
- if (predicate(node)) {
2244
- return {
2245
- pos: i > 0 ? $pos.before(i) : 0,
2246
- start: $pos.start(i),
2247
- depth: i,
2248
- node,
2249
- };
2250
- }
2251
- }
2252
- }
2253
-
2254
- function findParentNode(predicate) {
2255
- return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
2256
- }
2257
-
2258
- function isList(name, extensions) {
2259
- const { nodeExtensions } = splitExtensions(extensions);
2260
- const extension = nodeExtensions.find(item => item.name === name);
2261
- if (!extension) {
2262
- return false;
2263
- }
2264
- const context = {
2265
- name: extension.name,
2266
- options: extension.options,
2267
- storage: extension.storage,
2268
- };
2269
- const group = callOrReturn(getExtensionField(extension, 'group', context));
2270
- if (typeof group !== 'string') {
2271
- return false;
2272
- }
2273
- return group.split(' ').includes('list');
2274
- }
2275
-
2276
2707
  const joinListBackwards = (tr, listType) => {
2277
2708
  const list = findParentNode(node => node.type === listType)(tr.selection);
2278
2709
  if (!list) {
@@ -2354,73 +2785,6 @@ const toggleList = (listTypeOrName, itemTypeOrName) => ({ editor, tr, state, dis
2354
2785
  .run();
2355
2786
  };
2356
2787
 
2357
- function isMarkActive(state, typeOrName, attributes = {}) {
2358
- const { empty, ranges } = state.selection;
2359
- const type = typeOrName
2360
- ? getMarkType(typeOrName, state.schema)
2361
- : null;
2362
- if (empty) {
2363
- return !!(state.storedMarks || state.selection.$from.marks())
2364
- .filter(mark => {
2365
- if (!type) {
2366
- return true;
2367
- }
2368
- return type.name === mark.type.name;
2369
- })
2370
- .find(mark => objectIncludes(mark.attrs, attributes, { strict: false }));
2371
- }
2372
- let selectionRange = 0;
2373
- const markRanges = [];
2374
- ranges.forEach(({ $from, $to }) => {
2375
- const from = $from.pos;
2376
- const to = $to.pos;
2377
- state.doc.nodesBetween(from, to, (node, pos) => {
2378
- if (!node.isText && !node.marks.length) {
2379
- return;
2380
- }
2381
- const relativeFrom = Math.max(from, pos);
2382
- const relativeTo = Math.min(to, pos + node.nodeSize);
2383
- const range = relativeTo - relativeFrom;
2384
- selectionRange += range;
2385
- markRanges.push(...node.marks.map(mark => ({
2386
- mark,
2387
- from: relativeFrom,
2388
- to: relativeTo,
2389
- })));
2390
- });
2391
- });
2392
- if (selectionRange === 0) {
2393
- return false;
2394
- }
2395
- // calculate range of matched mark
2396
- const matchedRange = markRanges
2397
- .filter(markRange => {
2398
- if (!type) {
2399
- return true;
2400
- }
2401
- return type.name === markRange.mark.type.name;
2402
- })
2403
- .filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))
2404
- .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
2405
- // calculate range of marks that excludes the searched mark
2406
- // for example `code` doesn’t allow any other marks
2407
- const excludedRange = markRanges
2408
- .filter(markRange => {
2409
- if (!type) {
2410
- return true;
2411
- }
2412
- return markRange.mark.type !== type
2413
- && markRange.mark.type.excludes(type);
2414
- })
2415
- .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
2416
- // we only include the result of `excludedRange`
2417
- // if there is a match at all
2418
- const range = matchedRange > 0
2419
- ? matchedRange + excludedRange
2420
- : matchedRange;
2421
- return range >= selectionRange;
2422
- }
2423
-
2424
2788
  const toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands }) => {
2425
2789
  const { extendEmptyMarkRange = false } = options;
2426
2790
  const type = getMarkType(typeOrName, state.schema);
@@ -2808,78 +3172,10 @@ var extensions = /*#__PURE__*/Object.freeze({
2808
3172
  ClipboardTextSerializer: ClipboardTextSerializer,
2809
3173
  Commands: Commands,
2810
3174
  Editable: Editable,
2811
- FocusEvents: FocusEvents,
2812
- Keymap: Keymap,
2813
- Tabindex: Tabindex
2814
- });
2815
-
2816
- function getNodeAttributes(state, typeOrName) {
2817
- const type = getNodeType(typeOrName, state.schema);
2818
- const { from, to } = state.selection;
2819
- const nodes = [];
2820
- state.doc.nodesBetween(from, to, node => {
2821
- nodes.push(node);
2822
- });
2823
- const node = nodes
2824
- .reverse()
2825
- .find(nodeItem => nodeItem.type.name === type.name);
2826
- if (!node) {
2827
- return {};
2828
- }
2829
- return { ...node.attrs };
2830
- }
2831
-
2832
- function getAttributes(state, typeOrName) {
2833
- const schemaType = getSchemaTypeNameByName(typeof typeOrName === 'string'
2834
- ? typeOrName
2835
- : typeOrName.name, state.schema);
2836
- if (schemaType === 'node') {
2837
- return getNodeAttributes(state, typeOrName);
2838
- }
2839
- if (schemaType === 'mark') {
2840
- return getMarkAttributes(state, typeOrName);
2841
- }
2842
- return {};
2843
- }
2844
-
2845
- function getHTMLFromFragment(fragment, schema) {
2846
- const documentFragment = DOMSerializer
2847
- .fromSchema(schema)
2848
- .serializeFragment(fragment);
2849
- const temporaryDocument = document.implementation.createHTMLDocument();
2850
- const container = temporaryDocument.createElement('div');
2851
- container.appendChild(documentFragment);
2852
- return container.innerHTML;
2853
- }
2854
-
2855
- function getText(node, options) {
2856
- const range = {
2857
- from: 0,
2858
- to: node.content.size,
2859
- };
2860
- return getTextBetween(node, range, options);
2861
- }
2862
-
2863
- function isActive(state, name, attributes = {}) {
2864
- if (!name) {
2865
- return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes);
2866
- }
2867
- const schemaType = getSchemaTypeNameByName(name, state.schema);
2868
- if (schemaType === 'node') {
2869
- return isNodeActive(state, name, attributes);
2870
- }
2871
- if (schemaType === 'mark') {
2872
- return isMarkActive(state, name, attributes);
2873
- }
2874
- return false;
2875
- }
2876
-
2877
- function isNodeEmpty(node) {
2878
- var _a;
2879
- const defaultContent = (_a = node.type.createAndFill()) === null || _a === void 0 ? void 0 : _a.toJSON();
2880
- const content = node.toJSON();
2881
- return JSON.stringify(defaultContent) === JSON.stringify(content);
2882
- }
3175
+ FocusEvents: FocusEvents,
3176
+ Keymap: Keymap,
3177
+ Tabindex: Tabindex
3178
+ });
2883
3179
 
2884
3180
  const style = `.ProseMirror {
2885
3181
  position: relative;
@@ -3185,407 +3481,143 @@ class Editor extends EventEmitter {
3185
3481
  /**
3186
3482
  * Creates all node views.
3187
3483
  */
3188
- createNodeViews() {
3189
- this.view.setProps({
3190
- nodeViews: this.extensionManager.nodeViews,
3191
- });
3192
- }
3193
- captureTransaction(fn) {
3194
- this.isCapturingTransaction = true;
3195
- fn();
3196
- this.isCapturingTransaction = false;
3197
- const tr = this.capturedTransaction;
3198
- this.capturedTransaction = null;
3199
- return tr;
3200
- }
3201
- /**
3202
- * The callback over which to send transactions (state updates) produced by the view.
3203
- *
3204
- * @param transaction An editor state transaction
3205
- */
3206
- dispatchTransaction(transaction) {
3207
- if (this.isCapturingTransaction) {
3208
- if (!this.capturedTransaction) {
3209
- this.capturedTransaction = transaction;
3210
- return;
3211
- }
3212
- transaction.steps.forEach(step => { var _a; return (_a = this.capturedTransaction) === null || _a === void 0 ? void 0 : _a.step(step); });
3213
- return;
3214
- }
3215
- const state = this.state.apply(transaction);
3216
- const selectionHasChanged = !this.state.selection.eq(state.selection);
3217
- this.view.updateState(state);
3218
- this.emit('transaction', {
3219
- editor: this,
3220
- transaction,
3221
- });
3222
- if (selectionHasChanged) {
3223
- this.emit('selectionUpdate', {
3224
- editor: this,
3225
- transaction,
3226
- });
3227
- }
3228
- const focus = transaction.getMeta('focus');
3229
- const blur = transaction.getMeta('blur');
3230
- if (focus) {
3231
- this.emit('focus', {
3232
- editor: this,
3233
- event: focus.event,
3234
- transaction,
3235
- });
3236
- }
3237
- if (blur) {
3238
- this.emit('blur', {
3239
- editor: this,
3240
- event: blur.event,
3241
- transaction,
3242
- });
3243
- }
3244
- if (!transaction.docChanged || transaction.getMeta('preventUpdate')) {
3245
- return;
3246
- }
3247
- this.emit('update', {
3248
- editor: this,
3249
- transaction,
3250
- });
3251
- }
3252
- /**
3253
- * Get attributes of the currently selected node or mark.
3254
- */
3255
- getAttributes(nameOrType) {
3256
- return getAttributes(this.state, nameOrType);
3257
- }
3258
- isActive(nameOrAttributes, attributesOrUndefined) {
3259
- const name = typeof nameOrAttributes === 'string'
3260
- ? nameOrAttributes
3261
- : null;
3262
- const attributes = typeof nameOrAttributes === 'string'
3263
- ? attributesOrUndefined
3264
- : nameOrAttributes;
3265
- return isActive(this.state, name, attributes);
3266
- }
3267
- /**
3268
- * Get the document as JSON.
3269
- */
3270
- getJSON() {
3271
- return this.state.doc.toJSON();
3272
- }
3273
- /**
3274
- * Get the document as HTML.
3275
- */
3276
- getHTML() {
3277
- return getHTMLFromFragment(this.state.doc.content, this.schema);
3278
- }
3279
- /**
3280
- * Get the document as text.
3281
- */
3282
- getText(options) {
3283
- const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
3284
- return getText(this.state.doc, {
3285
- blockSeparator,
3286
- textSerializers: {
3287
- ...textSerializers,
3288
- ...getTextSerializersFromSchema(this.schema),
3289
- },
3290
- });
3291
- }
3292
- /**
3293
- * Check if there is no content.
3294
- */
3295
- get isEmpty() {
3296
- return isNodeEmpty(this.state.doc);
3297
- }
3298
- /**
3299
- * Get the number of characters for the current document.
3300
- *
3301
- * @deprecated
3302
- */
3303
- getCharacterCount() {
3304
- console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.');
3305
- return this.state.doc.content.size - 2;
3306
- }
3307
- /**
3308
- * Destroy the editor.
3309
- */
3310
- destroy() {
3311
- this.emit('destroy');
3312
- if (this.view) {
3313
- this.view.destroy();
3314
- }
3315
- this.removeAllListeners();
3316
- }
3317
- /**
3318
- * Check if the editor is already destroyed.
3319
- */
3320
- get isDestroyed() {
3321
- var _a;
3322
- // @ts-ignore
3323
- return !((_a = this.view) === null || _a === void 0 ? void 0 : _a.docView);
3324
- }
3325
- }
3326
-
3327
- /**
3328
- * Returns a new `Transform` based on all steps of the passed transactions.
3329
- */
3330
- function combineTransactionSteps(oldDoc, transactions) {
3331
- const transform = new Transform(oldDoc);
3332
- transactions.forEach(transaction => {
3333
- transaction.steps.forEach(step => {
3334
- transform.step(step);
3335
- });
3336
- });
3337
- return transform;
3338
- }
3339
-
3340
- function findChildren(node, predicate) {
3341
- const nodesWithPos = [];
3342
- node.descendants((child, pos) => {
3343
- if (predicate(child)) {
3344
- nodesWithPos.push({
3345
- node: child,
3346
- pos,
3347
- });
3348
- }
3349
- });
3350
- return nodesWithPos;
3351
- }
3352
-
3353
- /**
3354
- * Same as `findChildren` but searches only within a `range`.
3355
- */
3356
- function findChildrenInRange(node, range, predicate) {
3357
- const nodesWithPos = [];
3358
- // if (range.from === range.to) {
3359
- // const nodeAt = node.nodeAt(range.from)
3360
- // if (nodeAt) {
3361
- // nodesWithPos.push({
3362
- // node: nodeAt,
3363
- // pos: range.from,
3364
- // })
3365
- // }
3366
- // }
3367
- node.nodesBetween(range.from, range.to, (child, pos) => {
3368
- if (predicate(child)) {
3369
- nodesWithPos.push({
3370
- node: child,
3371
- pos,
3372
- });
3373
- }
3374
- });
3375
- return nodesWithPos;
3376
- }
3377
-
3378
- function getSchema(extensions) {
3379
- const resolvedExtensions = ExtensionManager.resolve(extensions);
3380
- return getSchemaByResolvedExtensions(resolvedExtensions);
3381
- }
3382
-
3383
- function generateHTML(doc, extensions) {
3384
- const schema = getSchema(extensions);
3385
- const contentNode = Node$1.fromJSON(schema, doc);
3386
- return getHTMLFromFragment(contentNode.content, schema);
3387
- }
3388
-
3389
- function generateJSON(html, extensions) {
3390
- const schema = getSchema(extensions);
3391
- const dom = elementFromString(html);
3392
- return DOMParser.fromSchema(schema)
3393
- .parse(dom)
3394
- .toJSON();
3395
- }
3396
-
3397
- function generateText(doc, extensions, options) {
3398
- const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
3399
- const schema = getSchema(extensions);
3400
- const contentNode = Node$1.fromJSON(schema, doc);
3401
- return getText(contentNode, {
3402
- blockSeparator,
3403
- textSerializers: {
3404
- ...textSerializers,
3405
- ...getTextSerializersFromSchema(schema),
3406
- },
3407
- });
3408
- }
3409
-
3410
- /**
3411
- * Removes duplicated values within an array.
3412
- * Supports numbers, strings and objects.
3413
- */
3414
- function removeDuplicates(array, by = JSON.stringify) {
3415
- const seen = {};
3416
- return array.filter(item => {
3417
- const key = by(item);
3418
- return Object.prototype.hasOwnProperty.call(seen, key)
3419
- ? false
3420
- : (seen[key] = true);
3421
- });
3422
- }
3423
-
3424
- /**
3425
- * Removes duplicated ranges and ranges that are
3426
- * fully captured by other ranges.
3427
- */
3428
- function simplifyChangedRanges(changes) {
3429
- const uniqueChanges = removeDuplicates(changes);
3430
- return uniqueChanges.length === 1
3431
- ? uniqueChanges
3432
- : uniqueChanges.filter((change, index) => {
3433
- const rest = uniqueChanges.filter((_, i) => i !== index);
3434
- return !rest.some(otherChange => {
3435
- return change.oldRange.from >= otherChange.oldRange.from
3436
- && change.oldRange.to <= otherChange.oldRange.to
3437
- && change.newRange.from >= otherChange.newRange.from
3438
- && change.newRange.to <= otherChange.newRange.to;
3439
- });
3484
+ createNodeViews() {
3485
+ this.view.setProps({
3486
+ nodeViews: this.extensionManager.nodeViews,
3440
3487
  });
3441
- }
3442
- /**
3443
- * Returns a list of changed ranges
3444
- * based on the first and last state of all steps.
3445
- */
3446
- function getChangedRanges(transform) {
3447
- const { mapping, steps } = transform;
3448
- const changes = [];
3449
- mapping.maps.forEach((stepMap, index) => {
3450
- const ranges = [];
3451
- // This accounts for step changes where no range was actually altered
3452
- // e.g. when setting a mark, node attribute, etc.
3453
- // @ts-ignore
3454
- if (!stepMap.ranges.length) {
3455
- const { from, to } = steps[index];
3456
- if (from === undefined || to === undefined) {
3488
+ }
3489
+ captureTransaction(fn) {
3490
+ this.isCapturingTransaction = true;
3491
+ fn();
3492
+ this.isCapturingTransaction = false;
3493
+ const tr = this.capturedTransaction;
3494
+ this.capturedTransaction = null;
3495
+ return tr;
3496
+ }
3497
+ /**
3498
+ * The callback over which to send transactions (state updates) produced by the view.
3499
+ *
3500
+ * @param transaction An editor state transaction
3501
+ */
3502
+ dispatchTransaction(transaction) {
3503
+ if (this.isCapturingTransaction) {
3504
+ if (!this.capturedTransaction) {
3505
+ this.capturedTransaction = transaction;
3457
3506
  return;
3458
3507
  }
3459
- ranges.push({ from, to });
3508
+ transaction.steps.forEach(step => { var _a; return (_a = this.capturedTransaction) === null || _a === void 0 ? void 0 : _a.step(step); });
3509
+ return;
3460
3510
  }
3461
- else {
3462
- stepMap.forEach((from, to) => {
3463
- ranges.push({ from, to });
3511
+ const state = this.state.apply(transaction);
3512
+ const selectionHasChanged = !this.state.selection.eq(state.selection);
3513
+ this.view.updateState(state);
3514
+ this.emit('transaction', {
3515
+ editor: this,
3516
+ transaction,
3517
+ });
3518
+ if (selectionHasChanged) {
3519
+ this.emit('selectionUpdate', {
3520
+ editor: this,
3521
+ transaction,
3464
3522
  });
3465
3523
  }
3466
- ranges.forEach(({ from, to }) => {
3467
- const newStart = mapping.slice(index).map(from, -1);
3468
- const newEnd = mapping.slice(index).map(to);
3469
- const oldStart = mapping.invert().map(newStart, -1);
3470
- const oldEnd = mapping.invert().map(newEnd);
3471
- changes.push({
3472
- oldRange: {
3473
- from: oldStart,
3474
- to: oldEnd,
3475
- },
3476
- newRange: {
3477
- from: newStart,
3478
- to: newEnd,
3479
- },
3524
+ const focus = transaction.getMeta('focus');
3525
+ const blur = transaction.getMeta('blur');
3526
+ if (focus) {
3527
+ this.emit('focus', {
3528
+ editor: this,
3529
+ event: focus.event,
3530
+ transaction,
3480
3531
  });
3481
- });
3482
- });
3483
- return simplifyChangedRanges(changes);
3484
- }
3485
-
3486
- function getDebugJSON(node, startOffset = 0) {
3487
- const isTopNode = node.type === node.type.schema.topNodeType;
3488
- const increment = isTopNode ? 0 : 1;
3489
- const from = startOffset;
3490
- const to = from + node.nodeSize;
3491
- const marks = node.marks.map(mark => {
3492
- const output = {
3493
- type: mark.type.name,
3494
- };
3495
- if (Object.keys(mark.attrs).length) {
3496
- output.attrs = { ...mark.attrs };
3497
3532
  }
3498
- return output;
3499
- });
3500
- const attrs = { ...node.attrs };
3501
- const output = {
3502
- type: node.type.name,
3503
- from,
3504
- to,
3505
- };
3506
- if (Object.keys(attrs).length) {
3507
- output.attrs = attrs;
3533
+ if (blur) {
3534
+ this.emit('blur', {
3535
+ editor: this,
3536
+ event: blur.event,
3537
+ transaction,
3538
+ });
3539
+ }
3540
+ if (!transaction.docChanged || transaction.getMeta('preventUpdate')) {
3541
+ return;
3542
+ }
3543
+ this.emit('update', {
3544
+ editor: this,
3545
+ transaction,
3546
+ });
3508
3547
  }
3509
- if (marks.length) {
3510
- output.marks = marks;
3548
+ /**
3549
+ * Get attributes of the currently selected node or mark.
3550
+ */
3551
+ getAttributes(nameOrType) {
3552
+ return getAttributes(this.state, nameOrType);
3511
3553
  }
3512
- if (node.content.childCount) {
3513
- output.content = [];
3514
- node.forEach((child, offset) => {
3515
- var _a;
3516
- (_a = output.content) === null || _a === void 0 ? void 0 : _a.push(getDebugJSON(child, startOffset + offset + increment));
3517
- });
3554
+ isActive(nameOrAttributes, attributesOrUndefined) {
3555
+ const name = typeof nameOrAttributes === 'string'
3556
+ ? nameOrAttributes
3557
+ : null;
3558
+ const attributes = typeof nameOrAttributes === 'string'
3559
+ ? attributesOrUndefined
3560
+ : nameOrAttributes;
3561
+ return isActive(this.state, name, attributes);
3518
3562
  }
3519
- if (node.text) {
3520
- output.text = node.text;
3563
+ /**
3564
+ * Get the document as JSON.
3565
+ */
3566
+ getJSON() {
3567
+ return this.state.doc.toJSON();
3521
3568
  }
3522
- return output;
3523
- }
3524
-
3525
- function getMarksBetween(from, to, doc) {
3526
- const marks = [];
3527
- // get all inclusive marks on empty selection
3528
- if (from === to) {
3529
- doc
3530
- .resolve(from)
3531
- .marks()
3532
- .forEach(mark => {
3533
- const $pos = doc.resolve(from - 1);
3534
- const range = getMarkRange($pos, mark.type);
3535
- if (!range) {
3536
- return;
3537
- }
3538
- marks.push({
3539
- mark,
3540
- ...range,
3541
- });
3542
- });
3569
+ /**
3570
+ * Get the document as HTML.
3571
+ */
3572
+ getHTML() {
3573
+ return getHTMLFromFragment(this.state.doc.content, this.schema);
3543
3574
  }
3544
- else {
3545
- doc.nodesBetween(from, to, (node, pos) => {
3546
- marks.push(...node.marks.map(mark => ({
3547
- from: pos,
3548
- to: pos + node.nodeSize,
3549
- mark,
3550
- })));
3575
+ /**
3576
+ * Get the document as text.
3577
+ */
3578
+ getText(options) {
3579
+ const { blockSeparator = '\n\n', textSerializers = {}, } = options || {};
3580
+ return getText(this.state.doc, {
3581
+ blockSeparator,
3582
+ textSerializers: {
3583
+ ...textSerializers,
3584
+ ...getTextSerializersFromSchema(this.schema),
3585
+ },
3551
3586
  });
3552
3587
  }
3553
- return marks;
3554
- }
3555
-
3556
- function isNodeSelection(value) {
3557
- return value instanceof NodeSelection;
3558
- }
3559
-
3560
- function posToDOMRect(view, from, to) {
3561
- const minPos = 0;
3562
- const maxPos = view.state.doc.content.size;
3563
- const resolvedFrom = minMax(from, minPos, maxPos);
3564
- const resolvedEnd = minMax(to, minPos, maxPos);
3565
- const start = view.coordsAtPos(resolvedFrom);
3566
- const end = view.coordsAtPos(resolvedEnd, -1);
3567
- const top = Math.min(start.top, end.top);
3568
- const bottom = Math.max(start.bottom, end.bottom);
3569
- const left = Math.min(start.left, end.left);
3570
- const right = Math.max(start.right, end.right);
3571
- const width = right - left;
3572
- const height = bottom - top;
3573
- const x = left;
3574
- const y = top;
3575
- const data = {
3576
- top,
3577
- bottom,
3578
- left,
3579
- right,
3580
- width,
3581
- height,
3582
- x,
3583
- y,
3584
- };
3585
- return {
3586
- ...data,
3587
- toJSON: () => data,
3588
- };
3588
+ /**
3589
+ * Check if there is no content.
3590
+ */
3591
+ get isEmpty() {
3592
+ return isNodeEmpty(this.state.doc);
3593
+ }
3594
+ /**
3595
+ * Get the number of characters for the current document.
3596
+ *
3597
+ * @deprecated
3598
+ */
3599
+ getCharacterCount() {
3600
+ console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.');
3601
+ return this.state.doc.content.size - 2;
3602
+ }
3603
+ /**
3604
+ * Destroy the editor.
3605
+ */
3606
+ destroy() {
3607
+ this.emit('destroy');
3608
+ if (this.view) {
3609
+ this.view.destroy();
3610
+ }
3611
+ this.removeAllListeners();
3612
+ }
3613
+ /**
3614
+ * Check if the editor is already destroyed.
3615
+ */
3616
+ get isDestroyed() {
3617
+ var _a;
3618
+ // @ts-ignore
3619
+ return !((_a = this.view) === null || _a === void 0 ? void 0 : _a.docView);
3620
+ }
3589
3621
  }
3590
3622
 
3591
3623
  /**