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

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