@a3s-lab/office 0.46.0 → 0.47.0

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/dist/4174.js CHANGED
@@ -215,1617 +215,1898 @@ function normalizedReferenceText(value) {
215
215
  function stringAttribute(value) {
216
216
  return 'string' == typeof value ? value.trim() : '';
217
217
  }
218
- const BOOKMARK_NAME_PATTERN = /^[\p{L}_][\p{L}\p{N}_]*$/u;
219
- const BOOKMARK_UI_NAME_PATTERN = /^[\p{L}][\p{L}\p{N}_]*$/u;
220
- const MAX_BOOKMARK_NAME_LENGTH = 40;
221
- const MAX_BOOKMARK_NATIVE_ID = 0x7fffffff;
222
- const MISSING_LINK_CLASS = 'work-document-link-missing';
223
- const DOCUMENT_BOOKMARK_VALIDATION_MESSAGE = '书签名称需以字母开头,只能包含字母、数字和下划线,且不超过 40 个字符。';
224
- const DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE = '文档中已存在同名书签。';
225
- const DocumentBookmarkBoundary = core_Node.create({
226
- name: 'documentBookmarkBoundary',
227
- inline: true,
228
- group: 'inline',
229
- atom: true,
230
- selectable: false,
231
- addCommands () {
232
- return {
233
- insertDocumentBookmark: (name)=>(props)=>insertDocumentBookmarkCommand(props, name),
234
- deleteDocumentBookmark: (id)=>(props)=>deleteDocumentBookmarkCommand(props, id)
235
- };
236
- },
237
- addProseMirrorPlugins () {
238
- return [
239
- createDocumentBookmarkPlugin(this.name)
240
- ];
241
- },
242
- addAttributes () {
243
- return {
244
- id: {
245
- default: '',
246
- parseHTML: (element)=>element.dataset.bookmarkId ?? '',
247
- renderHTML: ()=>({})
248
- },
249
- name: {
250
- default: '',
251
- parseHTML: (element)=>element.dataset.bookmarkName ?? '',
252
- renderHTML: ()=>({})
253
- },
254
- nativeId: {
255
- default: null,
256
- parseHTML: (element)=>normalizeDocumentBookmarkNativeId(element.dataset.officeBookmarkId),
257
- renderHTML: ()=>({})
258
- },
259
- kind: {
260
- default: 'start',
261
- parseHTML: (element)=>documentBookmarkBoundaryKind(element.dataset.bookmarkKind),
262
- renderHTML: ()=>({})
263
- }
264
- };
265
- },
266
- parseHTML () {
267
- return [
268
- {
269
- tag: 'span[data-document-bookmark-boundary]',
270
- getAttrs: (node)=>{
271
- if (!(node instanceof HTMLElement)) return false;
272
- return {
273
- id: node.dataset.bookmarkId ?? '',
274
- name: node.dataset.bookmarkName ?? '',
275
- nativeId: normalizeDocumentBookmarkNativeId(node.dataset.officeBookmarkId),
276
- kind: documentBookmarkBoundaryKind(node.dataset.bookmarkKind)
277
- };
278
- }
279
- }
280
- ];
281
- },
282
- renderHTML ({ node, HTMLAttributes }) {
283
- const name = normalizeDocumentBookmarkName(node.attrs.name) ?? '';
284
- const nativeId = normalizeDocumentBookmarkNativeId(node.attrs.nativeId);
285
- const kind = documentBookmarkBoundaryKind(node.attrs.kind);
286
- return [
287
- 'span',
288
- mergeAttributes(HTMLAttributes, {
289
- ...'start' === kind && name ? {
290
- id: name
291
- } : {},
292
- 'data-document-bookmark-boundary': 'true',
293
- 'data-bookmark-kind': kind,
294
- 'data-bookmark-id': 'string' == typeof node.attrs.id ? node.attrs.id : '',
295
- 'data-bookmark-name': name,
296
- 'data-office-bookmark-id': null === nativeId ? void 0 : String(nativeId),
297
- class: `work-document-bookmark-boundary ${kind}`,
298
- contenteditable: 'false',
299
- 'aria-hidden': 'true'
300
- })
301
- ];
302
- },
303
- renderText () {
304
- return '';
305
- }
306
- });
307
- function validateDocumentBookmarkName(value) {
308
- const name = value.trim();
309
- return BOOKMARK_UI_NAME_PATTERN.test(name) && Array.from(name).length <= MAX_BOOKMARK_NAME_LENGTH ? null : DOCUMENT_BOOKMARK_VALIDATION_MESSAGE;
218
+ const DOCUMENT_HTML_FINGERPRINT_VERSION = 'p1';
219
+ const DOCUMENT_HTML_HASH_BASE = 0x01000193;
220
+ const LEGACY_FNV_OFFSET = 0x811c9dc5;
221
+ function createDocumentHtmlFingerprintSegment(source, from = 0, to = source.length) {
222
+ if (!Number.isSafeInteger(from) || !Number.isSafeInteger(to) || from < 0 || to < from || to > source.length) throw new RangeError('The document HTML fingerprint range is invalid.');
223
+ let hash = 0;
224
+ for(let index = from; index < to; index += 1)hash = Math.imul(hash, DOCUMENT_HTML_HASH_BASE) + source.charCodeAt(index) >>> 0;
225
+ const length = to - from;
226
+ return {
227
+ hash,
228
+ length,
229
+ power: documentHtmlHashPower(length)
230
+ };
310
231
  }
311
- function normalizeDocumentBookmarkName(value) {
312
- if ('string' != typeof value) return null;
313
- const name = value.trim();
314
- return BOOKMARK_NAME_PATTERN.test(name) && Array.from(name).length <= MAX_BOOKMARK_NAME_LENGTH ? name : null;
232
+ function combineDocumentHtmlFingerprintSegments(segments) {
233
+ let combined = {
234
+ hash: 0,
235
+ length: 0,
236
+ power: 1
237
+ };
238
+ for (const segment of segments)combined = {
239
+ hash: Math.imul(combined.hash, segment.power) + segment.hash >>> 0,
240
+ length: combined.length + segment.length,
241
+ power: Math.imul(combined.power, segment.power) >>> 0
242
+ };
243
+ return combined;
315
244
  }
316
- function normalizeDocumentBookmarkNativeId(value) {
317
- if (null == value || '' === value || 'string' == typeof value && !value.trim()) return null;
318
- const number = 'number' == typeof value ? value : Number(value);
319
- return Number.isSafeInteger(number) && number >= 0 && number <= MAX_BOOKMARK_NATIVE_ID ? number : null;
245
+ function documentHtmlFingerprintForSegment(segment) {
246
+ return `${DOCUMENT_HTML_FINGERPRINT_VERSION}:${segment.length.toString(36)}:${segment.hash.toString(36)}`;
320
247
  }
321
- function editorDocumentBookmarks(editor) {
322
- return collectDocumentBookmarkPairs(editor.state.doc, 'documentBookmarkBoundary').pairs.map(({ id, name, nativeId, from, to })=>({
323
- id,
324
- name,
325
- nativeId,
326
- from,
327
- to
328
- }));
248
+ function documentHtmlFingerprint(html) {
249
+ return documentHtmlFingerprintForSegment(createDocumentHtmlFingerprintSegment(html));
329
250
  }
330
- function editorDocumentBookmarkReferenceTargets(editor) {
331
- return collectDocumentBookmarkPairs(editor.state.doc, 'documentBookmarkBoundary').pairs.map((bookmark)=>{
332
- const display = documentBookmarkReferenceDisplay(editor.state.doc, bookmark);
333
- return {
334
- type: 'bookmark',
335
- id: bookmark.id,
336
- name: bookmark.name,
337
- title: display,
338
- display,
339
- instruction: documentBookmarkReferenceInstruction(bookmark.name)
340
- };
341
- });
251
+ function documentHtmlFingerprintMatches(html, candidate) {
252
+ return candidate.startsWith(`${DOCUMENT_HTML_FINGERPRINT_VERSION}:`) ? candidate === documentHtmlFingerprint(html) : candidate === legacyDocumentHtmlFingerprint(html);
342
253
  }
343
- function activeDocumentBookmark(editor) {
344
- const { from, to } = editor.state.selection;
345
- const matches = editorDocumentBookmarks(editor).filter((bookmark)=>from >= bookmark.from + 1 && to <= bookmark.to);
346
- return matches.sort((left, right)=>left.to - left.from - (right.to - right.from))[0] ?? null;
254
+ function legacyDocumentHtmlFingerprint(html) {
255
+ let hash = LEGACY_FNV_OFFSET;
256
+ for(let index = 0; index < html.length; index += 1){
257
+ hash ^= html.charCodeAt(index);
258
+ hash = Math.imul(hash, DOCUMENT_HTML_HASH_BASE);
259
+ }
260
+ return `${html.length.toString(36)}:${(hash >>> 0).toString(36)}`;
347
261
  }
348
- function documentBookmarkNameExists(editor, value, exceptId) {
349
- const name = value.trim().toLowerCase();
350
- return editorDocumentBookmarks(editor).some((bookmark)=>bookmark.id !== exceptId && bookmark.name.toLowerCase() === name);
262
+ function documentHtmlHashPower(length) {
263
+ let exponent = length;
264
+ let factor = DOCUMENT_HTML_HASH_BASE;
265
+ let power = 1;
266
+ while(exponent > 0){
267
+ if (exponent % 2 === 1) power = Math.imul(power, factor) >>> 0;
268
+ factor = Math.imul(factor, factor) >>> 0;
269
+ exponent = Math.floor(exponent / 2);
270
+ }
271
+ return power;
351
272
  }
352
- function normalizeDocumentBookmarksHtml(source) {
353
- const document1 = new DOMParser().parseFromString(source, 'text/html');
354
- const boundaries = Array.from(document1.body.querySelectorAll('span[data-document-bookmark-boundary]'));
355
- const collection = collectDomBookmarkPairs(boundaries);
356
- for (const orphan of collection.orphans)orphan.remove();
357
- const registry = createBookmarkRegistry();
358
- for (const pair of collection.pairs){
359
- const previous = {
360
- id: bookmarkInternalId(pair.id),
361
- name: normalizeDocumentBookmarkName(pair.name) ?? ''
273
+ const SECTION_CLOSE = '</section>';
274
+ const TABLE_BODY_OPEN = '<tbody>';
275
+ const TABLE_CLOSE = '</tbody></table>';
276
+ const projectionFingerprints = new WeakMap();
277
+ function createDocumentLazyHtmlProjection(html, root) {
278
+ if (!html.startsWith('<section') || !html.endsWith(SECTION_CLOSE)) return null;
279
+ const sectionOpenEnd = html.indexOf('>') + 1;
280
+ if (sectionOpenEnd <= 0) return null;
281
+ let cursor = sectionOpenEnd;
282
+ const orderedRanges = [];
283
+ const ranges = new Map();
284
+ for (const chunk of lazyDocumentLeafChunks(root)){
285
+ const id = 'string' == typeof chunk.attrs?.id ? chunk.attrs.id : '';
286
+ if (!id || ranges.has(id)) return null;
287
+ const from = cursor;
288
+ let tablePart = null;
289
+ const content = chunk.content ?? [];
290
+ for (const node of content){
291
+ const scanned = scanSimpleDocumentNodeHtml(html, cursor, node);
292
+ if (!scanned) return null;
293
+ cursor = scanned.to;
294
+ if (scanned.tablePart) {
295
+ if (1 !== content.length || tablePart) return null;
296
+ tablePart = scanned.tablePart;
297
+ }
298
+ }
299
+ const range = {
300
+ from,
301
+ id,
302
+ tablePart,
303
+ to: cursor
362
304
  };
363
- const identity = uniqueDocumentBookmarkIdentity(pair, registry);
364
- applyBookmarkIdentityToElement(pair.start, identity, 'start');
365
- applyBookmarkIdentityToElement(pair.end, identity, 'end');
366
- if (previous.id && previous.name && (previous.id !== identity.id || previous.name !== identity.name)) retargetDomBookmarkReferences(document1.body, pair.start, pair.end, previous, identity);
305
+ orderedRanges.push(range);
306
+ ranges.set(id, range);
367
307
  }
368
- synchronizeDomInternalLinks(document1.body, registry.names);
369
- return document1.body.innerHTML;
308
+ if (cursor !== html.length - SECTION_CLOSE.length) return null;
309
+ return {
310
+ html,
311
+ orderedRanges,
312
+ ranges
313
+ };
370
314
  }
371
- function insertDocumentBookmarkCommand({ dispatch, editor, state, tr }, value) {
372
- const name = value.trim();
373
- const boundaryType = editor.schema.nodes.documentBookmarkBoundary;
374
- const selection = state.selection;
375
- if (!boundaryType || validateDocumentBookmarkName(name) || documentBookmarkNameExists(editor, name) || !selection.$from.parent.inlineContent || !selection.$to.parent.inlineContent) return false;
376
- if (!dispatch) return true;
377
- const registry = bookmarkRegistryForDocument(state.doc, boundaryType.name);
378
- const identity = uniqueDocumentBookmarkIdentity({
379
- id: createUniqueBookmarkId(registry.ids),
380
- name,
381
- nativeId: nextBookmarkNativeId(registry.nativeIds)
382
- }, registry);
383
- const start = boundaryType.create({
384
- ...identity,
385
- kind: 'start'
386
- });
387
- const end = boundaryType.create({
388
- ...identity,
389
- kind: 'end'
390
- });
391
- const from = selection.from;
392
- const to = selection.to;
393
- tr.insert(to, end);
394
- tr.insert(from, start);
395
- tr.setSelection(TextSelection.create(tr.doc, from + 1, to + 1));
396
- dispatch(tr.scrollIntoView());
397
- return true;
315
+ function patchDocumentLazyHtmlProjection(projection, replacements) {
316
+ if (!replacements.size) return projection.html;
317
+ for (const id of replacements.keys())if (!projection.ranges.has(id)) return null;
318
+ const fingerprintState = documentLazyHtmlFingerprintState(projection);
319
+ const parts = [];
320
+ let cursor = 0;
321
+ for (const range of projection.orderedRanges){
322
+ const replacement = replacements.get(range.id);
323
+ if (void 0 !== replacement) {
324
+ parts.push(projection.html.slice(cursor, range.from), replacement);
325
+ fingerprintState.ranges.set(range.id, createDocumentHtmlFingerprintSegment(replacement));
326
+ cursor = range.to;
327
+ }
328
+ }
329
+ parts.push(projection.html.slice(cursor));
330
+ const html = parts.join('');
331
+ let offset = 0;
332
+ for (const range of projection.orderedRanges){
333
+ const previousFrom = range.from;
334
+ const previousLength = range.to - previousFrom;
335
+ const replacement = replacements.get(range.id);
336
+ const nextLength = replacement?.length ?? previousLength;
337
+ range.from = previousFrom + offset;
338
+ range.to = range.from + nextLength;
339
+ offset += nextLength - previousLength;
340
+ }
341
+ projection.html = html;
342
+ fingerprintState.fingerprint = documentHtmlFingerprintForSegment(combineDocumentHtmlFingerprintSegments([
343
+ fingerprintState.prefix,
344
+ ...projection.orderedRanges.map((range)=>{
345
+ const segment = fingerprintState.ranges.get(range.id);
346
+ if (!segment) throw new Error('The lazy HTML fingerprint segment is missing.');
347
+ return segment;
348
+ }),
349
+ fingerprintState.suffix
350
+ ]));
351
+ return html;
398
352
  }
399
- function deleteDocumentBookmarkCommand({ dispatch, editor, state, tr }, id) {
400
- const bookmark = collectDocumentBookmarkPairs(state.doc, editor.schema.nodes.documentBookmarkBoundary?.name ?? '').pairs.find((candidate)=>candidate.id === id);
401
- if (!bookmark) return false;
402
- if (!dispatch) return true;
403
- tr.delete(bookmark.to, bookmark.to + bookmark.end.node.nodeSize);
404
- tr.delete(bookmark.from, bookmark.from + bookmark.start.node.nodeSize);
405
- tr.setSelection(TextSelection.near(tr.doc.resolve(bookmark.from)));
406
- dispatch(tr.scrollIntoView());
407
- return true;
353
+ function documentLazyHtmlProjectionFingerprint(projection) {
354
+ return projectionFingerprints.get(projection)?.fingerprint ?? null;
408
355
  }
409
- function createDocumentBookmarkPlugin(boundaryNodeName) {
410
- return new Plugin({
411
- view (view) {
412
- const transaction = normalizeDocumentBookmarks(view.state, boundaryNodeName);
413
- if (transaction) {
414
- transaction.setMeta('addToHistory', false);
415
- view.dispatch(transaction);
416
- }
417
- return {};
418
- },
419
- appendTransaction (transactions, oldState, newState) {
420
- if (!transactions.some((transaction)=>transaction.docChanged)) return null;
421
- return normalizeDocumentBookmarks(newState, boundaryNodeName, oldState, transactions);
422
- }
423
- });
356
+ function documentLazyHtmlChunkFragment(html, tablePart) {
357
+ if (!tablePart || 'complete' === tablePart) return html;
358
+ const bodyOpen = html.indexOf(TABLE_BODY_OPEN);
359
+ const bodyClose = html.lastIndexOf('</tbody>');
360
+ if (bodyOpen < 0 || bodyClose < bodyOpen) return null;
361
+ const rowsFrom = bodyOpen + TABLE_BODY_OPEN.length;
362
+ if ('first' === tablePart) return html.slice(0, bodyClose);
363
+ if ('middle' === tablePart) return html.slice(rowsFrom, bodyClose);
364
+ return html.slice(rowsFrom);
424
365
  }
425
- function normalizeDocumentBookmarks(state, boundaryNodeName, oldState, transactions = []) {
426
- if ('documentBookmarkBoundary' === boundaryNodeName && !documentHasIntegrityFeature(state.doc, 1)) return null;
427
- const collection = collectDocumentBookmarkPairs(state.doc, boundaryNodeName);
428
- const retained = oldState ? retainedDocumentBookmarkPairs(oldState, state, transactions, boundaryNodeName) : new Set();
429
- const ordered = [
430
- ...collection.pairs.filter((pair)=>retained.has(pairPositionKey(pair))),
431
- ...collection.pairs.filter((pair)=>!retained.has(pairPositionKey(pair)))
366
+ function lazyDocumentLeafChunks(root) {
367
+ const chunks = [];
368
+ const pending = [
369
+ root
432
370
  ];
433
- const registry = createBookmarkRegistry();
434
- const updates = new Map();
435
- const renames = [];
436
- const effectivePairs = [];
437
- for (const pair of ordered){
438
- const identity = uniqueDocumentBookmarkIdentity(pair, registry);
439
- if (!sameBookmarkIdentity(pair.start.node.attrs, identity)) updates.set(pair.from, identity);
440
- if (!sameBookmarkIdentity(pair.end.node.attrs, identity)) updates.set(pair.to, identity);
441
- if (!sameBookmarkIdentity(pair, identity)) {
442
- if (pair.id && pair.name && (pair.id !== identity.id || pair.name !== identity.name)) renames.push({
443
- from: pair.from,
444
- to: pair.to,
445
- previousId: pair.id,
446
- nextId: identity.id,
447
- previousName: pair.name,
448
- nextName: identity.name
449
- });
371
+ while(pending.length){
372
+ const node = pending.pop();
373
+ if (!node) continue;
374
+ if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
375
+ chunks.push(node);
376
+ continue;
377
+ }
378
+ for(let index = (node.content?.length ?? 0) - 1; index >= 0; index -= 1){
379
+ const child = node.content?.[index];
380
+ if (child) pending.push(child);
450
381
  }
451
- effectivePairs.push({
452
- ...pair,
453
- ...identity
454
- });
455
- }
456
- const tr = state.tr;
457
- for (const boundary of documentBookmarkBoundaries(state.doc, boundaryNodeName)){
458
- const identity = updates.get(boundary.position);
459
- if (identity) tr.setNodeMarkup(boundary.position, void 0, {
460
- ...boundary.node.attrs,
461
- ...identity
462
- });
463
382
  }
464
- synchronizeInternalLinkMarks(state, tr, effectivePairs, renames);
465
- synchronizeDocumentBookmarkReferenceNodes(state, tr, effectivePairs, renames);
466
- for (const orphan of [
467
- ...collection.orphans
468
- ].sort((left, right)=>right.position - left.position))tr.delete(orphan.position, orphan.position + orphan.node.nodeSize);
469
- return tr.docChanged ? tr : null;
383
+ return chunks;
470
384
  }
471
- function retainedDocumentBookmarkPairs(oldState, newState, transactions, boundaryNodeName) {
472
- const mapping = transactionMapping(transactions);
473
- const retained = new Set();
474
- const current = collectDocumentBookmarkPairs(newState.doc, boundaryNodeName).pairs;
475
- const currentById = new Map();
476
- for (const pair of current){
477
- const matches = currentById.get(pair.id) ?? [];
478
- matches.push(pair);
479
- currentById.set(pair.id, matches);
385
+ function documentLazyHtmlFingerprintState(projection) {
386
+ const existing = projectionFingerprints.get(projection);
387
+ if (existing) return existing;
388
+ const first = projection.orderedRanges[0];
389
+ const last = projection.orderedRanges.at(-1);
390
+ const ranges = new Map();
391
+ for (const range of projection.orderedRanges)ranges.set(range.id, createDocumentHtmlFingerprintSegment(projection.html, range.from, range.to));
392
+ const prefix = createDocumentHtmlFingerprintSegment(projection.html, 0, first?.from ?? projection.html.length);
393
+ const suffix = createDocumentHtmlFingerprintSegment(projection.html, last?.to ?? projection.html.length, projection.html.length);
394
+ const state = {
395
+ fingerprint: documentHtmlFingerprintForSegment(combineDocumentHtmlFingerprintSegments([
396
+ prefix,
397
+ ...projection.orderedRanges.map((range)=>{
398
+ const segment = ranges.get(range.id);
399
+ if (!segment) throw new Error('The lazy HTML fingerprint segment is missing.');
400
+ return segment;
401
+ }),
402
+ suffix
403
+ ])),
404
+ prefix,
405
+ ranges,
406
+ suffix
407
+ };
408
+ projectionFingerprints.set(projection, state);
409
+ return state;
410
+ }
411
+ function scanSimpleDocumentNodeHtml(html, from, node) {
412
+ if ('paragraph' === node.type) {
413
+ if (!html.startsWith('<p>', from)) return null;
414
+ const close = html.indexOf('</p>', from + 3);
415
+ return close < 0 ? null : {
416
+ tablePart: null,
417
+ to: close + 4
418
+ };
480
419
  }
481
- for (const previous of collectDocumentBookmarkPairs(oldState.doc, boundaryNodeName).pairs){
482
- const mappedFrom = mapping.mapResult(previous.from, 1);
483
- const mappedTo = mapping.mapResult(previous.to, 1);
484
- const exact = current.find((pair)=>pair.from === mappedFrom.pos && pair.to === mappedTo.pos && pair.id === previous.id);
485
- if (exact) {
486
- retained.add(pairPositionKey(exact));
487
- continue;
488
- }
489
- const sameIdentity = currentById.get(previous.id);
490
- if (sameIdentity?.length === 1) retained.add(pairPositionKey(sameIdentity[0]));
420
+ if ('table' !== node.type) return null;
421
+ const virtualIndex = Number(node.attrs?.virtualTableIndex);
422
+ const virtualCount = Number(node.attrs?.virtualTableCount);
423
+ const virtual = Boolean('string' == typeof node.attrs?.virtualTableId && node.attrs.virtualTableId && Number.isSafeInteger(virtualIndex) && Number.isSafeInteger(virtualCount) && virtualIndex >= 0 && virtualCount > 0 && virtualIndex < virtualCount);
424
+ let cursor = from;
425
+ if (!virtual || 0 === virtualIndex) {
426
+ if (!html.startsWith('<table', cursor)) return null;
427
+ const body = html.indexOf(TABLE_BODY_OPEN, cursor);
428
+ if (body < 0) return null;
429
+ cursor = body + TABLE_BODY_OPEN.length;
491
430
  }
492
- return retained;
431
+ for (const row of node.content ?? []){
432
+ if ('tableRow' !== row.type || !html.startsWith('<tr>', cursor)) return null;
433
+ const close = html.indexOf('</tr>', cursor + 4);
434
+ if (close < 0) return null;
435
+ cursor = close + 5;
436
+ }
437
+ if (!virtual || virtualIndex === virtualCount - 1) {
438
+ if (!html.startsWith(TABLE_CLOSE, cursor)) return null;
439
+ cursor += TABLE_CLOSE.length;
440
+ }
441
+ const tablePart = virtual ? 1 === virtualCount ? 'complete' : 0 === virtualIndex ? 'first' : virtualIndex === virtualCount - 1 ? 'last' : 'middle' : 'complete';
442
+ return {
443
+ tablePart,
444
+ to: cursor
445
+ };
493
446
  }
494
- function synchronizeInternalLinkMarks(state, tr, bookmarks, renames) {
495
- const linkType = state.schema.marks.link;
496
- if (!linkType) return;
497
- const names = new Set(bookmarks.map((bookmark)=>bookmark.name.toLowerCase()));
498
- state.doc.descendants((node, position)=>{
499
- if (!node.isText) return;
500
- const link = node.marks.find((mark)=>mark.type === linkType);
501
- if (!link) return;
502
- const href = 'string' == typeof link.attrs.href ? link.attrs.href : '';
503
- if (!href.startsWith('#')) return;
504
- const target = href.slice(1);
505
- const rename = renames.filter((candidate)=>position > candidate.from && position < candidate.to && target.toLowerCase() === candidate.previousName.toLowerCase()).sort((left, right)=>left.to - left.from - (right.to - right.from))[0];
506
- const nextHref = rename ? `#${rename.nextName}` : href;
507
- const nextTarget = nextHref.slice(1).toLowerCase();
508
- const nextClass = toggleClassToken(link.attrs.class, MISSING_LINK_CLASS, !names.has(nextTarget));
509
- if (nextHref === href && nextClass === normalizedClass(link.attrs.class)) return;
510
- replaceLinkMark(state, tr, node, position, link, {
511
- ...link.attrs,
512
- href: nextHref,
513
- class: nextClass || null
514
- });
447
+ const DOCUMENT_LAZY_BLOCK_NODE = 'documentLazyBlock';
448
+ const DOCUMENT_LAZY_INITIAL_CHUNK_COUNT = 2;
449
+ const DOCUMENT_LAZY_POSITION_BOUNDARY = '\ufffc';
450
+ const SIMPLE_LAZY_CONTAINER_TYPES = new Set([
451
+ 'paragraph',
452
+ 'table',
453
+ 'tableCell',
454
+ 'tableRow'
455
+ ]);
456
+ const preparedModels = new WeakMap();
457
+ function prepareLazyDocumentEditorSource(model, allowCreate, html) {
458
+ const startedAt = lazyDocumentNow();
459
+ const cacheHit = preparedModels.has(model);
460
+ let prepared = preparedModels.get(model);
461
+ if (prepared) {
462
+ if (!prepared.root) {
463
+ const htmlProjection = prepared.htmlProjection;
464
+ const rebuilt = createPreparedLazyDocumentEditorSource(model.root, prepared.payloads);
465
+ if (!rebuilt) return null;
466
+ rebuilt.htmlProjection = htmlProjection;
467
+ prepared = rebuilt;
468
+ preparedModels.set(model, rebuilt);
469
+ }
470
+ } else {
471
+ if (!allowCreate) return null;
472
+ const created = createPreparedLazyDocumentEditorSource(model.root);
473
+ if (!created) return null;
474
+ prepared = created;
475
+ preparedModels.set(model, created);
476
+ }
477
+ if (!prepared.htmlProjection && html) prepared.htmlProjection = createDocumentLazyHtmlProjection(html, model.root);
478
+ if (!prepared.root) return null;
479
+ const result = {
480
+ lazyChunkCount: prepared.lazyChunkCount,
481
+ payloads: prepared.payloads,
482
+ root: prepared.root
483
+ };
484
+ recordLazyDocumentMeasure('a3s-office.document.lazy-editor-source', startedAt, lazyDocumentNow(), {
485
+ cacheHit
515
486
  });
487
+ return result;
516
488
  }
517
- function replaceLinkMark(_state, tr, node, position, link, attributes) {
518
- tr.removeMark(position, position + node.nodeSize, link.type);
519
- tr.addMark(position, position + node.nodeSize, link.type.create(attributes));
489
+ function documentLazyHtmlProjection(model) {
490
+ return model ? preparedModels.get(model)?.htmlProjection ?? null : null;
520
491
  }
521
- function collectDocumentBookmarkPairs(document1, boundaryNodeName) {
522
- const boundaries = documentBookmarkBoundaries(document1, boundaryNodeName);
523
- const open = new Map();
524
- const pairs = [];
525
- const orphans = [];
526
- for (const boundary of boundaries){
527
- const key = bookmarkBoundaryPairKey(boundary.node.attrs);
528
- if ('start' === boundary.kind) {
529
- const stack = open.get(key) ?? [];
530
- stack.push(boundary);
531
- open.set(key, stack);
532
- continue;
533
- }
534
- const stack = open.get(key);
535
- const start = stack?.pop();
536
- if (!start) {
537
- orphans.push(boundary);
538
- continue;
539
- }
540
- pairs.push(bookmarkPair(start, boundary));
492
+ function invalidateDocumentLazyHtmlProjection(model) {
493
+ if (model) {
494
+ const prepared = preparedModels.get(model);
495
+ if (prepared) prepared.htmlProjection = null;
541
496
  }
542
- for (const stack of open.values())orphans.push(...stack);
543
- pairs.sort((left, right)=>left.from - right.from || left.to - right.to);
544
- return {
545
- pairs,
546
- orphans
497
+ }
498
+ function documentLazyChunkContent(model, chunkId) {
499
+ if (!model || !chunkId) return null;
500
+ return preparedModels.get(model)?.payloads.get(chunkId) ?? null;
501
+ }
502
+ function materializeLazyDocumentEditorRoot(root, model) {
503
+ const prepared = model ? preparedModels.get(model) : null;
504
+ if (!prepared) return root;
505
+ const visit = (node)=>{
506
+ if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
507
+ const id = documentChunkId(node);
508
+ if (!id) return node;
509
+ if (documentChunkIsLazy(node)) {
510
+ const payload = prepared.payloads.get(id);
511
+ if (!payload) throw new Error(`The lazy document chunk payload "${id}" is missing.`);
512
+ return {
513
+ ...node,
514
+ content: [
515
+ ...payload
516
+ ]
517
+ };
518
+ }
519
+ if (node.content?.length) prepared.payloads.set(id, node.content);
520
+ return node;
521
+ }
522
+ if (!node.content?.length) return node;
523
+ let changed = false;
524
+ const content = node.content.map((child)=>{
525
+ const next = visit(child);
526
+ if (next !== child) changed = true;
527
+ return next;
528
+ });
529
+ return changed ? {
530
+ ...node,
531
+ content
532
+ } : node;
547
533
  };
534
+ return visit(root);
548
535
  }
549
- function documentBookmarkBoundaries(document1, boundaryNodeName) {
550
- if (!boundaryNodeName) return [];
551
- const boundaries = [];
552
- document1.descendants((node, position)=>{
553
- if (node.type.name !== boundaryNodeName) return;
554
- boundaries.push({
555
- node,
556
- position,
557
- kind: documentBookmarkBoundaryKind(node.attrs.kind)
558
- });
536
+ function transferLazyDocumentModelState(previous, next) {
537
+ if (!previous) return;
538
+ const prepared = preparedModels.get(previous);
539
+ if (!prepared) return;
540
+ preparedModels.set(next, {
541
+ htmlProjection: prepared.htmlProjection,
542
+ lazyChunkCount: prepared.lazyChunkCount,
543
+ payloads: prepared.payloads,
544
+ root: null
559
545
  });
560
- return boundaries;
561
- }
562
- function bookmarkPair(start, end) {
563
- return {
564
- id: bookmarkInternalId(start.node.attrs.id),
565
- name: normalizeDocumentBookmarkName(start.node.attrs.name) ?? '',
566
- nativeId: normalizeDocumentBookmarkNativeId(start.node.attrs.nativeId) ?? -1,
567
- from: start.position,
568
- to: end.position,
569
- start,
570
- end
571
- };
572
546
  }
573
- function bookmarkRegistryForDocument(document1, boundaryNodeName) {
574
- const registry = createBookmarkRegistry();
575
- for (const pair of collectDocumentBookmarkPairs(document1, boundaryNodeName).pairs){
576
- const identity = bookmarkIdentity(pair);
577
- if (identity) reserveBookmarkIdentity(identity, registry);
547
+ function simpleDocumentNodeSize(node) {
548
+ if ('text' === node.type) return 'string' == typeof node.text && node.text ? node.text.length : null;
549
+ if (!SIMPLE_LAZY_CONTAINER_TYPES.has(node.type)) return null;
550
+ let size = 2;
551
+ for (const child of node.content ?? []){
552
+ const childSize = simpleDocumentNodeSize(child);
553
+ if (null === childSize) return null;
554
+ size += childSize;
578
555
  }
579
- return registry;
556
+ return size;
580
557
  }
581
- function uniqueDocumentBookmarkIdentity(source, registry) {
582
- const preferred = bookmarkIdentity(source);
583
- if (preferred && !bookmarkIdentityConflicts(preferred, registry)) {
584
- reserveBookmarkIdentity(preferred, registry);
585
- return preferred;
586
- }
587
- const baseName = normalizeDocumentBookmarkName(source.name) ?? 'Bookmark';
588
- const identity = {
589
- id: createUniqueBookmarkId(registry.ids),
590
- name: uniqueBookmarkName(baseName, registry.names),
591
- nativeId: nextBookmarkNativeId(registry.nativeIds)
558
+ function createPreparedLazyDocumentEditorSource(root, previousPayloads) {
559
+ const payloads = new Map();
560
+ let leafIndex = 0;
561
+ let lazyChunkCount = 0;
562
+ let unsupported = false;
563
+ const visit = (node)=>{
564
+ if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
565
+ const id = documentChunkId(node);
566
+ const payload = id ? previousPayloads?.get(id) ?? node.content ?? [] : [];
567
+ if (!id || !payload.length) {
568
+ unsupported = true;
569
+ return node;
570
+ }
571
+ payloads.set(id, payload);
572
+ const currentIndex = leafIndex;
573
+ leafIndex += 1;
574
+ if (currentIndex < DOCUMENT_LAZY_INITIAL_CHUNK_COUNT) return node;
575
+ const placeholder = lazyPlaceholderForContent(id, payload);
576
+ if (!placeholder) {
577
+ unsupported = true;
578
+ return node;
579
+ }
580
+ lazyChunkCount += 1;
581
+ return {
582
+ ...node,
583
+ content: [
584
+ placeholder
585
+ ]
586
+ };
587
+ }
588
+ if (!node.content?.length) return node;
589
+ let changed = false;
590
+ const content = node.content.map((child)=>{
591
+ const next = visit(child);
592
+ if (next !== child) changed = true;
593
+ return next;
594
+ });
595
+ return changed ? {
596
+ ...node,
597
+ content
598
+ } : node;
599
+ };
600
+ const compactRoot = visit(root);
601
+ if (unsupported || leafIndex <= DOCUMENT_LAZY_INITIAL_CHUNK_COUNT || 0 === lazyChunkCount) return null;
602
+ return {
603
+ htmlProjection: null,
604
+ lazyChunkCount,
605
+ payloads,
606
+ root: compactRoot
592
607
  };
593
- reserveBookmarkIdentity(identity, registry);
594
- return identity;
595
- }
596
- function bookmarkIdentity(source) {
597
- const id = bookmarkInternalId(source.id);
598
- const name = normalizeDocumentBookmarkName(source.name);
599
- const nativeId = normalizeDocumentBookmarkNativeId(source.nativeId);
600
- return id && name && null !== nativeId ? {
601
- id,
602
- name,
603
- nativeId
604
- } : null;
605
- }
606
- function bookmarkIdentityConflicts(identity, registry) {
607
- return registry.ids.has(identity.id) || registry.names.has(identity.name.toLowerCase()) || registry.nativeIds.has(identity.nativeId);
608
- }
609
- function reserveBookmarkIdentity(identity, registry) {
610
- registry.ids.add(identity.id);
611
- registry.names.add(identity.name.toLowerCase());
612
- registry.nativeIds.add(identity.nativeId);
613
608
  }
614
- function createBookmarkRegistry() {
609
+ function lazyPlaceholderForContent(chunkId, content) {
610
+ const tapeParts = [];
611
+ const statistics = {
612
+ contentSize: 0,
613
+ paragraphCount: 0
614
+ };
615
+ for (const node of content)if (!appendDocumentNodePositionTape(node, tapeParts, statistics)) return null;
616
+ if (statistics.contentSize < 2 || tapeParts[0] !== DOCUMENT_LAZY_POSITION_BOUNDARY || tapeParts.at(-1) !== DOCUMENT_LAZY_POSITION_BOUNDARY) return null;
617
+ tapeParts[0] = '';
618
+ tapeParts[tapeParts.length - 1] = '';
619
+ const filler = tapeParts.join('');
615
620
  return {
616
- ids: new Set(),
617
- names: new Set(),
618
- nativeIds: new Set()
621
+ type: DOCUMENT_LAZY_BLOCK_NODE,
622
+ attrs: {
623
+ chunkId,
624
+ contentSize: statistics.contentSize,
625
+ paragraphCount: statistics.paragraphCount
626
+ },
627
+ ...filler ? {
628
+ content: [
629
+ {
630
+ type: 'text',
631
+ text: filler
632
+ }
633
+ ]
634
+ } : {
635
+ content: []
636
+ }
619
637
  };
620
638
  }
621
- function createUniqueBookmarkId(ids) {
622
- for(let attempt = 0; attempt < 8; attempt += 1){
623
- const id = createWorkId('bookmark');
624
- if (!ids.has(id)) return id;
639
+ function appendDocumentNodePositionTape(node, parts, statistics) {
640
+ if ('text' === node.type) {
641
+ if ('string' != typeof node.text || !node.text) return false;
642
+ parts.push(node.text);
643
+ statistics.contentSize += node.text.length;
644
+ return true;
625
645
  }
626
- let suffix = 1;
627
- while(ids.has(`bookmark-${suffix}`))suffix += 1;
628
- return `bookmark-${suffix}`;
646
+ if (!SIMPLE_LAZY_CONTAINER_TYPES.has(node.type)) return false;
647
+ if ('paragraph' === node.type) statistics.paragraphCount += 1;
648
+ statistics.contentSize += 2;
649
+ parts.push(DOCUMENT_LAZY_POSITION_BOUNDARY);
650
+ for (const child of node.content ?? [])if (!appendDocumentNodePositionTape(child, parts, statistics)) return false;
651
+ parts.push(DOCUMENT_LAZY_POSITION_BOUNDARY);
652
+ return true;
629
653
  }
630
- function uniqueBookmarkName(base, names) {
631
- if (!names.has(base.toLowerCase())) return base;
632
- let suffix = 2;
633
- while(suffix <= MAX_BOOKMARK_NATIVE_ID){
634
- const ending = `_${suffix}`;
635
- const prefix = Array.from(base).slice(0, MAX_BOOKMARK_NAME_LENGTH - ending.length).join('');
636
- const candidate = `${prefix}${ending}`;
637
- if (!names.has(candidate.toLowerCase())) return candidate;
638
- suffix += 1;
639
- }
640
- throw new Error('No unique Word bookmark name is available.');
654
+ function documentChunkIsLazy(node) {
655
+ return 'documentChunk' === node.type && node.attrs?.windowContainer !== true && node.content?.length === 1 && node.content[0]?.type === DOCUMENT_LAZY_BLOCK_NODE;
641
656
  }
642
- function nextBookmarkNativeId(ids) {
643
- for(let id = 0; id <= MAX_BOOKMARK_NATIVE_ID; id += 1)if (!ids.has(id)) return id;
644
- throw new Error('No unique Word bookmark identifier is available.');
657
+ function documentChunkId(node) {
658
+ const id = node.attrs?.id;
659
+ return 'string' == typeof id && id ? id : null;
645
660
  }
646
- function sameBookmarkIdentity(source, identity) {
647
- return bookmarkInternalId(source.id) === identity.id && normalizeDocumentBookmarkName(source.name) === identity.name && normalizeDocumentBookmarkNativeId(source.nativeId) === identity.nativeId;
661
+ function lazyDocumentNow() {
662
+ return globalThis.performance?.now?.() ?? Date.now();
648
663
  }
649
- function bookmarkInternalId(value) {
650
- return 'string' == typeof value ? value.trim() : '';
664
+ function recordLazyDocumentMeasure(name, start, end, detail) {
665
+ try {
666
+ globalThis.performance?.measure(name, {
667
+ detail,
668
+ end,
669
+ start
670
+ });
671
+ } catch {}
651
672
  }
652
- function bookmarkBoundaryPairKey(attributes) {
653
- const id = bookmarkInternalId(attributes.id);
654
- if (id) return `id:${id}`;
655
- return `legacy:${String(attributes.name ?? '')}:${String(attributes.nativeId ?? '')}`;
673
+ const statisticsByDocument = new WeakMap();
674
+ function documentWordCount(value) {
675
+ let asciiCount = 0;
676
+ let inAsciiWord = false;
677
+ for(let index = 0; index < value.length; index += 1){
678
+ const code = value.charCodeAt(index);
679
+ const asciiLetterOrNumber = code >= 0x30 && code <= 0x39 || code >= 0x41 && code <= 0x5a || code >= 0x61 && code <= 0x7a;
680
+ if (asciiLetterOrNumber) {
681
+ if (!inAsciiWord) asciiCount += 1;
682
+ inAsciiWord = true;
683
+ continue;
684
+ }
685
+ inAsciiWord = false;
686
+ if (!(code <= 0x7f) && 0xfffc !== code) return unicodeDocumentWordCount(value);
687
+ }
688
+ return asciiCount;
656
689
  }
657
- function documentBookmarkBoundaryKind(value) {
658
- return 'end' === value ? 'end' : 'start';
690
+ function unicodeDocumentWordCount(value) {
691
+ let count = 0;
692
+ 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;
693
+ return count;
694
+ }
695
+ function documentTextStatistics(editor) {
696
+ const document1 = editor.state.doc;
697
+ const cached = statisticsByDocument.get(document1);
698
+ if (cached) return cached;
699
+ const source = editor.getText({
700
+ blockSeparator: '\n'
701
+ });
702
+ let characterCountWithSpaces = 0;
703
+ let characterCountWithoutSpaces = 0;
704
+ for(let index = 0; index < source.length; index += 1){
705
+ const codePoint = source.codePointAt(index);
706
+ if (void 0 !== codePoint) {
707
+ if (codePoint > 0xffff) index += 1;
708
+ if (codePoint !== "".codePointAt(0) && 0x0a !== codePoint && 0x0d !== codePoint) {
709
+ characterCountWithSpaces += 1;
710
+ if (!isEcmaScriptWhitespace(codePoint)) characterCountWithoutSpaces += 1;
711
+ }
712
+ }
713
+ }
714
+ let paragraphCount = 0;
715
+ document1.descendants((node)=>{
716
+ if (node.type.name === DOCUMENT_LAZY_BLOCK_NODE) {
717
+ const logicalParagraphs = Number(node.attrs.paragraphCount);
718
+ paragraphCount += Number.isSafeInteger(logicalParagraphs) && logicalParagraphs >= 0 ? logicalParagraphs : 0;
719
+ return false;
720
+ }
721
+ if (node.isTextblock) paragraphCount += 1;
722
+ return true;
723
+ });
724
+ const statistics = {
725
+ characterCountWithSpaces,
726
+ characterCountWithoutSpaces,
727
+ paragraphCount,
728
+ wordCount: documentWordCount(source)
729
+ };
730
+ statisticsByDocument.set(document1, statistics);
731
+ return statistics;
659
732
  }
660
- function transactionMapping(transactions) {
661
- const mapping = new Mapping();
662
- for (const transaction of transactions)mapping.appendMapping(transaction.mapping);
663
- return mapping;
733
+ function transferDocumentTextStatistics(previous, next) {
734
+ const statistics = statisticsByDocument.get(previous);
735
+ if (statistics) statisticsByDocument.set(next, statistics);
664
736
  }
665
- function pairPositionKey(pair) {
666
- return `${pair.from}:${pair.to}`;
737
+ function transferChangedDocumentTextStatistics(previous, next, changes) {
738
+ const cached = statisticsByDocument.get(previous);
739
+ if (!cached || !changes.length) return false;
740
+ const statistics = {
741
+ ...cached
742
+ };
743
+ for (const { after, before } of changes){
744
+ applyDocumentTextStatisticsDelta(statistics, simpleDocumentSubtreeStatistics(before), -1);
745
+ applyDocumentTextStatisticsDelta(statistics, simpleDocumentSubtreeStatistics(after), 1);
746
+ }
747
+ if (Object.values(statistics).some((value)=>value < 0)) return false;
748
+ statisticsByDocument.set(next, statistics);
749
+ return true;
667
750
  }
668
- function collectDomBookmarkPairs(boundaries) {
669
- const open = new Map();
670
- const pairs = [];
671
- const orphans = [];
672
- for (const boundary of boundaries){
673
- const key = domBookmarkPairKey(boundary);
674
- if ('start' === documentBookmarkBoundaryKind(boundary.dataset.bookmarkKind)) {
675
- const stack = open.get(key) ?? [];
676
- stack.push(boundary);
677
- open.set(key, stack);
678
- continue;
751
+ function simpleDocumentSubtreeStatistics(root) {
752
+ const statistics = {
753
+ characterCountWithSpaces: 0,
754
+ characterCountWithoutSpaces: 0,
755
+ paragraphCount: 0,
756
+ wordCount: 0
757
+ };
758
+ const visit = (node)=>{
759
+ if (node.type.name === DOCUMENT_LAZY_BLOCK_NODE) {
760
+ accumulateDocumentTextStatistics(statistics, node.textContent);
761
+ const logicalParagraphs = Number(node.attrs.paragraphCount);
762
+ statistics.paragraphCount += Number.isSafeInteger(logicalParagraphs) && logicalParagraphs >= 0 ? logicalParagraphs : 0;
763
+ return;
679
764
  }
680
- const start = open.get(key)?.pop();
681
- if (!start) {
682
- orphans.push(boundary);
683
- continue;
765
+ if (node.isTextblock) {
766
+ accumulateDocumentTextStatistics(statistics, node.textContent);
767
+ statistics.paragraphCount += 1;
768
+ return;
684
769
  }
685
- pairs.push({
686
- id: start.dataset.bookmarkId,
687
- name: start.dataset.bookmarkName,
688
- nativeId: normalizeDocumentBookmarkNativeId(start.dataset.officeBookmarkId) ?? void 0,
689
- start,
690
- end: boundary
691
- });
692
- }
693
- for (const stack of open.values())orphans.push(...stack);
694
- return {
695
- pairs,
696
- orphans
770
+ node.forEach(visit);
697
771
  };
772
+ visit(root);
773
+ return statistics;
698
774
  }
699
- function domBookmarkPairKey(element) {
700
- const id = element.dataset.bookmarkId?.trim();
701
- return id ? `id:${id}` : `legacy:${element.dataset.bookmarkName ?? ''}:${element.dataset.officeBookmarkId ?? ''}`;
775
+ function accumulateDocumentTextStatistics(statistics, source) {
776
+ for(let index = 0; index < source.length; index += 1){
777
+ const codePoint = source.codePointAt(index);
778
+ if (void 0 !== codePoint) {
779
+ if (codePoint > 0xffff) index += 1;
780
+ if (codePoint !== "".codePointAt(0) && 0x0a !== codePoint && 0x0d !== codePoint) {
781
+ statistics.characterCountWithSpaces += 1;
782
+ if (!isEcmaScriptWhitespace(codePoint)) statistics.characterCountWithoutSpaces += 1;
783
+ }
784
+ }
785
+ }
786
+ statistics.wordCount += documentWordCount(source);
702
787
  }
703
- function applyBookmarkIdentityToElement(element, identity, kind) {
704
- element.dataset.documentBookmarkBoundary = 'true';
705
- element.dataset.bookmarkKind = kind;
706
- element.dataset.bookmarkId = identity.id;
707
- element.dataset.bookmarkName = identity.name;
708
- element.dataset.officeBookmarkId = String(identity.nativeId);
709
- element.classList.add('work-document-bookmark-boundary', kind);
710
- element.contentEditable = 'false';
711
- element.setAttribute('aria-hidden', 'true');
712
- if ('start' === kind) element.id = identity.name;
713
- else element.removeAttribute('id');
788
+ function applyDocumentTextStatisticsDelta(target, value, direction) {
789
+ target.characterCountWithSpaces += direction * value.characterCountWithSpaces;
790
+ target.characterCountWithoutSpaces += direction * value.characterCountWithoutSpaces;
791
+ target.paragraphCount += direction * value.paragraphCount;
792
+ target.wordCount += direction * value.wordCount;
714
793
  }
715
- function synchronizeDomInternalLinks(root, names) {
716
- for (const link of root.querySelectorAll('a[href^="#"]')){
717
- const target = (link.getAttribute('href') ?? '').slice(1).toLowerCase();
718
- link.classList.toggle(MISSING_LINK_CLASS, !names.has(target));
719
- if (!link.className) link.removeAttribute('class');
720
- }
794
+ function isEcmaScriptWhitespace(codePoint) {
795
+ 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;
721
796
  }
722
- function normalizedClass(value) {
723
- return 'string' == typeof value ? value.trim().split(/\s+/).filter(Boolean).join(' ') : '';
797
+ const FIELD_SELECTOR = 'span[data-document-field]';
798
+ const FIELD_TEXT_BOUNDARY = '\uFFFC';
799
+ const FIELD_COMMANDS = {
800
+ page: 'PAGE',
801
+ numPages: 'NUMPAGES',
802
+ section: 'SECTION',
803
+ sectionPages: 'SECTIONPAGES',
804
+ date: 'DATE \\@ "yyyy年M月d日"',
805
+ time: 'TIME \\@ "HH:mm"',
806
+ wordCount: 'NUMWORDS',
807
+ characterCount: 'NUMCHARS',
808
+ pageReference: 'PAGEREF'
809
+ };
810
+ const FIELD_LABELS = {
811
+ page: '当前页码',
812
+ numPages: '总页数',
813
+ section: '当前节号',
814
+ sectionPages: '本节页数',
815
+ date: '当前日期',
816
+ time: '当前时间',
817
+ wordCount: '字数',
818
+ characterCount: '字符数',
819
+ pageReference: '目标页码'
820
+ };
821
+ function documentFieldKind(value) {
822
+ if ('page' === value || 'numPages' === value || 'section' === value || 'sectionPages' === value || 'date' === value || 'time' === value || 'wordCount' === value || 'characterCount' === value || 'pageReference' === value) return value;
823
+ return null;
724
824
  }
725
- function toggleClassToken(value, token, enabled) {
726
- const tokens = new Set(normalizedClass(value).split(' ').filter(Boolean));
727
- if (enabled) tokens.add(token);
728
- else tokens.delete(token);
729
- return Array.from(tokens).join(' ');
825
+ function docxDocumentFieldKind(instruction) {
826
+ const command = /^\s*([a-z][a-z0-9]*)\b/i.exec(instruction)?.[1]?.toUpperCase();
827
+ if ('PAGE' === command) return 'page';
828
+ if ('NUMPAGES' === command) return 'numPages';
829
+ if ('SECTION' === command) return 'section';
830
+ if ('SECTIONPAGES' === command) return 'sectionPages';
831
+ if ('DATE' === command) return 'date';
832
+ if ('TIME' === command) return 'time';
833
+ if ('NUMWORDS' === command) return 'wordCount';
834
+ if ('NUMCHARS' === command) return 'characterCount';
835
+ if ('PAGEREF' === command) return 'pageReference';
836
+ return null;
730
837
  }
731
- const CAPTION_SELECTOR = 'figcaption[data-document-caption]';
732
- const REFERENCE_SELECTOR = 'span[data-document-cross-reference]';
733
- function normalizeDocumentCaptionsHtml(source) {
734
- const document1 = new DOMParser().parseFromString(source, 'text/html');
735
- const targets = normalizeCaptions(document1);
736
- normalizeReferences(document1, new Map(targets.map((target)=>[
737
- target.id,
738
- target
739
- ])));
740
- return document1.body.innerHTML;
838
+ function documentFieldInstruction(kind, options = {}) {
839
+ if ('pageReference' === kind) return documentPageReferenceInstruction(options.targetName, '', true);
840
+ return FIELD_COMMANDS[kind];
741
841
  }
742
- function documentCaptionKind(value) {
743
- return 'figure' === value || 'table' === value ? value : null;
842
+ function documentPageReferenceInstruction(targetName, source = '', defaultHyperlink = false) {
843
+ const target = normalizeFieldTarget(targetName);
844
+ if (!target) return FIELD_COMMANDS.pageReference;
845
+ const switches = [];
846
+ if (/(?:^|\s)\\h(?:\s|$)/i.test(source)) switches.push('\\h');
847
+ if (/(?:^|\s)\\\*\s+MERGEFORMAT(?:\s|$)/i.test(source)) switches.push('\\* MERGEFORMAT');
848
+ if (!switches.length && defaultHyperlink) switches.push('\\h');
849
+ return `PAGEREF ${target}${switches.length ? ` ${switches.join(' ')}` : ''}`;
744
850
  }
745
- function documentCaptionLabel(kind) {
746
- return 'table' === kind ? '表' : '图';
851
+ function documentFieldLabel(kind) {
852
+ return FIELD_LABELS[kind];
747
853
  }
748
- function documentCaptionDisplay(kind, number) {
749
- return `${documentCaptionLabel(kind)} ${positiveInteger(number)}`;
854
+ function documentFieldDisplay(kind, context, instruction = documentFieldInstruction(kind), cachedValue = '') {
855
+ if ('page' === kind) return String(positiveInteger(context.pageNumber));
856
+ if ('numPages' === kind) return String(positiveInteger(context.totalPages));
857
+ if ('section' === kind) return String(positiveInteger(context.sectionNumber));
858
+ if ('sectionPages' === kind) return String(positiveInteger(context.sectionPages));
859
+ if ('wordCount' === kind) return String(nonNegativeInteger(context.wordCount, cachedValue));
860
+ if ('characterCount' === kind) return String(nonNegativeInteger(context.characterCount, cachedValue));
861
+ if ('pageReference' === kind) {
862
+ const target = docxDocumentFieldTarget(instruction);
863
+ const hasResolutionContext = void 0 !== context.referencePageNumber || void 0 !== context.bookmarkPageNumbers;
864
+ const page = context.referencePageNumber ?? (target ? context.bookmarkPageNumbers?.get(`name:${target.toLowerCase()}`) : void 0);
865
+ return page && Number.isSafeInteger(page) && page > 0 ? String(page) : hasResolutionContext ? '引用缺失' : cachedValue.trim() || '引用缺失';
866
+ }
867
+ const now = validDate(context.now) ?? new Date();
868
+ const format = dateFormatSwitch(instruction) ?? ('date' === kind ? 'yyyy年M月d日' : 'HH:mm');
869
+ const display = formatWordDate(now, format);
870
+ return display || cachedValue || documentFieldLabel(kind);
750
871
  }
751
- function normalizeCaptions(document1) {
752
- const counters = {
753
- figure: 0,
754
- table: 0
755
- };
872
+ function normalizeDocumentFieldsHtml(source) {
873
+ const document1 = new DOMParser().parseFromString(source, 'text/html');
756
874
  const usedIds = new Set();
757
- return Array.from(document1.body.querySelectorAll(CAPTION_SELECTOR)).map((element, index)=>{
758
- const kind = documentCaptionKind(element.dataset.captionKind) ?? 'figure';
759
- counters[kind] += 1;
760
- const number = counters[kind];
761
- const id = uniqueCaptionId(element.dataset.captionId, kind, index + 1, usedIds);
762
- const label = documentCaptionLabel(kind);
763
- element.dataset.documentCaption = 'true';
764
- element.dataset.captionId = id;
765
- element.dataset.captionKind = kind;
766
- element.dataset.captionNumber = String(number);
767
- element.dataset.captionLabel = label;
768
- element.classList.add('work-document-caption');
769
- const title = element.textContent?.replace(/\s+/g, ' ').trim() ?? '';
770
- const display = documentCaptionDisplay(kind, number);
771
- element.setAttribute('aria-label', title ? `${display} ${title}` : display);
772
- return {
773
- id,
774
- kind,
775
- number,
776
- label,
777
- title,
778
- display
779
- };
780
- });
781
- }
782
- function normalizeReferences(document1, targets) {
783
- for (const element of Array.from(document1.body.querySelectorAll(REFERENCE_SELECTOR))){
784
- if ('bookmark' === element.dataset.referenceTargetType) continue;
785
- const id = element.dataset.referenceTargetId?.trim() ?? '';
786
- const target = targets.get(id);
787
- element.dataset.documentCrossReference = 'true';
788
- element.dataset.referenceTargetId = id;
789
- element.classList.add('work-document-cross-reference');
790
- if (!target) {
791
- element.dataset.referenceOrphaned = 'true';
792
- element.textContent = '引用缺失';
875
+ for (const [index, element] of Array.from(document1.body.querySelectorAll(FIELD_SELECTOR)).entries()){
876
+ const instruction = element.dataset.fieldInstruction?.trim() ?? '';
877
+ const kind = documentFieldKind(element.dataset.fieldKind) ?? docxDocumentFieldKind(instruction);
878
+ if (!kind) {
879
+ element.replaceWith(document1.createTextNode(element.textContent ?? ''));
793
880
  continue;
794
881
  }
795
- delete element.dataset.referenceOrphaned;
796
- element.dataset.captionKind = target.kind;
797
- element.dataset.captionNumber = String(target.number);
798
- element.dataset.captionLabel = target.label;
799
- element.textContent = target.display;
882
+ const display = element.dataset.fieldDisplay?.trim() || element.textContent?.trim() || documentFieldLabel(kind);
883
+ element.dataset.documentField = 'true';
884
+ element.dataset.fieldId = uniqueFieldId(element.dataset.fieldId, index + 1, usedIds);
885
+ element.dataset.fieldKind = kind;
886
+ element.dataset.fieldInstruction = instruction || documentFieldInstruction(kind);
887
+ if ('pageReference' === kind) {
888
+ const targetName = normalizeFieldTarget(element.dataset.fieldTargetName) ?? docxDocumentFieldTarget(instruction);
889
+ if (targetName) {
890
+ element.dataset.fieldTargetName = targetName;
891
+ element.dataset.fieldInstruction = documentPageReferenceInstruction(targetName, instruction);
892
+ delete element.dataset.fieldOrphaned;
893
+ } else {
894
+ delete element.dataset.fieldTargetName;
895
+ element.dataset.fieldOrphaned = 'true';
896
+ }
897
+ }
898
+ element.dataset.fieldDisplay = display;
899
+ element.classList.add('work-document-field');
900
+ element.textContent = display;
901
+ }
902
+ return document1.body.innerHTML;
903
+ }
904
+ function resolveDocumentFieldsHtml(source, context) {
905
+ const document1 = new DOMParser().parseFromString(normalizeDocumentFieldsHtml(source), 'text/html');
906
+ for (const element of Array.from(document1.body.querySelectorAll(FIELD_SELECTOR))){
907
+ const kind = documentFieldKind(element.dataset.fieldKind);
908
+ if (!kind) continue;
909
+ const referencePageNumber = 'pageReference' === kind ? context.referencePageNumber ?? (element.dataset.fieldTargetId ? context.bookmarkPageNumbers?.get(`id:${element.dataset.fieldTargetId.trim()}`) : void 0) : context.referencePageNumber;
910
+ const display = documentFieldDisplay(kind, {
911
+ ...context,
912
+ referencePageNumber
913
+ }, element.dataset.fieldInstruction, element.dataset.fieldDisplay);
914
+ element.dataset.fieldDisplay = display;
915
+ element.textContent = display;
800
916
  }
917
+ return document1.body.innerHTML;
801
918
  }
802
- function uniqueCaptionId(source, kind, index, usedIds) {
919
+ function uniqueFieldId(source, index, usedIds) {
803
920
  const candidate = source?.trim();
804
921
  if (candidate && !usedIds.has(candidate)) {
805
922
  usedIds.add(candidate);
806
923
  return candidate;
807
924
  }
808
925
  let suffix = index;
809
- while(usedIds.has(`document-${kind}-caption-${suffix}`))suffix += 1;
810
- const id = `document-${kind}-caption-${suffix}`;
926
+ while(usedIds.has(`document-field-${suffix}`))suffix += 1;
927
+ const id = `document-field-${suffix}`;
811
928
  usedIds.add(id);
812
929
  return id;
813
930
  }
814
- function positiveInteger(value) {
815
- const number = Number(value);
816
- return Number.isSafeInteger(number) && number > 0 ? number : 1;
931
+ function dateFormatSwitch(instruction) {
932
+ return /\\@\s+"([^"]+)"/i.exec(instruction)?.[1] ?? null;
817
933
  }
818
- const CITATION_SELECTOR = 'span[data-document-citation]';
819
- const BIBLIOGRAPHY_SELECTOR = 'section[data-document-bibliography]';
820
- const STYLE_DETAILS = {
821
- apa: {
822
- name: 'APA',
823
- selectedStyle: '\\APASixthEditionOfficeOnline.xsl'
824
- },
825
- mla: {
826
- name: 'MLA',
827
- selectedStyle: '\\MLASeventhEditionOfficeOnline.xsl'
828
- },
829
- chicago: {
830
- name: 'Chicago',
831
- selectedStyle: '\\CHICAGO.XSL'
832
- },
833
- ieee: {
834
- name: 'IEEE',
835
- selectedStyle: '\\IEEE.XSL'
934
+ function docxDocumentFieldTarget(instruction) {
935
+ const kind = docxDocumentFieldKind(instruction);
936
+ if ('pageReference' !== kind) return null;
937
+ return normalizeFieldTarget(/^\s*PAGEREF\s+([^\s\\]+)/i.exec(instruction)?.[1]);
938
+ }
939
+ function supportedDocxDocumentFieldInstruction(instruction) {
940
+ const kind = docxDocumentFieldKind(instruction);
941
+ if (!kind) return false;
942
+ const source = instruction.trim();
943
+ if ('pageReference' === kind) {
944
+ const match = /^PAGEREF\s+([^\s\\]+)([\s\S]*)$/i.exec(source);
945
+ const target = normalizeFieldTarget(match?.[1]);
946
+ if (!target || !match) return false;
947
+ let rest = match[2] ?? '';
948
+ const hyperlink = /^\s+\\h\b/i.exec(rest);
949
+ if (hyperlink) rest = rest.slice(hyperlink[0].length);
950
+ return onlyMergeFormatSwitch(rest);
951
+ }
952
+ if ('date' === kind || 'time' === kind) {
953
+ const command = 'date' === kind ? 'DATE' : 'TIME';
954
+ const match = new RegExp(`^${command}\\b([\\s\\S]*)$`, 'i').exec(source);
955
+ if (!match) return false;
956
+ let rest = match[1] ?? '';
957
+ const format = /^\s+\\@\s+"[^"\r\n]{1,128}"/i.exec(rest);
958
+ if (format) rest = rest.slice(format[0].length);
959
+ return onlyMergeFormatSwitch(rest);
960
+ }
961
+ const command = FIELD_COMMANDS[kind];
962
+ const match = new RegExp(`^${command}\\b([\\s\\S]*)$`, 'i').exec(source);
963
+ return Boolean(match && onlyMergeFormatSwitch(match[1] ?? ''));
964
+ }
965
+ function onlyMergeFormatSwitch(source) {
966
+ return /^(?:\s+\\\*\s+MERGEFORMAT)?\s*$/i.test(source);
967
+ }
968
+ function documentFieldStatisticsFromText(source) {
969
+ const normalized = source.replace(/\r\n?/g, '\n');
970
+ let characterCount = 0;
971
+ for(let index = 0; index < normalized.length; index += 1){
972
+ const codePoint = normalized.codePointAt(index);
973
+ if (void 0 !== codePoint) {
974
+ if (codePoint > 0xffff) index += 1;
975
+ if (0x0a !== codePoint && 0xfffc !== codePoint) characterCount += 1;
976
+ }
836
977
  }
837
- };
838
- function createDocumentBibliography(style = 'apa') {
839
978
  return {
840
- style,
841
- styleName: STYLE_DETAILS[style].name,
842
- selectedStyle: STYLE_DETAILS[style].selectedStyle,
843
- sources: []
979
+ wordCount: documentWordCount(normalized),
980
+ characterCount
844
981
  };
845
982
  }
846
- function documentCitationStyle(value) {
847
- if ('mla' === value || 'chicago' === value || 'ieee' === value) return value;
848
- return 'apa';
983
+ function documentFieldStatisticsFromHtml(source) {
984
+ const document1 = new DOMParser().parseFromString(source, 'text/html');
985
+ for (const field of Array.from(document1.body.querySelectorAll('[data-document-field]')))field.replaceWith(document1.createTextNode(FIELD_TEXT_BOUNDARY));
986
+ return documentFieldStatisticsFromText(document1.body.textContent ?? '');
849
987
  }
850
- function documentCitationStyleDetails(style) {
851
- return STYLE_DETAILS[style];
988
+ function formatWordDate(date, format) {
989
+ const hour12 = date.getHours() % 12 || 12;
990
+ const replacements = {
991
+ 'AM/PM': date.getHours() < 12 ? 'AM' : 'PM',
992
+ 'am/pm': date.getHours() < 12 ? 'am' : 'pm',
993
+ yyyy: String(date.getFullYear()).padStart(4, '0'),
994
+ yy: String(date.getFullYear() % 100).padStart(2, '0'),
995
+ MMMM: new Intl.DateTimeFormat('zh-CN', {
996
+ month: 'long'
997
+ }).format(date),
998
+ MMM: new Intl.DateTimeFormat('zh-CN', {
999
+ month: 'short'
1000
+ }).format(date),
1001
+ MM: String(date.getMonth() + 1).padStart(2, '0'),
1002
+ M: String(date.getMonth() + 1),
1003
+ dddd: new Intl.DateTimeFormat('zh-CN', {
1004
+ weekday: 'long'
1005
+ }).format(date),
1006
+ ddd: new Intl.DateTimeFormat('zh-CN', {
1007
+ weekday: 'short'
1008
+ }).format(date),
1009
+ dd: String(date.getDate()).padStart(2, '0'),
1010
+ d: String(date.getDate()),
1011
+ HH: String(date.getHours()).padStart(2, '0'),
1012
+ H: String(date.getHours()),
1013
+ hh: String(hour12).padStart(2, '0'),
1014
+ h: String(hour12),
1015
+ mm: String(date.getMinutes()).padStart(2, '0'),
1016
+ m: String(date.getMinutes()),
1017
+ ss: String(date.getSeconds()).padStart(2, '0'),
1018
+ s: String(date.getSeconds())
1019
+ };
1020
+ return format.replace(/AM\/PM|am\/pm|yyyy|MMMM|dddd|MMM|ddd|yy|MM|dd|HH|hh|mm|ss|M|d|H|h|m|s/g, (token)=>replacements[token] ?? token);
852
1021
  }
853
- function documentCitationTagsFromInstruction(instruction) {
854
- const primary = /^\s*CITATION\s+(?:"([^"]+)"|([^\s\\]+))/i.exec(instruction);
855
- if (!primary) return [];
856
- const tags = [
857
- primary[1] || primary[2]
858
- ];
859
- const additional = /\\m\s+(?:"([^"]+)"|([^\s\\]+))/gi;
860
- for (const match of instruction.matchAll(additional))tags.push(match[1] || match[2]);
861
- return uniqueCitationTags(tags);
1022
+ function positiveInteger(value) {
1023
+ return Number.isSafeInteger(value) && value > 0 ? value : 1;
862
1024
  }
863
- function documentCitationInstruction(tags) {
864
- const normalized = uniqueCitationTags(tags);
865
- if (!normalized.length) return '';
866
- return [
867
- 'CITATION',
868
- citationTagInstructionValue(normalized[0]),
869
- ...normalized.slice(1).flatMap((tag)=>[
870
- '\\m',
871
- citationTagInstructionValue(tag)
872
- ]),
873
- '\\l',
874
- '2052'
875
- ].join(' ');
1025
+ function nonNegativeInteger(value, cachedValue) {
1026
+ if (void 0 !== value && Number.isSafeInteger(value) && value >= 0) return value;
1027
+ const cached = Number(cachedValue.trim());
1028
+ return Number.isSafeInteger(cached) && cached >= 0 ? cached : 0;
876
1029
  }
877
- function renameDocumentCitationTagInInstruction(instruction, previousTag, nextTag) {
878
- return instruction.replace(/(^\s*CITATION\s+|\\m\s+)("[^"]+"|[^\s\\]+)/gi, (match, prefix, source)=>{
879
- const tag = source.startsWith('"') ? source.slice(1, -1) : source;
880
- return tag === previousTag ? `${prefix}${citationTagInstructionValue(nextTag)}` : match;
881
- });
1030
+ function normalizeFieldTarget(value) {
1031
+ if ('string' != typeof value) return null;
1032
+ const target = value.trim();
1033
+ return /^[\p{L}_][\p{L}\p{N}_]*$/u.test(target) ? target : null;
882
1034
  }
883
- function documentCitationTags(value) {
884
- return uniqueCitationTags((value ?? '').split(/\s+/));
1035
+ function validDate(value) {
1036
+ return value && Number.isFinite(value.getTime()) ? value : null;
885
1037
  }
886
- function isValidDocumentCitationTag(value) {
887
- return /^[A-Za-z0-9_:.+-]{1,80}$/.test(value);
1038
+ const BOOKMARK_NAME_PATTERN = /^[\p{L}_][\p{L}\p{N}_]*$/u;
1039
+ const BOOKMARK_UI_NAME_PATTERN = /^[\p{L}][\p{L}\p{N}_]*$/u;
1040
+ const MAX_BOOKMARK_NAME_LENGTH = 40;
1041
+ const MAX_BOOKMARK_NATIVE_ID = 0x7fffffff;
1042
+ const MISSING_LINK_CLASS = 'work-document-link-missing';
1043
+ const DOCUMENT_BOOKMARK_VALIDATION_MESSAGE = '书签名称需以字母开头,只能包含字母、数字和下划线,且不超过 40 个字符。';
1044
+ const DOCUMENT_BOOKMARK_DUPLICATE_MESSAGE = '文档中已存在同名书签。';
1045
+ const DocumentBookmarkBoundary = core_Node.create({
1046
+ name: 'documentBookmarkBoundary',
1047
+ inline: true,
1048
+ group: 'inline',
1049
+ atom: true,
1050
+ selectable: false,
1051
+ addCommands () {
1052
+ return {
1053
+ insertDocumentBookmark: (name)=>(props)=>insertDocumentBookmarkCommand(props, name),
1054
+ deleteDocumentBookmark: (id)=>(props)=>deleteDocumentBookmarkCommand(props, id)
1055
+ };
1056
+ },
1057
+ addProseMirrorPlugins () {
1058
+ return [
1059
+ createDocumentBookmarkPlugin(this.name)
1060
+ ];
1061
+ },
1062
+ addAttributes () {
1063
+ return {
1064
+ id: {
1065
+ default: '',
1066
+ parseHTML: (element)=>element.dataset.bookmarkId ?? '',
1067
+ renderHTML: ()=>({})
1068
+ },
1069
+ name: {
1070
+ default: '',
1071
+ parseHTML: (element)=>element.dataset.bookmarkName ?? '',
1072
+ renderHTML: ()=>({})
1073
+ },
1074
+ nativeId: {
1075
+ default: null,
1076
+ parseHTML: (element)=>normalizeDocumentBookmarkNativeId(element.dataset.officeBookmarkId),
1077
+ renderHTML: ()=>({})
1078
+ },
1079
+ kind: {
1080
+ default: 'start',
1081
+ parseHTML: (element)=>documentBookmarkBoundaryKind(element.dataset.bookmarkKind),
1082
+ renderHTML: ()=>({})
1083
+ }
1084
+ };
1085
+ },
1086
+ parseHTML () {
1087
+ return [
1088
+ {
1089
+ tag: 'span[data-document-bookmark-boundary]',
1090
+ getAttrs: (node)=>{
1091
+ if (!(node instanceof HTMLElement)) return false;
1092
+ return {
1093
+ id: node.dataset.bookmarkId ?? '',
1094
+ name: node.dataset.bookmarkName ?? '',
1095
+ nativeId: normalizeDocumentBookmarkNativeId(node.dataset.officeBookmarkId),
1096
+ kind: documentBookmarkBoundaryKind(node.dataset.bookmarkKind)
1097
+ };
1098
+ }
1099
+ }
1100
+ ];
1101
+ },
1102
+ renderHTML ({ node, HTMLAttributes }) {
1103
+ const name = normalizeDocumentBookmarkName(node.attrs.name) ?? '';
1104
+ const nativeId = normalizeDocumentBookmarkNativeId(node.attrs.nativeId);
1105
+ const kind = documentBookmarkBoundaryKind(node.attrs.kind);
1106
+ return [
1107
+ 'span',
1108
+ mergeAttributes(HTMLAttributes, {
1109
+ ...'start' === kind && name ? {
1110
+ id: name
1111
+ } : {},
1112
+ 'data-document-bookmark-boundary': 'true',
1113
+ 'data-bookmark-kind': kind,
1114
+ 'data-bookmark-id': 'string' == typeof node.attrs.id ? node.attrs.id : '',
1115
+ 'data-bookmark-name': name,
1116
+ 'data-office-bookmark-id': null === nativeId ? void 0 : String(nativeId),
1117
+ class: `work-document-bookmark-boundary ${kind}`,
1118
+ contenteditable: 'false',
1119
+ 'aria-hidden': 'true'
1120
+ })
1121
+ ];
1122
+ },
1123
+ renderText () {
1124
+ return '';
1125
+ }
1126
+ });
1127
+ function validateDocumentBookmarkName(value) {
1128
+ const name = value.trim();
1129
+ return BOOKMARK_UI_NAME_PATTERN.test(name) && Array.from(name).length <= MAX_BOOKMARK_NAME_LENGTH ? null : DOCUMENT_BOOKMARK_VALIDATION_MESSAGE;
888
1130
  }
889
- function resolveDocumentCitation(tags, bibliography, instruction = '', cachedValue = '') {
890
- const normalized = uniqueCitationTags(tags);
891
- if (!normalized.length) return {
892
- text: cachedValue || '缺失引文',
893
- orphaned: true
894
- };
895
- const sources = new Map((bibliography?.sources ?? []).map((source)=>[
896
- source.tag,
897
- source
898
- ]));
899
- const missing = normalized.filter((tag)=>!sources.has(tag));
900
- if (missing.length) return {
901
- text: 1 === missing.length ? `缺失引文:${missing[0]}` : `缺失引文:${missing.join('、')}`,
902
- orphaned: true
903
- };
904
- const selected = normalized.flatMap((tag)=>{
905
- const source = sources.get(tag);
906
- return source ? [
907
- source
908
- ] : [];
1131
+ function normalizeDocumentBookmarkName(value) {
1132
+ if ('string' != typeof value) return null;
1133
+ const name = value.trim();
1134
+ return BOOKMARK_NAME_PATTERN.test(name) && Array.from(name).length <= MAX_BOOKMARK_NAME_LENGTH ? name : null;
1135
+ }
1136
+ function normalizeDocumentBookmarkNativeId(value) {
1137
+ if (null == value || '' === value || 'string' == typeof value && !value.trim()) return null;
1138
+ const number = 'number' == typeof value ? value : Number(value);
1139
+ return Number.isSafeInteger(number) && number >= 0 && number <= MAX_BOOKMARK_NATIVE_ID ? number : null;
1140
+ }
1141
+ function editorDocumentBookmarks(editor) {
1142
+ return collectDocumentBookmarkPairs(editor.state.doc, 'documentBookmarkBoundary').pairs.map(({ id, name, nativeId, from, to })=>({
1143
+ id,
1144
+ name,
1145
+ nativeId,
1146
+ from,
1147
+ to
1148
+ }));
1149
+ }
1150
+ function editorDocumentBookmarkReferenceTargets(editor) {
1151
+ return collectDocumentBookmarkPairs(editor.state.doc, 'documentBookmarkBoundary').pairs.map((bookmark)=>{
1152
+ const display = documentBookmarkReferenceDisplay(editor.state.doc, bookmark);
1153
+ return {
1154
+ type: 'bookmark',
1155
+ id: bookmark.id,
1156
+ name: bookmark.name,
1157
+ title: display,
1158
+ display,
1159
+ instruction: documentBookmarkReferenceInstruction(bookmark.name)
1160
+ };
909
1161
  });
910
- const style = bibliography?.style ?? 'apa';
911
- const suppressAuthor = /(?:^|\s)\\n(?:\s|$)/i.test(instruction);
912
- const suppressYear = /(?:^|\s)\\v(?:\s|$)/i.test(instruction);
913
- const prefix = citationSwitch(instruction, 'f');
914
- const suffix = citationSwitch(instruction, 'p');
915
- const text = 'ieee' === style ? ieeeCitation(selected, bibliography?.sources ?? []) : 'mla' === style ? mlaCitation(selected, suppressAuthor) : 'chicago' === style ? chicagoCitation(selected, suppressAuthor, suppressYear) : apaCitation(selected, suppressAuthor, suppressYear);
916
- return {
917
- text: `${prefix}${text}${suffix}`.trim() || cachedValue || normalized.join('; '),
918
- orphaned: false
919
- };
920
1162
  }
921
- function normalizeDocumentCitationsHtml(source, bibliography) {
1163
+ function activeDocumentBookmark(editor) {
1164
+ const { from, to } = editor.state.selection;
1165
+ const matches = editorDocumentBookmarks(editor).filter((bookmark)=>from >= bookmark.from + 1 && to <= bookmark.to);
1166
+ return matches.sort((left, right)=>left.to - left.from - (right.to - right.from))[0] ?? null;
1167
+ }
1168
+ function documentBookmarkNameExists(editor, value, exceptId) {
1169
+ const name = value.trim().toLowerCase();
1170
+ return editorDocumentBookmarks(editor).some((bookmark)=>bookmark.id !== exceptId && bookmark.name.toLowerCase() === name);
1171
+ }
1172
+ function normalizeDocumentBookmarksHtml(source) {
922
1173
  const document1 = new DOMParser().parseFromString(source, 'text/html');
923
- const usedIds = new Set();
924
- for (const [index, element] of Array.from(document1.body.querySelectorAll(CITATION_SELECTOR)).entries()){
925
- const instruction = element.dataset.citationInstruction?.trim() ?? '';
926
- const tags = documentCitationTags(element.dataset.citationTags);
927
- const normalizedTags = tags.length ? tags : documentCitationTagsFromInstruction(instruction);
928
- if (!normalizedTags.length) {
929
- element.replaceWith(document1.createTextNode(element.textContent ?? ''));
930
- continue;
1174
+ const boundaries = Array.from(document1.body.querySelectorAll('span[data-document-bookmark-boundary]'));
1175
+ const collection = collectDomBookmarkPairs(boundaries);
1176
+ for (const orphan of collection.orphans)orphan.remove();
1177
+ const registry = createBookmarkRegistry();
1178
+ for (const pair of collection.pairs){
1179
+ const previous = {
1180
+ id: bookmarkInternalId(pair.id),
1181
+ name: normalizeDocumentBookmarkName(pair.name) ?? ''
1182
+ };
1183
+ const identity = uniqueDocumentBookmarkIdentity(pair, registry);
1184
+ applyBookmarkIdentityToElement(pair.start, identity, 'start');
1185
+ applyBookmarkIdentityToElement(pair.end, identity, 'end');
1186
+ if (previous.id && previous.name && (previous.id !== identity.id || previous.name !== identity.name)) {
1187
+ retargetDomBookmarkReferences(document1.body, pair.start, pair.end, previous, identity);
1188
+ retargetDomPageReferences(document1.body, pair.start, pair.end, previous, identity);
931
1189
  }
932
- const cached = element.dataset.citationDisplay?.trim() || element.textContent?.trim() || '';
933
- const resolved = resolveDocumentCitation(normalizedTags, bibliography, instruction, cached);
934
- element.dataset.documentCitation = 'true';
935
- element.dataset.citationId = uniqueCitationId(element.dataset.citationId, index + 1, usedIds);
936
- element.dataset.citationTags = normalizedTags.join(' ');
937
- element.dataset.citationInstruction = instruction || documentCitationInstruction(normalizedTags);
938
- element.dataset.citationDisplay = resolved.text;
939
- if (resolved.orphaned) element.dataset.citationOrphaned = 'true';
940
- else delete element.dataset.citationOrphaned;
941
- element.classList.add('work-document-citation');
942
- element.textContent = resolved.text;
943
- }
944
- for (const [index, element] of Array.from(document1.body.querySelectorAll(BIBLIOGRAPHY_SELECTOR)).entries()){
945
- if (!bibliography) continue;
946
- const replacement = createBibliographyElement(document1, bibliography, element.dataset.bibliographyId || `document-bibliography-${index + 1}`);
947
- element.replaceWith(replacement);
948
1190
  }
1191
+ synchronizeDomInternalLinks(document1.body, registry.names);
949
1192
  return document1.body.innerHTML;
950
1193
  }
951
- function renderDocumentBibliographyHtml(bibliography, id = 'document-bibliography-1') {
952
- const document1 = new DOMParser().parseFromString('', 'text/html');
953
- document1.body.append(createBibliographyElement(document1, bibliography, id));
954
- return document1.body.innerHTML;
1194
+ function insertDocumentBookmarkCommand({ dispatch, editor, state, tr }, value) {
1195
+ const name = value.trim();
1196
+ const boundaryType = editor.schema.nodes.documentBookmarkBoundary;
1197
+ const selection = state.selection;
1198
+ if (!boundaryType || validateDocumentBookmarkName(name) || documentBookmarkNameExists(editor, name) || !selection.$from.parent.inlineContent || !selection.$to.parent.inlineContent) return false;
1199
+ if (!dispatch) return true;
1200
+ const registry = bookmarkRegistryForDocument(state.doc, boundaryType.name);
1201
+ const identity = uniqueDocumentBookmarkIdentity({
1202
+ id: createUniqueBookmarkId(registry.ids),
1203
+ name,
1204
+ nativeId: nextBookmarkNativeId(registry.nativeIds)
1205
+ }, registry);
1206
+ const start = boundaryType.create({
1207
+ ...identity,
1208
+ kind: 'start'
1209
+ });
1210
+ const end = boundaryType.create({
1211
+ ...identity,
1212
+ kind: 'end'
1213
+ });
1214
+ const from = selection.from;
1215
+ const to = selection.to;
1216
+ tr.insert(to, end);
1217
+ tr.insert(from, start);
1218
+ tr.setSelection(TextSelection.create(tr.doc, from + 1, to + 1));
1219
+ dispatch(tr.scrollIntoView());
1220
+ return true;
955
1221
  }
956
- function documentBibliographyEntry(source, bibliography, index) {
957
- const people = primaryCitationContributor(source);
958
- const authors = people?.corporate?.trim() || people?.people?.map((person)=>bibliographyPersonName(person, bibliography.style)).join(', ') || '未知作者';
959
- const title = source.title.trim() || 'Untitled';
960
- const year = source.year?.trim() || 'n.d.';
961
- const container = citationContainer(source);
962
- const url = source.url?.trim();
963
- if ('ieee' === bibliography.style) return [
964
- `[${index + 1}] ${authors}, “${title}.”`,
965
- container ? `${container},` : '',
966
- year ? `${year}.` : '',
967
- url ?? ''
968
- ].filter(Boolean).join(' ');
969
- if ('mla' === bibliography.style) return [
970
- `${authors}.`,
971
- `“${title}.”`,
972
- container ? `${container},` : '',
973
- `${year}.`,
974
- url ?? ''
975
- ].filter(Boolean).join(' ');
976
- if ('chicago' === bibliography.style) return [
977
- `${authors}.`,
978
- `${year}.`,
979
- `“${title}.”`,
980
- container ? `${container}.` : '',
981
- url ?? ''
982
- ].filter(Boolean).join(' ');
983
- return [
984
- `${authors}.`,
985
- `(${year}).`,
986
- `${title}.`,
987
- container ? `${container}.` : '',
988
- url ?? ''
989
- ].filter(Boolean).join(' ');
1222
+ function deleteDocumentBookmarkCommand({ dispatch, editor, state, tr }, id) {
1223
+ const bookmark = collectDocumentBookmarkPairs(state.doc, editor.schema.nodes.documentBookmarkBoundary?.name ?? '').pairs.find((candidate)=>candidate.id === id);
1224
+ if (!bookmark) return false;
1225
+ if (!dispatch) return true;
1226
+ tr.delete(bookmark.to, bookmark.to + bookmark.end.node.nodeSize);
1227
+ tr.delete(bookmark.from, bookmark.from + bookmark.start.node.nodeSize);
1228
+ tr.setSelection(TextSelection.near(tr.doc.resolve(bookmark.from)));
1229
+ dispatch(tr.scrollIntoView());
1230
+ return true;
990
1231
  }
991
- function primaryCitationContributor(source) {
992
- return source.contributors?.Author ?? Object.values(source.contributors ?? {})[0];
1232
+ function createDocumentBookmarkPlugin(boundaryNodeName) {
1233
+ return new Plugin({
1234
+ view (view) {
1235
+ const transaction = normalizeDocumentBookmarks(view.state, boundaryNodeName);
1236
+ if (transaction) {
1237
+ transaction.setMeta('addToHistory', false);
1238
+ view.dispatch(transaction);
1239
+ }
1240
+ return {};
1241
+ },
1242
+ appendTransaction (transactions, oldState, newState) {
1243
+ if (!transactions.some((transaction)=>transaction.docChanged)) return null;
1244
+ return normalizeDocumentBookmarks(newState, boundaryNodeName, oldState, transactions);
1245
+ }
1246
+ });
993
1247
  }
994
- function createBibliographyElement(document1, bibliography, id) {
995
- const section = document1.createElement('section');
996
- section.dataset.documentBibliography = 'true';
997
- section.dataset.bibliographyId = id;
998
- section.dataset.bibliographyStyle = bibliography.style;
999
- section.className = 'work-document-bibliography';
1000
- const heading = document1.createElement('h2');
1001
- heading.textContent = '参考文献';
1002
- section.append(heading);
1003
- if (!bibliography.sources.length) {
1004
- const empty = document1.createElement('p');
1005
- empty.dataset.bibliographyEmpty = 'true';
1006
- empty.textContent = '尚无文献源';
1007
- section.append(empty);
1008
- return section;
1248
+ function normalizeDocumentBookmarks(state, boundaryNodeName, oldState, transactions = []) {
1249
+ if ('documentBookmarkBoundary' === boundaryNodeName && !documentHasIntegrityFeature(state.doc, 1)) return null;
1250
+ const collection = collectDocumentBookmarkPairs(state.doc, boundaryNodeName);
1251
+ const retained = oldState ? retainedDocumentBookmarkPairs(oldState, state, transactions, boundaryNodeName) : new Set();
1252
+ const ordered = [
1253
+ ...collection.pairs.filter((pair)=>retained.has(pairPositionKey(pair))),
1254
+ ...collection.pairs.filter((pair)=>!retained.has(pairPositionKey(pair)))
1255
+ ];
1256
+ const registry = createBookmarkRegistry();
1257
+ const updates = new Map();
1258
+ const renames = [];
1259
+ const effectivePairs = [];
1260
+ for (const pair of ordered){
1261
+ const identity = uniqueDocumentBookmarkIdentity(pair, registry);
1262
+ if (!sameBookmarkIdentity(pair.start.node.attrs, identity)) updates.set(pair.from, identity);
1263
+ if (!sameBookmarkIdentity(pair.end.node.attrs, identity)) updates.set(pair.to, identity);
1264
+ if (!sameBookmarkIdentity(pair, identity)) {
1265
+ if (pair.id && pair.name && (pair.id !== identity.id || pair.name !== identity.name)) renames.push({
1266
+ from: pair.from,
1267
+ to: pair.to,
1268
+ previousId: pair.id,
1269
+ nextId: identity.id,
1270
+ previousName: pair.name,
1271
+ nextName: identity.name
1272
+ });
1273
+ }
1274
+ effectivePairs.push({
1275
+ ...pair,
1276
+ ...identity
1277
+ });
1009
1278
  }
1010
- bibliography.sources.forEach((source, index)=>{
1011
- const paragraph = document1.createElement('p');
1012
- paragraph.dataset.bibliographyEntry = source.tag;
1013
- paragraph.textContent = documentBibliographyEntry(source, bibliography, index);
1014
- section.append(paragraph);
1279
+ const tr = state.tr;
1280
+ for (const boundary of documentBookmarkBoundaries(state.doc, boundaryNodeName)){
1281
+ const identity = updates.get(boundary.position);
1282
+ if (identity) tr.setNodeMarkup(boundary.position, void 0, {
1283
+ ...boundary.node.attrs,
1284
+ ...identity
1285
+ });
1286
+ }
1287
+ synchronizeInternalLinkMarks(state, tr, effectivePairs, renames);
1288
+ synchronizeDocumentBookmarkReferenceNodes(state, tr, effectivePairs, renames);
1289
+ synchronizeDocumentPageReferenceNodes(state, tr, effectivePairs, renames);
1290
+ for (const orphan of [
1291
+ ...collection.orphans
1292
+ ].sort((left, right)=>right.position - left.position))tr.delete(orphan.position, orphan.position + orphan.node.nodeSize);
1293
+ return tr.docChanged ? tr : null;
1294
+ }
1295
+ function retainedDocumentBookmarkPairs(oldState, newState, transactions, boundaryNodeName) {
1296
+ const mapping = transactionMapping(transactions);
1297
+ const retained = new Set();
1298
+ const current = collectDocumentBookmarkPairs(newState.doc, boundaryNodeName).pairs;
1299
+ const currentById = new Map();
1300
+ for (const pair of current){
1301
+ const matches = currentById.get(pair.id) ?? [];
1302
+ matches.push(pair);
1303
+ currentById.set(pair.id, matches);
1304
+ }
1305
+ for (const previous of collectDocumentBookmarkPairs(oldState.doc, boundaryNodeName).pairs){
1306
+ const mappedFrom = mapping.mapResult(previous.from, 1);
1307
+ const mappedTo = mapping.mapResult(previous.to, 1);
1308
+ const exact = current.find((pair)=>pair.from === mappedFrom.pos && pair.to === mappedTo.pos && pair.id === previous.id);
1309
+ if (exact) {
1310
+ retained.add(pairPositionKey(exact));
1311
+ continue;
1312
+ }
1313
+ const sameIdentity = currentById.get(previous.id);
1314
+ if (sameIdentity?.length === 1) retained.add(pairPositionKey(sameIdentity[0]));
1315
+ }
1316
+ return retained;
1317
+ }
1318
+ function synchronizeInternalLinkMarks(state, tr, bookmarks, renames) {
1319
+ const linkType = state.schema.marks.link;
1320
+ if (!linkType) return;
1321
+ const names = new Set(bookmarks.map((bookmark)=>bookmark.name.toLowerCase()));
1322
+ state.doc.descendants((node, position)=>{
1323
+ if (!node.isText) return;
1324
+ const link = node.marks.find((mark)=>mark.type === linkType);
1325
+ if (!link) return;
1326
+ const href = 'string' == typeof link.attrs.href ? link.attrs.href : '';
1327
+ if (!href.startsWith('#')) return;
1328
+ const target = href.slice(1);
1329
+ const rename = renames.filter((candidate)=>position > candidate.from && position < candidate.to && target.toLowerCase() === candidate.previousName.toLowerCase()).sort((left, right)=>left.to - left.from - (right.to - right.from))[0];
1330
+ const nextHref = rename ? `#${rename.nextName}` : href;
1331
+ const nextTarget = nextHref.slice(1).toLowerCase();
1332
+ const nextClass = toggleClassToken(link.attrs.class, MISSING_LINK_CLASS, !names.has(nextTarget));
1333
+ if (nextHref === href && nextClass === normalizedClass(link.attrs.class)) return;
1334
+ replaceLinkMark(state, tr, node, position, link, {
1335
+ ...link.attrs,
1336
+ href: nextHref,
1337
+ class: nextClass || null
1338
+ });
1015
1339
  });
1016
- return section;
1017
1340
  }
1018
- function apaCitation(sources, suppressAuthor, suppressYear) {
1019
- const items = sources.map((source)=>{
1020
- const author = suppressAuthor ? '' : citationAuthor(source, '&');
1021
- const year = suppressYear ? '' : source.year?.trim() || 'n.d.';
1022
- return [
1023
- author,
1024
- year
1025
- ].filter(Boolean).join(', ');
1026
- });
1027
- return `(${items.join('; ')})`;
1341
+ function replaceLinkMark(_state, tr, node, position, link, attributes) {
1342
+ tr.removeMark(position, position + node.nodeSize, link.type);
1343
+ tr.addMark(position, position + node.nodeSize, link.type.create(attributes));
1028
1344
  }
1029
- function mlaCitation(sources, suppressAuthor) {
1030
- return `(${sources.map((source)=>suppressAuthor ? source.year?.trim() || 'n.d.' : citationAuthor(source, 'and')).join('; ')})`;
1345
+ function collectDocumentBookmarkPairs(document1, boundaryNodeName) {
1346
+ const boundaries = documentBookmarkBoundaries(document1, boundaryNodeName);
1347
+ const open = new Map();
1348
+ const pairs = [];
1349
+ const orphans = [];
1350
+ for (const boundary of boundaries){
1351
+ const key = bookmarkBoundaryPairKey(boundary.node.attrs);
1352
+ if ('start' === boundary.kind) {
1353
+ const stack = open.get(key) ?? [];
1354
+ stack.push(boundary);
1355
+ open.set(key, stack);
1356
+ continue;
1357
+ }
1358
+ const stack = open.get(key);
1359
+ const start = stack?.pop();
1360
+ if (!start) {
1361
+ orphans.push(boundary);
1362
+ continue;
1363
+ }
1364
+ pairs.push(bookmarkPair(start, boundary));
1365
+ }
1366
+ for (const stack of open.values())orphans.push(...stack);
1367
+ pairs.sort((left, right)=>left.from - right.from || left.to - right.to);
1368
+ return {
1369
+ pairs,
1370
+ orphans
1371
+ };
1031
1372
  }
1032
- function chicagoCitation(sources, suppressAuthor, suppressYear) {
1033
- return `(${sources.map((source)=>[
1034
- suppressAuthor ? '' : citationAuthor(source, 'and'),
1035
- suppressYear ? '' : source.year?.trim() || 'n.d.'
1036
- ].filter(Boolean).join(' ')).join('; ')})`;
1373
+ function documentBookmarkBoundaries(document1, boundaryNodeName) {
1374
+ if (!boundaryNodeName) return [];
1375
+ const boundaries = [];
1376
+ document1.descendants((node, position)=>{
1377
+ if (node.type.name !== boundaryNodeName) return;
1378
+ boundaries.push({
1379
+ node,
1380
+ position,
1381
+ kind: documentBookmarkBoundaryKind(node.attrs.kind)
1382
+ });
1383
+ });
1384
+ return boundaries;
1037
1385
  }
1038
- function ieeeCitation(sources, allSources) {
1039
- const indexes = sources.map((source)=>allSources.findIndex((candidate)=>candidate.id === source.id || candidate.tag === source.tag) + 1).filter((index)=>index > 0);
1040
- return `[${indexes.join(', ')}]`;
1386
+ function bookmarkPair(start, end) {
1387
+ return {
1388
+ id: bookmarkInternalId(start.node.attrs.id),
1389
+ name: normalizeDocumentBookmarkName(start.node.attrs.name) ?? '',
1390
+ nativeId: normalizeDocumentBookmarkNativeId(start.node.attrs.nativeId) ?? -1,
1391
+ from: start.position,
1392
+ to: end.position,
1393
+ start,
1394
+ end
1395
+ };
1041
1396
  }
1042
- function citationAuthor(source, conjunction) {
1043
- const contributor = primaryCitationContributor(source);
1044
- if (contributor?.corporate?.trim()) return contributor.corporate.trim();
1045
- const names = (contributor?.people ?? []).map((person)=>person.last.trim() || person.first.trim()).filter(Boolean);
1046
- if (!names.length) return source.title.trim() || source.tag;
1047
- if (1 === names.length) return names[0];
1048
- if (2 === names.length) return `${names[0]} ${conjunction} ${names[1]}`;
1049
- return `${names[0]} et al.`;
1397
+ function bookmarkRegistryForDocument(document1, boundaryNodeName) {
1398
+ const registry = createBookmarkRegistry();
1399
+ for (const pair of collectDocumentBookmarkPairs(document1, boundaryNodeName).pairs){
1400
+ const identity = bookmarkIdentity(pair);
1401
+ if (identity) reserveBookmarkIdentity(identity, registry);
1402
+ }
1403
+ return registry;
1050
1404
  }
1051
- function bibliographyPersonName(person, style) {
1052
- const first = [
1053
- person.first,
1054
- person.middle
1055
- ].filter(Boolean).join(' ').trim();
1056
- const suffix = person.suffix?.trim();
1057
- if ('ieee' === style) {
1058
- const initials = [
1059
- person.first,
1060
- person.middle
1061
- ].filter(Boolean).map((value)=>`${Array.from(value ?? '')[0] ?? ''}.`).join(' ');
1062
- return [
1063
- initials,
1064
- person.last,
1065
- suffix
1066
- ].filter(Boolean).join(' ');
1405
+ function uniqueDocumentBookmarkIdentity(source, registry) {
1406
+ const preferred = bookmarkIdentity(source);
1407
+ if (preferred && !bookmarkIdentityConflicts(preferred, registry)) {
1408
+ reserveBookmarkIdentity(preferred, registry);
1409
+ return preferred;
1067
1410
  }
1068
- return [
1069
- person.last,
1070
- first ? `, ${first}` : '',
1071
- suffix ? `, ${suffix}` : ''
1072
- ].join('');
1411
+ const baseName = normalizeDocumentBookmarkName(source.name) ?? 'Bookmark';
1412
+ const identity = {
1413
+ id: createUniqueBookmarkId(registry.ids),
1414
+ name: uniqueBookmarkName(baseName, registry.names),
1415
+ nativeId: nextBookmarkNativeId(registry.nativeIds)
1416
+ };
1417
+ reserveBookmarkIdentity(identity, registry);
1418
+ return identity;
1073
1419
  }
1074
- function citationContainer(source) {
1075
- return source.journalName?.trim() || source.publisher?.trim() || source.conferenceName?.trim() || source.institution?.trim() || '';
1420
+ function bookmarkIdentity(source) {
1421
+ const id = bookmarkInternalId(source.id);
1422
+ const name = normalizeDocumentBookmarkName(source.name);
1423
+ const nativeId = normalizeDocumentBookmarkNativeId(source.nativeId);
1424
+ return id && name && null !== nativeId ? {
1425
+ id,
1426
+ name,
1427
+ nativeId
1428
+ } : null;
1076
1429
  }
1077
- function citationSwitch(instruction, name) {
1078
- const expression = new RegExp(`\\\\${name}\\s+"([^"]*)"`, 'i');
1079
- return expression.exec(instruction)?.[1] ?? '';
1430
+ function bookmarkIdentityConflicts(identity, registry) {
1431
+ return registry.ids.has(identity.id) || registry.names.has(identity.name.toLowerCase()) || registry.nativeIds.has(identity.nativeId);
1080
1432
  }
1081
- function citationTagInstructionValue(tag) {
1082
- return /^[A-Za-z0-9_:.+-]+$/.test(tag) ? tag : `"${tag.replaceAll('"', '')}"`;
1433
+ function reserveBookmarkIdentity(identity, registry) {
1434
+ registry.ids.add(identity.id);
1435
+ registry.names.add(identity.name.toLowerCase());
1436
+ registry.nativeIds.add(identity.nativeId);
1083
1437
  }
1084
- function uniqueCitationTags(tags) {
1085
- const result = [];
1086
- const seen = new Set();
1087
- for (const source of tags){
1088
- const tag = source.trim();
1089
- if (!(!tag || seen.has(tag))) {
1090
- seen.add(tag);
1091
- result.push(tag);
1092
- }
1438
+ function createBookmarkRegistry() {
1439
+ return {
1440
+ ids: new Set(),
1441
+ names: new Set(),
1442
+ nativeIds: new Set()
1443
+ };
1444
+ }
1445
+ function createUniqueBookmarkId(ids) {
1446
+ for(let attempt = 0; attempt < 8; attempt += 1){
1447
+ const id = createWorkId('bookmark');
1448
+ if (!ids.has(id)) return id;
1093
1449
  }
1094
- return result;
1450
+ let suffix = 1;
1451
+ while(ids.has(`bookmark-${suffix}`))suffix += 1;
1452
+ return `bookmark-${suffix}`;
1095
1453
  }
1096
- function uniqueCitationId(source, index, usedIds) {
1097
- const candidate = source?.trim();
1098
- if (candidate && !usedIds.has(candidate)) {
1099
- usedIds.add(candidate);
1100
- return candidate;
1454
+ function uniqueBookmarkName(base, names) {
1455
+ if (!names.has(base.toLowerCase())) return base;
1456
+ let suffix = 2;
1457
+ while(suffix <= MAX_BOOKMARK_NATIVE_ID){
1458
+ const ending = `_${suffix}`;
1459
+ const prefix = Array.from(base).slice(0, MAX_BOOKMARK_NAME_LENGTH - ending.length).join('');
1460
+ const candidate = `${prefix}${ending}`;
1461
+ if (!names.has(candidate.toLowerCase())) return candidate;
1462
+ suffix += 1;
1101
1463
  }
1102
- let suffix = index;
1103
- while(usedIds.has(`document-citation-${suffix}`))suffix += 1;
1104
- const id = `document-citation-${suffix}`;
1105
- usedIds.add(id);
1106
- return id;
1464
+ throw new Error('No unique Word bookmark name is available.');
1107
1465
  }
1108
- const DEFAULT_DOCUMENT_COLUMNS = {
1109
- count: 1,
1110
- spacing: 12,
1111
- separator: false
1112
- };
1113
- const MAX_COLUMNS = 6;
1114
- const MIN_COLUMN_PERCENT = 5;
1115
- function normalizeDocumentColumns(columns) {
1116
- const customCount = Array.isArray(columns?.custom) ? columns.custom.length : 0;
1117
- const count = clampInteger(columns?.count, customCount || DEFAULT_DOCUMENT_COLUMNS.count, 1, MAX_COLUMNS);
1118
- const spacing = clampNumber(columns?.spacing, DEFAULT_DOCUMENT_COLUMNS.spacing, 0, 30);
1119
- const normalized = {
1120
- count,
1121
- spacing: roundOne(spacing),
1122
- separator: Boolean(columns?.separator)
1466
+ function nextBookmarkNativeId(ids) {
1467
+ for(let id = 0; id <= MAX_BOOKMARK_NATIVE_ID; id += 1)if (!ids.has(id)) return id;
1468
+ throw new Error('No unique Word bookmark identifier is available.');
1469
+ }
1470
+ function sameBookmarkIdentity(source, identity) {
1471
+ return bookmarkInternalId(source.id) === identity.id && normalizeDocumentBookmarkName(source.name) === identity.name && normalizeDocumentBookmarkNativeId(source.nativeId) === identity.nativeId;
1472
+ }
1473
+ function bookmarkInternalId(value) {
1474
+ return 'string' == typeof value ? value.trim() : '';
1475
+ }
1476
+ function bookmarkBoundaryPairKey(attributes) {
1477
+ const id = bookmarkInternalId(attributes.id);
1478
+ if (id) return `id:${id}`;
1479
+ return `legacy:${String(attributes.name ?? '')}:${String(attributes.nativeId ?? '')}`;
1480
+ }
1481
+ function documentBookmarkBoundaryKind(value) {
1482
+ return 'end' === value ? 'end' : 'start';
1483
+ }
1484
+ function transactionMapping(transactions) {
1485
+ const mapping = new Mapping();
1486
+ for (const transaction of transactions)mapping.appendMapping(transaction.mapping);
1487
+ return mapping;
1488
+ }
1489
+ function pairPositionKey(pair) {
1490
+ return `${pair.from}:${pair.to}`;
1491
+ }
1492
+ function collectDomBookmarkPairs(boundaries) {
1493
+ const open = new Map();
1494
+ const pairs = [];
1495
+ const orphans = [];
1496
+ for (const boundary of boundaries){
1497
+ const key = domBookmarkPairKey(boundary);
1498
+ if ('start' === documentBookmarkBoundaryKind(boundary.dataset.bookmarkKind)) {
1499
+ const stack = open.get(key) ?? [];
1500
+ stack.push(boundary);
1501
+ open.set(key, stack);
1502
+ continue;
1503
+ }
1504
+ const start = open.get(key)?.pop();
1505
+ if (!start) {
1506
+ orphans.push(boundary);
1507
+ continue;
1508
+ }
1509
+ pairs.push({
1510
+ id: start.dataset.bookmarkId,
1511
+ name: start.dataset.bookmarkName,
1512
+ nativeId: normalizeDocumentBookmarkNativeId(start.dataset.officeBookmarkId) ?? void 0,
1513
+ start,
1514
+ end: boundary
1515
+ });
1516
+ }
1517
+ for (const stack of open.values())orphans.push(...stack);
1518
+ return {
1519
+ pairs,
1520
+ orphans
1123
1521
  };
1124
- if (count < 2 || !customCount) return normalized;
1125
- const source = columns?.custom ?? [];
1126
- const widths = normalizedPercentages(Array.from({
1127
- length: count
1128
- }, (_, index)=>finiteNumber(source[index]?.widthPercent, 100 / count)), 100, MIN_COLUMN_PERCENT);
1129
- normalized.custom = Array.from({
1130
- length: count
1131
- }, (_, index)=>({
1132
- widthPercent: widths[index],
1133
- spacing: index === count - 1 ? 0 : roundOne(clampNumber(source[index]?.spacing, normalized.spacing, 0, 30))
1134
- }));
1135
- return normalized;
1136
1522
  }
1137
- function serializeDocumentColumns(columns) {
1138
- return JSON.stringify(normalizeDocumentColumns(columns));
1523
+ function domBookmarkPairKey(element) {
1524
+ const id = element.dataset.bookmarkId?.trim();
1525
+ return id ? `id:${id}` : `legacy:${element.dataset.bookmarkName ?? ''}:${element.dataset.officeBookmarkId ?? ''}`;
1139
1526
  }
1140
- function parseDocumentColumns(source, legacy = {}, fallback) {
1141
- if (source?.trim()) try {
1142
- return normalizeDocumentColumns(JSON.parse(source));
1143
- } catch {}
1144
- if (void 0 !== legacy.count || void 0 !== legacy.spacing || void 0 !== legacy.separator) return normalizeDocumentColumns(legacy);
1145
- return normalizeDocumentColumns(fallback);
1527
+ function applyBookmarkIdentityToElement(element, identity, kind) {
1528
+ element.dataset.documentBookmarkBoundary = 'true';
1529
+ element.dataset.bookmarkKind = kind;
1530
+ element.dataset.bookmarkId = identity.id;
1531
+ element.dataset.bookmarkName = identity.name;
1532
+ element.dataset.officeBookmarkId = String(identity.nativeId);
1533
+ element.classList.add('work-document-bookmark-boundary', kind);
1534
+ element.contentEditable = 'false';
1535
+ element.setAttribute('aria-hidden', 'true');
1536
+ if ('start' === kind) element.id = identity.name;
1537
+ else element.removeAttribute('id');
1146
1538
  }
1147
- function setCustomDocumentColumns(columns, enabled) {
1148
- const normalized = normalizeDocumentColumns(columns);
1149
- if (!enabled || normalized.count < 2) return {
1150
- ...normalized,
1151
- custom: void 0
1152
- };
1153
- if (normalized.custom) return normalized;
1154
- return normalizeDocumentColumns({
1155
- ...normalized,
1156
- custom: Array.from({
1157
- length: normalized.count
1158
- }, (_, index)=>({
1159
- widthPercent: 100 / normalized.count,
1160
- spacing: index === normalized.count - 1 ? 0 : normalized.spacing
1161
- }))
1162
- });
1539
+ function synchronizeDomInternalLinks(root, names) {
1540
+ for (const link of root.querySelectorAll('a[href^="#"]')){
1541
+ const target = (link.getAttribute('href') ?? '').slice(1).toLowerCase();
1542
+ link.classList.toggle(MISSING_LINK_CLASS, !names.has(target));
1543
+ if (!link.className) link.removeAttribute('class');
1544
+ }
1163
1545
  }
1164
- function updateDocumentColumnWidth(columns, index, widthPercent) {
1165
- const normalized = setCustomDocumentColumns(columns, true);
1166
- if (!normalized.custom?.[index]) return normalized;
1167
- const maximum = 100 - MIN_COLUMN_PERCENT * (normalized.custom.length - 1);
1168
- const target = Math.min(maximum, Math.max(MIN_COLUMN_PERCENT, finiteNumber(widthPercent, 100 / normalized.count)));
1169
- const otherIndexes = normalized.custom.map((_, itemIndex)=>itemIndex).filter((itemIndex)=>itemIndex !== index);
1170
- const otherWidths = normalizedPercentages(otherIndexes.map((itemIndex)=>normalized.custom?.[itemIndex].widthPercent ?? 1), 100 - target, MIN_COLUMN_PERCENT);
1171
- const custom = normalized.custom.map((column, itemIndex)=>{
1172
- const otherIndex = otherIndexes.indexOf(itemIndex);
1173
- return {
1174
- ...column,
1175
- widthPercent: itemIndex === index ? roundOne(target) : otherWidths[otherIndex]
1176
- };
1546
+ function synchronizeDocumentPageReferenceNodes(state, tr, bookmarks, renames) {
1547
+ state.doc.descendants((node, position)=>{
1548
+ if ('documentField' !== node.type.name) return;
1549
+ const kind = 'pageReference' === node.attrs.kind ? 'pageReference' : docxDocumentFieldKind(work_document_bookmarks_stringAttribute(node.attrs.instruction));
1550
+ if ('pageReference' !== kind) return;
1551
+ let targetId = work_document_bookmarks_stringAttribute(node.attrs.targetId);
1552
+ let targetName = work_document_bookmarks_stringAttribute(node.attrs.targetName) || docxDocumentFieldTarget(work_document_bookmarks_stringAttribute(node.attrs.instruction)) || '';
1553
+ const rename = renames.filter((candidate)=>position > candidate.from && position < candidate.to && (targetId && targetId === candidate.previousId || targetName && targetName.toLowerCase() === candidate.previousName.toLowerCase())).sort((left, right)=>left.to - left.from - (right.to - right.from))[0];
1554
+ if (rename) {
1555
+ targetId = rename.nextId;
1556
+ targetName = rename.nextName;
1557
+ }
1558
+ const target = bookmarks.find((bookmark)=>targetId && bookmark.id === targetId || targetName && bookmark.name.toLowerCase() === targetName.toLowerCase()) ?? null;
1559
+ const nextTargetId = target?.id ?? targetId;
1560
+ const nextTargetName = target?.name ?? targetName;
1561
+ const instruction = target ? documentPageReferenceInstruction(nextTargetName, work_document_bookmarks_stringAttribute(node.attrs.instruction)) : work_document_bookmarks_stringAttribute(node.attrs.instruction);
1562
+ const orphaned = !target;
1563
+ if (node.attrs.targetId === nextTargetId && node.attrs.targetName === nextTargetName && node.attrs.instruction === instruction && node.attrs.orphaned === orphaned) return;
1564
+ tr.setNodeMarkup(position, void 0, {
1565
+ ...node.attrs,
1566
+ targetId: nextTargetId,
1567
+ targetName: nextTargetName,
1568
+ instruction,
1569
+ orphaned
1570
+ });
1177
1571
  });
1178
- return {
1179
- ...normalized,
1180
- custom
1181
- };
1182
1572
  }
1183
- function normalizedPercentages(values, total, minimum) {
1184
- if (!values.length) return [];
1185
- const result = Array(values.length).fill(0);
1186
- const remaining = new Set(values.map((_, index)=>index));
1187
- let remainingTotal = total;
1188
- while(remaining.size){
1189
- const sourceTotal = Array.from(remaining).reduce((sum, index)=>sum + Math.max(0.001, finiteNumber(values[index], 1)), 0);
1190
- const belowMinimum = Array.from(remaining).filter((index)=>Math.max(0.001, finiteNumber(values[index], 1)) / sourceTotal * remainingTotal < minimum);
1191
- if (!belowMinimum.length) {
1192
- for (const index of remaining)result[index] = Math.max(0.001, finiteNumber(values[index], 1)) / sourceTotal * remainingTotal;
1193
- break;
1194
- }
1195
- for (const index of belowMinimum){
1196
- result[index] = minimum;
1197
- remaining.delete(index);
1198
- remainingTotal -= minimum;
1573
+ function retargetDomPageReferences(root, start, end, previous, next) {
1574
+ const range = root.ownerDocument.createRange();
1575
+ range.setStartAfter(start);
1576
+ range.setEndBefore(end);
1577
+ for (const field of root.querySelectorAll('span[data-document-field][data-field-kind="pageReference"]')){
1578
+ if (!range.intersectsNode(field)) continue;
1579
+ const id = field.dataset.fieldTargetId?.trim() ?? '';
1580
+ const name = field.dataset.fieldTargetName?.trim() ?? '';
1581
+ if (id === previous.id || name.toLowerCase() === previous.name.toLowerCase()) {
1582
+ field.dataset.fieldTargetId = next.id;
1583
+ field.dataset.fieldTargetName = next.name;
1584
+ field.dataset.fieldInstruction = documentPageReferenceInstruction(next.name, field.dataset.fieldInstruction);
1585
+ delete field.dataset.fieldOrphaned;
1199
1586
  }
1200
1587
  }
1201
- const rounded = result.map(roundOne);
1202
- const adjustmentIndex = rounded.indexOf(Math.max(...rounded));
1203
- rounded[adjustmentIndex] = roundOne(rounded[adjustmentIndex] + total - rounded.reduce((sum, value)=>sum + value, 0));
1204
- return rounded;
1205
1588
  }
1206
- function clampInteger(value, fallback, minimum, maximum) {
1207
- return Math.min(maximum, Math.max(minimum, Math.round(finiteNumber(value, fallback))));
1208
- }
1209
- function clampNumber(value, fallback, minimum, maximum) {
1210
- return Math.min(maximum, Math.max(minimum, finiteNumber(value, fallback)));
1211
- }
1212
- function finiteNumber(value, fallback) {
1213
- return Number.isFinite(value) ? Number(value) : fallback;
1589
+ function normalizedClass(value) {
1590
+ return 'string' == typeof value ? value.trim().split(/\s+/).filter(Boolean).join(' ') : '';
1214
1591
  }
1215
- function roundOne(value) {
1216
- return Math.round(10 * value) / 10;
1592
+ function toggleClassToken(value, token, enabled) {
1593
+ const tokens = new Set(normalizedClass(value).split(' ').filter(Boolean));
1594
+ if (enabled) tokens.add(token);
1595
+ else tokens.delete(token);
1596
+ return Array.from(tokens).join(' ');
1217
1597
  }
1218
- const FIELD_SELECTOR = 'span[data-document-field]';
1219
- const FIELD_COMMANDS = {
1220
- page: 'PAGE',
1221
- numPages: 'NUMPAGES',
1222
- section: 'SECTION',
1223
- sectionPages: 'SECTIONPAGES',
1224
- date: 'DATE \\@ "yyyy年M月d日"',
1225
- time: 'TIME \\@ "HH:mm"'
1226
- };
1227
- const FIELD_LABELS = {
1228
- page: '当前页码',
1229
- numPages: '总页数',
1230
- section: '当前节号',
1231
- sectionPages: '本节页数',
1232
- date: '当前日期',
1233
- time: '当前时间'
1234
- };
1235
- function documentFieldKind(value) {
1236
- if ('page' === value || 'numPages' === value || 'section' === value || 'sectionPages' === value || 'date' === value || 'time' === value) return value;
1237
- return null;
1598
+ function work_document_bookmarks_stringAttribute(value) {
1599
+ return 'string' == typeof value ? value.trim() : '';
1238
1600
  }
1239
- function docxDocumentFieldKind(instruction) {
1240
- const command = /^\s*([a-z][a-z0-9]*)\b/i.exec(instruction)?.[1]?.toUpperCase();
1241
- if ('PAGE' === command) return 'page';
1242
- if ('NUMPAGES' === command) return 'numPages';
1243
- if ('SECTION' === command) return 'section';
1244
- if ('SECTIONPAGES' === command) return 'sectionPages';
1245
- if ('DATE' === command) return 'date';
1246
- if ('TIME' === command) return 'time';
1247
- return null;
1601
+ const CAPTION_SELECTOR = 'figcaption[data-document-caption]';
1602
+ const REFERENCE_SELECTOR = 'span[data-document-cross-reference]';
1603
+ function normalizeDocumentCaptionsHtml(source) {
1604
+ const document1 = new DOMParser().parseFromString(source, 'text/html');
1605
+ const targets = normalizeCaptions(document1);
1606
+ normalizeReferences(document1, new Map(targets.map((target)=>[
1607
+ target.id,
1608
+ target
1609
+ ])));
1610
+ return document1.body.innerHTML;
1248
1611
  }
1249
- function documentFieldInstruction(kind) {
1250
- return FIELD_COMMANDS[kind];
1612
+ function documentCaptionKind(value) {
1613
+ return 'figure' === value || 'table' === value ? value : null;
1251
1614
  }
1252
- function documentFieldLabel(kind) {
1253
- return FIELD_LABELS[kind];
1615
+ function documentCaptionLabel(kind) {
1616
+ return 'table' === kind ? '表' : '图';
1254
1617
  }
1255
- function documentFieldDisplay(kind, context, instruction = documentFieldInstruction(kind), cachedValue = '') {
1256
- if ('page' === kind) return String(work_document_fields_positiveInteger(context.pageNumber));
1257
- if ('numPages' === kind) return String(work_document_fields_positiveInteger(context.totalPages));
1258
- if ('section' === kind) return String(work_document_fields_positiveInteger(context.sectionNumber));
1259
- if ('sectionPages' === kind) return String(work_document_fields_positiveInteger(context.sectionPages));
1260
- const now = validDate(context.now) ?? new Date();
1261
- const format = dateFormatSwitch(instruction) ?? ('date' === kind ? 'yyyy年M月d日' : 'HH:mm');
1262
- const display = formatWordDate(now, format);
1263
- return display || cachedValue || documentFieldLabel(kind);
1618
+ function documentCaptionDisplay(kind, number) {
1619
+ return `${documentCaptionLabel(kind)} ${work_document_captions_positiveInteger(number)}`;
1264
1620
  }
1265
- function normalizeDocumentFieldsHtml(source) {
1266
- const document1 = new DOMParser().parseFromString(source, 'text/html');
1621
+ function normalizeCaptions(document1) {
1622
+ const counters = {
1623
+ figure: 0,
1624
+ table: 0
1625
+ };
1267
1626
  const usedIds = new Set();
1268
- for (const [index, element] of Array.from(document1.body.querySelectorAll(FIELD_SELECTOR)).entries()){
1269
- const instruction = element.dataset.fieldInstruction?.trim() ?? '';
1270
- const kind = documentFieldKind(element.dataset.fieldKind) ?? docxDocumentFieldKind(instruction);
1271
- if (!kind) {
1272
- element.replaceWith(document1.createTextNode(element.textContent ?? ''));
1627
+ return Array.from(document1.body.querySelectorAll(CAPTION_SELECTOR)).map((element, index)=>{
1628
+ const kind = documentCaptionKind(element.dataset.captionKind) ?? 'figure';
1629
+ counters[kind] += 1;
1630
+ const number = counters[kind];
1631
+ const id = uniqueCaptionId(element.dataset.captionId, kind, index + 1, usedIds);
1632
+ const label = documentCaptionLabel(kind);
1633
+ element.dataset.documentCaption = 'true';
1634
+ element.dataset.captionId = id;
1635
+ element.dataset.captionKind = kind;
1636
+ element.dataset.captionNumber = String(number);
1637
+ element.dataset.captionLabel = label;
1638
+ element.classList.add('work-document-caption');
1639
+ const title = element.textContent?.replace(/\s+/g, ' ').trim() ?? '';
1640
+ const display = documentCaptionDisplay(kind, number);
1641
+ element.setAttribute('aria-label', title ? `${display} ${title}` : display);
1642
+ return {
1643
+ id,
1644
+ kind,
1645
+ number,
1646
+ label,
1647
+ title,
1648
+ display
1649
+ };
1650
+ });
1651
+ }
1652
+ function normalizeReferences(document1, targets) {
1653
+ for (const element of Array.from(document1.body.querySelectorAll(REFERENCE_SELECTOR))){
1654
+ if ('bookmark' === element.dataset.referenceTargetType) continue;
1655
+ const id = element.dataset.referenceTargetId?.trim() ?? '';
1656
+ const target = targets.get(id);
1657
+ element.dataset.documentCrossReference = 'true';
1658
+ element.dataset.referenceTargetId = id;
1659
+ element.classList.add('work-document-cross-reference');
1660
+ if (!target) {
1661
+ element.dataset.referenceOrphaned = 'true';
1662
+ element.textContent = '引用缺失';
1273
1663
  continue;
1274
1664
  }
1275
- const display = element.dataset.fieldDisplay?.trim() || element.textContent?.trim() || documentFieldLabel(kind);
1276
- element.dataset.documentField = 'true';
1277
- element.dataset.fieldId = uniqueFieldId(element.dataset.fieldId, index + 1, usedIds);
1278
- element.dataset.fieldKind = kind;
1279
- element.dataset.fieldInstruction = instruction || documentFieldInstruction(kind);
1280
- element.dataset.fieldDisplay = display;
1281
- element.classList.add('work-document-field');
1282
- element.textContent = display;
1283
- }
1284
- return document1.body.innerHTML;
1285
- }
1286
- function resolveDocumentFieldsHtml(source, context) {
1287
- const document1 = new DOMParser().parseFromString(normalizeDocumentFieldsHtml(source), 'text/html');
1288
- for (const element of Array.from(document1.body.querySelectorAll(FIELD_SELECTOR))){
1289
- const kind = documentFieldKind(element.dataset.fieldKind);
1290
- if (!kind) continue;
1291
- const display = documentFieldDisplay(kind, context, element.dataset.fieldInstruction, element.dataset.fieldDisplay);
1292
- element.dataset.fieldDisplay = display;
1293
- element.textContent = display;
1665
+ delete element.dataset.referenceOrphaned;
1666
+ element.dataset.captionKind = target.kind;
1667
+ element.dataset.captionNumber = String(target.number);
1668
+ element.dataset.captionLabel = target.label;
1669
+ element.textContent = target.display;
1294
1670
  }
1295
- return document1.body.innerHTML;
1296
1671
  }
1297
- function uniqueFieldId(source, index, usedIds) {
1672
+ function uniqueCaptionId(source, kind, index, usedIds) {
1298
1673
  const candidate = source?.trim();
1299
1674
  if (candidate && !usedIds.has(candidate)) {
1300
1675
  usedIds.add(candidate);
1301
1676
  return candidate;
1302
1677
  }
1303
1678
  let suffix = index;
1304
- while(usedIds.has(`document-field-${suffix}`))suffix += 1;
1305
- const id = `document-field-${suffix}`;
1679
+ while(usedIds.has(`document-${kind}-caption-${suffix}`))suffix += 1;
1680
+ const id = `document-${kind}-caption-${suffix}`;
1306
1681
  usedIds.add(id);
1307
1682
  return id;
1308
1683
  }
1309
- function dateFormatSwitch(instruction) {
1310
- return /\\@\s+"([^"]+)"/i.exec(instruction)?.[1] ?? null;
1311
- }
1312
- function formatWordDate(date, format) {
1313
- const hour12 = date.getHours() % 12 || 12;
1314
- const replacements = {
1315
- 'AM/PM': date.getHours() < 12 ? 'AM' : 'PM',
1316
- 'am/pm': date.getHours() < 12 ? 'am' : 'pm',
1317
- yyyy: String(date.getFullYear()).padStart(4, '0'),
1318
- yy: String(date.getFullYear() % 100).padStart(2, '0'),
1319
- MMMM: new Intl.DateTimeFormat('zh-CN', {
1320
- month: 'long'
1321
- }).format(date),
1322
- MMM: new Intl.DateTimeFormat('zh-CN', {
1323
- month: 'short'
1324
- }).format(date),
1325
- MM: String(date.getMonth() + 1).padStart(2, '0'),
1326
- M: String(date.getMonth() + 1),
1327
- dddd: new Intl.DateTimeFormat('zh-CN', {
1328
- weekday: 'long'
1329
- }).format(date),
1330
- ddd: new Intl.DateTimeFormat('zh-CN', {
1331
- weekday: 'short'
1332
- }).format(date),
1333
- dd: String(date.getDate()).padStart(2, '0'),
1334
- d: String(date.getDate()),
1335
- HH: String(date.getHours()).padStart(2, '0'),
1336
- H: String(date.getHours()),
1337
- hh: String(hour12).padStart(2, '0'),
1338
- h: String(hour12),
1339
- mm: String(date.getMinutes()).padStart(2, '0'),
1340
- m: String(date.getMinutes()),
1341
- ss: String(date.getSeconds()).padStart(2, '0'),
1342
- s: String(date.getSeconds())
1343
- };
1344
- return format.replace(/AM\/PM|am\/pm|yyyy|MMMM|dddd|MMM|ddd|yy|MM|dd|HH|hh|mm|ss|M|d|H|h|m|s/g, (token)=>replacements[token] ?? token);
1345
- }
1346
- function work_document_fields_positiveInteger(value) {
1347
- return Number.isSafeInteger(value) && value > 0 ? value : 1;
1348
- }
1349
- function validDate(value) {
1350
- return value && Number.isFinite(value.getTime()) ? value : null;
1684
+ function work_document_captions_positiveInteger(value) {
1685
+ const number = Number(value);
1686
+ return Number.isSafeInteger(number) && number > 0 ? number : 1;
1351
1687
  }
1352
- const DEFAULT_DOCUMENT_MARGINS = {
1353
- top: 25,
1354
- right: 23,
1355
- bottom: 25,
1356
- left: 23
1688
+ const CITATION_SELECTOR = 'span[data-document-citation]';
1689
+ const BIBLIOGRAPHY_SELECTOR = 'section[data-document-bibliography]';
1690
+ const STYLE_DETAILS = {
1691
+ apa: {
1692
+ name: 'APA',
1693
+ selectedStyle: '\\APASixthEditionOfficeOnline.xsl'
1694
+ },
1695
+ mla: {
1696
+ name: 'MLA',
1697
+ selectedStyle: '\\MLASeventhEditionOfficeOnline.xsl'
1698
+ },
1699
+ chicago: {
1700
+ name: 'Chicago',
1701
+ selectedStyle: '\\CHICAGO.XSL'
1702
+ },
1703
+ ieee: {
1704
+ name: 'IEEE',
1705
+ selectedStyle: '\\IEEE.XSL'
1706
+ }
1357
1707
  };
1358
- function documentMargins(content) {
1359
- return {
1360
- top: validMargin(content.margins?.top, DEFAULT_DOCUMENT_MARGINS.top),
1361
- right: validMargin(content.margins?.right, DEFAULT_DOCUMENT_MARGINS.right),
1362
- bottom: validMargin(content.margins?.bottom, DEFAULT_DOCUMENT_MARGINS.bottom),
1363
- left: validMargin(content.margins?.left, DEFAULT_DOCUMENT_MARGINS.left)
1364
- };
1365
- }
1366
- function clampDocumentMargin(value) {
1367
- return Math.min(60, Math.max(5, Math.round(10 * value) / 10));
1368
- }
1369
- function millimetersToPixels(value) {
1370
- return 96 * value / 25.4;
1371
- }
1372
- function validMargin(value, fallback) {
1373
- return Number.isFinite(value) ? clampDocumentMargin(value) : fallback;
1374
- }
1375
- const DOCUMENT_HTML_FINGERPRINT_VERSION = 'p1';
1376
- const DOCUMENT_HTML_HASH_BASE = 0x01000193;
1377
- const LEGACY_FNV_OFFSET = 0x811c9dc5;
1378
- function createDocumentHtmlFingerprintSegment(source, from = 0, to = source.length) {
1379
- if (!Number.isSafeInteger(from) || !Number.isSafeInteger(to) || from < 0 || to < from || to > source.length) throw new RangeError('The document HTML fingerprint range is invalid.');
1380
- let hash = 0;
1381
- for(let index = from; index < to; index += 1)hash = Math.imul(hash, DOCUMENT_HTML_HASH_BASE) + source.charCodeAt(index) >>> 0;
1382
- const length = to - from;
1708
+ function createDocumentBibliography(style = 'apa') {
1383
1709
  return {
1384
- hash,
1385
- length,
1386
- power: documentHtmlHashPower(length)
1387
- };
1388
- }
1389
- function combineDocumentHtmlFingerprintSegments(segments) {
1390
- let combined = {
1391
- hash: 0,
1392
- length: 0,
1393
- power: 1
1394
- };
1395
- for (const segment of segments)combined = {
1396
- hash: Math.imul(combined.hash, segment.power) + segment.hash >>> 0,
1397
- length: combined.length + segment.length,
1398
- power: Math.imul(combined.power, segment.power) >>> 0
1710
+ style,
1711
+ styleName: STYLE_DETAILS[style].name,
1712
+ selectedStyle: STYLE_DETAILS[style].selectedStyle,
1713
+ sources: []
1399
1714
  };
1400
- return combined;
1401
- }
1402
- function documentHtmlFingerprintForSegment(segment) {
1403
- return `${DOCUMENT_HTML_FINGERPRINT_VERSION}:${segment.length.toString(36)}:${segment.hash.toString(36)}`;
1404
1715
  }
1405
- function documentHtmlFingerprint(html) {
1406
- return documentHtmlFingerprintForSegment(createDocumentHtmlFingerprintSegment(html));
1716
+ function documentCitationStyle(value) {
1717
+ if ('mla' === value || 'chicago' === value || 'ieee' === value) return value;
1718
+ return 'apa';
1407
1719
  }
1408
- function documentHtmlFingerprintMatches(html, candidate) {
1409
- return candidate.startsWith(`${DOCUMENT_HTML_FINGERPRINT_VERSION}:`) ? candidate === documentHtmlFingerprint(html) : candidate === legacyDocumentHtmlFingerprint(html);
1720
+ function documentCitationStyleDetails(style) {
1721
+ return STYLE_DETAILS[style];
1410
1722
  }
1411
- function legacyDocumentHtmlFingerprint(html) {
1412
- let hash = LEGACY_FNV_OFFSET;
1413
- for(let index = 0; index < html.length; index += 1){
1414
- hash ^= html.charCodeAt(index);
1415
- hash = Math.imul(hash, DOCUMENT_HTML_HASH_BASE);
1416
- }
1417
- return `${html.length.toString(36)}:${(hash >>> 0).toString(36)}`;
1723
+ function documentCitationTagsFromInstruction(instruction) {
1724
+ const primary = /^\s*CITATION\s+(?:"([^"]+)"|([^\s\\]+))/i.exec(instruction);
1725
+ if (!primary) return [];
1726
+ const tags = [
1727
+ primary[1] || primary[2]
1728
+ ];
1729
+ const additional = /\\m\s+(?:"([^"]+)"|([^\s\\]+))/gi;
1730
+ for (const match of instruction.matchAll(additional))tags.push(match[1] || match[2]);
1731
+ return uniqueCitationTags(tags);
1418
1732
  }
1419
- function documentHtmlHashPower(length) {
1420
- let exponent = length;
1421
- let factor = DOCUMENT_HTML_HASH_BASE;
1422
- let power = 1;
1423
- while(exponent > 0){
1424
- if (exponent % 2 === 1) power = Math.imul(power, factor) >>> 0;
1425
- factor = Math.imul(factor, factor) >>> 0;
1426
- exponent = Math.floor(exponent / 2);
1427
- }
1428
- return power;
1733
+ function documentCitationInstruction(tags) {
1734
+ const normalized = uniqueCitationTags(tags);
1735
+ if (!normalized.length) return '';
1736
+ return [
1737
+ 'CITATION',
1738
+ citationTagInstructionValue(normalized[0]),
1739
+ ...normalized.slice(1).flatMap((tag)=>[
1740
+ '\\m',
1741
+ citationTagInstructionValue(tag)
1742
+ ]),
1743
+ '\\l',
1744
+ '2052'
1745
+ ].join(' ');
1429
1746
  }
1430
- const SECTION_CLOSE = '</section>';
1431
- const TABLE_BODY_OPEN = '<tbody>';
1432
- const TABLE_CLOSE = '</tbody></table>';
1433
- const projectionFingerprints = new WeakMap();
1434
- function createDocumentLazyHtmlProjection(html, root) {
1435
- if (!html.startsWith('<section') || !html.endsWith(SECTION_CLOSE)) return null;
1436
- const sectionOpenEnd = html.indexOf('>') + 1;
1437
- if (sectionOpenEnd <= 0) return null;
1438
- let cursor = sectionOpenEnd;
1439
- const orderedRanges = [];
1440
- const ranges = new Map();
1441
- for (const chunk of lazyDocumentLeafChunks(root)){
1442
- const id = 'string' == typeof chunk.attrs?.id ? chunk.attrs.id : '';
1443
- if (!id || ranges.has(id)) return null;
1444
- const from = cursor;
1445
- let tablePart = null;
1446
- const content = chunk.content ?? [];
1447
- for (const node of content){
1448
- const scanned = scanSimpleDocumentNodeHtml(html, cursor, node);
1449
- if (!scanned) return null;
1450
- cursor = scanned.to;
1451
- if (scanned.tablePart) {
1452
- if (1 !== content.length || tablePart) return null;
1453
- tablePart = scanned.tablePart;
1454
- }
1455
- }
1456
- const range = {
1457
- from,
1458
- id,
1459
- tablePart,
1460
- to: cursor
1461
- };
1462
- orderedRanges.push(range);
1463
- ranges.set(id, range);
1464
- }
1465
- if (cursor !== html.length - SECTION_CLOSE.length) return null;
1466
- return {
1467
- html,
1468
- orderedRanges,
1469
- ranges
1470
- };
1747
+ function renameDocumentCitationTagInInstruction(instruction, previousTag, nextTag) {
1748
+ return instruction.replace(/(^\s*CITATION\s+|\\m\s+)("[^"]+"|[^\s\\]+)/gi, (match, prefix, source)=>{
1749
+ const tag = source.startsWith('"') ? source.slice(1, -1) : source;
1750
+ return tag === previousTag ? `${prefix}${citationTagInstructionValue(nextTag)}` : match;
1751
+ });
1471
1752
  }
1472
- function patchDocumentLazyHtmlProjection(projection, replacements) {
1473
- if (!replacements.size) return projection.html;
1474
- for (const id of replacements.keys())if (!projection.ranges.has(id)) return null;
1475
- const fingerprintState = documentLazyHtmlFingerprintState(projection);
1476
- const parts = [];
1477
- let cursor = 0;
1478
- for (const range of projection.orderedRanges){
1479
- const replacement = replacements.get(range.id);
1480
- if (void 0 !== replacement) {
1481
- parts.push(projection.html.slice(cursor, range.from), replacement);
1482
- fingerprintState.ranges.set(range.id, createDocumentHtmlFingerprintSegment(replacement));
1483
- cursor = range.to;
1484
- }
1485
- }
1486
- parts.push(projection.html.slice(cursor));
1487
- const html = parts.join('');
1488
- let offset = 0;
1489
- for (const range of projection.orderedRanges){
1490
- const previousFrom = range.from;
1491
- const previousLength = range.to - previousFrom;
1492
- const replacement = replacements.get(range.id);
1493
- const nextLength = replacement?.length ?? previousLength;
1494
- range.from = previousFrom + offset;
1495
- range.to = range.from + nextLength;
1496
- offset += nextLength - previousLength;
1497
- }
1498
- projection.html = html;
1499
- fingerprintState.fingerprint = documentHtmlFingerprintForSegment(combineDocumentHtmlFingerprintSegments([
1500
- fingerprintState.prefix,
1501
- ...projection.orderedRanges.map((range)=>{
1502
- const segment = fingerprintState.ranges.get(range.id);
1503
- if (!segment) throw new Error('The lazy HTML fingerprint segment is missing.');
1504
- return segment;
1505
- }),
1506
- fingerprintState.suffix
1507
- ]));
1508
- return html;
1753
+ function documentCitationTags(value) {
1754
+ return uniqueCitationTags((value ?? '').split(/\s+/));
1509
1755
  }
1510
- function documentLazyHtmlProjectionFingerprint(projection) {
1511
- return projectionFingerprints.get(projection)?.fingerprint ?? null;
1756
+ function isValidDocumentCitationTag(value) {
1757
+ return /^[A-Za-z0-9_:.+-]{1,80}$/.test(value);
1512
1758
  }
1513
- function documentLazyHtmlChunkFragment(html, tablePart) {
1514
- if (!tablePart || 'complete' === tablePart) return html;
1515
- const bodyOpen = html.indexOf(TABLE_BODY_OPEN);
1516
- const bodyClose = html.lastIndexOf('</tbody>');
1517
- if (bodyOpen < 0 || bodyClose < bodyOpen) return null;
1518
- const rowsFrom = bodyOpen + TABLE_BODY_OPEN.length;
1519
- if ('first' === tablePart) return html.slice(0, bodyClose);
1520
- if ('middle' === tablePart) return html.slice(rowsFrom, bodyClose);
1521
- return html.slice(rowsFrom);
1759
+ function resolveDocumentCitation(tags, bibliography, instruction = '', cachedValue = '') {
1760
+ const normalized = uniqueCitationTags(tags);
1761
+ if (!normalized.length) return {
1762
+ text: cachedValue || '缺失引文',
1763
+ orphaned: true
1764
+ };
1765
+ const sources = new Map((bibliography?.sources ?? []).map((source)=>[
1766
+ source.tag,
1767
+ source
1768
+ ]));
1769
+ const missing = normalized.filter((tag)=>!sources.has(tag));
1770
+ if (missing.length) return {
1771
+ text: 1 === missing.length ? `缺失引文:${missing[0]}` : `缺失引文:${missing.join('、')}`,
1772
+ orphaned: true
1773
+ };
1774
+ const selected = normalized.flatMap((tag)=>{
1775
+ const source = sources.get(tag);
1776
+ return source ? [
1777
+ source
1778
+ ] : [];
1779
+ });
1780
+ const style = bibliography?.style ?? 'apa';
1781
+ const suppressAuthor = /(?:^|\s)\\n(?:\s|$)/i.test(instruction);
1782
+ const suppressYear = /(?:^|\s)\\v(?:\s|$)/i.test(instruction);
1783
+ const prefix = citationSwitch(instruction, 'f');
1784
+ const suffix = citationSwitch(instruction, 'p');
1785
+ const text = 'ieee' === style ? ieeeCitation(selected, bibliography?.sources ?? []) : 'mla' === style ? mlaCitation(selected, suppressAuthor) : 'chicago' === style ? chicagoCitation(selected, suppressAuthor, suppressYear) : apaCitation(selected, suppressAuthor, suppressYear);
1786
+ return {
1787
+ text: `${prefix}${text}${suffix}`.trim() || cachedValue || normalized.join('; '),
1788
+ orphaned: false
1789
+ };
1522
1790
  }
1523
- function lazyDocumentLeafChunks(root) {
1524
- const chunks = [];
1525
- const pending = [
1526
- root
1527
- ];
1528
- while(pending.length){
1529
- const node = pending.pop();
1530
- if (!node) continue;
1531
- if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
1532
- chunks.push(node);
1791
+ function normalizeDocumentCitationsHtml(source, bibliography) {
1792
+ const document1 = new DOMParser().parseFromString(source, 'text/html');
1793
+ const usedIds = new Set();
1794
+ for (const [index, element] of Array.from(document1.body.querySelectorAll(CITATION_SELECTOR)).entries()){
1795
+ const instruction = element.dataset.citationInstruction?.trim() ?? '';
1796
+ const tags = documentCitationTags(element.dataset.citationTags);
1797
+ const normalizedTags = tags.length ? tags : documentCitationTagsFromInstruction(instruction);
1798
+ if (!normalizedTags.length) {
1799
+ element.replaceWith(document1.createTextNode(element.textContent ?? ''));
1533
1800
  continue;
1534
1801
  }
1535
- for(let index = (node.content?.length ?? 0) - 1; index >= 0; index -= 1){
1536
- const child = node.content?.[index];
1537
- if (child) pending.push(child);
1538
- }
1802
+ const cached = element.dataset.citationDisplay?.trim() || element.textContent?.trim() || '';
1803
+ const resolved = resolveDocumentCitation(normalizedTags, bibliography, instruction, cached);
1804
+ element.dataset.documentCitation = 'true';
1805
+ element.dataset.citationId = uniqueCitationId(element.dataset.citationId, index + 1, usedIds);
1806
+ element.dataset.citationTags = normalizedTags.join(' ');
1807
+ element.dataset.citationInstruction = instruction || documentCitationInstruction(normalizedTags);
1808
+ element.dataset.citationDisplay = resolved.text;
1809
+ if (resolved.orphaned) element.dataset.citationOrphaned = 'true';
1810
+ else delete element.dataset.citationOrphaned;
1811
+ element.classList.add('work-document-citation');
1812
+ element.textContent = resolved.text;
1539
1813
  }
1540
- return chunks;
1814
+ for (const [index, element] of Array.from(document1.body.querySelectorAll(BIBLIOGRAPHY_SELECTOR)).entries()){
1815
+ if (!bibliography) continue;
1816
+ const replacement = createBibliographyElement(document1, bibliography, element.dataset.bibliographyId || `document-bibliography-${index + 1}`);
1817
+ element.replaceWith(replacement);
1818
+ }
1819
+ return document1.body.innerHTML;
1541
1820
  }
1542
- function documentLazyHtmlFingerprintState(projection) {
1543
- const existing = projectionFingerprints.get(projection);
1544
- if (existing) return existing;
1545
- const first = projection.orderedRanges[0];
1546
- const last = projection.orderedRanges.at(-1);
1547
- const ranges = new Map();
1548
- for (const range of projection.orderedRanges)ranges.set(range.id, createDocumentHtmlFingerprintSegment(projection.html, range.from, range.to));
1549
- const prefix = createDocumentHtmlFingerprintSegment(projection.html, 0, first?.from ?? projection.html.length);
1550
- const suffix = createDocumentHtmlFingerprintSegment(projection.html, last?.to ?? projection.html.length, projection.html.length);
1551
- const state = {
1552
- fingerprint: documentHtmlFingerprintForSegment(combineDocumentHtmlFingerprintSegments([
1553
- prefix,
1554
- ...projection.orderedRanges.map((range)=>{
1555
- const segment = ranges.get(range.id);
1556
- if (!segment) throw new Error('The lazy HTML fingerprint segment is missing.');
1557
- return segment;
1558
- }),
1559
- suffix
1560
- ])),
1561
- prefix,
1562
- ranges,
1563
- suffix
1564
- };
1565
- projectionFingerprints.set(projection, state);
1566
- return state;
1821
+ function renderDocumentBibliographyHtml(bibliography, id = 'document-bibliography-1') {
1822
+ const document1 = new DOMParser().parseFromString('', 'text/html');
1823
+ document1.body.append(createBibliographyElement(document1, bibliography, id));
1824
+ return document1.body.innerHTML;
1567
1825
  }
1568
- function scanSimpleDocumentNodeHtml(html, from, node) {
1569
- if ('paragraph' === node.type) {
1570
- if (!html.startsWith('<p>', from)) return null;
1571
- const close = html.indexOf('</p>', from + 3);
1572
- return close < 0 ? null : {
1573
- tablePart: null,
1574
- to: close + 4
1575
- };
1576
- }
1577
- if ('table' !== node.type) return null;
1578
- const virtualIndex = Number(node.attrs?.virtualTableIndex);
1579
- const virtualCount = Number(node.attrs?.virtualTableCount);
1580
- const virtual = Boolean('string' == typeof node.attrs?.virtualTableId && node.attrs.virtualTableId && Number.isSafeInteger(virtualIndex) && Number.isSafeInteger(virtualCount) && virtualIndex >= 0 && virtualCount > 0 && virtualIndex < virtualCount);
1581
- let cursor = from;
1582
- if (!virtual || 0 === virtualIndex) {
1583
- if (!html.startsWith('<table', cursor)) return null;
1584
- const body = html.indexOf(TABLE_BODY_OPEN, cursor);
1585
- if (body < 0) return null;
1586
- cursor = body + TABLE_BODY_OPEN.length;
1587
- }
1588
- for (const row of node.content ?? []){
1589
- if ('tableRow' !== row.type || !html.startsWith('<tr>', cursor)) return null;
1590
- const close = html.indexOf('</tr>', cursor + 4);
1591
- if (close < 0) return null;
1592
- cursor = close + 5;
1593
- }
1594
- if (!virtual || virtualIndex === virtualCount - 1) {
1595
- if (!html.startsWith(TABLE_CLOSE, cursor)) return null;
1596
- cursor += TABLE_CLOSE.length;
1597
- }
1598
- const tablePart = virtual ? 1 === virtualCount ? 'complete' : 0 === virtualIndex ? 'first' : virtualIndex === virtualCount - 1 ? 'last' : 'middle' : 'complete';
1599
- return {
1600
- tablePart,
1601
- to: cursor
1602
- };
1826
+ function documentBibliographyEntry(source, bibliography, index) {
1827
+ const people = primaryCitationContributor(source);
1828
+ const authors = people?.corporate?.trim() || people?.people?.map((person)=>bibliographyPersonName(person, bibliography.style)).join(', ') || '未知作者';
1829
+ const title = source.title.trim() || 'Untitled';
1830
+ const year = source.year?.trim() || 'n.d.';
1831
+ const container = citationContainer(source);
1832
+ const url = source.url?.trim();
1833
+ if ('ieee' === bibliography.style) return [
1834
+ `[${index + 1}] ${authors}, “${title}.”`,
1835
+ container ? `${container},` : '',
1836
+ year ? `${year}.` : '',
1837
+ url ?? ''
1838
+ ].filter(Boolean).join(' ');
1839
+ if ('mla' === bibliography.style) return [
1840
+ `${authors}.`,
1841
+ `“${title}.”`,
1842
+ container ? `${container},` : '',
1843
+ `${year}.`,
1844
+ url ?? ''
1845
+ ].filter(Boolean).join(' ');
1846
+ if ('chicago' === bibliography.style) return [
1847
+ `${authors}.`,
1848
+ `${year}.`,
1849
+ `“${title}.”`,
1850
+ container ? `${container}.` : '',
1851
+ url ?? ''
1852
+ ].filter(Boolean).join(' ');
1853
+ return [
1854
+ `${authors}.`,
1855
+ `(${year}).`,
1856
+ `${title}.`,
1857
+ container ? `${container}.` : '',
1858
+ url ?? ''
1859
+ ].filter(Boolean).join(' ');
1603
1860
  }
1604
- const DOCUMENT_LAZY_BLOCK_NODE = 'documentLazyBlock';
1605
- const DOCUMENT_LAZY_INITIAL_CHUNK_COUNT = 2;
1606
- const DOCUMENT_LAZY_POSITION_BOUNDARY = '\ufffc';
1607
- const SIMPLE_LAZY_CONTAINER_TYPES = new Set([
1608
- 'paragraph',
1609
- 'table',
1610
- 'tableCell',
1611
- 'tableRow'
1612
- ]);
1613
- const preparedModels = new WeakMap();
1614
- function prepareLazyDocumentEditorSource(model, allowCreate, html) {
1615
- const startedAt = lazyDocumentNow();
1616
- const cacheHit = preparedModels.has(model);
1617
- let prepared = preparedModels.get(model);
1618
- if (prepared) {
1619
- if (!prepared.root) {
1620
- const htmlProjection = prepared.htmlProjection;
1621
- const rebuilt = createPreparedLazyDocumentEditorSource(model.root, prepared.payloads);
1622
- if (!rebuilt) return null;
1623
- rebuilt.htmlProjection = htmlProjection;
1624
- prepared = rebuilt;
1625
- preparedModels.set(model, rebuilt);
1626
- }
1627
- } else {
1628
- if (!allowCreate) return null;
1629
- const created = createPreparedLazyDocumentEditorSource(model.root);
1630
- if (!created) return null;
1631
- prepared = created;
1632
- preparedModels.set(model, created);
1861
+ function primaryCitationContributor(source) {
1862
+ return source.contributors?.Author ?? Object.values(source.contributors ?? {})[0];
1863
+ }
1864
+ function createBibliographyElement(document1, bibliography, id) {
1865
+ const section = document1.createElement('section');
1866
+ section.dataset.documentBibliography = 'true';
1867
+ section.dataset.bibliographyId = id;
1868
+ section.dataset.bibliographyStyle = bibliography.style;
1869
+ section.className = 'work-document-bibliography';
1870
+ const heading = document1.createElement('h2');
1871
+ heading.textContent = '参考文献';
1872
+ section.append(heading);
1873
+ if (!bibliography.sources.length) {
1874
+ const empty = document1.createElement('p');
1875
+ empty.dataset.bibliographyEmpty = 'true';
1876
+ empty.textContent = '尚无文献源';
1877
+ section.append(empty);
1878
+ return section;
1633
1879
  }
1634
- if (!prepared.htmlProjection && html) prepared.htmlProjection = createDocumentLazyHtmlProjection(html, model.root);
1635
- if (!prepared.root) return null;
1636
- const result = {
1637
- lazyChunkCount: prepared.lazyChunkCount,
1638
- payloads: prepared.payloads,
1639
- root: prepared.root
1640
- };
1641
- recordLazyDocumentMeasure('a3s-office.document.lazy-editor-source', startedAt, lazyDocumentNow(), {
1642
- cacheHit
1880
+ bibliography.sources.forEach((source, index)=>{
1881
+ const paragraph = document1.createElement('p');
1882
+ paragraph.dataset.bibliographyEntry = source.tag;
1883
+ paragraph.textContent = documentBibliographyEntry(source, bibliography, index);
1884
+ section.append(paragraph);
1885
+ });
1886
+ return section;
1887
+ }
1888
+ function apaCitation(sources, suppressAuthor, suppressYear) {
1889
+ const items = sources.map((source)=>{
1890
+ const author = suppressAuthor ? '' : citationAuthor(source, '&');
1891
+ const year = suppressYear ? '' : source.year?.trim() || 'n.d.';
1892
+ return [
1893
+ author,
1894
+ year
1895
+ ].filter(Boolean).join(', ');
1643
1896
  });
1644
- return result;
1897
+ return `(${items.join('; ')})`;
1645
1898
  }
1646
- function documentLazyHtmlProjection(model) {
1647
- return model ? preparedModels.get(model)?.htmlProjection ?? null : null;
1899
+ function mlaCitation(sources, suppressAuthor) {
1900
+ return `(${sources.map((source)=>suppressAuthor ? source.year?.trim() || 'n.d.' : citationAuthor(source, 'and')).join('; ')})`;
1648
1901
  }
1649
- function invalidateDocumentLazyHtmlProjection(model) {
1650
- if (model) {
1651
- const prepared = preparedModels.get(model);
1652
- if (prepared) prepared.htmlProjection = null;
1902
+ function chicagoCitation(sources, suppressAuthor, suppressYear) {
1903
+ return `(${sources.map((source)=>[
1904
+ suppressAuthor ? '' : citationAuthor(source, 'and'),
1905
+ suppressYear ? '' : source.year?.trim() || 'n.d.'
1906
+ ].filter(Boolean).join(' ')).join('; ')})`;
1907
+ }
1908
+ function ieeeCitation(sources, allSources) {
1909
+ const indexes = sources.map((source)=>allSources.findIndex((candidate)=>candidate.id === source.id || candidate.tag === source.tag) + 1).filter((index)=>index > 0);
1910
+ return `[${indexes.join(', ')}]`;
1911
+ }
1912
+ function citationAuthor(source, conjunction) {
1913
+ const contributor = primaryCitationContributor(source);
1914
+ if (contributor?.corporate?.trim()) return contributor.corporate.trim();
1915
+ const names = (contributor?.people ?? []).map((person)=>person.last.trim() || person.first.trim()).filter(Boolean);
1916
+ if (!names.length) return source.title.trim() || source.tag;
1917
+ if (1 === names.length) return names[0];
1918
+ if (2 === names.length) return `${names[0]} ${conjunction} ${names[1]}`;
1919
+ return `${names[0]} et al.`;
1920
+ }
1921
+ function bibliographyPersonName(person, style) {
1922
+ const first = [
1923
+ person.first,
1924
+ person.middle
1925
+ ].filter(Boolean).join(' ').trim();
1926
+ const suffix = person.suffix?.trim();
1927
+ if ('ieee' === style) {
1928
+ const initials = [
1929
+ person.first,
1930
+ person.middle
1931
+ ].filter(Boolean).map((value)=>`${Array.from(value ?? '')[0] ?? ''}.`).join(' ');
1932
+ return [
1933
+ initials,
1934
+ person.last,
1935
+ suffix
1936
+ ].filter(Boolean).join(' ');
1653
1937
  }
1938
+ return [
1939
+ person.last,
1940
+ first ? `, ${first}` : '',
1941
+ suffix ? `, ${suffix}` : ''
1942
+ ].join('');
1654
1943
  }
1655
- function documentLazyChunkContent(model, chunkId) {
1656
- if (!model || !chunkId) return null;
1657
- return preparedModels.get(model)?.payloads.get(chunkId) ?? null;
1944
+ function citationContainer(source) {
1945
+ return source.journalName?.trim() || source.publisher?.trim() || source.conferenceName?.trim() || source.institution?.trim() || '';
1658
1946
  }
1659
- function materializeLazyDocumentEditorRoot(root, model) {
1660
- const prepared = model ? preparedModels.get(model) : null;
1661
- if (!prepared) return root;
1662
- const visit = (node)=>{
1663
- if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
1664
- const id = documentChunkId(node);
1665
- if (!id) return node;
1666
- if (documentChunkIsLazy(node)) {
1667
- const payload = prepared.payloads.get(id);
1668
- if (!payload) throw new Error(`The lazy document chunk payload "${id}" is missing.`);
1669
- return {
1670
- ...node,
1671
- content: [
1672
- ...payload
1673
- ]
1674
- };
1675
- }
1676
- if (node.content?.length) prepared.payloads.set(id, node.content);
1677
- return node;
1678
- }
1679
- if (!node.content?.length) return node;
1680
- let changed = false;
1681
- const content = node.content.map((child)=>{
1682
- const next = visit(child);
1683
- if (next !== child) changed = true;
1684
- return next;
1685
- });
1686
- return changed ? {
1687
- ...node,
1688
- content
1689
- } : node;
1690
- };
1691
- return visit(root);
1947
+ function citationSwitch(instruction, name) {
1948
+ const expression = new RegExp(`\\\\${name}\\s+"([^"]*)"`, 'i');
1949
+ return expression.exec(instruction)?.[1] ?? '';
1692
1950
  }
1693
- function transferLazyDocumentModelState(previous, next) {
1694
- if (!previous) return;
1695
- const prepared = preparedModels.get(previous);
1696
- if (!prepared) return;
1697
- preparedModels.set(next, {
1698
- htmlProjection: prepared.htmlProjection,
1699
- lazyChunkCount: prepared.lazyChunkCount,
1700
- payloads: prepared.payloads,
1701
- root: null
1702
- });
1951
+ function citationTagInstructionValue(tag) {
1952
+ return /^[A-Za-z0-9_:.+-]+$/.test(tag) ? tag : `"${tag.replaceAll('"', '')}"`;
1703
1953
  }
1704
- function simpleDocumentNodeSize(node) {
1705
- if ('text' === node.type) return 'string' == typeof node.text && node.text ? node.text.length : null;
1706
- if (!SIMPLE_LAZY_CONTAINER_TYPES.has(node.type)) return null;
1707
- let size = 2;
1708
- for (const child of node.content ?? []){
1709
- const childSize = simpleDocumentNodeSize(child);
1710
- if (null === childSize) return null;
1711
- size += childSize;
1954
+ function uniqueCitationTags(tags) {
1955
+ const result = [];
1956
+ const seen = new Set();
1957
+ for (const source of tags){
1958
+ const tag = source.trim();
1959
+ if (!(!tag || seen.has(tag))) {
1960
+ seen.add(tag);
1961
+ result.push(tag);
1962
+ }
1712
1963
  }
1713
- return size;
1964
+ return result;
1714
1965
  }
1715
- function createPreparedLazyDocumentEditorSource(root, previousPayloads) {
1716
- const payloads = new Map();
1717
- let leafIndex = 0;
1718
- let lazyChunkCount = 0;
1719
- let unsupported = false;
1720
- const visit = (node)=>{
1721
- if ('documentChunk' === node.type && node.attrs?.windowContainer !== true) {
1722
- const id = documentChunkId(node);
1723
- const payload = id ? previousPayloads?.get(id) ?? node.content ?? [] : [];
1724
- if (!id || !payload.length) {
1725
- unsupported = true;
1726
- return node;
1727
- }
1728
- payloads.set(id, payload);
1729
- const currentIndex = leafIndex;
1730
- leafIndex += 1;
1731
- if (currentIndex < DOCUMENT_LAZY_INITIAL_CHUNK_COUNT) return node;
1732
- const placeholder = lazyPlaceholderForContent(id, payload);
1733
- if (!placeholder) {
1734
- unsupported = true;
1735
- return node;
1736
- }
1737
- lazyChunkCount += 1;
1738
- return {
1739
- ...node,
1740
- content: [
1741
- placeholder
1742
- ]
1743
- };
1744
- }
1745
- if (!node.content?.length) return node;
1746
- let changed = false;
1747
- const content = node.content.map((child)=>{
1748
- const next = visit(child);
1749
- if (next !== child) changed = true;
1750
- return next;
1751
- });
1752
- return changed ? {
1753
- ...node,
1754
- content
1755
- } : node;
1756
- };
1757
- const compactRoot = visit(root);
1758
- if (unsupported || leafIndex <= DOCUMENT_LAZY_INITIAL_CHUNK_COUNT || 0 === lazyChunkCount) return null;
1759
- return {
1760
- htmlProjection: null,
1761
- lazyChunkCount,
1762
- payloads,
1763
- root: compactRoot
1966
+ function uniqueCitationId(source, index, usedIds) {
1967
+ const candidate = source?.trim();
1968
+ if (candidate && !usedIds.has(candidate)) {
1969
+ usedIds.add(candidate);
1970
+ return candidate;
1971
+ }
1972
+ let suffix = index;
1973
+ while(usedIds.has(`document-citation-${suffix}`))suffix += 1;
1974
+ const id = `document-citation-${suffix}`;
1975
+ usedIds.add(id);
1976
+ return id;
1977
+ }
1978
+ const DEFAULT_DOCUMENT_COLUMNS = {
1979
+ count: 1,
1980
+ spacing: 12,
1981
+ separator: false
1982
+ };
1983
+ const MAX_COLUMNS = 6;
1984
+ const MIN_COLUMN_PERCENT = 5;
1985
+ function normalizeDocumentColumns(columns) {
1986
+ const customCount = Array.isArray(columns?.custom) ? columns.custom.length : 0;
1987
+ const count = clampInteger(columns?.count, customCount || DEFAULT_DOCUMENT_COLUMNS.count, 1, MAX_COLUMNS);
1988
+ const spacing = clampNumber(columns?.spacing, DEFAULT_DOCUMENT_COLUMNS.spacing, 0, 30);
1989
+ const normalized = {
1990
+ count,
1991
+ spacing: roundOne(spacing),
1992
+ separator: Boolean(columns?.separator)
1764
1993
  };
1994
+ if (count < 2 || !customCount) return normalized;
1995
+ const source = columns?.custom ?? [];
1996
+ const widths = normalizedPercentages(Array.from({
1997
+ length: count
1998
+ }, (_, index)=>finiteNumber(source[index]?.widthPercent, 100 / count)), 100, MIN_COLUMN_PERCENT);
1999
+ normalized.custom = Array.from({
2000
+ length: count
2001
+ }, (_, index)=>({
2002
+ widthPercent: widths[index],
2003
+ spacing: index === count - 1 ? 0 : roundOne(clampNumber(source[index]?.spacing, normalized.spacing, 0, 30))
2004
+ }));
2005
+ return normalized;
1765
2006
  }
1766
- function lazyPlaceholderForContent(chunkId, content) {
1767
- const tapeParts = [];
1768
- const statistics = {
1769
- contentSize: 0,
1770
- paragraphCount: 0
2007
+ function serializeDocumentColumns(columns) {
2008
+ return JSON.stringify(normalizeDocumentColumns(columns));
2009
+ }
2010
+ function parseDocumentColumns(source, legacy = {}, fallback) {
2011
+ if (source?.trim()) try {
2012
+ return normalizeDocumentColumns(JSON.parse(source));
2013
+ } catch {}
2014
+ if (void 0 !== legacy.count || void 0 !== legacy.spacing || void 0 !== legacy.separator) return normalizeDocumentColumns(legacy);
2015
+ return normalizeDocumentColumns(fallback);
2016
+ }
2017
+ function setCustomDocumentColumns(columns, enabled) {
2018
+ const normalized = normalizeDocumentColumns(columns);
2019
+ if (!enabled || normalized.count < 2) return {
2020
+ ...normalized,
2021
+ custom: void 0
1771
2022
  };
1772
- for (const node of content)if (!appendDocumentNodePositionTape(node, tapeParts, statistics)) return null;
1773
- if (statistics.contentSize < 2 || tapeParts[0] !== DOCUMENT_LAZY_POSITION_BOUNDARY || tapeParts.at(-1) !== DOCUMENT_LAZY_POSITION_BOUNDARY) return null;
1774
- tapeParts[0] = '';
1775
- tapeParts[tapeParts.length - 1] = '';
1776
- const filler = tapeParts.join('');
2023
+ if (normalized.custom) return normalized;
2024
+ return normalizeDocumentColumns({
2025
+ ...normalized,
2026
+ custom: Array.from({
2027
+ length: normalized.count
2028
+ }, (_, index)=>({
2029
+ widthPercent: 100 / normalized.count,
2030
+ spacing: index === normalized.count - 1 ? 0 : normalized.spacing
2031
+ }))
2032
+ });
2033
+ }
2034
+ function updateDocumentColumnWidth(columns, index, widthPercent) {
2035
+ const normalized = setCustomDocumentColumns(columns, true);
2036
+ if (!normalized.custom?.[index]) return normalized;
2037
+ const maximum = 100 - MIN_COLUMN_PERCENT * (normalized.custom.length - 1);
2038
+ const target = Math.min(maximum, Math.max(MIN_COLUMN_PERCENT, finiteNumber(widthPercent, 100 / normalized.count)));
2039
+ const otherIndexes = normalized.custom.map((_, itemIndex)=>itemIndex).filter((itemIndex)=>itemIndex !== index);
2040
+ const otherWidths = normalizedPercentages(otherIndexes.map((itemIndex)=>normalized.custom?.[itemIndex].widthPercent ?? 1), 100 - target, MIN_COLUMN_PERCENT);
2041
+ const custom = normalized.custom.map((column, itemIndex)=>{
2042
+ const otherIndex = otherIndexes.indexOf(itemIndex);
2043
+ return {
2044
+ ...column,
2045
+ widthPercent: itemIndex === index ? roundOne(target) : otherWidths[otherIndex]
2046
+ };
2047
+ });
1777
2048
  return {
1778
- type: DOCUMENT_LAZY_BLOCK_NODE,
1779
- attrs: {
1780
- chunkId,
1781
- contentSize: statistics.contentSize,
1782
- paragraphCount: statistics.paragraphCount
1783
- },
1784
- ...filler ? {
1785
- content: [
1786
- {
1787
- type: 'text',
1788
- text: filler
1789
- }
1790
- ]
1791
- } : {
1792
- content: []
1793
- }
2049
+ ...normalized,
2050
+ custom
1794
2051
  };
1795
2052
  }
1796
- function appendDocumentNodePositionTape(node, parts, statistics) {
1797
- if ('text' === node.type) {
1798
- if ('string' != typeof node.text || !node.text) return false;
1799
- parts.push(node.text);
1800
- statistics.contentSize += node.text.length;
1801
- return true;
2053
+ function normalizedPercentages(values, total, minimum) {
2054
+ if (!values.length) return [];
2055
+ const result = Array(values.length).fill(0);
2056
+ const remaining = new Set(values.map((_, index)=>index));
2057
+ let remainingTotal = total;
2058
+ while(remaining.size){
2059
+ const sourceTotal = Array.from(remaining).reduce((sum, index)=>sum + Math.max(0.001, finiteNumber(values[index], 1)), 0);
2060
+ const belowMinimum = Array.from(remaining).filter((index)=>Math.max(0.001, finiteNumber(values[index], 1)) / sourceTotal * remainingTotal < minimum);
2061
+ if (!belowMinimum.length) {
2062
+ for (const index of remaining)result[index] = Math.max(0.001, finiteNumber(values[index], 1)) / sourceTotal * remainingTotal;
2063
+ break;
2064
+ }
2065
+ for (const index of belowMinimum){
2066
+ result[index] = minimum;
2067
+ remaining.delete(index);
2068
+ remainingTotal -= minimum;
2069
+ }
1802
2070
  }
1803
- if (!SIMPLE_LAZY_CONTAINER_TYPES.has(node.type)) return false;
1804
- if ('paragraph' === node.type) statistics.paragraphCount += 1;
1805
- statistics.contentSize += 2;
1806
- parts.push(DOCUMENT_LAZY_POSITION_BOUNDARY);
1807
- for (const child of node.content ?? [])if (!appendDocumentNodePositionTape(child, parts, statistics)) return false;
1808
- parts.push(DOCUMENT_LAZY_POSITION_BOUNDARY);
1809
- return true;
2071
+ const rounded = result.map(roundOne);
2072
+ const adjustmentIndex = rounded.indexOf(Math.max(...rounded));
2073
+ rounded[adjustmentIndex] = roundOne(rounded[adjustmentIndex] + total - rounded.reduce((sum, value)=>sum + value, 0));
2074
+ return rounded;
1810
2075
  }
1811
- function documentChunkIsLazy(node) {
1812
- return 'documentChunk' === node.type && node.attrs?.windowContainer !== true && node.content?.length === 1 && node.content[0]?.type === DOCUMENT_LAZY_BLOCK_NODE;
2076
+ function clampInteger(value, fallback, minimum, maximum) {
2077
+ return Math.min(maximum, Math.max(minimum, Math.round(finiteNumber(value, fallback))));
1813
2078
  }
1814
- function documentChunkId(node) {
1815
- const id = node.attrs?.id;
1816
- return 'string' == typeof id && id ? id : null;
2079
+ function clampNumber(value, fallback, minimum, maximum) {
2080
+ return Math.min(maximum, Math.max(minimum, finiteNumber(value, fallback)));
1817
2081
  }
1818
- function lazyDocumentNow() {
1819
- return globalThis.performance?.now?.() ?? Date.now();
2082
+ function finiteNumber(value, fallback) {
2083
+ return Number.isFinite(value) ? Number(value) : fallback;
1820
2084
  }
1821
- function recordLazyDocumentMeasure(name, start, end, detail) {
1822
- try {
1823
- globalThis.performance?.measure(name, {
1824
- detail,
1825
- end,
1826
- start
1827
- });
1828
- } catch {}
2085
+ function roundOne(value) {
2086
+ return Math.round(10 * value) / 10;
2087
+ }
2088
+ const DEFAULT_DOCUMENT_MARGINS = {
2089
+ top: 25,
2090
+ right: 23,
2091
+ bottom: 25,
2092
+ left: 23
2093
+ };
2094
+ function documentMargins(content) {
2095
+ return {
2096
+ top: validMargin(content.margins?.top, DEFAULT_DOCUMENT_MARGINS.top),
2097
+ right: validMargin(content.margins?.right, DEFAULT_DOCUMENT_MARGINS.right),
2098
+ bottom: validMargin(content.margins?.bottom, DEFAULT_DOCUMENT_MARGINS.bottom),
2099
+ left: validMargin(content.margins?.left, DEFAULT_DOCUMENT_MARGINS.left)
2100
+ };
2101
+ }
2102
+ function clampDocumentMargin(value) {
2103
+ return Math.min(60, Math.max(5, Math.round(10 * value) / 10));
2104
+ }
2105
+ function millimetersToPixels(value) {
2106
+ return 96 * value / 25.4;
2107
+ }
2108
+ function validMargin(value, fallback) {
2109
+ return Number.isFinite(value) ? clampDocumentMargin(value) : fallback;
1829
2110
  }
1830
2111
  const DOCUMENT_MODEL_SCHEMA = 'a3s.office.document';
1831
2112
  const DOCUMENT_MODEL_VERSION = 1;
@@ -1853,7 +2134,7 @@ function work_document_model_createSchemaValidatedWorkDocumentModel(html, root,
1853
2134
  schemaValidatedDocumentModels.set(model, {
1854
2135
  html,
1855
2136
  htmlFingerprint: model.htmlFingerprint,
1856
- initialIntegrityFeatures: nonNegativeInteger(options.initialIntegrityFeatures),
2137
+ initialIntegrityFeatures: work_document_model_nonNegativeInteger(options.initialIntegrityFeatures),
1857
2138
  root
1858
2139
  });
1859
2140
  return model;
@@ -1902,7 +2183,7 @@ function nextRevision(previous) {
1902
2183
  const revision = previous?.schema === DOCUMENT_MODEL_SCHEMA && previous.version === DOCUMENT_MODEL_VERSION && Number.isSafeInteger(previous.revision) && previous.revision > 0 ? previous.revision : 0;
1903
2184
  return revision >= Number.MAX_SAFE_INTEGER ? 1 : revision + 1;
1904
2185
  }
1905
- function nonNegativeInteger(value) {
2186
+ function work_document_model_nonNegativeInteger(value) {
1906
2187
  const number = Number(value);
1907
2188
  return Number.isSafeInteger(number) && number >= 0 ? number : null;
1908
2189
  }
@@ -10882,130 +11163,6 @@ function setNumberDataset(element, name, value) {
10882
11163
  if (Number.isSafeInteger(number) && number >= 0) element.dataset[name] = String(number);
10883
11164
  else delete element.dataset[name];
10884
11165
  }
10885
- const statisticsByDocument = new WeakMap();
10886
- function documentWordCount(value) {
10887
- let asciiCount = 0;
10888
- let inAsciiWord = false;
10889
- for(let index = 0; index < value.length; index += 1){
10890
- const code = value.charCodeAt(index);
10891
- const asciiLetterOrNumber = code >= 0x30 && code <= 0x39 || code >= 0x41 && code <= 0x5a || code >= 0x61 && code <= 0x7a;
10892
- if (asciiLetterOrNumber) {
10893
- if (!inAsciiWord) asciiCount += 1;
10894
- inAsciiWord = true;
10895
- continue;
10896
- }
10897
- inAsciiWord = false;
10898
- if (!(code <= 0x7f) && 0xfffc !== code) return unicodeDocumentWordCount(value);
10899
- }
10900
- return asciiCount;
10901
- }
10902
- function unicodeDocumentWordCount(value) {
10903
- let count = 0;
10904
- 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;
10905
- return count;
10906
- }
10907
- function documentTextStatistics(editor) {
10908
- const document1 = editor.state.doc;
10909
- const cached = statisticsByDocument.get(document1);
10910
- if (cached) return cached;
10911
- const source = editor.getText({
10912
- blockSeparator: '\n'
10913
- });
10914
- let characterCountWithSpaces = 0;
10915
- let characterCountWithoutSpaces = 0;
10916
- for(let index = 0; index < source.length; index += 1){
10917
- const codePoint = source.codePointAt(index);
10918
- if (void 0 !== codePoint) {
10919
- if (codePoint > 0xffff) index += 1;
10920
- if (codePoint !== "".codePointAt(0) && 0x0a !== codePoint && 0x0d !== codePoint) {
10921
- characterCountWithSpaces += 1;
10922
- if (!isEcmaScriptWhitespace(codePoint)) characterCountWithoutSpaces += 1;
10923
- }
10924
- }
10925
- }
10926
- let paragraphCount = 0;
10927
- document1.descendants((node)=>{
10928
- if (node.type.name === DOCUMENT_LAZY_BLOCK_NODE) {
10929
- const logicalParagraphs = Number(node.attrs.paragraphCount);
10930
- paragraphCount += Number.isSafeInteger(logicalParagraphs) && logicalParagraphs >= 0 ? logicalParagraphs : 0;
10931
- return false;
10932
- }
10933
- if (node.isTextblock) paragraphCount += 1;
10934
- return true;
10935
- });
10936
- const statistics = {
10937
- characterCountWithSpaces,
10938
- characterCountWithoutSpaces,
10939
- paragraphCount,
10940
- wordCount: documentWordCount(source)
10941
- };
10942
- statisticsByDocument.set(document1, statistics);
10943
- return statistics;
10944
- }
10945
- function transferDocumentTextStatistics(previous, next) {
10946
- const statistics = statisticsByDocument.get(previous);
10947
- if (statistics) statisticsByDocument.set(next, statistics);
10948
- }
10949
- function transferChangedDocumentTextStatistics(previous, next, changes) {
10950
- const cached = statisticsByDocument.get(previous);
10951
- if (!cached || !changes.length) return false;
10952
- const statistics = {
10953
- ...cached
10954
- };
10955
- for (const { after, before } of changes){
10956
- applyDocumentTextStatisticsDelta(statistics, simpleDocumentSubtreeStatistics(before), -1);
10957
- applyDocumentTextStatisticsDelta(statistics, simpleDocumentSubtreeStatistics(after), 1);
10958
- }
10959
- if (Object.values(statistics).some((value)=>value < 0)) return false;
10960
- statisticsByDocument.set(next, statistics);
10961
- return true;
10962
- }
10963
- function simpleDocumentSubtreeStatistics(root) {
10964
- const statistics = {
10965
- characterCountWithSpaces: 0,
10966
- characterCountWithoutSpaces: 0,
10967
- paragraphCount: 0,
10968
- wordCount: 0
10969
- };
10970
- const visit = (node)=>{
10971
- if (node.type.name === DOCUMENT_LAZY_BLOCK_NODE) {
10972
- accumulateDocumentTextStatistics(statistics, node.textContent);
10973
- const logicalParagraphs = Number(node.attrs.paragraphCount);
10974
- statistics.paragraphCount += Number.isSafeInteger(logicalParagraphs) && logicalParagraphs >= 0 ? logicalParagraphs : 0;
10975
- return;
10976
- }
10977
- if (node.isTextblock) {
10978
- accumulateDocumentTextStatistics(statistics, node.textContent);
10979
- statistics.paragraphCount += 1;
10980
- return;
10981
- }
10982
- node.forEach(visit);
10983
- };
10984
- visit(root);
10985
- return statistics;
10986
- }
10987
- function accumulateDocumentTextStatistics(statistics, source) {
10988
- for(let index = 0; index < source.length; index += 1){
10989
- const codePoint = source.codePointAt(index);
10990
- if (void 0 !== codePoint) {
10991
- if (codePoint > 0xffff) index += 1;
10992
- if (codePoint !== "".codePointAt(0) && 0x0a !== codePoint && 0x0d !== codePoint) {
10993
- statistics.characterCountWithSpaces += 1;
10994
- if (!isEcmaScriptWhitespace(codePoint)) statistics.characterCountWithoutSpaces += 1;
10995
- }
10996
- }
10997
- }
10998
- statistics.wordCount += documentWordCount(source);
10999
- }
11000
- function applyDocumentTextStatisticsDelta(target, value, direction) {
11001
- target.characterCountWithSpaces += direction * value.characterCountWithSpaces;
11002
- target.characterCountWithoutSpaces += direction * value.characterCountWithoutSpaces;
11003
- target.paragraphCount += direction * value.paragraphCount;
11004
- target.wordCount += direction * value.wordCount;
11005
- }
11006
- function isEcmaScriptWhitespace(codePoint) {
11007
- 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;
11008
- }
11009
11166
  const INITIAL_DOCUMENT_CHUNK_WINDOW_SIZE = 2;
11010
11167
  const DOCUMENT_CHUNK_WINDOW_ROOT_MARGIN = '0px';
11011
11168
  const DOCUMENT_CHUNK_PAGINATION_META = 'documentChunkPaginationGeometry';
@@ -12348,15 +12505,33 @@ function resolveDocumentPageFields(pages, sections) {
12348
12505
  ]));
12349
12506
  const sectionPages = new Map();
12350
12507
  for (const page of pages)for (const sectionId of new Set(page.segments.map((segment)=>segment.sectionId)))sectionPages.set(sectionId, (sectionPages.get(sectionId) ?? 0) + 1);
12508
+ const statistics = documentFieldStatisticsFromHtml(sections.map((section)=>section.html).join('\n'));
12509
+ const bookmarkPages = documentBookmarkPageNumbers(pages);
12351
12510
  const now = new Date();
12352
12511
  for (const page of pages)for (const segment of page.segments)segment.html = resolveDocumentFieldsHtml(segment.html, {
12353
12512
  pageNumber: page.pageNumber,
12354
12513
  totalPages: pages.length,
12355
12514
  sectionNumber: sectionNumbers.get(segment.sectionId) ?? 1,
12356
12515
  sectionPages: sectionPages.get(segment.sectionId) ?? 1,
12516
+ wordCount: statistics.wordCount,
12517
+ characterCount: statistics.characterCount,
12518
+ bookmarkPageNumbers: bookmarkPages,
12357
12519
  now
12358
12520
  });
12359
12521
  }
12522
+ function documentBookmarkPageNumbers(pages) {
12523
+ const values = new Map();
12524
+ for (const page of pages)for (const segment of page.segments){
12525
+ const document1 = new DOMParser().parseFromString(segment.html, 'text/html');
12526
+ for (const boundary of Array.from(document1.body.querySelectorAll('[data-document-bookmark-boundary][data-bookmark-kind="start"]'))){
12527
+ const id = boundary.dataset.bookmarkId?.trim();
12528
+ const name = boundary.dataset.bookmarkName?.trim();
12529
+ if (id && !values.has(`id:${id}`)) values.set(`id:${id}`, page.pageNumber);
12530
+ if (name && !values.has(`name:${name.toLowerCase()}`)) values.set(`name:${name.toLowerCase()}`, page.pageNumber);
12531
+ }
12532
+ }
12533
+ return values;
12534
+ }
12360
12535
  function samePhysicalPageLayout(current, next) {
12361
12536
  return current.pageSize === next.pageSize && current.orientation === next.orientation && JSON.stringify(current.pageGeometry) === JSON.stringify(next.pageGeometry) && current.margins.top === next.margins.top && current.margins.right === next.margins.right && current.margins.bottom === next.margins.bottom && current.margins.left === next.margins.left && JSON.stringify(current.pageMargins) === JSON.stringify(next.pageMargins);
12362
12537
  }
@@ -12447,7 +12622,7 @@ const DocumentField = core_Node.create({
12447
12622
  },
12448
12623
  addCommands () {
12449
12624
  return {
12450
- insertDocumentField: (kind)=>(props)=>insertDocumentFieldCommand(props, kind),
12625
+ insertDocumentField: (kind, options = {})=>(props)=>insertDocumentFieldCommand(props, kind, options),
12451
12626
  refreshDocumentFields: (content, options)=>(props)=>refreshDocumentFieldsCommand(props, content, options)
12452
12627
  };
12453
12628
  },
@@ -12456,7 +12631,10 @@ const DocumentField = core_Node.create({
12456
12631
  id: work_document_field_node_hiddenAttribute(''),
12457
12632
  kind: work_document_field_node_hiddenAttribute('page'),
12458
12633
  instruction: work_document_field_node_hiddenAttribute('PAGE'),
12459
- display: work_document_field_node_hiddenAttribute('1')
12634
+ display: work_document_field_node_hiddenAttribute('1'),
12635
+ targetId: work_document_field_node_hiddenAttribute(''),
12636
+ targetName: work_document_field_node_hiddenAttribute(''),
12637
+ orphaned: work_document_field_node_hiddenAttribute(false)
12460
12638
  };
12461
12639
  },
12462
12640
  parseHTML () {
@@ -12467,11 +12645,15 @@ const DocumentField = core_Node.create({
12467
12645
  if (!(node instanceof HTMLElement)) return false;
12468
12646
  const instruction = node.dataset.fieldInstruction?.trim() ?? '';
12469
12647
  const kind = documentFieldKind(node.dataset.fieldKind) ?? docxDocumentFieldKind(instruction) ?? 'page';
12648
+ const targetName = node.dataset.fieldTargetName ?? docxDocumentFieldTarget(instruction) ?? '';
12470
12649
  return {
12471
12650
  id: node.dataset.fieldId ?? '',
12472
12651
  kind,
12473
- instruction: instruction || documentFieldInstruction(kind),
12474
- display: node.dataset.fieldDisplay?.trim() || node.textContent?.trim() || documentFieldLabel(kind)
12652
+ instruction: 'pageReference' === kind && targetName ? documentPageReferenceInstruction(targetName, instruction) : instruction || documentFieldInstruction(kind),
12653
+ display: node.dataset.fieldDisplay?.trim() || node.textContent?.trim() || documentFieldLabel(kind),
12654
+ targetId: node.dataset.fieldTargetId ?? '',
12655
+ targetName,
12656
+ orphaned: 'true' === node.dataset.fieldOrphaned
12475
12657
  };
12476
12658
  }
12477
12659
  }
@@ -12481,14 +12663,23 @@ const DocumentField = core_Node.create({
12481
12663
  const instruction = 'string' == typeof node.attrs.instruction ? node.attrs.instruction.trim() : '';
12482
12664
  const kind = documentFieldKind(node.attrs.kind) ?? docxDocumentFieldKind(instruction) ?? 'page';
12483
12665
  const display = 'string' == typeof node.attrs.display && node.attrs.display.trim() ? node.attrs.display.trim() : documentFieldLabel(kind);
12666
+ const targetName = 'string' == typeof node.attrs.targetName ? node.attrs.targetName : '';
12667
+ const normalizedInstruction = 'pageReference' === kind && targetName ? documentPageReferenceInstruction(targetName, instruction) : instruction || documentFieldInstruction(kind);
12484
12668
  return [
12485
12669
  'span',
12486
12670
  mergeAttributes(HTMLAttributes, {
12487
12671
  'data-document-field': 'true',
12488
12672
  'data-field-id': 'string' == typeof node.attrs.id ? node.attrs.id : '',
12489
12673
  'data-field-kind': kind,
12490
- 'data-field-instruction': instruction || documentFieldInstruction(kind),
12674
+ 'data-field-instruction': normalizedInstruction,
12491
12675
  'data-field-display': display,
12676
+ ...'pageReference' === kind && 'string' == typeof node.attrs.targetId && node.attrs.targetId ? {
12677
+ 'data-field-target-id': node.attrs.targetId
12678
+ } : {},
12679
+ ...'pageReference' === kind && 'string' == typeof node.attrs.targetName && node.attrs.targetName ? {
12680
+ 'data-field-target-name': node.attrs.targetName
12681
+ } : {},
12682
+ 'data-field-orphaned': node.attrs.orphaned ? 'true' : void 0,
12492
12683
  class: 'work-document-field',
12493
12684
  title: documentFieldLabel(kind)
12494
12685
  }),
@@ -12500,16 +12691,26 @@ const DocumentField = core_Node.create({
12500
12691
  return 'string' == typeof node.attrs.display && node.attrs.display.trim() ? node.attrs.display.trim() : documentFieldLabel(kind);
12501
12692
  }
12502
12693
  });
12503
- function insertDocumentFieldCommand({ dispatch, editor, state, tr }, kind) {
12694
+ function insertDocumentFieldCommand({ dispatch, editor, state, tr }, kind, options) {
12504
12695
  const fieldType = editor.schema.nodes.documentField;
12505
12696
  if (!fieldType) return false;
12697
+ const targetName = 'string' == typeof options.targetName ? options.targetName.trim() : '';
12698
+ if ('pageReference' === kind && !targetName) return false;
12506
12699
  if (!dispatch) return true;
12507
- const instruction = documentFieldInstruction(kind);
12700
+ const instruction = documentFieldInstruction(kind, options);
12701
+ const statistics = documentFieldStatisticsFromText(state.doc.textBetween(0, state.doc.content.size, '\n', '\uFFFC'));
12702
+ const context = {
12703
+ ...fallbackContext(state),
12704
+ ...statistics
12705
+ };
12508
12706
  tr.replaceSelectionWith(fieldType.create({
12509
12707
  id: createWorkId('field'),
12510
12708
  kind,
12511
12709
  instruction,
12512
- display: documentFieldDisplay(kind, fallbackContext(state), instruction)
12710
+ display: documentFieldDisplay(kind, context, instruction),
12711
+ targetId: options.targetId?.trim() ?? '',
12712
+ targetName,
12713
+ orphaned: 'pageReference' === kind && !documentBookmarkExists(state, options)
12513
12714
  }), false);
12514
12715
  tr.scrollIntoView();
12515
12716
  return true;
@@ -12529,6 +12730,7 @@ function refreshDocumentFieldsCommand({ editor, state, tr }, content, options =
12529
12730
  ]
12530
12731
  ] : []));
12531
12732
  const now = work_document_field_node_validDate(options.now) ?? new Date();
12733
+ const statistics = documentFieldStatisticsFromText(state.doc.textBetween(0, state.doc.content.size, '\n', '\uFFFC'));
12532
12734
  let fieldIndex = 0;
12533
12735
  state.doc.descendants((node, position)=>{
12534
12736
  if ('documentField' !== node.type.name) return;
@@ -12540,12 +12742,20 @@ function refreshDocumentFieldsCommand({ editor, state, tr }, content, options =
12540
12742
  const context = options.resolveContext?.(position);
12541
12743
  const display = context ? documentFieldDisplay(kind, {
12542
12744
  ...context,
12745
+ wordCount: context.wordCount ?? statistics.wordCount,
12746
+ characterCount: context.characterCount ?? statistics.characterCount,
12747
+ referencePageNumber: 'pageReference' === kind ? resolveBookmarkPageNumber(state, node, options.resolveContext) : context.referencePageNumber,
12543
12748
  now: work_document_field_node_validDate(context.now) ?? now
12544
12749
  }, work_document_field_node_stringAttribute(node.attrs.instruction), work_document_field_node_stringAttribute(node.attrs.display)) : fallbackById.get(work_document_field_node_stringAttribute(node.attrs.id)) ?? fallback;
12545
- if (!display || node.attrs.display === display) return;
12750
+ if (!display) return;
12751
+ const targetName = work_document_field_node_stringAttribute(node.attrs.targetName) || docxDocumentFieldTarget(work_document_field_node_stringAttribute(node.attrs.instruction)) || '';
12752
+ const orphaned = 'pageReference' === kind ? !bookmarkBoundaryForField(state, node, targetName) : false;
12753
+ if (node.attrs.display === display && node.attrs.targetName === targetName && node.attrs.orphaned === orphaned) return;
12546
12754
  tr.setNodeMarkup(position, void 0, {
12547
12755
  ...node.attrs,
12548
- display
12756
+ display,
12757
+ targetName,
12758
+ orphaned
12549
12759
  });
12550
12760
  });
12551
12761
  if (tr.docChanged && false === options.addToHistory) tr.setMeta('addToHistory', false);
@@ -12605,6 +12815,48 @@ function work_document_field_node_validDate(value) {
12605
12815
  function work_document_field_node_stringAttribute(value) {
12606
12816
  return 'string' == typeof value ? value.trim() : '';
12607
12817
  }
12818
+ function documentBookmarkExists(state, options) {
12819
+ const targetId = work_document_field_node_stringAttribute(options.targetId);
12820
+ const targetName = work_document_field_node_stringAttribute(options.targetName).toLowerCase();
12821
+ let found = false;
12822
+ state.doc.descendants((node)=>{
12823
+ if ('documentBookmarkBoundary' === node.type.name && 'start' === node.attrs.kind && (targetId && work_document_field_node_stringAttribute(node.attrs.id) === targetId || targetName && work_document_field_node_stringAttribute(node.attrs.name).toLowerCase() === targetName)) {
12824
+ found = true;
12825
+ return false;
12826
+ }
12827
+ return !found;
12828
+ });
12829
+ return found;
12830
+ }
12831
+ function bookmarkBoundaryForField(state, node, targetName) {
12832
+ const targetId = work_document_field_node_stringAttribute(node.attrs.targetId);
12833
+ const normalizedName = targetName.toLowerCase();
12834
+ let found = null;
12835
+ state.doc.descendants((candidate)=>{
12836
+ if (found || 'documentBookmarkBoundary' !== candidate.type.name || 'start' !== candidate.attrs.kind) return null === found;
12837
+ if (targetId && work_document_field_node_stringAttribute(candidate.attrs.id) === targetId || normalizedName && work_document_field_node_stringAttribute(candidate.attrs.name).toLowerCase() === normalizedName) {
12838
+ found = candidate;
12839
+ return false;
12840
+ }
12841
+ return true;
12842
+ });
12843
+ return found;
12844
+ }
12845
+ function resolveBookmarkPageNumber(state, node, resolveContext) {
12846
+ if (!resolveContext) return null;
12847
+ const targetId = work_document_field_node_stringAttribute(node.attrs.targetId);
12848
+ const targetName = work_document_field_node_stringAttribute(node.attrs.targetName).toLowerCase() || (docxDocumentFieldTarget(work_document_field_node_stringAttribute(node.attrs.instruction)) ?? '').toLowerCase();
12849
+ let page = null;
12850
+ state.doc.descendants((candidate, position)=>{
12851
+ if (null !== page || 'documentBookmarkBoundary' !== candidate.type.name || 'start' !== candidate.attrs.kind) return null === page;
12852
+ if (targetId && work_document_field_node_stringAttribute(candidate.attrs.id) === targetId || targetName && work_document_field_node_stringAttribute(candidate.attrs.name).toLowerCase() === targetName) {
12853
+ page = resolveContext(position)?.pageNumber ?? null;
12854
+ return false;
12855
+ }
12856
+ return true;
12857
+ });
12858
+ return page;
12859
+ }
12608
12860
  const DEFAULT_WRAP_SIDE = 'bothSides';
12609
12861
  const WRAP_COORDINATE_SIZE = 21600;
12610
12862
  const MAXIMUM_WRAP_COORDINATE = 2147483647;
@@ -22508,4 +22760,4 @@ function registerDocumentPageSurfaceGeometry(element, provider) {
22508
22760
  function documentPageSurfaceGeometryForElement(element) {
22509
22761
  return documentPageSurfaceProviders.get(element)?.() ?? null;
22510
22762
  }
22511
- 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_LEGACY_TEXT_EFFECT_NAMES, DOCUMENT_LEGACY_TEXT_EMBOSS_ATTRIBUTE, DOCUMENT_LEGACY_TEXT_IMPRINT_ATTRIBUTE, DOCUMENT_LEGACY_TEXT_OUTLINE_ATTRIBUTE, DOCUMENT_LEGACY_TEXT_SHADOW_ATTRIBUTE, DOCUMENT_OPEN_TYPE_ATTRIBUTE, DOCUMENT_PAGE_BORDER_EDGES, DOCUMENT_PAGE_MARGIN_KEYS, DOCUMENT_PARAGRAPH_CHANGE_ATTRIBUTES, DOCUMENT_PARAGRAPH_FORMAT_ATTRIBUTES, DOCUMENT_STRIKE_STYLE_ATTRIBUTE, DOCUMENT_TABLE_ROW_ID_ATTRIBUTE, DOCUMENT_TABLE_ROW_TEXT_ID_ATTRIBUTE, DOCUMENT_TABLE_STYLE_OPTIONS, DOCUMENT_TEXT_BOX_DEFAULTS, DOCUMENT_TEXT_BOX_LIMITS, DOCUMENT_UNDERLINE_STYLE_ATTRIBUTE, DocumentCharacterFormatting, DocumentEquation, DocumentFontFamily, DocumentHighlight, DocumentImage, DocumentParagraphFormatting, DocumentScriptFontFormatting, DocumentStrike, DocumentSubscript, DocumentSuperscript, DocumentTableRowIdentity, DocumentTextStyle, DocumentUnderline, MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT, MAX_DOCUMENT_NUMBERING_START, activeDocumentBookmark, activeDocumentSection, activeDocumentTableStyle, applyDocumentImageCropToElement, applyDocumentImageIdentityToElement, applyDocumentImageLayerToElement, applyDocumentImageTransformToElement, applyDocumentImageWrapContourToElement, applyDocumentPageGeometry, applyDocumentTableGeometryToElement, applyDocumentTableRowIdentityToElement, applyDocumentTextCaseStyle, canChangeDocumentIndent, canInsertDocumentComment, canSetDocumentTableRowRepeatHeader, clampDocumentMargin, collectDocumentChanges, collectDocumentCommentAnchors, collectDocumentNotes, collectDocumentTextLayoutParagraphs, createDocumentBibliography, createDocumentEquationElement, createDocumentImageIdentityRegistry, createDocumentNoteElement, createSchemaDerivedWorkDocumentModel, createWorkDocumentBlob, createWorkDocumentExtensions, createWorkDocumentModel, createWorkOfficeDocumentCollaborationBinding as createOfficeDocumentCollaborationBinding, defaultDocumentImageTransform, defaultDocumentImageWrapContour, documentAutoLineHeight, documentBookmarkNameExists, documentBookmarkReferenceInstruction, documentBulletListStyle, documentCaptionKind, documentCaptionLabel, documentCharacterPositionDomAttributes, documentCharacterPositionHalfPointsFromElement, documentCharacterPositionPoints, documentCharacterScaleDomAttributes, documentCharacterScalePercentFromElement, documentCharacterSpacingDomAttributes, documentCharacterSpacingPoints, documentCharacterSpacingTwipsFromElement, documentChunkMountedIds, documentCitationCount, documentCitationInstruction, documentCitationStyle, documentCitationStyleDetails, documentCitationTags, documentCitationTagsFromInstruction, documentCommentDraftRange, documentCommentViews, documentContentLayoutProperties, documentEmphasisMarkDomAttributes, documentEmphasisMarkFromElement, documentEquationFromElement, documentEquationText, documentHasIndex, documentHasTableOfContents, documentHiddenTextDomAttributes, documentHiddenTextFromElement, documentHiddenTextKeyboardShortcut, documentImageCropFromElement, documentImageIdentityFromElement, documentImageLayerFromElement, documentImageLayoutFromElement, documentImageLayoutOptions, documentImagePositionFromElement, documentImageProperties, documentImageTransformFromElement, documentImageWrapContourFromElement, documentInitialSectionLayout, documentKerningDomAttributes, documentKerningIsEffective, documentKerningThresholdHalfPointsFromElement, documentKerningThresholdPoints, documentLazyHtmlChunkFragment, documentLazyHtmlProjection, documentLazyHtmlProjectionFingerprint, documentLegacyTextEffectsConflict, documentLegacyTextEffectsCss, documentLegacyTextEffectsDomAttributes, documentLegacyTextEffectsFromElement, documentLegacyTextEffectsFromTextStyleAttributes, documentModelForContent, documentModelForHtml, documentModelHasTrustedInitialIntegrityFeatures, documentModelUsesWindowing, documentNoteKey, documentNoteKind, documentOpenTypeCssProperties, documentOpenTypeDomAttributes, documentOpenTypeFeaturesFromElement, documentOrderedListState, documentPageBordersVisible, documentPageChromeLegacyFields, documentPageGeometryForLayout, documentPageHorizontalMarginTwips, documentPageMarginBody, documentPageMarginsForLayout, documentPageMetrics, documentPageSurfaceGeometryForElement, documentPaperSizeForGeometry, documentParagraphDirection, documentParagraphIndent, documentParagraphPagination, documentParagraphSpacing, documentParagraphTabStops, documentSectionById, documentSectionDomAttributes, documentSections, documentStrikeDomAttributes, documentStrikeFormattingFromElement, documentStrikeStyle, documentTabLeaderLabel, documentTableBordersFromElement, documentTableCellFormat, documentTableCellMarginOverridesFromElement, documentTableColumnPercentagesFromElement, documentTableGeometryFromElement, documentTableHorizontalAlignment, documentTableRowIdentityFromElement, documentTableRowOptions, documentTableSizing, documentTextBoxProperties, documentTextBoxPropertiesFromElement, documentTextCaseFromWordFlags, documentTextCaseKeyboardShortcuts, documentTextLayoutBatches, documentTextStatistics, documentTransactionsOnlyHydrateChunks, documentUnderlineColor, documentUnderlineDomAttributes, documentUnderlineFormattingFromElement, documentUnderlineKeyboardShortcuts, documentUnderlineStyle, documentWordLineHeightFactor, docxBookmarkReferenceTarget, docxDocumentFieldKind, editorDocumentBookmarkReferenceTargets, editorDocumentCaptionTargets, fileNameWithoutExtension, forgetWorkSourceBlob as forgetSourceBlob, importWorkDocumentFile, importedDocumentCharacterFormatting, initializeWorkOfficeDocumentCollaboration as initializeOfficeDocumentCollaboration, invalidateDocumentLazyHtmlProjection, isContourImageLayout, isDocumentCharacterFormatMark, isDocumentOpenTypeFeaturePatch, isValidDocumentCitationTag, materializeLazyDocumentEditorRoot, measureDocumentLayoutBlocksIncrementally, millimetersToPixels, mountWorkLiveDocumentCapture, moveWorkSourceBlob, nextDocumentTabAlignment, normalizeDocumentBookmarkName, normalizeDocumentBookmarkNativeId, normalizeDocumentBookmarkReferencesHtml, normalizeDocumentBookmarksHtml, normalizeDocumentCaptionsHtml, normalizeDocumentCharacterPositionHalfPoints, normalizeDocumentCharacterScalePercent, normalizeDocumentCharacterSpacingTwips, normalizeDocumentCitationsHtml, normalizeDocumentColumns, normalizeDocumentEmphasisMark, normalizeDocumentEquation, normalizeDocumentFieldsHtml, normalizeDocumentHiddenText, normalizeDocumentHtml, normalizeDocumentImageAlignment, normalizeDocumentImageCrop, normalizeDocumentImageIdentity, normalizeDocumentImageLayer, normalizeDocumentImageLayoutOptions, normalizeDocumentImagePosition, normalizeDocumentImageTransform, normalizeDocumentImageWrapContour, normalizeDocumentImageWrapSide, normalizeDocumentKerningThresholdHalfPoints, normalizeDocumentLegacyTextEffect, normalizeDocumentLegacyTextEffects, normalizeDocumentNotesHtml, normalizeDocumentOpenTypeFeatures, normalizeDocumentOpenTypeLigatures, normalizeDocumentOpenTypeNumberForm, normalizeDocumentOpenTypeNumberSpacing, normalizeDocumentOpenTypeStylisticSets, normalizeDocumentPageBorders, normalizeDocumentPageChrome, normalizeDocumentPageGeometry, normalizeDocumentPageMargins, normalizeDocumentPaperSource, normalizeDocumentParagraphIndent, normalizeDocumentStrikeStyle, normalizeDocumentTabStops, normalizeDocumentTableBorderStyle, normalizeDocumentTableBorderWidth, normalizeDocumentTableRowHeightRule, normalizeDocumentTableRowIdentity, normalizeDocumentTableVerticalAlign, normalizeDocumentTextBoxProperties, normalizeDocumentTextCase, normalizeDocumentUnderlineColor, normalizeDocumentUnderlineStyle, normalizeTableColor, normalizedTabPosition, numberingTypeFromFormat, pageTwipsToMillimeters, parseDocumentCharacterFormatting, parseDocumentNumberingChange, parseDocumentOpenTypeFeatures, parseDocumentParagraphFormatting, patchDocumentLazyHtmlProjection, patchDocumentOpenTypeFeatures, positionWorkLiveDocumentCapture, readWorkOfficeDocumentCollaboration as readOfficeDocumentCollaboration, readWorkSourceBlob as readSourceBlob, registerDocumentPageSurfaceGeometry, rememberWorkSourceBlob as registerSourceBlob, renderDocumentAutoLineHeight, renderDocumentTableBorders, renderDocumentTableCellMarginOverrides, resolveAllDocumentChanges, resolveDocumentPageBorders, resolveDocumentPageChrome, resolveDocumentPageMargins, resolveDocumentPageSize, resolveWorkDocumentEditorInput, retainAnchoredDocumentComments, sanitizeDocumentPageChromeHtml, selectedDocumentChunkId, selectedDocumentIndexDraft, selectedDocumentIndexEntry, selectedDocumentIndexOptions, selectedDocumentTableOfContentsOptions, serializeDocumentCharacterFormatting, serializeDocumentNumberingChange, serializeDocumentOpenTypeFeatures, serializeDocumentParagraphFormatting, serializeDocumentTabStops, serializeWorkDocumentNode, setCustomDocumentColumns, supportedDocxBookmarkReferenceInstruction, syncDocumentContentFromHtml, textBoxCss, textBoxDomAttributes, transferChangedDocumentTextStatistics, uniqueDocumentImageIdentity, updateDocumentColumnWidth, updateDocumentCustomPageMillimeters, updateDocumentGutterPosition, updateDocumentMirrorMargins, updateDocumentPageChromeVariant, updateDocumentPageMarginMillimeters, updateDocumentPageMarginMode, updateDocumentPageOrientation, updateDocumentPaperSizePreset, validateDocumentBookmarkName, windowDocumentModel, workDocumentSchema, workOfficeDocumentCollaborationFragment as officeDocumentCollaborationFragment, work_document_model_codec_createWorkDocumentModelFromContent as createWorkDocumentModelFromContent, work_document_model_codec_materializeWorkDocumentContent as materializeWorkDocumentContent, work_document_model_createSchemaValidatedWorkDocumentModel as createSchemaValidatedWorkDocumentModel, work_document_page_margins_twipsToMillimeters, work_file_download_downloadBlob, work_file_download_safeFileName, wrapsBesideImage };
22763
+ 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_LEGACY_TEXT_EFFECT_NAMES, DOCUMENT_LEGACY_TEXT_EMBOSS_ATTRIBUTE, DOCUMENT_LEGACY_TEXT_IMPRINT_ATTRIBUTE, DOCUMENT_LEGACY_TEXT_OUTLINE_ATTRIBUTE, DOCUMENT_LEGACY_TEXT_SHADOW_ATTRIBUTE, DOCUMENT_OPEN_TYPE_ATTRIBUTE, DOCUMENT_PAGE_BORDER_EDGES, DOCUMENT_PAGE_MARGIN_KEYS, DOCUMENT_PARAGRAPH_CHANGE_ATTRIBUTES, DOCUMENT_PARAGRAPH_FORMAT_ATTRIBUTES, DOCUMENT_STRIKE_STYLE_ATTRIBUTE, DOCUMENT_TABLE_ROW_ID_ATTRIBUTE, DOCUMENT_TABLE_ROW_TEXT_ID_ATTRIBUTE, DOCUMENT_TABLE_STYLE_OPTIONS, DOCUMENT_TEXT_BOX_DEFAULTS, DOCUMENT_TEXT_BOX_LIMITS, DOCUMENT_UNDERLINE_STYLE_ATTRIBUTE, DocumentCharacterFormatting, DocumentEquation, DocumentFontFamily, DocumentHighlight, DocumentImage, DocumentParagraphFormatting, DocumentScriptFontFormatting, DocumentStrike, DocumentSubscript, DocumentSuperscript, DocumentTableRowIdentity, DocumentTextStyle, DocumentUnderline, MAX_DOCUMENT_IMAGE_RELATIVE_HEIGHT, MAX_DOCUMENT_NUMBERING_START, activeDocumentBookmark, activeDocumentSection, activeDocumentTableStyle, applyDocumentImageCropToElement, applyDocumentImageIdentityToElement, applyDocumentImageLayerToElement, applyDocumentImageTransformToElement, applyDocumentImageWrapContourToElement, applyDocumentPageGeometry, applyDocumentTableGeometryToElement, applyDocumentTableRowIdentityToElement, applyDocumentTextCaseStyle, canChangeDocumentIndent, canInsertDocumentComment, canSetDocumentTableRowRepeatHeader, clampDocumentMargin, collectDocumentChanges, collectDocumentCommentAnchors, collectDocumentNotes, collectDocumentTextLayoutParagraphs, createDocumentBibliography, createDocumentEquationElement, createDocumentImageIdentityRegistry, createDocumentNoteElement, createSchemaDerivedWorkDocumentModel, createWorkDocumentBlob, createWorkDocumentExtensions, createWorkDocumentModel, createWorkOfficeDocumentCollaborationBinding as createOfficeDocumentCollaborationBinding, defaultDocumentImageTransform, defaultDocumentImageWrapContour, documentAutoLineHeight, documentBookmarkNameExists, documentBookmarkReferenceInstruction, documentBulletListStyle, documentCaptionKind, documentCaptionLabel, documentCharacterPositionDomAttributes, documentCharacterPositionHalfPointsFromElement, documentCharacterPositionPoints, documentCharacterScaleDomAttributes, documentCharacterScalePercentFromElement, documentCharacterSpacingDomAttributes, documentCharacterSpacingPoints, documentCharacterSpacingTwipsFromElement, documentChunkMountedIds, documentCitationCount, documentCitationInstruction, documentCitationStyle, documentCitationStyleDetails, documentCitationTags, documentCitationTagsFromInstruction, documentCommentDraftRange, documentCommentViews, documentContentLayoutProperties, documentEmphasisMarkDomAttributes, documentEmphasisMarkFromElement, documentEquationFromElement, documentEquationText, documentHasIndex, documentHasTableOfContents, documentHiddenTextDomAttributes, documentHiddenTextFromElement, documentHiddenTextKeyboardShortcut, documentImageCropFromElement, documentImageIdentityFromElement, documentImageLayerFromElement, documentImageLayoutFromElement, documentImageLayoutOptions, documentImagePositionFromElement, documentImageProperties, documentImageTransformFromElement, documentImageWrapContourFromElement, documentInitialSectionLayout, documentKerningDomAttributes, documentKerningIsEffective, documentKerningThresholdHalfPointsFromElement, documentKerningThresholdPoints, documentLazyHtmlChunkFragment, documentLazyHtmlProjection, documentLazyHtmlProjectionFingerprint, documentLegacyTextEffectsConflict, documentLegacyTextEffectsCss, documentLegacyTextEffectsDomAttributes, documentLegacyTextEffectsFromElement, documentLegacyTextEffectsFromTextStyleAttributes, documentModelForContent, documentModelForHtml, documentModelHasTrustedInitialIntegrityFeatures, documentModelUsesWindowing, documentNoteKey, documentNoteKind, documentOpenTypeCssProperties, documentOpenTypeDomAttributes, documentOpenTypeFeaturesFromElement, documentOrderedListState, documentPageBordersVisible, documentPageChromeLegacyFields, documentPageGeometryForLayout, documentPageHorizontalMarginTwips, documentPageMarginBody, documentPageMarginsForLayout, documentPageMetrics, documentPageSurfaceGeometryForElement, documentPaperSizeForGeometry, documentParagraphDirection, documentParagraphIndent, documentParagraphPagination, documentParagraphSpacing, documentParagraphTabStops, documentSectionById, documentSectionDomAttributes, documentSections, documentStrikeDomAttributes, documentStrikeFormattingFromElement, documentStrikeStyle, documentTabLeaderLabel, documentTableBordersFromElement, documentTableCellFormat, documentTableCellMarginOverridesFromElement, documentTableColumnPercentagesFromElement, documentTableGeometryFromElement, documentTableHorizontalAlignment, documentTableRowIdentityFromElement, documentTableRowOptions, documentTableSizing, documentTextBoxProperties, documentTextBoxPropertiesFromElement, documentTextCaseFromWordFlags, documentTextCaseKeyboardShortcuts, documentTextLayoutBatches, documentTextStatistics, documentTransactionsOnlyHydrateChunks, documentUnderlineColor, documentUnderlineDomAttributes, documentUnderlineFormattingFromElement, documentUnderlineKeyboardShortcuts, documentUnderlineStyle, documentWordLineHeightFactor, docxBookmarkReferenceTarget, docxDocumentFieldKind, docxDocumentFieldTarget, editorDocumentBookmarkReferenceTargets, editorDocumentCaptionTargets, fileNameWithoutExtension, forgetWorkSourceBlob as forgetSourceBlob, importWorkDocumentFile, importedDocumentCharacterFormatting, initializeWorkOfficeDocumentCollaboration as initializeOfficeDocumentCollaboration, invalidateDocumentLazyHtmlProjection, isContourImageLayout, isDocumentCharacterFormatMark, isDocumentOpenTypeFeaturePatch, isValidDocumentCitationTag, materializeLazyDocumentEditorRoot, measureDocumentLayoutBlocksIncrementally, millimetersToPixels, mountWorkLiveDocumentCapture, moveWorkSourceBlob, nextDocumentTabAlignment, normalizeDocumentBookmarkName, normalizeDocumentBookmarkNativeId, normalizeDocumentBookmarkReferencesHtml, normalizeDocumentBookmarksHtml, normalizeDocumentCaptionsHtml, normalizeDocumentCharacterPositionHalfPoints, normalizeDocumentCharacterScalePercent, normalizeDocumentCharacterSpacingTwips, normalizeDocumentCitationsHtml, normalizeDocumentColumns, normalizeDocumentEmphasisMark, normalizeDocumentEquation, normalizeDocumentFieldsHtml, normalizeDocumentHiddenText, normalizeDocumentHtml, normalizeDocumentImageAlignment, normalizeDocumentImageCrop, normalizeDocumentImageIdentity, normalizeDocumentImageLayer, normalizeDocumentImageLayoutOptions, normalizeDocumentImagePosition, normalizeDocumentImageTransform, normalizeDocumentImageWrapContour, normalizeDocumentImageWrapSide, normalizeDocumentKerningThresholdHalfPoints, normalizeDocumentLegacyTextEffect, normalizeDocumentLegacyTextEffects, normalizeDocumentNotesHtml, normalizeDocumentOpenTypeFeatures, normalizeDocumentOpenTypeLigatures, normalizeDocumentOpenTypeNumberForm, normalizeDocumentOpenTypeNumberSpacing, normalizeDocumentOpenTypeStylisticSets, normalizeDocumentPageBorders, normalizeDocumentPageChrome, normalizeDocumentPageGeometry, normalizeDocumentPageMargins, normalizeDocumentPaperSource, normalizeDocumentParagraphIndent, normalizeDocumentStrikeStyle, normalizeDocumentTabStops, normalizeDocumentTableBorderStyle, normalizeDocumentTableBorderWidth, normalizeDocumentTableRowHeightRule, normalizeDocumentTableRowIdentity, normalizeDocumentTableVerticalAlign, normalizeDocumentTextBoxProperties, normalizeDocumentTextCase, normalizeDocumentUnderlineColor, normalizeDocumentUnderlineStyle, normalizeTableColor, normalizedTabPosition, numberingTypeFromFormat, pageTwipsToMillimeters, parseDocumentCharacterFormatting, parseDocumentNumberingChange, parseDocumentOpenTypeFeatures, parseDocumentParagraphFormatting, patchDocumentLazyHtmlProjection, patchDocumentOpenTypeFeatures, positionWorkLiveDocumentCapture, readWorkOfficeDocumentCollaboration as readOfficeDocumentCollaboration, readWorkSourceBlob as readSourceBlob, registerDocumentPageSurfaceGeometry, rememberWorkSourceBlob as registerSourceBlob, renderDocumentAutoLineHeight, renderDocumentTableBorders, renderDocumentTableCellMarginOverrides, resolveAllDocumentChanges, resolveDocumentPageBorders, resolveDocumentPageChrome, resolveDocumentPageMargins, resolveDocumentPageSize, resolveWorkDocumentEditorInput, retainAnchoredDocumentComments, sanitizeDocumentPageChromeHtml, selectedDocumentChunkId, selectedDocumentIndexDraft, selectedDocumentIndexEntry, selectedDocumentIndexOptions, selectedDocumentTableOfContentsOptions, serializeDocumentCharacterFormatting, serializeDocumentNumberingChange, serializeDocumentOpenTypeFeatures, serializeDocumentParagraphFormatting, serializeDocumentTabStops, serializeWorkDocumentNode, setCustomDocumentColumns, supportedDocxBookmarkReferenceInstruction, supportedDocxDocumentFieldInstruction, syncDocumentContentFromHtml, textBoxCss, textBoxDomAttributes, transferChangedDocumentTextStatistics, uniqueDocumentImageIdentity, updateDocumentColumnWidth, updateDocumentCustomPageMillimeters, updateDocumentGutterPosition, updateDocumentMirrorMargins, updateDocumentPageChromeVariant, updateDocumentPageMarginMillimeters, updateDocumentPageMarginMode, updateDocumentPageOrientation, updateDocumentPaperSizePreset, validateDocumentBookmarkName, windowDocumentModel, workDocumentSchema, workOfficeDocumentCollaborationFragment as officeDocumentCollaborationFragment, work_document_model_codec_createWorkDocumentModelFromContent as createWorkDocumentModelFromContent, work_document_model_codec_materializeWorkDocumentContent as materializeWorkDocumentContent, work_document_model_createSchemaValidatedWorkDocumentModel as createSchemaValidatedWorkDocumentModel, work_document_page_margins_twipsToMillimeters, work_file_download_downloadBlob, work_file_download_safeFileName, wrapsBesideImage };