@a3s-lab/office 0.48.1 → 0.49.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/COLLABORATION_ROADMAP.md +14 -6
- package/README.md +16 -0
- package/dist/{0~3320.js → 0~5600.js} +271 -13
- package/dist/0~controlled-editor-composition.js +167 -15
- package/dist/0~document-editor.js +65 -10
- package/dist/0~work-docx-export.js +143 -9
- package/dist/0~work-docx-import.js +5 -120
- package/dist/0~work-office-diagnostics.js +16 -5
- package/dist/4174.js +93 -8
- package/dist/internal/features/work/editors/controlled-editor-composition.d.ts +53 -7
- package/dist/internal/features/work/editors/use-document-pagination.d.ts +3 -1
- package/dist/internal/features/work/work-document-changes.d.ts +4 -2
- package/dist/internal/features/work/work-docx-change-import.d.ts +10 -1
- package/dist/internal/features/work/work-docx-move-revision-export.d.ts +35 -0
- package/dist/internal/features/work/work-types.d.ts +3 -1
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +9 -0
- package/docs/latest/en/browser-editor-architecture.md +14 -2
- package/package.json +11 -3
|
@@ -1172,6 +1172,7 @@ function documentChangeKindLabel(kind) {
|
|
|
1172
1172
|
if ('formatting' === kind) return '格式';
|
|
1173
1173
|
if ('paragraph-formatting' === kind) return '段落格式';
|
|
1174
1174
|
if ('numbering' === kind) return '编号格式';
|
|
1175
|
+
if ('move' === kind) return '移动';
|
|
1175
1176
|
return '删除';
|
|
1176
1177
|
}
|
|
1177
1178
|
function documentChangeWindowKey(change) {
|
|
@@ -19872,7 +19873,10 @@ function createDocumentPaginationRunCoordinator({ cancelFrame, onAbort, onCoales
|
|
|
19872
19873
|
};
|
|
19873
19874
|
}
|
|
19874
19875
|
const MAX_DOCUMENT_BLOCK_RESIZE_OBSERVATIONS = 4096;
|
|
19875
|
-
function
|
|
19876
|
+
function neverComposing() {
|
|
19877
|
+
return false;
|
|
19878
|
+
}
|
|
19879
|
+
function useDocumentPagination({ compositionRevision = 0, editor, documentRevision, enabled, isComposing = neverComposing, layoutKey, page, selectionVersion, wasmUrl, layoutFonts, loadedLayoutFontIds }) {
|
|
19876
19880
|
const client = useOfficeKernelClient(wasmUrl, layoutFonts);
|
|
19877
19881
|
const editorMounted = useEditorMounted(editor);
|
|
19878
19882
|
const revision = useRef(0);
|
|
@@ -20011,6 +20015,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20011
20015
|
for (const element of observedElements)observer?.observe(element);
|
|
20012
20016
|
};
|
|
20013
20017
|
const run = async (signal)=>{
|
|
20018
|
+
if (isComposing()) return void measurementRange.ensureDirty();
|
|
20014
20019
|
stopObservingBlocks();
|
|
20015
20020
|
const nextRevision = ++revision.current;
|
|
20016
20021
|
editor.commands.clearDocumentPagination(nextRevision);
|
|
@@ -20040,7 +20045,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20040
20045
|
documentRevision,
|
|
20041
20046
|
paragraphs
|
|
20042
20047
|
}, signal);
|
|
20043
|
-
if (disposed || signal.aborted || nextRevision !== revision.current || editor.isDestroyed) return;
|
|
20048
|
+
if (disposed || signal.aborted || nextRevision !== revision.current || editor.isDestroyed || isComposing()) return;
|
|
20044
20049
|
textLayoutEngine ||= textLayout.engine;
|
|
20045
20050
|
unsupportedTextLayoutCount += textLayout.unsupportedParagraphIds.length + textLayout.layouts.filter((layout)=>layout.missingGlyphCount > 0).length;
|
|
20046
20051
|
for (const layout of textLayout.layouts)if (0 === layout.missingGlyphCount) {
|
|
@@ -20051,7 +20056,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20051
20056
|
editorDom.dataset.paginationTextEngine = textLayoutEngine;
|
|
20052
20057
|
editorDom.dataset.paginationUnsupportedText = String(unsupportedTextLayoutCount);
|
|
20053
20058
|
} catch (error) {
|
|
20054
|
-
if (signal.aborted || error instanceof DOMException && 'AbortError' === error.name) return;
|
|
20059
|
+
if (signal.aborted || isComposing() || error instanceof DOMException && 'AbortError' === error.name) return;
|
|
20055
20060
|
textLayouts.clear();
|
|
20056
20061
|
fallbackGlyphCount = 0;
|
|
20057
20062
|
editorDom.dataset.paginationTextEngine = 'dom';
|
|
@@ -20069,6 +20074,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20069
20074
|
const snapshot = await measureDocumentLayoutBlocksIncrementally(editor, measurementCache.current, measurementStart, textLayouts, 1000000, {
|
|
20070
20075
|
signal
|
|
20071
20076
|
});
|
|
20077
|
+
if (disposed || signal.aborted || nextRevision !== revision.current || editor.isDestroyed || isComposing()) return;
|
|
20072
20078
|
measurementCache.current = snapshot;
|
|
20073
20079
|
editorDom.dataset.paginationBlocks = String(snapshot.blocks.length);
|
|
20074
20080
|
editorDom.dataset.paginationFlows = String(new Set(snapshot.blocks.flatMap((block)=>block.block.flowId ? [
|
|
@@ -20098,7 +20104,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20098
20104
|
pageStyles: snapshot.pageStyles,
|
|
20099
20105
|
blocks: layoutPlan.blocks
|
|
20100
20106
|
}, signal);
|
|
20101
|
-
if (disposed || signal.aborted || nextRevision !== revision.current || editor.isDestroyed) return;
|
|
20107
|
+
if (disposed || signal.aborted || nextRevision !== revision.current || editor.isDestroyed || isComposing()) return;
|
|
20102
20108
|
const layout = previousPagination && layoutPlan.startPageIndex > 0 ? mergeIncrementalDocumentLayout(previousPagination.layout, partialLayout) : partialLayout;
|
|
20103
20109
|
const blockById = new Map(snapshot.blocks.map((block)=>[
|
|
20104
20110
|
block.block.id,
|
|
@@ -20147,7 +20153,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20147
20153
|
measurementRange.commit(measurementPass);
|
|
20148
20154
|
setPagination(nextPagination);
|
|
20149
20155
|
} catch (error) {
|
|
20150
|
-
if (disposed || signal.aborted || error instanceof DOMException && 'AbortError' === error.name) return;
|
|
20156
|
+
if (disposed || signal.aborted || isComposing() || error instanceof DOMException && 'AbortError' === error.name) return;
|
|
20151
20157
|
editorDom.dataset.paginationState = 'error';
|
|
20152
20158
|
editorDom.dataset.paginationError = error instanceof Error ? `${error.name}: ${error.message}` : 'UnknownError';
|
|
20153
20159
|
editor.commands.clearDocumentPagination(nextRevision);
|
|
@@ -20167,7 +20173,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20167
20173
|
onCoalescedRequest: ()=>updateDiagnostic('coalescedRequests', 'paginationCoalescedRequests'),
|
|
20168
20174
|
onError: (error)=>{
|
|
20169
20175
|
measurementRange.restoreActive();
|
|
20170
|
-
if (disposed || editor.isDestroyed) return;
|
|
20176
|
+
if (disposed || editor.isDestroyed || isComposing()) return;
|
|
20171
20177
|
editorDom.dataset.paginationState = 'error';
|
|
20172
20178
|
editorDom.dataset.paginationError = error instanceof Error ? `${error.name}: ${error.message}` : 'UnknownError';
|
|
20173
20179
|
paginationCache.current = null;
|
|
@@ -20184,6 +20190,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20184
20190
|
run
|
|
20185
20191
|
});
|
|
20186
20192
|
const schedule = (invalidateActive = false)=>{
|
|
20193
|
+
if (isComposing()) return;
|
|
20187
20194
|
coordinator.request({
|
|
20188
20195
|
invalidateActive
|
|
20189
20196
|
});
|
|
@@ -20200,6 +20207,15 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20200
20207
|
markDirty(earliestChangedPosition(transaction));
|
|
20201
20208
|
schedule(true);
|
|
20202
20209
|
};
|
|
20210
|
+
const handleCompositionStart = ()=>{
|
|
20211
|
+
measurementRange.ensureDirty();
|
|
20212
|
+
coordinator.request({
|
|
20213
|
+
invalidateActive: true
|
|
20214
|
+
});
|
|
20215
|
+
};
|
|
20216
|
+
const handleCompositionEnd = ()=>{
|
|
20217
|
+
schedule(true);
|
|
20218
|
+
};
|
|
20203
20219
|
const handleLoadedAsset = (event)=>{
|
|
20204
20220
|
updateDiagnostic('assetTriggers', 'paginationAssetTriggers');
|
|
20205
20221
|
const target = event.target;
|
|
@@ -20219,6 +20235,8 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20219
20235
|
};
|
|
20220
20236
|
const fonts = document.fonts;
|
|
20221
20237
|
editor.on('update', handleDocumentUpdate);
|
|
20238
|
+
editorDom.addEventListener('compositionstart', handleCompositionStart);
|
|
20239
|
+
editorDom.addEventListener('compositionend', handleCompositionEnd);
|
|
20222
20240
|
editorDom.addEventListener('load', handleLoadedAsset, true);
|
|
20223
20241
|
fonts?.addEventListener('loadingdone', handleFontLoading);
|
|
20224
20242
|
window.addEventListener('resize', handleWindowResize);
|
|
@@ -20227,6 +20245,8 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20227
20245
|
disposed = true;
|
|
20228
20246
|
stopObservingBlocks();
|
|
20229
20247
|
editor.off('update', handleDocumentUpdate);
|
|
20248
|
+
editorDom.removeEventListener('compositionstart', handleCompositionStart);
|
|
20249
|
+
editorDom.removeEventListener('compositionend', handleCompositionEnd);
|
|
20230
20250
|
editorDom.removeEventListener('load', handleLoadedAsset, true);
|
|
20231
20251
|
fonts?.removeEventListener('loadingdone', handleFontLoading);
|
|
20232
20252
|
window.removeEventListener('resize', handleWindowResize);
|
|
@@ -20234,6 +20254,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20234
20254
|
};
|
|
20235
20255
|
}, [
|
|
20236
20256
|
client,
|
|
20257
|
+
compositionRevision,
|
|
20237
20258
|
documentRevision,
|
|
20238
20259
|
editor,
|
|
20239
20260
|
editorMounted,
|
|
@@ -20243,6 +20264,7 @@ function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, p
|
|
|
20243
20264
|
layoutFonts,
|
|
20244
20265
|
loadedLayoutFontKey,
|
|
20245
20266
|
loadedLayoutFontIds,
|
|
20267
|
+
isComposing,
|
|
20246
20268
|
pageKey
|
|
20247
20269
|
]);
|
|
20248
20270
|
const resolveFieldContext = useMemo(()=>pagination ? createDocumentFieldPaginationContextResolver(pagination) : null, [
|
|
@@ -20929,12 +20951,24 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
|
|
|
20929
20951
|
},
|
|
20930
20952
|
handleDOMEvents: {
|
|
20931
20953
|
compositionstart: ()=>{
|
|
20932
|
-
composition.start();
|
|
20954
|
+
composition.start(editorRef.current);
|
|
20955
|
+
return false;
|
|
20956
|
+
},
|
|
20957
|
+
compositionupdate: ()=>{
|
|
20958
|
+
composition.start(editorRef.current);
|
|
20933
20959
|
return false;
|
|
20934
20960
|
},
|
|
20935
|
-
|
|
20961
|
+
beforeinput: (_view, event)=>{
|
|
20962
|
+
composition.noteInput(event);
|
|
20963
|
+
return false;
|
|
20964
|
+
},
|
|
20965
|
+
input: (_view, event)=>{
|
|
20966
|
+
composition.noteInput(event);
|
|
20967
|
+
return false;
|
|
20968
|
+
},
|
|
20969
|
+
compositionend: (_view, event)=>{
|
|
20936
20970
|
const current = editorRef.current;
|
|
20937
|
-
if (current) composition.end(current, (settled)=>settleCompositionRef.current(settled));
|
|
20971
|
+
if (current) composition.end(current, event.data ?? null, (settled, snapshot)=>settleCompositionRef.current(settled, snapshot));
|
|
20938
20972
|
return false;
|
|
20939
20973
|
}
|
|
20940
20974
|
}
|
|
@@ -21010,7 +21044,20 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
|
|
|
21010
21044
|
publishDocumentUpdate,
|
|
21011
21045
|
queueLazyDocumentPublication
|
|
21012
21046
|
]);
|
|
21013
|
-
settleCompositionRef.current = (current)=>{
|
|
21047
|
+
settleCompositionRef.current = (current, snapshot)=>{
|
|
21048
|
+
if (null !== snapshot.text && snapshot.range && !current.isDestroyed) {
|
|
21049
|
+
const maximum = current.state.doc.content.size;
|
|
21050
|
+
const from = Math.max(0, Math.min(maximum, snapshot.range.from));
|
|
21051
|
+
const to = Math.max(0, Math.min(maximum, snapshot.range.to));
|
|
21052
|
+
const start = Math.min(from, to);
|
|
21053
|
+
const end = Math.max(from, to);
|
|
21054
|
+
const currentText = current.state.doc.textBetween(start, end, '\n', '\n');
|
|
21055
|
+
if (currentText !== snapshot.text) {
|
|
21056
|
+
if (trackChangesRef.current) current.commands.replaceDocumentTextWithTrackedChange(start, end, snapshot.text);
|
|
21057
|
+
else current.view.dispatch(current.state.tr.insertText(snapshot.text, start, end).setMeta('composition', snapshot.id).setMeta('uiEvent', 'input'));
|
|
21058
|
+
current.view.dom.dataset.documentCompositionNormalized = 'true';
|
|
21059
|
+
}
|
|
21060
|
+
}
|
|
21014
21061
|
const pending = pendingLazyPublicationRef.current;
|
|
21015
21062
|
if (pending) {
|
|
21016
21063
|
pending.cancel();
|
|
@@ -21065,6 +21112,8 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
|
|
|
21065
21112
|
composition.destroy();
|
|
21066
21113
|
},
|
|
21067
21114
|
onTransaction: ({ appendedTransactions, editor: current, transaction })=>{
|
|
21115
|
+
composition.recordTransaction(transaction);
|
|
21116
|
+
for (const appended of appendedTransactions ?? [])composition.recordTransaction(appended);
|
|
21068
21117
|
if (documentTransactionsOnlyHydrateChunks([
|
|
21069
21118
|
transaction,
|
|
21070
21119
|
...appendedTransactions
|
|
@@ -21429,10 +21478,16 @@ function DocumentEditorSurface({ artifactId, autoFocus = true, collaboration, co
|
|
|
21429
21478
|
JSON.stringify(layout.pageMargins),
|
|
21430
21479
|
layout.pageSize
|
|
21431
21480
|
]);
|
|
21481
|
+
const documentCompositionIsActive = useCallback(()=>composition.isBlocking(editor), [
|
|
21482
|
+
composition,
|
|
21483
|
+
editor
|
|
21484
|
+
]);
|
|
21432
21485
|
const pagination = useDocumentPagination({
|
|
21486
|
+
compositionRevision,
|
|
21433
21487
|
editor,
|
|
21434
21488
|
documentRevision: editorInput.revision,
|
|
21435
21489
|
enabled: Boolean(editor && 'page' === viewMode),
|
|
21490
|
+
isComposing: documentCompositionIsActive,
|
|
21436
21491
|
layoutKey: [
|
|
21437
21492
|
layout.breakAfter,
|
|
21438
21493
|
layout.columns.count,
|
|
@@ -4128,6 +4128,131 @@ function directListItemContentRoot(item) {
|
|
|
4128
4128
|
for (const child of item.childNodes)if (!(child instanceof HTMLElement) || 'ol' !== child.tagName.toLowerCase() && 'ul' !== child.tagName.toLowerCase()) root.append(child.cloneNode(true));
|
|
4129
4129
|
return root;
|
|
4130
4130
|
}
|
|
4131
|
+
const work_docx_move_revision_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
4132
|
+
const work_docx_move_revision_export_STORY_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
|
|
4133
|
+
const MAX_MOVE_REVISION_PATCHES = 65536;
|
|
4134
|
+
const MAX_MOVE_TEXT_LENGTH = 1000000;
|
|
4135
|
+
class DocxMoveRevisionPatchCollector {
|
|
4136
|
+
patches = [];
|
|
4137
|
+
registrations = new WeakMap();
|
|
4138
|
+
nextWireId = -1;
|
|
4139
|
+
register(element, id, revisionDate = work_docx_move_revision_export_normalizedRevisionDate(element.dataset.changeDate)) {
|
|
4140
|
+
if (!element.hasAttribute('data-document-change') || 'move' !== element.dataset.changeKind) return null;
|
|
4141
|
+
const existing = this.registrations.get(element);
|
|
4142
|
+
if (existing) return existing;
|
|
4143
|
+
const role = moveRole(element.dataset.changeMoveRole);
|
|
4144
|
+
const author = element.dataset.changeAuthor?.trim() ?? '';
|
|
4145
|
+
const date = revisionDate;
|
|
4146
|
+
const key = element.dataset.changeId?.trim() ?? '';
|
|
4147
|
+
const text = element.textContent ?? '';
|
|
4148
|
+
if (!role || !key || key.length > 255 || /[\u0000-\u001f\u007f]/.test(key) || !Number.isSafeInteger(id) || id < 0 || !author || author.length > 255 || /[\u0000-\u001f\u007f]/.test(author) || !text || text.length > MAX_MOVE_TEXT_LENGTH || element.querySelector('[data-document-change]') || element.querySelector('[data-document-equation], [data-document-field], [data-document-note-reference], [data-document-content-control], img, br')) throw new Error('Document contains an invalid move revision.');
|
|
4149
|
+
if (this.patches.length >= MAX_MOVE_REVISION_PATCHES) throw new Error('Document exceeds the move revision limit.');
|
|
4150
|
+
const wireId = this.nextWireId;
|
|
4151
|
+
this.nextWireId -= 1;
|
|
4152
|
+
const registration = {
|
|
4153
|
+
kind: 'from' === role ? 'move-from' : 'move-to',
|
|
4154
|
+
wireId,
|
|
4155
|
+
id,
|
|
4156
|
+
author,
|
|
4157
|
+
date
|
|
4158
|
+
};
|
|
4159
|
+
this.registrations.set(element, registration);
|
|
4160
|
+
this.patches.push({
|
|
4161
|
+
wireId,
|
|
4162
|
+
id,
|
|
4163
|
+
role,
|
|
4164
|
+
author,
|
|
4165
|
+
date
|
|
4166
|
+
});
|
|
4167
|
+
return registration;
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
async function patchDocxMoveRevisions(buffer, patches) {
|
|
4171
|
+
if (!patches.length) return buffer;
|
|
4172
|
+
if (patches.length > MAX_MOVE_REVISION_PATCHES) throw new Error('Document exceeds the move revision limit.');
|
|
4173
|
+
const archive = await jszip.loadAsync(buffer);
|
|
4174
|
+
const byWireId = new Map();
|
|
4175
|
+
const logicalSides = new Map();
|
|
4176
|
+
for (const patch of patches){
|
|
4177
|
+
if (byWireId.has(patch.wireId)) throw new Error('Generated DOCX move revision wire IDs are duplicated.');
|
|
4178
|
+
byWireId.set(patch.wireId, patch);
|
|
4179
|
+
const logicalKey = `${patch.id}\u0000${patch.author}\u0000${patch.date}`;
|
|
4180
|
+
const sides = logicalSides.get(logicalKey) ?? {
|
|
4181
|
+
from: [],
|
|
4182
|
+
to: []
|
|
4183
|
+
};
|
|
4184
|
+
sides[patch.role].push(patch);
|
|
4185
|
+
logicalSides.set(logicalKey, sides);
|
|
4186
|
+
}
|
|
4187
|
+
for (const sides of logicalSides.values())if (!sides.from.length || !sides.to.length) throw new Error('A DOCX move revision must contain both moveFrom and moveTo sides.');
|
|
4188
|
+
const applied = new Map();
|
|
4189
|
+
const emittedText = new Map();
|
|
4190
|
+
for (const entry of Object.values(archive.files)){
|
|
4191
|
+
if (entry.dir || !work_docx_move_revision_export_STORY_PATTERN.test(entry.name)) continue;
|
|
4192
|
+
const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
|
|
4193
|
+
let changed = false;
|
|
4194
|
+
const wrappers = [
|
|
4195
|
+
...descendants(document, 'ins'),
|
|
4196
|
+
...descendants(document, 'del')
|
|
4197
|
+
];
|
|
4198
|
+
for (const wrapper of wrappers){
|
|
4199
|
+
if (!wrapper.parentNode) continue;
|
|
4200
|
+
if (!DOCX_WORDPROCESSING_NAMESPACES.has(wrapper.namespaceURI ?? '')) continue;
|
|
4201
|
+
const wireId = numericWordAttribute(wrapper, 'id');
|
|
4202
|
+
if (null === wireId) continue;
|
|
4203
|
+
const patch = byWireId.get(wireId);
|
|
4204
|
+
if (!patch) continue;
|
|
4205
|
+
const expectedName = 'from' === patch.role ? 'del' : 'ins';
|
|
4206
|
+
if (wrapper.localName !== expectedName) throw new Error(`Generated DOCX move revision ${patch.wireId} has the wrong wrapper kind.`);
|
|
4207
|
+
const replacement = document.createElementNS(wrapper.namespaceURI, `${xmlNamespacePrefix(wrapper, wrapper.namespaceURI) ?? 'w'}:${'from' === patch.role ? 'moveFrom' : 'moveTo'}`);
|
|
4208
|
+
for (const child of Array.from(wrapper.attributes)){
|
|
4209
|
+
const namespace = xmlAttributeNamespace(wrapper, child);
|
|
4210
|
+
if (namespace) replacement.setAttributeNS(namespace, child.name, child.value);
|
|
4211
|
+
else replacement.setAttribute(child.name, child.value);
|
|
4212
|
+
}
|
|
4213
|
+
work_docx_move_revision_export_setWordAttribute(replacement, 'id', String(patch.id));
|
|
4214
|
+
work_docx_move_revision_export_setWordAttribute(replacement, 'author', patch.author);
|
|
4215
|
+
work_docx_move_revision_export_setWordAttribute(replacement, 'date', patch.date);
|
|
4216
|
+
const text = Array.from(wrapper.querySelectorAll('*')).filter((element)=>'from' === patch.role ? 'delText' === element.localName : 't' === element.localName).map((element)=>element.textContent ?? '').join('');
|
|
4217
|
+
emittedText.set(patch.wireId, `${emittedText.get(patch.wireId) ?? ''}${text}`);
|
|
4218
|
+
while(wrapper.firstChild)replacement.append(wrapper.firstChild);
|
|
4219
|
+
wrapper.replaceWith(replacement);
|
|
4220
|
+
applied.set(patch.wireId, (applied.get(patch.wireId) ?? 0) + 1);
|
|
4221
|
+
changed = true;
|
|
4222
|
+
}
|
|
4223
|
+
if (changed) archive.file(entry.name, serializeUtf8Xml(document));
|
|
4224
|
+
}
|
|
4225
|
+
const missing = patches.filter((patch)=>!applied.has(patch.wireId));
|
|
4226
|
+
if (missing.length) throw new Error(`DOCX move revision markers were not emitted: ${missing.map((patch)=>patch.wireId).join(', ')}.`);
|
|
4227
|
+
for (const sides of logicalSides.values()){
|
|
4228
|
+
const from = sides.from.map((patch)=>emittedText.get(patch.wireId) ?? '').join('');
|
|
4229
|
+
const to = sides.to.map((patch)=>emittedText.get(patch.wireId) ?? '').join('');
|
|
4230
|
+
if (!from || !to || from !== to) throw new Error('A DOCX move revision source and destination must contain the same text.');
|
|
4231
|
+
}
|
|
4232
|
+
return archive.generateAsync({
|
|
4233
|
+
type: 'arraybuffer'
|
|
4234
|
+
});
|
|
4235
|
+
}
|
|
4236
|
+
function moveRole(value) {
|
|
4237
|
+
return 'from' === value || 'to' === value ? value : null;
|
|
4238
|
+
}
|
|
4239
|
+
function numericWordAttribute(element, localName) {
|
|
4240
|
+
const namespace = element.namespaceURI;
|
|
4241
|
+
if (!namespace) return null;
|
|
4242
|
+
const attributes = Array.from(element.attributes).filter((candidate)=>xmlAttributeLocalName(candidate) === localName && xmlAttributeNamespace(element, candidate) === namespace);
|
|
4243
|
+
if (1 !== attributes.length) return null;
|
|
4244
|
+
const value = Number(attributes[0]?.value);
|
|
4245
|
+
return Number.isSafeInteger(value) ? value : null;
|
|
4246
|
+
}
|
|
4247
|
+
function work_docx_move_revision_export_setWordAttribute(element, localName, value) {
|
|
4248
|
+
const namespace = element.namespaceURI ?? work_docx_move_revision_export_WORD_NAMESPACE;
|
|
4249
|
+
const prefix = xmlNamespacePrefix(element, namespace) ?? element.prefix ?? 'w';
|
|
4250
|
+
element.setAttributeNS(namespace, `${prefix}:${localName}`, value);
|
|
4251
|
+
}
|
|
4252
|
+
function work_docx_move_revision_export_normalizedRevisionDate(value) {
|
|
4253
|
+
const time = Date.parse(value ?? '');
|
|
4254
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString();
|
|
4255
|
+
}
|
|
4131
4256
|
const DRAWINGML_NAMESPACES = new Set([
|
|
4132
4257
|
'http://schemas.openxmlformats.org/drawingml/2006/main',
|
|
4133
4258
|
'http://purl.oclc.org/ooxml/drawingml/main'
|
|
@@ -8526,6 +8651,8 @@ async function createDocxBlob(content, sourcePackage) {
|
|
|
8526
8651
|
usedOpenTypeMarkers: new Set(),
|
|
8527
8652
|
paragraphFormattingChangePatches: new DocxParagraphFormattingChangePatchCollector(),
|
|
8528
8653
|
numberingChangePatches: new DocxNumberingChangePatchCollector(),
|
|
8654
|
+
moveRevisionPatches: new DocxMoveRevisionPatchCollector(),
|
|
8655
|
+
moveRevisionDefaultDate: new Date().toISOString(),
|
|
8529
8656
|
tableOfContentsPatches: new DocxTableOfContentsPatchCollector(),
|
|
8530
8657
|
indexPatches: new DocxIndexPatchCollector(),
|
|
8531
8658
|
hasExplicitZeroCharacterSpacing: false,
|
|
@@ -8606,7 +8733,8 @@ async function createDocxBlob(content, sourcePackage) {
|
|
|
8606
8733
|
const indexPatched = await patchDocxIndexes(tableOfContentsPatched, noteContext.indexPatches.patches);
|
|
8607
8734
|
const paragraphFormattingChangesPatched = await patchDocxParagraphFormattingChanges(indexPatched, noteContext.paragraphFormattingChangePatches.patches);
|
|
8608
8735
|
const numberingChangesPatched = await patchDocxNumberingChanges(paragraphFormattingChangesPatched, noteContext.numberingChangePatches.patches);
|
|
8609
|
-
const
|
|
8736
|
+
const moveRevisionsPatched = await patchDocxMoveRevisions(numberingChangesPatched, noteContext.moveRevisionPatches.patches);
|
|
8737
|
+
const equationPatched = await patchDocxEquations(moveRevisionsPatched, noteContext.equationPatches.patches);
|
|
8610
8738
|
const patched = await patchDocxPageColor(equationPatched, normalizedContent.pageColor);
|
|
8611
8739
|
const contentControlsPatched = await patchDocxContentControls(patched, noteContext.contentControlPatches.patches);
|
|
8612
8740
|
const preserved = sourcePackage ? await preserveDocxSourcePackage(contentControlsPatched, sourcePackage, {
|
|
@@ -8987,19 +9115,19 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
|
|
|
8987
9115
|
markDocxRunStyleMarkersUsed(inherited.style, noteContext);
|
|
8988
9116
|
markDocxRunBorderUsed(inherited.border, noteContext);
|
|
8989
9117
|
markDocxRunShadingUsed(inherited.shading, noteContext);
|
|
8990
|
-
if (revision?.kind === 'insertion') return [
|
|
9118
|
+
if (revision?.kind === 'insertion' || revision?.kind === 'move-to') return [
|
|
8991
9119
|
new docx.InsertedTextRun({
|
|
8992
9120
|
...inherited,
|
|
8993
|
-
id: revision.id,
|
|
9121
|
+
id: revision.wireId ?? revision.id,
|
|
8994
9122
|
author: revision.author,
|
|
8995
9123
|
date: revision.date,
|
|
8996
9124
|
text: node.textContent
|
|
8997
9125
|
})
|
|
8998
9126
|
];
|
|
8999
|
-
if (revision?.kind === 'deletion') return [
|
|
9127
|
+
if (revision?.kind === 'deletion' || revision?.kind === 'move-from') return [
|
|
9000
9128
|
new docx.DeletedTextRun({
|
|
9001
9129
|
...inherited,
|
|
9002
|
-
id: revision.id,
|
|
9130
|
+
id: revision.wireId ?? revision.id,
|
|
9003
9131
|
author: revision.author,
|
|
9004
9132
|
date: revision.date,
|
|
9005
9133
|
text: node.textContent
|
|
@@ -9050,7 +9178,7 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
|
|
|
9050
9178
|
for (const child of node.childNodes)children.push(...await visit(child, inherited, revision));
|
|
9051
9179
|
return docxContentControlRuns(node, docx, noteContext.contentControlPatches, children);
|
|
9052
9180
|
}
|
|
9053
|
-
const textRevisionKind = 'del' === tag ? 'deletion' : 'ins' === tag ? 'insertion' : null;
|
|
9181
|
+
const textRevisionKind = 'del' === tag ? 'move' === node.dataset.changeKind ? 'move-from' : 'deletion' : 'ins' === tag ? 'move' === node.dataset.changeKind ? 'move-to' : 'insertion' : null;
|
|
9054
9182
|
const change = node.hasAttribute('data-document-change') && textRevisionKind ? docxTextRevision(node, textRevisionKind, noteContext) : revision;
|
|
9055
9183
|
const formattingChange = node.hasAttribute('data-document-change') && 'formatting' === node.dataset.changeKind ? noteContext.formattingChangePatches.register(node, docxRevisionId(node, noteContext)) : null;
|
|
9056
9184
|
const commentBoundary = node.hasAttribute('data-document-comment') ? nextDocxCommentBoundary(node.dataset.commentId, noteContext) : null;
|
|
@@ -9273,12 +9401,18 @@ function docxTextRevision(element, kind, context) {
|
|
|
9273
9401
|
const id = docxRevisionId(element, context);
|
|
9274
9402
|
const sourceDate = element.dataset.changeDate?.trim() ?? '';
|
|
9275
9403
|
const time = Date.parse(sourceDate);
|
|
9276
|
-
|
|
9404
|
+
const revision = {
|
|
9277
9405
|
kind,
|
|
9278
9406
|
id,
|
|
9279
9407
|
author: element.dataset.changeAuthor?.trim() || 'A3S Work',
|
|
9280
|
-
date: Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString()
|
|
9408
|
+
date: Number.isFinite(time) ? new Date(time).toISOString() : 'move-from' === kind || 'move-to' === kind ? context.moveRevisionDefaultDate : new Date().toISOString()
|
|
9281
9409
|
};
|
|
9410
|
+
if ('move-from' === kind || 'move-to' === kind) {
|
|
9411
|
+
const registration = context.moveRevisionPatches.register(element, id, revision.date);
|
|
9412
|
+
if (!registration) throw new Error('Document contains an invalid move revision.');
|
|
9413
|
+
revision.wireId = registration.wireId;
|
|
9414
|
+
}
|
|
9415
|
+
return revision;
|
|
9282
9416
|
}
|
|
9283
9417
|
function docxRevisionId(element, context, suffix) {
|
|
9284
9418
|
const baseKey = element.dataset.changeId?.trim() || `change-${context.nextChangeId}`;
|
|
@@ -9293,7 +9427,7 @@ function docxRevisionId(element, context, suffix) {
|
|
|
9293
9427
|
}
|
|
9294
9428
|
function documentHasTrackedChanges(html) {
|
|
9295
9429
|
const document = new DOMParser().parseFromString(html, 'text/html');
|
|
9296
|
-
return Boolean(document.body.querySelector('ins[data-document-change], del[data-document-change], span[data-document-change][data-change-kind="formatting"], [data-document-change][data-change-kind="paragraph-formatting"], [data-document-change][data-change-kind="numbering"]'));
|
|
9430
|
+
return Boolean(document.body.querySelector('ins[data-document-change], del[data-document-change], span[data-document-change][data-change-kind="formatting"], [data-document-change][data-change-kind="paragraph-formatting"], [data-document-change][data-change-kind="numbering"], [data-document-change][data-change-kind="move"]'));
|
|
9297
9431
|
}
|
|
9298
9432
|
function anchoredDocumentComments(content) {
|
|
9299
9433
|
const document = new DOMParser().parseFromString(content.html, 'text/html');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import jszip from "jszip";
|
|
2
2
|
import { documentRunBorderDomAttributes, contentTypeForPart, bytesToDataUrl, OoxmlPackage, normalizeDocumentIndexesHtml, firstDescendant, normalizeDocumentParagraphId, applyDocumentParagraphIdentityToElement, descendants, documentHighlightDomAttributes, attribute as work_ooxml_package_attribute, documentHighlightFromDocxValue, serializeDocxThemeReference, normalizeDocumentTableOfContentsHtml, directChildren, documentParagraphShadingDomAttributes, documentProofingDomAttributes, documentParagraphBordersDomAttributes, directChild, documentScriptFontSegments, normalizeDocumentParagraphIdentity, documentRunShadingDomAttributes } from "./6968.js";
|
|
3
3
|
import { normalizeDocumentBookmarkName, renderDocumentTableCellMarginOverrides, applyDocumentImageIdentityToElement, documentCharacterPositionDomAttributes, createDocumentImageIdentityRegistry, normalizeDocumentColumns, documentStrikeDomAttributes, normalizeDocumentImageWrapContour, DOCUMENT_TABLE_ROW_ID_ATTRIBUTE, normalizeDocumentBookmarksHtml, applyDocumentTableRowIdentityToElement, normalizeDocumentCaptionsHtml, documentCharacterSpacingDomAttributes, normalizeDocumentImageAlignment, applyDocumentImageLayerToElement, normalizeDocumentNotesHtml, normalizeDocumentBookmarkReferencesHtml, documentCitationTagsFromInstruction, applyDocumentImageWrapContourToElement, documentUnderlineDomAttributes, documentHiddenTextDomAttributes, normalizeDocumentPageChrome, normalizeDocumentImageLayoutOptions, applyDocumentImageCropToElement, documentPageMarginsForLayout, applyDocumentPageGeometry, createDocumentNoteElement, normalizeDocumentImageLayer, DOCUMENT_TABLE_ROW_TEXT_ID_ATTRIBUTE, documentBookmarkReferenceInstruction, applyDocumentImageTransformToElement, documentInitialSectionLayout, normalizeDocumentFieldsHtml, documentCaptionLabel, documentTableBordersFromElement, isContourImageLayout, normalizeDocumentCitationsHtml, normalizeDocumentImageIdentity, normalizeDocumentImageWrapSide, supportedDocxDocumentFieldInstruction, documentKerningDomAttributes, docxDocumentFieldKind, renderDocumentTableBorders, docxBookmarkReferenceTarget, normalizeDocumentImagePosition, wrapsBesideImage, documentPageChromeLegacyFields, documentCharacterScaleDomAttributes, DEFAULT_DOCUMENT_TABLE_GEOMETRY, documentSectionDomAttributes, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, documentPageMarginBody, documentLegacyTextEffectsDomAttributes, normalizeDocumentTableRowIdentity, normalizeDocumentBookmarkNativeId, documentOpenTypeDomAttributes, uniqueDocumentImageIdentity, applyDocumentTableGeometryToElement, normalizeDocumentImageCrop, documentContentLayoutProperties, documentEmphasisMarkDomAttributes, docxDocumentFieldTarget } from "./4174.js";
|
|
4
|
-
import { hasImportedDocxIndexMarkers, markDocxRunFormattingIntoState, applyImportedDocxParagraphSpacingMarkers, hasImportedDocxParagraphDirectionMarkers, hasImportedDocxParagraphIndentMarkers, resolveDocxParagraphBordersForParagraph, applyImportedDocxTextBoxMarkers, applyImportedDocxNumberingChangeMarkers, applyImportedDocxParagraphShadingMarkers, markDocxParagraphSpacing, docxCaptionBookmark, docxFieldResultText, applyImportedDocxContentControlMarkers, createDocxParagraphStyleResolver, markDocxTextBoxes, docxTableCellStyleLayers,
|
|
4
|
+
import { hasImportedDocxIndexMarkers, markDocxRunFormattingIntoState, applyImportedDocxParagraphSpacingMarkers, hasImportedDocxParagraphDirectionMarkers, hasImportedDocxParagraphIndentMarkers, resolveDocxParagraphBordersForParagraph, applyImportedDocxTextBoxMarkers, applyImportedDocxNumberingChangeMarkers, applyImportedDocxParagraphShadingMarkers, applyImportedDocxChangeMarkers, markDocxParagraphSpacing, docxCaptionBookmark, docxFieldResultText, applyImportedDocxContentControlMarkers, createDocxParagraphStyleResolver, markDocxTextBoxes, docxTableCellStyleLayers, markDocxTextChanges, markDocxContentControls, createImportedDocxRunFormattingMarkerState, parseDocxPaperSource, resolveDocxLegacyTextEffects, markDocxTablesOfContents, applyImportedDocxIndexMarkers, docxTablePropertySources, applyImportedDocxTableOfContentsMarkers, markDocxIndexes, parseDirectDocxParagraphShading, markDocxPackageEquations, hasImportedDocxParagraphSpacingMarkers, markDocxParagraphIndents, parseDocxPageMargins, resolveDocxRunBorder, parseDocxPageGeometry, docxCaptionReferenceTarget, applyImportedDocxEquationMarkers, hasImportedDocxParagraphPaginationMarkers, createDocxTableStyleResolver, hasImportedDocxParagraphShadingMarkers, isDocxEquationLikeRoot, importedDocxStrike, docxTableRunPropertySources, hasImportedDocxNumberingChangeMarkers, inspectDocxPageMarginSettings, hasImportedDocxRunFormattingMarkers, hasImportedDocxTableOfContentsMarkers, applyImportedDocxParagraphFormattingChangeMarkers, hasImportedDocxContentControlMarkers, hasImportedDocxParagraphTabStopMarkers, resolveDocxRunShading, docxCaptionSequenceKind, applyImportedDocxRunFormattingMarkers, markDocxParagraphAlignments, markDocxParagraphDirections, hasImportedDocxParagraphBorderMarkers, hasImportedDocxParagraphFormattingChangeMarkers, resolveDocxHiddenText, docxEquationHtml, docxFieldOccurrences, hasImportedDocxParagraphAlignmentMarkers, markDocxParagraphTabStops, applyImportedDocxParagraphIndentMarkers, applyImportedDocxParagraphDirectionMarkers, applyImportedDocxParagraphAlignmentMarkers, resolveDocxTableStyleResolver, hasImportedDocxTextBoxMarkers, importedDocxUnderline, markDocxParagraphBorders, markDocxNumberingChanges, markDocxParagraphPagination, hasImportedDocxChangeMarkers, docxFieldOccurrenceIsInlineEditable, applyImportedDocxParagraphBorderMarkers, applyImportedDocxParagraphPaginationMarkers, applyImportedDocxParagraphTabStopMarkers, docxRunPropertySources, markDocxParagraphFormattingChanges, markDocxParagraphShading, parseDocxPageBorders } from "./0~5600.js";
|
|
5
5
|
import { readDocxBibliography, resolveDocxProofing, XMLNS_NAMESPACE, docxCharacterScalePercentFromProperties, docxCharacterSpacingTwipsFromProperties, parseBoundedDocxInteger, resolveDocxOpenTypeFeatures, xmlAttributeLocalName, resolveDocxEmphasisMark, createDocxThemeResolver, docxCharacterPositionHalfPointsFromProperties, readDocxImageTransform, docxThemeColor, parseDocxParagraphDefaultCollapsed, resolveDocxKerningThresholdHalfPoints, resolveDocxThemeResolver, DOCX_WORDPROCESSING_NAMESPACES, xmlAttributeNamespace } from "./0~3534.js";
|
|
6
6
|
import { importDocxPageColor, importedDocumentNoteId } from "./0~4980.js";
|
|
7
7
|
const WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
@@ -428,121 +428,6 @@ function work_docx_caption_import_textNodes(root) {
|
|
|
428
428
|
while(walker.nextNode())nodes.push(walker.currentNode);
|
|
429
429
|
return nodes;
|
|
430
430
|
}
|
|
431
|
-
const work_docx_change_import_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
432
|
-
const work_docx_change_import_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
|
|
433
|
-
function markDocxTextChanges(document) {
|
|
434
|
-
const changes = [];
|
|
435
|
-
const revisions = [
|
|
436
|
-
...descendants(document, 'ins'),
|
|
437
|
-
...descendants(document, 'del')
|
|
438
|
-
].sort(compareDocumentOrder);
|
|
439
|
-
for (const revision of revisions){
|
|
440
|
-
if (!revision.parentNode || closestRevision(revision.parentElement)) continue;
|
|
441
|
-
const kind = 'del' === revision.localName ? 'deletion' : 'insertion';
|
|
442
|
-
if (!revisionText(revision, kind)) continue;
|
|
443
|
-
if ('deletion' === kind) convertDeletedText(document, revision);
|
|
444
|
-
const index = changes.length + 1;
|
|
445
|
-
const sourceId = work_ooxml_package_attribute(revision, 'id')?.trim() ?? '';
|
|
446
|
-
const id = uniqueChangeId(sourceId ? `docx-change-${sourceId}` : `docx-change-${index}`, changes);
|
|
447
|
-
const marker = {
|
|
448
|
-
id,
|
|
449
|
-
kind,
|
|
450
|
-
author: work_ooxml_package_attribute(revision, 'author')?.trim() || '未知审阅者',
|
|
451
|
-
date: normalizeDate(work_ooxml_package_attribute(revision, 'date')),
|
|
452
|
-
start: `__A3S_WORK_CHANGE_START_${index}__`,
|
|
453
|
-
end: `__A3S_WORK_CHANGE_END_${index}__`
|
|
454
|
-
};
|
|
455
|
-
unwrapRevision(document, revision, marker.start, marker.end);
|
|
456
|
-
changes.push(marker);
|
|
457
|
-
}
|
|
458
|
-
return {
|
|
459
|
-
changes
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
function applyImportedDocxChangeMarkers(document, markers) {
|
|
463
|
-
for (const marker of markers.changes){
|
|
464
|
-
const element = document.createElement('deletion' === marker.kind ? 'del' : 'ins');
|
|
465
|
-
element.dataset.documentChange = 'true';
|
|
466
|
-
element.dataset.changeKind = marker.kind;
|
|
467
|
-
element.dataset.changeId = marker.id;
|
|
468
|
-
element.dataset.changeAuthor = marker.author;
|
|
469
|
-
element.dataset.changeDate = marker.date;
|
|
470
|
-
wrapMarkerRange(document.body, marker.start, marker.end, element);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
function hasImportedDocxChangeMarkers(markers) {
|
|
474
|
-
return markers.changes.length > 0;
|
|
475
|
-
}
|
|
476
|
-
function revisionText(revision, kind) {
|
|
477
|
-
const names = 'deletion' === kind ? [
|
|
478
|
-
'delText',
|
|
479
|
-
't'
|
|
480
|
-
] : [
|
|
481
|
-
't'
|
|
482
|
-
];
|
|
483
|
-
return names.flatMap((name)=>descendants(revision, name)).map((element)=>element.textContent ?? '').join('');
|
|
484
|
-
}
|
|
485
|
-
function convertDeletedText(document, revision) {
|
|
486
|
-
for (const deleted of descendants(revision, 'delText')){
|
|
487
|
-
const text = document.createElementNS(work_docx_change_import_WORD_NAMESPACE, 'w:t');
|
|
488
|
-
const preserve = deleted.getAttributeNS(work_docx_change_import_XML_NAMESPACE, 'space') ?? deleted.getAttribute('xml:space');
|
|
489
|
-
if (preserve) text.setAttributeNS(work_docx_change_import_XML_NAMESPACE, 'xml:space', preserve);
|
|
490
|
-
text.textContent = deleted.textContent;
|
|
491
|
-
deleted.replaceWith(text);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
function unwrapRevision(document, revision, start, end) {
|
|
495
|
-
const parent = revision.parentNode;
|
|
496
|
-
if (!parent) return;
|
|
497
|
-
parent.insertBefore(work_docx_change_import_markerRun(document, start), revision);
|
|
498
|
-
while(revision.firstChild)parent.insertBefore(revision.firstChild, revision);
|
|
499
|
-
parent.insertBefore(work_docx_change_import_markerRun(document, end), revision);
|
|
500
|
-
revision.remove();
|
|
501
|
-
}
|
|
502
|
-
function work_docx_change_import_markerRun(document, value) {
|
|
503
|
-
const run = document.createElementNS(work_docx_change_import_WORD_NAMESPACE, 'w:r');
|
|
504
|
-
const text = document.createElementNS(work_docx_change_import_WORD_NAMESPACE, 'w:t');
|
|
505
|
-
text.setAttributeNS(work_docx_change_import_XML_NAMESPACE, 'xml:space', 'preserve');
|
|
506
|
-
text.textContent = value;
|
|
507
|
-
run.append(text);
|
|
508
|
-
return run;
|
|
509
|
-
}
|
|
510
|
-
function wrapMarkerRange(root, start, end, wrapper) {
|
|
511
|
-
const html = root.innerHTML;
|
|
512
|
-
const startIndex = html.indexOf(start);
|
|
513
|
-
const endIndex = html.indexOf(end, startIndex + start.length);
|
|
514
|
-
if (startIndex < 0 || endIndex < 0) return false;
|
|
515
|
-
const closingTag = `</${wrapper.localName}>`;
|
|
516
|
-
const serializedWrapper = wrapper.outerHTML;
|
|
517
|
-
if (!serializedWrapper.endsWith(closingTag)) return false;
|
|
518
|
-
const openingTag = serializedWrapper.slice(0, -closingTag.length);
|
|
519
|
-
root.innerHTML = `${html.slice(0, startIndex)}${openingTag}${html.slice(startIndex + start.length, endIndex)}${closingTag}${html.slice(endIndex + end.length)}`;
|
|
520
|
-
return true;
|
|
521
|
-
}
|
|
522
|
-
function closestRevision(element) {
|
|
523
|
-
let current = element;
|
|
524
|
-
while(current){
|
|
525
|
-
if ('ins' === current.localName || 'del' === current.localName) return current;
|
|
526
|
-
current = current.parentElement;
|
|
527
|
-
}
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
function uniqueChangeId(base, changes) {
|
|
531
|
-
const ids = new Set(changes.map((change)=>change.id));
|
|
532
|
-
if (!ids.has(base)) return base;
|
|
533
|
-
let suffix = 2;
|
|
534
|
-
while(ids.has(`${base}-${suffix}`))suffix += 1;
|
|
535
|
-
return `${base}-${suffix}`;
|
|
536
|
-
}
|
|
537
|
-
function normalizeDate(value) {
|
|
538
|
-
if (!value) return '';
|
|
539
|
-
const time = Date.parse(value);
|
|
540
|
-
return Number.isFinite(time) ? new Date(time).toISOString() : '';
|
|
541
|
-
}
|
|
542
|
-
function compareDocumentOrder(left, right) {
|
|
543
|
-
if (left === right) return 0;
|
|
544
|
-
return left.compareDocumentPosition(right) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
545
|
-
}
|
|
546
431
|
const work_docx_citation_import_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
547
432
|
const work_docx_citation_import_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
|
|
548
433
|
function markDocxCitationFields(document) {
|
|
@@ -744,7 +629,7 @@ async function markDocxComments(document, archive) {
|
|
|
744
629
|
const workIds = new Map();
|
|
745
630
|
const ranges = [];
|
|
746
631
|
const comments = [];
|
|
747
|
-
const starts = descendants(document, 'commentRangeStart').sort(
|
|
632
|
+
const starts = descendants(document, 'commentRangeStart').sort(compareDocumentOrder);
|
|
748
633
|
for (const start of starts){
|
|
749
634
|
const sourceId = work_ooxml_package_attribute(start, 'id')?.trim() ?? '';
|
|
750
635
|
if (!sourceId) continue;
|
|
@@ -794,7 +679,7 @@ async function readCommentDefinitions(archive) {
|
|
|
794
679
|
return {
|
|
795
680
|
sourceId: work_ooxml_package_attribute(comment, 'id')?.trim() ?? '',
|
|
796
681
|
author: work_ooxml_package_attribute(comment, 'author')?.trim() || '未知审阅者',
|
|
797
|
-
date:
|
|
682
|
+
date: normalizeDate(work_ooxml_package_attribute(comment, 'date')),
|
|
798
683
|
text: commentText(comment),
|
|
799
684
|
paraId,
|
|
800
685
|
parentParaId: metadata?.parentParaId ?? '',
|
|
@@ -922,12 +807,12 @@ function uniqueWorkCommentId(base, comments) {
|
|
|
922
807
|
while(ids.has(`${base}-${suffix}`))suffix += 1;
|
|
923
808
|
return `${base}-${suffix}`;
|
|
924
809
|
}
|
|
925
|
-
function
|
|
810
|
+
function normalizeDate(value) {
|
|
926
811
|
if (!value) return '';
|
|
927
812
|
const time = Date.parse(value);
|
|
928
813
|
return Number.isFinite(time) ? new Date(time).toISOString() : '';
|
|
929
814
|
}
|
|
930
|
-
function
|
|
815
|
+
function compareDocumentOrder(left, right) {
|
|
931
816
|
if (left === right) return 0;
|
|
932
817
|
return left.compareDocumentPosition(right) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
933
818
|
}
|