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