@ansonlai/docx-redline-js 0.8.2 → 0.8.3
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/CHANGELOG.md +586 -553
- package/README.md +901 -890
- package/core/list-targeting.js +12 -10
- package/dist/docx-redline-js.esm.js +154 -28
- package/dist/docx-redline-js.esm.js.map +3 -3
- package/dist/docx-redline-js.esm.min.js +86 -86
- package/dist/docx-redline-js.esm.min.js.map +3 -3
- package/dist/docx-redline.bundle.cjs +154 -28
- package/dist/docx-redline.bundle.cjs.map +3 -3
- package/dist/docx-redline.bundle.js +154 -28
- package/dist/docx-redline.bundle.js.map +3 -3
- package/orchestration/list-structural-fallback.js +530 -518
- package/package.json +1 -1
- package/pipeline/list-generation.js +203 -168
- package/services/document-inspection.js +10 -7
- package/services/document-operation-mutations.js +1597 -1586
- package/services/numbering-helpers.js +108 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ansonlai/docx-redline-js standalone CommonJS bundle v0.8.
|
|
1
|
+
// @ansonlai/docx-redline-js standalone CommonJS bundle v0.8.3 — https://github.com/AnsonLai/docx-redline-js
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -10590,7 +10590,37 @@ async function executeListGeneration(options) {
|
|
|
10590
10590
|
}];
|
|
10591
10591
|
}
|
|
10592
10592
|
}
|
|
10593
|
-
|
|
10593
|
+
const sourceParagraphCount = (originalRunModel || []).filter((run) => run.kind === RunKind.PARAGRAPH_START).length;
|
|
10594
|
+
if (generateRedlines && sourceParagraphCount > 0 && (sourceParagraphCount > 1 || deletionRuns.length === 0)) {
|
|
10595
|
+
let segment = null;
|
|
10596
|
+
const flushSegment = () => {
|
|
10597
|
+
if (!segment) return;
|
|
10598
|
+
results.push(serializeToOoxml(segment, null, [], {
|
|
10599
|
+
author,
|
|
10600
|
+
generateRedlines,
|
|
10601
|
+
revisionIdAllocator
|
|
10602
|
+
}));
|
|
10603
|
+
segment = null;
|
|
10604
|
+
};
|
|
10605
|
+
for (const run of originalRunModel) {
|
|
10606
|
+
if (run.kind === RunKind.PARAGRAPH_START) {
|
|
10607
|
+
flushSegment();
|
|
10608
|
+
segment = [{
|
|
10609
|
+
...run,
|
|
10610
|
+
pPrElement: null,
|
|
10611
|
+
pPrXml: addParagraphMarkRevision(
|
|
10612
|
+
run.pPrElement || run.pPrXml || null,
|
|
10613
|
+
"del",
|
|
10614
|
+
author,
|
|
10615
|
+
revisionIdAllocator
|
|
10616
|
+
)
|
|
10617
|
+
}];
|
|
10618
|
+
} else if (segment && (run.kind === "text" || run.kind === "run")) {
|
|
10619
|
+
segment.push({ ...run, kind: "deletion", author });
|
|
10620
|
+
}
|
|
10621
|
+
}
|
|
10622
|
+
flushSegment();
|
|
10623
|
+
} else if (generateRedlines && deletionRuns.length > 0) {
|
|
10594
10624
|
const deletedPPr = addParagraphMarkRevision(sourcePPr, "del", author, revisionIdAllocator);
|
|
10595
10625
|
const deletedParagraph = serializeToOoxml(deletionRuns, deletedPPr, [], {
|
|
10596
10626
|
author,
|
|
@@ -16788,12 +16818,12 @@ function validateParagraphBoundaryMutation(targetParagraph, modifiedText, option
|
|
|
16788
16818
|
}
|
|
16789
16819
|
|
|
16790
16820
|
// core/list-targeting.js
|
|
16791
|
-
function
|
|
16792
|
-
if (!node
|
|
16793
|
-
const
|
|
16794
|
-
|
|
16795
|
-
|
|
16796
|
-
return
|
|
16821
|
+
function getDirectChildByLocalName(node, localName2) {
|
|
16822
|
+
if (!node) return null;
|
|
16823
|
+
for (const child of Array.from(node.childNodes || [])) {
|
|
16824
|
+
if (child?.nodeType === 1 && child.localName === localName2) return child;
|
|
16825
|
+
}
|
|
16826
|
+
return null;
|
|
16797
16827
|
}
|
|
16798
16828
|
function readValAttribute(element) {
|
|
16799
16829
|
if (!element) return null;
|
|
@@ -16910,15 +16940,15 @@ function buildListEntriesForInsertion(parsedItems, normalizedTargetText, anchorL
|
|
|
16910
16940
|
}
|
|
16911
16941
|
function getParagraphListInfo(paragraph) {
|
|
16912
16942
|
if (!paragraph) return null;
|
|
16913
|
-
const pPr =
|
|
16943
|
+
const pPr = getDirectChildByLocalName(paragraph, "pPr");
|
|
16914
16944
|
if (!pPr) return null;
|
|
16915
|
-
const numPr =
|
|
16945
|
+
const numPr = getDirectChildByLocalName(pPr, "numPr");
|
|
16916
16946
|
if (!numPr) return null;
|
|
16917
|
-
const numIdEl =
|
|
16947
|
+
const numIdEl = getDirectChildByLocalName(numPr, "numId");
|
|
16918
16948
|
if (!numIdEl) return null;
|
|
16919
16949
|
const numId = readValAttribute(numIdEl);
|
|
16920
16950
|
if (!numId || numId === "0") return null;
|
|
16921
|
-
const ilvlEl =
|
|
16951
|
+
const ilvlEl = getDirectChildByLocalName(numPr, "ilvl");
|
|
16922
16952
|
const ilvlRaw = readValAttribute(ilvlEl);
|
|
16923
16953
|
const ilvl = Number.parseInt(ilvlRaw || "0", 10);
|
|
16924
16954
|
return {
|
|
@@ -17328,8 +17358,13 @@ function enforceListBindingOnParagraphNodes(nodes, options = {}) {
|
|
|
17328
17358
|
}
|
|
17329
17359
|
return updated;
|
|
17330
17360
|
}
|
|
17361
|
+
function parseOxmlFragment(oxml) {
|
|
17362
|
+
const text2 = String(oxml || "");
|
|
17363
|
+
const declared = /xmlns:w\s*=/.test(text2);
|
|
17364
|
+
return parseOoxmlSafe(declared ? text2 : `<w:root xmlns:w="${NS_W}">${text2}</w:root>`, "application/xml").doc;
|
|
17365
|
+
}
|
|
17331
17366
|
function getFirstParagraphFromOxml(oxml) {
|
|
17332
|
-
const doc =
|
|
17367
|
+
const doc = parseOxmlFragment(oxml);
|
|
17333
17368
|
if (!doc) return null;
|
|
17334
17369
|
const parseError = getXmlParseError(doc);
|
|
17335
17370
|
if (parseError) return null;
|
|
@@ -17356,17 +17391,17 @@ function setElementVal(element, value) {
|
|
|
17356
17391
|
element.setAttribute("w:val", String(value));
|
|
17357
17392
|
}
|
|
17358
17393
|
function extractFirstParagraphNumIdFromOxml(oxml) {
|
|
17359
|
-
const doc =
|
|
17394
|
+
const doc = parseOxmlFragment(oxml);
|
|
17360
17395
|
if (!doc) return null;
|
|
17361
17396
|
const parseError = getXmlParseError(doc);
|
|
17362
17397
|
if (parseError) return null;
|
|
17363
17398
|
const paragraphs = getDocumentParagraphNodes(doc);
|
|
17364
|
-
const
|
|
17365
|
-
|
|
17366
|
-
|
|
17367
|
-
|
|
17368
|
-
|
|
17369
|
-
|
|
17399
|
+
for (const paragraph of paragraphs) {
|
|
17400
|
+
const numIdNodes = Array.from(paragraph.getElementsByTagNameNS("*", "numId"));
|
|
17401
|
+
for (const numIdNode of numIdNodes) {
|
|
17402
|
+
const numId = getElementId(numIdNode, ["w:val", "val"]);
|
|
17403
|
+
if (numId != null) return String(numId);
|
|
17404
|
+
}
|
|
17370
17405
|
}
|
|
17371
17406
|
return null;
|
|
17372
17407
|
}
|
|
@@ -17862,6 +17897,8 @@ function createDynamicNumberingIdState(numberingXml, options = {}) {
|
|
|
17862
17897
|
const baseNumId = Math.max(minId, maxUsedNumId + 1);
|
|
17863
17898
|
const baseAbstractNumId = Math.max(minId, maxUsedAbstractNumId + 1);
|
|
17864
17899
|
return {
|
|
17900
|
+
// Kept so list edits can recognise which existing definition a source list uses and continue it.
|
|
17901
|
+
sourceNumberingXml: String(numberingXml || ""),
|
|
17865
17902
|
nextNumId: nextAvailableId(baseNumId, usedNumIds, maxPreferred),
|
|
17866
17903
|
nextAbstractNumId: nextAvailableId(baseAbstractNumId, usedAbstractNumIds, maxPreferred),
|
|
17867
17904
|
usedNumIds,
|
|
@@ -18002,6 +18039,82 @@ function buildExplicitDecimalMultilevelNumberingXml(numId, abstractNumId, startA
|
|
|
18002
18039
|
</w:num>
|
|
18003
18040
|
</w:numbering>`.trim();
|
|
18004
18041
|
}
|
|
18042
|
+
function isTrackedDeletedParagraph(node) {
|
|
18043
|
+
if (!node || node.nodeType !== 1 || node.localName !== "p") return false;
|
|
18044
|
+
const pPr = Array.from(node.childNodes || []).find((child) => isDirectWordChild(child, "pPr"));
|
|
18045
|
+
const rPr = pPr && Array.from(pPr.childNodes || []).find((child) => isDirectWordChild(child, "rPr"));
|
|
18046
|
+
return !!rPr && Array.from(rPr.childNodes || []).some((child) => isDirectWordChild(child, "del"));
|
|
18047
|
+
}
|
|
18048
|
+
function paragraphNumIdNodes(node) {
|
|
18049
|
+
return Array.from(node?.getElementsByTagNameNS?.("*", "numId") || []).filter((numIdNode) => numIdNode.parentNode?.localName === "numPr");
|
|
18050
|
+
}
|
|
18051
|
+
function levelFormats(abstractNum) {
|
|
18052
|
+
const formats = /* @__PURE__ */ new Map();
|
|
18053
|
+
for (const lvl of Array.from(abstractNum?.getElementsByTagNameNS?.("*", "lvl") || [])) {
|
|
18054
|
+
const ilvl = getElementId2(lvl, ["w:ilvl", "ilvl"]);
|
|
18055
|
+
const fmt = Array.from(lvl.getElementsByTagNameNS("*", "numFmt"))[0]?.getAttribute("w:val") || null;
|
|
18056
|
+
if (ilvl != null && fmt) formats.set(ilvl, fmt);
|
|
18057
|
+
}
|
|
18058
|
+
return formats;
|
|
18059
|
+
}
|
|
18060
|
+
function normalizeListFormat(format) {
|
|
18061
|
+
return format === "bullet" || format === "circle" || format === "square" ? "bullet" : format;
|
|
18062
|
+
}
|
|
18063
|
+
function abstractNumForNumId(numberingDoc, numId) {
|
|
18064
|
+
const num = Array.from(numberingDoc.getElementsByTagNameNS("*", "num")).find((candidate) => getElementId2(candidate, ["w:numId", "numId"]) === numId);
|
|
18065
|
+
const abstractId = num && getElementId2(Array.from(num.getElementsByTagNameNS("*", "abstractNumId"))[0], ["w:val", "val"]);
|
|
18066
|
+
if (abstractId == null) return null;
|
|
18067
|
+
return Array.from(numberingDoc.getElementsByTagNameNS("*", "abstractNum")).find((candidate) => getElementId2(candidate, ["w:abstractNumId", "abstractNumId"]) === abstractId) || null;
|
|
18068
|
+
}
|
|
18069
|
+
function reuseSourceListNumbering(numberingXml, replacementNodes, sourceParagraphs, numberingIdState) {
|
|
18070
|
+
const sourceXml = numberingIdState?.sourceNumberingXml;
|
|
18071
|
+
if (!numberingXml || !String(sourceXml || "").trim() || !Array.isArray(replacementNodes)) return null;
|
|
18072
|
+
const sourceNumIds = /* @__PURE__ */ new Set();
|
|
18073
|
+
for (const paragraph of sourceParagraphs || []) {
|
|
18074
|
+
for (const numIdNode of paragraphNumIdNodes(paragraph)) {
|
|
18075
|
+
const id = getElementId2(numIdNode, ["w:val", "val"]);
|
|
18076
|
+
if (id != null && id !== 0) sourceNumIds.add(id);
|
|
18077
|
+
}
|
|
18078
|
+
}
|
|
18079
|
+
if (sourceNumIds.size !== 1) return null;
|
|
18080
|
+
const [sourceNumId] = sourceNumIds;
|
|
18081
|
+
const sourceDoc = parseOoxml(sourceXml);
|
|
18082
|
+
const payloadDoc = parseOoxml(numberingXml);
|
|
18083
|
+
if (hasXmlParseError(sourceDoc) || hasXmlParseError(payloadDoc)) return null;
|
|
18084
|
+
const sourceAbstract = abstractNumForNumId(sourceDoc, sourceNumId);
|
|
18085
|
+
if (!sourceAbstract) return null;
|
|
18086
|
+
const sourceFormats = levelFormats(sourceAbstract);
|
|
18087
|
+
const newParagraphNumIds = [];
|
|
18088
|
+
for (const node of replacementNodes) {
|
|
18089
|
+
if (isTrackedDeletedParagraph(node)) continue;
|
|
18090
|
+
for (const numIdNode of paragraphNumIdNodes(node)) {
|
|
18091
|
+
const ilvlNode = Array.from(numIdNode.parentNode.childNodes || []).find((child) => child.localName === "ilvl");
|
|
18092
|
+
newParagraphNumIds.push({
|
|
18093
|
+
numId: getElementId2(numIdNode, ["w:val", "val"]),
|
|
18094
|
+
ilvl: ilvlNode ? getElementId2(ilvlNode, ["w:val", "val"]) ?? 0 : 0
|
|
18095
|
+
});
|
|
18096
|
+
}
|
|
18097
|
+
}
|
|
18098
|
+
if (newParagraphNumIds.length === 0) return null;
|
|
18099
|
+
const payloadFormats = /* @__PURE__ */ new Map();
|
|
18100
|
+
for (const { numId, ilvl } of newParagraphNumIds) {
|
|
18101
|
+
if (numId == null) return null;
|
|
18102
|
+
if (!payloadFormats.has(numId)) {
|
|
18103
|
+
const abstract = abstractNumForNumId(payloadDoc, numId);
|
|
18104
|
+
if (!abstract) return null;
|
|
18105
|
+
payloadFormats.set(numId, levelFormats(abstract));
|
|
18106
|
+
}
|
|
18107
|
+
const generated = payloadFormats.get(numId).get(ilvl);
|
|
18108
|
+
const existing = sourceFormats.get(ilvl);
|
|
18109
|
+
if (!generated || !existing || normalizeListFormat(generated) !== normalizeListFormat(existing)) return null;
|
|
18110
|
+
}
|
|
18111
|
+
const clonedNodes = replacementNodes.map((node) => node?.cloneNode ? node.cloneNode(true) : node);
|
|
18112
|
+
for (const node of clonedNodes) {
|
|
18113
|
+
if (isTrackedDeletedParagraph(node)) continue;
|
|
18114
|
+
for (const numIdNode of paragraphNumIdNodes(node)) setElementVal2(numIdNode, sourceNumId);
|
|
18115
|
+
}
|
|
18116
|
+
return { numId: sourceNumId, replacementNodes: clonedNodes };
|
|
18117
|
+
}
|
|
18005
18118
|
function remapNumberingPayloadForDocument(numberingXml, replacementNodes, numberingIdState) {
|
|
18006
18119
|
const numberingDoc = parseOoxml(numberingXml);
|
|
18007
18120
|
if (hasXmlParseError(numberingDoc)) {
|
|
@@ -18039,6 +18152,7 @@ function remapNumberingPayloadForDocument(numberingXml, replacementNodes, number
|
|
|
18039
18152
|
}
|
|
18040
18153
|
const clonedNodes = Array.isArray(replacementNodes) ? replacementNodes.map((node) => node?.cloneNode ? node.cloneNode(true) : node) : [];
|
|
18041
18154
|
for (const node of clonedNodes) {
|
|
18155
|
+
if (isTrackedDeletedParagraph(node)) continue;
|
|
18042
18156
|
const numIdNodes = Array.from(node?.getElementsByTagNameNS?.("*", "numId") || []);
|
|
18043
18157
|
for (const numIdNode of numIdNodes) {
|
|
18044
18158
|
const oldNumRef = getElementId2(numIdNode, ["w:val", "val"]);
|
|
@@ -18859,6 +18973,8 @@ function areRevisionTokensEqual(a, b) {
|
|
|
18859
18973
|
var attr2 = (node, name) => node?.getAttribute?.(`w:${name}`) || node?.getAttribute?.(name) || "";
|
|
18860
18974
|
var descendants = (node, name) => Array.from(node?.getElementsByTagNameNS?.(NS_W, name) || []);
|
|
18861
18975
|
var first = (node, name) => descendants(node, name)[0] || null;
|
|
18976
|
+
var directChild2 = (node, name) => Array.from(node?.childNodes || []).find((child) => child?.nodeType === 1 && child.localName === name && (!child.namespaceURI || child.namespaceURI === NS_W)) || null;
|
|
18977
|
+
var currentPPr = (paragraph) => directChild2(paragraph, "pPr");
|
|
18862
18978
|
function hasAncestor(node, localName2) {
|
|
18863
18979
|
let cursor = node?.parentNode;
|
|
18864
18980
|
while (cursor) {
|
|
@@ -18874,11 +18990,11 @@ function parseXml2(xml, partName, required = false) {
|
|
|
18874
18990
|
return { doc: parsed.doc, warnings: parsed.warnings || [] };
|
|
18875
18991
|
}
|
|
18876
18992
|
function paragraphListProperties(paragraph) {
|
|
18877
|
-
const numPr =
|
|
18993
|
+
const numPr = directChild2(currentPPr(paragraph), "numPr");
|
|
18878
18994
|
if (!numPr) return null;
|
|
18879
|
-
const numId = attr2(
|
|
18995
|
+
const numId = attr2(directChild2(numPr, "numId"), "val");
|
|
18880
18996
|
if (!numId || numId === "0") return null;
|
|
18881
|
-
return { numId, level: Number.parseInt(attr2(
|
|
18997
|
+
return { numId, level: Number.parseInt(attr2(directChild2(numPr, "ilvl"), "val") || "0", 10) || 0 };
|
|
18882
18998
|
}
|
|
18883
18999
|
function parseNumbering(numberingDoc) {
|
|
18884
19000
|
const abstracts = /* @__PURE__ */ new Map();
|
|
@@ -18963,11 +19079,11 @@ function createNumberingResolver(numberingDoc) {
|
|
|
18963
19079
|
};
|
|
18964
19080
|
}
|
|
18965
19081
|
function headingLevel(paragraph) {
|
|
18966
|
-
const pPr =
|
|
18967
|
-
const style = attr2(
|
|
19082
|
+
const pPr = currentPPr(paragraph);
|
|
19083
|
+
const style = attr2(directChild2(pPr, "pStyle"), "val");
|
|
18968
19084
|
const match = style.match(/^heading\s*([1-9])$/i);
|
|
18969
19085
|
if (match) return Math.min(Number(match[1]), 6);
|
|
18970
|
-
const outline = Number.parseInt(attr2(
|
|
19086
|
+
const outline = Number.parseInt(attr2(directChild2(pPr, "outlineLvl"), "val"), 10);
|
|
18971
19087
|
return Number.isInteger(outline) ? Math.min(outline + 1, 6) : null;
|
|
18972
19088
|
}
|
|
18973
19089
|
function structuralContext(paragraph, text2) {
|
|
@@ -19146,7 +19262,7 @@ function inspectDocumentParts(parts, options = {}) {
|
|
|
19146
19262
|
const ids = [...new Set([...descendants(paragraph, "commentRangeStart"), ...descendants(paragraph, "commentReference")].map((node) => attr2(node, "id")).filter(Boolean))];
|
|
19147
19263
|
const authors = revisionAuthors(paragraph);
|
|
19148
19264
|
const list = resolveNumbering(paragraphListProperties(paragraph));
|
|
19149
|
-
const styleId = attr2(
|
|
19265
|
+
const styleId = attr2(directChild2(currentPPr(paragraph), "pStyle"), "val") || null;
|
|
19150
19266
|
const structure = structuralContext(paragraph, text2);
|
|
19151
19267
|
const index = zeroIndex + 1;
|
|
19152
19268
|
const provision = list?.label && list.format !== "bullet" ? list.label : null;
|
|
@@ -24547,7 +24663,9 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
|
|
|
24547
24663
|
paragraphText,
|
|
24548
24664
|
generateRedlines ? createRevisionMetadata(author, xmlDoc) : null,
|
|
24549
24665
|
author,
|
|
24550
|
-
|
|
24666
|
+
// A wholly inserted paragraph must track its own paragraph mark; otherwise Word's
|
|
24667
|
+
// Reject All removes the text but leaves an empty paragraph behind.
|
|
24668
|
+
{ generateRedlines, trackParagraphMark: true }
|
|
24551
24669
|
);
|
|
24552
24670
|
const imported = xmlDoc.importNode(plainParagraph, true);
|
|
24553
24671
|
ensureParagraphIdsOnImportedNode(imported, operationSession);
|
|
@@ -24678,6 +24796,14 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
|
|
|
24678
24796
|
const extracted = extractReplacementNodes(result.oxml);
|
|
24679
24797
|
let replacementNodes = removeListPackagingSentinel(extracted.replacementNodes, result.warnings);
|
|
24680
24798
|
let numberingXml = extracted.numberingXml;
|
|
24799
|
+
if (numberingXml && runtimeContext?.numberingIdState && !useTableScope) {
|
|
24800
|
+
const sourceParagraphs = useListScope ? listScopeEdit.paragraphs : explicitRangeParagraphs || [targetParagraph];
|
|
24801
|
+
const continued = reuseSourceListNumbering(numberingXml, replacementNodes, sourceParagraphs, runtimeContext.numberingIdState);
|
|
24802
|
+
if (continued) {
|
|
24803
|
+
replacementNodes = continued.replacementNodes;
|
|
24804
|
+
numberingXml = null;
|
|
24805
|
+
}
|
|
24806
|
+
}
|
|
24681
24807
|
if (numberingXml && runtimeContext?.numberingIdState) {
|
|
24682
24808
|
const normalizedNumbering = remapNumberingPayloadForDocument(numberingXml, replacementNodes, runtimeContext.numberingIdState);
|
|
24683
24809
|
replacementNodes = normalizedNumbering.replacementNodes;
|