@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 bundle v0.8.
|
|
1
|
+
// @ansonlai/docx-redline-js standalone 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;
|
|
@@ -9001,15 +9001,29 @@ function createDiffEngine(options = {}) {
|
|
|
9001
9001
|
engine.Diff_Timeout = timeout;
|
|
9002
9002
|
return engine;
|
|
9003
9003
|
}
|
|
9004
|
-
function tokenize(text2) {
|
|
9004
|
+
function tokenize(text2, atomicChars = null) {
|
|
9005
9005
|
const tokens = [];
|
|
9006
|
+
const pushWord = (word) => {
|
|
9007
|
+
if (!atomicChars || atomicChars.size === 0) {
|
|
9008
|
+
tokens.push(word);
|
|
9009
|
+
return;
|
|
9010
|
+
}
|
|
9011
|
+
let start = 0;
|
|
9012
|
+
for (let index = 0; index < word.length; index++) {
|
|
9013
|
+
if (!atomicChars.has(word[index])) continue;
|
|
9014
|
+
if (index > start) tokens.push(word.slice(start, index));
|
|
9015
|
+
tokens.push(word[index]);
|
|
9016
|
+
start = index + 1;
|
|
9017
|
+
}
|
|
9018
|
+
if (start < word.length) tokens.push(word.slice(start));
|
|
9019
|
+
};
|
|
9006
9020
|
const leading = text2.match(/^\s+/);
|
|
9007
9021
|
if (leading) tokens.push(leading[0]);
|
|
9008
9022
|
const regex = /(\S+)(\s*)/g;
|
|
9009
9023
|
regex.lastIndex = leading?.[0].length || 0;
|
|
9010
9024
|
let match;
|
|
9011
9025
|
while ((match = regex.exec(text2)) !== null) {
|
|
9012
|
-
if (match[1])
|
|
9026
|
+
if (match[1]) pushWord(match[1]);
|
|
9013
9027
|
if (match[2]) tokens.push(match[2]);
|
|
9014
9028
|
}
|
|
9015
9029
|
return tokens;
|
|
@@ -9037,8 +9051,8 @@ function wordsToChars(text1, text2, options = {}) {
|
|
|
9037
9051
|
}
|
|
9038
9052
|
return { chars, tokenIds };
|
|
9039
9053
|
}
|
|
9040
|
-
const tokens1 = tokenize(text1);
|
|
9041
|
-
const tokens2 = tokenize(text2);
|
|
9054
|
+
const tokens1 = tokenize(text1, options.atomicChars);
|
|
9055
|
+
const tokens2 = tokenize(text2, options.atomicChars);
|
|
9042
9056
|
const encoded1 = mapTokensToChars(tokens1);
|
|
9043
9057
|
const encoded2 = mapTokensToChars(tokens2);
|
|
9044
9058
|
return {
|
|
@@ -14443,7 +14457,11 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
|
|
|
14443
14457
|
if (mapping.paragraphs.length === 0) {
|
|
14444
14458
|
return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: false });
|
|
14445
14459
|
}
|
|
14446
|
-
const
|
|
14460
|
+
const wordDiffs = computeWordDiffs(mapping.originalFullText, mapping.processedModifiedText, {
|
|
14461
|
+
...diffOptions,
|
|
14462
|
+
atomicChars: new Set(mapping.referenceMap.keys())
|
|
14463
|
+
});
|
|
14464
|
+
const diffs = keepUnchangedTextOutsideWrappers(anchorSharedReferences(wordDiffs, mapping.referenceMap), mapping);
|
|
14447
14465
|
return withOoxmlSourceType(applyReconstructionDiffs(
|
|
14448
14466
|
xmlDoc,
|
|
14449
14467
|
diffs,
|
|
@@ -14455,6 +14473,109 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
|
|
|
14455
14473
|
options
|
|
14456
14474
|
));
|
|
14457
14475
|
}
|
|
14476
|
+
function forEachChangeGroup(diffs, onEqual, onGroup) {
|
|
14477
|
+
let index = 0;
|
|
14478
|
+
while (index < diffs.length) {
|
|
14479
|
+
if (diffs[index][0] === 0) {
|
|
14480
|
+
onEqual(diffs[index]);
|
|
14481
|
+
index++;
|
|
14482
|
+
continue;
|
|
14483
|
+
}
|
|
14484
|
+
const group = [];
|
|
14485
|
+
while (index < diffs.length && diffs[index][0] !== 0) group.push(diffs[index++]);
|
|
14486
|
+
onGroup(
|
|
14487
|
+
group,
|
|
14488
|
+
group.filter(([op]) => op === -1).map(([, text2]) => text2).join(""),
|
|
14489
|
+
group.filter(([op]) => op === 1).map(([, text2]) => text2).join("")
|
|
14490
|
+
);
|
|
14491
|
+
}
|
|
14492
|
+
}
|
|
14493
|
+
function pushChange(result, deleted, inserted) {
|
|
14494
|
+
if (deleted === inserted) {
|
|
14495
|
+
if (deleted) result.push([0, deleted]);
|
|
14496
|
+
return;
|
|
14497
|
+
}
|
|
14498
|
+
if (deleted) result.push([-1, deleted]);
|
|
14499
|
+
if (inserted) result.push([1, inserted]);
|
|
14500
|
+
}
|
|
14501
|
+
function anchorSharedReferences(diffs, referenceMap) {
|
|
14502
|
+
if (referenceMap.size === 0) return diffs;
|
|
14503
|
+
const result = [];
|
|
14504
|
+
forEachChangeGroup(diffs, (part) => result.push(part), (group, deleted, inserted) => {
|
|
14505
|
+
const deletedRefs = [...deleted].filter((char) => referenceMap.has(char) && inserted.includes(char));
|
|
14506
|
+
const insertedRefs = [...inserted].filter((char) => deletedRefs.includes(char));
|
|
14507
|
+
if (deletedRefs.length === 0 || deletedRefs.join("") !== insertedRefs.join("")) {
|
|
14508
|
+
result.push(...group);
|
|
14509
|
+
return;
|
|
14510
|
+
}
|
|
14511
|
+
let deletedStart = 0;
|
|
14512
|
+
let insertedStart = 0;
|
|
14513
|
+
for (const ref of deletedRefs) {
|
|
14514
|
+
const deletedAt = deleted.indexOf(ref, deletedStart);
|
|
14515
|
+
const insertedAt = inserted.indexOf(ref, insertedStart);
|
|
14516
|
+
pushChange(result, deleted.slice(deletedStart, deletedAt), inserted.slice(insertedStart, insertedAt));
|
|
14517
|
+
result.push([0, ref]);
|
|
14518
|
+
deletedStart = deletedAt + 1;
|
|
14519
|
+
insertedStart = insertedAt + 1;
|
|
14520
|
+
}
|
|
14521
|
+
pushChange(result, deleted.slice(deletedStart), inserted.slice(insertedStart));
|
|
14522
|
+
});
|
|
14523
|
+
return result;
|
|
14524
|
+
}
|
|
14525
|
+
function commonPrefixLength(left, right) {
|
|
14526
|
+
const limit = Math.min(left.length, right.length);
|
|
14527
|
+
let length = 0;
|
|
14528
|
+
while (length < limit && left[length] === right[length]) length++;
|
|
14529
|
+
return length;
|
|
14530
|
+
}
|
|
14531
|
+
function commonSuffixLength(left, right, limit) {
|
|
14532
|
+
let length = 0;
|
|
14533
|
+
while (length < limit && left[left.length - 1 - length] === right[right.length - 1 - length]) length++;
|
|
14534
|
+
return length;
|
|
14535
|
+
}
|
|
14536
|
+
function keepUnchangedTextOutsideWrappers(diffs, mapping) {
|
|
14537
|
+
const wrapperAt = (index) => mapping.getRunProperties(index).wrapper || null;
|
|
14538
|
+
const result = [];
|
|
14539
|
+
let originalOffset = 0;
|
|
14540
|
+
forEachChangeGroup(diffs, (part) => {
|
|
14541
|
+
result.push(part);
|
|
14542
|
+
originalOffset += part[1].length;
|
|
14543
|
+
}, (group, deleted, inserted) => {
|
|
14544
|
+
const start = originalOffset;
|
|
14545
|
+
const end = start + deleted.length;
|
|
14546
|
+
originalOffset = end;
|
|
14547
|
+
const isWrapperBoundary = (index) => index > start && index < end && wrapperAt(index - 1) !== wrapperAt(index);
|
|
14548
|
+
let prefix = 0;
|
|
14549
|
+
let suffix = 0;
|
|
14550
|
+
if (deleted && inserted) {
|
|
14551
|
+
const sharedSuffix = commonSuffixLength(deleted, inserted, Math.min(deleted.length, inserted.length));
|
|
14552
|
+
for (let length = sharedSuffix; length > 0; length--) {
|
|
14553
|
+
if (isWrapperBoundary(end - length)) {
|
|
14554
|
+
suffix = length;
|
|
14555
|
+
break;
|
|
14556
|
+
}
|
|
14557
|
+
}
|
|
14558
|
+
const sharedPrefix = Math.min(
|
|
14559
|
+
commonPrefixLength(deleted, inserted),
|
|
14560
|
+
Math.min(deleted.length, inserted.length) - suffix
|
|
14561
|
+
);
|
|
14562
|
+
for (let length = sharedPrefix; length > 0; length--) {
|
|
14563
|
+
if (isWrapperBoundary(start + length)) {
|
|
14564
|
+
prefix = length;
|
|
14565
|
+
break;
|
|
14566
|
+
}
|
|
14567
|
+
}
|
|
14568
|
+
}
|
|
14569
|
+
if (prefix === 0 && suffix === 0) {
|
|
14570
|
+
result.push(...group);
|
|
14571
|
+
return;
|
|
14572
|
+
}
|
|
14573
|
+
if (prefix > 0) result.push([0, deleted.slice(0, prefix)]);
|
|
14574
|
+
pushChange(result, deleted.slice(prefix, deleted.length - suffix), inserted.slice(prefix, inserted.length - suffix));
|
|
14575
|
+
if (suffix > 0) result.push([0, deleted.slice(deleted.length - suffix)]);
|
|
14576
|
+
});
|
|
14577
|
+
return result;
|
|
14578
|
+
}
|
|
14458
14579
|
|
|
14459
14580
|
// engine/table-mode.js
|
|
14460
14581
|
function noChanges(serializer, xmlDoc) {
|