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