@ansonlai/docx-redline-js 0.8.1 → 0.8.2
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 +14 -0
- package/dist/docx-redline-js.esm.js +127 -6
- package/dist/docx-redline-js.esm.js.map +3 -3
- package/dist/docx-redline-js.esm.min.js +74 -74
- package/dist/docx-redline-js.esm.min.js.map +3 -3
- package/dist/docx-redline.bundle.cjs +127 -6
- package/dist/docx-redline.bundle.cjs.map +3 -3
- package/dist/docx-redline.bundle.js +127 -6
- package/dist/docx-redline.bundle.js.map +3 -3
- package/engine/reconstruction-mode.js +183 -54
- package/package.json +1 -1
- package/pipeline/diff-engine.js +274 -258
- package/scripts/run-tests.mjs +14 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ansonlai/docx-redline-js standalone CommonJS bundle v0.8.
|
|
1
|
+
// @ansonlai/docx-redline-js standalone CommonJS bundle v0.8.2 — 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;
|
|
@@ -9139,15 +9139,29 @@ function createDiffEngine(options = {}) {
|
|
|
9139
9139
|
engine.Diff_Timeout = timeout;
|
|
9140
9140
|
return engine;
|
|
9141
9141
|
}
|
|
9142
|
-
function tokenize(text2) {
|
|
9142
|
+
function tokenize(text2, atomicChars = null) {
|
|
9143
9143
|
const tokens = [];
|
|
9144
|
+
const pushWord = (word) => {
|
|
9145
|
+
if (!atomicChars || atomicChars.size === 0) {
|
|
9146
|
+
tokens.push(word);
|
|
9147
|
+
return;
|
|
9148
|
+
}
|
|
9149
|
+
let start = 0;
|
|
9150
|
+
for (let index = 0; index < word.length; index++) {
|
|
9151
|
+
if (!atomicChars.has(word[index])) continue;
|
|
9152
|
+
if (index > start) tokens.push(word.slice(start, index));
|
|
9153
|
+
tokens.push(word[index]);
|
|
9154
|
+
start = index + 1;
|
|
9155
|
+
}
|
|
9156
|
+
if (start < word.length) tokens.push(word.slice(start));
|
|
9157
|
+
};
|
|
9144
9158
|
const leading = text2.match(/^\s+/);
|
|
9145
9159
|
if (leading) tokens.push(leading[0]);
|
|
9146
9160
|
const regex = /(\S+)(\s*)/g;
|
|
9147
9161
|
regex.lastIndex = leading?.[0].length || 0;
|
|
9148
9162
|
let match;
|
|
9149
9163
|
while ((match = regex.exec(text2)) !== null) {
|
|
9150
|
-
if (match[1])
|
|
9164
|
+
if (match[1]) pushWord(match[1]);
|
|
9151
9165
|
if (match[2]) tokens.push(match[2]);
|
|
9152
9166
|
}
|
|
9153
9167
|
return tokens;
|
|
@@ -9175,8 +9189,8 @@ function wordsToChars(text1, text2, options = {}) {
|
|
|
9175
9189
|
}
|
|
9176
9190
|
return { chars, tokenIds };
|
|
9177
9191
|
}
|
|
9178
|
-
const tokens1 = tokenize(text1);
|
|
9179
|
-
const tokens2 = tokenize(text2);
|
|
9192
|
+
const tokens1 = tokenize(text1, options.atomicChars);
|
|
9193
|
+
const tokens2 = tokenize(text2, options.atomicChars);
|
|
9180
9194
|
const encoded1 = mapTokensToChars(tokens1);
|
|
9181
9195
|
const encoded2 = mapTokensToChars(tokens2);
|
|
9182
9196
|
return {
|
|
@@ -14581,7 +14595,11 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
|
|
|
14581
14595
|
if (mapping.paragraphs.length === 0) {
|
|
14582
14596
|
return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: false });
|
|
14583
14597
|
}
|
|
14584
|
-
const
|
|
14598
|
+
const wordDiffs = computeWordDiffs(mapping.originalFullText, mapping.processedModifiedText, {
|
|
14599
|
+
...diffOptions,
|
|
14600
|
+
atomicChars: new Set(mapping.referenceMap.keys())
|
|
14601
|
+
});
|
|
14602
|
+
const diffs = keepUnchangedTextOutsideWrappers(anchorSharedReferences(wordDiffs, mapping.referenceMap), mapping);
|
|
14585
14603
|
return withOoxmlSourceType(applyReconstructionDiffs(
|
|
14586
14604
|
xmlDoc,
|
|
14587
14605
|
diffs,
|
|
@@ -14593,6 +14611,109 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
|
|
|
14593
14611
|
options
|
|
14594
14612
|
));
|
|
14595
14613
|
}
|
|
14614
|
+
function forEachChangeGroup(diffs, onEqual, onGroup) {
|
|
14615
|
+
let index = 0;
|
|
14616
|
+
while (index < diffs.length) {
|
|
14617
|
+
if (diffs[index][0] === 0) {
|
|
14618
|
+
onEqual(diffs[index]);
|
|
14619
|
+
index++;
|
|
14620
|
+
continue;
|
|
14621
|
+
}
|
|
14622
|
+
const group = [];
|
|
14623
|
+
while (index < diffs.length && diffs[index][0] !== 0) group.push(diffs[index++]);
|
|
14624
|
+
onGroup(
|
|
14625
|
+
group,
|
|
14626
|
+
group.filter(([op]) => op === -1).map(([, text2]) => text2).join(""),
|
|
14627
|
+
group.filter(([op]) => op === 1).map(([, text2]) => text2).join("")
|
|
14628
|
+
);
|
|
14629
|
+
}
|
|
14630
|
+
}
|
|
14631
|
+
function pushChange(result, deleted, inserted) {
|
|
14632
|
+
if (deleted === inserted) {
|
|
14633
|
+
if (deleted) result.push([0, deleted]);
|
|
14634
|
+
return;
|
|
14635
|
+
}
|
|
14636
|
+
if (deleted) result.push([-1, deleted]);
|
|
14637
|
+
if (inserted) result.push([1, inserted]);
|
|
14638
|
+
}
|
|
14639
|
+
function anchorSharedReferences(diffs, referenceMap) {
|
|
14640
|
+
if (referenceMap.size === 0) return diffs;
|
|
14641
|
+
const result = [];
|
|
14642
|
+
forEachChangeGroup(diffs, (part) => result.push(part), (group, deleted, inserted) => {
|
|
14643
|
+
const deletedRefs = [...deleted].filter((char) => referenceMap.has(char) && inserted.includes(char));
|
|
14644
|
+
const insertedRefs = [...inserted].filter((char) => deletedRefs.includes(char));
|
|
14645
|
+
if (deletedRefs.length === 0 || deletedRefs.join("") !== insertedRefs.join("")) {
|
|
14646
|
+
result.push(...group);
|
|
14647
|
+
return;
|
|
14648
|
+
}
|
|
14649
|
+
let deletedStart = 0;
|
|
14650
|
+
let insertedStart = 0;
|
|
14651
|
+
for (const ref of deletedRefs) {
|
|
14652
|
+
const deletedAt = deleted.indexOf(ref, deletedStart);
|
|
14653
|
+
const insertedAt = inserted.indexOf(ref, insertedStart);
|
|
14654
|
+
pushChange(result, deleted.slice(deletedStart, deletedAt), inserted.slice(insertedStart, insertedAt));
|
|
14655
|
+
result.push([0, ref]);
|
|
14656
|
+
deletedStart = deletedAt + 1;
|
|
14657
|
+
insertedStart = insertedAt + 1;
|
|
14658
|
+
}
|
|
14659
|
+
pushChange(result, deleted.slice(deletedStart), inserted.slice(insertedStart));
|
|
14660
|
+
});
|
|
14661
|
+
return result;
|
|
14662
|
+
}
|
|
14663
|
+
function commonPrefixLength(left, right) {
|
|
14664
|
+
const limit = Math.min(left.length, right.length);
|
|
14665
|
+
let length = 0;
|
|
14666
|
+
while (length < limit && left[length] === right[length]) length++;
|
|
14667
|
+
return length;
|
|
14668
|
+
}
|
|
14669
|
+
function commonSuffixLength(left, right, limit) {
|
|
14670
|
+
let length = 0;
|
|
14671
|
+
while (length < limit && left[left.length - 1 - length] === right[right.length - 1 - length]) length++;
|
|
14672
|
+
return length;
|
|
14673
|
+
}
|
|
14674
|
+
function keepUnchangedTextOutsideWrappers(diffs, mapping) {
|
|
14675
|
+
const wrapperAt = (index) => mapping.getRunProperties(index).wrapper || null;
|
|
14676
|
+
const result = [];
|
|
14677
|
+
let originalOffset = 0;
|
|
14678
|
+
forEachChangeGroup(diffs, (part) => {
|
|
14679
|
+
result.push(part);
|
|
14680
|
+
originalOffset += part[1].length;
|
|
14681
|
+
}, (group, deleted, inserted) => {
|
|
14682
|
+
const start = originalOffset;
|
|
14683
|
+
const end = start + deleted.length;
|
|
14684
|
+
originalOffset = end;
|
|
14685
|
+
const isWrapperBoundary = (index) => index > start && index < end && wrapperAt(index - 1) !== wrapperAt(index);
|
|
14686
|
+
let prefix = 0;
|
|
14687
|
+
let suffix = 0;
|
|
14688
|
+
if (deleted && inserted) {
|
|
14689
|
+
const sharedSuffix = commonSuffixLength(deleted, inserted, Math.min(deleted.length, inserted.length));
|
|
14690
|
+
for (let length = sharedSuffix; length > 0; length--) {
|
|
14691
|
+
if (isWrapperBoundary(end - length)) {
|
|
14692
|
+
suffix = length;
|
|
14693
|
+
break;
|
|
14694
|
+
}
|
|
14695
|
+
}
|
|
14696
|
+
const sharedPrefix = Math.min(
|
|
14697
|
+
commonPrefixLength(deleted, inserted),
|
|
14698
|
+
Math.min(deleted.length, inserted.length) - suffix
|
|
14699
|
+
);
|
|
14700
|
+
for (let length = sharedPrefix; length > 0; length--) {
|
|
14701
|
+
if (isWrapperBoundary(start + length)) {
|
|
14702
|
+
prefix = length;
|
|
14703
|
+
break;
|
|
14704
|
+
}
|
|
14705
|
+
}
|
|
14706
|
+
}
|
|
14707
|
+
if (prefix === 0 && suffix === 0) {
|
|
14708
|
+
result.push(...group);
|
|
14709
|
+
return;
|
|
14710
|
+
}
|
|
14711
|
+
if (prefix > 0) result.push([0, deleted.slice(0, prefix)]);
|
|
14712
|
+
pushChange(result, deleted.slice(prefix, deleted.length - suffix), inserted.slice(prefix, inserted.length - suffix));
|
|
14713
|
+
if (suffix > 0) result.push([0, deleted.slice(deleted.length - suffix)]);
|
|
14714
|
+
});
|
|
14715
|
+
return result;
|
|
14716
|
+
}
|
|
14596
14717
|
|
|
14597
14718
|
// engine/table-mode.js
|
|
14598
14719
|
function noChanges(serializer, xmlDoc) {
|