@a3s-lab/office 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -7
- package/dist/0~1403.js +20 -12
- package/dist/0~2805.js +241 -241
- package/dist/0~4980.js +1 -1
- package/dist/0~5050.js +1 -20
- package/dist/0~5093.js +547 -547
- package/dist/0~6090.js +63 -4
- package/dist/0~7048.js +31 -29
- package/dist/0~document-editor.js +694 -90
- package/dist/0~pdf-viewer.js +41 -10
- package/dist/0~presentation-editor.js +8 -6
- package/dist/0~spreadsheet-editor.js +11892 -9682
- package/dist/0~work-document-import-worker-client.js +237 -0
- package/dist/0~work-docx-export.js +1 -1
- package/dist/0~work-docx-import.js +1 -1
- package/dist/0~work-docx-large-document-import.js +559 -0
- package/dist/0~work-office-diagnostics.js +46 -32
- package/dist/4104.js +5070 -3112
- package/dist/432.js +51 -7
- package/dist/4476.js +201 -7
- package/dist/5184.js +24 -2
- package/dist/{8928.js → 6282.js} +2338 -93
- package/dist/8715.js +895 -888
- package/dist/9356.js +3 -1
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/features/work/editors/document-editor-support.d.ts +1 -9
- package/dist/internal/features/work/editors/document-external-content.d.ts +1 -1
- package/dist/internal/features/work/editors/office-menu-keyboard.d.ts +1 -0
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +3 -1
- package/dist/internal/features/work/editors/presentation-slide-thumbnail.d.ts +3 -2
- package/dist/internal/features/work/editors/presentation-thumbnail-rail.d.ts +2 -1
- package/dist/internal/features/work/editors/presentation-workspace.d.ts +2 -1
- package/dist/internal/features/work/editors/spreadsheet-border-ribbon.d.ts +5 -0
- package/dist/internal/features/work/editors/spreadsheet-calculation-projection.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-border-command.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-border-per-cell.d.ts +5 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-border.d.ts +28 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-fill-command.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-fill.d.ts +12 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-range.d.ts +14 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-style-command.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-style-ribbon.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-style.d.ts +41 -0
- package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +293 -0
- package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +10 -4
- package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +3 -1
- package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +8 -2
- package/dist/internal/features/work/editors/spreadsheet-home-format-ribbon.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-in-place-workbook-sync.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-number-format.d.ts +15 -3
- package/dist/internal/features/work/editors/spreadsheet-operation-projection.d.ts +14 -0
- package/dist/internal/features/work/editors/spreadsheet-structure-selection.d.ts +7 -0
- package/dist/internal/features/work/editors/use-document-pagination.d.ts +5 -0
- package/dist/internal/features/work/editors/use-presentation-design-commands.d.ts +2 -1
- package/dist/internal/features/work/editors/use-spreadsheet-workbook-sync.d.ts +2 -1
- package/dist/internal/features/work/spreadsheet-history-value.d.ts +1 -0
- package/dist/internal/features/work/spreadsheet-sparse.d.ts +6 -1
- package/dist/internal/features/work/work-document-chunk-geometry.d.ts +28 -0
- package/dist/internal/features/work/work-document-chunk-node.d.ts +23 -0
- package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
- package/dist/internal/features/work/work-document-html-fingerprint.d.ts +15 -0
- package/dist/internal/features/work/work-document-import-worker-client.d.ts +13 -0
- package/dist/internal/features/work/work-document-import-worker-protocol.d.ts +36 -0
- package/dist/internal/features/work/work-document-import.worker.d.ts +1 -0
- package/dist/internal/features/work/work-document-incremental-layout.d.ts +1 -1
- package/dist/internal/features/work/work-document-integrity-index.d.ts +14 -0
- package/dist/internal/features/work/work-document-lazy-block.d.ts +3 -0
- package/dist/internal/features/work/work-document-lazy-html.d.ts +21 -0
- package/dist/internal/features/work/work-document-lazy-model.d.ts +29 -0
- package/dist/internal/features/work/work-document-lazy-pagination-measurement.d.ts +3 -0
- package/dist/internal/features/work/work-document-lazy-preview.d.ts +21 -0
- package/dist/internal/features/work/work-document-lazy-update.d.ts +14 -0
- package/dist/internal/features/work/work-document-model.d.ts +18 -2
- package/dist/internal/features/work/work-document-page-surface-registry.d.ts +16 -0
- package/dist/internal/features/work/work-document-pagination-dom.d.ts +1 -0
- package/dist/internal/features/work/work-document-pagination-types.d.ts +1 -0
- package/dist/internal/features/work/work-document-pagination.d.ts +1 -1
- package/dist/internal/features/work/work-document-text-statistics.d.ts +25 -0
- package/dist/internal/features/work/work-document-windowing.d.ts +23 -0
- package/dist/internal/features/work/work-docx-large-document-import.d.ts +35 -0
- package/dist/internal/features/work/work-docx-large-document-parser.d.ts +29 -0
- package/dist/internal/features/work/work-file-import.d.ts +5 -0
- package/dist/internal/features/work/work-file-io.d.ts +3 -3
- package/dist/internal/features/work/work-file-kind.d.ts +5 -0
- package/dist/internal/features/work/work-office-diagnostics.d.ts +10 -1
- package/dist/internal/features/work/work-ooxml-package.d.ts +8 -0
- package/dist/internal/features/work/work-presentation-layouts.d.ts +7 -1
- package/dist/internal/features/work/work-repository.d.ts +1 -0
- package/dist/internal/features/work/work-spreadsheet-compatibility-summary.d.ts +10 -0
- package/dist/internal/features/work/work-spreadsheet-file-export.d.ts +2 -0
- package/dist/internal/features/work/work-spreadsheet-file-import.d.ts +3 -0
- package/dist/internal/features/work/work-spreadsheet-import-diagnostics.d.ts +2 -0
- package/dist/internal/features/work/work-spreadsheet-import-worker-client.d.ts +6 -0
- package/dist/internal/features/work/work-spreadsheet-import-worker-protocol.d.ts +32 -0
- package/dist/internal/features/work/work-spreadsheet-import.worker.d.ts +1 -0
- package/dist/internal/features/work/work-spreadsheet-matrix-profile.d.ts +64 -0
- package/dist/internal/features/work/work-spreadsheet-package-scan-worker-client.d.ts +6 -0
- package/dist/internal/features/work/work-spreadsheet-package-scan-worker-protocol.d.ts +49 -0
- package/dist/internal/features/work/work-spreadsheet-package-scan.worker.d.ts +1 -0
- package/dist/internal/features/work/work-xlsx-cell-borders.d.ts +25 -0
- package/dist/internal/features/work/work-xlsx-cell-style-writer.d.ts +21 -0
- package/dist/internal/features/work/work-xlsx-cell-style-xml.d.ts +17 -0
- package/dist/internal/features/work/work-xlsx-cell-styles.d.ts +13 -0
- package/dist/internal/features/work/work-xlsx-colors.d.ts +6 -0
- package/dist/internal/features/work/work-xlsx-formula-diagnostics.d.ts +3 -1
- package/dist/internal/features/work/work-xlsx-formulas.d.ts +2 -1
- package/dist/internal/features/work/work-xlsx-interop.d.ts +8 -5
- package/dist/internal/features/work/work-xlsx-pivot-diagnostics.d.ts +2 -1
- package/dist/internal/features/work/work-xlsx-plain-fast-path.d.ts +54 -0
- package/dist/internal/features/work/work-xlsx-style-gate.d.ts +1 -0
- package/dist/internal/features/work/work-xlsx-worksheet-scan.d.ts +13 -0
- package/dist/internal/features/work/work-xlsx-worksheet.d.ts +12 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/office-kernel.worker.js +31 -29
- package/dist/react.d.ts +7 -1
- package/dist/styles.css +557 -3
- package/dist/work-document-import.worker.js +558 -0
- package/dist/work-spreadsheet-import.worker.js +24745 -0
- package/dist/work-spreadsheet-import.worker.js.LICENSE.txt +3 -0
- package/dist/work-spreadsheet-package-scan.worker.js +4161 -0
- package/dist/work-spreadsheet-package-scan.worker.js.LICENSE.txt +11 -0
- package/docs/latest/en/browser-editor-architecture.md +351 -15
- package/package.json +23 -3
package/dist/{8928.js → 6282.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Extension, Mark, Node as core_Node, ResizableNodeView, generateHTML, getSchema, mergeAttributes } from "@tiptap/core";
|
|
2
2
|
import { Collaboration, isChangeOrigin } from "@tiptap/extension-collaboration";
|
|
3
|
-
import { NodeSelection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
|
|
3
|
+
import { NodeSelection, Plugin, PluginKey, Selection, TextSelection } from "@tiptap/pm/state";
|
|
4
4
|
import { AddMarkStep, Mapping, RemoveMarkStep, ReplaceAroundStep, ReplaceStep } from "@tiptap/pm/transform";
|
|
5
5
|
import { defaultDeleteFilter, defaultProtectedNodes, prosemirrorJSONToYXmlFragment, ySyncPluginKey, yXmlFragmentToProsemirrorJSON } from "@tiptap/y-tiptap";
|
|
6
6
|
import extension_color from "@tiptap/extension-color";
|
|
@@ -195,6 +195,71 @@ function normalizedReferenceText(value) {
|
|
|
195
195
|
function stringAttribute(value) {
|
|
196
196
|
return 'string' == typeof value ? value.trim() : '';
|
|
197
197
|
}
|
|
198
|
+
const DOCUMENT_INTEGRITY_BOOKMARK = 1;
|
|
199
|
+
const DOCUMENT_INTEGRITY_FIELD = 2;
|
|
200
|
+
const DOCUMENT_INTEGRITY_IMAGE = 4;
|
|
201
|
+
const DOCUMENT_INTEGRITY_NOTE = 8;
|
|
202
|
+
const DOCUMENT_INTEGRITY_PARAGRAPH_IDENTITY = 16;
|
|
203
|
+
const DOCUMENT_INTEGRITY_TABLE_ROW_IDENTITY = 32;
|
|
204
|
+
const featureCache = new WeakMap();
|
|
205
|
+
function documentHasIntegrityFeature(document1, feature) {
|
|
206
|
+
return (documentIntegrityFeatures(document1, true) & feature) !== 0;
|
|
207
|
+
}
|
|
208
|
+
function primeDocumentIntegrityFeatures(document1) {
|
|
209
|
+
document1.forEach(primeDocumentIntegrityNode);
|
|
210
|
+
}
|
|
211
|
+
function primeDocumentIntegrityNode(node) {
|
|
212
|
+
if ('documentChunk' === node.type.name) {
|
|
213
|
+
const features = nonNegativeInteger(node.attrs.integrityFeatures);
|
|
214
|
+
if (null !== features) featureCache.set(node, features);
|
|
215
|
+
if (true !== node.attrs.windowContainer) return;
|
|
216
|
+
}
|
|
217
|
+
node.forEach(primeDocumentIntegrityNode);
|
|
218
|
+
}
|
|
219
|
+
function documentIntegrityFeatures(node, cacheNode) {
|
|
220
|
+
if (cacheNode) {
|
|
221
|
+
const cached = featureCache.get(node);
|
|
222
|
+
if (void 0 !== cached) return cached;
|
|
223
|
+
}
|
|
224
|
+
let features = nodeIntegrityFeatures(node);
|
|
225
|
+
node.forEach((child)=>{
|
|
226
|
+
features |= documentIntegrityFeatures(child, 'documentChunk' === child.type.name);
|
|
227
|
+
});
|
|
228
|
+
if (cacheNode) featureCache.set(node, features);
|
|
229
|
+
return features;
|
|
230
|
+
}
|
|
231
|
+
function nodeIntegrityFeatures(node) {
|
|
232
|
+
let features = 0;
|
|
233
|
+
switch(node.type.name){
|
|
234
|
+
case 'documentBookmarkBoundary':
|
|
235
|
+
features |= DOCUMENT_INTEGRITY_BOOKMARK;
|
|
236
|
+
break;
|
|
237
|
+
case 'documentCrossReference':
|
|
238
|
+
if ('bookmark' === node.attrs.targetType) features |= DOCUMENT_INTEGRITY_BOOKMARK;
|
|
239
|
+
break;
|
|
240
|
+
case 'documentField':
|
|
241
|
+
features |= DOCUMENT_INTEGRITY_FIELD;
|
|
242
|
+
break;
|
|
243
|
+
case 'image':
|
|
244
|
+
features |= DOCUMENT_INTEGRITY_IMAGE;
|
|
245
|
+
break;
|
|
246
|
+
case 'documentNote':
|
|
247
|
+
case 'documentNoteReference':
|
|
248
|
+
features |= DOCUMENT_INTEGRITY_NOTE;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
if (node.isText && node.marks.some((mark)=>'link' === mark.type.name && 'string' == typeof mark.attrs.href && mark.attrs.href.startsWith('#'))) features |= DOCUMENT_INTEGRITY_BOOKMARK;
|
|
252
|
+
if (identityComponent(node.attrs.paragraphId) || identityComponent(node.attrs.textId)) features |= DOCUMENT_INTEGRITY_PARAGRAPH_IDENTITY;
|
|
253
|
+
if (identityComponent(node.attrs.rowId) || identityComponent(node.attrs.rowTextId)) features |= DOCUMENT_INTEGRITY_TABLE_ROW_IDENTITY;
|
|
254
|
+
return features;
|
|
255
|
+
}
|
|
256
|
+
function identityComponent(value) {
|
|
257
|
+
return 'string' == typeof value && value.trim().length > 0;
|
|
258
|
+
}
|
|
259
|
+
function nonNegativeInteger(value) {
|
|
260
|
+
const number = Number(value);
|
|
261
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : null;
|
|
262
|
+
}
|
|
198
263
|
const BOOKMARK_NAME_PATTERN = /^[\p{L}_][\p{L}\p{N}_]*$/u;
|
|
199
264
|
const BOOKMARK_UI_NAME_PATTERN = /^[\p{L}][\p{L}\p{N}_]*$/u;
|
|
200
265
|
const MAX_BOOKMARK_NAME_LENGTH = 40;
|
|
@@ -403,6 +468,7 @@ function createDocumentBookmarkPlugin(boundaryNodeName) {
|
|
|
403
468
|
});
|
|
404
469
|
}
|
|
405
470
|
function normalizeDocumentBookmarks(state, boundaryNodeName, oldState, transactions = []) {
|
|
471
|
+
if ('documentBookmarkBoundary' === boundaryNodeName && !documentHasIntegrityFeature(state.doc, 1)) return null;
|
|
406
472
|
const collection = collectDocumentBookmarkPairs(state.doc, boundaryNodeName);
|
|
407
473
|
const retained = oldState ? retainedDocumentBookmarkPairs(oldState, state, transactions, boundaryNodeName) : new Set();
|
|
408
474
|
const ordered = [
|
|
@@ -1351,31 +1417,519 @@ function millimetersToPixels(value) {
|
|
|
1351
1417
|
function validMargin(value, fallback) {
|
|
1352
1418
|
return Number.isFinite(value) ? clampDocumentMargin(value) : fallback;
|
|
1353
1419
|
}
|
|
1420
|
+
const DOCUMENT_HTML_FINGERPRINT_VERSION = 'p1';
|
|
1421
|
+
const DOCUMENT_HTML_HASH_BASE = 0x01000193;
|
|
1422
|
+
const LEGACY_FNV_OFFSET = 0x811c9dc5;
|
|
1423
|
+
function createDocumentHtmlFingerprintSegment(source, from = 0, to = source.length) {
|
|
1424
|
+
if (!Number.isSafeInteger(from) || !Number.isSafeInteger(to) || from < 0 || to < from || to > source.length) throw new RangeError('The document HTML fingerprint range is invalid.');
|
|
1425
|
+
let hash = 0;
|
|
1426
|
+
for(let index = from; index < to; index += 1)hash = Math.imul(hash, DOCUMENT_HTML_HASH_BASE) + source.charCodeAt(index) >>> 0;
|
|
1427
|
+
const length = to - from;
|
|
1428
|
+
return {
|
|
1429
|
+
hash,
|
|
1430
|
+
length,
|
|
1431
|
+
power: documentHtmlHashPower(length)
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
function combineDocumentHtmlFingerprintSegments(segments) {
|
|
1435
|
+
let combined = {
|
|
1436
|
+
hash: 0,
|
|
1437
|
+
length: 0,
|
|
1438
|
+
power: 1
|
|
1439
|
+
};
|
|
1440
|
+
for (const segment of segments)combined = {
|
|
1441
|
+
hash: Math.imul(combined.hash, segment.power) + segment.hash >>> 0,
|
|
1442
|
+
length: combined.length + segment.length,
|
|
1443
|
+
power: Math.imul(combined.power, segment.power) >>> 0
|
|
1444
|
+
};
|
|
1445
|
+
return combined;
|
|
1446
|
+
}
|
|
1447
|
+
function documentHtmlFingerprintForSegment(segment) {
|
|
1448
|
+
return `${DOCUMENT_HTML_FINGERPRINT_VERSION}:${segment.length.toString(36)}:${segment.hash.toString(36)}`;
|
|
1449
|
+
}
|
|
1450
|
+
function documentHtmlFingerprint(html) {
|
|
1451
|
+
return documentHtmlFingerprintForSegment(createDocumentHtmlFingerprintSegment(html));
|
|
1452
|
+
}
|
|
1453
|
+
function documentHtmlFingerprintMatches(html, candidate) {
|
|
1454
|
+
return candidate.startsWith(`${DOCUMENT_HTML_FINGERPRINT_VERSION}:`) ? candidate === documentHtmlFingerprint(html) : candidate === legacyDocumentHtmlFingerprint(html);
|
|
1455
|
+
}
|
|
1456
|
+
function legacyDocumentHtmlFingerprint(html) {
|
|
1457
|
+
let hash = LEGACY_FNV_OFFSET;
|
|
1458
|
+
for(let index = 0; index < html.length; index += 1){
|
|
1459
|
+
hash ^= html.charCodeAt(index);
|
|
1460
|
+
hash = Math.imul(hash, DOCUMENT_HTML_HASH_BASE);
|
|
1461
|
+
}
|
|
1462
|
+
return `${html.length.toString(36)}:${(hash >>> 0).toString(36)}`;
|
|
1463
|
+
}
|
|
1464
|
+
function documentHtmlHashPower(length) {
|
|
1465
|
+
let exponent = length;
|
|
1466
|
+
let factor = DOCUMENT_HTML_HASH_BASE;
|
|
1467
|
+
let power = 1;
|
|
1468
|
+
while(exponent > 0){
|
|
1469
|
+
if (exponent % 2 === 1) power = Math.imul(power, factor) >>> 0;
|
|
1470
|
+
factor = Math.imul(factor, factor) >>> 0;
|
|
1471
|
+
exponent = Math.floor(exponent / 2);
|
|
1472
|
+
}
|
|
1473
|
+
return power;
|
|
1474
|
+
}
|
|
1475
|
+
const SECTION_CLOSE = '</section>';
|
|
1476
|
+
const TABLE_BODY_OPEN = '<tbody>';
|
|
1477
|
+
const TABLE_CLOSE = '</tbody></table>';
|
|
1478
|
+
const projectionFingerprints = new WeakMap();
|
|
1479
|
+
function createDocumentLazyHtmlProjection(html, root) {
|
|
1480
|
+
if (!html.startsWith('<section') || !html.endsWith(SECTION_CLOSE)) return null;
|
|
1481
|
+
const sectionOpenEnd = html.indexOf('>') + 1;
|
|
1482
|
+
if (sectionOpenEnd <= 0) return null;
|
|
1483
|
+
let cursor = sectionOpenEnd;
|
|
1484
|
+
const orderedRanges = [];
|
|
1485
|
+
const ranges = new Map();
|
|
1486
|
+
for (const chunk of lazyDocumentLeafChunks(root)){
|
|
1487
|
+
const id = 'string' == typeof chunk.attrs?.id ? chunk.attrs.id : '';
|
|
1488
|
+
if (!id || ranges.has(id)) return null;
|
|
1489
|
+
const from = cursor;
|
|
1490
|
+
let tablePart = null;
|
|
1491
|
+
const content = chunk.content ?? [];
|
|
1492
|
+
for (const node of content){
|
|
1493
|
+
const scanned = scanSimpleDocumentNodeHtml(html, cursor, node);
|
|
1494
|
+
if (!scanned) return null;
|
|
1495
|
+
cursor = scanned.to;
|
|
1496
|
+
if (scanned.tablePart) {
|
|
1497
|
+
if (1 !== content.length || tablePart) return null;
|
|
1498
|
+
tablePart = scanned.tablePart;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
const range = {
|
|
1502
|
+
from,
|
|
1503
|
+
id,
|
|
1504
|
+
tablePart,
|
|
1505
|
+
to: cursor
|
|
1506
|
+
};
|
|
1507
|
+
orderedRanges.push(range);
|
|
1508
|
+
ranges.set(id, range);
|
|
1509
|
+
}
|
|
1510
|
+
if (cursor !== html.length - SECTION_CLOSE.length) return null;
|
|
1511
|
+
return {
|
|
1512
|
+
html,
|
|
1513
|
+
orderedRanges,
|
|
1514
|
+
ranges
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
function patchDocumentLazyHtmlProjection(projection, replacements) {
|
|
1518
|
+
if (!replacements.size) return projection.html;
|
|
1519
|
+
for (const id of replacements.keys())if (!projection.ranges.has(id)) return null;
|
|
1520
|
+
const fingerprintState = documentLazyHtmlFingerprintState(projection);
|
|
1521
|
+
const parts = [];
|
|
1522
|
+
let cursor = 0;
|
|
1523
|
+
for (const range of projection.orderedRanges){
|
|
1524
|
+
const replacement = replacements.get(range.id);
|
|
1525
|
+
if (void 0 !== replacement) {
|
|
1526
|
+
parts.push(projection.html.slice(cursor, range.from), replacement);
|
|
1527
|
+
fingerprintState.ranges.set(range.id, createDocumentHtmlFingerprintSegment(replacement));
|
|
1528
|
+
cursor = range.to;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
parts.push(projection.html.slice(cursor));
|
|
1532
|
+
const html = parts.join('');
|
|
1533
|
+
let offset = 0;
|
|
1534
|
+
for (const range of projection.orderedRanges){
|
|
1535
|
+
const previousFrom = range.from;
|
|
1536
|
+
const previousLength = range.to - previousFrom;
|
|
1537
|
+
const replacement = replacements.get(range.id);
|
|
1538
|
+
const nextLength = replacement?.length ?? previousLength;
|
|
1539
|
+
range.from = previousFrom + offset;
|
|
1540
|
+
range.to = range.from + nextLength;
|
|
1541
|
+
offset += nextLength - previousLength;
|
|
1542
|
+
}
|
|
1543
|
+
projection.html = html;
|
|
1544
|
+
fingerprintState.fingerprint = documentHtmlFingerprintForSegment(combineDocumentHtmlFingerprintSegments([
|
|
1545
|
+
fingerprintState.prefix,
|
|
1546
|
+
...projection.orderedRanges.map((range)=>{
|
|
1547
|
+
const segment = fingerprintState.ranges.get(range.id);
|
|
1548
|
+
if (!segment) throw new Error('The lazy HTML fingerprint segment is missing.');
|
|
1549
|
+
return segment;
|
|
1550
|
+
}),
|
|
1551
|
+
fingerprintState.suffix
|
|
1552
|
+
]));
|
|
1553
|
+
return html;
|
|
1554
|
+
}
|
|
1555
|
+
function documentLazyHtmlProjectionFingerprint(projection) {
|
|
1556
|
+
return projectionFingerprints.get(projection)?.fingerprint ?? null;
|
|
1557
|
+
}
|
|
1558
|
+
function documentLazyHtmlChunkFragment(html, tablePart) {
|
|
1559
|
+
if (!tablePart || 'complete' === tablePart) return html;
|
|
1560
|
+
const bodyOpen = html.indexOf(TABLE_BODY_OPEN);
|
|
1561
|
+
const bodyClose = html.lastIndexOf('</tbody>');
|
|
1562
|
+
if (bodyOpen < 0 || bodyClose < bodyOpen) return null;
|
|
1563
|
+
const rowsFrom = bodyOpen + TABLE_BODY_OPEN.length;
|
|
1564
|
+
if ('first' === tablePart) return html.slice(0, bodyClose);
|
|
1565
|
+
if ('middle' === tablePart) return html.slice(rowsFrom, bodyClose);
|
|
1566
|
+
return html.slice(rowsFrom);
|
|
1567
|
+
}
|
|
1568
|
+
function lazyDocumentLeafChunks(root) {
|
|
1569
|
+
const chunks = [];
|
|
1570
|
+
const pending = [
|
|
1571
|
+
root
|
|
1572
|
+
];
|
|
1573
|
+
while(pending.length){
|
|
1574
|
+
const node = pending.pop();
|
|
1575
|
+
if (!node) continue;
|
|
1576
|
+
if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
|
|
1577
|
+
chunks.push(node);
|
|
1578
|
+
continue;
|
|
1579
|
+
}
|
|
1580
|
+
for(let index = (node.content?.length ?? 0) - 1; index >= 0; index -= 1){
|
|
1581
|
+
const child = node.content?.[index];
|
|
1582
|
+
if (child) pending.push(child);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
return chunks;
|
|
1586
|
+
}
|
|
1587
|
+
function documentLazyHtmlFingerprintState(projection) {
|
|
1588
|
+
const existing = projectionFingerprints.get(projection);
|
|
1589
|
+
if (existing) return existing;
|
|
1590
|
+
const first = projection.orderedRanges[0];
|
|
1591
|
+
const last = projection.orderedRanges.at(-1);
|
|
1592
|
+
const ranges = new Map();
|
|
1593
|
+
for (const range of projection.orderedRanges)ranges.set(range.id, createDocumentHtmlFingerprintSegment(projection.html, range.from, range.to));
|
|
1594
|
+
const prefix = createDocumentHtmlFingerprintSegment(projection.html, 0, first?.from ?? projection.html.length);
|
|
1595
|
+
const suffix = createDocumentHtmlFingerprintSegment(projection.html, last?.to ?? projection.html.length, projection.html.length);
|
|
1596
|
+
const state = {
|
|
1597
|
+
fingerprint: documentHtmlFingerprintForSegment(combineDocumentHtmlFingerprintSegments([
|
|
1598
|
+
prefix,
|
|
1599
|
+
...projection.orderedRanges.map((range)=>{
|
|
1600
|
+
const segment = ranges.get(range.id);
|
|
1601
|
+
if (!segment) throw new Error('The lazy HTML fingerprint segment is missing.');
|
|
1602
|
+
return segment;
|
|
1603
|
+
}),
|
|
1604
|
+
suffix
|
|
1605
|
+
])),
|
|
1606
|
+
prefix,
|
|
1607
|
+
ranges,
|
|
1608
|
+
suffix
|
|
1609
|
+
};
|
|
1610
|
+
projectionFingerprints.set(projection, state);
|
|
1611
|
+
return state;
|
|
1612
|
+
}
|
|
1613
|
+
function scanSimpleDocumentNodeHtml(html, from, node) {
|
|
1614
|
+
if ('paragraph' === node.type) {
|
|
1615
|
+
if (!html.startsWith('<p>', from)) return null;
|
|
1616
|
+
const close = html.indexOf('</p>', from + 3);
|
|
1617
|
+
return close < 0 ? null : {
|
|
1618
|
+
tablePart: null,
|
|
1619
|
+
to: close + 4
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
if ('table' !== node.type) return null;
|
|
1623
|
+
const virtualIndex = Number(node.attrs?.virtualTableIndex);
|
|
1624
|
+
const virtualCount = Number(node.attrs?.virtualTableCount);
|
|
1625
|
+
const virtual = Boolean('string' == typeof node.attrs?.virtualTableId && node.attrs.virtualTableId && Number.isSafeInteger(virtualIndex) && Number.isSafeInteger(virtualCount) && virtualIndex >= 0 && virtualCount > 0 && virtualIndex < virtualCount);
|
|
1626
|
+
let cursor = from;
|
|
1627
|
+
if (!virtual || 0 === virtualIndex) {
|
|
1628
|
+
if (!html.startsWith('<table', cursor)) return null;
|
|
1629
|
+
const body = html.indexOf(TABLE_BODY_OPEN, cursor);
|
|
1630
|
+
if (body < 0) return null;
|
|
1631
|
+
cursor = body + TABLE_BODY_OPEN.length;
|
|
1632
|
+
}
|
|
1633
|
+
for (const row of node.content ?? []){
|
|
1634
|
+
if ('tableRow' !== row.type || !html.startsWith('<tr>', cursor)) return null;
|
|
1635
|
+
const close = html.indexOf('</tr>', cursor + 4);
|
|
1636
|
+
if (close < 0) return null;
|
|
1637
|
+
cursor = close + 5;
|
|
1638
|
+
}
|
|
1639
|
+
if (!virtual || virtualIndex === virtualCount - 1) {
|
|
1640
|
+
if (!html.startsWith(TABLE_CLOSE, cursor)) return null;
|
|
1641
|
+
cursor += TABLE_CLOSE.length;
|
|
1642
|
+
}
|
|
1643
|
+
const tablePart = virtual ? 1 === virtualCount ? 'complete' : 0 === virtualIndex ? 'first' : virtualIndex === virtualCount - 1 ? 'last' : 'middle' : 'complete';
|
|
1644
|
+
return {
|
|
1645
|
+
tablePart,
|
|
1646
|
+
to: cursor
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
const DOCUMENT_LAZY_BLOCK_NODE = 'documentLazyBlock';
|
|
1650
|
+
const DOCUMENT_LAZY_INITIAL_CHUNK_COUNT = 2;
|
|
1651
|
+
const DOCUMENT_LAZY_POSITION_BOUNDARY = '\ufffc';
|
|
1652
|
+
const SIMPLE_LAZY_CONTAINER_TYPES = new Set([
|
|
1653
|
+
'paragraph',
|
|
1654
|
+
'table',
|
|
1655
|
+
'tableCell',
|
|
1656
|
+
'tableRow'
|
|
1657
|
+
]);
|
|
1658
|
+
const preparedModels = new WeakMap();
|
|
1659
|
+
function prepareLazyDocumentEditorSource(model, allowCreate, html) {
|
|
1660
|
+
const startedAt = lazyDocumentNow();
|
|
1661
|
+
const cacheHit = preparedModels.has(model);
|
|
1662
|
+
let prepared = preparedModels.get(model);
|
|
1663
|
+
if (prepared) {
|
|
1664
|
+
if (!prepared.root) {
|
|
1665
|
+
const htmlProjection = prepared.htmlProjection;
|
|
1666
|
+
const rebuilt = createPreparedLazyDocumentEditorSource(model.root, prepared.payloads);
|
|
1667
|
+
if (!rebuilt) return null;
|
|
1668
|
+
rebuilt.htmlProjection = htmlProjection;
|
|
1669
|
+
prepared = rebuilt;
|
|
1670
|
+
preparedModels.set(model, rebuilt);
|
|
1671
|
+
}
|
|
1672
|
+
} else {
|
|
1673
|
+
if (!allowCreate) return null;
|
|
1674
|
+
const created = createPreparedLazyDocumentEditorSource(model.root);
|
|
1675
|
+
if (!created) return null;
|
|
1676
|
+
prepared = created;
|
|
1677
|
+
preparedModels.set(model, created);
|
|
1678
|
+
}
|
|
1679
|
+
if (!prepared.htmlProjection && html) prepared.htmlProjection = createDocumentLazyHtmlProjection(html, model.root);
|
|
1680
|
+
if (!prepared.root) return null;
|
|
1681
|
+
const result = {
|
|
1682
|
+
lazyChunkCount: prepared.lazyChunkCount,
|
|
1683
|
+
payloads: prepared.payloads,
|
|
1684
|
+
root: prepared.root
|
|
1685
|
+
};
|
|
1686
|
+
recordLazyDocumentMeasure('a3s-office.document.lazy-editor-source', startedAt, lazyDocumentNow(), {
|
|
1687
|
+
cacheHit
|
|
1688
|
+
});
|
|
1689
|
+
return result;
|
|
1690
|
+
}
|
|
1691
|
+
function documentLazyHtmlProjection(model) {
|
|
1692
|
+
return model ? preparedModels.get(model)?.htmlProjection ?? null : null;
|
|
1693
|
+
}
|
|
1694
|
+
function invalidateDocumentLazyHtmlProjection(model) {
|
|
1695
|
+
if (model) {
|
|
1696
|
+
const prepared = preparedModels.get(model);
|
|
1697
|
+
if (prepared) prepared.htmlProjection = null;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
function documentLazyChunkContent(model, chunkId) {
|
|
1701
|
+
if (!model || !chunkId) return null;
|
|
1702
|
+
return preparedModels.get(model)?.payloads.get(chunkId) ?? null;
|
|
1703
|
+
}
|
|
1704
|
+
function materializeLazyDocumentEditorRoot(root, model) {
|
|
1705
|
+
const prepared = model ? preparedModels.get(model) : null;
|
|
1706
|
+
if (!prepared) return root;
|
|
1707
|
+
const visit = (node)=>{
|
|
1708
|
+
if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
|
|
1709
|
+
const id = documentChunkId(node);
|
|
1710
|
+
if (!id) return node;
|
|
1711
|
+
if (documentChunkIsLazy(node)) {
|
|
1712
|
+
const payload = prepared.payloads.get(id);
|
|
1713
|
+
if (!payload) throw new Error(`The lazy document chunk payload "${id}" is missing.`);
|
|
1714
|
+
return {
|
|
1715
|
+
...node,
|
|
1716
|
+
content: [
|
|
1717
|
+
...payload
|
|
1718
|
+
]
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
if (node.content?.length) prepared.payloads.set(id, node.content);
|
|
1722
|
+
return node;
|
|
1723
|
+
}
|
|
1724
|
+
if (!node.content?.length) return node;
|
|
1725
|
+
let changed = false;
|
|
1726
|
+
const content = node.content.map((child)=>{
|
|
1727
|
+
const next = visit(child);
|
|
1728
|
+
if (next !== child) changed = true;
|
|
1729
|
+
return next;
|
|
1730
|
+
});
|
|
1731
|
+
return changed ? {
|
|
1732
|
+
...node,
|
|
1733
|
+
content
|
|
1734
|
+
} : node;
|
|
1735
|
+
};
|
|
1736
|
+
return visit(root);
|
|
1737
|
+
}
|
|
1738
|
+
function transferLazyDocumentModelState(previous, next) {
|
|
1739
|
+
if (!previous) return;
|
|
1740
|
+
const prepared = preparedModels.get(previous);
|
|
1741
|
+
if (!prepared) return;
|
|
1742
|
+
preparedModels.set(next, {
|
|
1743
|
+
htmlProjection: prepared.htmlProjection,
|
|
1744
|
+
lazyChunkCount: prepared.lazyChunkCount,
|
|
1745
|
+
payloads: prepared.payloads,
|
|
1746
|
+
root: null
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
function simpleDocumentNodeSize(node) {
|
|
1750
|
+
if ('text' === node.type) return 'string' == typeof node.text && node.text ? node.text.length : null;
|
|
1751
|
+
if (!SIMPLE_LAZY_CONTAINER_TYPES.has(node.type)) return null;
|
|
1752
|
+
let size = 2;
|
|
1753
|
+
for (const child of node.content ?? []){
|
|
1754
|
+
const childSize = simpleDocumentNodeSize(child);
|
|
1755
|
+
if (null === childSize) return null;
|
|
1756
|
+
size += childSize;
|
|
1757
|
+
}
|
|
1758
|
+
return size;
|
|
1759
|
+
}
|
|
1760
|
+
function createPreparedLazyDocumentEditorSource(root, previousPayloads) {
|
|
1761
|
+
const payloads = new Map();
|
|
1762
|
+
let leafIndex = 0;
|
|
1763
|
+
let lazyChunkCount = 0;
|
|
1764
|
+
let unsupported = false;
|
|
1765
|
+
const visit = (node)=>{
|
|
1766
|
+
if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
|
|
1767
|
+
const id = documentChunkId(node);
|
|
1768
|
+
const payload = id ? previousPayloads?.get(id) ?? node.content ?? [] : [];
|
|
1769
|
+
if (!id || !payload.length) {
|
|
1770
|
+
unsupported = true;
|
|
1771
|
+
return node;
|
|
1772
|
+
}
|
|
1773
|
+
payloads.set(id, payload);
|
|
1774
|
+
const currentIndex = leafIndex;
|
|
1775
|
+
leafIndex += 1;
|
|
1776
|
+
if (currentIndex < DOCUMENT_LAZY_INITIAL_CHUNK_COUNT) return node;
|
|
1777
|
+
const placeholder = lazyPlaceholderForContent(id, payload);
|
|
1778
|
+
if (!placeholder) {
|
|
1779
|
+
unsupported = true;
|
|
1780
|
+
return node;
|
|
1781
|
+
}
|
|
1782
|
+
lazyChunkCount += 1;
|
|
1783
|
+
return {
|
|
1784
|
+
...node,
|
|
1785
|
+
content: [
|
|
1786
|
+
placeholder
|
|
1787
|
+
]
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
if (!node.content?.length) return node;
|
|
1791
|
+
let changed = false;
|
|
1792
|
+
const content = node.content.map((child)=>{
|
|
1793
|
+
const next = visit(child);
|
|
1794
|
+
if (next !== child) changed = true;
|
|
1795
|
+
return next;
|
|
1796
|
+
});
|
|
1797
|
+
return changed ? {
|
|
1798
|
+
...node,
|
|
1799
|
+
content
|
|
1800
|
+
} : node;
|
|
1801
|
+
};
|
|
1802
|
+
const compactRoot = visit(root);
|
|
1803
|
+
if (unsupported || leafIndex <= DOCUMENT_LAZY_INITIAL_CHUNK_COUNT || 0 === lazyChunkCount) return null;
|
|
1804
|
+
return {
|
|
1805
|
+
htmlProjection: null,
|
|
1806
|
+
lazyChunkCount,
|
|
1807
|
+
payloads,
|
|
1808
|
+
root: compactRoot
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
function lazyPlaceholderForContent(chunkId, content) {
|
|
1812
|
+
const tapeParts = [];
|
|
1813
|
+
const statistics = {
|
|
1814
|
+
contentSize: 0,
|
|
1815
|
+
paragraphCount: 0
|
|
1816
|
+
};
|
|
1817
|
+
for (const node of content)if (!appendDocumentNodePositionTape(node, tapeParts, statistics)) return null;
|
|
1818
|
+
if (statistics.contentSize < 2 || tapeParts[0] !== DOCUMENT_LAZY_POSITION_BOUNDARY || tapeParts.at(-1) !== DOCUMENT_LAZY_POSITION_BOUNDARY) return null;
|
|
1819
|
+
tapeParts[0] = '';
|
|
1820
|
+
tapeParts[tapeParts.length - 1] = '';
|
|
1821
|
+
const filler = tapeParts.join('');
|
|
1822
|
+
return {
|
|
1823
|
+
type: DOCUMENT_LAZY_BLOCK_NODE,
|
|
1824
|
+
attrs: {
|
|
1825
|
+
chunkId,
|
|
1826
|
+
contentSize: statistics.contentSize,
|
|
1827
|
+
paragraphCount: statistics.paragraphCount
|
|
1828
|
+
},
|
|
1829
|
+
...filler ? {
|
|
1830
|
+
content: [
|
|
1831
|
+
{
|
|
1832
|
+
type: 'text',
|
|
1833
|
+
text: filler
|
|
1834
|
+
}
|
|
1835
|
+
]
|
|
1836
|
+
} : {
|
|
1837
|
+
content: []
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
function appendDocumentNodePositionTape(node, parts, statistics) {
|
|
1842
|
+
if ('text' === node.type) {
|
|
1843
|
+
if ('string' != typeof node.text || !node.text) return false;
|
|
1844
|
+
parts.push(node.text);
|
|
1845
|
+
statistics.contentSize += node.text.length;
|
|
1846
|
+
return true;
|
|
1847
|
+
}
|
|
1848
|
+
if (!SIMPLE_LAZY_CONTAINER_TYPES.has(node.type)) return false;
|
|
1849
|
+
if ('paragraph' === node.type) statistics.paragraphCount += 1;
|
|
1850
|
+
statistics.contentSize += 2;
|
|
1851
|
+
parts.push(DOCUMENT_LAZY_POSITION_BOUNDARY);
|
|
1852
|
+
for (const child of node.content ?? [])if (!appendDocumentNodePositionTape(child, parts, statistics)) return false;
|
|
1853
|
+
parts.push(DOCUMENT_LAZY_POSITION_BOUNDARY);
|
|
1854
|
+
return true;
|
|
1855
|
+
}
|
|
1856
|
+
function documentChunkIsLazy(node) {
|
|
1857
|
+
return 'documentChunk' === node.type && node.attrs?.windowContainer !== true && node.content?.length === 1 && node.content[0]?.type === DOCUMENT_LAZY_BLOCK_NODE;
|
|
1858
|
+
}
|
|
1859
|
+
function documentChunkId(node) {
|
|
1860
|
+
const id = node.attrs?.id;
|
|
1861
|
+
return 'string' == typeof id && id ? id : null;
|
|
1862
|
+
}
|
|
1863
|
+
function lazyDocumentNow() {
|
|
1864
|
+
return globalThis.performance?.now?.() ?? Date.now();
|
|
1865
|
+
}
|
|
1866
|
+
function recordLazyDocumentMeasure(name, start, end, detail) {
|
|
1867
|
+
try {
|
|
1868
|
+
globalThis.performance?.measure(name, {
|
|
1869
|
+
detail,
|
|
1870
|
+
end,
|
|
1871
|
+
start
|
|
1872
|
+
});
|
|
1873
|
+
} catch {}
|
|
1874
|
+
}
|
|
1354
1875
|
const DOCUMENT_MODEL_SCHEMA = 'a3s.office.document';
|
|
1355
1876
|
const DOCUMENT_MODEL_VERSION = 1;
|
|
1356
1877
|
const MAX_DOCUMENT_MODEL_DEPTH = 256;
|
|
1357
|
-
const MAX_DOCUMENT_MODEL_NODES =
|
|
1358
|
-
const MAX_DOCUMENT_MODEL_MARKS =
|
|
1359
|
-
const MAX_DOCUMENT_MODEL_ATTRIBUTE_VALUES =
|
|
1878
|
+
const MAX_DOCUMENT_MODEL_NODES = 2000000;
|
|
1879
|
+
const MAX_DOCUMENT_MODEL_MARKS = 2000000;
|
|
1880
|
+
const MAX_DOCUMENT_MODEL_ATTRIBUTE_VALUES = 10000000;
|
|
1881
|
+
const schemaValidatedDocumentModels = new WeakMap();
|
|
1360
1882
|
function createWorkDocumentModel(html, root, previous) {
|
|
1361
|
-
return
|
|
1883
|
+
return createWorkDocumentModelWithFingerprint(root, documentHtmlFingerprint(html), previous);
|
|
1884
|
+
}
|
|
1885
|
+
function createWorkDocumentModelWithFingerprint(root, htmlFingerprint, previous) {
|
|
1886
|
+
const model = {
|
|
1362
1887
|
schema: DOCUMENT_MODEL_SCHEMA,
|
|
1363
1888
|
version: DOCUMENT_MODEL_VERSION,
|
|
1364
1889
|
revision: nextRevision(previous),
|
|
1365
|
-
htmlFingerprint
|
|
1890
|
+
htmlFingerprint,
|
|
1366
1891
|
root
|
|
1367
1892
|
};
|
|
1893
|
+
transferLazyDocumentModelState(previous, model);
|
|
1894
|
+
return model;
|
|
1895
|
+
}
|
|
1896
|
+
function createSchemaValidatedWorkDocumentModel(html, root, options = {}) {
|
|
1897
|
+
const model = createWorkDocumentModel(html, root, options.previous);
|
|
1898
|
+
schemaValidatedDocumentModels.set(model, {
|
|
1899
|
+
html,
|
|
1900
|
+
htmlFingerprint: model.htmlFingerprint,
|
|
1901
|
+
initialIntegrityFeatures: work_document_model_nonNegativeInteger(options.initialIntegrityFeatures),
|
|
1902
|
+
root
|
|
1903
|
+
});
|
|
1904
|
+
return model;
|
|
1905
|
+
}
|
|
1906
|
+
function createSchemaDerivedWorkDocumentModel(html, root, previous, htmlFingerprint = documentHtmlFingerprint(html)) {
|
|
1907
|
+
const model = createWorkDocumentModelWithFingerprint(root, htmlFingerprint, previous);
|
|
1908
|
+
schemaValidatedDocumentModels.set(model, {
|
|
1909
|
+
html,
|
|
1910
|
+
htmlFingerprint: model.htmlFingerprint,
|
|
1911
|
+
initialIntegrityFeatures: (previous && schemaValidatedDocumentModels.get(previous)?.initialIntegrityFeatures) ?? null,
|
|
1912
|
+
root
|
|
1913
|
+
});
|
|
1914
|
+
return model;
|
|
1915
|
+
}
|
|
1916
|
+
function documentModelHasTrustedInitialIntegrityFeatures(model) {
|
|
1917
|
+
if (!model) return false;
|
|
1918
|
+
const validated = schemaValidatedDocumentModels.get(model);
|
|
1919
|
+
return validated?.root === model.root && null !== validated.initialIntegrityFeatures;
|
|
1368
1920
|
}
|
|
1369
1921
|
function documentModelForContent(content) {
|
|
1370
1922
|
return documentModelForHtml(content.model, content.html);
|
|
1371
1923
|
}
|
|
1372
1924
|
function documentModelForHtml(candidate, html) {
|
|
1373
1925
|
if (!isRecord(candidate)) return null;
|
|
1374
|
-
|
|
1926
|
+
const validated = schemaValidatedDocumentModels.get(candidate);
|
|
1927
|
+
const trustedHtmlMatches = Boolean(validated && validated.root === candidate.root && validated.html === html && validated.htmlFingerprint === candidate.htmlFingerprint);
|
|
1928
|
+
if (candidate.schema !== DOCUMENT_MODEL_SCHEMA || candidate.version !== DOCUMENT_MODEL_VERSION || !Number.isSafeInteger(candidate.revision) || Number(candidate.revision) < 1 || 'string' != typeof candidate.htmlFingerprint || !trustedHtmlMatches && !documentHtmlFingerprintMatches(html, candidate.htmlFingerprint) || validated?.root !== candidate.root && !isDocumentRoot(candidate.root)) return null;
|
|
1375
1929
|
return candidate;
|
|
1376
1930
|
}
|
|
1377
|
-
function resolveWorkDocumentEditorInput(content, fallbackHtml) {
|
|
1378
|
-
const model =
|
|
1931
|
+
function resolveWorkDocumentEditorInput(content, fallbackHtml, validatedModel = documentModelForContent(content)) {
|
|
1932
|
+
const model = validatedModel;
|
|
1379
1933
|
if (!model) return {
|
|
1380
1934
|
model: null,
|
|
1381
1935
|
source: fallbackHtml,
|
|
@@ -1384,23 +1938,19 @@ function resolveWorkDocumentEditorInput(content, fallbackHtml) {
|
|
|
1384
1938
|
};
|
|
1385
1939
|
return {
|
|
1386
1940
|
model,
|
|
1387
|
-
source: model.root,
|
|
1941
|
+
source: prepareLazyDocumentEditorSource(model, documentModelHasTrustedInitialIntegrityFeatures(model), content.html)?.root ?? model.root,
|
|
1388
1942
|
sourceKey: `model:${model.revision}:${model.htmlFingerprint}`,
|
|
1389
1943
|
revision: model.revision
|
|
1390
1944
|
};
|
|
1391
1945
|
}
|
|
1392
|
-
function documentHtmlFingerprint(html) {
|
|
1393
|
-
let hash = 0x811c9dc5;
|
|
1394
|
-
for(let index = 0; index < html.length; index += 1){
|
|
1395
|
-
hash ^= html.charCodeAt(index);
|
|
1396
|
-
hash = Math.imul(hash, 0x01000193);
|
|
1397
|
-
}
|
|
1398
|
-
return `${html.length.toString(36)}:${(hash >>> 0).toString(36)}`;
|
|
1399
|
-
}
|
|
1400
1946
|
function nextRevision(previous) {
|
|
1401
1947
|
const revision = previous?.schema === DOCUMENT_MODEL_SCHEMA && previous.version === DOCUMENT_MODEL_VERSION && Number.isSafeInteger(previous.revision) && previous.revision > 0 ? previous.revision : 0;
|
|
1402
1948
|
return revision >= Number.MAX_SAFE_INTEGER ? 1 : revision + 1;
|
|
1403
1949
|
}
|
|
1950
|
+
function work_document_model_nonNegativeInteger(value) {
|
|
1951
|
+
const number = Number(value);
|
|
1952
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : null;
|
|
1953
|
+
}
|
|
1404
1954
|
function isDocumentRoot(value) {
|
|
1405
1955
|
if (!isRecord(value) || 'doc' !== value.type) return false;
|
|
1406
1956
|
const nodes = [
|
|
@@ -6503,6 +7053,7 @@ function createDocumentImageIdentityPlugin(imageNodeName = 'image') {
|
|
|
6503
7053
|
});
|
|
6504
7054
|
}
|
|
6505
7055
|
function normalizeDocumentImageIdentities(state, imageNodeName, oldState, transactions = []) {
|
|
7056
|
+
if ('image' === imageNodeName && !documentHasIntegrityFeature(state.doc, 4)) return null;
|
|
6506
7057
|
const images = documentImages(state.doc, imageNodeName);
|
|
6507
7058
|
if (!images.length) return null;
|
|
6508
7059
|
const retainedPositions = oldState ? retainedDocumentImagePositions(oldState, state, transactions, imageNodeName) : new Set();
|
|
@@ -6795,14 +7346,14 @@ function createDocumentParagraphIdentityPlugin(types, rotateTextId) {
|
|
|
6795
7346
|
});
|
|
6796
7347
|
}
|
|
6797
7348
|
function normalizeDocumentParagraphIdentities(state, trackedTypes, rotateTextId, oldState, transactions = []) {
|
|
7349
|
+
if (!documentHasIntegrityFeature(state.doc, 16)) return null;
|
|
6798
7350
|
const paragraphs = documentParagraphs(state.doc, trackedTypes);
|
|
6799
|
-
|
|
6800
|
-
if (!identifiedParagraphs.length) return null;
|
|
7351
|
+
if (!paragraphs.length) return null;
|
|
6801
7352
|
const retainedPositions = oldState ? retainedDocumentParagraphPositions(oldState, state, transactions, trackedTypes) : new Set();
|
|
6802
7353
|
const editedPositions = oldState && rotateTextId() && !transactions.some(isHistoryTransaction) ? editedDocumentParagraphPositions(oldState, state, transactions, trackedTypes) : new Set();
|
|
6803
7354
|
const ordered = [
|
|
6804
|
-
...
|
|
6805
|
-
...
|
|
7355
|
+
...paragraphs.filter((item)=>retainedPositions.has(item.position)),
|
|
7356
|
+
...paragraphs.filter((item)=>!retainedPositions.has(item.position))
|
|
6806
7357
|
];
|
|
6807
7358
|
const registry = createDocumentParagraphIdentityRegistry();
|
|
6808
7359
|
const updates = new Map();
|
|
@@ -6882,7 +7433,7 @@ function paragraphsById(paragraphs) {
|
|
|
6882
7433
|
function documentParagraphs(document1, trackedTypes) {
|
|
6883
7434
|
const paragraphs = [];
|
|
6884
7435
|
document1.descendants((node, position)=>{
|
|
6885
|
-
if (trackedTypes.has(node.type.name)) paragraphs.push({
|
|
7436
|
+
if (trackedTypes.has(node.type.name) && hasDocumentParagraphIdentityComponent(node)) paragraphs.push({
|
|
6886
7437
|
node,
|
|
6887
7438
|
position
|
|
6888
7439
|
});
|
|
@@ -7051,14 +7602,14 @@ function createDocumentTableRowIdentityPlugin(rotateTextId) {
|
|
|
7051
7602
|
});
|
|
7052
7603
|
}
|
|
7053
7604
|
function normalizeDocumentTableRowIdentities(state, rotateTextId, oldState, transactions = []) {
|
|
7605
|
+
if (!documentHasIntegrityFeature(state.doc, 32)) return null;
|
|
7054
7606
|
const rows = documentTableRows(state.doc);
|
|
7055
|
-
|
|
7056
|
-
if (!identifiedRows.length) return null;
|
|
7607
|
+
if (!rows.length) return null;
|
|
7057
7608
|
const retainedPositions = oldState ? retainedDocumentTableRowPositions(oldState, state, transactions) : new Set();
|
|
7058
7609
|
const editedPositions = oldState && rotateTextId() && !transactions.some(isHistoryTransaction) ? editedDocumentTableRowPositions(oldState, state, transactions) : new Set();
|
|
7059
7610
|
const ordered = [
|
|
7060
|
-
...
|
|
7061
|
-
...
|
|
7611
|
+
...rows.filter((item)=>retainedPositions.has(item.position)),
|
|
7612
|
+
...rows.filter((item)=>!retainedPositions.has(item.position))
|
|
7062
7613
|
];
|
|
7063
7614
|
const registry = createDocumentParagraphIdentityRegistry();
|
|
7064
7615
|
const updates = new Map();
|
|
@@ -7156,7 +7707,7 @@ function tableRowsById(rows) {
|
|
|
7156
7707
|
function documentTableRows(document1) {
|
|
7157
7708
|
const rows = [];
|
|
7158
7709
|
document1.descendants((node, position)=>{
|
|
7159
|
-
if ('tableRow' === node.type.name) rows.push({
|
|
7710
|
+
if ('tableRow' === node.type.name && hasDocumentTableRowIdentityComponent(node)) rows.push({
|
|
7160
7711
|
node,
|
|
7161
7712
|
position
|
|
7162
7713
|
});
|
|
@@ -9416,68 +9967,1107 @@ const DocumentSuperscript = extension_superscript.extend({
|
|
|
9416
9967
|
};
|
|
9417
9968
|
}
|
|
9418
9969
|
});
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9970
|
+
function documentChunkViewportRange(geometry, viewportTop, viewportBottom) {
|
|
9971
|
+
if (!geometry.length) return {
|
|
9972
|
+
end: 0,
|
|
9973
|
+
start: 0
|
|
9974
|
+
};
|
|
9975
|
+
const first = firstDocumentChunkEndingAfter(geometry, viewportTop);
|
|
9976
|
+
const last = lastDocumentChunkStartingBefore(geometry, viewportBottom);
|
|
9977
|
+
return {
|
|
9978
|
+
end: Math.min(geometry.length, Math.max(first + 1, last + 1)),
|
|
9979
|
+
start: Math.max(0, first)
|
|
9980
|
+
};
|
|
9981
|
+
}
|
|
9982
|
+
function documentChunkGeometry(document1, breaks) {
|
|
9983
|
+
const geometry = [];
|
|
9984
|
+
const orderedBreaks = [
|
|
9985
|
+
...breaks
|
|
9986
|
+
].sort((left, right)=>left.position - right.position);
|
|
9987
|
+
let breakIndex = 0;
|
|
9988
|
+
let top = 0;
|
|
9989
|
+
document1.descendants((node, position, parent)=>{
|
|
9990
|
+
if ('documentChunk' !== node.type.name) return true;
|
|
9991
|
+
if (true === node.attrs.windowContainer) return true;
|
|
9992
|
+
const nodeEnd = position + node.nodeSize;
|
|
9993
|
+
while(breakIndex < orderedBreaks.length && (orderedBreaks[breakIndex]?.position ?? 0) <= position)breakIndex += 1;
|
|
9994
|
+
let chunkBreakIndex = breakIndex;
|
|
9995
|
+
let paginationExtraHeight = 0;
|
|
9996
|
+
while(chunkBreakIndex < orderedBreaks.length && (orderedBreaks[chunkBreakIndex]?.position ?? nodeEnd) < nodeEnd){
|
|
9997
|
+
paginationExtraHeight += Math.max(0, orderedBreaks[chunkBreakIndex]?.spacerHeight ?? 0);
|
|
9998
|
+
chunkBreakIndex += 1;
|
|
9999
|
+
}
|
|
10000
|
+
breakIndex = chunkBreakIndex;
|
|
10001
|
+
const height = positiveNumber(node.attrs.estimatedHeight, 1) + paginationExtraHeight;
|
|
10002
|
+
geometry.push({
|
|
10003
|
+
...parent?.type.name === 'documentChunk' && true === parent.attrs.windowContainer ? {
|
|
10004
|
+
containerId: work_document_chunk_geometry_documentChunkId(parent)
|
|
10005
|
+
} : {},
|
|
10006
|
+
end: top + height,
|
|
10007
|
+
from: position,
|
|
10008
|
+
id: work_document_chunk_geometry_documentChunkId(node),
|
|
10009
|
+
paginationExtraHeight,
|
|
10010
|
+
start: top,
|
|
10011
|
+
to: nodeEnd
|
|
10012
|
+
});
|
|
10013
|
+
top += height;
|
|
10014
|
+
return false;
|
|
10015
|
+
});
|
|
10016
|
+
return geometry;
|
|
10017
|
+
}
|
|
10018
|
+
function documentChunkPositions(document1) {
|
|
10019
|
+
const positions = new Map();
|
|
10020
|
+
const ancestorIdsByNode = new WeakMap();
|
|
10021
|
+
document1.descendants((node, position, parent)=>{
|
|
10022
|
+
const parentAncestorIds = parent ? ancestorIdsByNode.get(parent) ?? [] : [];
|
|
10023
|
+
if ('documentChunk' !== node.type.name) {
|
|
10024
|
+
ancestorIdsByNode.set(node, parentAncestorIds);
|
|
10025
|
+
return true;
|
|
10026
|
+
}
|
|
10027
|
+
const ancestorIds = parent?.type.name === 'documentChunk' ? [
|
|
10028
|
+
...parentAncestorIds,
|
|
10029
|
+
work_document_chunk_geometry_documentChunkId(parent)
|
|
10030
|
+
] : parentAncestorIds;
|
|
10031
|
+
const id = work_document_chunk_geometry_documentChunkId(node);
|
|
10032
|
+
positions.set(id, {
|
|
10033
|
+
ancestorIds,
|
|
10034
|
+
from: position,
|
|
10035
|
+
to: position + node.nodeSize
|
|
10036
|
+
});
|
|
10037
|
+
ancestorIdsByNode.set(node, ancestorIds);
|
|
10038
|
+
return true === node.attrs.windowContainer;
|
|
10039
|
+
});
|
|
10040
|
+
return positions;
|
|
10041
|
+
}
|
|
10042
|
+
function work_document_chunk_geometry_documentChunkId(node) {
|
|
10043
|
+
return 'string' == typeof node.attrs.id && node.attrs.id ? node.attrs.id : `document-chunk-${node.nodeSize}`;
|
|
10044
|
+
}
|
|
10045
|
+
function firstDocumentChunkEndingAfter(geometry, offset) {
|
|
10046
|
+
let lower = 0;
|
|
10047
|
+
let upper = geometry.length - 1;
|
|
10048
|
+
let result = upper;
|
|
10049
|
+
while(lower <= upper){
|
|
10050
|
+
const middle = Math.floor((lower + upper) / 2);
|
|
10051
|
+
const chunk = geometry[middle];
|
|
10052
|
+
if (chunk && chunk.end >= offset) {
|
|
10053
|
+
result = middle;
|
|
10054
|
+
upper = middle - 1;
|
|
10055
|
+
} else lower = middle + 1;
|
|
10056
|
+
}
|
|
10057
|
+
return result;
|
|
10058
|
+
}
|
|
10059
|
+
function lastDocumentChunkStartingBefore(geometry, offset) {
|
|
10060
|
+
let lower = 0;
|
|
10061
|
+
let upper = geometry.length - 1;
|
|
10062
|
+
let result = 0;
|
|
10063
|
+
while(lower <= upper){
|
|
10064
|
+
const middle = Math.floor((lower + upper) / 2);
|
|
10065
|
+
const chunk = geometry[middle];
|
|
10066
|
+
if (chunk && chunk.start <= offset) {
|
|
10067
|
+
result = middle;
|
|
10068
|
+
lower = middle + 1;
|
|
10069
|
+
} else upper = middle - 1;
|
|
10070
|
+
}
|
|
10071
|
+
return result;
|
|
10072
|
+
}
|
|
10073
|
+
function positiveNumber(value, fallback) {
|
|
10074
|
+
const number = Number(value);
|
|
10075
|
+
return Number.isFinite(number) && number > 0 ? number : fallback;
|
|
10076
|
+
}
|
|
10077
|
+
const DOCUMENT_LAZY_PREVIEW_POOL_LIMIT = 12;
|
|
10078
|
+
function createDocumentLazyPreviewPool() {
|
|
10079
|
+
return {
|
|
10080
|
+
available: new Map(),
|
|
10081
|
+
size: 0
|
|
10082
|
+
};
|
|
10083
|
+
}
|
|
10084
|
+
function mountDocumentLazyPreview(pool, dom, content, firstPosition, paginationExtraHeight) {
|
|
10085
|
+
const shape = lazyDocumentPreviewShape(content);
|
|
10086
|
+
const available = pool.available.get(shape);
|
|
10087
|
+
let lease = available?.pop();
|
|
10088
|
+
if (lease) {
|
|
10089
|
+
pool.size -= 1;
|
|
10090
|
+
if (!available?.length) pool.available.delete(shape);
|
|
10091
|
+
} else lease = createDocumentLazyPreview(content, shape);
|
|
10092
|
+
let position = firstPosition;
|
|
10093
|
+
for(let index = 0; index < content.length; index += 1){
|
|
10094
|
+
const node = content[index];
|
|
10095
|
+
const element = lease.nodes[index];
|
|
10096
|
+
if (!node || !element) throw new Error('The lazy document preview shape is incomplete.');
|
|
10097
|
+
const size = bindLazyDocumentNode(node, element, position);
|
|
10098
|
+
if (null === size) throw new Error('The lazy document preview shape changed during reuse.');
|
|
10099
|
+
position += size;
|
|
10100
|
+
}
|
|
10101
|
+
dom.replaceChildren(...lease.nodes, lease.spacer);
|
|
10102
|
+
dom.dataset.documentLazyPreview = 'true';
|
|
10103
|
+
dom.setAttribute('contenteditable', 'false');
|
|
10104
|
+
updateDocumentLazyPreviewSpacer(lease, paginationExtraHeight);
|
|
10105
|
+
return lease;
|
|
10106
|
+
}
|
|
10107
|
+
function releaseDocumentLazyPreview(pool, dom, lease) {
|
|
10108
|
+
if (!lease) return;
|
|
10109
|
+
dom.replaceChildren();
|
|
10110
|
+
if (pool.size >= DOCUMENT_LAZY_PREVIEW_POOL_LIMIT) return;
|
|
10111
|
+
const available = pool.available.get(lease.shape) ?? [];
|
|
10112
|
+
available.push(lease);
|
|
10113
|
+
pool.available.set(lease.shape, available);
|
|
10114
|
+
pool.size += 1;
|
|
10115
|
+
}
|
|
10116
|
+
function updateDocumentLazyPreviewSpacer(lease, height) {
|
|
10117
|
+
if (!lease) return;
|
|
10118
|
+
lease.spacer.style.height = `${Math.max(0, height)}px`;
|
|
10119
|
+
}
|
|
10120
|
+
function documentLazyPreviewPointerPosition(event, fallback) {
|
|
10121
|
+
if (!(event instanceof MouseEvent) || 0 !== event.button) return null;
|
|
10122
|
+
const target = event.target;
|
|
10123
|
+
if (!(target instanceof Element)) return null;
|
|
10124
|
+
const preview = target.closest('[data-document-lazy-preview="true"]');
|
|
10125
|
+
if (!preview) return null;
|
|
10126
|
+
const textElement = target.closest('[data-document-text-position]');
|
|
10127
|
+
const base = Number(textElement?.dataset.documentTextPosition);
|
|
10128
|
+
let offset = 0;
|
|
10129
|
+
const caret = target.ownerDocument.caretPositionFromPoint?.(event.clientX, event.clientY);
|
|
10130
|
+
if (caret && caret.offsetNode.nodeType === globalThis.Node.TEXT_NODE && textElement?.contains(caret.offsetNode)) offset = Math.max(0, Math.min(caret.offset, caret.offsetNode.textContent?.length ?? 0));
|
|
10131
|
+
return Number.isSafeInteger(base) ? base + offset : fallback;
|
|
10132
|
+
}
|
|
10133
|
+
function createDocumentLazyPreview(content, shape) {
|
|
10134
|
+
const nodes = [];
|
|
10135
|
+
let position = 0;
|
|
10136
|
+
for (const node of content){
|
|
10137
|
+
const rendered = createLazyDocumentNode(node, position);
|
|
10138
|
+
nodes.push(rendered.element);
|
|
10139
|
+
position += rendered.size;
|
|
10140
|
+
}
|
|
10141
|
+
const spacer = document.createElement('div');
|
|
10142
|
+
spacer.dataset.documentLazyPaginationSpacer = 'true';
|
|
10143
|
+
spacer.setAttribute('aria-hidden', 'true');
|
|
10144
|
+
return {
|
|
10145
|
+
nodes,
|
|
10146
|
+
shape,
|
|
10147
|
+
spacer
|
|
10148
|
+
};
|
|
10149
|
+
}
|
|
10150
|
+
function createLazyDocumentNode(node, position) {
|
|
10151
|
+
if ('text' === node.type && node.text) {
|
|
10152
|
+
const span = document.createElement('span');
|
|
10153
|
+
span.dataset.documentTextPosition = String(position);
|
|
10154
|
+
span.append(document.createTextNode(node.text));
|
|
9426
10155
|
return {
|
|
9427
|
-
|
|
9428
|
-
|
|
10156
|
+
element: span,
|
|
10157
|
+
size: node.text.length
|
|
9429
10158
|
};
|
|
9430
|
-
}
|
|
9431
|
-
|
|
10159
|
+
}
|
|
10160
|
+
if ('paragraph' === node.type) {
|
|
10161
|
+
const paragraph = document.createElement('p');
|
|
10162
|
+
const size = appendLazyDocumentChildren(node, paragraph, position + 1);
|
|
10163
|
+
if (!paragraph.hasChildNodes()) paragraph.append(document.createElement('br'));
|
|
9432
10164
|
return {
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
refreshDocumentCitations: (content)=>(props)=>refreshDocumentCitationsCommand(props, content),
|
|
9436
|
-
renameDocumentCitationTag: (previousTag, nextTag)=>(props)=>renameDocumentCitationTagCommand(props, previousTag, nextTag),
|
|
9437
|
-
setDocumentBibliography: (bibliography, renamedTag)=>({ dispatch, editor })=>{
|
|
9438
|
-
const content = this.options.getContent();
|
|
9439
|
-
if (!content) return false;
|
|
9440
|
-
if (!dispatch) return true;
|
|
9441
|
-
const next = {
|
|
9442
|
-
...content,
|
|
9443
|
-
bibliography
|
|
9444
|
-
};
|
|
9445
|
-
this.options.onContentChange(next);
|
|
9446
|
-
const chain = editor.chain();
|
|
9447
|
-
if (renamedTag) chain.renameDocumentCitationTag(renamedTag.previous, renamedTag.next);
|
|
9448
|
-
chain.refreshDocumentCitations(next).run();
|
|
9449
|
-
return true;
|
|
9450
|
-
}
|
|
10165
|
+
element: paragraph,
|
|
10166
|
+
size: size + 2
|
|
9451
10167
|
};
|
|
9452
|
-
}
|
|
9453
|
-
|
|
10168
|
+
}
|
|
10169
|
+
if ('table' === node.type) {
|
|
10170
|
+
const wrapper = document.createElement('div');
|
|
10171
|
+
wrapper.className = 'tableWrapper';
|
|
10172
|
+
const table = document.createElement('table');
|
|
10173
|
+
bindLazyDocumentTableAttributes(table, node);
|
|
10174
|
+
const body = document.createElement('tbody');
|
|
10175
|
+
table.append(body);
|
|
10176
|
+
wrapper.append(table);
|
|
10177
|
+
const size = appendLazyDocumentChildren(node, body, position + 1);
|
|
9454
10178
|
return {
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
instruction: work_document_citation_nodes_hiddenAttribute(''),
|
|
9458
|
-
display: work_document_citation_nodes_hiddenAttribute(''),
|
|
9459
|
-
orphaned: work_document_citation_nodes_hiddenAttribute(false)
|
|
10179
|
+
element: wrapper,
|
|
10180
|
+
size: size + 2
|
|
9460
10181
|
};
|
|
9461
|
-
}
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
10182
|
+
}
|
|
10183
|
+
if ('tableRow' === node.type) {
|
|
10184
|
+
const row = document.createElement('tr');
|
|
10185
|
+
const size = appendLazyDocumentChildren(node, row, position + 1);
|
|
10186
|
+
return {
|
|
10187
|
+
element: row,
|
|
10188
|
+
size: size + 2
|
|
10189
|
+
};
|
|
10190
|
+
}
|
|
10191
|
+
if ('tableCell' === node.type || 'tableHeader' === node.type) {
|
|
10192
|
+
const cell = document.createElement('tableHeader' === node.type ? 'th' : 'td');
|
|
10193
|
+
const size = appendLazyDocumentChildren(node, cell, position + 1);
|
|
10194
|
+
return {
|
|
10195
|
+
element: cell,
|
|
10196
|
+
size: size + 2
|
|
10197
|
+
};
|
|
10198
|
+
}
|
|
10199
|
+
throw new Error(`Unsupported lazy document preview node: ${node.type}`);
|
|
10200
|
+
}
|
|
10201
|
+
function appendLazyDocumentChildren(node, parent, firstPosition) {
|
|
10202
|
+
let size = 0;
|
|
10203
|
+
for (const child of node.content ?? []){
|
|
10204
|
+
const rendered = createLazyDocumentNode(child, firstPosition + size);
|
|
10205
|
+
parent.append(rendered.element);
|
|
10206
|
+
size += rendered.size;
|
|
10207
|
+
}
|
|
10208
|
+
return size;
|
|
10209
|
+
}
|
|
10210
|
+
function bindLazyDocumentNode(node, element, position) {
|
|
10211
|
+
if ('text' === node.type && node.text && 'SPAN' === element.tagName) {
|
|
10212
|
+
const text = element.firstChild;
|
|
10213
|
+
if (!(text instanceof Text) || 1 !== element.childNodes.length) return null;
|
|
10214
|
+
element.dataset.documentTextPosition = String(position);
|
|
10215
|
+
if (text.data !== node.text) text.data = node.text;
|
|
10216
|
+
return node.text.length;
|
|
10217
|
+
}
|
|
10218
|
+
if ('paragraph' === node.type && 'P' === element.tagName) {
|
|
10219
|
+
if (!(node.content?.length ?? 0)) return 1 === element.children.length && element.firstElementChild?.tagName === 'BR' ? 2 : null;
|
|
10220
|
+
const size = bindLazyDocumentChildren(node, element, position + 1);
|
|
10221
|
+
return null === size ? null : size + 2;
|
|
10222
|
+
}
|
|
10223
|
+
if ('table' === node.type && element.classList.contains('tableWrapper')) {
|
|
10224
|
+
const table = element.firstElementChild;
|
|
10225
|
+
const body = table?.firstElementChild;
|
|
10226
|
+
if (!(table instanceof HTMLTableElement) || body?.tagName !== 'TBODY') return null;
|
|
10227
|
+
bindLazyDocumentTableAttributes(table, node);
|
|
10228
|
+
const size = bindLazyDocumentChildren(node, body, position + 1);
|
|
10229
|
+
return null === size ? null : size + 2;
|
|
10230
|
+
}
|
|
10231
|
+
if ('tableRow' === node.type && 'TR' === element.tagName) {
|
|
10232
|
+
const size = bindLazyDocumentChildren(node, element, position + 1);
|
|
10233
|
+
return null === size ? null : size + 2;
|
|
10234
|
+
}
|
|
10235
|
+
const expectedCellTag = 'tableHeader' === node.type ? 'TH' : 'TD';
|
|
10236
|
+
if (('tableCell' === node.type || 'tableHeader' === node.type) && element.tagName === expectedCellTag) {
|
|
10237
|
+
const size = bindLazyDocumentChildren(node, element, position + 1);
|
|
10238
|
+
return null === size ? null : size + 2;
|
|
10239
|
+
}
|
|
10240
|
+
return null;
|
|
10241
|
+
}
|
|
10242
|
+
function bindLazyDocumentChildren(node, parent, firstPosition) {
|
|
10243
|
+
const children = node.content ?? [];
|
|
10244
|
+
if (parent.children.length !== children.length) return null;
|
|
10245
|
+
let size = 0;
|
|
10246
|
+
for(let index = 0; index < children.length; index += 1){
|
|
10247
|
+
const child = children[index];
|
|
10248
|
+
const element = parent.children.item(index);
|
|
10249
|
+
if (!child || !(element instanceof HTMLElement)) return null;
|
|
10250
|
+
const childSize = bindLazyDocumentNode(child, element, firstPosition + size);
|
|
10251
|
+
if (null === childSize) return null;
|
|
10252
|
+
size += childSize;
|
|
10253
|
+
}
|
|
10254
|
+
return size;
|
|
10255
|
+
}
|
|
10256
|
+
function bindLazyDocumentTableAttributes(table, node) {
|
|
10257
|
+
setBooleanDataset(table, 'officeTableImported', node.attrs?.officeImported === true);
|
|
10258
|
+
setStringDataset(table, 'documentVirtualTableId', node.attrs?.virtualTableId);
|
|
10259
|
+
setNumberDataset(table, 'documentVirtualTableIndex', node.attrs?.virtualTableIndex);
|
|
10260
|
+
setNumberDataset(table, 'documentVirtualTableCount', node.attrs?.virtualTableCount);
|
|
10261
|
+
}
|
|
10262
|
+
function lazyDocumentPreviewShape(content) {
|
|
10263
|
+
const parts = [];
|
|
10264
|
+
for (const node of content)appendLazyDocumentPreviewShape(node, parts);
|
|
10265
|
+
return parts.join('');
|
|
10266
|
+
}
|
|
10267
|
+
function appendLazyDocumentPreviewShape(node, parts) {
|
|
10268
|
+
const children = node.content ?? [];
|
|
10269
|
+
parts.push(node.type, '[', String(children.length), '](');
|
|
10270
|
+
for (const child of children)appendLazyDocumentPreviewShape(child, parts);
|
|
10271
|
+
parts.push(')');
|
|
10272
|
+
}
|
|
10273
|
+
function setBooleanDataset(element, name, value) {
|
|
10274
|
+
if (value) element.dataset[name] = 'true';
|
|
10275
|
+
else delete element.dataset[name];
|
|
10276
|
+
}
|
|
10277
|
+
function setStringDataset(element, name, value) {
|
|
10278
|
+
if ('string' == typeof value && value) element.dataset[name] = value;
|
|
10279
|
+
else delete element.dataset[name];
|
|
10280
|
+
}
|
|
10281
|
+
function setNumberDataset(element, name, value) {
|
|
10282
|
+
const number = Number(value);
|
|
10283
|
+
if (Number.isSafeInteger(number) && number >= 0) element.dataset[name] = String(number);
|
|
10284
|
+
else delete element.dataset[name];
|
|
10285
|
+
}
|
|
10286
|
+
const statisticsByDocument = new WeakMap();
|
|
10287
|
+
function documentWordCount(value) {
|
|
10288
|
+
let asciiCount = 0;
|
|
10289
|
+
let inAsciiWord = false;
|
|
10290
|
+
for(let index = 0; index < value.length; index += 1){
|
|
10291
|
+
const code = value.charCodeAt(index);
|
|
10292
|
+
const asciiLetterOrNumber = code >= 0x30 && code <= 0x39 || code >= 0x41 && code <= 0x5a || code >= 0x61 && code <= 0x7a;
|
|
10293
|
+
if (asciiLetterOrNumber) {
|
|
10294
|
+
if (!inAsciiWord) asciiCount += 1;
|
|
10295
|
+
inAsciiWord = true;
|
|
10296
|
+
continue;
|
|
10297
|
+
}
|
|
10298
|
+
inAsciiWord = false;
|
|
10299
|
+
if (!(code <= 0x7f) && 0xfffc !== code) return unicodeDocumentWordCount(value);
|
|
10300
|
+
}
|
|
10301
|
+
return asciiCount;
|
|
10302
|
+
}
|
|
10303
|
+
function unicodeDocumentWordCount(value) {
|
|
10304
|
+
let count = 0;
|
|
10305
|
+
for (const _match of value.matchAll(/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]|[\p{L}\p{N}]+/gu))count += 1;
|
|
10306
|
+
return count;
|
|
10307
|
+
}
|
|
10308
|
+
function documentTextStatistics(editor) {
|
|
10309
|
+
const document1 = editor.state.doc;
|
|
10310
|
+
const cached = statisticsByDocument.get(document1);
|
|
10311
|
+
if (cached) return cached;
|
|
10312
|
+
const source = editor.getText({
|
|
10313
|
+
blockSeparator: '\n'
|
|
10314
|
+
});
|
|
10315
|
+
let characterCountWithSpaces = 0;
|
|
10316
|
+
let characterCountWithoutSpaces = 0;
|
|
10317
|
+
for(let index = 0; index < source.length; index += 1){
|
|
10318
|
+
const codePoint = source.codePointAt(index);
|
|
10319
|
+
if (void 0 !== codePoint) {
|
|
10320
|
+
if (codePoint > 0xffff) index += 1;
|
|
10321
|
+
if (codePoint !== "".codePointAt(0) && 0x0a !== codePoint && 0x0d !== codePoint) {
|
|
10322
|
+
characterCountWithSpaces += 1;
|
|
10323
|
+
if (!isEcmaScriptWhitespace(codePoint)) characterCountWithoutSpaces += 1;
|
|
9479
10324
|
}
|
|
9480
|
-
|
|
10325
|
+
}
|
|
10326
|
+
}
|
|
10327
|
+
let paragraphCount = 0;
|
|
10328
|
+
document1.descendants((node)=>{
|
|
10329
|
+
if (node.type.name === DOCUMENT_LAZY_BLOCK_NODE) {
|
|
10330
|
+
const logicalParagraphs = Number(node.attrs.paragraphCount);
|
|
10331
|
+
paragraphCount += Number.isSafeInteger(logicalParagraphs) && logicalParagraphs >= 0 ? logicalParagraphs : 0;
|
|
10332
|
+
return false;
|
|
10333
|
+
}
|
|
10334
|
+
if (node.isTextblock) paragraphCount += 1;
|
|
10335
|
+
return true;
|
|
10336
|
+
});
|
|
10337
|
+
const statistics = {
|
|
10338
|
+
characterCountWithSpaces,
|
|
10339
|
+
characterCountWithoutSpaces,
|
|
10340
|
+
paragraphCount,
|
|
10341
|
+
wordCount: documentWordCount(source)
|
|
10342
|
+
};
|
|
10343
|
+
statisticsByDocument.set(document1, statistics);
|
|
10344
|
+
return statistics;
|
|
10345
|
+
}
|
|
10346
|
+
function transferDocumentTextStatistics(previous, next) {
|
|
10347
|
+
const statistics = statisticsByDocument.get(previous);
|
|
10348
|
+
if (statistics) statisticsByDocument.set(next, statistics);
|
|
10349
|
+
}
|
|
10350
|
+
function transferChangedDocumentTextStatistics(previous, next, changes) {
|
|
10351
|
+
const cached = statisticsByDocument.get(previous);
|
|
10352
|
+
if (!cached || !changes.length) return false;
|
|
10353
|
+
const statistics = {
|
|
10354
|
+
...cached
|
|
10355
|
+
};
|
|
10356
|
+
for (const { after, before } of changes){
|
|
10357
|
+
applyDocumentTextStatisticsDelta(statistics, simpleDocumentSubtreeStatistics(before), -1);
|
|
10358
|
+
applyDocumentTextStatisticsDelta(statistics, simpleDocumentSubtreeStatistics(after), 1);
|
|
10359
|
+
}
|
|
10360
|
+
if (Object.values(statistics).some((value)=>value < 0)) return false;
|
|
10361
|
+
statisticsByDocument.set(next, statistics);
|
|
10362
|
+
return true;
|
|
10363
|
+
}
|
|
10364
|
+
function simpleDocumentSubtreeStatistics(root) {
|
|
10365
|
+
const statistics = {
|
|
10366
|
+
characterCountWithSpaces: 0,
|
|
10367
|
+
characterCountWithoutSpaces: 0,
|
|
10368
|
+
paragraphCount: 0,
|
|
10369
|
+
wordCount: 0
|
|
10370
|
+
};
|
|
10371
|
+
const visit = (node)=>{
|
|
10372
|
+
if (node.type.name === DOCUMENT_LAZY_BLOCK_NODE) {
|
|
10373
|
+
accumulateDocumentTextStatistics(statistics, node.textContent);
|
|
10374
|
+
const logicalParagraphs = Number(node.attrs.paragraphCount);
|
|
10375
|
+
statistics.paragraphCount += Number.isSafeInteger(logicalParagraphs) && logicalParagraphs >= 0 ? logicalParagraphs : 0;
|
|
10376
|
+
return;
|
|
10377
|
+
}
|
|
10378
|
+
if (node.isTextblock) {
|
|
10379
|
+
accumulateDocumentTextStatistics(statistics, node.textContent);
|
|
10380
|
+
statistics.paragraphCount += 1;
|
|
10381
|
+
return;
|
|
10382
|
+
}
|
|
10383
|
+
node.forEach(visit);
|
|
10384
|
+
};
|
|
10385
|
+
visit(root);
|
|
10386
|
+
return statistics;
|
|
10387
|
+
}
|
|
10388
|
+
function accumulateDocumentTextStatistics(statistics, source) {
|
|
10389
|
+
for(let index = 0; index < source.length; index += 1){
|
|
10390
|
+
const codePoint = source.codePointAt(index);
|
|
10391
|
+
if (void 0 !== codePoint) {
|
|
10392
|
+
if (codePoint > 0xffff) index += 1;
|
|
10393
|
+
if (codePoint !== "".codePointAt(0) && 0x0a !== codePoint && 0x0d !== codePoint) {
|
|
10394
|
+
statistics.characterCountWithSpaces += 1;
|
|
10395
|
+
if (!isEcmaScriptWhitespace(codePoint)) statistics.characterCountWithoutSpaces += 1;
|
|
10396
|
+
}
|
|
10397
|
+
}
|
|
10398
|
+
}
|
|
10399
|
+
statistics.wordCount += documentWordCount(source);
|
|
10400
|
+
}
|
|
10401
|
+
function applyDocumentTextStatisticsDelta(target, value, direction) {
|
|
10402
|
+
target.characterCountWithSpaces += direction * value.characterCountWithSpaces;
|
|
10403
|
+
target.characterCountWithoutSpaces += direction * value.characterCountWithoutSpaces;
|
|
10404
|
+
target.paragraphCount += direction * value.paragraphCount;
|
|
10405
|
+
target.wordCount += direction * value.wordCount;
|
|
10406
|
+
}
|
|
10407
|
+
function isEcmaScriptWhitespace(codePoint) {
|
|
10408
|
+
return codePoint >= 0x09 && codePoint <= 0x0d || 0x20 === codePoint || 0xa0 === codePoint || 0x1680 === codePoint || codePoint >= 0x2000 && codePoint <= 0x200a || 0x2028 === codePoint || 0x2029 === codePoint || 0x202f === codePoint || 0x205f === codePoint || 0x3000 === codePoint || 0xfeff === codePoint;
|
|
10409
|
+
}
|
|
10410
|
+
const INITIAL_DOCUMENT_CHUNK_WINDOW_SIZE = 2;
|
|
10411
|
+
const DOCUMENT_CHUNK_WINDOW_ROOT_MARGIN = '0px';
|
|
10412
|
+
const DOCUMENT_CHUNK_PAGINATION_META = 'documentChunkPaginationGeometry';
|
|
10413
|
+
const DOCUMENT_CHUNK_VISIBLE_IDS_META = 'documentChunkVisibleIds';
|
|
10414
|
+
const DOCUMENT_CHUNK_HYDRATION_META = 'documentChunkLazyHydration';
|
|
10415
|
+
const documentChunkWindowKey = new PluginKey('documentChunkWindow');
|
|
10416
|
+
const documentChunkElementRegistries = new WeakMap();
|
|
10417
|
+
const documentChunkContentSources = new WeakMap();
|
|
10418
|
+
const DocumentChunk = core_Node.create({
|
|
10419
|
+
name: 'documentChunk',
|
|
10420
|
+
group: 'block',
|
|
10421
|
+
content: 'block+',
|
|
10422
|
+
defining: true,
|
|
10423
|
+
selectable: false,
|
|
10424
|
+
addOptions () {
|
|
10425
|
+
return {
|
|
10426
|
+
getContent: ()=>null,
|
|
10427
|
+
trustInitialIntegrityFeatures: false
|
|
10428
|
+
};
|
|
10429
|
+
},
|
|
10430
|
+
addAttributes () {
|
|
10431
|
+
return {
|
|
10432
|
+
id: {
|
|
10433
|
+
default: ''
|
|
10434
|
+
},
|
|
10435
|
+
blockCount: {
|
|
10436
|
+
default: 0
|
|
10437
|
+
},
|
|
10438
|
+
estimatedHeight: {
|
|
10439
|
+
default: 1
|
|
10440
|
+
},
|
|
10441
|
+
integrityFeatures: {
|
|
10442
|
+
default: null,
|
|
10443
|
+
rendered: false
|
|
10444
|
+
},
|
|
10445
|
+
windowContainer: {
|
|
10446
|
+
default: false
|
|
10447
|
+
}
|
|
10448
|
+
};
|
|
10449
|
+
},
|
|
10450
|
+
parseHTML () {
|
|
10451
|
+
return [
|
|
10452
|
+
{
|
|
10453
|
+
tag: 'div[data-document-chunk]',
|
|
10454
|
+
getAttrs: (element)=>{
|
|
10455
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
10456
|
+
return {
|
|
10457
|
+
id: element.dataset.documentChunkId ?? '',
|
|
10458
|
+
blockCount: work_document_chunk_node_positiveInteger(element.dataset.documentChunkBlocks, 0),
|
|
10459
|
+
estimatedHeight: work_document_chunk_node_positiveNumber(element.dataset.documentChunkEstimatedHeight, 1),
|
|
10460
|
+
windowContainer: 'true' === element.dataset.documentChunkWindowContainer
|
|
10461
|
+
};
|
|
10462
|
+
}
|
|
10463
|
+
}
|
|
10464
|
+
];
|
|
10465
|
+
},
|
|
10466
|
+
renderHTML ({ node, HTMLAttributes }) {
|
|
10467
|
+
return [
|
|
10468
|
+
'div',
|
|
10469
|
+
mergeAttributes(HTMLAttributes, documentChunkDomAttributes(node)),
|
|
10470
|
+
0
|
|
10471
|
+
];
|
|
10472
|
+
},
|
|
10473
|
+
addNodeView () {
|
|
10474
|
+
return ({ decorations, editor, getPos, node })=>createDocumentChunkNodeView(editor, node, decorations, getPos);
|
|
10475
|
+
},
|
|
10476
|
+
addProseMirrorPlugins () {
|
|
10477
|
+
const editor = this.editor;
|
|
10478
|
+
const getContent = this.options.getContent;
|
|
10479
|
+
documentChunkContentSources.set(editor, getContent);
|
|
10480
|
+
const trustInitialIntegrityFeatures = this.options.trustInitialIntegrityFeatures;
|
|
10481
|
+
return [
|
|
10482
|
+
new Plugin({
|
|
10483
|
+
key: documentChunkWindowKey,
|
|
10484
|
+
appendTransaction: (transactions, _previous, next)=>{
|
|
10485
|
+
if (!transactions.some((transaction)=>transaction.selectionSet)) return null;
|
|
10486
|
+
const selected = selectedDocumentChunkId(next);
|
|
10487
|
+
if (!selected) return null;
|
|
10488
|
+
const transaction = next.tr;
|
|
10489
|
+
return hydrateDocumentChunks(editor, transaction, next, [
|
|
10490
|
+
selected
|
|
10491
|
+
]).length ? transaction : null;
|
|
10492
|
+
},
|
|
10493
|
+
state: {
|
|
10494
|
+
init: (_configuration, state)=>{
|
|
10495
|
+
if (trustInitialIntegrityFeatures) primeDocumentIntegrityFeatures(state.doc);
|
|
10496
|
+
return createDocumentChunkWindowState(state);
|
|
10497
|
+
},
|
|
10498
|
+
apply: (transaction, current, _previous, next)=>updateDocumentChunkWindowState(transaction, current, next)
|
|
10499
|
+
},
|
|
10500
|
+
props: {
|
|
10501
|
+
decorations: (state)=>documentChunkWindowKey.getState(state)?.decorations ?? null,
|
|
10502
|
+
handleDOMEvents: {
|
|
10503
|
+
pointerdown: (view, event)=>handleDocumentChunkLazyPreviewPointerDown(editor, view, event)
|
|
10504
|
+
},
|
|
10505
|
+
handleKeyDown: (view, event)=>handleDocumentChunkBoundaryKeyDown(editor, view, event)
|
|
10506
|
+
},
|
|
10507
|
+
view: (view)=>createDocumentChunkIntersectionView(editor, view)
|
|
10508
|
+
})
|
|
10509
|
+
];
|
|
10510
|
+
}
|
|
10511
|
+
});
|
|
10512
|
+
function handleDocumentChunkBoundaryKeyDown(editor, view, event) {
|
|
10513
|
+
const state = documentChunkWindowKey.getState(view.state);
|
|
10514
|
+
if (!state?.chunkCount || event.altKey || event.shiftKey || !event.ctrlKey && !event.metaKey) return false;
|
|
10515
|
+
const moveToStart = 'Home' === event.key || event.metaKey && 'ArrowUp' === event.key;
|
|
10516
|
+
const moveToEnd = 'End' === event.key || event.metaKey && 'ArrowDown' === event.key;
|
|
10517
|
+
if (!moveToStart && !moveToEnd) return false;
|
|
10518
|
+
const selection = moveToStart ? Selection.atStart(view.state.doc) : Selection.atEnd(view.state.doc);
|
|
10519
|
+
const transaction = view.state.tr.setSelection(selection);
|
|
10520
|
+
const selected = selectedDocumentChunkId({
|
|
10521
|
+
doc: view.state.doc,
|
|
10522
|
+
selection
|
|
10523
|
+
});
|
|
10524
|
+
if (selected) hydrateDocumentChunks(editor, transaction, view.state, [
|
|
10525
|
+
selected
|
|
10526
|
+
]);
|
|
10527
|
+
const nextSelection = moveToStart ? Selection.atStart(transaction.doc) : Selection.atEnd(transaction.doc);
|
|
10528
|
+
view.dispatch(transaction.setSelection(nextSelection).scrollIntoView());
|
|
10529
|
+
return true;
|
|
10530
|
+
}
|
|
10531
|
+
function createDocumentChunkNodeView(editor, initialNode, initialDecorations, getPos) {
|
|
10532
|
+
let node = initialNode;
|
|
10533
|
+
const id = work_document_chunk_geometry_documentChunkId(node);
|
|
10534
|
+
const mounted = documentChunkIsMounted(node, initialDecorations);
|
|
10535
|
+
let lazyPreview = mounted && documentChunkHasLazyContent(node);
|
|
10536
|
+
const registry = documentChunkElementRegistryFor(editor);
|
|
10537
|
+
let paginationExtraHeight = registry.paginationExtraHeights.get(id) ?? 0;
|
|
10538
|
+
const dom = document.createElement('div');
|
|
10539
|
+
let lazyPreviewLease = null;
|
|
10540
|
+
applyDocumentChunkNodeViewAttributes(dom, node, mounted, paginationExtraHeight);
|
|
10541
|
+
if (lazyPreview) lazyPreviewLease = mountDocumentLazyPreview(registry.lazyPreviewPool, dom, requiredDocumentLazyChunkContent(editor, id), (getPos() ?? 0) + 1, paginationExtraHeight);
|
|
10542
|
+
const controller = {
|
|
10543
|
+
element: dom,
|
|
10544
|
+
setPaginationExtraHeight (height) {
|
|
10545
|
+
if (height === paginationExtraHeight) return;
|
|
10546
|
+
paginationExtraHeight = height;
|
|
10547
|
+
applyDocumentChunkNodeViewAttributes(dom, node, mounted, paginationExtraHeight);
|
|
10548
|
+
if (lazyPreview) {
|
|
10549
|
+
dom.setAttribute('contenteditable', 'false');
|
|
10550
|
+
updateDocumentLazyPreviewSpacer(lazyPreviewLease, height);
|
|
10551
|
+
}
|
|
10552
|
+
}
|
|
10553
|
+
};
|
|
10554
|
+
const unregister = registerDocumentChunkController(editor, id, controller);
|
|
10555
|
+
return {
|
|
10556
|
+
dom,
|
|
10557
|
+
...mounted && !lazyPreview ? {
|
|
10558
|
+
contentDOM: dom
|
|
10559
|
+
} : {},
|
|
10560
|
+
destroy () {
|
|
10561
|
+
releaseDocumentLazyPreview(registry.lazyPreviewPool, dom, lazyPreviewLease);
|
|
10562
|
+
lazyPreviewLease = null;
|
|
10563
|
+
unregister();
|
|
10564
|
+
},
|
|
10565
|
+
ignoreMutation () {
|
|
10566
|
+
return !mounted || lazyPreview;
|
|
10567
|
+
},
|
|
10568
|
+
update (nextNode, decorations) {
|
|
10569
|
+
if (nextNode.type !== node.type) return false;
|
|
10570
|
+
if (work_document_chunk_geometry_documentChunkId(nextNode) !== id) return false;
|
|
10571
|
+
if (mounted && documentChunkHasLazyContent(nextNode) !== lazyPreview) return false;
|
|
10572
|
+
if (documentChunkIsMounted(nextNode, decorations) !== mounted) return false;
|
|
10573
|
+
node = nextNode;
|
|
10574
|
+
lazyPreview = mounted && documentChunkHasLazyContent(node);
|
|
10575
|
+
applyDocumentChunkNodeViewAttributes(dom, node, mounted, paginationExtraHeight);
|
|
10576
|
+
if (lazyPreview) dom.setAttribute('contenteditable', 'false');
|
|
10577
|
+
return true;
|
|
10578
|
+
}
|
|
10579
|
+
};
|
|
10580
|
+
}
|
|
10581
|
+
function documentChunkHasLazyContent(node) {
|
|
10582
|
+
return 1 === node.childCount && node.firstChild?.type.name === DOCUMENT_LAZY_BLOCK_NODE;
|
|
10583
|
+
}
|
|
10584
|
+
function requiredDocumentLazyChunkContent(editor, chunkId) {
|
|
10585
|
+
const payload = documentLazyChunkContentForEditor(editor, chunkId);
|
|
10586
|
+
if (!payload?.length) throw new Error(`The lazy document chunk payload "${chunkId}" is missing.`);
|
|
10587
|
+
return payload;
|
|
10588
|
+
}
|
|
10589
|
+
function handleDocumentChunkLazyPreviewPointerDown(editor, view, event) {
|
|
10590
|
+
const target = event.target;
|
|
10591
|
+
const chunk = target instanceof Element ? target.closest('[data-document-chunk][data-document-lazy-preview="true"]') : null;
|
|
10592
|
+
const id = chunk?.dataset.documentChunkId;
|
|
10593
|
+
if (!id) return false;
|
|
10594
|
+
const position = documentChunkWindowKey.getState(view.state)?.positions.get(id);
|
|
10595
|
+
const logicalPosition = documentLazyPreviewPointerPosition(event, (position?.from ?? 0) + 2);
|
|
10596
|
+
if (null === logicalPosition) return false;
|
|
10597
|
+
event.preventDefault();
|
|
10598
|
+
editor.commands.setTextSelection(logicalPosition);
|
|
10599
|
+
view.focus();
|
|
10600
|
+
return true;
|
|
10601
|
+
}
|
|
10602
|
+
function createDocumentChunkWindowState(state) {
|
|
10603
|
+
const geometry = documentChunkGeometry(state.doc, []);
|
|
10604
|
+
const ids = geometry.map(({ id })=>id);
|
|
10605
|
+
const positions = documentChunkPositions(state.doc);
|
|
10606
|
+
const fallback = "u" < typeof IntersectionObserver;
|
|
10607
|
+
const mountedIds = fallback ? new Set(ids) : new Set(ids.slice(0, INITIAL_DOCUMENT_CHUNK_WINDOW_SIZE));
|
|
10608
|
+
const selected = selectedDocumentChunkId(state);
|
|
10609
|
+
if (selected) mountedIds.add(selected);
|
|
10610
|
+
return {
|
|
10611
|
+
chunkCount: ids.length,
|
|
10612
|
+
decorations: documentChunkDecorations(state.doc, mountedIds, positions),
|
|
10613
|
+
fallback,
|
|
10614
|
+
geometry,
|
|
10615
|
+
ids,
|
|
10616
|
+
mountedIds,
|
|
10617
|
+
positions
|
|
10618
|
+
};
|
|
10619
|
+
}
|
|
10620
|
+
function updateDocumentChunkWindowState(transaction, current, next) {
|
|
10621
|
+
const meta = transaction.getMeta(documentChunkWindowKey);
|
|
10622
|
+
const paginationMeta = transaction.getMeta(DOCUMENT_CHUNK_PAGINATION_META);
|
|
10623
|
+
const hydrationMeta = transaction.getMeta(DOCUMENT_CHUNK_HYDRATION_META);
|
|
10624
|
+
const appendedTo = transaction.getMeta('appendedTransaction');
|
|
10625
|
+
const sourcePaginationMeta = appendedTo?.getMeta(DOCUMENT_CHUNK_PAGINATION_META);
|
|
10626
|
+
const paginationBreaks = paginationMeta?.breaks ?? sourcePaginationMeta?.breaks.map((pageBreak)=>({
|
|
10627
|
+
...pageBreak,
|
|
10628
|
+
position: transaction.mapping.map(pageBreak.position, -1)
|
|
10629
|
+
}));
|
|
10630
|
+
if (!meta && !paginationMeta && !transaction.docChanged && !transaction.selectionSet) return current;
|
|
10631
|
+
const geometry = paginationBreaks ? documentChunkGeometry(next.doc, paginationBreaks) : transaction.docChanged && !hydrationMeta ? documentChunkGeometry(next.doc, []) : current.geometry;
|
|
10632
|
+
const ids = geometry === current.geometry ? current.ids : geometry.map(({ id })=>id);
|
|
10633
|
+
const positions = geometry === current.geometry ? current.positions : documentChunkPositions(next.doc);
|
|
10634
|
+
const known = geometry === current.geometry ? null : new Set(ids);
|
|
10635
|
+
const mountedIds = current.fallback ? new Set(ids) : new Set(Array.from(meta?.visibleIds ?? current.mountedIds).filter((id)=>!known || known.has(id)));
|
|
10636
|
+
const selected = selectedDocumentChunkId(next);
|
|
10637
|
+
if (selected) mountedIds.add(selected);
|
|
10638
|
+
if (!transaction.docChanged && geometry === current.geometry && setsEqual(mountedIds, current.mountedIds) && ids.length === current.chunkCount) return current;
|
|
10639
|
+
return {
|
|
10640
|
+
chunkCount: ids.length,
|
|
10641
|
+
decorations: documentChunkDecorations(next.doc, mountedIds, positions),
|
|
10642
|
+
fallback: current.fallback,
|
|
10643
|
+
geometry,
|
|
10644
|
+
ids,
|
|
10645
|
+
mountedIds,
|
|
10646
|
+
positions
|
|
10647
|
+
};
|
|
10648
|
+
}
|
|
10649
|
+
function createDocumentChunkIntersectionView(editor, initialView) {
|
|
10650
|
+
let view = initialView;
|
|
10651
|
+
let observer = null;
|
|
10652
|
+
let scrollRoot = null;
|
|
10653
|
+
let publishQueued = false;
|
|
10654
|
+
let mountFrame = 0;
|
|
10655
|
+
let synchronizedGeometry = null;
|
|
10656
|
+
const observed = new Map();
|
|
10657
|
+
const intersecting = new Set();
|
|
10658
|
+
const registry = documentChunkElementRegistryFor(editor);
|
|
10659
|
+
const dispatchVisibleWindow = (requestedIds)=>{
|
|
10660
|
+
if (view.isDestroyed) return;
|
|
10661
|
+
const state = documentChunkWindowKey.getState(view.state);
|
|
10662
|
+
if (!state) return;
|
|
10663
|
+
const visibleIds = new Set(requestedIds);
|
|
10664
|
+
const selected = selectedDocumentChunkId(view.state);
|
|
10665
|
+
if (selected) visibleIds.add(selected);
|
|
10666
|
+
if (setsEqual(visibleIds, state.mountedIds)) return;
|
|
10667
|
+
const startedAt = globalThis.performance?.now?.() ?? Date.now();
|
|
10668
|
+
const transaction = view.state.tr;
|
|
10669
|
+
view.dispatch(transaction.setMeta(documentChunkWindowKey, {
|
|
10670
|
+
visibleIds: [
|
|
10671
|
+
...visibleIds
|
|
10672
|
+
]
|
|
10673
|
+
}).setMeta(DOCUMENT_CHUNK_VISIBLE_IDS_META, {
|
|
10674
|
+
visibleIds: [
|
|
10675
|
+
...visibleIds
|
|
10676
|
+
]
|
|
10677
|
+
}).setMeta('addToHistory', false).setMeta('preventUpdate', true));
|
|
10678
|
+
const duration = (globalThis.performance?.now?.() ?? Date.now()) - startedAt;
|
|
10679
|
+
const updates = Number(view.dom.dataset.documentWindowUpdates) || 0;
|
|
10680
|
+
const total = Number(view.dom.dataset.documentWindowTotalMs) || 0;
|
|
10681
|
+
const maximum = Number(view.dom.dataset.documentWindowMaxMs) || 0;
|
|
10682
|
+
view.dom.dataset.documentWindowUpdates = String(updates + 1);
|
|
10683
|
+
view.dom.dataset.documentWindowLastMs = String(roundDuration(duration));
|
|
10684
|
+
view.dom.dataset.documentWindowTotalMs = String(roundDuration(total + duration));
|
|
10685
|
+
view.dom.dataset.documentWindowMaxMs = String(roundDuration(Math.max(maximum, duration)));
|
|
10686
|
+
};
|
|
10687
|
+
const publishIntersectionWindow = ()=>{
|
|
10688
|
+
const state = documentChunkWindowKey.getState(view.state);
|
|
10689
|
+
if (!state) return;
|
|
10690
|
+
const visibleIds = new Set(Array.from(intersecting).filter((id)=>registry.controllers.has(id)));
|
|
10691
|
+
for (const id of [
|
|
10692
|
+
...intersecting
|
|
10693
|
+
]){
|
|
10694
|
+
const index = state.ids.indexOf(id);
|
|
10695
|
+
if (index > 0) visibleIds.add(state.ids[index - 1]);
|
|
10696
|
+
if (index >= 0 && index + 1 < state.ids.length) visibleIds.add(state.ids[index + 1]);
|
|
10697
|
+
}
|
|
10698
|
+
dispatchVisibleWindow(visibleIds);
|
|
10699
|
+
};
|
|
10700
|
+
const publishGeometryWindow = ()=>{
|
|
10701
|
+
if (!scrollRoot || view.isDestroyed) return;
|
|
10702
|
+
const state = documentChunkWindowKey.getState(view.state);
|
|
10703
|
+
if (!state?.geometry.length) return;
|
|
10704
|
+
const rootRect = scrollRoot.getBoundingClientRect();
|
|
10705
|
+
const editorRect = view.dom.getBoundingClientRect();
|
|
10706
|
+
const scale = view.dom.offsetWidth > 0 ? Math.max(0.01, editorRect.width / view.dom.offsetWidth) : 1;
|
|
10707
|
+
const viewportTop = Math.max(0, (rootRect.top - editorRect.top) / scale);
|
|
10708
|
+
const viewportBottom = Math.max(viewportTop, (rootRect.bottom - editorRect.top) / scale);
|
|
10709
|
+
const { end, start } = documentChunkViewportRange(state.geometry, viewportTop, viewportBottom);
|
|
10710
|
+
dispatchVisibleWindow(state.geometry.slice(start, end).map(({ id })=>id));
|
|
10711
|
+
};
|
|
10712
|
+
const queuePublication = (publish)=>{
|
|
10713
|
+
if (publishQueued) return;
|
|
10714
|
+
publishQueued = true;
|
|
10715
|
+
queueMicrotask(()=>{
|
|
10716
|
+
publishQueued = false;
|
|
10717
|
+
publish();
|
|
10718
|
+
});
|
|
10719
|
+
};
|
|
10720
|
+
const handleIntersections = (entries)=>{
|
|
10721
|
+
for (const entry of entries){
|
|
10722
|
+
const element = entry.target;
|
|
10723
|
+
const id = element.dataset.documentChunkId;
|
|
10724
|
+
if (id) if (entry.isIntersecting) intersecting.add(id);
|
|
10725
|
+
else intersecting.delete(id);
|
|
10726
|
+
}
|
|
10727
|
+
queuePublication(publishIntersectionWindow);
|
|
10728
|
+
};
|
|
10729
|
+
const handleScroll = ()=>queuePublication(publishGeometryWindow);
|
|
10730
|
+
const disconnectObserver = ()=>{
|
|
10731
|
+
observer?.disconnect();
|
|
10732
|
+
observer = null;
|
|
10733
|
+
observed.clear();
|
|
10734
|
+
intersecting.clear();
|
|
10735
|
+
};
|
|
10736
|
+
const ensureFallbackObserver = ()=>{
|
|
10737
|
+
const state = documentChunkWindowKey.getState(view.state);
|
|
10738
|
+
if (scrollRoot || observer || !state || state.fallback || 0 === state.chunkCount) return false;
|
|
10739
|
+
observer = new IntersectionObserver(handleIntersections, {
|
|
10740
|
+
root: null,
|
|
10741
|
+
rootMargin: DOCUMENT_CHUNK_WINDOW_ROOT_MARGIN,
|
|
10742
|
+
threshold: 0
|
|
10743
|
+
});
|
|
10744
|
+
return true;
|
|
10745
|
+
};
|
|
10746
|
+
const synchronizeElements = ()=>{
|
|
10747
|
+
if (!observer) return;
|
|
10748
|
+
for (const [id, element] of observed)if (registry.controllers.get(id)?.element !== element) {
|
|
10749
|
+
observer.unobserve(element);
|
|
10750
|
+
observed.delete(id);
|
|
10751
|
+
intersecting.delete(id);
|
|
10752
|
+
}
|
|
10753
|
+
for (const [id, controller] of registry.controllers){
|
|
10754
|
+
const element = controller.element;
|
|
10755
|
+
if (id && observed.get(id) !== element) {
|
|
10756
|
+
observed.set(id, element);
|
|
10757
|
+
observer.observe(element);
|
|
10758
|
+
}
|
|
10759
|
+
}
|
|
10760
|
+
};
|
|
10761
|
+
const updateDatasets = ()=>{
|
|
10762
|
+
const state = documentChunkWindowKey.getState(view.state);
|
|
10763
|
+
view.dom.dataset.documentWindowed = String(Boolean(state?.chunkCount));
|
|
10764
|
+
view.dom.dataset.documentChunkCount = String(state?.chunkCount ?? 0);
|
|
10765
|
+
view.dom.dataset.documentMountedChunkCount = String(state?.mountedIds.size ?? 0);
|
|
10766
|
+
if (state?.fallback && state.chunkCount) view.dom.dataset.documentWindowingFallback = 'true';
|
|
10767
|
+
else delete view.dom.dataset.documentWindowingFallback;
|
|
10768
|
+
};
|
|
10769
|
+
const ensureDriver = ()=>{
|
|
10770
|
+
const nextRoot = view.dom.closest('.work-document-scroll');
|
|
10771
|
+
if (nextRoot) {
|
|
10772
|
+
if (nextRoot !== scrollRoot) {
|
|
10773
|
+
scrollRoot?.removeEventListener('scroll', handleScroll);
|
|
10774
|
+
scrollRoot = nextRoot;
|
|
10775
|
+
scrollRoot.addEventListener('scroll', handleScroll, {
|
|
10776
|
+
passive: true
|
|
10777
|
+
});
|
|
10778
|
+
}
|
|
10779
|
+
disconnectObserver();
|
|
10780
|
+
queuePublication(publishGeometryWindow);
|
|
10781
|
+
return;
|
|
10782
|
+
}
|
|
10783
|
+
if (scrollRoot) {
|
|
10784
|
+
scrollRoot.removeEventListener('scroll', handleScroll);
|
|
10785
|
+
scrollRoot = null;
|
|
10786
|
+
}
|
|
10787
|
+
const created = ensureFallbackObserver();
|
|
10788
|
+
if (created) synchronizeElements();
|
|
10789
|
+
};
|
|
10790
|
+
const synchronize = ()=>{
|
|
10791
|
+
updateDatasets();
|
|
10792
|
+
const state = documentChunkWindowKey.getState(view.state);
|
|
10793
|
+
if (state && state.geometry !== synchronizedGeometry) {
|
|
10794
|
+
synchronizedGeometry = state.geometry;
|
|
10795
|
+
synchronizeDocumentChunkPaginationHeights(registry, state.geometry);
|
|
10796
|
+
}
|
|
10797
|
+
ensureDriver();
|
|
10798
|
+
if (observer) synchronizeElements();
|
|
10799
|
+
};
|
|
10800
|
+
const handleElementChange = ({ id, next, previous })=>{
|
|
10801
|
+
if (previous && observed.get(id) === previous.element) {
|
|
10802
|
+
observer?.unobserve(previous.element);
|
|
10803
|
+
observed.delete(id);
|
|
10804
|
+
intersecting.delete(id);
|
|
10805
|
+
}
|
|
10806
|
+
if (next && observer) {
|
|
10807
|
+
observed.set(id, next.element);
|
|
10808
|
+
observer.observe(next.element);
|
|
10809
|
+
}
|
|
10810
|
+
};
|
|
10811
|
+
registry.listeners.add(handleElementChange);
|
|
10812
|
+
synchronize();
|
|
10813
|
+
queueMicrotask(synchronize);
|
|
10814
|
+
if ('function' == typeof requestAnimationFrame) mountFrame = requestAnimationFrame(()=>{
|
|
10815
|
+
mountFrame = 0;
|
|
10816
|
+
synchronize();
|
|
10817
|
+
});
|
|
10818
|
+
return {
|
|
10819
|
+
destroy () {
|
|
10820
|
+
if (mountFrame) cancelAnimationFrame(mountFrame);
|
|
10821
|
+
registry.listeners.delete(handleElementChange);
|
|
10822
|
+
scrollRoot?.removeEventListener('scroll', handleScroll);
|
|
10823
|
+
scrollRoot = null;
|
|
10824
|
+
disconnectObserver();
|
|
10825
|
+
},
|
|
10826
|
+
update (nextView) {
|
|
10827
|
+
view = nextView;
|
|
10828
|
+
synchronize();
|
|
10829
|
+
}
|
|
10830
|
+
};
|
|
10831
|
+
}
|
|
10832
|
+
function documentLazyChunkContentForEditor(editor, chunkId) {
|
|
10833
|
+
const content = documentChunkContentSources.get(editor)?.() ?? null;
|
|
10834
|
+
return documentLazyChunkContent(content?.model, chunkId);
|
|
10835
|
+
}
|
|
10836
|
+
function documentTransactionsOnlyHydrateChunks(transactions) {
|
|
10837
|
+
const changed = transactions.filter((transaction)=>transaction.docChanged);
|
|
10838
|
+
return changed.length > 0 && changed.every((transaction)=>Boolean(transaction.getMeta(DOCUMENT_CHUNK_HYDRATION_META)));
|
|
10839
|
+
}
|
|
10840
|
+
function hydrateDocumentChunks(editor, transaction, state, requestedIds) {
|
|
10841
|
+
const previousDocument = transaction.doc;
|
|
10842
|
+
const candidates = Array.from(new Set(requestedIds)).map((id)=>({
|
|
10843
|
+
id,
|
|
10844
|
+
position: documentChunkWindowKey.getState(state)?.positions.get(id)
|
|
10845
|
+
})).filter((candidate)=>Boolean(candidate.position)).sort((left, right)=>right.position.from - left.position.from);
|
|
10846
|
+
const hydratedIds = [];
|
|
10847
|
+
const originalAnchor = transaction.selection.anchor;
|
|
10848
|
+
const originalHead = transaction.selection.head;
|
|
10849
|
+
for (const { id, position } of candidates){
|
|
10850
|
+
const chunk = transaction.doc.nodeAt(position.from);
|
|
10851
|
+
if (!chunk || 'documentChunk' !== chunk.type.name || 1 !== chunk.childCount || chunk.firstChild?.type.name !== DOCUMENT_LAZY_BLOCK_NODE) continue;
|
|
10852
|
+
const payload = documentLazyChunkContentForEditor(editor, id);
|
|
10853
|
+
if (!payload?.length) continue;
|
|
10854
|
+
let content;
|
|
10855
|
+
try {
|
|
10856
|
+
content = Fragment.fromArray(payload.map((node)=>transaction.doc.type.schema.nodeFromJSON(node)));
|
|
10857
|
+
} catch (error) {
|
|
10858
|
+
throw new Error(`The lazy document chunk "${id}" cannot be hydrated.`, {
|
|
10859
|
+
cause: error
|
|
10860
|
+
});
|
|
10861
|
+
}
|
|
10862
|
+
if (content.size !== chunk.content.size) throw new Error(`The lazy document chunk "${id}" changed its logical size during hydration.`);
|
|
10863
|
+
transaction.replaceWith(position.from + 1, position.to - 1, content);
|
|
10864
|
+
hydratedIds.push(id);
|
|
10865
|
+
}
|
|
10866
|
+
if (!hydratedIds.length) return hydratedIds;
|
|
10867
|
+
transferDocumentTextStatistics(previousDocument, transaction.doc);
|
|
10868
|
+
transaction.setMeta(DOCUMENT_CHUNK_HYDRATION_META, {
|
|
10869
|
+
ids: hydratedIds
|
|
10870
|
+
}).setMeta('addToHistory', false).setMeta('preventUpdate', true);
|
|
10871
|
+
const maximum = transaction.doc.content.size;
|
|
10872
|
+
if (originalAnchor <= maximum && originalHead <= maximum) {
|
|
10873
|
+
const anchor = transaction.doc.resolve(originalAnchor);
|
|
10874
|
+
const head = transaction.doc.resolve(originalHead);
|
|
10875
|
+
if (anchor.parent.inlineContent && head.parent.inlineContent) transaction.setSelection(TextSelection.create(transaction.doc, originalAnchor, originalHead));
|
|
10876
|
+
}
|
|
10877
|
+
return hydratedIds;
|
|
10878
|
+
}
|
|
10879
|
+
function documentChunkDecorations(document1, mountedIds, positions) {
|
|
10880
|
+
const decoratedIds = new Set();
|
|
10881
|
+
for (const id of mountedIds){
|
|
10882
|
+
const position = positions.get(id);
|
|
10883
|
+
if (position) {
|
|
10884
|
+
decoratedIds.add(id);
|
|
10885
|
+
for (const ancestorId of position.ancestorIds)decoratedIds.add(ancestorId);
|
|
10886
|
+
}
|
|
10887
|
+
}
|
|
10888
|
+
const decorations = Array.from(decoratedIds).flatMap((id)=>{
|
|
10889
|
+
const position = positions.get(id);
|
|
10890
|
+
return position ? [
|
|
10891
|
+
Decoration.node(position.from, position.to, {}, {
|
|
10892
|
+
documentChunkMounted: true
|
|
10893
|
+
})
|
|
10894
|
+
] : [];
|
|
10895
|
+
});
|
|
10896
|
+
return DecorationSet.create(document1, decorations);
|
|
10897
|
+
}
|
|
10898
|
+
function selectedDocumentChunkId(state) {
|
|
10899
|
+
const resolved = state.doc.resolve(state.selection.anchor);
|
|
10900
|
+
for(let depth = resolved.depth; depth >= 0; depth -= 1){
|
|
10901
|
+
const node = resolved.node(depth);
|
|
10902
|
+
if ('documentChunk' === node.type.name) return work_document_chunk_geometry_documentChunkId(node);
|
|
10903
|
+
}
|
|
10904
|
+
return null;
|
|
10905
|
+
}
|
|
10906
|
+
function documentChunkMountedIds(state) {
|
|
10907
|
+
const chunkState = documentChunkWindowKey.getState(state);
|
|
10908
|
+
return chunkState?.chunkCount ? [
|
|
10909
|
+
...chunkState.mountedIds
|
|
10910
|
+
] : null;
|
|
10911
|
+
}
|
|
10912
|
+
function documentChunkIsMounted(_node, decorations) {
|
|
10913
|
+
return decorations.some((decoration)=>true === decoration.spec.documentChunkMounted);
|
|
10914
|
+
}
|
|
10915
|
+
function documentChunkElementRegistryFor(editor) {
|
|
10916
|
+
let registry = documentChunkElementRegistries.get(editor);
|
|
10917
|
+
if (!registry) {
|
|
10918
|
+
registry = {
|
|
10919
|
+
controllers: new Map(),
|
|
10920
|
+
lazyPreviewPool: createDocumentLazyPreviewPool(),
|
|
10921
|
+
listeners: new Set(),
|
|
10922
|
+
paginationExtraHeights: new Map()
|
|
10923
|
+
};
|
|
10924
|
+
documentChunkElementRegistries.set(editor, registry);
|
|
10925
|
+
}
|
|
10926
|
+
return registry;
|
|
10927
|
+
}
|
|
10928
|
+
function registerDocumentChunkController(editor, id, controller) {
|
|
10929
|
+
const registry = documentChunkElementRegistryFor(editor);
|
|
10930
|
+
const previous = registry.controllers.get(id);
|
|
10931
|
+
registry.controllers.set(id, controller);
|
|
10932
|
+
const added = {
|
|
10933
|
+
id,
|
|
10934
|
+
next: controller,
|
|
10935
|
+
previous
|
|
10936
|
+
};
|
|
10937
|
+
for (const listener of registry.listeners)listener(added);
|
|
10938
|
+
return ()=>{
|
|
10939
|
+
if (registry.controllers.get(id) !== controller) return;
|
|
10940
|
+
registry.controllers.delete(id);
|
|
10941
|
+
const removed = {
|
|
10942
|
+
id,
|
|
10943
|
+
previous: controller
|
|
10944
|
+
};
|
|
10945
|
+
for (const listener of registry.listeners)listener(removed);
|
|
10946
|
+
};
|
|
10947
|
+
}
|
|
10948
|
+
function synchronizeDocumentChunkPaginationHeights(registry, geometry) {
|
|
10949
|
+
const heights = new Map();
|
|
10950
|
+
for (const chunk of geometry){
|
|
10951
|
+
heights.set(chunk.id, chunk.paginationExtraHeight);
|
|
10952
|
+
if (chunk.containerId) heights.set(chunk.containerId, (heights.get(chunk.containerId) ?? 0) + chunk.paginationExtraHeight);
|
|
10953
|
+
}
|
|
10954
|
+
const nextIds = new Set();
|
|
10955
|
+
for (const [id, height] of heights){
|
|
10956
|
+
nextIds.add(id);
|
|
10957
|
+
registry.paginationExtraHeights.set(id, height);
|
|
10958
|
+
registry.controllers.get(id)?.setPaginationExtraHeight(height);
|
|
10959
|
+
}
|
|
10960
|
+
for (const id of [
|
|
10961
|
+
...registry.paginationExtraHeights.keys()
|
|
10962
|
+
])if (!nextIds.has(id)) {
|
|
10963
|
+
registry.paginationExtraHeights.delete(id);
|
|
10964
|
+
registry.controllers.get(id)?.setPaginationExtraHeight(0);
|
|
10965
|
+
}
|
|
10966
|
+
}
|
|
10967
|
+
function documentChunkDomAttributes(node) {
|
|
10968
|
+
return {
|
|
10969
|
+
'data-document-chunk': 'true',
|
|
10970
|
+
'data-document-chunk-id': work_document_chunk_geometry_documentChunkId(node),
|
|
10971
|
+
'data-document-chunk-blocks': String(work_document_chunk_node_positiveInteger(node.attrs.blockCount, node.childCount)),
|
|
10972
|
+
'data-document-chunk-estimated-height': String(work_document_chunk_node_positiveNumber(node.attrs.estimatedHeight, 1)),
|
|
10973
|
+
'data-document-chunk-window-container': String(true === node.attrs.windowContainer)
|
|
10974
|
+
};
|
|
10975
|
+
}
|
|
10976
|
+
function applyDocumentChunkNodeViewAttributes(dom, node, mounted, paginationExtraHeight) {
|
|
10977
|
+
for (const [name, value] of Object.entries(documentChunkDomAttributes(node)))dom.setAttribute(name, value);
|
|
10978
|
+
dom.dataset.documentChunkMounted = String(mounted);
|
|
10979
|
+
dom.dataset.documentChunkPaginationExtraHeight = String(paginationExtraHeight);
|
|
10980
|
+
dom.className = `work-document-chunk ${mounted ? 'mounted' : 'placeholder'}`;
|
|
10981
|
+
if (mounted) {
|
|
10982
|
+
dom.removeAttribute('aria-hidden');
|
|
10983
|
+
dom.removeAttribute('contenteditable');
|
|
10984
|
+
dom.style.removeProperty('height');
|
|
10985
|
+
dom.style.removeProperty('contain');
|
|
10986
|
+
return;
|
|
10987
|
+
}
|
|
10988
|
+
dom.setAttribute('aria-hidden', 'true');
|
|
10989
|
+
dom.setAttribute('contenteditable', 'false');
|
|
10990
|
+
dom.style.height = `${work_document_chunk_node_positiveNumber(node.attrs.estimatedHeight, 1) + paginationExtraHeight}px`;
|
|
10991
|
+
dom.style.contain = 'strict';
|
|
10992
|
+
}
|
|
10993
|
+
function setsEqual(left, right) {
|
|
10994
|
+
if (left.size !== right.size) return false;
|
|
10995
|
+
for (const value of left)if (!right.has(value)) return false;
|
|
10996
|
+
return true;
|
|
10997
|
+
}
|
|
10998
|
+
function work_document_chunk_node_positiveInteger(value, fallback) {
|
|
10999
|
+
const number = Number(value);
|
|
11000
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : fallback;
|
|
11001
|
+
}
|
|
11002
|
+
function work_document_chunk_node_positiveNumber(value, fallback) {
|
|
11003
|
+
const number = Number(value);
|
|
11004
|
+
return Number.isFinite(number) && number > 0 ? number : fallback;
|
|
11005
|
+
}
|
|
11006
|
+
function roundDuration(value) {
|
|
11007
|
+
return Math.round(10 * value) / 10;
|
|
11008
|
+
}
|
|
11009
|
+
const DocumentCitation = core_Node.create({
|
|
11010
|
+
name: 'documentCitation',
|
|
11011
|
+
inline: true,
|
|
11012
|
+
group: 'inline',
|
|
11013
|
+
atom: true,
|
|
11014
|
+
selectable: true,
|
|
11015
|
+
addOptions () {
|
|
11016
|
+
return {
|
|
11017
|
+
getContent: ()=>null,
|
|
11018
|
+
onContentChange: ()=>void 0
|
|
11019
|
+
};
|
|
11020
|
+
},
|
|
11021
|
+
addCommands () {
|
|
11022
|
+
return {
|
|
11023
|
+
insertDocumentCitation: (source, bibliography)=>(props)=>insertDocumentCitationCommand(props, source, bibliography),
|
|
11024
|
+
insertDocumentBibliography: (bibliography)=>(props)=>insertDocumentBibliographyCommand(props, bibliography),
|
|
11025
|
+
refreshDocumentCitations: (content)=>(props)=>refreshDocumentCitationsCommand(props, content),
|
|
11026
|
+
renameDocumentCitationTag: (previousTag, nextTag)=>(props)=>renameDocumentCitationTagCommand(props, previousTag, nextTag),
|
|
11027
|
+
setDocumentBibliography: (bibliography, renamedTag)=>({ dispatch, editor })=>{
|
|
11028
|
+
const content = this.options.getContent();
|
|
11029
|
+
if (!content) return false;
|
|
11030
|
+
if (!dispatch) return true;
|
|
11031
|
+
const next = {
|
|
11032
|
+
...content,
|
|
11033
|
+
bibliography
|
|
11034
|
+
};
|
|
11035
|
+
this.options.onContentChange(next);
|
|
11036
|
+
const chain = editor.chain();
|
|
11037
|
+
if (renamedTag) chain.renameDocumentCitationTag(renamedTag.previous, renamedTag.next);
|
|
11038
|
+
chain.refreshDocumentCitations(next).run();
|
|
11039
|
+
return true;
|
|
11040
|
+
}
|
|
11041
|
+
};
|
|
11042
|
+
},
|
|
11043
|
+
addAttributes () {
|
|
11044
|
+
return {
|
|
11045
|
+
id: work_document_citation_nodes_hiddenAttribute(''),
|
|
11046
|
+
tags: work_document_citation_nodes_hiddenAttribute(''),
|
|
11047
|
+
instruction: work_document_citation_nodes_hiddenAttribute(''),
|
|
11048
|
+
display: work_document_citation_nodes_hiddenAttribute(''),
|
|
11049
|
+
orphaned: work_document_citation_nodes_hiddenAttribute(false)
|
|
11050
|
+
};
|
|
11051
|
+
},
|
|
11052
|
+
parseHTML () {
|
|
11053
|
+
return [
|
|
11054
|
+
{
|
|
11055
|
+
tag: 'span[data-document-citation]',
|
|
11056
|
+
getAttrs: (node)=>{
|
|
11057
|
+
if (!(node instanceof HTMLElement)) return false;
|
|
11058
|
+
const instruction = node.dataset.citationInstruction?.trim() ?? '';
|
|
11059
|
+
const tags = documentCitationTags(node.dataset.citationTags);
|
|
11060
|
+
const resolvedTags = tags.length ? tags : documentCitationTagsFromInstruction(instruction);
|
|
11061
|
+
return {
|
|
11062
|
+
id: node.dataset.citationId ?? '',
|
|
11063
|
+
tags: resolvedTags.join(' '),
|
|
11064
|
+
instruction: instruction || documentCitationInstruction(resolvedTags),
|
|
11065
|
+
display: node.dataset.citationDisplay?.trim() || node.textContent?.trim() || '缺失引文',
|
|
11066
|
+
orphaned: 'true' === node.dataset.citationOrphaned
|
|
11067
|
+
};
|
|
11068
|
+
}
|
|
11069
|
+
}
|
|
11070
|
+
];
|
|
9481
11071
|
},
|
|
9482
11072
|
renderHTML ({ node, HTMLAttributes }) {
|
|
9483
11073
|
const tags = 'string' == typeof node.attrs.tags ? documentCitationTags(node.attrs.tags) : [];
|
|
@@ -10028,6 +11618,7 @@ function createDocumentFieldIdentityPlugin(fieldNodeName = 'documentField') {
|
|
|
10028
11618
|
});
|
|
10029
11619
|
}
|
|
10030
11620
|
function normalizeDocumentFieldIdentities(state, fieldNodeName, oldState, transactions = []) {
|
|
11621
|
+
if ('documentField' === fieldNodeName && !documentHasIntegrityFeature(state.doc, 2)) return null;
|
|
10031
11622
|
const fields = documentFields(state.doc, fieldNodeName);
|
|
10032
11623
|
if (!fields.length) return null;
|
|
10033
11624
|
const retained = oldState ? retainedDocumentFieldPositions(oldState, state, transactions, fieldNodeName) : new Set();
|
|
@@ -11196,6 +12787,42 @@ function syncImageDimension(element, dimension, value) {
|
|
|
11196
12787
|
element.style.removeProperty(dimension);
|
|
11197
12788
|
element.removeAttribute(dimension);
|
|
11198
12789
|
}
|
|
12790
|
+
const DocumentLazyBlock = core_Node.create({
|
|
12791
|
+
name: DOCUMENT_LAZY_BLOCK_NODE,
|
|
12792
|
+
group: 'block',
|
|
12793
|
+
content: 'text*',
|
|
12794
|
+
selectable: false,
|
|
12795
|
+
addAttributes () {
|
|
12796
|
+
return {
|
|
12797
|
+
chunkId: {
|
|
12798
|
+
default: ''
|
|
12799
|
+
},
|
|
12800
|
+
contentSize: {
|
|
12801
|
+
default: 2
|
|
12802
|
+
},
|
|
12803
|
+
paragraphCount: {
|
|
12804
|
+
default: 0
|
|
12805
|
+
}
|
|
12806
|
+
};
|
|
12807
|
+
},
|
|
12808
|
+
parseHTML () {
|
|
12809
|
+
return [
|
|
12810
|
+
{
|
|
12811
|
+
tag: 'div[data-document-lazy-block]'
|
|
12812
|
+
}
|
|
12813
|
+
];
|
|
12814
|
+
},
|
|
12815
|
+
renderHTML ({ HTMLAttributes }) {
|
|
12816
|
+
return [
|
|
12817
|
+
'div',
|
|
12818
|
+
mergeAttributes(HTMLAttributes, {
|
|
12819
|
+
'data-document-lazy-block': 'true',
|
|
12820
|
+
'aria-hidden': 'true'
|
|
12821
|
+
}),
|
|
12822
|
+
0
|
|
12823
|
+
];
|
|
12824
|
+
}
|
|
12825
|
+
});
|
|
11199
12826
|
const MAX_DOCUMENT_NUMBERING_START = 2147483647;
|
|
11200
12827
|
const DocumentBulletList = BulletList.extend({
|
|
11201
12828
|
addAttributes () {
|
|
@@ -11437,6 +13064,7 @@ function createDocumentNoteIntegrityPlugin(referenceNodeName = 'documentNoteRefe
|
|
|
11437
13064
|
});
|
|
11438
13065
|
}
|
|
11439
13066
|
function normalizeDocumentNoteGraph(state, referenceNodeName, definitionNodeName, oldState, transactions = []) {
|
|
13067
|
+
if ('documentNoteReference' === referenceNodeName && 'documentNote' === definitionNodeName && !documentHasIntegrityFeature(state.doc, 8)) return null;
|
|
11440
13068
|
const allReferences = documentNoteNodes(state.doc, referenceNodeName);
|
|
11441
13069
|
const nestedReferences = allReferences.filter((reference)=>positionInsideNode(state.doc, reference.position, definitionNodeName));
|
|
11442
13070
|
const nestedReferencePositions = new Set(nestedReferences.map(({ position })=>position));
|
|
@@ -12349,6 +13977,29 @@ function reusableDocumentLayoutBlocks(previous, blockId, element, nodeTo, dirtyF
|
|
|
12349
13977
|
element
|
|
12350
13978
|
}));
|
|
12351
13979
|
}
|
|
13980
|
+
function reusableDocumentChunkLayoutBlocks(previous, element, chunkFrom, chunkTo, dirtyFrom) {
|
|
13981
|
+
if (chunkTo > dirtyFrom || chunkTo <= chunkFrom || !previous.length) return [];
|
|
13982
|
+
let lower = 0;
|
|
13983
|
+
let upper = previous.length;
|
|
13984
|
+
while(lower < upper){
|
|
13985
|
+
const middle = Math.floor((lower + upper) / 2);
|
|
13986
|
+
if (previous[middle].from < chunkFrom) lower = middle + 1;
|
|
13987
|
+
else upper = middle;
|
|
13988
|
+
}
|
|
13989
|
+
const reused = [];
|
|
13990
|
+
for(let index = lower; index < previous.length; index += 1){
|
|
13991
|
+
const candidate = previous[index];
|
|
13992
|
+
if (candidate.from >= chunkTo) break;
|
|
13993
|
+
if (!(candidate.to > chunkTo)) reused.push({
|
|
13994
|
+
...candidate,
|
|
13995
|
+
block: {
|
|
13996
|
+
...candidate.block
|
|
13997
|
+
},
|
|
13998
|
+
element
|
|
13999
|
+
});
|
|
14000
|
+
}
|
|
14001
|
+
return reused;
|
|
14002
|
+
}
|
|
12352
14003
|
function isDocumentListNode(node) {
|
|
12353
14004
|
return 'bulletList' === node.type.name || 'orderedList' === node.type.name;
|
|
12354
14005
|
}
|
|
@@ -12422,6 +14073,401 @@ function elementForNode(editor, position) {
|
|
|
12422
14073
|
if (!node || node.nodeType !== Node.ELEMENT_NODE) return null;
|
|
12423
14074
|
return node;
|
|
12424
14075
|
}
|
|
14076
|
+
const DOCUMENT_WINDOW_BLOCK_SIZE = 128;
|
|
14077
|
+
const DOCUMENT_WINDOW_BLOCK_THRESHOLD = 2048;
|
|
14078
|
+
const DOCUMENT_TABLE_WINDOW_ROW_SIZE = 16;
|
|
14079
|
+
const DOCUMENT_TABLE_WINDOW_ROW_THRESHOLD = 512;
|
|
14080
|
+
const DOCUMENT_TABLE_WINDOW_GROUP_SIZE = 32;
|
|
14081
|
+
const DEFAULT_PARAGRAPH_LINE_HEIGHT = 21;
|
|
14082
|
+
const DEFAULT_PARAGRAPH_AFTER = 8;
|
|
14083
|
+
const DEFAULT_TABLE_ROW_HEIGHT = 22;
|
|
14084
|
+
const DOCUMENT_TEXT_CHARACTERS_PER_LINE = 88;
|
|
14085
|
+
const DEFAULT_DOCUMENT_WINDOWING_OPTIONS = {
|
|
14086
|
+
blockSize: DOCUMENT_WINDOW_BLOCK_SIZE,
|
|
14087
|
+
blockThreshold: DOCUMENT_WINDOW_BLOCK_THRESHOLD,
|
|
14088
|
+
tableRowSize: DOCUMENT_TABLE_WINDOW_ROW_SIZE,
|
|
14089
|
+
tableRowThreshold: DOCUMENT_TABLE_WINDOW_ROW_THRESHOLD
|
|
14090
|
+
};
|
|
14091
|
+
function windowDocumentModel(root, requested = {}) {
|
|
14092
|
+
if (documentSectionModelUsesWindowing(root)) return root;
|
|
14093
|
+
const options = normalizeWindowingOptions(requested);
|
|
14094
|
+
let changed = false;
|
|
14095
|
+
const content = root.content?.map((node, sectionIndex)=>{
|
|
14096
|
+
if ('documentSection' !== node.type || !node.content?.length) return node;
|
|
14097
|
+
const needsBlockWindow = node.content.length >= options.blockThreshold;
|
|
14098
|
+
const needsTableWindow = node.content.some((child)=>'table' === child.type && (child.content?.length ?? 0) >= options.tableRowThreshold);
|
|
14099
|
+
if (!needsBlockWindow && !needsTableWindow) return node;
|
|
14100
|
+
changed = true;
|
|
14101
|
+
return {
|
|
14102
|
+
...node,
|
|
14103
|
+
content: windowSectionContent(node.content, sectionIndex, options)
|
|
14104
|
+
};
|
|
14105
|
+
});
|
|
14106
|
+
return changed ? {
|
|
14107
|
+
...root,
|
|
14108
|
+
content
|
|
14109
|
+
} : root;
|
|
14110
|
+
}
|
|
14111
|
+
function materializeWindowedDocumentModel(root) {
|
|
14112
|
+
if (!documentModelUsesWindowing(root)) return root;
|
|
14113
|
+
return {
|
|
14114
|
+
...root,
|
|
14115
|
+
content: root.content?.map((node)=>'documentSection' === node.type ? materializeWindowedSection(node) : materializeNestedWindowing(node))
|
|
14116
|
+
};
|
|
14117
|
+
}
|
|
14118
|
+
function documentModelUsesWindowing(root) {
|
|
14119
|
+
const pending = [
|
|
14120
|
+
root
|
|
14121
|
+
];
|
|
14122
|
+
while(pending.length){
|
|
14123
|
+
const node = pending.pop();
|
|
14124
|
+
if (!node) continue;
|
|
14125
|
+
if ('documentChunk' === node.type || 'table' === node.type && 'string' == typeof node.attrs?.virtualTableId) return true;
|
|
14126
|
+
if (node.content) pending.push(...node.content);
|
|
14127
|
+
}
|
|
14128
|
+
return false;
|
|
14129
|
+
}
|
|
14130
|
+
function documentSectionModelUsesWindowing(root) {
|
|
14131
|
+
for (const section of root.content ?? [])if ('documentSection' === section.type && section.content?.some((node)=>'documentChunk' === node.type || 'table' === node.type && 'string' == typeof node.attrs?.virtualTableId)) return true;
|
|
14132
|
+
return false;
|
|
14133
|
+
}
|
|
14134
|
+
function estimateDocumentNodeHeight(node) {
|
|
14135
|
+
if ('tableRow' === node.type) return estimateDocumentTableRowHeight(node);
|
|
14136
|
+
if ('table' === node.type) {
|
|
14137
|
+
const rows = node.content ?? [];
|
|
14138
|
+
const rowsHeight = rows.reduce((height, row)=>height + estimateDocumentTableRowHeight(row), 0);
|
|
14139
|
+
return Math.max(DEFAULT_TABLE_ROW_HEIGHT, rowsHeight + (node.attrs?.officeImported ? 0 : 40));
|
|
14140
|
+
}
|
|
14141
|
+
if ('image' === node.type) {
|
|
14142
|
+
const height = finitePositiveNumber(node.attrs?.height);
|
|
14143
|
+
return Math.max(DEFAULT_PARAGRAPH_LINE_HEIGHT, (height ?? 120) + 36);
|
|
14144
|
+
}
|
|
14145
|
+
if ('horizontalRule' === node.type || 'pageBreak' === node.type) return 38;
|
|
14146
|
+
if ('heading' === node.type) {
|
|
14147
|
+
const level = finitePositiveNumber(node.attrs?.level) ?? 1;
|
|
14148
|
+
const lineHeight = level <= 1 ? 32 : 2 === level ? 28 : 25;
|
|
14149
|
+
return estimatedDocumentNodeTextLines(node) * lineHeight + 18;
|
|
14150
|
+
}
|
|
14151
|
+
if ('paragraph' === node.type) return estimatedDocumentNodeTextLines(node) * DEFAULT_PARAGRAPH_LINE_HEIGHT + DEFAULT_PARAGRAPH_AFTER;
|
|
14152
|
+
if ('bulletList' === node.type || 'orderedList' === node.type) return Math.max(DEFAULT_PARAGRAPH_LINE_HEIGHT + DEFAULT_PARAGRAPH_AFTER, (node.content ?? []).reduce((height, child)=>height + estimateDocumentNodeHeight(child), 0));
|
|
14153
|
+
if ('listItem' === node.type || 'blockquote' === node.type) return Math.max(DEFAULT_PARAGRAPH_LINE_HEIGHT + DEFAULT_PARAGRAPH_AFTER, (node.content ?? []).reduce((height, child)=>height + estimateDocumentNodeHeight(child), 0));
|
|
14154
|
+
if ('documentChunk' === node.type) return finitePositiveNumber(node.attrs?.estimatedHeight) ?? (node.content ?? []).reduce((height, child)=>height + estimateDocumentNodeHeight(child), 0);
|
|
14155
|
+
const childrenHeight = (node.content ?? []).reduce((height, child)=>height + estimateDocumentNodeHeight(child), 0);
|
|
14156
|
+
return Math.max(DEFAULT_PARAGRAPH_LINE_HEIGHT + DEFAULT_PARAGRAPH_AFTER, childrenHeight);
|
|
14157
|
+
}
|
|
14158
|
+
function estimateDocumentTableRowHeight(row) {
|
|
14159
|
+
const explicit = finitePositiveNumber(row.attrs?.rowHeight);
|
|
14160
|
+
let maximumLines = 1;
|
|
14161
|
+
for (const cell of row.content ?? [])maximumLines = Math.max(maximumLines, estimatedDocumentNodeTextLines(cell));
|
|
14162
|
+
const contentHeight = maximumLines * DEFAULT_PARAGRAPH_LINE_HEIGHT + 1;
|
|
14163
|
+
return Math.max(DEFAULT_TABLE_ROW_HEIGHT, explicit ?? 0, contentHeight);
|
|
14164
|
+
}
|
|
14165
|
+
function windowSectionContent(source, sectionIndex, options) {
|
|
14166
|
+
const logicalBlocks = source.flatMap((node, blockIndex)=>splitLargeDocumentTable(node, sectionIndex, blockIndex, options));
|
|
14167
|
+
const chunks = [];
|
|
14168
|
+
let pending = [];
|
|
14169
|
+
const flush = ()=>{
|
|
14170
|
+
if (!pending.length) return;
|
|
14171
|
+
const chunkIndex = chunks.length;
|
|
14172
|
+
chunks.push({
|
|
14173
|
+
type: 'documentChunk',
|
|
14174
|
+
attrs: {
|
|
14175
|
+
id: `document-chunk-${sectionIndex + 1}-${chunkIndex + 1}`,
|
|
14176
|
+
blockCount: pending.length,
|
|
14177
|
+
estimatedHeight: roundHeight(pending.reduce((height, node)=>height + estimateDocumentNodeHeight(node), 0)),
|
|
14178
|
+
...void 0 !== options.trustedIntegrityFeatures ? {
|
|
14179
|
+
integrityFeatures: options.trustedIntegrityFeatures
|
|
14180
|
+
} : {}
|
|
14181
|
+
},
|
|
14182
|
+
content: pending
|
|
14183
|
+
});
|
|
14184
|
+
pending = [];
|
|
14185
|
+
};
|
|
14186
|
+
for (const node of logicalBlocks){
|
|
14187
|
+
if ('string' == typeof node.attrs?.virtualTableId) {
|
|
14188
|
+
flush();
|
|
14189
|
+
pending = [
|
|
14190
|
+
node
|
|
14191
|
+
];
|
|
14192
|
+
flush();
|
|
14193
|
+
continue;
|
|
14194
|
+
}
|
|
14195
|
+
pending.push(node);
|
|
14196
|
+
if (pending.length >= options.blockSize) flush();
|
|
14197
|
+
}
|
|
14198
|
+
flush();
|
|
14199
|
+
return groupVirtualTableChunks(chunks, sectionIndex);
|
|
14200
|
+
}
|
|
14201
|
+
function groupVirtualTableChunks(chunks, sectionIndex) {
|
|
14202
|
+
const grouped = [];
|
|
14203
|
+
let index = 0;
|
|
14204
|
+
while(index < chunks.length){
|
|
14205
|
+
const tableId = virtualTableChunkId(chunks[index]);
|
|
14206
|
+
if (!tableId) {
|
|
14207
|
+
grouped.push(chunks[index]);
|
|
14208
|
+
index += 1;
|
|
14209
|
+
continue;
|
|
14210
|
+
}
|
|
14211
|
+
let end = index + 1;
|
|
14212
|
+
while(end < chunks.length && virtualTableChunkId(chunks[end]) === tableId)end += 1;
|
|
14213
|
+
const run = chunks.slice(index, end);
|
|
14214
|
+
if (run.length <= DOCUMENT_TABLE_WINDOW_GROUP_SIZE) {
|
|
14215
|
+
grouped.push(...run);
|
|
14216
|
+
index = end;
|
|
14217
|
+
continue;
|
|
14218
|
+
}
|
|
14219
|
+
for(let offset = 0; offset < run.length; offset += DOCUMENT_TABLE_WINDOW_GROUP_SIZE){
|
|
14220
|
+
const leaves = run.slice(offset, offset + DOCUMENT_TABLE_WINDOW_GROUP_SIZE);
|
|
14221
|
+
const groupIndex = Math.floor(offset / DOCUMENT_TABLE_WINDOW_GROUP_SIZE);
|
|
14222
|
+
const integrityFeatures = combinedDocumentChunkIntegrityFeatures(leaves);
|
|
14223
|
+
grouped.push({
|
|
14224
|
+
type: 'documentChunk',
|
|
14225
|
+
attrs: {
|
|
14226
|
+
id: `document-chunk-${sectionIndex + 1}-table-group-${grouped.length + 1}-${groupIndex + 1}`,
|
|
14227
|
+
blockCount: leaves.reduce((count, leaf)=>count + work_document_windowing_positiveInteger(leaf.attrs?.blockCount, 1), 0),
|
|
14228
|
+
estimatedHeight: roundHeight(leaves.reduce((height, leaf)=>height + (finitePositiveNumber(leaf.attrs?.estimatedHeight) ?? 1), 0)),
|
|
14229
|
+
windowContainer: true,
|
|
14230
|
+
...null !== integrityFeatures ? {
|
|
14231
|
+
integrityFeatures
|
|
14232
|
+
} : {}
|
|
14233
|
+
},
|
|
14234
|
+
content: leaves
|
|
14235
|
+
});
|
|
14236
|
+
}
|
|
14237
|
+
index = end;
|
|
14238
|
+
}
|
|
14239
|
+
return grouped;
|
|
14240
|
+
}
|
|
14241
|
+
function virtualTableChunkId(node) {
|
|
14242
|
+
const table = node?.type === 'documentChunk' ? node.content?.[0] : void 0;
|
|
14243
|
+
return table?.type === 'table' && 'string' == typeof table.attrs?.virtualTableId ? table.attrs.virtualTableId : null;
|
|
14244
|
+
}
|
|
14245
|
+
function splitLargeDocumentTable(node, sectionIndex, blockIndex, options) {
|
|
14246
|
+
const rows = node.content ?? [];
|
|
14247
|
+
if ('table' !== node.type || rows.length < options.tableRowThreshold) return [
|
|
14248
|
+
node
|
|
14249
|
+
];
|
|
14250
|
+
const sliceCount = Math.ceil(rows.length / options.tableRowSize);
|
|
14251
|
+
const tableId = `document-table-${sectionIndex + 1}-${blockIndex + 1}`;
|
|
14252
|
+
return Array.from({
|
|
14253
|
+
length: sliceCount
|
|
14254
|
+
}, (_, index)=>({
|
|
14255
|
+
...node,
|
|
14256
|
+
attrs: {
|
|
14257
|
+
...node.attrs ?? {},
|
|
14258
|
+
virtualTableId: tableId,
|
|
14259
|
+
virtualTableIndex: index,
|
|
14260
|
+
virtualTableCount: sliceCount
|
|
14261
|
+
},
|
|
14262
|
+
content: rows.slice(index * options.tableRowSize, (index + 1) * options.tableRowSize)
|
|
14263
|
+
}));
|
|
14264
|
+
}
|
|
14265
|
+
function materializeWindowedSection(section) {
|
|
14266
|
+
const flattened = flattenDocumentChunks(section.content ?? []);
|
|
14267
|
+
const content = [];
|
|
14268
|
+
for (const node of flattened){
|
|
14269
|
+
const tableId = 'table' === node.type && 'string' == typeof node.attrs?.virtualTableId ? node.attrs.virtualTableId : null;
|
|
14270
|
+
const previous = content.at(-1);
|
|
14271
|
+
if (tableId && previous?.type === 'table' && previous.attrs?.virtualTableId === tableId) {
|
|
14272
|
+
previous.content = [
|
|
14273
|
+
...previous.content ?? [],
|
|
14274
|
+
...node.content ?? []
|
|
14275
|
+
];
|
|
14276
|
+
continue;
|
|
14277
|
+
}
|
|
14278
|
+
content.push(node);
|
|
14279
|
+
}
|
|
14280
|
+
return {
|
|
14281
|
+
...section,
|
|
14282
|
+
content: content.map(stripVirtualTableAttributes)
|
|
14283
|
+
};
|
|
14284
|
+
}
|
|
14285
|
+
function flattenDocumentChunks(nodes) {
|
|
14286
|
+
return nodes.flatMap((node)=>'documentChunk' === node.type ? flattenDocumentChunks(node.content ?? []) : [
|
|
14287
|
+
materializeNestedWindowing(node)
|
|
14288
|
+
]);
|
|
14289
|
+
}
|
|
14290
|
+
function materializeNestedWindowing(node) {
|
|
14291
|
+
if (!node.content?.length) return node;
|
|
14292
|
+
return {
|
|
14293
|
+
...node,
|
|
14294
|
+
content: node.content.map(materializeNestedWindowing)
|
|
14295
|
+
};
|
|
14296
|
+
}
|
|
14297
|
+
function stripVirtualTableAttributes(node) {
|
|
14298
|
+
if ('table' !== node.type || !node.attrs?.virtualTableId) return node;
|
|
14299
|
+
const { virtualTableId: _virtualTableId, virtualTableIndex: _virtualTableIndex, virtualTableCount: _virtualTableCount, ...attrs } = node.attrs;
|
|
14300
|
+
return {
|
|
14301
|
+
...node,
|
|
14302
|
+
attrs
|
|
14303
|
+
};
|
|
14304
|
+
}
|
|
14305
|
+
function normalizeWindowingOptions(requested) {
|
|
14306
|
+
const trustedIntegrityFeatures = work_document_windowing_nonNegativeInteger(requested.trustedIntegrityFeatures);
|
|
14307
|
+
return {
|
|
14308
|
+
blockSize: work_document_windowing_positiveInteger(requested.blockSize, DEFAULT_DOCUMENT_WINDOWING_OPTIONS.blockSize),
|
|
14309
|
+
blockThreshold: work_document_windowing_positiveInteger(requested.blockThreshold, DEFAULT_DOCUMENT_WINDOWING_OPTIONS.blockThreshold),
|
|
14310
|
+
tableRowSize: work_document_windowing_positiveInteger(requested.tableRowSize, DEFAULT_DOCUMENT_WINDOWING_OPTIONS.tableRowSize),
|
|
14311
|
+
tableRowThreshold: work_document_windowing_positiveInteger(requested.tableRowThreshold, DEFAULT_DOCUMENT_WINDOWING_OPTIONS.tableRowThreshold),
|
|
14312
|
+
...null !== trustedIntegrityFeatures ? {
|
|
14313
|
+
trustedIntegrityFeatures
|
|
14314
|
+
} : {}
|
|
14315
|
+
};
|
|
14316
|
+
}
|
|
14317
|
+
function combinedDocumentChunkIntegrityFeatures(chunks) {
|
|
14318
|
+
let features = 0;
|
|
14319
|
+
for (const chunk of chunks){
|
|
14320
|
+
const value = work_document_windowing_nonNegativeInteger(chunk.attrs?.integrityFeatures);
|
|
14321
|
+
if (null === value) return null;
|
|
14322
|
+
features |= value;
|
|
14323
|
+
}
|
|
14324
|
+
return features;
|
|
14325
|
+
}
|
|
14326
|
+
function work_document_windowing_positiveInteger(value, fallback) {
|
|
14327
|
+
const number = Number(value);
|
|
14328
|
+
return Number.isSafeInteger(number) && number > 0 ? number : fallback;
|
|
14329
|
+
}
|
|
14330
|
+
function work_document_windowing_nonNegativeInteger(value) {
|
|
14331
|
+
const number = Number(value);
|
|
14332
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : null;
|
|
14333
|
+
}
|
|
14334
|
+
function finitePositiveNumber(value) {
|
|
14335
|
+
const number = Number(value);
|
|
14336
|
+
return Number.isFinite(number) && number > 0 ? number : null;
|
|
14337
|
+
}
|
|
14338
|
+
function estimatedDocumentNodeTextLines(node) {
|
|
14339
|
+
let completedLines = 0;
|
|
14340
|
+
let currentLineLength = 0;
|
|
14341
|
+
let hasText = false;
|
|
14342
|
+
const visit = (current)=>{
|
|
14343
|
+
if ('string' == typeof current.text) {
|
|
14344
|
+
const text = current.text;
|
|
14345
|
+
if (!text) return;
|
|
14346
|
+
hasText = true;
|
|
14347
|
+
let start = 0;
|
|
14348
|
+
let newline = text.indexOf('\n');
|
|
14349
|
+
while(newline >= 0){
|
|
14350
|
+
currentLineLength += newline - start;
|
|
14351
|
+
completedLines += Math.max(1, Math.ceil(currentLineLength / DOCUMENT_TEXT_CHARACTERS_PER_LINE));
|
|
14352
|
+
currentLineLength = 0;
|
|
14353
|
+
start = newline + 1;
|
|
14354
|
+
newline = text.indexOf('\n', start);
|
|
14355
|
+
}
|
|
14356
|
+
currentLineLength += text.length - start;
|
|
14357
|
+
return;
|
|
14358
|
+
}
|
|
14359
|
+
for (const child of current.content ?? [])visit(child);
|
|
14360
|
+
};
|
|
14361
|
+
visit(node);
|
|
14362
|
+
if (!hasText) return 1;
|
|
14363
|
+
return completedLines + Math.max(1, Math.ceil(currentLineLength / DOCUMENT_TEXT_CHARACTERS_PER_LINE));
|
|
14364
|
+
}
|
|
14365
|
+
function roundHeight(value) {
|
|
14366
|
+
return Math.max(1, Math.round(100 * value) / 100);
|
|
14367
|
+
}
|
|
14368
|
+
function estimateLazyDocumentLayoutBlocks(nodes, element, chunkId, firstPosition) {
|
|
14369
|
+
const blocks = [];
|
|
14370
|
+
let position = firstPosition;
|
|
14371
|
+
for(let index = 0; index < nodes.length; index += 1){
|
|
14372
|
+
const node = nodes[index];
|
|
14373
|
+
if (!node) continue;
|
|
14374
|
+
const nodeSize = simpleDocumentNodeSize(node);
|
|
14375
|
+
if (null === nodeSize) return [];
|
|
14376
|
+
const id = `${chunkId}-child-${index}-${position}`;
|
|
14377
|
+
if ('table' === node.type) blocks.push(...estimateLazyDocumentTableRows(node, element, id, position));
|
|
14378
|
+
else {
|
|
14379
|
+
const block = measuredDocumentBlock({
|
|
14380
|
+
block: {
|
|
14381
|
+
id,
|
|
14382
|
+
height: estimateDocumentNodeHeight(node),
|
|
14383
|
+
breakBefore: false,
|
|
14384
|
+
breakAfter: 'pageBreak' === node.type,
|
|
14385
|
+
keepTogether: false,
|
|
14386
|
+
keepWithNext: false
|
|
14387
|
+
},
|
|
14388
|
+
element,
|
|
14389
|
+
from: position,
|
|
14390
|
+
to: position + nodeSize
|
|
14391
|
+
});
|
|
14392
|
+
block.observeResize = false;
|
|
14393
|
+
blocks.push(block);
|
|
14394
|
+
}
|
|
14395
|
+
position += nodeSize;
|
|
14396
|
+
}
|
|
14397
|
+
return blocks;
|
|
14398
|
+
}
|
|
14399
|
+
function estimateLazyDocumentTableRows(table, element, blockId, tablePosition) {
|
|
14400
|
+
const rows = table.content ?? [];
|
|
14401
|
+
if (!rows.length) return [];
|
|
14402
|
+
const virtualTableId = 'string' == typeof table.attrs?.virtualTableId && table.attrs.virtualTableId ? table.attrs.virtualTableId : blockId;
|
|
14403
|
+
const sliceIndex = Number.isSafeInteger(table.attrs?.virtualTableIndex) ? Number(table.attrs?.virtualTableIndex) : 0;
|
|
14404
|
+
const flowId = `${virtualTableId}-slice-${sliceIndex}`;
|
|
14405
|
+
const columnCount = estimateLazyDocumentTableColumnCount(table);
|
|
14406
|
+
const tableBreak = {
|
|
14407
|
+
tableId: flowId,
|
|
14408
|
+
columnCount,
|
|
14409
|
+
colgroupHtml: '',
|
|
14410
|
+
repeatedHeaderRowsHtml: [],
|
|
14411
|
+
repeatedHeaderOverlayHtml: '',
|
|
14412
|
+
repeatHeaderHeight: 0,
|
|
14413
|
+
tableWidth: 0,
|
|
14414
|
+
leadingCellOffsetLeft: 0
|
|
14415
|
+
};
|
|
14416
|
+
const result = [];
|
|
14417
|
+
let rowPosition = tablePosition + 1;
|
|
14418
|
+
for(let rowIndex = 0; rowIndex < rows.length; rowIndex += 1){
|
|
14419
|
+
const row = rows[rowIndex];
|
|
14420
|
+
if (!row) continue;
|
|
14421
|
+
const rowSize = simpleDocumentNodeSize(row);
|
|
14422
|
+
if (null === rowSize) return [];
|
|
14423
|
+
result.push({
|
|
14424
|
+
block: {
|
|
14425
|
+
id: `${flowId}-row-${rowIndex}`,
|
|
14426
|
+
height: estimateDocumentTableRowHeight(row),
|
|
14427
|
+
flowId,
|
|
14428
|
+
flowIndex: rowIndex,
|
|
14429
|
+
flowCount: rows.length,
|
|
14430
|
+
minimumFragmentsPerPage: 1
|
|
14431
|
+
},
|
|
14432
|
+
element,
|
|
14433
|
+
from: rowPosition,
|
|
14434
|
+
to: rowPosition + rowSize,
|
|
14435
|
+
inlineOffsetLeft: 0,
|
|
14436
|
+
inlineOffsetRight: 0,
|
|
14437
|
+
observeResize: false,
|
|
14438
|
+
selectionRanges: estimateLazyDocumentTableRowSelectionRanges(row, rowPosition),
|
|
14439
|
+
tableBreak
|
|
14440
|
+
});
|
|
14441
|
+
rowPosition += rowSize;
|
|
14442
|
+
}
|
|
14443
|
+
return result;
|
|
14444
|
+
}
|
|
14445
|
+
function estimateLazyDocumentTableRowSelectionRanges(row, rowPosition) {
|
|
14446
|
+
const ranges = [];
|
|
14447
|
+
let cellPosition = rowPosition + 1;
|
|
14448
|
+
for (const cell of row.content ?? []){
|
|
14449
|
+
const cellSize = simpleDocumentNodeSize(cell);
|
|
14450
|
+
if (null === cellSize) return [];
|
|
14451
|
+
ranges.push({
|
|
14452
|
+
from: cellPosition + 1,
|
|
14453
|
+
to: Math.max(cellPosition + 1, cellPosition + cellSize - 1)
|
|
14454
|
+
});
|
|
14455
|
+
cellPosition += cellSize;
|
|
14456
|
+
}
|
|
14457
|
+
return ranges;
|
|
14458
|
+
}
|
|
14459
|
+
function estimateLazyDocumentTableColumnCount(table) {
|
|
14460
|
+
let maximum = 1;
|
|
14461
|
+
for (const row of table.content ?? []){
|
|
14462
|
+
let columns = 0;
|
|
14463
|
+
for (const cell of row.content ?? []){
|
|
14464
|
+
const colspan = Number(cell.attrs?.colspan);
|
|
14465
|
+
columns += Number.isSafeInteger(colspan) && colspan > 0 ? colspan : 1;
|
|
14466
|
+
}
|
|
14467
|
+
maximum = Math.max(maximum, columns);
|
|
14468
|
+
}
|
|
14469
|
+
return maximum;
|
|
14470
|
+
}
|
|
12425
14471
|
const MAX_DOCUMENT_TEXT_LAYOUT_PARAGRAPHS = 16384;
|
|
12426
14472
|
const MAX_DOCUMENT_TEXT_LAYOUT_RUNS = 16384;
|
|
12427
14473
|
const MAX_DOCUMENT_TEXT_LAYOUT_BYTES = 1048576;
|
|
@@ -12442,6 +14488,7 @@ function collectDocumentTextLayoutParagraphs(editor, fonts, loadedFontIds, previ
|
|
|
12442
14488
|
let textBytes = 0;
|
|
12443
14489
|
const collectParagraph = (node, element, id, position)=>{
|
|
12444
14490
|
if (paragraphs.length >= MAX_DOCUMENT_TEXT_LAYOUT_PARAGRAPHS) return;
|
|
14491
|
+
if (!isDocumentLineFragmentNode(node)) return;
|
|
12445
14492
|
if (reusableDocumentLayoutBlocks(previous?.blocks ?? [], id, element, position + node.nodeSize, dirtyFrom).length) return;
|
|
12446
14493
|
const paragraph = documentTextLayoutParagraph(node, element, id, fonts, loadedFontIds);
|
|
12447
14494
|
if (!paragraph) return;
|
|
@@ -13567,7 +15614,9 @@ async function measureDocumentLayoutBlocksIncrementally(editor, previous = null,
|
|
|
13567
15614
|
const pageStyleByMetrics = new Map();
|
|
13568
15615
|
const counts = {
|
|
13569
15616
|
measured: 0,
|
|
13570
|
-
|
|
15617
|
+
prefixOpen: true,
|
|
15618
|
+
reused: 0,
|
|
15619
|
+
reusedPrefix: 0
|
|
13571
15620
|
};
|
|
13572
15621
|
const checkpoint = options.checkpoint ?? createDocumentLayoutMeasurementCheckpoint();
|
|
13573
15622
|
throwIfDocumentMeasurementAborted(options.signal);
|
|
@@ -13599,6 +15648,7 @@ async function measureDocumentLayoutBlocksIncrementally(editor, previous = null,
|
|
|
13599
15648
|
],
|
|
13600
15649
|
measuredBlockCount: counts.measured,
|
|
13601
15650
|
reusedBlockCount: counts.reused,
|
|
15651
|
+
reusedPrefixBlockCount: counts.reusedPrefix,
|
|
13602
15652
|
unsupportedLayout
|
|
13603
15653
|
};
|
|
13604
15654
|
}
|
|
@@ -13640,42 +15690,54 @@ function measureSectionBlock(editor, node, offset, index, sectionPosition, previ
|
|
|
13640
15690
|
const element = elementForNode(editor, position);
|
|
13641
15691
|
if (!element) return;
|
|
13642
15692
|
const id = documentBlockId(sectionPosition, index, position);
|
|
15693
|
+
if ('documentChunk' === node.type.name) {
|
|
15694
|
+
const reused = reusableDocumentChunkLayoutBlocks(previous, element, position, position + node.nodeSize, dirtyFrom);
|
|
15695
|
+
if (reused.length) {
|
|
15696
|
+
recordReusedDocumentBlocks(counts, reused.length);
|
|
15697
|
+
sectionBlocks.push(...reused);
|
|
15698
|
+
return;
|
|
15699
|
+
}
|
|
15700
|
+
const estimated = estimatedDocumentChunkBlocks(editor, node, element, id, position);
|
|
15701
|
+
recordMeasuredDocumentBlocks(counts, estimated.length);
|
|
15702
|
+
sectionBlocks.push(...estimated);
|
|
15703
|
+
return;
|
|
15704
|
+
}
|
|
13643
15705
|
const paragraphPagination = documentNodeParagraphPagination(node);
|
|
13644
15706
|
if (isDocumentListNode(node)) {
|
|
13645
15707
|
const reused = reusableDocumentListLayoutBlocks(previous, id, position + node.nodeSize, dirtyFrom);
|
|
13646
15708
|
if (reused.length) {
|
|
13647
|
-
counts
|
|
15709
|
+
recordReusedDocumentBlocks(counts, reused.length);
|
|
13648
15710
|
sectionBlocks.push(...reused);
|
|
13649
15711
|
return;
|
|
13650
15712
|
}
|
|
13651
15713
|
const listBlocks = measureDocumentListBlocks(editor, node, element, id, position, textLayouts, maximumFragmentedTableRowHeight);
|
|
13652
15714
|
if (listBlocks.length) {
|
|
13653
|
-
counts
|
|
15715
|
+
recordMeasuredDocumentBlocks(counts, listBlocks.length);
|
|
13654
15716
|
sectionBlocks.push(...listBlocks);
|
|
13655
15717
|
return;
|
|
13656
15718
|
}
|
|
13657
15719
|
}
|
|
13658
15720
|
const reused = reusableDocumentLayoutBlocks(previous, id, element, position + node.nodeSize, dirtyFrom);
|
|
13659
15721
|
if (reused.length) {
|
|
13660
|
-
counts
|
|
15722
|
+
recordReusedDocumentBlocks(counts, reused.length);
|
|
13661
15723
|
sectionBlocks.push(...reused);
|
|
13662
15724
|
return;
|
|
13663
15725
|
}
|
|
13664
15726
|
if ('table' === node.type.name) {
|
|
13665
15727
|
const tableRows = measureDocumentTableRows(editor, node, element, id, position, maximumFragmentedTableRowHeight);
|
|
13666
15728
|
if (tableRows.length) {
|
|
13667
|
-
counts
|
|
15729
|
+
recordMeasuredDocumentBlocks(counts, tableRows.length);
|
|
13668
15730
|
sectionBlocks.push(...tableRows);
|
|
13669
15731
|
return;
|
|
13670
15732
|
}
|
|
13671
15733
|
}
|
|
13672
15734
|
const lineFragments = measureParagraphLineFragments(editor, node, element, id, position, position + node.nodeSize, paragraphPagination, textLayouts.get(id));
|
|
13673
15735
|
if (lineFragments.length > 1) {
|
|
13674
|
-
counts
|
|
15736
|
+
recordMeasuredDocumentBlocks(counts, lineFragments.length);
|
|
13675
15737
|
sectionBlocks.push(...lineFragments);
|
|
13676
15738
|
return;
|
|
13677
15739
|
}
|
|
13678
|
-
counts
|
|
15740
|
+
recordMeasuredDocumentBlocks(counts, 1);
|
|
13679
15741
|
sectionBlocks.push(measuredDocumentBlock({
|
|
13680
15742
|
block: {
|
|
13681
15743
|
id,
|
|
@@ -13690,6 +15752,126 @@ function measureSectionBlock(editor, node, offset, index, sectionPosition, previ
|
|
|
13690
15752
|
to: position + node.nodeSize
|
|
13691
15753
|
}));
|
|
13692
15754
|
}
|
|
15755
|
+
function estimatedDocumentChunkBlocks(editor, chunk, element, chunkId, chunkPosition) {
|
|
15756
|
+
const blocks = [];
|
|
15757
|
+
chunk.forEach((node, offset, index)=>{
|
|
15758
|
+
const position = chunkPosition + offset + 1;
|
|
15759
|
+
const id = `${chunkId}-child-${index}-${position}`;
|
|
15760
|
+
if ('documentChunk' === node.type.name) return void blocks.push(...estimatedDocumentChunkBlocks(editor, node, element, id, position));
|
|
15761
|
+
if ('documentLazyBlock' === node.type.name) {
|
|
15762
|
+
const sourceId = 'string' == typeof chunk.attrs.id ? chunk.attrs.id : '';
|
|
15763
|
+
const payload = documentLazyChunkContentForEditor(editor, sourceId);
|
|
15764
|
+
if (payload?.length) blocks.push(...estimateLazyDocumentLayoutBlocks(payload, element, chunkId, position));
|
|
15765
|
+
return;
|
|
15766
|
+
}
|
|
15767
|
+
if ('table' === node.type.name) return void blocks.push(...estimatedDocumentTableRows(node, element, id, position));
|
|
15768
|
+
const pagination = documentNodeParagraphPagination(node);
|
|
15769
|
+
const block = measuredDocumentBlock({
|
|
15770
|
+
block: {
|
|
15771
|
+
id,
|
|
15772
|
+
height: estimatedProseMirrorNodeHeight(node),
|
|
15773
|
+
breakBefore: pagination.pageBreakBefore,
|
|
15774
|
+
breakAfter: 'pageBreak' === node.type.name,
|
|
15775
|
+
keepTogether: pagination.keepLines || shouldKeepDocumentBlockTogether(node),
|
|
15776
|
+
keepWithNext: pagination.keepWithNext
|
|
15777
|
+
},
|
|
15778
|
+
element,
|
|
15779
|
+
from: position,
|
|
15780
|
+
to: position + node.nodeSize
|
|
15781
|
+
});
|
|
15782
|
+
block.observeResize = false;
|
|
15783
|
+
blocks.push(block);
|
|
15784
|
+
});
|
|
15785
|
+
return blocks;
|
|
15786
|
+
}
|
|
15787
|
+
function estimatedDocumentTableRows(table, element, blockId, tablePosition) {
|
|
15788
|
+
const rowCount = table.childCount;
|
|
15789
|
+
if (!rowCount) return [];
|
|
15790
|
+
const virtualTableId = 'string' == typeof table.attrs.virtualTableId && table.attrs.virtualTableId ? table.attrs.virtualTableId : blockId;
|
|
15791
|
+
const sliceIndex = Number.isSafeInteger(table.attrs.virtualTableIndex) ? Number(table.attrs.virtualTableIndex) : 0;
|
|
15792
|
+
const flowId = `${virtualTableId}-slice-${sliceIndex}`;
|
|
15793
|
+
const columnCount = estimatedDocumentTableColumnCount(table);
|
|
15794
|
+
const tableBreak = {
|
|
15795
|
+
tableId: flowId,
|
|
15796
|
+
columnCount,
|
|
15797
|
+
colgroupHtml: '',
|
|
15798
|
+
repeatedHeaderRowsHtml: [],
|
|
15799
|
+
repeatedHeaderOverlayHtml: '',
|
|
15800
|
+
repeatHeaderHeight: 0,
|
|
15801
|
+
tableWidth: 0,
|
|
15802
|
+
leadingCellOffsetLeft: 0
|
|
15803
|
+
};
|
|
15804
|
+
const result = [];
|
|
15805
|
+
table.forEach((row, offset, rowIndex)=>{
|
|
15806
|
+
const position = tablePosition + offset + 1;
|
|
15807
|
+
result.push({
|
|
15808
|
+
block: {
|
|
15809
|
+
id: `${flowId}-row-${rowIndex}`,
|
|
15810
|
+
height: estimatedProseMirrorTableRowHeight(row),
|
|
15811
|
+
flowId,
|
|
15812
|
+
flowIndex: rowIndex,
|
|
15813
|
+
flowCount: rowCount,
|
|
15814
|
+
minimumFragmentsPerPage: 1
|
|
15815
|
+
},
|
|
15816
|
+
element,
|
|
15817
|
+
from: position,
|
|
15818
|
+
to: position + row.nodeSize,
|
|
15819
|
+
inlineOffsetLeft: 0,
|
|
15820
|
+
inlineOffsetRight: 0,
|
|
15821
|
+
observeResize: false,
|
|
15822
|
+
selectionRanges: estimatedDocumentTableRowSelectionRanges(row, position),
|
|
15823
|
+
tableBreak
|
|
15824
|
+
});
|
|
15825
|
+
});
|
|
15826
|
+
return result;
|
|
15827
|
+
}
|
|
15828
|
+
function estimatedDocumentTableRowSelectionRanges(row, rowPosition) {
|
|
15829
|
+
const ranges = [];
|
|
15830
|
+
row.forEach((cell, offset)=>{
|
|
15831
|
+
const cellPosition = rowPosition + offset + 1;
|
|
15832
|
+
ranges.push({
|
|
15833
|
+
from: cellPosition + 1,
|
|
15834
|
+
to: Math.max(cellPosition + 1, cellPosition + cell.nodeSize - 1)
|
|
15835
|
+
});
|
|
15836
|
+
});
|
|
15837
|
+
return ranges;
|
|
15838
|
+
}
|
|
15839
|
+
function estimatedDocumentTableColumnCount(table) {
|
|
15840
|
+
let maximum = 1;
|
|
15841
|
+
table.forEach((row)=>{
|
|
15842
|
+
let columns = 0;
|
|
15843
|
+
row.forEach((cell)=>{
|
|
15844
|
+
const colspan = Number(cell.attrs.colspan);
|
|
15845
|
+
columns += Number.isSafeInteger(colspan) && colspan > 0 ? colspan : 1;
|
|
15846
|
+
});
|
|
15847
|
+
maximum = Math.max(maximum, columns);
|
|
15848
|
+
});
|
|
15849
|
+
return maximum;
|
|
15850
|
+
}
|
|
15851
|
+
function estimatedProseMirrorTableRowHeight(row) {
|
|
15852
|
+
const explicit = Number(row.attrs.rowHeight);
|
|
15853
|
+
let maximumTextLength = 0;
|
|
15854
|
+
row.forEach((cell)=>{
|
|
15855
|
+
maximumTextLength = Math.max(maximumTextLength, cell.textContent.length);
|
|
15856
|
+
});
|
|
15857
|
+
const lines = Math.max(1, Math.ceil(maximumTextLength / 88));
|
|
15858
|
+
return Math.max(22, Number.isFinite(explicit) && explicit > 0 ? explicit : 0, 21 * lines + 1);
|
|
15859
|
+
}
|
|
15860
|
+
function estimatedProseMirrorNodeHeight(node) {
|
|
15861
|
+
const lines = Math.max(1, Math.ceil(node.textContent.length / 88));
|
|
15862
|
+
if ('heading' === node.type.name) {
|
|
15863
|
+
const level = Number(node.attrs.level);
|
|
15864
|
+
const lineHeight = level <= 1 ? 32 : 2 === level ? 28 : 25;
|
|
15865
|
+
return lines * lineHeight + 18;
|
|
15866
|
+
}
|
|
15867
|
+
if ('image' === node.type.name) {
|
|
15868
|
+
const height = Number(node.attrs.height);
|
|
15869
|
+
return Math.max(21, (Number.isFinite(height) && height > 0 ? height : 120) + 36);
|
|
15870
|
+
}
|
|
15871
|
+
if ('horizontalRule' === node.type.name || 'pageBreak' === node.type.name) return 38;
|
|
15872
|
+
if ('bulletList' === node.type.name || 'orderedList' === node.type.name) return Math.max(29, 29 * node.childCount);
|
|
15873
|
+
return 21 * lines + 8;
|
|
15874
|
+
}
|
|
13693
15875
|
function finishMeasuredSection(section, sectionPosition, sectionIndex, layout, pageStyle, sectionBlocks, result) {
|
|
13694
15876
|
const last = sectionBlocks.at(-1);
|
|
13695
15877
|
if (last && 'continuous' !== layout.breakAfter && 'nextColumn' !== layout.breakAfter) last.block.breakAfter = true;
|
|
@@ -13707,6 +15889,16 @@ function finishMeasuredSection(section, sectionPosition, sectionIndex, layout, p
|
|
|
13707
15889
|
}
|
|
13708
15890
|
result.push(...sectionBlocks);
|
|
13709
15891
|
}
|
|
15892
|
+
function recordReusedDocumentBlocks(counts, count) {
|
|
15893
|
+
if (count <= 0) return;
|
|
15894
|
+
counts.reused += count;
|
|
15895
|
+
if (counts.prefixOpen) counts.reusedPrefix += count;
|
|
15896
|
+
}
|
|
15897
|
+
function recordMeasuredDocumentBlocks(counts, count) {
|
|
15898
|
+
if (count <= 0) return;
|
|
15899
|
+
counts.measured += count;
|
|
15900
|
+
counts.prefixOpen = false;
|
|
15901
|
+
}
|
|
13710
15902
|
function createDocumentLayoutMeasurementCheckpoint() {
|
|
13711
15903
|
let deadline = documentMeasurementNow() + DOCUMENT_LAYOUT_MEASUREMENT_SLICE_MS;
|
|
13712
15904
|
return async (signal)=>{
|
|
@@ -15437,6 +17629,33 @@ const DocumentTable = Table.extend({
|
|
|
15437
17629
|
renderHTML: (attributes)=>attributes.officeImported ? {
|
|
15438
17630
|
'data-office-table-imported': 'true'
|
|
15439
17631
|
} : {}
|
|
17632
|
+
},
|
|
17633
|
+
virtualTableId: {
|
|
17634
|
+
default: null,
|
|
17635
|
+
parseHTML: (element)=>element.dataset.documentVirtualTableId || null,
|
|
17636
|
+
renderHTML: (attributes)=>'string' == typeof attributes.virtualTableId && attributes.virtualTableId ? {
|
|
17637
|
+
'data-document-virtual-table-id': attributes.virtualTableId
|
|
17638
|
+
} : {}
|
|
17639
|
+
},
|
|
17640
|
+
virtualTableIndex: {
|
|
17641
|
+
default: null,
|
|
17642
|
+
parseHTML: (element)=>nonNegativeSafeInteger(element.dataset.documentVirtualTableIndex),
|
|
17643
|
+
renderHTML: (attributes)=>{
|
|
17644
|
+
const index = nonNegativeSafeInteger(attributes.virtualTableIndex);
|
|
17645
|
+
return null === index ? {} : {
|
|
17646
|
+
'data-document-virtual-table-index': String(index)
|
|
17647
|
+
};
|
|
17648
|
+
}
|
|
17649
|
+
},
|
|
17650
|
+
virtualTableCount: {
|
|
17651
|
+
default: null,
|
|
17652
|
+
parseHTML: (element)=>positiveSafeInteger(element.dataset.documentVirtualTableCount),
|
|
17653
|
+
renderHTML: (attributes)=>{
|
|
17654
|
+
const count = positiveSafeInteger(attributes.virtualTableCount);
|
|
17655
|
+
return null === count ? {} : {
|
|
17656
|
+
'data-document-virtual-table-count': String(count)
|
|
17657
|
+
};
|
|
17658
|
+
}
|
|
15440
17659
|
}
|
|
15441
17660
|
};
|
|
15442
17661
|
}
|
|
@@ -15445,6 +17664,15 @@ const DocumentTable = Table.extend({
|
|
|
15445
17664
|
allowTableNodeSelection: true,
|
|
15446
17665
|
View: WorkDocumentTableView
|
|
15447
17666
|
});
|
|
17667
|
+
function nonNegativeSafeInteger(value) {
|
|
17668
|
+
if (null == value || '' === value) return null;
|
|
17669
|
+
const number = Number(value);
|
|
17670
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : null;
|
|
17671
|
+
}
|
|
17672
|
+
function positiveSafeInteger(value) {
|
|
17673
|
+
const number = Number(value);
|
|
17674
|
+
return Number.isSafeInteger(number) && number > 0 ? number : null;
|
|
17675
|
+
}
|
|
15448
17676
|
const DocumentTableSizing = Extension.create({
|
|
15449
17677
|
name: 'documentTableSizing',
|
|
15450
17678
|
addCommands () {
|
|
@@ -16230,6 +18458,13 @@ function createWorkDocumentExtensions(options = {}) {
|
|
|
16230
18458
|
DocumentBulletList,
|
|
16231
18459
|
DocumentOrderedList,
|
|
16232
18460
|
DocumentListCommands,
|
|
18461
|
+
DocumentLazyBlock,
|
|
18462
|
+
DocumentChunk.configure({
|
|
18463
|
+
...options.getContent ? {
|
|
18464
|
+
getContent: options.getContent
|
|
18465
|
+
} : {},
|
|
18466
|
+
trustInitialIntegrityFeatures: Boolean(options.trustInitialIntegrityFeatures)
|
|
18467
|
+
}),
|
|
16233
18468
|
DocumentSection,
|
|
16234
18469
|
DocumentBookmarkBoundary,
|
|
16235
18470
|
DocumentCaption,
|
|
@@ -16299,13 +18534,13 @@ function workDocumentSchema() {
|
|
|
16299
18534
|
return work_document_model_codec_schema;
|
|
16300
18535
|
}
|
|
16301
18536
|
function serializeWorkDocumentNode(root) {
|
|
16302
|
-
return generateHTML(root, documentExtensions());
|
|
18537
|
+
return generateHTML(materializeWindowedDocumentModel(root), documentExtensions());
|
|
16303
18538
|
}
|
|
16304
18539
|
function createWorkDocumentModelFromContent(content) {
|
|
16305
18540
|
const previous = content.model;
|
|
16306
18541
|
const synchronized = syncDocumentContentFromHtml(content, content.html);
|
|
16307
18542
|
const document1 = new DOMParser().parseFromString(synchronized.html, 'text/html');
|
|
16308
|
-
const root = model_DOMParser.fromSchema(workDocumentSchema()).parse(document1.body).toJSON();
|
|
18543
|
+
const root = windowDocumentModel(model_DOMParser.fromSchema(workDocumentSchema()).parse(document1.body).toJSON());
|
|
16309
18544
|
return {
|
|
16310
18545
|
...synchronized,
|
|
16311
18546
|
model: createWorkDocumentModel(synchronized.html, root, previous)
|
|
@@ -18082,4 +20317,14 @@ function capturePages(surface) {
|
|
|
18082
20317
|
width: surface.pageWidth
|
|
18083
20318
|
}));
|
|
18084
20319
|
}
|
|
18085
|
-
|
|
20320
|
+
const documentPageSurfaceProviders = new WeakMap();
|
|
20321
|
+
function registerDocumentPageSurfaceGeometry(element, provider) {
|
|
20322
|
+
documentPageSurfaceProviders.set(element, provider);
|
|
20323
|
+
return ()=>{
|
|
20324
|
+
if (documentPageSurfaceProviders.get(element) === provider) documentPageSurfaceProviders.delete(element);
|
|
20325
|
+
};
|
|
20326
|
+
}
|
|
20327
|
+
function documentPageSurfaceGeometryForElement(element) {
|
|
20328
|
+
return documentPageSurfaceProviders.get(element)?.() ?? null;
|
|
20329
|
+
}
|
|
20330
|
+
export { DEFAULT_DOCUMENT_TABLE_CELL_FORMAT, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, DEFAULT_DOCUMENT_TABLE_GEOMETRY, DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE, DOCUMENT_CHUNK_HYDRATION_META, DOCUMENT_CHUNK_PAGINATION_META, DOCUMENT_CHUNK_VISIBLE_IDS_META, DOCUMENT_PAGE_BORDER_EDGES, DOCUMENT_PAGE_MARGIN_KEYS, DOCUMENT_PARAGRAPH_BORDER_EDGES, DOCUMENT_PARAGRAPH_BORDER_STYLES, DOCUMENT_PARAGRAPH_SHADING_PATTERNS, DOCUMENT_TABLE_ROW_ID_ATTRIBUTE, DOCUMENT_TABLE_ROW_TEXT_ID_ATTRIBUTE, DOCUMENT_TABLE_STYLE_OPTIONS, DocumentEquation, DocumentImage, DocumentParagraphFormatting, DocumentParagraphIdentity, DocumentSubscript, DocumentSuperscript, DocumentTableRowIdentity, DocxThemePatchCollector, MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT, MAX_DOCUMENT_NUMBERING_START, activeDocumentBookmark, activeDocumentSection, activeDocumentTableStyle, applyDocumentImageCropToElement, applyDocumentImageIdentityToElement, applyDocumentImageLayerToElement, applyDocumentImageWrapContourToElement, applyDocumentPageGeometry, applyDocumentParagraphIdentityToElement, applyDocumentTableGeometryToElement, applyDocumentTableRowIdentityToElement, canChangeDocumentIndent, canInsertDocumentComment, canSetDocumentTableRowRepeatHeader, clampDocumentMargin, collectDocumentChanges, collectDocumentCommentAnchors, collectDocumentNotes, collectDocumentTextLayoutParagraphs, createDocumentBibliography, createDocumentEquationElement, createDocumentImageIdentityRegistry, createDocumentNoteElement, createDocumentParagraphIdentityRegistry, createSchemaDerivedWorkDocumentModel, createSchemaValidatedWorkDocumentModel, createWorkDocumentExtensions, createWorkDocumentModel, createWorkDocumentModelFromContent, createWorkOfficeDocumentCollaborationBinding as createOfficeDocumentCollaborationBinding, defaultDocumentImageWrapContour, documentAutoLineHeight, documentBookmarkNameExists, documentBookmarkReferenceInstruction, documentBulletListStyle, documentCaptionKind, documentCaptionLabel, documentChunkMountedIds, documentCitationCount, documentCitationInstruction, documentCitationStyle, documentCitationStyleDetails, documentCitationTags, documentCitationTagsFromInstruction, documentCommentDraftRange, documentCommentViews, documentContentLayoutProperties, documentEquationFromElement, documentEquationText, documentImageCropFromElement, documentImageIdentityFromElement, documentImageLayerFromElement, documentImageLayoutFromElement, documentImageLayoutOptions, documentImagePositionFromElement, documentImageProperties, documentImageWrapContourFromElement, documentInitialSectionLayout, documentLazyHtmlChunkFragment, documentLazyHtmlProjection, documentLazyHtmlProjectionFingerprint, documentModelForContent, documentModelForHtml, documentModelHasTrustedInitialIntegrityFeatures, documentModelUsesWindowing, documentNoteKey, documentNoteKind, documentOrderedListState, documentPageBordersVisible, documentPageChromeLegacyFields, documentPageGeometryForLayout, documentPageHorizontalMarginTwips, documentPageMarginBody, documentPageMarginsForLayout, documentPageMetrics, documentPageSurfaceGeometryForElement, documentPaperSizeForGeometry, documentParagraphBordersDomAttributes, documentParagraphDirection, documentParagraphIdentityFromElement, documentParagraphIndent, documentParagraphPagination, documentParagraphShadingDomAttributes, documentParagraphSpacing, documentParagraphTabStops, documentSectionById, documentSectionDomAttributes, documentSections, documentTabLeaderLabel, documentTableBordersFromElement, documentTableCellFormat, documentTableCellMarginOverridesFromElement, documentTableColumnPercentagesFromElement, documentTableGeometryFromElement, documentTableHorizontalAlignment, documentTableRowIdentityFromElement, documentTableRowOptions, documentTableSizing, documentTextLayoutBatches, documentTextStatistics, documentTransactionsOnlyHydrateChunks, documentWordLineHeightFactor, docxBookmarkReferenceTarget, docxDocumentFieldKind, editorDocumentBookmarkReferenceTargets, editorDocumentCaptionTargets, importedDocumentCharacterFormatting, initializeWorkOfficeDocumentCollaboration as initializeOfficeDocumentCollaboration, invalidateDocumentLazyHtmlProjection, isContourImageLayout, isDocumentParagraphArtBorderStyle, isValidDocumentCitationTag, materializeLazyDocumentEditorRoot, materializeWorkDocumentContent, measureDocumentLayoutBlocksIncrementally, millimetersToPixels, mountWorkLiveDocumentCapture, nextDocumentTabAlignment, normalizeDocumentBookmarkName, normalizeDocumentBookmarkNativeId, normalizeDocumentBookmarkReferencesHtml, normalizeDocumentBookmarksHtml, normalizeDocumentCaptionsHtml, normalizeDocumentCitationsHtml, normalizeDocumentColumns, normalizeDocumentEquation, normalizeDocumentFieldsHtml, normalizeDocumentHtml, normalizeDocumentImageAlignment, normalizeDocumentImageCrop, normalizeDocumentImageIdentity, normalizeDocumentImageLayer, normalizeDocumentImageLayoutOptions, normalizeDocumentImagePosition, normalizeDocumentImageWrapContour, normalizeDocumentImageWrapSide, normalizeDocumentNotesHtml, normalizeDocumentPageBorders, normalizeDocumentPageChrome, normalizeDocumentPageGeometry, normalizeDocumentPageMargins, normalizeDocumentPaperSource, normalizeDocumentParagraphBorder, normalizeDocumentParagraphBorders, normalizeDocumentParagraphId, normalizeDocumentParagraphIdentity, normalizeDocumentParagraphIndent, normalizeDocumentTabStops, normalizeDocumentTableBorderStyle, normalizeDocumentTableBorderWidth, normalizeDocumentTableRowHeightRule, normalizeDocumentTableRowIdentity, normalizeDocumentTableVerticalAlign, normalizeTableColor, normalizedTabPosition, pageTwipsToMillimeters, parseDocumentCharacterFormatting, parseDocumentParagraphBordersElement, parseDocumentParagraphFormatting, parseDocumentParagraphShadingElement, parseDocxThemeReference, patchDocumentLazyHtmlProjection, patchDocxThemeReferences, positionWorkLiveDocumentCapture, readWorkOfficeDocumentCollaboration as readOfficeDocumentCollaboration, registerDocumentPageSurfaceGeometry, renderDocumentAutoLineHeight, renderDocumentTableBorders, renderDocumentTableCellMarginOverrides, resolveDocumentPageBorders, resolveDocumentPageChrome, resolveDocumentPageMargins, resolveDocumentPageSize, resolveWorkDocumentEditorInput, retainAnchoredDocumentComments, sanitizeDocumentPageChromeHtml, selectedDocumentChunkId, serializeDocumentParagraphFormatting, serializeDocumentTabStops, serializeDocxThemeReference, serializeWorkDocumentNode, setCustomDocumentColumns, supportedDocxBookmarkReferenceInstruction, syncDocumentContentFromHtml, transferChangedDocumentTextStatistics, uniqueDocumentImageIdentity, uniqueDocumentParagraphIdentity, updateDocumentColumnWidth, updateDocumentCustomPageMillimeters, updateDocumentGutterPosition, updateDocumentMirrorMargins, updateDocumentPageChromeVariant, updateDocumentPageMarginMillimeters, updateDocumentPageMarginMode, updateDocumentPageOrientation, updateDocumentPaperSizePreset, validateDocumentBookmarkName, windowDocumentModel, workDocumentSchema, workOfficeDocumentCollaborationFragment as officeDocumentCollaborationFragment, work_document_page_margins_twipsToMillimeters, wrapsBesideImage };
|