@ansonlai/docx-redline-js 0.1.0 → 0.1.4
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/AGENTS.md +38 -1
- package/ARCHITECTURE.md +11 -9
- package/README.md +52 -5
- package/dist/docx-redline-js.esm.js +304 -1
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +65 -65
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/index.js +11 -6
- package/package.json +6 -6
- package/services/revision-comment-management.js +387 -0
- package/services/standalone-operation-runner.js +11 -11
- package/standalone.js +0 -5
package/index.js
CHANGED
|
@@ -234,12 +234,17 @@ export { preprocessMarkdown } from './pipeline/markdown-processor.js';
|
|
|
234
234
|
export { serializeToOoxml, wrapInDocumentFragment } from './pipeline/serialization.js';
|
|
235
235
|
|
|
236
236
|
// Comment engine
|
|
237
|
-
export {
|
|
238
|
-
injectCommentsIntoOoxml,
|
|
239
|
-
injectCommentsIntoPackage,
|
|
240
|
-
buildCommentElement,
|
|
241
|
-
buildCommentsPartXml
|
|
242
|
-
} from './services/comment-engine.js';
|
|
237
|
+
export {
|
|
238
|
+
injectCommentsIntoOoxml,
|
|
239
|
+
injectCommentsIntoPackage,
|
|
240
|
+
buildCommentElement,
|
|
241
|
+
buildCommentsPartXml
|
|
242
|
+
} from './services/comment-engine.js';
|
|
243
|
+
export {
|
|
244
|
+
acceptTrackedChangesInOoxml,
|
|
245
|
+
rejectTrackedChangesInOoxml,
|
|
246
|
+
deleteCommentsByAuthorInOoxml
|
|
247
|
+
} from './services/revision-comment-management.js';
|
|
243
248
|
|
|
244
249
|
// Formatting removal utilities
|
|
245
250
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ansonlai/docx-redline-js",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Host-independent OOXML reconciliation engine for .docx manipulation with track changes",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"import": "./index.js",
|
|
12
12
|
"default": "./index.js"
|
|
13
13
|
},
|
|
14
|
-
"./standalone": "./standalone.js",
|
|
14
|
+
"./standalone-runner": "./services/standalone-operation-runner.js",
|
|
15
|
+
"./redline-operation-converter": "./orchestration/redline-operation-converter.js",
|
|
15
16
|
"./adapters/*": "./adapters/*",
|
|
16
17
|
"./core/*": "./core/*",
|
|
17
18
|
"./engine/*": "./engine/*",
|
|
@@ -27,7 +28,6 @@
|
|
|
27
28
|
"services/",
|
|
28
29
|
"orchestration/",
|
|
29
30
|
"index.js",
|
|
30
|
-
"standalone.js",
|
|
31
31
|
"dist/",
|
|
32
32
|
"ARCHITECTURE.md",
|
|
33
33
|
"AGENTS.md",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "node scripts/build.mjs",
|
|
54
54
|
"test": "node scripts/run-tests.mjs",
|
|
55
|
-
"test:isolation": "node tests/
|
|
55
|
+
"test:isolation": "node tests/no_word_api_index_check.mjs && node tests/core_dependency_graph_check.mjs",
|
|
56
56
|
"prepublishOnly": "npm run test:isolation && npm run build"
|
|
57
57
|
},
|
|
58
58
|
"keywords": [
|
|
@@ -68,9 +68,9 @@
|
|
|
68
68
|
],
|
|
69
69
|
"repository": {
|
|
70
70
|
"type": "git",
|
|
71
|
-
"url": "https://github.com/
|
|
71
|
+
"url": "https://github.com/AnsonLai/docx-redline-js.git"
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
74
74
|
"node": ">=18.0.0"
|
|
75
75
|
}
|
|
76
|
-
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Revision/comment management utilities for OOXML payloads.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { NS_W } from '../core/types.js';
|
|
6
|
+
import { createParser, createSerializer } from '../adapters/xml-adapter.js';
|
|
7
|
+
import { getXmlParseError } from '../core/xml-query.js';
|
|
8
|
+
|
|
9
|
+
function getAttributeByLocalName(node, localName) {
|
|
10
|
+
if (!node || !node.attributes) return '';
|
|
11
|
+
for (const attr of Array.from(node.attributes)) {
|
|
12
|
+
if ((attr.localName || '').toLowerCase() === localName.toLowerCase()) {
|
|
13
|
+
return String(attr.value || '');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return String(
|
|
17
|
+
node.getAttribute?.(`w:${localName}`)
|
|
18
|
+
|| node.getAttribute?.(localName)
|
|
19
|
+
|| ''
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizeAuthor(author) {
|
|
24
|
+
return typeof author === 'string' ? author.trim().toLowerCase() : '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isElement(node) {
|
|
28
|
+
return !!node && node.nodeType === 1;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isWordElement(node, localName) {
|
|
32
|
+
return isElement(node)
|
|
33
|
+
&& node.namespaceURI === NS_W
|
|
34
|
+
&& String(node.localName || '').toLowerCase() === localName.toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getWordElementsByLocalName(xmlDoc, localName) {
|
|
38
|
+
return Array.from(xmlDoc.getElementsByTagNameNS(NS_W, localName));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveAuthorFilter(options = {}) {
|
|
42
|
+
if (options?.allAuthors === true) {
|
|
43
|
+
return { valid: true, allAuthors: true, normalizedAuthor: '' };
|
|
44
|
+
}
|
|
45
|
+
const normalizedAuthor = normalizeAuthor(options?.author);
|
|
46
|
+
if (!normalizedAuthor) {
|
|
47
|
+
return {
|
|
48
|
+
valid: false,
|
|
49
|
+
allAuthors: false,
|
|
50
|
+
normalizedAuthor: '',
|
|
51
|
+
warning: 'No author provided. Pass { author } or set { allAuthors: true }.'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return { valid: true, allAuthors: false, normalizedAuthor };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function authorMatchesNode(node, filter) {
|
|
58
|
+
if (filter.allAuthors) return true;
|
|
59
|
+
const nodeAuthor = normalizeAuthor(getAttributeByLocalName(node, 'author'));
|
|
60
|
+
return !!nodeAuthor && nodeAuthor === filter.normalizedAuthor;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function parseXmlWithWarnings(oxml, parseFailurePrefix) {
|
|
64
|
+
const parser = createParser();
|
|
65
|
+
const xmlDoc = parser.parseFromString(oxml, 'application/xml');
|
|
66
|
+
const parseError = getXmlParseError(xmlDoc);
|
|
67
|
+
if (parseError) {
|
|
68
|
+
return {
|
|
69
|
+
xmlDoc: null,
|
|
70
|
+
serializer: null,
|
|
71
|
+
warning: `${parseFailurePrefix}: ${parseError.textContent || 'parse error'}`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return { xmlDoc, serializer: createSerializer(), warning: null };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function removeNode(node) {
|
|
78
|
+
if (node?.parentNode) {
|
|
79
|
+
node.parentNode.removeChild(node);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function unwrapNode(node) {
|
|
86
|
+
const parent = node?.parentNode;
|
|
87
|
+
if (!parent) return false;
|
|
88
|
+
while (node.firstChild) {
|
|
89
|
+
parent.insertBefore(node.firstChild, node);
|
|
90
|
+
}
|
|
91
|
+
parent.removeChild(node);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isTableRowRevisionMarker(node) {
|
|
96
|
+
const parent = node?.parentNode;
|
|
97
|
+
return isWordElement(parent, 'trPr') && isWordElement(parent?.parentNode, 'tr');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Accepts tracked changes (`w:ins`, `w:del`, and *PrChange tags) for one author
|
|
102
|
+
* or all authors in the provided OOXML payload.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} oxml
|
|
105
|
+
* @param {{ author?: string, allAuthors?: boolean }} [options]
|
|
106
|
+
* @returns {{ oxml: string, hasChanges: boolean, acceptedCount: number, warnings: string[] }}
|
|
107
|
+
*/
|
|
108
|
+
export function acceptTrackedChangesInOoxml(oxml, options = {}) {
|
|
109
|
+
const warnings = [];
|
|
110
|
+
const filter = resolveAuthorFilter(options);
|
|
111
|
+
if (!filter.valid) {
|
|
112
|
+
return { oxml, hasChanges: false, acceptedCount: 0, warnings: [filter.warning] };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const parseResult = parseXmlWithWarnings(oxml, 'Failed to parse OOXML');
|
|
116
|
+
if (!parseResult.xmlDoc) {
|
|
117
|
+
return { oxml, hasChanges: false, acceptedCount: 0, warnings: [parseResult.warning] };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const { xmlDoc, serializer } = parseResult;
|
|
121
|
+
let acceptedCount = 0;
|
|
122
|
+
|
|
123
|
+
for (const insNode of getWordElementsByLocalName(xmlDoc, 'ins')) {
|
|
124
|
+
if (!insNode.parentNode || !authorMatchesNode(insNode, filter)) continue;
|
|
125
|
+
if (isTableRowRevisionMarker(insNode)) {
|
|
126
|
+
if (removeNode(insNode)) acceptedCount += 1;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (unwrapNode(insNode)) acceptedCount += 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const delNode of getWordElementsByLocalName(xmlDoc, 'del')) {
|
|
133
|
+
if (!delNode.parentNode || !authorMatchesNode(delNode, filter)) continue;
|
|
134
|
+
if (isTableRowRevisionMarker(delNode)) {
|
|
135
|
+
const rowNode = delNode.parentNode?.parentNode;
|
|
136
|
+
if (removeNode(rowNode)) acceptedCount += 1;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (removeNode(delNode)) acceptedCount += 1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const changeTags = ['rPrChange', 'pPrChange', 'tblPrChange', 'trPrChange', 'tcPrChange'];
|
|
143
|
+
for (const localName of changeTags) {
|
|
144
|
+
for (const changeNode of getWordElementsByLocalName(xmlDoc, localName)) {
|
|
145
|
+
if (!changeNode.parentNode || !authorMatchesNode(changeNode, filter)) continue;
|
|
146
|
+
if (removeNode(changeNode)) acceptedCount += 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
oxml: serializer.serializeToString(xmlDoc),
|
|
152
|
+
hasChanges: acceptedCount > 0,
|
|
153
|
+
acceptedCount,
|
|
154
|
+
warnings
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function convertDeletionTextNodes(xmlDoc, delNode) {
|
|
159
|
+
for (const delTextNode of Array.from(delNode.getElementsByTagNameNS(NS_W, 'delText'))) {
|
|
160
|
+
const normalText = xmlDoc.createElementNS(NS_W, 'w:t');
|
|
161
|
+
const spaceValue = delTextNode.getAttribute('xml:space');
|
|
162
|
+
if (spaceValue) {
|
|
163
|
+
normalText.setAttribute('xml:space', spaceValue);
|
|
164
|
+
}
|
|
165
|
+
while (delTextNode.firstChild) {
|
|
166
|
+
normalText.appendChild(delTextNode.firstChild);
|
|
167
|
+
}
|
|
168
|
+
delTextNode.parentNode?.replaceChild(normalText, delTextNode);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function rejectPropertyChangeNode(changeNode, localName) {
|
|
173
|
+
const parent = changeNode?.parentNode;
|
|
174
|
+
if (!parent) return false;
|
|
175
|
+
|
|
176
|
+
const baseLocalName = localName.endsWith('Change')
|
|
177
|
+
? localName.slice(0, -'Change'.length)
|
|
178
|
+
: '';
|
|
179
|
+
|
|
180
|
+
if (
|
|
181
|
+
!baseLocalName
|
|
182
|
+
|| String(parent.localName || '').toLowerCase() !== baseLocalName.toLowerCase()
|
|
183
|
+
|| parent.namespaceURI !== NS_W
|
|
184
|
+
) {
|
|
185
|
+
return removeNode(changeNode);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const historicalNode = Array.from(changeNode.childNodes || []).find(
|
|
189
|
+
child => child.nodeType === 1 && child.namespaceURI === NS_W
|
|
190
|
+
&& String(child.localName || '').toLowerCase() === baseLocalName.toLowerCase()
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
if (!historicalNode) {
|
|
194
|
+
return removeNode(changeNode);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const toAppend = Array.from(historicalNode.childNodes || []);
|
|
198
|
+
while (parent.firstChild) {
|
|
199
|
+
parent.removeChild(parent.firstChild);
|
|
200
|
+
}
|
|
201
|
+
for (const node of toAppend) {
|
|
202
|
+
const clone = xmlDocImportNode(parent.ownerDocument, node);
|
|
203
|
+
parent.appendChild(clone);
|
|
204
|
+
}
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function xmlDocImportNode(xmlDoc, node) {
|
|
209
|
+
if (xmlDoc && typeof xmlDoc.importNode === 'function') {
|
|
210
|
+
return xmlDoc.importNode(node, true);
|
|
211
|
+
}
|
|
212
|
+
return node.cloneNode(true);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Rejects tracked changes (`w:ins`, `w:del`, and *PrChange tags) for one author
|
|
217
|
+
* or all authors in the provided OOXML payload.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} oxml
|
|
220
|
+
* @param {{ author?: string, allAuthors?: boolean }} [options]
|
|
221
|
+
* @returns {{ oxml: string, hasChanges: boolean, rejectedCount: number, warnings: string[] }}
|
|
222
|
+
*/
|
|
223
|
+
export function rejectTrackedChangesInOoxml(oxml, options = {}) {
|
|
224
|
+
const warnings = [];
|
|
225
|
+
const filter = resolveAuthorFilter(options);
|
|
226
|
+
if (!filter.valid) {
|
|
227
|
+
return { oxml, hasChanges: false, rejectedCount: 0, warnings: [filter.warning] };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const parseResult = parseXmlWithWarnings(oxml, 'Failed to parse OOXML');
|
|
231
|
+
if (!parseResult.xmlDoc) {
|
|
232
|
+
return { oxml, hasChanges: false, rejectedCount: 0, warnings: [parseResult.warning] };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const { xmlDoc, serializer } = parseResult;
|
|
236
|
+
let rejectedCount = 0;
|
|
237
|
+
|
|
238
|
+
for (const insNode of getWordElementsByLocalName(xmlDoc, 'ins')) {
|
|
239
|
+
if (!insNode.parentNode || !authorMatchesNode(insNode, filter)) continue;
|
|
240
|
+
if (isTableRowRevisionMarker(insNode)) {
|
|
241
|
+
const rowNode = insNode.parentNode?.parentNode;
|
|
242
|
+
if (removeNode(rowNode)) rejectedCount += 1;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (removeNode(insNode)) rejectedCount += 1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
for (const delNode of getWordElementsByLocalName(xmlDoc, 'del')) {
|
|
249
|
+
if (!delNode.parentNode || !authorMatchesNode(delNode, filter)) continue;
|
|
250
|
+
if (isTableRowRevisionMarker(delNode)) {
|
|
251
|
+
if (removeNode(delNode)) rejectedCount += 1;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
convertDeletionTextNodes(xmlDoc, delNode);
|
|
255
|
+
if (unwrapNode(delNode)) rejectedCount += 1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const changeTags = ['rPrChange', 'pPrChange', 'tblPrChange', 'trPrChange', 'tcPrChange'];
|
|
259
|
+
for (const localName of changeTags) {
|
|
260
|
+
for (const changeNode of getWordElementsByLocalName(xmlDoc, localName)) {
|
|
261
|
+
if (!changeNode.parentNode || !authorMatchesNode(changeNode, filter)) continue;
|
|
262
|
+
if (rejectPropertyChangeNode(changeNode, localName)) rejectedCount += 1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
oxml: serializer.serializeToString(xmlDoc),
|
|
268
|
+
hasChanges: rejectedCount > 0,
|
|
269
|
+
rejectedCount,
|
|
270
|
+
warnings
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function collectCommentTargetIds(xmlDoc, filter) {
|
|
275
|
+
const targetIds = new Set();
|
|
276
|
+
const commentNodes = getWordElementsByLocalName(xmlDoc, 'comment');
|
|
277
|
+
|
|
278
|
+
for (const commentNode of commentNodes) {
|
|
279
|
+
if (!authorMatchesNode(commentNode, filter)) continue;
|
|
280
|
+
const id = getAttributeByLocalName(commentNode, 'id');
|
|
281
|
+
if (id) targetIds.add(id);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return { targetIds, commentNodes };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function removeCommentNodesById(commentNodes, targetIds) {
|
|
288
|
+
let removed = 0;
|
|
289
|
+
for (const commentNode of commentNodes) {
|
|
290
|
+
const id = getAttributeByLocalName(commentNode, 'id');
|
|
291
|
+
if (!id || !targetIds.has(id)) continue;
|
|
292
|
+
if (removeNode(commentNode)) removed += 1;
|
|
293
|
+
}
|
|
294
|
+
return removed;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function runIsOnlyCommentReference(runNode) {
|
|
298
|
+
if (!isWordElement(runNode, 'r')) return false;
|
|
299
|
+
const meaningfulChildren = Array.from(runNode.childNodes || []).filter(child => {
|
|
300
|
+
if (child.nodeType === 3) return String(child.nodeValue || '').trim().length > 0;
|
|
301
|
+
if (child.nodeType !== 1) return false;
|
|
302
|
+
if (child.namespaceURI !== NS_W) return true;
|
|
303
|
+
const local = String(child.localName || '').toLowerCase();
|
|
304
|
+
return local !== 'rpr' && local !== 'commentreference';
|
|
305
|
+
});
|
|
306
|
+
return meaningfulChildren.length === 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function removeCommentAnchors(xmlDoc, targetIds) {
|
|
310
|
+
let removed = 0;
|
|
311
|
+
const anchorTags = ['commentRangeStart', 'commentRangeEnd', 'commentReference'];
|
|
312
|
+
|
|
313
|
+
for (const localName of anchorTags) {
|
|
314
|
+
for (const node of getWordElementsByLocalName(xmlDoc, localName)) {
|
|
315
|
+
if (!node.parentNode) continue;
|
|
316
|
+
const id = getAttributeByLocalName(node, 'id');
|
|
317
|
+
if (!id || !targetIds.has(id)) continue;
|
|
318
|
+
|
|
319
|
+
if (localName === 'commentReference' && runIsOnlyCommentReference(node.parentNode)) {
|
|
320
|
+
if (removeNode(node.parentNode)) {
|
|
321
|
+
removed += 1;
|
|
322
|
+
}
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (removeNode(node)) {
|
|
326
|
+
removed += 1;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return removed;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Deletes comments authored by one user (or all users) and removes matching
|
|
335
|
+
* comment anchors/references from the OOXML payload.
|
|
336
|
+
*
|
|
337
|
+
* @param {string} oxml
|
|
338
|
+
* @param {{ author?: string, allAuthors?: boolean }} [options]
|
|
339
|
+
* @returns {{ oxml: string, hasChanges: boolean, commentsRemoved: number, referencesRemoved: number, warnings: string[] }}
|
|
340
|
+
*/
|
|
341
|
+
export function deleteCommentsByAuthorInOoxml(oxml, options = {}) {
|
|
342
|
+
const warnings = [];
|
|
343
|
+
const filter = resolveAuthorFilter(options);
|
|
344
|
+
if (!filter.valid) {
|
|
345
|
+
return {
|
|
346
|
+
oxml,
|
|
347
|
+
hasChanges: false,
|
|
348
|
+
commentsRemoved: 0,
|
|
349
|
+
referencesRemoved: 0,
|
|
350
|
+
warnings: [filter.warning]
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const parseResult = parseXmlWithWarnings(oxml, 'Failed to parse OOXML');
|
|
355
|
+
if (!parseResult.xmlDoc) {
|
|
356
|
+
return {
|
|
357
|
+
oxml,
|
|
358
|
+
hasChanges: false,
|
|
359
|
+
commentsRemoved: 0,
|
|
360
|
+
referencesRemoved: 0,
|
|
361
|
+
warnings: [parseResult.warning]
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const { xmlDoc, serializer } = parseResult;
|
|
366
|
+
const { targetIds, commentNodes } = collectCommentTargetIds(xmlDoc, filter);
|
|
367
|
+
|
|
368
|
+
if (filter.allAuthors) {
|
|
369
|
+
for (const localName of ['commentRangeStart', 'commentRangeEnd', 'commentReference']) {
|
|
370
|
+
for (const node of getWordElementsByLocalName(xmlDoc, localName)) {
|
|
371
|
+
const id = getAttributeByLocalName(node, 'id');
|
|
372
|
+
if (id) targetIds.add(id);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const commentsRemoved = removeCommentNodesById(commentNodes, targetIds);
|
|
378
|
+
const referencesRemoved = removeCommentAnchors(xmlDoc, targetIds);
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
oxml: serializer.serializeToString(xmlDoc),
|
|
382
|
+
hasChanges: commentsRemoved > 0 || referencesRemoved > 0,
|
|
383
|
+
commentsRemoved,
|
|
384
|
+
referencesRemoved,
|
|
385
|
+
warnings
|
|
386
|
+
};
|
|
387
|
+
}
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
resolveParagraphRangeByRefs,
|
|
38
38
|
extractReplacementNodesFromOoxml,
|
|
39
39
|
normalizeBodySectionOrderStandalone
|
|
40
|
-
} from '../
|
|
40
|
+
} from '../index.js';
|
|
41
41
|
|
|
42
42
|
const NS_W = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
43
43
|
|
|
@@ -46,8 +46,8 @@ function getParagraphText(paragraph) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function resolveTargetParagraph(xmlDoc, targetText, targetRef, opType, runtimeContext = null, options = {}) {
|
|
49
|
-
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => {};
|
|
50
|
-
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => {};
|
|
49
|
+
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => { };
|
|
50
|
+
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => { };
|
|
51
51
|
return resolveTargetParagraphWithSnapshotShared(xmlDoc, {
|
|
52
52
|
targetText,
|
|
53
53
|
targetRef,
|
|
@@ -536,7 +536,7 @@ async function tryExplicitDecimalHeaderListConversion({
|
|
|
536
536
|
author,
|
|
537
537
|
runtimeContext,
|
|
538
538
|
generateRedlines = true,
|
|
539
|
-
onInfo = () => {}
|
|
539
|
+
onInfo = () => { }
|
|
540
540
|
}) {
|
|
541
541
|
if (!targetParagraph) return null;
|
|
542
542
|
const scopedParagraphOxml = serializer.serializeToString(targetParagraph);
|
|
@@ -644,7 +644,7 @@ async function trySingleParagraphListStructuralFallback({
|
|
|
644
644
|
author,
|
|
645
645
|
runtimeContext,
|
|
646
646
|
generateRedlines = true,
|
|
647
|
-
onInfo = () => {}
|
|
647
|
+
onInfo = () => { }
|
|
648
648
|
}) {
|
|
649
649
|
if (!targetParagraph) return null;
|
|
650
650
|
|
|
@@ -767,8 +767,8 @@ async function trySingleParagraphListStructuralFallback({
|
|
|
767
767
|
|
|
768
768
|
async function applyToParagraphByExactText(documentXml, targetText, modifiedText, author, targetRef = null, targetEndRef = null, runtimeContext = null, options = {}) {
|
|
769
769
|
const generateRedlines = options.generateRedlines !== false;
|
|
770
|
-
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => {};
|
|
771
|
-
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => {};
|
|
770
|
+
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => { };
|
|
771
|
+
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => { };
|
|
772
772
|
const parser = createParser();
|
|
773
773
|
const serializer = createSerializer();
|
|
774
774
|
const xmlDoc = parser.parseFromString(documentXml, 'application/xml');
|
|
@@ -1079,8 +1079,8 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
|
|
|
1079
1079
|
|
|
1080
1080
|
async function applyHighlightToParagraphByExactText(documentXml, targetText, textToHighlight, color, author, targetRef = null, runtimeContext = null, options = {}) {
|
|
1081
1081
|
const generateRedlines = options.generateRedlines !== false;
|
|
1082
|
-
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => {};
|
|
1083
|
-
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => {};
|
|
1082
|
+
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => { };
|
|
1083
|
+
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => { };
|
|
1084
1084
|
const parser = createParser();
|
|
1085
1085
|
const serializer = createSerializer();
|
|
1086
1086
|
const xmlDoc = parser.parseFromString(documentXml, 'application/xml');
|
|
@@ -1098,8 +1098,8 @@ async function applyHighlightToParagraphByExactText(documentXml, targetText, tex
|
|
|
1098
1098
|
}
|
|
1099
1099
|
|
|
1100
1100
|
async function applyCommentToParagraphByExactText(documentXml, targetText, textToComment, commentContent, author, targetRef = null, runtimeContext = null, options = {}) {
|
|
1101
|
-
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => {};
|
|
1102
|
-
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => {};
|
|
1101
|
+
const onInfo = typeof options?.onInfo === 'function' ? options.onInfo : () => { };
|
|
1102
|
+
const onWarn = typeof options?.onWarn === 'function' ? options.onWarn : () => { };
|
|
1103
1103
|
const parser = createParser();
|
|
1104
1104
|
const serializer = createSerializer();
|
|
1105
1105
|
const xmlDoc = parser.parseFromString(documentXml, 'application/xml');
|