@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.
@@ -1,4 +1,4 @@
1
- // @ansonlai/docx-redline-js standalone bundle v0.8.1 — https://github.com/AnsonLai/docx-redline-js
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]) tokens.push(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 diffs = computeWordDiffs(mapping.originalFullText, mapping.processedModifiedText, diffOptions);
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) {