@createiq/htmldiff 1.2.0-beta.1 → 1.2.0-beta.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/dist/HtmlDiff.cjs CHANGED
@@ -1580,7 +1580,7 @@ function preprocessByContent(genesis, cpLatest, meCurrent, gTables, cTables, mTa
1580
1580
  placeholderToDiff
1581
1581
  };
1582
1582
  }
1583
- const POSITIONAL_PAIR_SIMILARITY_THRESHOLD = .5;
1583
+ const POSITIONAL_PAIR_SIMILARITY_THRESHOLD = .15;
1584
1584
  function positionallyAligned(genesis, cpLatest, meCurrent, gTables, cTables, mTables) {
1585
1585
  if (gTables.length !== cTables.length || cTables.length !== mTables.length) return false;
1586
1586
  for (let i = 0; i < gTables.length; i++) {
@@ -2044,6 +2044,16 @@ var HtmlDiff = class HtmlDiff {
2044
2044
  newText;
2045
2045
  oldText;
2046
2046
  tablePreprocessDepth = 0;
2047
+ /**
2048
+ * Tracks currently-open formatting-tag wraps. Each entry pairs the
2049
+ * opening tag (so a later closing tag can find its match) with the
2050
+ * styling info needed to RE-OPEN the wrap if an overlapping
2051
+ * formatting-tag close forces it to split. Without the styling info,
2052
+ * an overlap like `<strong>X</strong>` ↔ `<u>X</u>` produces an
2053
+ * unclosable wrap (the closing tag for the outer wrap arrives while
2054
+ * an inner wrap is still on the stack); see `insertTag`'s closing
2055
+ * handler for the split logic.
2056
+ */
2047
2057
  specialTagDiffStack = [];
2048
2058
  newWords = [];
2049
2059
  oldWords = [];
@@ -2510,38 +2520,52 @@ var HtmlDiff = class HtmlDiff {
2510
2520
  if (words.length === 0) break;
2511
2521
  const indexOfFirstNonTag = words.findIndex((x) => !Utils_default.isTag(x));
2512
2522
  const indexLastTagInFirstTagBlock = indexOfFirstNonTag === -1 ? words.length - 1 : indexOfFirstNonTag - 1;
2513
- let specialCaseTagInjection = "";
2514
- let specialCaseTagInjectionIsBefore = false;
2523
+ let preInject = "";
2524
+ let postInject = "";
2515
2525
  if (HtmlDiff.SpecialCaseOpeningTagRegex.test(words[0])) {
2516
2526
  const tagNames = /* @__PURE__ */ new Set();
2517
2527
  for (const word of words) if (Utils_default.isTag(word)) tagNames.add(Utils_default.getTagName(word));
2518
2528
  const styledTagNames = Array.from(tagNames).join(" ");
2519
- this.specialTagDiffStack.push(words[0]);
2520
- specialCaseTagInjection = `<ins${Utils_default.composeTagAttributes(`mod ${styledTagNames}`, metadata ?? {})}>`;
2529
+ const styledCssClass = `mod ${styledTagNames}`;
2530
+ this.specialTagDiffStack.push({
2531
+ tag: words[0],
2532
+ styledTagNames,
2533
+ cssClass: styledCssClass,
2534
+ metadata
2535
+ });
2536
+ postInject = `<ins${Utils_default.composeTagAttributes(styledCssClass, metadata ?? {})}>`;
2521
2537
  if (tag === HtmlDiff.DelTag) {
2522
2538
  words.shift();
2523
2539
  while (words.length > 0 && HtmlDiff.SpecialCaseOpeningTagRegex.test(words[0])) words.shift();
2524
2540
  }
2525
2541
  } else if (HtmlDiff.SpecialCaseClosingTagsSet.has(words[0].toLowerCase())) {
2526
- const openingTag = this.specialTagDiffStack.length === 0 ? null : this.specialTagDiffStack.pop();
2527
2542
  let tagIndexToCompare = indexLastTagInFirstTagBlock;
2528
2543
  if (tag === HtmlDiff.DelTag && indexOfFirstNonTag === -1) {
2529
2544
  if (words.slice(0, indexLastTagInFirstTagBlock + 1).some((w) => !HtmlDiff.SpecialCaseClosingTagsSet.has(w.toLowerCase()))) tagIndexToCompare = 0;
2530
2545
  }
2531
- const openingAndClosingTagsMatch = !!openingTag && Utils_default.getTagName(openingTag) === Utils_default.getTagName(words[tagIndexToCompare]);
2532
- if (openingTag && openingAndClosingTagsMatch) {
2533
- specialCaseTagInjection = "</ins>";
2534
- specialCaseTagInjectionIsBefore = true;
2535
- } else if (openingTag) this.specialTagDiffStack.push(openingTag);
2546
+ const closingTagName = Utils_default.getTagName(words[tagIndexToCompare]);
2547
+ let matchIdx = -1;
2548
+ for (let i = this.specialTagDiffStack.length - 1; i >= 0; i--) if (Utils_default.getTagName(this.specialTagDiffStack[i].tag) === closingTagName) {
2549
+ matchIdx = i;
2550
+ break;
2551
+ }
2552
+ if (matchIdx >= 0) {
2553
+ const aboveEntries = this.specialTagDiffStack.splice(matchIdx + 1);
2554
+ this.specialTagDiffStack.pop();
2555
+ preInject = "</ins>".repeat(aboveEntries.length + 1);
2556
+ for (const entry of aboveEntries) {
2557
+ postInject += `<ins${Utils_default.composeTagAttributes(entry.cssClass, entry.metadata ?? {})}>`;
2558
+ this.specialTagDiffStack.push(entry);
2559
+ }
2560
+ }
2536
2561
  if (tag === HtmlDiff.DelTag) {
2537
2562
  words.shift();
2538
2563
  while (words.length > 0 && HtmlDiff.SpecialCaseClosingTagsSet.has(words[0].toLowerCase())) words.shift();
2539
2564
  }
2540
2565
  }
2541
- if (words.length === 0 && specialCaseTagInjection.length === 0) break;
2566
+ if (words.length === 0 && preInject.length === 0 && postInject.length === 0) break;
2542
2567
  const isTagForExtraction = tag === HtmlDiff.DelTag ? (x) => Utils_default.isTag(x) && !HtmlDiff.SpecialCaseOpeningTagRegex.test(x) && !HtmlDiff.SpecialCaseClosingTagsSet.has(x.toLowerCase()) : Utils_default.isTag;
2543
- if (specialCaseTagInjectionIsBefore) this.content.push(specialCaseTagInjection + this.extractConsecutiveWords(words, isTagForExtraction).join(""));
2544
- else this.content.push(this.extractConsecutiveWords(words, isTagForExtraction).join("") + specialCaseTagInjection);
2568
+ this.content.push(preInject + this.extractConsecutiveWords(words, isTagForExtraction).join("") + postInject);
2545
2569
  if (words.length === 0) continue;
2546
2570
  this.insertTag(tag, cssClass, words, metadata);
2547
2571
  break;