@adeu/core 1.14.0 → 1.15.0
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/index.cjs +2198 -1788
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -12
- package/dist/index.d.ts +7 -12
- package/dist/index.js +2198 -1788
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/comment_dedup.test.ts +269 -0
- package/src/domain.test.ts +280 -77
- package/src/domain.ts +146 -46
- package/src/engine.qa.test.ts +162 -0
- package/src/engine.ts +274 -89
- package/src/ingest.ts +283 -133
- package/src/mapper.ts +425 -196
- package/src/models.ts +2 -0
- package/src/repro_heading_bug.test.ts +174 -0
- package/src/search_write_engine.test.ts +112 -0
package/dist/index.cjs
CHANGED
|
@@ -1333,7 +1333,12 @@ ${header}`;
|
|
|
1333
1333
|
this._add_virtual_text(prefix, current, item);
|
|
1334
1334
|
current += prefix.length;
|
|
1335
1335
|
}
|
|
1336
|
-
current = this._map_paragraph_content(
|
|
1336
|
+
current = this._map_paragraph_content(
|
|
1337
|
+
item,
|
|
1338
|
+
current,
|
|
1339
|
+
style_cache,
|
|
1340
|
+
default_pstyle
|
|
1341
|
+
);
|
|
1337
1342
|
is_first_para = false;
|
|
1338
1343
|
previous_item = item;
|
|
1339
1344
|
} else if (item instanceof Table) {
|
|
@@ -1406,7 +1411,13 @@ ${header}`;
|
|
|
1406
1411
|
}
|
|
1407
1412
|
_map_paragraph_content(paragraph, start_offset, style_cache, default_pstyle) {
|
|
1408
1413
|
let current = start_offset;
|
|
1409
|
-
const span = {
|
|
1414
|
+
const span = {
|
|
1415
|
+
start: current,
|
|
1416
|
+
end: current,
|
|
1417
|
+
text: "",
|
|
1418
|
+
run: null,
|
|
1419
|
+
paragraph
|
|
1420
|
+
};
|
|
1410
1421
|
this.spans.push(span);
|
|
1411
1422
|
const active_ids = /* @__PURE__ */ new Set();
|
|
1412
1423
|
const active_ins = {};
|
|
@@ -1424,7 +1435,7 @@ ${header}`;
|
|
|
1424
1435
|
this._add_virtual_text(s_tok, current, paragraph);
|
|
1425
1436
|
current += s_tok.length;
|
|
1426
1437
|
}
|
|
1427
|
-
for (const [kind, txt, r_obj, i_id, d_id] of pending_runs) {
|
|
1438
|
+
for (const [kind, txt, r_obj, i_id, d_id, c_ids] of pending_runs) {
|
|
1428
1439
|
if (kind === "virtual") {
|
|
1429
1440
|
this._add_virtual_text(txt, current, paragraph, active_hyperlink_id);
|
|
1430
1441
|
} else {
|
|
@@ -1436,7 +1447,8 @@ ${header}`;
|
|
|
1436
1447
|
paragraph,
|
|
1437
1448
|
ins_id: i_id || void 0,
|
|
1438
1449
|
del_id: d_id || void 0,
|
|
1439
|
-
hyperlink_id: active_hyperlink_id || void 0
|
|
1450
|
+
hyperlink_id: active_hyperlink_id || void 0,
|
|
1451
|
+
comment_ids: c_ids.length > 0 ? c_ids : void 0
|
|
1440
1452
|
};
|
|
1441
1453
|
this.spans.push(s);
|
|
1442
1454
|
this._text_chunks.push(txt);
|
|
@@ -1450,8 +1462,16 @@ ${header}`;
|
|
|
1450
1462
|
pending_runs = [];
|
|
1451
1463
|
};
|
|
1452
1464
|
const items = Array.from(iter_paragraph_content(paragraph));
|
|
1453
|
-
const is_heading = is_heading_paragraph(
|
|
1454
|
-
|
|
1465
|
+
const is_heading = is_heading_paragraph(
|
|
1466
|
+
paragraph,
|
|
1467
|
+
style_cache,
|
|
1468
|
+
default_pstyle
|
|
1469
|
+
);
|
|
1470
|
+
const native_heading = is_native_heading(
|
|
1471
|
+
paragraph,
|
|
1472
|
+
style_cache,
|
|
1473
|
+
default_pstyle
|
|
1474
|
+
);
|
|
1455
1475
|
let leading_strip_active = is_heading;
|
|
1456
1476
|
for (let i = 0; i < items.length; i++) {
|
|
1457
1477
|
const item = items[i];
|
|
@@ -1486,7 +1506,12 @@ ${header}`;
|
|
|
1486
1506
|
const curr_ins_id = Object.keys(active_ins).pop() || null;
|
|
1487
1507
|
const curr_del_id = Object.keys(active_del).pop() || null;
|
|
1488
1508
|
if (full_seg_text && !(this.clean_view && curr_del_id) && !(this.original_view && curr_ins_id)) {
|
|
1489
|
-
const new_wrappers = this.clean_view || this.original_view ? ["", ""] : this._get_wrappers(
|
|
1509
|
+
const new_wrappers = this.clean_view || this.original_view ? ["", ""] : this._get_wrappers(
|
|
1510
|
+
curr_ins_id,
|
|
1511
|
+
curr_del_id,
|
|
1512
|
+
active_ids,
|
|
1513
|
+
active_fmt
|
|
1514
|
+
);
|
|
1490
1515
|
const new_style = [prefix, suffix];
|
|
1491
1516
|
if (pending_runs.length > 0 && new_wrappers[0] === current_wrappers[0] && new_wrappers[1] === current_wrappers[1]) {
|
|
1492
1517
|
let skip_leading_prefix = false;
|
|
@@ -1494,36 +1519,58 @@ ${header}`;
|
|
|
1494
1519
|
pending_runs.pop();
|
|
1495
1520
|
skip_leading_prefix = true;
|
|
1496
1521
|
}
|
|
1522
|
+
const curr_comment_ids = Array.from(active_ids);
|
|
1497
1523
|
for (const [kind, txt, r_obj] of run_parts) {
|
|
1498
1524
|
if (skip_leading_prefix && kind === "virtual" && txt === new_style[0]) {
|
|
1499
1525
|
skip_leading_prefix = false;
|
|
1500
1526
|
continue;
|
|
1501
1527
|
}
|
|
1502
|
-
pending_runs.push([
|
|
1528
|
+
pending_runs.push([
|
|
1529
|
+
kind,
|
|
1530
|
+
txt,
|
|
1531
|
+
r_obj,
|
|
1532
|
+
curr_ins_id,
|
|
1533
|
+
curr_del_id,
|
|
1534
|
+
curr_comment_ids
|
|
1535
|
+
]);
|
|
1503
1536
|
}
|
|
1504
1537
|
current_style = new_style;
|
|
1505
1538
|
} else {
|
|
1506
1539
|
flush_pending_runs();
|
|
1507
1540
|
current_wrappers = new_wrappers;
|
|
1508
1541
|
current_style = new_style;
|
|
1542
|
+
const curr_comment_ids = Array.from(active_ids);
|
|
1509
1543
|
for (const [kind, txt, r_obj] of run_parts) {
|
|
1510
|
-
pending_runs.push([
|
|
1544
|
+
pending_runs.push([
|
|
1545
|
+
kind,
|
|
1546
|
+
txt,
|
|
1547
|
+
r_obj,
|
|
1548
|
+
curr_ins_id,
|
|
1549
|
+
curr_del_id,
|
|
1550
|
+
curr_comment_ids
|
|
1551
|
+
]);
|
|
1511
1552
|
}
|
|
1512
1553
|
}
|
|
1513
1554
|
}
|
|
1514
1555
|
if (!this.clean_view && !this.original_view) {
|
|
1515
1556
|
const has_meta = Object.keys(active_ins).length > 0 || Object.keys(active_del).length > 0 || active_ids.size > 0 || Object.keys(active_fmt).length > 0;
|
|
1516
1557
|
if (has_meta) {
|
|
1517
|
-
deferred_meta_states.push([
|
|
1558
|
+
deferred_meta_states.push([
|
|
1559
|
+
{ ...active_ins },
|
|
1560
|
+
{ ...active_del },
|
|
1561
|
+
new Set(active_ids),
|
|
1562
|
+
{ ...active_fmt }
|
|
1563
|
+
]);
|
|
1518
1564
|
}
|
|
1519
1565
|
let should_defer = false;
|
|
1520
|
-
const
|
|
1521
|
-
if (
|
|
1566
|
+
const has_any_meta = curr_ins_id !== null || curr_del_id !== null || Object.keys(active_fmt).length > 0 || active_ids.size > 0;
|
|
1567
|
+
if (has_any_meta) {
|
|
1522
1568
|
let j = i + 1;
|
|
1523
|
-
let
|
|
1569
|
+
let next_has_meta = false;
|
|
1524
1570
|
let temp_ins_count = Object.keys(active_ins).length;
|
|
1525
1571
|
let temp_del_count = Object.keys(active_del).length;
|
|
1526
1572
|
let temp_fmt_count = Object.keys(active_fmt).length;
|
|
1573
|
+
const temp_comment_ids = new Set(active_ids);
|
|
1527
1574
|
while (j < items.length) {
|
|
1528
1575
|
const next_item = items[j];
|
|
1529
1576
|
if (next_item instanceof Run) {
|
|
@@ -1531,22 +1578,27 @@ ${header}`;
|
|
|
1531
1578
|
j++;
|
|
1532
1579
|
continue;
|
|
1533
1580
|
}
|
|
1534
|
-
if (temp_ins_count > 0 || temp_del_count > 0 || temp_fmt_count > 0) {
|
|
1535
|
-
|
|
1581
|
+
if (temp_ins_count > 0 || temp_del_count > 0 || temp_fmt_count > 0 || temp_comment_ids.size > 0) {
|
|
1582
|
+
next_has_meta = true;
|
|
1536
1583
|
}
|
|
1537
1584
|
break;
|
|
1538
1585
|
} else {
|
|
1539
1586
|
const ev = next_item;
|
|
1540
1587
|
if (ev.type === "ins_start") temp_ins_count++;
|
|
1541
|
-
else if (ev.type === "ins_end")
|
|
1588
|
+
else if (ev.type === "ins_end")
|
|
1589
|
+
temp_ins_count = Math.max(0, temp_ins_count - 1);
|
|
1542
1590
|
else if (ev.type === "del_start") temp_del_count++;
|
|
1543
|
-
else if (ev.type === "del_end")
|
|
1591
|
+
else if (ev.type === "del_end")
|
|
1592
|
+
temp_del_count = Math.max(0, temp_del_count - 1);
|
|
1544
1593
|
else if (ev.type === "fmt_start") temp_fmt_count++;
|
|
1545
|
-
else if (ev.type === "fmt_end")
|
|
1594
|
+
else if (ev.type === "fmt_end")
|
|
1595
|
+
temp_fmt_count = Math.max(0, temp_fmt_count - 1);
|
|
1596
|
+
else if (ev.type === "start") temp_comment_ids.add(ev.id);
|
|
1597
|
+
else if (ev.type === "end") temp_comment_ids.delete(ev.id);
|
|
1546
1598
|
}
|
|
1547
1599
|
j++;
|
|
1548
1600
|
}
|
|
1549
|
-
if (
|
|
1601
|
+
if (next_has_meta) should_defer = true;
|
|
1550
1602
|
}
|
|
1551
1603
|
if (!should_defer && deferred_meta_states.length > 0) {
|
|
1552
1604
|
const meta_block = this._build_merged_meta_block(deferred_meta_states);
|
|
@@ -1635,7 +1687,8 @@ ${header}`;
|
|
|
1635
1687
|
_get_wrappers(ins_id, del_id, active_ids, active_fmt) {
|
|
1636
1688
|
if (del_id) return ["{--", "--}"];
|
|
1637
1689
|
if (ins_id) return ["{++", "++}"];
|
|
1638
|
-
if (active_ids.size > 0 || Object.keys(active_fmt).length > 0)
|
|
1690
|
+
if (active_ids.size > 0 || Object.keys(active_fmt).length > 0)
|
|
1691
|
+
return ["{==", "==}"];
|
|
1639
1692
|
return ["", ""];
|
|
1640
1693
|
}
|
|
1641
1694
|
_build_merged_meta_block(states_list) {
|
|
@@ -1643,7 +1696,9 @@ ${header}`;
|
|
|
1643
1696
|
const comment_lines = [];
|
|
1644
1697
|
const seen_sigs = /* @__PURE__ */ new Set();
|
|
1645
1698
|
for (const [ins_map, del_map, comments_set, fmt_map] of states_list) {
|
|
1646
|
-
for (const [uid, meta] of Object.entries(
|
|
1699
|
+
for (const [uid, meta] of Object.entries(
|
|
1700
|
+
ins_map
|
|
1701
|
+
)) {
|
|
1647
1702
|
const sig = `Chg:${uid}`;
|
|
1648
1703
|
if (!seen_sigs.has(sig)) {
|
|
1649
1704
|
const auth = meta.author || "Unknown";
|
|
@@ -1651,7 +1706,9 @@ ${header}`;
|
|
|
1651
1706
|
seen_sigs.add(sig);
|
|
1652
1707
|
}
|
|
1653
1708
|
}
|
|
1654
|
-
for (const [uid, meta] of Object.entries(
|
|
1709
|
+
for (const [uid, meta] of Object.entries(
|
|
1710
|
+
del_map
|
|
1711
|
+
)) {
|
|
1655
1712
|
const sig = `Chg:${uid}`;
|
|
1656
1713
|
if (!seen_sigs.has(sig)) {
|
|
1657
1714
|
const auth = meta.author || "Unknown";
|
|
@@ -1659,7 +1716,9 @@ ${header}`;
|
|
|
1659
1716
|
seen_sigs.add(sig);
|
|
1660
1717
|
}
|
|
1661
1718
|
}
|
|
1662
|
-
for (const [uid, meta] of Object.entries(
|
|
1719
|
+
for (const [uid, meta] of Object.entries(
|
|
1720
|
+
fmt_map
|
|
1721
|
+
)) {
|
|
1663
1722
|
const sig = `Chg:${uid}`;
|
|
1664
1723
|
if (!seen_sigs.has(sig)) {
|
|
1665
1724
|
const auth = meta.author || "Unknown";
|
|
@@ -1733,7 +1792,16 @@ ${header}`;
|
|
|
1733
1792
|
if (remaining) parts.push(escapeRegExp(remaining));
|
|
1734
1793
|
return parts.join("");
|
|
1735
1794
|
}
|
|
1736
|
-
find_match_index(target_text) {
|
|
1795
|
+
find_match_index(target_text, is_regex = false) {
|
|
1796
|
+
if (is_regex) {
|
|
1797
|
+
try {
|
|
1798
|
+
const pattern = new RegExp(target_text);
|
|
1799
|
+
const match = pattern.exec(this.full_text);
|
|
1800
|
+
if (match) return [match.index, match[0].length];
|
|
1801
|
+
} catch (e) {
|
|
1802
|
+
}
|
|
1803
|
+
return [-1, 0];
|
|
1804
|
+
}
|
|
1737
1805
|
let start_idx = this.full_text.indexOf(target_text);
|
|
1738
1806
|
if (start_idx !== -1) return [start_idx, target_text.length];
|
|
1739
1807
|
const norm_full = this._replace_smart_quotes(this.full_text);
|
|
@@ -1753,22 +1821,41 @@ ${header}`;
|
|
|
1753
1821
|
}
|
|
1754
1822
|
return [-1, 0];
|
|
1755
1823
|
}
|
|
1756
|
-
find_all_match_indices(target_text) {
|
|
1824
|
+
find_all_match_indices(target_text, is_regex = false) {
|
|
1757
1825
|
if (!target_text) return [];
|
|
1826
|
+
if (is_regex) {
|
|
1827
|
+
try {
|
|
1828
|
+
const pattern = new RegExp(target_text, "g");
|
|
1829
|
+
const matches2 = [...this.full_text.matchAll(pattern)];
|
|
1830
|
+
if (matches2.length > 0)
|
|
1831
|
+
return matches2.map((m) => [m.index, m[0].length]);
|
|
1832
|
+
} catch (e) {
|
|
1833
|
+
}
|
|
1834
|
+
return [];
|
|
1835
|
+
}
|
|
1758
1836
|
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1759
|
-
let matches = [
|
|
1837
|
+
let matches = [
|
|
1838
|
+
...this.full_text.matchAll(new RegExp(escapeRegExp(target_text), "g"))
|
|
1839
|
+
];
|
|
1760
1840
|
if (matches.length > 0) return matches.map((m) => [m.index, m[0].length]);
|
|
1761
1841
|
const norm_full = this._replace_smart_quotes(this.full_text);
|
|
1762
1842
|
const norm_target = this._replace_smart_quotes(target_text);
|
|
1763
|
-
matches = [
|
|
1843
|
+
matches = [
|
|
1844
|
+
...norm_full.matchAll(new RegExp(escapeRegExp(norm_target), "g"))
|
|
1845
|
+
];
|
|
1764
1846
|
if (matches.length > 0) return matches.map((m) => [m.index, m[0].length]);
|
|
1765
1847
|
const stripped_target = this._strip_markdown_formatting(target_text);
|
|
1766
|
-
matches = [
|
|
1848
|
+
matches = [
|
|
1849
|
+
...this.full_text.matchAll(
|
|
1850
|
+
new RegExp(escapeRegExp(stripped_target), "g")
|
|
1851
|
+
)
|
|
1852
|
+
];
|
|
1767
1853
|
if (matches.length > 0) return matches.map((m) => [m.index, m[0].length]);
|
|
1768
1854
|
try {
|
|
1769
1855
|
const pattern = new RegExp(this._make_fuzzy_regex(target_text), "g");
|
|
1770
1856
|
matches = [...this.full_text.matchAll(pattern)];
|
|
1771
|
-
if (matches.length > 0)
|
|
1857
|
+
if (matches.length > 0)
|
|
1858
|
+
return matches.map((m) => [m.index, m[0].length]);
|
|
1772
1859
|
} catch (e) {
|
|
1773
1860
|
}
|
|
1774
1861
|
return [];
|
|
@@ -1779,14 +1866,22 @@ ${header}`;
|
|
|
1779
1866
|
return this._resolve_runs_at_range(start_idx, start_idx + length);
|
|
1780
1867
|
}
|
|
1781
1868
|
find_target_runs_by_index(start_index, length, rebuild_map = true) {
|
|
1782
|
-
return this._resolve_runs_at_range(
|
|
1869
|
+
return this._resolve_runs_at_range(
|
|
1870
|
+
start_index,
|
|
1871
|
+
start_index + length,
|
|
1872
|
+
rebuild_map
|
|
1873
|
+
);
|
|
1783
1874
|
}
|
|
1784
1875
|
get_virtual_spans_in_range(start_index, length) {
|
|
1785
1876
|
const end_index = start_index + length;
|
|
1786
|
-
return this.spans.filter(
|
|
1877
|
+
return this.spans.filter(
|
|
1878
|
+
(s) => s.run === null && s.text === "\n\n" && s.start >= start_index && s.end <= end_index
|
|
1879
|
+
);
|
|
1787
1880
|
}
|
|
1788
1881
|
_resolve_runs_at_range(start_idx, end_idx, rebuild_map = true) {
|
|
1789
|
-
const affected_spans = this.spans.filter(
|
|
1882
|
+
const affected_spans = this.spans.filter(
|
|
1883
|
+
(s) => s.end > start_idx && s.start < end_idx
|
|
1884
|
+
);
|
|
1790
1885
|
if (affected_spans.length === 0) return [];
|
|
1791
1886
|
const working_runs = affected_spans.filter((s) => s.run !== null).map((s) => s.run);
|
|
1792
1887
|
if (working_runs.length === 0) return [];
|
|
@@ -1797,7 +1892,10 @@ ${header}`;
|
|
|
1797
1892
|
const local_start = start_idx - first_real_span.start;
|
|
1798
1893
|
if (local_start > 0) {
|
|
1799
1894
|
const idx_in_working = 0;
|
|
1800
|
-
const [, right_run] = this._split_run_at_index(
|
|
1895
|
+
const [, right_run] = this._split_run_at_index(
|
|
1896
|
+
working_runs[idx_in_working],
|
|
1897
|
+
local_start
|
|
1898
|
+
);
|
|
1801
1899
|
working_runs[idx_in_working] = right_run;
|
|
1802
1900
|
dom_modified = true;
|
|
1803
1901
|
start_split_adjustment = local_start;
|
|
@@ -1834,7 +1932,9 @@ ${header}`;
|
|
|
1834
1932
|
if (preceding[i].paragraph) return [null, preceding[i].paragraph];
|
|
1835
1933
|
}
|
|
1836
1934
|
}
|
|
1837
|
-
const containing = this.spans.filter(
|
|
1935
|
+
const containing = this.spans.filter(
|
|
1936
|
+
(s) => s.start < index && index < s.end
|
|
1937
|
+
);
|
|
1838
1938
|
if (containing.length > 0) {
|
|
1839
1939
|
const span = containing[0];
|
|
1840
1940
|
if (span.run === null) {
|
|
@@ -1857,10 +1957,12 @@ ${header}`;
|
|
|
1857
1957
|
const preceding_gap = this.spans.filter((s) => s.end < index);
|
|
1858
1958
|
if (preceding_gap.length > 0) {
|
|
1859
1959
|
for (let i = preceding_gap.length - 1; i >= 0; i--) {
|
|
1860
|
-
if (preceding_gap[i].run)
|
|
1960
|
+
if (preceding_gap[i].run)
|
|
1961
|
+
return [preceding_gap[i].run, preceding_gap[i].paragraph];
|
|
1861
1962
|
}
|
|
1862
1963
|
for (let i = preceding_gap.length - 1; i >= 0; i--) {
|
|
1863
|
-
if (preceding_gap[i].paragraph)
|
|
1964
|
+
if (preceding_gap[i].paragraph)
|
|
1965
|
+
return [null, preceding_gap[i].paragraph];
|
|
1864
1966
|
}
|
|
1865
1967
|
}
|
|
1866
1968
|
return [null, null];
|
|
@@ -1873,7 +1975,10 @@ ${header}`;
|
|
|
1873
1975
|
const new_r_element = run._element.cloneNode(true);
|
|
1874
1976
|
this._set_run_text_elements(new_r_element, right_text);
|
|
1875
1977
|
if (run._element.parentNode) {
|
|
1876
|
-
run._element.parentNode.insertBefore(
|
|
1978
|
+
run._element.parentNode.insertBefore(
|
|
1979
|
+
new_r_element,
|
|
1980
|
+
run._element.nextSibling
|
|
1981
|
+
);
|
|
1877
1982
|
}
|
|
1878
1983
|
const new_run = new Run(new_r_element, run._parent);
|
|
1879
1984
|
return [run, new_run];
|
|
@@ -1900,7 +2005,9 @@ ${header}`;
|
|
|
1900
2005
|
}
|
|
1901
2006
|
}
|
|
1902
2007
|
get_context_at_range(start_idx, end_idx) {
|
|
1903
|
-
const real_spans = this.spans.filter(
|
|
2008
|
+
const real_spans = this.spans.filter(
|
|
2009
|
+
(s) => s.run && s.end > start_idx && s.start < end_idx
|
|
2010
|
+
);
|
|
1904
2011
|
if (real_spans.length > 0) return real_spans[0];
|
|
1905
2012
|
return null;
|
|
1906
2013
|
}
|
|
@@ -2252,6 +2359,193 @@ function create_word_patch_diff(original_text, modified_text, original_path = "O
|
|
|
2252
2359
|
return output.join("\n");
|
|
2253
2360
|
}
|
|
2254
2361
|
|
|
2362
|
+
// src/pagination.ts
|
|
2363
|
+
var PAGE_TARGET_CHARS = 19e3;
|
|
2364
|
+
var APPENDIX_MARKER = "<!-- READONLY_BOUNDARY_START -->";
|
|
2365
|
+
var _CRITIC_TOKENS = {
|
|
2366
|
+
"{++": "++}",
|
|
2367
|
+
"{--": "--}",
|
|
2368
|
+
"{==": "==}",
|
|
2369
|
+
"{>>": "<<}"
|
|
2370
|
+
};
|
|
2371
|
+
var _CHG_ID_PATTERN = /\bChg:(\d+)\b/g;
|
|
2372
|
+
function split_structural_appendix(markdown) {
|
|
2373
|
+
if (!markdown) return ["", ""];
|
|
2374
|
+
const idx = markdown.indexOf(APPENDIX_MARKER);
|
|
2375
|
+
if (idx === -1) return [markdown, ""];
|
|
2376
|
+
const line_start = markdown.lastIndexOf("\n", idx) + 1;
|
|
2377
|
+
const body = markdown.substring(0, line_start).trimEnd();
|
|
2378
|
+
const appendix = markdown.substring(line_start);
|
|
2379
|
+
return [body, appendix];
|
|
2380
|
+
}
|
|
2381
|
+
function paginate(markdown_body, structural_appendix = "") {
|
|
2382
|
+
if (!markdown_body) {
|
|
2383
|
+
const appendix_clean = structural_appendix ? structural_appendix.trim() : "";
|
|
2384
|
+
const content = appendix_clean;
|
|
2385
|
+
return {
|
|
2386
|
+
pages: [{
|
|
2387
|
+
page: 1,
|
|
2388
|
+
total_pages: 1,
|
|
2389
|
+
has_next: false,
|
|
2390
|
+
has_prev: false,
|
|
2391
|
+
tracked_change_count: _count_tracked_changes(content),
|
|
2392
|
+
page_content: content
|
|
2393
|
+
}],
|
|
2394
|
+
total_pages: 1,
|
|
2395
|
+
body_pages: [""],
|
|
2396
|
+
body_page_offsets: [0]
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
const block_records = _tokenize_into_atomic_blocks(markdown_body);
|
|
2400
|
+
const [body_pages, body_page_offsets] = _assemble_pages(block_records);
|
|
2401
|
+
let final_pages;
|
|
2402
|
+
if (structural_appendix && structural_appendix.trim()) {
|
|
2403
|
+
const appendix = structural_appendix.trim();
|
|
2404
|
+
final_pages = body_pages.map((bp) => bp ? `${bp}
|
|
2405
|
+
|
|
2406
|
+
${appendix}` : appendix);
|
|
2407
|
+
} else {
|
|
2408
|
+
final_pages = [...body_pages];
|
|
2409
|
+
}
|
|
2410
|
+
const total = final_pages.length;
|
|
2411
|
+
const page_infos = final_pages.map((content, i) => ({
|
|
2412
|
+
page: i + 1,
|
|
2413
|
+
total_pages: total,
|
|
2414
|
+
has_next: i + 1 < total,
|
|
2415
|
+
has_prev: i + 1 > 1,
|
|
2416
|
+
tracked_change_count: _count_tracked_changes(content),
|
|
2417
|
+
page_content: content
|
|
2418
|
+
}));
|
|
2419
|
+
return {
|
|
2420
|
+
pages: page_infos,
|
|
2421
|
+
total_pages: total,
|
|
2422
|
+
body_pages,
|
|
2423
|
+
body_page_offsets
|
|
2424
|
+
};
|
|
2425
|
+
}
|
|
2426
|
+
function _tokenize_into_atomic_blocks(markdown_body) {
|
|
2427
|
+
const raw_blocks = _split_on_safe_paragraph_breaks(markdown_body);
|
|
2428
|
+
return _merge_footnote_sections(raw_blocks);
|
|
2429
|
+
}
|
|
2430
|
+
function _split_on_safe_paragraph_breaks(text) {
|
|
2431
|
+
const counters = { "++}": 0, "--}": 0, "==}": 0, "<<}": 0 };
|
|
2432
|
+
const blocks = [];
|
|
2433
|
+
let block_start = 0;
|
|
2434
|
+
let i = 0;
|
|
2435
|
+
const n = text.length;
|
|
2436
|
+
while (i < n) {
|
|
2437
|
+
let matched_open = false;
|
|
2438
|
+
for (const [open_tok, close_tok] of Object.entries(_CRITIC_TOKENS)) {
|
|
2439
|
+
if (text.startsWith(open_tok, i)) {
|
|
2440
|
+
counters[close_tok]++;
|
|
2441
|
+
i += open_tok.length;
|
|
2442
|
+
matched_open = true;
|
|
2443
|
+
break;
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
if (matched_open) continue;
|
|
2447
|
+
let matched_close = false;
|
|
2448
|
+
for (const close_tok of Object.values(_CRITIC_TOKENS)) {
|
|
2449
|
+
if (text.startsWith(close_tok, i)) {
|
|
2450
|
+
if (counters[close_tok] > 0) counters[close_tok]--;
|
|
2451
|
+
i += close_tok.length;
|
|
2452
|
+
matched_close = true;
|
|
2453
|
+
break;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
if (matched_close) continue;
|
|
2457
|
+
if (text[i] === "\n" && i + 1 < n && text[i + 1] === "\n") {
|
|
2458
|
+
if (Object.values(counters).every((c) => c === 0)) {
|
|
2459
|
+
const block_text = text.substring(block_start, i);
|
|
2460
|
+
if (block_text) blocks.push([block_text, block_start]);
|
|
2461
|
+
let j = i;
|
|
2462
|
+
while (j < n && text[j] === "\n") j++;
|
|
2463
|
+
i = j;
|
|
2464
|
+
block_start = i;
|
|
2465
|
+
continue;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
i++;
|
|
2469
|
+
}
|
|
2470
|
+
if (block_start < n) {
|
|
2471
|
+
const block_text = text.substring(block_start, n);
|
|
2472
|
+
if (block_text) blocks.push([block_text, block_start]);
|
|
2473
|
+
}
|
|
2474
|
+
return blocks;
|
|
2475
|
+
}
|
|
2476
|
+
function _merge_footnote_sections(blocks) {
|
|
2477
|
+
if (!blocks.length) return blocks;
|
|
2478
|
+
const merged = [];
|
|
2479
|
+
let i = 0;
|
|
2480
|
+
while (i < blocks.length) {
|
|
2481
|
+
const [block_text, block_offset] = blocks[i];
|
|
2482
|
+
const stripped = block_text.trimStart();
|
|
2483
|
+
const is_section_header = stripped.startsWith("## Footnotes") || stripped.startsWith("## Endnotes");
|
|
2484
|
+
if (!is_section_header) {
|
|
2485
|
+
merged.push([block_text, block_offset]);
|
|
2486
|
+
i++;
|
|
2487
|
+
continue;
|
|
2488
|
+
}
|
|
2489
|
+
let accumulated_text = block_text;
|
|
2490
|
+
let j = i + 1;
|
|
2491
|
+
while (j < blocks.length) {
|
|
2492
|
+
const [next_text] = blocks[j];
|
|
2493
|
+
const next_stripped = next_text.trimStart();
|
|
2494
|
+
if (next_stripped.startsWith("[^fn-") || next_stripped.startsWith("[^en-")) {
|
|
2495
|
+
accumulated_text = `${accumulated_text}
|
|
2496
|
+
|
|
2497
|
+
${next_text}`;
|
|
2498
|
+
j++;
|
|
2499
|
+
} else {
|
|
2500
|
+
break;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
merged.push([accumulated_text, block_offset]);
|
|
2504
|
+
i = j;
|
|
2505
|
+
}
|
|
2506
|
+
return merged;
|
|
2507
|
+
}
|
|
2508
|
+
function _assemble_pages(block_records) {
|
|
2509
|
+
if (!block_records.length) return [[""], [0]];
|
|
2510
|
+
const pages = [];
|
|
2511
|
+
const page_starts = [];
|
|
2512
|
+
let current_blocks = [];
|
|
2513
|
+
let current_size = 0;
|
|
2514
|
+
let current_start = -1;
|
|
2515
|
+
const flush_current = () => {
|
|
2516
|
+
if (current_blocks.length > 0) {
|
|
2517
|
+
pages.push(current_blocks.join("\n\n"));
|
|
2518
|
+
page_starts.push(current_start);
|
|
2519
|
+
}
|
|
2520
|
+
current_blocks = [];
|
|
2521
|
+
current_size = 0;
|
|
2522
|
+
current_start = -1;
|
|
2523
|
+
};
|
|
2524
|
+
for (const [block_text, block_offset] of block_records) {
|
|
2525
|
+
const block_size = block_text.length;
|
|
2526
|
+
const added_size = block_size + (current_blocks.length > 0 ? 2 : 0);
|
|
2527
|
+
if (current_blocks.length > 0 && current_size + added_size > PAGE_TARGET_CHARS) {
|
|
2528
|
+
flush_current();
|
|
2529
|
+
}
|
|
2530
|
+
if (current_blocks.length === 0 && block_size > PAGE_TARGET_CHARS) {
|
|
2531
|
+
pages.push(block_text);
|
|
2532
|
+
page_starts.push(block_offset);
|
|
2533
|
+
continue;
|
|
2534
|
+
}
|
|
2535
|
+
if (current_blocks.length === 0) current_start = block_offset;
|
|
2536
|
+
current_blocks.push(block_text);
|
|
2537
|
+
current_size += current_size > 0 ? added_size : block_size;
|
|
2538
|
+
}
|
|
2539
|
+
flush_current();
|
|
2540
|
+
if (!pages.length) return [[""], [0]];
|
|
2541
|
+
return [pages, page_starts];
|
|
2542
|
+
}
|
|
2543
|
+
function _count_tracked_changes(page_content) {
|
|
2544
|
+
const matches = [...page_content.matchAll(_CHG_ID_PATTERN)];
|
|
2545
|
+
const distinct = new Set(matches.map((m) => m[1]));
|
|
2546
|
+
return distinct.size;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2255
2549
|
// src/markup.ts
|
|
2256
2550
|
var AMBIGUITY_EXAMPLES_CAP = 5;
|
|
2257
2551
|
var AMBIGUITY_CONTEXT_CHARS = 50;
|
|
@@ -2731,7 +3025,8 @@ var RedlineEngine = class {
|
|
|
2731
3025
|
start_idx + length,
|
|
2732
3026
|
start_idx + length + 30
|
|
2733
3027
|
);
|
|
2734
|
-
const
|
|
3028
|
+
const insertion = new_text ? `{++${new_text}++}` : "";
|
|
3029
|
+
const critic_markup = `${context_before}{--${target_text}--}${insertion}${context_after}`;
|
|
2735
3030
|
let clean_text = critic_markup;
|
|
2736
3031
|
clean_text = clean_text.replace(/\{>>.*?<<\}/gs, "");
|
|
2737
3032
|
clean_text = clean_text.replace(/\{--.*?--\}/gs, "");
|
|
@@ -2749,6 +3044,37 @@ var RedlineEngine = class {
|
|
|
2749
3044
|
}
|
|
2750
3045
|
return maxId;
|
|
2751
3046
|
}
|
|
3047
|
+
_get_heading_path_and_page(start_idx, text, page_offsets) {
|
|
3048
|
+
let page = 1;
|
|
3049
|
+
for (let i = 0; i < page_offsets.length; i++) {
|
|
3050
|
+
if (start_idx >= page_offsets[i]) {
|
|
3051
|
+
page = i + 1;
|
|
3052
|
+
} else {
|
|
3053
|
+
break;
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
const textBefore = text.substring(0, start_idx);
|
|
3057
|
+
const lines = textBefore.split("\n");
|
|
3058
|
+
const path = [];
|
|
3059
|
+
let current_level = 999;
|
|
3060
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3061
|
+
const line = lines[i];
|
|
3062
|
+
const m = line.match(/^(#{1,6})\s+(.*)/);
|
|
3063
|
+
if (m) {
|
|
3064
|
+
const level = m[1].length;
|
|
3065
|
+
if (level < current_level) {
|
|
3066
|
+
let cleanHeading = m[2].replace(/\*\*|__|[*_]/g, "").replace(/\{#[^}]+\}/g, "").trim();
|
|
3067
|
+
if (cleanHeading.length > 80) {
|
|
3068
|
+
cleanHeading = cleanHeading.substring(0, 80) + "...";
|
|
3069
|
+
}
|
|
3070
|
+
path.unshift(cleanHeading);
|
|
3071
|
+
current_level = level;
|
|
3072
|
+
if (level === 1) break;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
return [path.join(" > "), page];
|
|
3077
|
+
}
|
|
2752
3078
|
accept_all_revisions() {
|
|
2753
3079
|
const parts_to_process = [this.doc.element];
|
|
2754
3080
|
for (const part of this.doc.pkg.parts) {
|
|
@@ -3447,13 +3773,15 @@ var RedlineEngine = class {
|
|
|
3447
3773
|
for (let i = 0; i < edits.length; i++) {
|
|
3448
3774
|
const edit = edits[i];
|
|
3449
3775
|
if (!edit.target_text) continue;
|
|
3450
|
-
|
|
3776
|
+
const is_regex = edit.regex || false;
|
|
3777
|
+
const match_mode = edit.match_mode || "strict";
|
|
3778
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
3451
3779
|
let activeText = this.mapper.full_text;
|
|
3452
3780
|
let target_mapper = this.mapper;
|
|
3453
3781
|
if (matches.length === 0) {
|
|
3454
3782
|
if (!this.clean_mapper)
|
|
3455
3783
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
3456
|
-
matches = this.clean_mapper.find_all_match_indices(edit.target_text);
|
|
3784
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
3457
3785
|
if (matches.length > 0) {
|
|
3458
3786
|
activeText = this.clean_mapper.full_text;
|
|
3459
3787
|
target_mapper = this.clean_mapper;
|
|
@@ -3475,7 +3803,7 @@ var RedlineEngine = class {
|
|
|
3475
3803
|
if (!this.original_mapper) {
|
|
3476
3804
|
this.original_mapper = new DocumentMapper(this.doc, false, true);
|
|
3477
3805
|
}
|
|
3478
|
-
const orig_matches = this.original_mapper.find_all_match_indices(edit.target_text);
|
|
3806
|
+
const orig_matches = this.original_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
3479
3807
|
if (orig_matches.length > 0) {
|
|
3480
3808
|
is_deleted_text = true;
|
|
3481
3809
|
for (const [start, length] of orig_matches) {
|
|
@@ -3512,7 +3840,7 @@ var RedlineEngine = class {
|
|
|
3512
3840
|
"${edit.target_text}"`
|
|
3513
3841
|
);
|
|
3514
3842
|
}
|
|
3515
|
-
} else if (matches.length > 1) {
|
|
3843
|
+
} else if (matches.length > 1 && match_mode === "strict") {
|
|
3516
3844
|
const positions = matches.map(([start, length]) => [
|
|
3517
3845
|
start,
|
|
3518
3846
|
start + length
|
|
@@ -3570,6 +3898,14 @@ var RedlineEngine = class {
|
|
|
3570
3898
|
if (auth && auth !== this.author) nestedAuthors.add(auth);
|
|
3571
3899
|
}
|
|
3572
3900
|
}
|
|
3901
|
+
if (s.comment_ids) {
|
|
3902
|
+
for (const cid of s.comment_ids) {
|
|
3903
|
+
const c_data = this.mapper.comments_map[cid];
|
|
3904
|
+
if (c_data && c_data.author && c_data.author !== this.author) {
|
|
3905
|
+
nestedAuthors.add(c_data.author);
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3573
3909
|
}
|
|
3574
3910
|
if (nestedAuthors.size > 0) {
|
|
3575
3911
|
errors.push(
|
|
@@ -3700,6 +4036,9 @@ var RedlineEngine = class {
|
|
|
3700
4036
|
if (this.clean_mapper) this.clean_mapper["_build_map"]();
|
|
3701
4037
|
}
|
|
3702
4038
|
}
|
|
4039
|
+
const [body_text] = split_structural_appendix(this.mapper.full_text);
|
|
4040
|
+
const pag_res = paginate(body_text, "");
|
|
4041
|
+
const page_offsets = pag_res.body_page_offsets;
|
|
3703
4042
|
const edits_reports = [];
|
|
3704
4043
|
let applied_edits = 0;
|
|
3705
4044
|
let skipped_edits = 0;
|
|
@@ -3723,9 +4062,8 @@ var RedlineEngine = class {
|
|
|
3723
4062
|
});
|
|
3724
4063
|
continue;
|
|
3725
4064
|
}
|
|
3726
|
-
const res = this.apply_edits([edit]);
|
|
3727
|
-
|
|
3728
|
-
if (applied > 0) {
|
|
4065
|
+
const res = this.apply_edits([edit], page_offsets);
|
|
4066
|
+
if (edit._applied_status) {
|
|
3729
4067
|
applied_edits++;
|
|
3730
4068
|
const previews = this._build_edit_context_previews(edit);
|
|
3731
4069
|
edits_reports.push({
|
|
@@ -3735,7 +4073,11 @@ var RedlineEngine = class {
|
|
|
3735
4073
|
warning,
|
|
3736
4074
|
error: null,
|
|
3737
4075
|
critic_markup: previews[0],
|
|
3738
|
-
clean_text: previews[1]
|
|
4076
|
+
clean_text: previews[1],
|
|
4077
|
+
pages: edit._pages || [],
|
|
4078
|
+
heading_path: edit._heading_path || "",
|
|
4079
|
+
occurrences_modified: edit._occurrences_modified || 0,
|
|
4080
|
+
match_mode: edit.match_mode || "strict"
|
|
3739
4081
|
});
|
|
3740
4082
|
} else {
|
|
3741
4083
|
skipped_edits++;
|
|
@@ -3757,9 +4099,9 @@ var RedlineEngine = class {
|
|
|
3757
4099
|
throw new BatchValidationError(errors);
|
|
3758
4100
|
}
|
|
3759
4101
|
const cloned_edits = edits.map((e) => JSON.parse(JSON.stringify(e)));
|
|
3760
|
-
const res = this.apply_edits(cloned_edits);
|
|
3761
|
-
applied_edits =
|
|
3762
|
-
skipped_edits =
|
|
4102
|
+
const res = this.apply_edits(cloned_edits, page_offsets);
|
|
4103
|
+
applied_edits = cloned_edits.filter((e) => e._applied_status).length;
|
|
4104
|
+
skipped_edits = cloned_edits.length - applied_edits;
|
|
3763
4105
|
for (const edit of cloned_edits) {
|
|
3764
4106
|
const success = edit._applied_status || false;
|
|
3765
4107
|
const error_msg = edit._error_msg || null;
|
|
@@ -3780,7 +4122,11 @@ var RedlineEngine = class {
|
|
|
3780
4122
|
warning,
|
|
3781
4123
|
error: error_msg,
|
|
3782
4124
|
critic_markup,
|
|
3783
|
-
clean_text
|
|
4125
|
+
clean_text,
|
|
4126
|
+
pages: edit._pages || [],
|
|
4127
|
+
heading_path: edit._heading_path || "",
|
|
4128
|
+
occurrences_modified: edit._occurrences_modified || 0,
|
|
4129
|
+
match_mode: edit.match_mode || "strict"
|
|
3784
4130
|
});
|
|
3785
4131
|
}
|
|
3786
4132
|
}
|
|
@@ -3796,9 +4142,13 @@ var RedlineEngine = class {
|
|
|
3796
4142
|
version: "1.10.0"
|
|
3797
4143
|
};
|
|
3798
4144
|
}
|
|
3799
|
-
apply_edits(edits) {
|
|
4145
|
+
apply_edits(edits, page_offsets = []) {
|
|
3800
4146
|
let applied = 0;
|
|
3801
4147
|
let skipped = 0;
|
|
4148
|
+
if (!page_offsets || page_offsets.length === 0) {
|
|
4149
|
+
const [body_text] = split_structural_appendix(this.mapper.full_text);
|
|
4150
|
+
page_offsets = paginate(body_text, "").body_page_offsets;
|
|
4151
|
+
}
|
|
3802
4152
|
const resolved_edits = [];
|
|
3803
4153
|
for (const edit of edits) {
|
|
3804
4154
|
edit._applied_status = false;
|
|
@@ -3900,6 +4250,19 @@ var RedlineEngine = class {
|
|
|
3900
4250
|
const parent = edit._parent_edit_ref;
|
|
3901
4251
|
if (parent) {
|
|
3902
4252
|
parent._applied_status = true;
|
|
4253
|
+
parent._occurrences_modified = (parent._occurrences_modified || 0) + 1;
|
|
4254
|
+
const [path, page] = this._get_heading_path_and_page(start, this.mapper.full_text, page_offsets);
|
|
4255
|
+
const pages = parent._pages || [];
|
|
4256
|
+
if (!pages.includes(page)) pages.unshift(page);
|
|
4257
|
+
parent._pages = pages;
|
|
4258
|
+
parent._heading_path = path;
|
|
4259
|
+
} else {
|
|
4260
|
+
edit._occurrences_modified = (edit._occurrences_modified || 0) + 1;
|
|
4261
|
+
const [path, page] = this._get_heading_path_and_page(start, this.mapper.full_text, page_offsets);
|
|
4262
|
+
const pages = edit._pages || [];
|
|
4263
|
+
if (!pages.includes(page)) pages.unshift(page);
|
|
4264
|
+
edit._pages = pages;
|
|
4265
|
+
edit._heading_path = path;
|
|
3903
4266
|
}
|
|
3904
4267
|
} else {
|
|
3905
4268
|
skipped++;
|
|
@@ -4052,96 +4415,192 @@ var RedlineEngine = class {
|
|
|
4052
4415
|
}
|
|
4053
4416
|
return false;
|
|
4054
4417
|
}
|
|
4055
|
-
/**
|
|
4056
|
-
* Returns the first match of `target_text` in the raw mapper that is NOT
|
|
4057
|
-
* entirely contained within a tracked deletion (<w:del>). Tracked-deleted
|
|
4058
|
-
* copies are not live, editable text, so an edit must resolve to a live
|
|
4059
|
-
* occurrence even when a dead copy appears earlier in the document
|
|
4060
|
-
* (BUG-23-5). Falls back to the plain first match when no live copy is
|
|
4061
|
-
* found (e.g. fuzzy/normalized matches the span filter cannot align).
|
|
4062
|
-
*/
|
|
4063
|
-
_first_live_match(target_text) {
|
|
4064
|
-
const all = this.mapper.find_all_match_indices(target_text);
|
|
4065
|
-
if (all.length <= 1) {
|
|
4066
|
-
return this.mapper.find_match_index(target_text);
|
|
4067
|
-
}
|
|
4068
|
-
for (const [start, length] of all) {
|
|
4069
|
-
const realSpans = this.mapper.spans.filter(
|
|
4070
|
-
(s) => s.run !== null && s.end > start && s.start < start + length
|
|
4071
|
-
);
|
|
4072
|
-
if (realSpans.length === 0) return [start, length];
|
|
4073
|
-
if (realSpans.some((s) => !s.del_id)) return [start, length];
|
|
4074
|
-
}
|
|
4075
|
-
return this.mapper.find_match_index(target_text);
|
|
4076
|
-
}
|
|
4077
4418
|
_pre_resolve_heuristic_edit(edit) {
|
|
4078
4419
|
if (!edit.target_text) return null;
|
|
4079
|
-
|
|
4420
|
+
const is_regex = edit.regex || false;
|
|
4421
|
+
const match_mode = edit.match_mode || "strict";
|
|
4422
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4080
4423
|
let use_clean_map = false;
|
|
4081
|
-
if (
|
|
4424
|
+
if (matches.length === 0) {
|
|
4082
4425
|
if (!this.clean_mapper)
|
|
4083
4426
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
);
|
|
4087
|
-
if (start_idx !== -1) use_clean_map = true;
|
|
4427
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4428
|
+
if (matches.length > 0) use_clean_map = true;
|
|
4088
4429
|
else return null;
|
|
4089
4430
|
}
|
|
4090
4431
|
const active_mapper = use_clean_map ? this.clean_mapper : this.mapper;
|
|
4091
|
-
|
|
4092
|
-
const
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4432
|
+
let live_matches = [];
|
|
4433
|
+
for (const [s, match_len] of matches) {
|
|
4434
|
+
const realSpans = active_mapper.spans.filter(
|
|
4435
|
+
(span) => span.run !== null && span.end > s && span.start < s + match_len
|
|
4436
|
+
);
|
|
4437
|
+
if (realSpans.length === 0 || realSpans.some((span) => !span.del_id)) {
|
|
4438
|
+
live_matches.push([s, match_len]);
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
if (live_matches.length === 0) return null;
|
|
4442
|
+
if (match_mode === "strict" || match_mode === "first") {
|
|
4443
|
+
live_matches = live_matches.slice(0, 1);
|
|
4444
|
+
}
|
|
4445
|
+
const all_sub_edits = [];
|
|
4446
|
+
for (const [start_idx, match_len] of live_matches) {
|
|
4447
|
+
const actual_doc_text = active_mapper.full_text.substring(
|
|
4448
|
+
start_idx,
|
|
4449
|
+
start_idx + match_len
|
|
4450
|
+
);
|
|
4451
|
+
let current_effective_new_text = edit.new_text || "";
|
|
4452
|
+
if (is_regex && current_effective_new_text) {
|
|
4453
|
+
try {
|
|
4454
|
+
current_effective_new_text = actual_doc_text.replace(new RegExp(edit.target_text), current_effective_new_text);
|
|
4455
|
+
} catch (e) {
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
const [edit_target_clean, edit_target_style] = this._parse_markdown_style(edit.target_text);
|
|
4459
|
+
const [edit_new_clean, edit_new_style] = this._parse_markdown_style(current_effective_new_text);
|
|
4460
|
+
if (edit_target_style !== edit_new_style) {
|
|
4461
|
+
const [actual_clean] = this._parse_markdown_style(actual_doc_text);
|
|
4462
|
+
const final_target2 = actual_clean;
|
|
4463
|
+
const final_new2 = edit_new_clean;
|
|
4464
|
+
const style_op = final_target2 === final_new2 ? "STYLE_ONLY" : "STYLE_AND_TEXT";
|
|
4465
|
+
const prefix_offset = actual_doc_text.indexOf(actual_clean);
|
|
4466
|
+
const effective_start_idx2 = start_idx + (prefix_offset !== -1 ? prefix_offset : 0);
|
|
4467
|
+
const resolved_style = edit_new_style !== null ? edit_new_style : "Normal";
|
|
4468
|
+
all_sub_edits.push({
|
|
4469
|
+
type: "modify",
|
|
4470
|
+
target_text: final_target2,
|
|
4471
|
+
new_text: final_new2,
|
|
4472
|
+
comment: edit.comment,
|
|
4473
|
+
_match_start_index: effective_start_idx2,
|
|
4474
|
+
_internal_op: style_op,
|
|
4475
|
+
_new_style: resolved_style,
|
|
4476
|
+
_active_mapper_ref: active_mapper
|
|
4477
|
+
});
|
|
4478
|
+
continue;
|
|
4479
|
+
}
|
|
4480
|
+
if (actual_doc_text === current_effective_new_text || edit.target_text === current_effective_new_text) {
|
|
4481
|
+
all_sub_edits.push({
|
|
4482
|
+
type: "modify",
|
|
4483
|
+
target_text: actual_doc_text,
|
|
4484
|
+
new_text: actual_doc_text,
|
|
4485
|
+
comment: edit.comment,
|
|
4486
|
+
_match_start_index: start_idx,
|
|
4487
|
+
_internal_op: "COMMENT_ONLY",
|
|
4488
|
+
_active_mapper_ref: active_mapper
|
|
4489
|
+
});
|
|
4490
|
+
continue;
|
|
4491
|
+
}
|
|
4492
|
+
let effective_op = "";
|
|
4493
|
+
let final_target = "";
|
|
4494
|
+
let final_new = "";
|
|
4495
|
+
let effective_start_idx = start_idx;
|
|
4496
|
+
if (current_effective_new_text.startsWith(actual_doc_text)) {
|
|
4497
|
+
effective_op = "INSERTION";
|
|
4498
|
+
final_new = current_effective_new_text.substring(actual_doc_text.length);
|
|
4499
|
+
effective_start_idx = start_idx + match_len;
|
|
4500
|
+
} else {
|
|
4501
|
+
const [prefix_len, suffix_len] = trim_common_context(
|
|
4502
|
+
actual_doc_text,
|
|
4503
|
+
current_effective_new_text
|
|
4504
|
+
);
|
|
4505
|
+
const t_end = actual_doc_text.length - suffix_len;
|
|
4506
|
+
const n_end = current_effective_new_text.length - suffix_len;
|
|
4507
|
+
final_target = actual_doc_text.substring(prefix_len, t_end);
|
|
4508
|
+
final_new = current_effective_new_text.substring(prefix_len, n_end);
|
|
4509
|
+
effective_start_idx = start_idx + prefix_len;
|
|
4510
|
+
if (!final_target && final_new) effective_op = "INSERTION";
|
|
4511
|
+
else if (final_target && !final_new) effective_op = "DELETION";
|
|
4512
|
+
else if (final_target && final_new) effective_op = "MODIFICATION";
|
|
4513
|
+
else effective_op = "COMMENT_ONLY";
|
|
4514
|
+
}
|
|
4515
|
+
all_sub_edits.push({
|
|
4098
4516
|
type: "modify",
|
|
4099
|
-
target_text:
|
|
4100
|
-
new_text:
|
|
4517
|
+
target_text: final_target,
|
|
4518
|
+
new_text: final_new,
|
|
4101
4519
|
comment: edit.comment,
|
|
4102
|
-
_match_start_index:
|
|
4103
|
-
_internal_op:
|
|
4520
|
+
_match_start_index: effective_start_idx,
|
|
4521
|
+
_internal_op: effective_op,
|
|
4104
4522
|
_active_mapper_ref: active_mapper
|
|
4105
|
-
};
|
|
4523
|
+
});
|
|
4106
4524
|
}
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
let effective_start_idx = start_idx;
|
|
4111
|
-
if (effective_new_text.startsWith(actual_doc_text)) {
|
|
4112
|
-
effective_op = "INSERTION";
|
|
4113
|
-
final_new = effective_new_text.substring(actual_doc_text.length);
|
|
4114
|
-
effective_start_idx = start_idx + match_len;
|
|
4115
|
-
} else {
|
|
4116
|
-
const [prefix_len, suffix_len] = trim_common_context(
|
|
4117
|
-
actual_doc_text,
|
|
4118
|
-
effective_new_text
|
|
4119
|
-
);
|
|
4120
|
-
const t_end = actual_doc_text.length - suffix_len;
|
|
4121
|
-
const n_end = effective_new_text.length - suffix_len;
|
|
4122
|
-
final_target = actual_doc_text.substring(prefix_len, t_end);
|
|
4123
|
-
final_new = effective_new_text.substring(prefix_len, n_end);
|
|
4124
|
-
effective_start_idx = start_idx + prefix_len;
|
|
4125
|
-
if (!final_target && final_new) effective_op = "INSERTION";
|
|
4126
|
-
else if (final_target && !final_new) effective_op = "DELETION";
|
|
4127
|
-
else if (final_target && final_new) effective_op = "MODIFICATION";
|
|
4128
|
-
else effective_op = "COMMENT_ONLY";
|
|
4129
|
-
}
|
|
4130
|
-
return {
|
|
4131
|
-
type: "modify",
|
|
4132
|
-
target_text: final_target,
|
|
4133
|
-
new_text: final_new,
|
|
4134
|
-
comment: edit.comment,
|
|
4135
|
-
_match_start_index: effective_start_idx,
|
|
4136
|
-
_internal_op: effective_op,
|
|
4137
|
-
_active_mapper_ref: active_mapper
|
|
4138
|
-
};
|
|
4525
|
+
if (all_sub_edits.length === 0) return null;
|
|
4526
|
+
if (match_mode === "all" || all_sub_edits.length > 1) return all_sub_edits;
|
|
4527
|
+
return all_sub_edits[0];
|
|
4139
4528
|
}
|
|
4140
4529
|
_apply_single_edit_indexed(edit, orig_new, rebuild_map) {
|
|
4141
4530
|
let op = edit._internal_op;
|
|
4142
4531
|
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
4143
4532
|
const start_idx = edit._resolved_start_idx !== void 0 && edit._resolved_start_idx !== null ? edit._resolved_start_idx : edit._match_start_index || 0;
|
|
4144
4533
|
const length = edit.target_text ? edit.target_text.length : 0;
|
|
4534
|
+
if (op === "STYLE_ONLY" || op === "STYLE_AND_TEXT") {
|
|
4535
|
+
const [anchor_run, anchor_para] = active_mapper.get_insertion_anchor(
|
|
4536
|
+
start_idx,
|
|
4537
|
+
rebuild_map
|
|
4538
|
+
);
|
|
4539
|
+
let target_para_el = null;
|
|
4540
|
+
if (anchor_para) {
|
|
4541
|
+
target_para_el = anchor_para._element;
|
|
4542
|
+
} else if (anchor_run) {
|
|
4543
|
+
let walker = anchor_run._element;
|
|
4544
|
+
while (walker && walker.tagName !== "w:p") {
|
|
4545
|
+
walker = walker.parentNode;
|
|
4546
|
+
}
|
|
4547
|
+
target_para_el = walker;
|
|
4548
|
+
}
|
|
4549
|
+
if (target_para_el && edit._new_style) {
|
|
4550
|
+
this._set_paragraph_style(target_para_el, edit._new_style);
|
|
4551
|
+
}
|
|
4552
|
+
if (op === "STYLE_ONLY") {
|
|
4553
|
+
if (edit.comment) {
|
|
4554
|
+
const target_runs2 = active_mapper.find_target_runs_by_index(
|
|
4555
|
+
start_idx,
|
|
4556
|
+
length,
|
|
4557
|
+
rebuild_map
|
|
4558
|
+
);
|
|
4559
|
+
if (target_runs2.length > 0) {
|
|
4560
|
+
const first_el = target_runs2[0]._element;
|
|
4561
|
+
const last_el = target_runs2[target_runs2.length - 1]._element;
|
|
4562
|
+
let start_p = first_el;
|
|
4563
|
+
while (start_p && start_p.tagName !== "w:p")
|
|
4564
|
+
start_p = start_p.parentNode;
|
|
4565
|
+
let end_p = last_el;
|
|
4566
|
+
while (end_p && end_p.tagName !== "w:p")
|
|
4567
|
+
end_p = end_p.parentNode;
|
|
4568
|
+
if (start_p && end_p) {
|
|
4569
|
+
const ascend_to_paragraph_child = (el, p) => {
|
|
4570
|
+
let cur = el;
|
|
4571
|
+
while (cur.parentNode && cur.parentNode !== p) {
|
|
4572
|
+
cur = cur.parentNode;
|
|
4573
|
+
}
|
|
4574
|
+
return cur;
|
|
4575
|
+
};
|
|
4576
|
+
const first_anchor = ascend_to_paragraph_child(first_el, start_p);
|
|
4577
|
+
const last_anchor = ascend_to_paragraph_child(last_el, end_p);
|
|
4578
|
+
if (start_p === end_p) {
|
|
4579
|
+
this._attach_comment(start_p, first_anchor, last_anchor, edit.comment);
|
|
4580
|
+
} else {
|
|
4581
|
+
this._attach_comment_spanning(
|
|
4582
|
+
start_p,
|
|
4583
|
+
first_anchor,
|
|
4584
|
+
end_p,
|
|
4585
|
+
last_anchor,
|
|
4586
|
+
edit.comment
|
|
4587
|
+
);
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
return true;
|
|
4593
|
+
}
|
|
4594
|
+
if (edit.target_text && edit.new_text) {
|
|
4595
|
+
op = "MODIFICATION";
|
|
4596
|
+
} else if (!edit.target_text && edit.new_text) {
|
|
4597
|
+
op = "INSERTION";
|
|
4598
|
+
} else if (edit.target_text && !edit.new_text) {
|
|
4599
|
+
op = "DELETION";
|
|
4600
|
+
} else {
|
|
4601
|
+
op = "COMMENT_ONLY";
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4145
4604
|
const del_id = ["DELETION", "MODIFICATION"].includes(op) ? this._getNextId() : null;
|
|
4146
4605
|
const ins_id = ["INSERTION", "MODIFICATION"].includes(op) ? this._getNextId() : null;
|
|
4147
4606
|
if (op === "COMMENT_ONLY") {
|
|
@@ -4524,1863 +4983,1814 @@ var RedlineEngine = class {
|
|
|
4524
4983
|
}
|
|
4525
4984
|
};
|
|
4526
4985
|
|
|
4527
|
-
// src/
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
}
|
|
4536
|
-
var _CHG_ID_PATTERN = /\bChg:(\d+)\b/g;
|
|
4537
|
-
function split_structural_appendix(markdown) {
|
|
4538
|
-
if (!markdown) return ["", ""];
|
|
4539
|
-
const idx = markdown.indexOf(APPENDIX_MARKER);
|
|
4540
|
-
if (idx === -1) return [markdown, ""];
|
|
4541
|
-
const line_start = markdown.lastIndexOf("\n", idx) + 1;
|
|
4542
|
-
const body = markdown.substring(0, line_start).trimEnd();
|
|
4543
|
-
const appendix = markdown.substring(line_start);
|
|
4544
|
-
return [body, appendix];
|
|
4545
|
-
}
|
|
4546
|
-
function paginate(markdown_body, structural_appendix = "") {
|
|
4547
|
-
if (!markdown_body) {
|
|
4548
|
-
const appendix_clean = structural_appendix ? structural_appendix.trim() : "";
|
|
4549
|
-
const content = appendix_clean;
|
|
4550
|
-
return {
|
|
4551
|
-
pages: [{
|
|
4552
|
-
page: 1,
|
|
4553
|
-
total_pages: 1,
|
|
4554
|
-
has_next: false,
|
|
4555
|
-
has_prev: false,
|
|
4556
|
-
tracked_change_count: _count_tracked_changes(content),
|
|
4557
|
-
page_content: content
|
|
4558
|
-
}],
|
|
4559
|
-
total_pages: 1,
|
|
4560
|
-
body_pages: [""],
|
|
4561
|
-
body_page_offsets: [0]
|
|
4562
|
-
};
|
|
4563
|
-
}
|
|
4564
|
-
const block_records = _tokenize_into_atomic_blocks(markdown_body);
|
|
4565
|
-
const [body_pages, body_page_offsets] = _assemble_pages(block_records);
|
|
4566
|
-
let final_pages;
|
|
4567
|
-
if (structural_appendix && structural_appendix.trim()) {
|
|
4568
|
-
const appendix = structural_appendix.trim();
|
|
4569
|
-
final_pages = body_pages.map((bp) => bp ? `${bp}
|
|
4570
|
-
|
|
4571
|
-
${appendix}` : appendix);
|
|
4572
|
-
} else {
|
|
4573
|
-
final_pages = [...body_pages];
|
|
4986
|
+
// src/sanitize/transforms.ts
|
|
4987
|
+
function findDescendantsByLocalName(element, localName) {
|
|
4988
|
+
const result = [];
|
|
4989
|
+
const all = element.getElementsByTagName("*");
|
|
4990
|
+
for (let i = 0; i < all.length; i++) {
|
|
4991
|
+
const tag = all[i].tagName;
|
|
4992
|
+
if (tag === localName || tag.endsWith(":" + localName)) {
|
|
4993
|
+
result.push(all[i]);
|
|
4994
|
+
}
|
|
4574
4995
|
}
|
|
4575
|
-
|
|
4576
|
-
const page_infos = final_pages.map((content, i) => ({
|
|
4577
|
-
page: i + 1,
|
|
4578
|
-
total_pages: total,
|
|
4579
|
-
has_next: i + 1 < total,
|
|
4580
|
-
has_prev: i + 1 > 1,
|
|
4581
|
-
tracked_change_count: _count_tracked_changes(content),
|
|
4582
|
-
page_content: content
|
|
4583
|
-
}));
|
|
4584
|
-
return {
|
|
4585
|
-
pages: page_infos,
|
|
4586
|
-
total_pages: total,
|
|
4587
|
-
body_pages,
|
|
4588
|
-
body_page_offsets
|
|
4589
|
-
};
|
|
4590
|
-
}
|
|
4591
|
-
function _tokenize_into_atomic_blocks(markdown_body) {
|
|
4592
|
-
const raw_blocks = _split_on_safe_paragraph_breaks(markdown_body);
|
|
4593
|
-
return _merge_footnote_sections(raw_blocks);
|
|
4996
|
+
return result;
|
|
4594
4997
|
}
|
|
4595
|
-
function
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4998
|
+
function coalesce_runs(doc) {
|
|
4999
|
+
let count = 0;
|
|
5000
|
+
function areRunsIdentical(rPr1, rPr2) {
|
|
5001
|
+
const xml1 = rPr1 ? rPr1.toString() : "";
|
|
5002
|
+
const xml2 = rPr2 ? rPr2.toString() : "";
|
|
5003
|
+
return xml1 === xml2;
|
|
5004
|
+
}
|
|
5005
|
+
function hasSpecialContent(run) {
|
|
5006
|
+
const safeTags = ["w:t", "w:tab", "w:br", "w:cr", "w:delText", "w:rPr"];
|
|
5007
|
+
for (let i = 0; i < run.childNodes.length; i++) {
|
|
5008
|
+
const child = run.childNodes[i];
|
|
5009
|
+
if (child.nodeType === 1) {
|
|
5010
|
+
const tag = child.tagName;
|
|
5011
|
+
if (!safeTags.includes(tag)) return true;
|
|
4609
5012
|
}
|
|
4610
5013
|
}
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
5014
|
+
return false;
|
|
5015
|
+
}
|
|
5016
|
+
function coalesceContainer(container) {
|
|
5017
|
+
const children = Array.from(container.childNodes).filter((n) => n.nodeType === 1);
|
|
5018
|
+
let i = 0;
|
|
5019
|
+
while (i < children.length - 1) {
|
|
5020
|
+
const curr = children[i];
|
|
5021
|
+
const nxt = children[i + 1];
|
|
5022
|
+
if (curr.tagName === "w:r" && nxt.tagName === "w:r") {
|
|
5023
|
+
if (!hasSpecialContent(curr) && !hasSpecialContent(nxt)) {
|
|
5024
|
+
const rPr1 = findChild(curr, "w:rPr");
|
|
5025
|
+
const rPr2 = findChild(nxt, "w:rPr");
|
|
5026
|
+
if (areRunsIdentical(rPr1, rPr2)) {
|
|
5027
|
+
let last_t = null;
|
|
5028
|
+
for (let c = 0; c < curr.childNodes.length; c++) {
|
|
5029
|
+
const child = curr.childNodes[c];
|
|
5030
|
+
if (child.nodeType === 1 && (child.tagName === "w:t" || child.tagName === "w:delText")) {
|
|
5031
|
+
last_t = child;
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
5034
|
+
const nxtChildren = Array.from(nxt.childNodes).filter((n) => n.nodeType === 1);
|
|
5035
|
+
for (const child of nxtChildren) {
|
|
5036
|
+
if (child.tagName === "w:rPr") continue;
|
|
5037
|
+
if ((child.tagName === "w:t" || child.tagName === "w:delText") && last_t && last_t.tagName === child.tagName) {
|
|
5038
|
+
const t1 = last_t.textContent || "";
|
|
5039
|
+
const t2 = child.textContent || "";
|
|
5040
|
+
const combined = t1 + t2;
|
|
5041
|
+
last_t.textContent = combined;
|
|
5042
|
+
if (combined.trim() !== combined) {
|
|
5043
|
+
last_t.setAttribute("xml:space", "preserve");
|
|
5044
|
+
}
|
|
5045
|
+
} else {
|
|
5046
|
+
curr.appendChild(child);
|
|
5047
|
+
if (child.tagName === "w:t" || child.tagName === "w:delText") {
|
|
5048
|
+
last_t = child;
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
container.removeChild(nxt);
|
|
5053
|
+
children.splice(i + 1, 1);
|
|
5054
|
+
count++;
|
|
5055
|
+
continue;
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
}
|
|
5059
|
+
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(curr.tagName)) {
|
|
5060
|
+
coalesceContainer(curr);
|
|
4619
5061
|
}
|
|
5062
|
+
i++;
|
|
4620
5063
|
}
|
|
4621
|
-
if (
|
|
4622
|
-
|
|
4623
|
-
if (
|
|
4624
|
-
|
|
4625
|
-
if (block_text) blocks.push([block_text, block_start]);
|
|
4626
|
-
let j = i;
|
|
4627
|
-
while (j < n && text[j] === "\n") j++;
|
|
4628
|
-
i = j;
|
|
4629
|
-
block_start = i;
|
|
4630
|
-
continue;
|
|
5064
|
+
if (children.length > 0) {
|
|
5065
|
+
const last = children[children.length - 1];
|
|
5066
|
+
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(last.tagName)) {
|
|
5067
|
+
coalesceContainer(last);
|
|
4631
5068
|
}
|
|
4632
5069
|
}
|
|
4633
|
-
i++;
|
|
4634
|
-
}
|
|
4635
|
-
if (block_start < n) {
|
|
4636
|
-
const block_text = text.substring(block_start, n);
|
|
4637
|
-
if (block_text) blocks.push([block_text, block_start]);
|
|
4638
5070
|
}
|
|
4639
|
-
|
|
5071
|
+
const paragraphs = findAllDescendants(doc.element, "w:p");
|
|
5072
|
+
for (const p of paragraphs) coalesceContainer(p);
|
|
5073
|
+
return count ? [`Adjacent identical runs coalesced: ${count}`] : [];
|
|
4640
5074
|
}
|
|
4641
|
-
function
|
|
4642
|
-
|
|
4643
|
-
const
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
const
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
merged.push([block_text, block_offset]);
|
|
4651
|
-
i++;
|
|
4652
|
-
continue;
|
|
4653
|
-
}
|
|
4654
|
-
let accumulated_text = block_text;
|
|
4655
|
-
let j = i + 1;
|
|
4656
|
-
while (j < blocks.length) {
|
|
4657
|
-
const [next_text] = blocks[j];
|
|
4658
|
-
const next_stripped = next_text.trimStart();
|
|
4659
|
-
if (next_stripped.startsWith("[^fn-") || next_stripped.startsWith("[^en-")) {
|
|
4660
|
-
accumulated_text = `${accumulated_text}
|
|
4661
|
-
|
|
4662
|
-
${next_text}`;
|
|
4663
|
-
j++;
|
|
4664
|
-
} else {
|
|
4665
|
-
break;
|
|
5075
|
+
function strip_rsid(doc) {
|
|
5076
|
+
let count = 0;
|
|
5077
|
+
const rsidAttrs = ["w:rsidR", "w:rsidRPr", "w:rsidRDefault", "w:rsidP", "w:rsidDel", "w:rsidSect", "w:rsidTr"];
|
|
5078
|
+
const all = doc.element.getElementsByTagName("*");
|
|
5079
|
+
for (let i = 0; i < all.length; i++) {
|
|
5080
|
+
for (const attr of rsidAttrs) {
|
|
5081
|
+
if (all[i].hasAttribute(attr)) {
|
|
5082
|
+
all[i].removeAttribute(attr);
|
|
5083
|
+
count++;
|
|
4666
5084
|
}
|
|
4667
5085
|
}
|
|
4668
|
-
merged.push([accumulated_text, block_offset]);
|
|
4669
|
-
i = j;
|
|
4670
5086
|
}
|
|
4671
|
-
|
|
5087
|
+
const rsidsElements = findAllDescendants(doc.element, "w:rsids");
|
|
5088
|
+
for (const el of rsidsElements) {
|
|
5089
|
+
if (el.parentNode) {
|
|
5090
|
+
el.parentNode.removeChild(el);
|
|
5091
|
+
count++;
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
return count ? [`rsid attributes: ${count} removed`] : [];
|
|
4672
5095
|
}
|
|
4673
|
-
function
|
|
4674
|
-
|
|
4675
|
-
const
|
|
4676
|
-
const
|
|
4677
|
-
let
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
page_starts.push(current_start);
|
|
5096
|
+
function strip_para_ids(doc) {
|
|
5097
|
+
let count = 0;
|
|
5098
|
+
const attrs = ["w14:paraId", "w14:textId"];
|
|
5099
|
+
const all = doc.element.getElementsByTagName("*");
|
|
5100
|
+
for (let i = 0; i < all.length; i++) {
|
|
5101
|
+
for (const attr of attrs) {
|
|
5102
|
+
if (all[i].hasAttribute(attr)) {
|
|
5103
|
+
all[i].removeAttribute(attr);
|
|
5104
|
+
count++;
|
|
5105
|
+
}
|
|
4684
5106
|
}
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
5107
|
+
}
|
|
5108
|
+
return count ? [`Paragraph/text IDs: ${count} removed`] : [];
|
|
5109
|
+
}
|
|
5110
|
+
function strip_proof_errors(doc) {
|
|
5111
|
+
const elements = findAllDescendants(doc.element, "w:proofErr");
|
|
5112
|
+
elements.forEach((el) => el.parentNode?.removeChild(el));
|
|
5113
|
+
return elements.length ? [`Spell check markers: ${elements.length} removed`] : [];
|
|
5114
|
+
}
|
|
5115
|
+
function strip_empty_properties(doc) {
|
|
5116
|
+
let count = 0;
|
|
5117
|
+
for (const tag of ["w:rPr", "w:pPr"]) {
|
|
5118
|
+
const elements = findAllDescendants(doc.element, tag);
|
|
5119
|
+
for (const el of elements) {
|
|
5120
|
+
if (el.childNodes.length === 0 || el.childNodes.length === 1 && el.childNodes[0].nodeType === 3 && !el.childNodes[0].textContent?.trim()) {
|
|
5121
|
+
el.parentNode?.removeChild(el);
|
|
5122
|
+
count++;
|
|
5123
|
+
}
|
|
4694
5124
|
}
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
5125
|
+
}
|
|
5126
|
+
return count ? [`Empty property elements: ${count} removed`] : [];
|
|
5127
|
+
}
|
|
5128
|
+
function strip_hidden_text(doc) {
|
|
5129
|
+
let count = 0;
|
|
5130
|
+
const elements = findAllDescendants(doc.element, "w:rPr");
|
|
5131
|
+
for (const rPr of elements) {
|
|
5132
|
+
if (findChild(rPr, "w:vanish") || findChild(rPr, "w:webHidden")) {
|
|
5133
|
+
const run = rPr.parentNode;
|
|
5134
|
+
if (run && run.tagName === "w:r" && run.parentNode) {
|
|
5135
|
+
run.parentNode.removeChild(run);
|
|
5136
|
+
count++;
|
|
5137
|
+
}
|
|
4699
5138
|
}
|
|
4700
|
-
if (current_blocks.length === 0) current_start = block_offset;
|
|
4701
|
-
current_blocks.push(block_text);
|
|
4702
|
-
current_size += current_size > 0 ? added_size : block_size;
|
|
4703
5139
|
}
|
|
4704
|
-
|
|
4705
|
-
if (!pages.length) return [[""], [0]];
|
|
4706
|
-
return [pages, page_starts];
|
|
5140
|
+
return count ? [`Hidden text runs: ${count} removed`] : [];
|
|
4707
5141
|
}
|
|
4708
|
-
function
|
|
4709
|
-
const
|
|
4710
|
-
const
|
|
4711
|
-
|
|
5142
|
+
function count_tracked_changes(doc) {
|
|
5143
|
+
const ins = findAllDescendants(doc.element, "w:ins").length;
|
|
5144
|
+
const del = findAllDescendants(doc.element, "w:del").length;
|
|
5145
|
+
const fmt = findAllDescendants(doc.element, "w:rPrChange").length + findAllDescendants(doc.element, "w:pPrChange").length + findAllDescendants(doc.element, "w:sectPrChange").length;
|
|
5146
|
+
return [ins, del, fmt];
|
|
4712
5147
|
}
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
if (a.length > b.length) {
|
|
4721
|
-
const temp = a;
|
|
4722
|
-
a = b;
|
|
4723
|
-
b = temp;
|
|
5148
|
+
function get_track_change_authors(doc) {
|
|
5149
|
+
const authors = /* @__PURE__ */ new Set();
|
|
5150
|
+
for (const tag of ["w:ins", "w:del", "w:rPrChange", "w:pPrChange", "w:sectPrChange"]) {
|
|
5151
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5152
|
+
const author = el.getAttribute("w:author");
|
|
5153
|
+
if (author) authors.add(author);
|
|
5154
|
+
}
|
|
4724
5155
|
}
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
5156
|
+
return authors;
|
|
5157
|
+
}
|
|
5158
|
+
function _getElementText(el) {
|
|
5159
|
+
const texts = [];
|
|
5160
|
+
const ts = findAllDescendants(el, "w:t");
|
|
5161
|
+
for (const t of ts) if (t.textContent) texts.push(t.textContent);
|
|
5162
|
+
const dts = findAllDescendants(el, "w:delText");
|
|
5163
|
+
for (const dt of dts) if (dt.textContent) texts.push(dt.textContent);
|
|
5164
|
+
return texts.join("");
|
|
5165
|
+
}
|
|
5166
|
+
function _truncate(text, maxLen = 60) {
|
|
5167
|
+
const clean = text.replace(/\n/g, " ").trim();
|
|
5168
|
+
if (clean.length <= maxLen) return clean;
|
|
5169
|
+
return clean.substring(0, maxLen - 3) + "...";
|
|
5170
|
+
}
|
|
5171
|
+
function accept_all_tracked_changes(doc) {
|
|
5172
|
+
const lines = [];
|
|
5173
|
+
const insEls = findAllDescendants(doc.element, "w:ins");
|
|
5174
|
+
const delEls = findAllDescendants(doc.element, "w:del");
|
|
5175
|
+
for (const ins of insEls) {
|
|
5176
|
+
const text = _getElementText(ins).trim();
|
|
5177
|
+
if (text) lines.push(` Accepted insertion: "${_truncate(text, 60)}"`);
|
|
5178
|
+
}
|
|
5179
|
+
for (const del of delEls) {
|
|
5180
|
+
const text = _getElementText(del).trim();
|
|
5181
|
+
if (text) lines.push(` Accepted deletion of: "${_truncate(text, 60)}"`);
|
|
5182
|
+
}
|
|
5183
|
+
const engine = new RedlineEngine(doc);
|
|
5184
|
+
engine.accept_all_revisions();
|
|
5185
|
+
for (const tag of ["w:rPrChange", "w:pPrChange", "w:sectPrChange"]) {
|
|
5186
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5187
|
+
el.parentNode?.removeChild(el);
|
|
4738
5188
|
}
|
|
4739
|
-
if (minInRow > maxDist) return maxDist + 1;
|
|
4740
|
-
row = newRow;
|
|
4741
5189
|
}
|
|
4742
|
-
|
|
5190
|
+
const total = insEls.length + delEls.length;
|
|
5191
|
+
if (total) {
|
|
5192
|
+
return [`Tracked changes auto-accepted: ${total}`].concat(lines);
|
|
5193
|
+
}
|
|
5194
|
+
return [];
|
|
4743
5195
|
}
|
|
4744
|
-
function
|
|
4745
|
-
|
|
4746
|
-
const
|
|
4747
|
-
|
|
4748
|
-
|
|
5196
|
+
function get_comments_summary(doc) {
|
|
5197
|
+
const data = extract_comments_data(doc.pkg);
|
|
5198
|
+
const comments = [];
|
|
5199
|
+
let openCount = 0;
|
|
5200
|
+
let resolvedCount = 0;
|
|
5201
|
+
for (const [cId, info] of Object.entries(data)) {
|
|
5202
|
+
if (info.resolved) resolvedCount++;
|
|
5203
|
+
else openCount++;
|
|
5204
|
+
comments.push({ id: cId, ...info });
|
|
4749
5205
|
}
|
|
4750
|
-
return
|
|
5206
|
+
return { total: comments.length, open: openCount, resolved: resolvedCount, comments };
|
|
4751
5207
|
}
|
|
4752
|
-
function
|
|
4753
|
-
const
|
|
4754
|
-
const
|
|
4755
|
-
|
|
4756
|
-
const
|
|
4757
|
-
const
|
|
4758
|
-
const
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
const
|
|
4765
|
-
|
|
4766
|
-
const inline_matches = text.matchAll(inline_re);
|
|
4767
|
-
for (const m of inline_matches) {
|
|
4768
|
-
extracted_terms.push(m[1].trim());
|
|
4769
|
-
}
|
|
4770
|
-
for (const term of extracted_terms) {
|
|
4771
|
-
if (definitions[term]) duplicates.add(term);
|
|
4772
|
-
else definitions[term] = { count: 0 };
|
|
5208
|
+
function remove_all_comments(doc) {
|
|
5209
|
+
const data = extract_comments_data(doc.pkg);
|
|
5210
|
+
const keys = Object.keys(data);
|
|
5211
|
+
if (keys.length === 0) return [];
|
|
5212
|
+
const lines = [];
|
|
5213
|
+
const cm = new CommentsManager(doc);
|
|
5214
|
+
for (const [cId, info] of Object.entries(data)) {
|
|
5215
|
+
const status = info.resolved ? "[Resolved]" : "[Open]";
|
|
5216
|
+
lines.push(` ${status} "${_truncate(info.text || "", 60)}" (${info.author || "Unknown"})`);
|
|
5217
|
+
cm.deleteComment(cId);
|
|
5218
|
+
}
|
|
5219
|
+
for (const tag of ["w:commentRangeStart", "w:commentRangeEnd"]) {
|
|
5220
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5221
|
+
el.parentNode?.removeChild(el);
|
|
4773
5222
|
}
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
5223
|
+
}
|
|
5224
|
+
const refs = findAllDescendants(doc.element, "w:commentReference");
|
|
5225
|
+
for (const ref of refs) {
|
|
5226
|
+
const parent = ref.parentNode;
|
|
5227
|
+
if (parent) {
|
|
5228
|
+
if (parent.tagName === "w:r" || parent.tagName.endsWith(":r")) {
|
|
5229
|
+
const nonRprChildren = Array.from(parent.childNodes).filter(
|
|
5230
|
+
(c) => c.nodeType === 1 && c.tagName !== "w:rPr" && c.tagName !== "rPr"
|
|
5231
|
+
);
|
|
5232
|
+
if (nonRprChildren.length <= 1) {
|
|
5233
|
+
parent.parentNode?.removeChild(parent);
|
|
5234
|
+
} else {
|
|
5235
|
+
parent.removeChild(ref);
|
|
4783
5236
|
}
|
|
5237
|
+
} else {
|
|
5238
|
+
parent.removeChild(ref);
|
|
4784
5239
|
}
|
|
4785
|
-
let target = null;
|
|
4786
|
-
if (node.tagName === "w:fldSimple") {
|
|
4787
|
-
const instr = node.getAttribute("w:instr") || "";
|
|
4788
|
-
const parts = instr.trim().split(/\s+/);
|
|
4789
|
-
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
4790
|
-
} else if (node.tagName === "w:instrText") {
|
|
4791
|
-
const instr = node.textContent || "";
|
|
4792
|
-
const parts = instr.trim().split(/\s+/);
|
|
4793
|
-
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
4794
|
-
}
|
|
4795
|
-
if (target) raw_references.push([target, short_text]);
|
|
4796
5240
|
}
|
|
4797
5241
|
}
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
5242
|
+
const resolvedCount = Object.values(data).filter((c) => c.resolved).length;
|
|
5243
|
+
const openCount = Object.values(data).filter((c) => !c.resolved).length;
|
|
5244
|
+
return [`Comments removed: ${keys.length} (${resolvedCount} resolved, ${openCount} open)`].concat(lines);
|
|
5245
|
+
}
|
|
5246
|
+
function eject_comment_parts(doc) {
|
|
5247
|
+
const pkg = doc.pkg;
|
|
5248
|
+
const comment_partnames = /* @__PURE__ */ new Set();
|
|
5249
|
+
for (const part of pkg.parts) {
|
|
5250
|
+
if (part.partname.toLowerCase().includes("comments")) {
|
|
5251
|
+
comment_partnames.add(part.partname);
|
|
5252
|
+
const withSlash = part.partname.startsWith("/") ? part.partname : "/" + part.partname;
|
|
5253
|
+
const withoutSlash = part.partname.startsWith("/") ? part.partname.substring(1) : part.partname;
|
|
5254
|
+
comment_partnames.add(withSlash);
|
|
5255
|
+
comment_partnames.add(withoutSlash);
|
|
4801
5256
|
}
|
|
4802
5257
|
}
|
|
4803
|
-
|
|
4804
|
-
const
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
5258
|
+
if (comment_partnames.size === 0) return;
|
|
5259
|
+
for (const part of pkg.parts) {
|
|
5260
|
+
if (part.partname.endsWith(".rels")) {
|
|
5261
|
+
const rels = findAllDescendants(part._element, "Relationship");
|
|
5262
|
+
const toRemove = [];
|
|
5263
|
+
for (const rel of rels) {
|
|
5264
|
+
const target = rel.getAttribute("Target") || "";
|
|
5265
|
+
if (target.toLowerCase().includes("comments")) {
|
|
5266
|
+
toRemove.push(rel);
|
|
5267
|
+
const sourcePath = part.partname.replace("/_rels/", "/").replace(".rels", "");
|
|
5268
|
+
const sourcePart = pkg.getPartByPath(sourcePath);
|
|
5269
|
+
if (sourcePart) {
|
|
5270
|
+
const relId = rel.getAttribute("Id");
|
|
5271
|
+
if (relId) sourcePart.rels.delete(relId);
|
|
5272
|
+
}
|
|
5273
|
+
}
|
|
5274
|
+
}
|
|
5275
|
+
for (const relEl of toRemove) {
|
|
5276
|
+
relEl.parentNode?.removeChild(relEl);
|
|
4818
5277
|
}
|
|
4819
5278
|
}
|
|
4820
5279
|
}
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
"A",
|
|
4830
|
-
"An",
|
|
4831
|
-
"Any",
|
|
4832
|
-
"All",
|
|
4833
|
-
"Some",
|
|
4834
|
-
"No",
|
|
4835
|
-
"Every",
|
|
4836
|
-
"Each",
|
|
4837
|
-
"As",
|
|
4838
|
-
"In",
|
|
4839
|
-
"Of",
|
|
4840
|
-
"For",
|
|
4841
|
-
"To",
|
|
4842
|
-
"On",
|
|
4843
|
-
"By",
|
|
4844
|
-
"With"
|
|
4845
|
-
]);
|
|
4846
|
-
const all_cap_pattern = /\b[A-Z][a-zA-Z]*(?:\s+[A-Z][a-zA-Z]*)*\b/g;
|
|
4847
|
-
const all_caps = new Set(base_text.match(all_cap_pattern) || []);
|
|
4848
|
-
const valid_terms = new Set(Object.keys(definitions));
|
|
4849
|
-
const terms_by_first_letter = {};
|
|
4850
|
-
for (const term of valid_terms) {
|
|
4851
|
-
const fl = term[0].toLowerCase();
|
|
4852
|
-
if (!terms_by_first_letter[fl]) terms_by_first_letter[fl] = [];
|
|
4853
|
-
terms_by_first_letter[fl].push(term);
|
|
4854
|
-
}
|
|
4855
|
-
const candidates_by_term = {};
|
|
4856
|
-
for (const raw_candidate of all_caps) {
|
|
4857
|
-
let candidate = raw_candidate.trim();
|
|
4858
|
-
const words = candidate.split(/\s+/);
|
|
4859
|
-
while (words.length > 0) {
|
|
4860
|
-
const first = words[0];
|
|
4861
|
-
const title = first.charAt(0).toUpperCase() + first.slice(1).toLowerCase();
|
|
4862
|
-
if (stop_words.has(title)) words.shift();
|
|
4863
|
-
else break;
|
|
4864
|
-
}
|
|
4865
|
-
candidate = words.join(" ");
|
|
4866
|
-
if (candidate.length < 4) continue;
|
|
4867
|
-
if (valid_terms.has(candidate)) continue;
|
|
4868
|
-
const first_letter = candidate[0].toLowerCase();
|
|
4869
|
-
let candidate_terms = terms_by_first_letter[first_letter] || [];
|
|
4870
|
-
if (candidate.length > 5) {
|
|
4871
|
-
for (const [k, v] of Object.entries(terms_by_first_letter)) {
|
|
4872
|
-
if (k !== first_letter) candidate_terms = candidate_terms.concat(v);
|
|
5280
|
+
const ctPart = pkg.getPartByPath("[Content_Types].xml");
|
|
5281
|
+
if (ctPart) {
|
|
5282
|
+
const overrides = findAllDescendants(ctPart._element, "Override");
|
|
5283
|
+
const toRemove = [];
|
|
5284
|
+
for (const override of overrides) {
|
|
5285
|
+
const partName = override.getAttribute("PartName") || "";
|
|
5286
|
+
if (comment_partnames.has(partName) || partName.toLowerCase().includes("comments")) {
|
|
5287
|
+
toRemove.push(override);
|
|
4873
5288
|
}
|
|
4874
5289
|
}
|
|
4875
|
-
for (const
|
|
4876
|
-
|
|
4877
|
-
if (candidate === term + "s" || candidate === term + "es") continue;
|
|
4878
|
-
if (term === candidate + "s" || term === candidate + "es") continue;
|
|
4879
|
-
const dist = boundedLevenshtein(candidate, term, 2);
|
|
4880
|
-
if (dist === 0 || dist > 2) continue;
|
|
4881
|
-
if (term.length <= 5) {
|
|
4882
|
-
if (dist > 1) continue;
|
|
4883
|
-
if (candidate[0].toLowerCase() !== term[0].toLowerCase()) continue;
|
|
4884
|
-
}
|
|
4885
|
-
if (!candidates_by_term[term]) candidates_by_term[term] = [];
|
|
4886
|
-
if (!candidates_by_term[term].includes(candidate)) candidates_by_term[term].push(candidate);
|
|
5290
|
+
for (const overrideEl of toRemove) {
|
|
5291
|
+
overrideEl.parentNode?.removeChild(overrideEl);
|
|
4887
5292
|
}
|
|
4888
5293
|
}
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
function diag_sort_key(msg) {
|
|
4895
|
-
if (msg.startsWith("[Error]")) return 0;
|
|
4896
|
-
if (msg.startsWith("[Warning]")) return 1;
|
|
4897
|
-
return 2;
|
|
5294
|
+
pkg.parts = pkg.parts.filter((p) => !p.partname.toLowerCase().includes("comments"));
|
|
5295
|
+
for (const key of Object.keys(pkg.unzipped)) {
|
|
5296
|
+
if (key.toLowerCase().includes("comments")) {
|
|
5297
|
+
delete pkg.unzipped[key];
|
|
5298
|
+
}
|
|
4898
5299
|
}
|
|
4899
|
-
diagnostics.sort((a, b) => {
|
|
4900
|
-
const keyA = diag_sort_key(a);
|
|
4901
|
-
const keyB = diag_sort_key(b);
|
|
4902
|
-
if (keyA !== keyB) return keyA - keyB;
|
|
4903
|
-
return a.localeCompare(b);
|
|
4904
|
-
});
|
|
4905
|
-
return [definitions, diagnostics, raw_anchors];
|
|
4906
5300
|
}
|
|
4907
|
-
function
|
|
4908
|
-
const
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
if (Object.keys(defs).length > 0) {
|
|
4918
|
-
has_content = true;
|
|
4919
|
-
lines.push("\n## Defined Terms");
|
|
4920
|
-
for (const [term, data] of Object.entries(defs)) {
|
|
4921
|
-
lines.push(`- "${term}" \u2014 used ${data.count} times.`);
|
|
5301
|
+
function replace_comment_authors(doc, newAuthor) {
|
|
5302
|
+
const cm = new CommentsManager(doc);
|
|
5303
|
+
if (!cm.commentsPart) return [];
|
|
5304
|
+
const original = /* @__PURE__ */ new Set();
|
|
5305
|
+
const comments = findAllDescendants(cm.commentsPart._element, "w:comment");
|
|
5306
|
+
for (const c of comments) {
|
|
5307
|
+
const author = c.getAttribute("w:author");
|
|
5308
|
+
if (author) {
|
|
5309
|
+
original.add(author);
|
|
5310
|
+
c.setAttribute("w:author", newAuthor);
|
|
4922
5311
|
}
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
lines.push("\n## Semantic Diagnostics");
|
|
4927
|
-
for (const diag of diagnostics) {
|
|
4928
|
-
lines.push(`- ${diag}`);
|
|
5312
|
+
if (c.hasAttribute("w:initials")) {
|
|
5313
|
+
const initials = newAuthor.split(" ").filter(Boolean).map((p) => p[0]).join("").toUpperCase();
|
|
5314
|
+
c.setAttribute("w:initials", initials);
|
|
4929
5315
|
}
|
|
4930
5316
|
}
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
5317
|
+
return original.size ? [`Comment authors replaced: ${Array.from(original).sort().join(", ")} \u2192 "${newAuthor}"`] : [];
|
|
5318
|
+
}
|
|
5319
|
+
function replace_change_authors(doc, newAuthor) {
|
|
5320
|
+
const original = /* @__PURE__ */ new Set();
|
|
5321
|
+
for (const tag of ["w:ins", "w:del", "w:rPrChange", "w:pPrChange"]) {
|
|
5322
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5323
|
+
const author = el.getAttribute("w:author");
|
|
5324
|
+
if (author) {
|
|
5325
|
+
original.add(author);
|
|
5326
|
+
el.setAttribute("w:author", newAuthor);
|
|
4938
5327
|
}
|
|
4939
5328
|
}
|
|
4940
5329
|
}
|
|
4941
|
-
|
|
4942
|
-
return lines.join("\n");
|
|
4943
|
-
}
|
|
4944
|
-
return "";
|
|
4945
|
-
}
|
|
4946
|
-
|
|
4947
|
-
// src/ingest.ts
|
|
4948
|
-
async function extractTextFromBuffer(buffer, cleanView = false) {
|
|
4949
|
-
const doc = await DocumentObject.load(buffer);
|
|
4950
|
-
return _extractTextFromDoc(doc, cleanView);
|
|
5330
|
+
return original.size ? [`Track change authors replaced: ${Array.from(original).sort().join(", ")} \u2192 "${newAuthor}"`] : [];
|
|
4951
5331
|
}
|
|
4952
|
-
function
|
|
4953
|
-
|
|
4954
|
-
const
|
|
4955
|
-
const
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
if (full_text.length > 0) cursor += 2;
|
|
4962
|
-
full_text.push(part_text);
|
|
4963
|
-
cursor += part_text.length;
|
|
5332
|
+
function normalize_change_dates(doc) {
|
|
5333
|
+
let count = 0;
|
|
5334
|
+
const fixed = "2025-01-01T00:00:00Z";
|
|
5335
|
+
for (const tag of ["w:ins", "w:del", "w:rPrChange", "w:pPrChange"]) {
|
|
5336
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5337
|
+
if (el.hasAttribute("w:date")) {
|
|
5338
|
+
el.setAttribute("w:date", fixed);
|
|
5339
|
+
count++;
|
|
5340
|
+
}
|
|
4964
5341
|
}
|
|
4965
5342
|
}
|
|
4966
|
-
|
|
4967
|
-
if (includeAppendix) {
|
|
4968
|
-
const appendix = build_structural_appendix(doc, base_text);
|
|
4969
|
-
if (appendix) base_text += appendix;
|
|
4970
|
-
}
|
|
4971
|
-
if (return_paragraph_offsets) {
|
|
4972
|
-
return { text: base_text, paragraph_offsets };
|
|
4973
|
-
}
|
|
4974
|
-
return base_text;
|
|
5343
|
+
return count ? [`Track change timestamps: ${count} normalized`] : [];
|
|
4975
5344
|
}
|
|
4976
|
-
function
|
|
4977
|
-
const
|
|
4978
|
-
const
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
const sep = `---
|
|
4986
|
-
${header}`;
|
|
4987
|
-
blocks.push(sep);
|
|
4988
|
-
local_cursor += sep.length;
|
|
4989
|
-
is_first_block = false;
|
|
4990
|
-
}
|
|
4991
|
-
for (const item of iter_block_items(container)) {
|
|
4992
|
-
if (!is_first_block) local_cursor += 2;
|
|
4993
|
-
const block_start = local_cursor;
|
|
4994
|
-
if (item.constructor.name === "FootnoteItem") {
|
|
4995
|
-
const fn_text = _extract_blocks(item, comments_map, cleanView, block_start, paragraph_offsets);
|
|
4996
|
-
if (fn_text) {
|
|
4997
|
-
blocks.push(fn_text);
|
|
4998
|
-
local_cursor = block_start + fn_text.length;
|
|
4999
|
-
is_first_block = false;
|
|
5000
|
-
} else if (!is_first_block) {
|
|
5001
|
-
local_cursor -= 2;
|
|
5002
|
-
}
|
|
5003
|
-
} else if (item instanceof Paragraph) {
|
|
5004
|
-
let prefix = get_paragraph_prefix(item, style_cache, default_pstyle);
|
|
5005
|
-
if (is_first_para && container.constructor.name === "FootnoteItem") {
|
|
5006
|
-
prefix = `[^${container.note_type}-${container.id}]: ` + prefix;
|
|
5345
|
+
function scrub_doc_properties(doc) {
|
|
5346
|
+
const lines = [];
|
|
5347
|
+
const corePart = doc.pkg.getPartByPath("docProps/core.xml");
|
|
5348
|
+
if (corePart) {
|
|
5349
|
+
const creators = findDescendantsByLocalName(corePart._element, "creator");
|
|
5350
|
+
creators.forEach((c) => {
|
|
5351
|
+
if (c.textContent) {
|
|
5352
|
+
lines.push(`Author: ${c.textContent}`);
|
|
5353
|
+
c.textContent = "";
|
|
5007
5354
|
}
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
if (
|
|
5012
|
-
|
|
5355
|
+
});
|
|
5356
|
+
const modifiers = findDescendantsByLocalName(corePart._element, "lastModifiedBy");
|
|
5357
|
+
modifiers.forEach((c) => {
|
|
5358
|
+
if (c.textContent) {
|
|
5359
|
+
lines.push(`Last modified by: ${c.textContent}`);
|
|
5360
|
+
c.textContent = "";
|
|
5013
5361
|
}
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
blocks.push(table_text);
|
|
5021
|
-
local_cursor = block_start + table_text.length;
|
|
5022
|
-
is_first_block = false;
|
|
5023
|
-
} else if (!is_first_block) {
|
|
5024
|
-
local_cursor -= 2;
|
|
5362
|
+
});
|
|
5363
|
+
const revisions = findDescendantsByLocalName(corePart._element, "revision");
|
|
5364
|
+
revisions.forEach((c) => {
|
|
5365
|
+
if (c.textContent && parseInt(c.textContent) > 1) {
|
|
5366
|
+
lines.push(`Revision count: ${c.textContent} \u2192 1`);
|
|
5367
|
+
c.textContent = "1";
|
|
5025
5368
|
}
|
|
5026
|
-
|
|
5369
|
+
});
|
|
5370
|
+
}
|
|
5371
|
+
const appPart = doc.pkg.getPartByPath("docProps/app.xml");
|
|
5372
|
+
if (appPart) {
|
|
5373
|
+
const docEl = appPart._element;
|
|
5374
|
+
const intFields = ["TotalTime", "Words", "Characters", "Paragraphs", "Lines", "CharactersWithSpaces"];
|
|
5375
|
+
for (const f of intFields) {
|
|
5376
|
+
findDescendantsByLocalName(docEl, f).forEach((el) => {
|
|
5377
|
+
if (el.textContent && el.textContent !== "0") {
|
|
5378
|
+
if (f === "TotalTime") lines.push(`Total editing time: ${el.textContent} minutes`);
|
|
5379
|
+
el.textContent = "0";
|
|
5380
|
+
}
|
|
5381
|
+
});
|
|
5382
|
+
}
|
|
5383
|
+
const strFields = ["Template", "Manager", "Company"];
|
|
5384
|
+
for (const f of strFields) {
|
|
5385
|
+
findDescendantsByLocalName(docEl, f).forEach((el) => {
|
|
5386
|
+
if (el.textContent) {
|
|
5387
|
+
lines.push(`${f}: ${el.textContent}`);
|
|
5388
|
+
el.textContent = "";
|
|
5389
|
+
}
|
|
5390
|
+
});
|
|
5027
5391
|
}
|
|
5028
5392
|
}
|
|
5029
|
-
return
|
|
5393
|
+
return lines.length ? ["Metadata scrubbed:", ...lines.map((l) => ` ${l}`)] : [];
|
|
5030
5394
|
}
|
|
5031
|
-
function
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
const
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
const wrapper_prefix_len = !cleanView && ins ? 4 : !cleanView && del_node ? 4 : 0;
|
|
5044
|
-
let cell_cursor = row_start + wrapper_prefix_len;
|
|
5045
|
-
let first_cell = true;
|
|
5046
|
-
for (const cell of row.cells) {
|
|
5047
|
-
if (seen_cells.has(cell)) continue;
|
|
5048
|
-
seen_cells.add(cell);
|
|
5049
|
-
if (!first_cell) cell_cursor += 3;
|
|
5050
|
-
const cell_content = _extract_blocks(cell, comments_map, cleanView, cell_cursor, paragraph_offsets);
|
|
5051
|
-
cell_texts.push(cell_content);
|
|
5052
|
-
cell_cursor += cell_content.length;
|
|
5053
|
-
first_cell = false;
|
|
5395
|
+
function scrub_timestamps(doc) {
|
|
5396
|
+
let modified = false;
|
|
5397
|
+
const epoch = "1970-01-01T00:00:00Z";
|
|
5398
|
+
const corePart = doc.pkg.getPartByPath("docProps/core.xml");
|
|
5399
|
+
if (corePart) {
|
|
5400
|
+
for (const tag of ["created", "modified", "lastPrinted"]) {
|
|
5401
|
+
findDescendantsByLocalName(corePart._element, tag).forEach((el) => {
|
|
5402
|
+
if (el.textContent && el.textContent !== epoch) {
|
|
5403
|
+
el.textContent = epoch;
|
|
5404
|
+
modified = true;
|
|
5405
|
+
}
|
|
5406
|
+
});
|
|
5054
5407
|
}
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5408
|
+
}
|
|
5409
|
+
return modified ? ["Timestamps normalized to epoch"] : [];
|
|
5410
|
+
}
|
|
5411
|
+
function strip_custom_xml(doc) {
|
|
5412
|
+
const customParts = doc.pkg.parts.filter((p) => p.partname.includes("/customXml"));
|
|
5413
|
+
if (customParts.length === 0) return [];
|
|
5414
|
+
const partnames = new Set(customParts.map((p) => p.partname));
|
|
5415
|
+
doc.pkg.parts = doc.pkg.parts.filter((p) => !partnames.has(p.partname));
|
|
5416
|
+
const removeRelationsTo = (relsPart) => {
|
|
5417
|
+
const toRemove = [];
|
|
5418
|
+
for (const rel of findAllDescendants(relsPart._element, "Relationship")) {
|
|
5419
|
+
const target = rel.getAttribute("Target");
|
|
5420
|
+
if (target && target.includes("customXml")) toRemove.push(rel);
|
|
5059
5421
|
}
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5422
|
+
toRemove.forEach((r) => r.parentNode?.removeChild(r));
|
|
5423
|
+
};
|
|
5424
|
+
const rootRels = doc.pkg.getPartByPath("_rels/.rels");
|
|
5425
|
+
if (rootRels) removeRelationsTo(rootRels);
|
|
5426
|
+
const docRels = doc.pkg.getOrCreateRelsPart(doc.part.partname);
|
|
5427
|
+
if (docRels) removeRelationsTo(docRels);
|
|
5428
|
+
for (const sdtPr of findAllDescendants(doc.element, "w:sdtPr")) {
|
|
5429
|
+
findChildren(sdtPr, "w:dataBinding").forEach((b) => sdtPr.removeChild(b));
|
|
5063
5430
|
}
|
|
5064
|
-
return
|
|
5431
|
+
return [`Custom XML parts: ${customParts.length} removed`];
|
|
5065
5432
|
}
|
|
5066
|
-
function
|
|
5067
|
-
|
|
5068
|
-
const
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
const items = Array.from(iter_paragraph_content(paragraph));
|
|
5077
|
-
const is_heading = is_heading_paragraph(paragraph, style_cache, default_pstyle);
|
|
5078
|
-
const native_heading = is_native_heading(paragraph, style_cache, default_pstyle);
|
|
5079
|
-
let leading_strip_active = is_heading;
|
|
5080
|
-
for (let i = 0; i < items.length; i++) {
|
|
5081
|
-
const item = items[i];
|
|
5082
|
-
if (item instanceof Run) {
|
|
5083
|
-
const [prefix, suffix] = get_run_style_markers(item, native_heading);
|
|
5084
|
-
const text = get_run_text(item);
|
|
5085
|
-
if (cleanView && Object.keys(active_del).length > 0) continue;
|
|
5086
|
-
if (leading_strip_active) {
|
|
5087
|
-
if (!text || !text.trim()) continue;
|
|
5088
|
-
leading_strip_active = false;
|
|
5089
|
-
}
|
|
5090
|
-
const seg = apply_formatting_to_segments(text, prefix, suffix);
|
|
5091
|
-
if (seg) {
|
|
5092
|
-
const new_wrappers = cleanView ? ["", ""] : _get_wrappers(active_ins, active_del, active_comments, active_fmt);
|
|
5093
|
-
const new_style = [prefix, suffix];
|
|
5094
|
-
if (pending_text && new_wrappers[0] === current_wrappers[0] && new_wrappers[1] === current_wrappers[1]) {
|
|
5095
|
-
if (new_style[0] === current_style[0] && new_style[1] === current_style[1] && current_style[0] !== "" && pending_text.endsWith(current_style[1]) && seg.startsWith(new_style[0])) {
|
|
5096
|
-
pending_text = pending_text.slice(0, -current_style[1].length) + seg.slice(new_style[0].length);
|
|
5097
|
-
} else {
|
|
5098
|
-
pending_text += seg;
|
|
5099
|
-
}
|
|
5100
|
-
current_style = new_style;
|
|
5101
|
-
} else {
|
|
5102
|
-
if (pending_text) parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5103
|
-
pending_text = seg;
|
|
5104
|
-
current_wrappers = new_wrappers;
|
|
5105
|
-
current_style = new_style;
|
|
5106
|
-
}
|
|
5107
|
-
if (!cleanView) {
|
|
5108
|
-
const has_meta = Object.keys(active_ins).length > 0 || Object.keys(active_del).length > 0 || active_comments.size > 0 || Object.keys(active_fmt).length > 0;
|
|
5109
|
-
if (has_meta) {
|
|
5110
|
-
deferred_meta_states.push([{ ...active_ins }, { ...active_del }, new Set(active_comments), { ...active_fmt }]);
|
|
5111
|
-
}
|
|
5112
|
-
let should_defer = false;
|
|
5113
|
-
const is_redline = Object.keys(active_ins).length > 0 || Object.keys(active_del).length > 0 || Object.keys(active_fmt).length > 0;
|
|
5114
|
-
if (is_redline) {
|
|
5115
|
-
let j = i + 1;
|
|
5116
|
-
let next_is_redline = false;
|
|
5117
|
-
let temp_ins = Object.keys(active_ins).length;
|
|
5118
|
-
let temp_del = Object.keys(active_del).length;
|
|
5119
|
-
let temp_fmt = Object.keys(active_fmt).length;
|
|
5120
|
-
while (j < items.length) {
|
|
5121
|
-
const next_item = items[j];
|
|
5122
|
-
if (next_item instanceof Run) {
|
|
5123
|
-
if (!get_run_text(next_item)) {
|
|
5124
|
-
j++;
|
|
5125
|
-
continue;
|
|
5126
|
-
}
|
|
5127
|
-
if (temp_ins > 0 || temp_del > 0 || temp_fmt > 0) next_is_redline = true;
|
|
5128
|
-
break;
|
|
5129
|
-
} else {
|
|
5130
|
-
const ev = next_item;
|
|
5131
|
-
if (ev.type === "ins_start") temp_ins++;
|
|
5132
|
-
else if (ev.type === "ins_end") temp_ins = Math.max(0, temp_ins - 1);
|
|
5133
|
-
else if (ev.type === "del_start") temp_del++;
|
|
5134
|
-
else if (ev.type === "del_end") temp_del = Math.max(0, temp_del - 1);
|
|
5135
|
-
else if (ev.type === "fmt_start") temp_fmt++;
|
|
5136
|
-
else if (ev.type === "fmt_end") temp_fmt = Math.max(0, temp_fmt - 1);
|
|
5137
|
-
}
|
|
5138
|
-
j++;
|
|
5139
|
-
}
|
|
5140
|
-
if (next_is_redline) should_defer = true;
|
|
5141
|
-
}
|
|
5142
|
-
if (!should_defer && deferred_meta_states.length > 0) {
|
|
5143
|
-
const meta_block = _build_merged_meta_block(deferred_meta_states, comments_map);
|
|
5144
|
-
if (meta_block) {
|
|
5145
|
-
if (pending_text) {
|
|
5146
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5147
|
-
pending_text = "";
|
|
5148
|
-
current_wrappers = ["", ""];
|
|
5149
|
-
current_style = ["", ""];
|
|
5150
|
-
}
|
|
5151
|
-
parts.push(`{>>${meta_block}<<}`);
|
|
5152
|
-
}
|
|
5153
|
-
deferred_meta_states.length = 0;
|
|
5154
|
-
}
|
|
5155
|
-
}
|
|
5156
|
-
}
|
|
5157
|
-
} else {
|
|
5158
|
-
const ev = item;
|
|
5159
|
-
leading_strip_active = false;
|
|
5160
|
-
if (!["ins_start", "ins_end", "del_start", "del_end", "fmt_start", "fmt_end"].includes(ev.type)) {
|
|
5161
|
-
if (pending_text) {
|
|
5162
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5163
|
-
pending_text = "";
|
|
5164
|
-
current_wrappers = ["", ""];
|
|
5165
|
-
current_style = ["", ""];
|
|
5166
|
-
}
|
|
5167
|
-
}
|
|
5168
|
-
if (ev.type === "start") active_comments.add(ev.id);
|
|
5169
|
-
else if (ev.type === "end") active_comments.delete(ev.id);
|
|
5170
|
-
else if (ev.type === "ins_start") active_ins[ev.id] = ev;
|
|
5171
|
-
else if (ev.type === "ins_end") delete active_ins[ev.id];
|
|
5172
|
-
else if (ev.type === "del_start") active_del[ev.id] = ev;
|
|
5173
|
-
else if (ev.type === "del_end") delete active_del[ev.id];
|
|
5174
|
-
else if (ev.type === "fmt_start") active_fmt[ev.id] = ev;
|
|
5175
|
-
else if (ev.type === "fmt_end") delete active_fmt[ev.id];
|
|
5176
|
-
else if (ev.type === "footnote" || ev.type === "endnote") {
|
|
5177
|
-
if (pending_text) {
|
|
5178
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5179
|
-
pending_text = "";
|
|
5180
|
-
current_wrappers = ["", ""];
|
|
5181
|
-
current_style = ["", ""];
|
|
5182
|
-
}
|
|
5183
|
-
parts.push(`[^${ev.type === "footnote" ? "fn" : "en"}-${ev.id}]`);
|
|
5184
|
-
} else if (ev.type === "hyperlink_start") {
|
|
5185
|
-
if (pending_text) {
|
|
5186
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5187
|
-
pending_text = "";
|
|
5188
|
-
current_wrappers = ["", ""];
|
|
5189
|
-
current_style = ["", ""];
|
|
5190
|
-
}
|
|
5191
|
-
parts.push("[");
|
|
5192
|
-
} else if (ev.type === "hyperlink_end") {
|
|
5193
|
-
if (pending_text) {
|
|
5194
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5195
|
-
pending_text = "";
|
|
5196
|
-
current_wrappers = ["", ""];
|
|
5197
|
-
current_style = ["", ""];
|
|
5198
|
-
}
|
|
5199
|
-
parts.push(`](${ev.date})`);
|
|
5200
|
-
} else if (ev.type === "xref_start") {
|
|
5201
|
-
if (pending_text) {
|
|
5202
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5203
|
-
pending_text = "";
|
|
5204
|
-
current_wrappers = ["", ""];
|
|
5205
|
-
current_style = ["", ""];
|
|
5206
|
-
}
|
|
5207
|
-
parts.push("[~");
|
|
5208
|
-
} else if (ev.type === "xref_end") {
|
|
5209
|
-
if (pending_text) {
|
|
5210
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5211
|
-
pending_text = "";
|
|
5212
|
-
current_wrappers = ["", ""];
|
|
5213
|
-
current_style = ["", ""];
|
|
5214
|
-
}
|
|
5215
|
-
parts.push(`~](#${ev.id})`);
|
|
5216
|
-
} else if (ev.type === "bookmark") {
|
|
5217
|
-
if (pending_text) {
|
|
5218
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5219
|
-
pending_text = "";
|
|
5220
|
-
current_wrappers = ["", ""];
|
|
5221
|
-
current_style = ["", ""];
|
|
5222
|
-
}
|
|
5223
|
-
parts.push(`{#${ev.id}}`);
|
|
5433
|
+
function strip_image_alt_text(doc) {
|
|
5434
|
+
let count = 0;
|
|
5435
|
+
for (const docPr of findDescendantsByLocalName(doc.element, "docPr")) {
|
|
5436
|
+
const descr = docPr.getAttribute("descr");
|
|
5437
|
+
if (descr) {
|
|
5438
|
+
const isShort = descr.length < 10;
|
|
5439
|
+
const isFile = descr.includes(".") && descr.length < 60;
|
|
5440
|
+
if (isShort || isFile) {
|
|
5441
|
+
docPr.removeAttribute("descr");
|
|
5442
|
+
count++;
|
|
5224
5443
|
}
|
|
5225
5444
|
}
|
|
5226
5445
|
}
|
|
5227
|
-
|
|
5228
|
-
if (deferred_meta_states.length > 0) {
|
|
5229
|
-
const meta_block = _build_merged_meta_block(deferred_meta_states, comments_map);
|
|
5230
|
-
if (meta_block) parts.push(`{>>${meta_block}<<}`);
|
|
5231
|
-
}
|
|
5232
|
-
return parts.join("");
|
|
5446
|
+
return count ? [`Image alt text: ${count} auto-generated descriptions removed`] : [];
|
|
5233
5447
|
}
|
|
5234
|
-
function
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
let render_comment2 = function(cid) {
|
|
5246
|
-
if (!comments_map[cid]) return;
|
|
5247
|
-
const sig = `Com:${cid}`;
|
|
5248
|
-
if (seen_sigs.has(sig)) return;
|
|
5249
|
-
const data = comments_map[cid];
|
|
5250
|
-
let header = `[${sig}] ${data.author}`;
|
|
5251
|
-
if (data.date) header += ` @ ${data.date}`;
|
|
5252
|
-
if (data.resolved) header += `(RESOLVED)`;
|
|
5253
|
-
comment_lines.push(`${header}: ${data.text}`);
|
|
5254
|
-
seen_sigs.add(sig);
|
|
5255
|
-
if (children_map[cid]) {
|
|
5256
|
-
const children = children_map[cid].sort((a, b) => (comments_map[a]?.date || "").localeCompare(comments_map[b]?.date || ""));
|
|
5257
|
-
for (const child_id of children) {
|
|
5258
|
-
render_comment2(child_id);
|
|
5448
|
+
function audit_hyperlinks(doc) {
|
|
5449
|
+
const internal = ["sharepoint.com", "onedrive.com", ".internal", "intranet", "localhost", "10.", "192.168.", "172.16."];
|
|
5450
|
+
const warnings = [];
|
|
5451
|
+
const docRels = doc.pkg.getOrCreateRelsPart(doc.part.partname);
|
|
5452
|
+
for (const rel of findAllDescendants(docRels._element, "Relationship")) {
|
|
5453
|
+
if (rel.getAttribute("TargetMode") === "External") {
|
|
5454
|
+
const url = rel.getAttribute("Target") || "";
|
|
5455
|
+
for (const pattern of internal) {
|
|
5456
|
+
if (url.toLowerCase().includes(pattern.toLowerCase())) {
|
|
5457
|
+
warnings.push(`Hyperlink targets internal URL: ${_truncate(url, 80)}`);
|
|
5458
|
+
break;
|
|
5259
5459
|
}
|
|
5260
5460
|
}
|
|
5261
|
-
};
|
|
5262
|
-
var render_comment = render_comment2;
|
|
5263
|
-
for (const [uid, meta] of Object.entries(ins_map)) {
|
|
5264
|
-
const sig = `Chg:${uid}`;
|
|
5265
|
-
if (!seen_sigs.has(sig)) {
|
|
5266
|
-
change_lines.push(`[${sig} insert] ${meta.author || "Unknown"}`);
|
|
5267
|
-
seen_sigs.add(sig);
|
|
5268
|
-
}
|
|
5269
|
-
}
|
|
5270
|
-
for (const [uid, meta] of Object.entries(del_map)) {
|
|
5271
|
-
const sig = `Chg:${uid}`;
|
|
5272
|
-
if (!seen_sigs.has(sig)) {
|
|
5273
|
-
change_lines.push(`[${sig} delete] ${meta.author || "Unknown"}`);
|
|
5274
|
-
seen_sigs.add(sig);
|
|
5275
|
-
}
|
|
5276
|
-
}
|
|
5277
|
-
for (const [uid, meta] of Object.entries(fmt_map)) {
|
|
5278
|
-
const sig = `Chg:${uid}`;
|
|
5279
|
-
if (!seen_sigs.has(sig)) {
|
|
5280
|
-
change_lines.push(`[${sig} format] ${meta.author || "Unknown"}`);
|
|
5281
|
-
seen_sigs.add(sig);
|
|
5282
|
-
}
|
|
5283
|
-
}
|
|
5284
|
-
const children_map = {};
|
|
5285
|
-
for (const [c_id, data] of Object.entries(comments_map)) {
|
|
5286
|
-
const p_id = data.parent_id;
|
|
5287
|
-
if (p_id) {
|
|
5288
|
-
if (!children_map[p_id]) children_map[p_id] = [];
|
|
5289
|
-
children_map[p_id].push(c_id);
|
|
5290
|
-
}
|
|
5291
|
-
}
|
|
5292
|
-
const sorted_ids = Array.from(comments_set).sort();
|
|
5293
|
-
for (const c_id of sorted_ids) {
|
|
5294
|
-
render_comment2(c_id);
|
|
5295
5461
|
}
|
|
5296
5462
|
}
|
|
5297
|
-
return
|
|
5463
|
+
return warnings;
|
|
5298
5464
|
}
|
|
5299
5465
|
|
|
5300
|
-
// src/
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
if (
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
const comments_map = extract_comments_data(doc.pkg);
|
|
5311
|
-
const block_records = _walk_doc_body(doc, comments_map);
|
|
5312
|
-
const heading_indices = [];
|
|
5313
|
-
for (let idx = 0; idx < block_records.length; idx++) {
|
|
5314
|
-
const rec = block_records[idx];
|
|
5315
|
-
if (!(rec.is_paragraph && _is_heading(rec.item))) continue;
|
|
5316
|
-
if (!_heading_passes_quality_filter(rec.item, comments_map)) continue;
|
|
5317
|
-
heading_indices.push(idx);
|
|
5466
|
+
// src/domain.ts
|
|
5467
|
+
function boundedLevenshtein(a, b, maxDist = 2) {
|
|
5468
|
+
if (a === b) return 0;
|
|
5469
|
+
if (Math.abs(a.length - b.length) > maxDist) return maxDist + 1;
|
|
5470
|
+
if (a.length === 0) return b.length <= maxDist ? b.length : maxDist + 1;
|
|
5471
|
+
if (b.length === 0) return a.length <= maxDist ? a.length : maxDist + 1;
|
|
5472
|
+
if (a.length > b.length) {
|
|
5473
|
+
const temp = a;
|
|
5474
|
+
a = b;
|
|
5475
|
+
b = temp;
|
|
5318
5476
|
}
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
h_pos,
|
|
5332
|
-
level
|
|
5333
|
-
);
|
|
5334
|
-
const owned_blocks = block_records.slice(rec_idx + 1, owned_end);
|
|
5335
|
-
const has_table = _direct_has_table(block_records, rec_idx + 1, owned_end);
|
|
5336
|
-
const footnote_ids = _collect_footnote_ids(owned_blocks);
|
|
5337
|
-
const page_num = _offset_to_page(rec.start_offset, body_page_offsets);
|
|
5338
|
-
nodes.push({ level, text, page: page_num, style, has_table, footnote_ids });
|
|
5477
|
+
let row = Array.from({ length: a.length + 1 }, (_, i) => i);
|
|
5478
|
+
for (let i = 1; i <= b.length; i++) {
|
|
5479
|
+
const newRow = [i];
|
|
5480
|
+
let minInRow = i;
|
|
5481
|
+
for (let j = 1; j <= a.length; j++) {
|
|
5482
|
+
const cost = a[j - 1] === b[i - 1] ? 0 : 1;
|
|
5483
|
+
const val = Math.min(row[j] + 1, newRow[j - 1] + 1, row[j - 1] + cost);
|
|
5484
|
+
newRow.push(val);
|
|
5485
|
+
if (val < minInRow) minInRow = val;
|
|
5486
|
+
}
|
|
5487
|
+
if (minInRow > maxDist) return maxDist + 1;
|
|
5488
|
+
row = newRow;
|
|
5339
5489
|
}
|
|
5340
|
-
return
|
|
5490
|
+
return row[a.length] <= maxDist ? row[a.length] : maxDist + 1;
|
|
5341
5491
|
}
|
|
5342
|
-
function
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5492
|
+
function _get_paragraph_text(p) {
|
|
5493
|
+
let text = "";
|
|
5494
|
+
const runs = findAllDescendants(p._element, "w:r");
|
|
5495
|
+
for (const r of runs) {
|
|
5496
|
+
text += get_run_text(new Run(r, p));
|
|
5347
5497
|
}
|
|
5348
|
-
return
|
|
5498
|
+
return text;
|
|
5349
5499
|
}
|
|
5350
|
-
function
|
|
5351
|
-
const
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5500
|
+
function extract_all_domain_metadata(doc, base_text) {
|
|
5501
|
+
const definitions = {};
|
|
5502
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
5503
|
+
const raw_anchors = {};
|
|
5504
|
+
const raw_references = [];
|
|
5505
|
+
const leading_re = /^(?:[\d.\-()a-zA-Z]+\s*)?["“]([A-Z][A-Za-z0-9\s\-&'’]{1,60})["”]/;
|
|
5506
|
+
const inline_re = /\([^)]*?["“]([A-Z][A-Za-z0-9\s\-&'’]{1,60})["”][^)]*?\)/g;
|
|
5507
|
+
for (const item of iter_block_items(doc)) {
|
|
5508
|
+
if (!(item instanceof Paragraph)) continue;
|
|
5509
|
+
const text = _get_paragraph_text(item).trim();
|
|
5510
|
+
if (!text) continue;
|
|
5511
|
+
const extracted_terms = [];
|
|
5512
|
+
const leading_match = text.match(leading_re);
|
|
5513
|
+
if (leading_match) extracted_terms.push(leading_match[1].trim());
|
|
5514
|
+
const inline_matches = text.matchAll(inline_re);
|
|
5515
|
+
for (const m of inline_matches) {
|
|
5516
|
+
extracted_terms.push(m[1].trim());
|
|
5358
5517
|
}
|
|
5359
|
-
const
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5518
|
+
for (const term of extracted_terms) {
|
|
5519
|
+
if (definitions[term]) duplicates.add(term);
|
|
5520
|
+
else definitions[term] = { count: 0 };
|
|
5521
|
+
}
|
|
5522
|
+
const short_text = text.length > 60 ? text.substring(0, 60) + "..." : text;
|
|
5523
|
+
const nodes = findAllDescendants(item._element, "*");
|
|
5524
|
+
for (const node of nodes) {
|
|
5525
|
+
if (node.tagName === "w:bookmarkStart") {
|
|
5526
|
+
const b_name = node.getAttribute("w:name");
|
|
5527
|
+
if (b_name && (!b_name.startsWith("_") || b_name.startsWith("_Ref"))) {
|
|
5528
|
+
if (!raw_anchors[b_name]) {
|
|
5529
|
+
raw_anchors[b_name] = {
|
|
5530
|
+
anchored_to: short_text,
|
|
5531
|
+
referenced_from: []
|
|
5532
|
+
};
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5536
|
+
let target = null;
|
|
5537
|
+
if (node.tagName === "w:fldSimple") {
|
|
5538
|
+
const instr = node.getAttribute("w:instr") || "";
|
|
5539
|
+
const parts = instr.trim().split(/\s+/);
|
|
5540
|
+
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
5541
|
+
} else if (node.tagName === "w:instrText") {
|
|
5542
|
+
const instr = node.textContent || "";
|
|
5543
|
+
const parts = instr.trim().split(/\s+/);
|
|
5544
|
+
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
5545
|
+
}
|
|
5546
|
+
if (target) raw_references.push([target, short_text]);
|
|
5363
5547
|
}
|
|
5364
5548
|
}
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
if (body_start_offset > 0) body_start_offset += 2;
|
|
5549
|
+
for (const [target, ref_text] of raw_references) {
|
|
5550
|
+
if (raw_anchors[target]) {
|
|
5551
|
+
raw_anchors[target].referenced_from.push(ref_text);
|
|
5552
|
+
}
|
|
5370
5553
|
}
|
|
5371
|
-
const
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
}
|
|
5387
|
-
cursor += block_len;
|
|
5388
|
-
is_first_block = false;
|
|
5389
|
-
} else if (item instanceof Table) {
|
|
5390
|
-
const table_text = extract_table(item, comments_map, false, 0);
|
|
5391
|
-
const block_len = table_text ? table_text.length : 0;
|
|
5392
|
-
if (!is_first_block) cursor += 2;
|
|
5393
|
-
const table_start = cursor;
|
|
5394
|
-
records.push({
|
|
5395
|
-
item,
|
|
5396
|
-
is_paragraph: false,
|
|
5397
|
-
is_table: true,
|
|
5398
|
-
start_offset: table_start,
|
|
5399
|
-
projected_length: block_len
|
|
5400
|
-
});
|
|
5401
|
-
_record_table_inner_blocks_lite(item, table_start, records, comments_map);
|
|
5402
|
-
cursor += block_len;
|
|
5403
|
-
is_first_block = false;
|
|
5554
|
+
const diagnostics = [];
|
|
5555
|
+
const def_keys = Object.keys(definitions);
|
|
5556
|
+
if (def_keys.length > 0) {
|
|
5557
|
+
const sorted_terms = def_keys.sort((a, b) => b.length - a.length);
|
|
5558
|
+
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5559
|
+
const alt = sorted_terms.map(escapeRegExp).join("|");
|
|
5560
|
+
const usage_pattern = new RegExp(`(?<!["\u201C])\\b(${alt})\\b(?![\u201D"])`, "g");
|
|
5561
|
+
for (const m of base_text.matchAll(usage_pattern)) {
|
|
5562
|
+
const matched_term = m[1];
|
|
5563
|
+
if (definitions[matched_term]) definitions[matched_term].count++;
|
|
5564
|
+
}
|
|
5565
|
+
for (const term of def_keys) {
|
|
5566
|
+
if (definitions[term].count === 0) {
|
|
5567
|
+
delete definitions[term];
|
|
5568
|
+
duplicates.delete(term);
|
|
5569
|
+
}
|
|
5404
5570
|
}
|
|
5405
5571
|
}
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
let cursor = table_start_offset;
|
|
5411
|
-
let rows_processed = 0;
|
|
5412
|
-
for (const row of table.rows) {
|
|
5413
|
-
if (rows_processed > 0) cursor += 1;
|
|
5414
|
-
const seen_cells = /* @__PURE__ */ new Set();
|
|
5415
|
-
let cells_in_row = 0;
|
|
5416
|
-
for (const cell of row.cells) {
|
|
5417
|
-
if (seen_cells.has(cell)) continue;
|
|
5418
|
-
seen_cells.add(cell);
|
|
5419
|
-
if (cells_in_row > 0) cursor += 3;
|
|
5420
|
-
const [new_cursor, found] = _walk_cell_for_offset(
|
|
5421
|
-
cell,
|
|
5422
|
-
target_el,
|
|
5423
|
-
cursor,
|
|
5424
|
-
comments_map
|
|
5425
|
-
);
|
|
5426
|
-
if (found) return new_cursor;
|
|
5427
|
-
cursor = new_cursor;
|
|
5428
|
-
cells_in_row++;
|
|
5429
|
-
}
|
|
5430
|
-
rows_processed++;
|
|
5572
|
+
for (const term of duplicates) {
|
|
5573
|
+
diagnostics.push(
|
|
5574
|
+
`[Error] Duplicate Definition: '${term}' is defined multiple times.`
|
|
5575
|
+
);
|
|
5431
5576
|
}
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5577
|
+
const stop_words = /* @__PURE__ */ new Set([
|
|
5578
|
+
"The",
|
|
5579
|
+
"This",
|
|
5580
|
+
"That",
|
|
5581
|
+
"Such",
|
|
5582
|
+
"A",
|
|
5583
|
+
"An",
|
|
5584
|
+
"Any",
|
|
5585
|
+
"All",
|
|
5586
|
+
"Some",
|
|
5587
|
+
"No",
|
|
5588
|
+
"Every",
|
|
5589
|
+
"Each",
|
|
5590
|
+
"As",
|
|
5591
|
+
"In",
|
|
5592
|
+
"Of",
|
|
5593
|
+
"For",
|
|
5594
|
+
"To",
|
|
5595
|
+
"On",
|
|
5596
|
+
"By",
|
|
5597
|
+
"With"
|
|
5598
|
+
]);
|
|
5599
|
+
const all_cap_pattern = /\b[A-Z][a-zA-Z]*(?:\s+[A-Z][a-zA-Z]*)*\b/g;
|
|
5600
|
+
const all_caps = new Set(base_text.match(all_cap_pattern) || []);
|
|
5601
|
+
const valid_terms = new Set(Object.keys(definitions));
|
|
5602
|
+
const terms_by_first_letter = {};
|
|
5603
|
+
for (const term of valid_terms) {
|
|
5604
|
+
const fl = term[0].toLowerCase();
|
|
5605
|
+
if (!terms_by_first_letter[fl]) terms_by_first_letter[fl] = [];
|
|
5606
|
+
terms_by_first_letter[fl].push(term);
|
|
5607
|
+
}
|
|
5608
|
+
const candidates_by_term = {};
|
|
5609
|
+
for (const raw_candidate of all_caps) {
|
|
5610
|
+
let candidate = raw_candidate.trim();
|
|
5611
|
+
const words = candidate.split(/\s+/);
|
|
5612
|
+
while (words.length > 0) {
|
|
5613
|
+
const first = words[0];
|
|
5614
|
+
const title = first.charAt(0).toUpperCase() + first.slice(1).toLowerCase();
|
|
5615
|
+
if (stop_words.has(title)) words.shift();
|
|
5616
|
+
else break;
|
|
5617
|
+
}
|
|
5618
|
+
candidate = words.join(" ");
|
|
5619
|
+
if (candidate.length < 4) continue;
|
|
5620
|
+
if (valid_terms.has(candidate)) continue;
|
|
5621
|
+
const first_letter = candidate[0].toLowerCase();
|
|
5622
|
+
let candidate_terms = terms_by_first_letter[first_letter] || [];
|
|
5623
|
+
if (candidate.length > 5) {
|
|
5624
|
+
for (const [k, v] of Object.entries(terms_by_first_letter)) {
|
|
5625
|
+
if (k !== first_letter) candidate_terms = candidate_terms.concat(v);
|
|
5454
5626
|
}
|
|
5455
|
-
const table_text = extract_table(inner_item, comments_map, false, 0);
|
|
5456
|
-
cursor += table_text ? table_text.length : 0;
|
|
5457
5627
|
}
|
|
5458
|
-
|
|
5628
|
+
for (const term of candidate_terms) {
|
|
5629
|
+
if (Math.abs(candidate.length - term.length) > 2) continue;
|
|
5630
|
+
if (candidate === term + "s" || candidate === term + "es") continue;
|
|
5631
|
+
if (term === candidate + "s" || term === candidate + "es") continue;
|
|
5632
|
+
const dist = boundedLevenshtein(candidate, term, 2);
|
|
5633
|
+
if (dist === 0 || dist > 2) continue;
|
|
5634
|
+
if (term.length <= 5) {
|
|
5635
|
+
if (dist > 1) continue;
|
|
5636
|
+
if (candidate[0].toLowerCase() !== term[0].toLowerCase()) continue;
|
|
5637
|
+
}
|
|
5638
|
+
if (!candidates_by_term[term]) candidates_by_term[term] = [];
|
|
5639
|
+
if (!candidates_by_term[term].includes(candidate))
|
|
5640
|
+
candidates_by_term[term].push(candidate);
|
|
5641
|
+
}
|
|
5459
5642
|
}
|
|
5460
|
-
|
|
5643
|
+
for (const [term, candidates] of Object.entries(candidates_by_term)) {
|
|
5644
|
+
candidates.sort();
|
|
5645
|
+
const c_str = candidates.map((c) => `'${c}'`).join(", ");
|
|
5646
|
+
diagnostics.push(`[Info] Possible Typos for '${term}': Found ${c_str}`);
|
|
5647
|
+
}
|
|
5648
|
+
function diag_sort_key(msg) {
|
|
5649
|
+
if (msg.startsWith("[Error]")) return 0;
|
|
5650
|
+
if (msg.startsWith("[Warning]")) return 1;
|
|
5651
|
+
return 2;
|
|
5652
|
+
}
|
|
5653
|
+
diagnostics.sort((a, b) => {
|
|
5654
|
+
const keyA = diag_sort_key(a);
|
|
5655
|
+
const keyB = diag_sort_key(b);
|
|
5656
|
+
if (keyA !== keyB) return keyA - keyB;
|
|
5657
|
+
return a.localeCompare(b);
|
|
5658
|
+
});
|
|
5659
|
+
return [definitions, diagnostics, raw_anchors];
|
|
5461
5660
|
}
|
|
5462
|
-
function
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5661
|
+
function extract_document_settings_warnings(doc) {
|
|
5662
|
+
const warnings = [];
|
|
5663
|
+
const settingsPart = doc.pkg.getPartByPath("word/settings.xml");
|
|
5664
|
+
if (!settingsPart) return warnings;
|
|
5665
|
+
const isTruthy = (el) => {
|
|
5666
|
+
if (!el.hasAttribute("w:val")) return true;
|
|
5667
|
+
const val = (el.getAttribute("w:val") || "").toLowerCase();
|
|
5668
|
+
return val !== "0" && val !== "false" && val !== "off";
|
|
5669
|
+
};
|
|
5670
|
+
const removePersonal = findDescendantsByLocalName(
|
|
5671
|
+
settingsPart._element,
|
|
5672
|
+
"removePersonalInformation"
|
|
5673
|
+
);
|
|
5674
|
+
if (removePersonal.length > 0 && isTruthy(removePersonal[0])) {
|
|
5675
|
+
warnings.push(
|
|
5676
|
+
"[Warning] Privacy flag `removePersonalInformation` is enabled in word/settings.xml. Microsoft Word will strip the `w:author`, `w:initials`, and `w:date` attributes from every tracked change and comment the next time this document is opened and saved. Edits made by this agent will lose attribution, breaking audit trails and any multi-turn workflow that relies on identifying prior edits."
|
|
5677
|
+
);
|
|
5467
5678
|
}
|
|
5468
|
-
|
|
5679
|
+
const removeDateTime = findDescendantsByLocalName(
|
|
5680
|
+
settingsPart._element,
|
|
5681
|
+
"removeDateAndTime"
|
|
5682
|
+
);
|
|
5683
|
+
if (removeDateTime.length > 0 && isTruthy(removeDateTime[0])) {
|
|
5684
|
+
warnings.push(
|
|
5685
|
+
"[Warning] Privacy flag `removeDateAndTime` is enabled in word/settings.xml. Microsoft Word will strip the `w:date` attribute from every tracked change and comment the next time this document is opened and saved. Timestamps on this agent's edits will be lost on the next Word save."
|
|
5686
|
+
);
|
|
5687
|
+
}
|
|
5688
|
+
return warnings;
|
|
5469
5689
|
}
|
|
5470
|
-
function
|
|
5471
|
-
const
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
projected_length: 0
|
|
5490
|
-
});
|
|
5491
|
-
} else if (inner_item instanceof Table) {
|
|
5492
|
-
records.push({
|
|
5493
|
-
item: inner_item,
|
|
5494
|
-
is_paragraph: false,
|
|
5495
|
-
is_table: true,
|
|
5496
|
-
start_offset: inherited_offset,
|
|
5497
|
-
projected_length: 0
|
|
5498
|
-
});
|
|
5499
|
-
_record_table_inner_blocks_lite(
|
|
5500
|
-
inner_item,
|
|
5501
|
-
inherited_offset,
|
|
5502
|
-
records,
|
|
5503
|
-
comments_map
|
|
5504
|
-
);
|
|
5505
|
-
}
|
|
5506
|
-
}
|
|
5690
|
+
function build_structural_appendix(doc, base_text) {
|
|
5691
|
+
const [defs, diagnostics, anchors] = extract_all_domain_metadata(
|
|
5692
|
+
doc,
|
|
5693
|
+
base_text
|
|
5694
|
+
);
|
|
5695
|
+
const settings_warnings = extract_document_settings_warnings(doc);
|
|
5696
|
+
const lines = [
|
|
5697
|
+
"\n\n---",
|
|
5698
|
+
"",
|
|
5699
|
+
"<!-- READONLY_BOUNDARY_START -->",
|
|
5700
|
+
"# Document Structure (Read-Only)",
|
|
5701
|
+
"The content below is metadata describing the document's reference structure. Do not include this section in any tracked changes or edits \u2014 it is for your context only and will be discarded on write."
|
|
5702
|
+
];
|
|
5703
|
+
let has_content = false;
|
|
5704
|
+
if (settings_warnings.length > 0) {
|
|
5705
|
+
has_content = true;
|
|
5706
|
+
lines.push("\n## Document Settings");
|
|
5707
|
+
for (const warning of settings_warnings) {
|
|
5708
|
+
lines.push(`- ${warning}`);
|
|
5507
5709
|
}
|
|
5508
5710
|
}
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
blocks.push(`---
|
|
5516
|
-
${header}`);
|
|
5711
|
+
if (Object.keys(defs).length > 0) {
|
|
5712
|
+
has_content = true;
|
|
5713
|
+
lines.push("\n## Defined Terms");
|
|
5714
|
+
for (const [term, data] of Object.entries(defs)) {
|
|
5715
|
+
lines.push(`- "${term}" \u2014 used ${data.count} times.`);
|
|
5716
|
+
}
|
|
5517
5717
|
}
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
} else if (item instanceof Paragraph) {
|
|
5524
|
-
let prefix = get_paragraph_prefix(item);
|
|
5525
|
-
if (is_first_para && c_type === "FootnoteItem")
|
|
5526
|
-
prefix = `[^${part.note_type}-${part.id}]: ${prefix}`;
|
|
5527
|
-
const p_text = build_paragraph_text(item, comments_map, false);
|
|
5528
|
-
blocks.push(prefix + p_text);
|
|
5529
|
-
is_first_para = false;
|
|
5530
|
-
} else if (item instanceof Table) {
|
|
5531
|
-
const table_text = extract_table(item, comments_map, false, 0);
|
|
5532
|
-
if (table_text) blocks.push(table_text);
|
|
5533
|
-
is_first_para = false;
|
|
5718
|
+
if (diagnostics.length > 0) {
|
|
5719
|
+
has_content = true;
|
|
5720
|
+
lines.push("\n## Semantic Diagnostics");
|
|
5721
|
+
for (const diag of diagnostics) {
|
|
5722
|
+
lines.push(`- ${diag}`);
|
|
5534
5723
|
}
|
|
5535
5724
|
}
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
}
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5725
|
+
if (Object.keys(anchors).length > 0) {
|
|
5726
|
+
has_content = true;
|
|
5727
|
+
lines.push("\n## Named Anchors");
|
|
5728
|
+
for (const [b_name, data] of Object.entries(anchors)) {
|
|
5729
|
+
lines.push(`- ${b_name} \u2192 Anchored to: "${data.anchored_to}"`);
|
|
5730
|
+
for (const ref of data.referenced_from) {
|
|
5731
|
+
lines.push(` - Referenced from: "${ref}"`);
|
|
5732
|
+
}
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
if (has_content) {
|
|
5736
|
+
return lines.join("\n");
|
|
5737
|
+
}
|
|
5738
|
+
return "";
|
|
5548
5739
|
}
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5740
|
+
|
|
5741
|
+
// src/ingest.ts
|
|
5742
|
+
async function extractTextFromBuffer(buffer, cleanView = false) {
|
|
5743
|
+
const doc = await DocumentObject.load(buffer);
|
|
5744
|
+
return _extractTextFromDoc(doc, cleanView);
|
|
5552
5745
|
}
|
|
5553
|
-
function
|
|
5554
|
-
const
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
text = text.replace(/(?<!\w)_(\S(?:.*?\S)?)_(?!\w)/g, "$1");
|
|
5572
|
-
return text;
|
|
5573
|
-
}
|
|
5574
|
-
function _determine_heading_style(paragraph) {
|
|
5575
|
-
const [style_cache, default_pstyle] = _get_style_cache(
|
|
5576
|
-
paragraph._parent.part || paragraph._parent
|
|
5577
|
-
);
|
|
5578
|
-
const pPr = findChild(paragraph._element, "w:pPr");
|
|
5579
|
-
let style_id = default_pstyle;
|
|
5580
|
-
if (pPr) {
|
|
5581
|
-
const pStyle = findChild(pPr, "w:pStyle");
|
|
5582
|
-
if (pStyle) style_id = pStyle.getAttribute("w:val") || default_pstyle;
|
|
5583
|
-
}
|
|
5584
|
-
let outline_level = null;
|
|
5585
|
-
let outline_level_from_style = false;
|
|
5586
|
-
if (pPr) {
|
|
5587
|
-
const oLvl = findChild(pPr, "w:outlineLvl");
|
|
5588
|
-
if (oLvl && /^\d+$/.test(oLvl.getAttribute("w:val") || "")) {
|
|
5589
|
-
outline_level = parseInt(oLvl.getAttribute("w:val"), 10);
|
|
5590
|
-
}
|
|
5591
|
-
}
|
|
5592
|
-
if (outline_level === null && style_id && style_cache && style_cache[style_id]) {
|
|
5593
|
-
outline_level = style_cache[style_id].outline_level;
|
|
5594
|
-
outline_level_from_style = true;
|
|
5595
|
-
}
|
|
5596
|
-
const style_name = style_id && style_cache && style_cache[style_id] ? style_cache[style_id].name : style_id;
|
|
5597
|
-
let normalized_style_name = style_name;
|
|
5598
|
-
if (normalized_style_name && typeof normalized_style_name === "string") {
|
|
5599
|
-
if (normalized_style_name.toLowerCase().startsWith("heading")) {
|
|
5600
|
-
normalized_style_name = normalized_style_name.replace(/^heading/i, "Heading");
|
|
5601
|
-
} else if (normalized_style_name.toLowerCase() === "title") {
|
|
5602
|
-
normalized_style_name = "Title";
|
|
5746
|
+
function _extractTextFromDoc(doc, cleanView = false, includeAppendix = true, return_paragraph_offsets = false) {
|
|
5747
|
+
const comments_map = extract_comments_data(doc.pkg);
|
|
5748
|
+
const full_text = [];
|
|
5749
|
+
const paragraph_offsets = /* @__PURE__ */ new Map();
|
|
5750
|
+
let cursor = 0;
|
|
5751
|
+
for (const part of iter_document_parts(doc)) {
|
|
5752
|
+
const part_cursor = full_text.length > 0 ? cursor + 2 : cursor;
|
|
5753
|
+
const part_text = _extract_blocks(
|
|
5754
|
+
part,
|
|
5755
|
+
comments_map,
|
|
5756
|
+
cleanView,
|
|
5757
|
+
part_cursor,
|
|
5758
|
+
return_paragraph_offsets ? paragraph_offsets : void 0
|
|
5759
|
+
);
|
|
5760
|
+
if (part_text) {
|
|
5761
|
+
if (full_text.length > 0) cursor += 2;
|
|
5762
|
+
full_text.push(part_text);
|
|
5763
|
+
cursor += part_text.length;
|
|
5603
5764
|
}
|
|
5604
5765
|
}
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
}
|
|
5766
|
+
let base_text = full_text.join("\n\n");
|
|
5767
|
+
if (includeAppendix) {
|
|
5768
|
+
const appendix = build_structural_appendix(doc, base_text);
|
|
5769
|
+
if (appendix) base_text += appendix;
|
|
5610
5770
|
}
|
|
5611
|
-
if (
|
|
5612
|
-
|
|
5613
|
-
return normalized_style_name;
|
|
5614
|
-
}
|
|
5615
|
-
return "(outline_level)";
|
|
5771
|
+
if (return_paragraph_offsets) {
|
|
5772
|
+
return { text: base_text, paragraph_offsets };
|
|
5616
5773
|
}
|
|
5617
|
-
|
|
5618
|
-
return normalized_style_name;
|
|
5619
|
-
if (normalized_style_name && /Heading[ ]?([1-6])(?![0-9])/.test(normalized_style_name))
|
|
5620
|
-
return normalized_style_name;
|
|
5621
|
-
return "(heuristic)";
|
|
5774
|
+
return base_text;
|
|
5622
5775
|
}
|
|
5623
|
-
function
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5776
|
+
function _extract_blocks(container, comments_map, cleanView, cursor, paragraph_offsets) {
|
|
5777
|
+
const part = container.part || container;
|
|
5778
|
+
const [style_cache, default_pstyle] = _get_style_cache(part);
|
|
5779
|
+
const blocks = [];
|
|
5780
|
+
let local_cursor = cursor;
|
|
5781
|
+
let is_first_block = true;
|
|
5782
|
+
let is_first_para = true;
|
|
5783
|
+
if (container.constructor && container.constructor.name === "NotesPart") {
|
|
5784
|
+
const header = container.note_type === "fn" ? "## Footnotes" : "## Endnotes";
|
|
5785
|
+
const sep = `---
|
|
5786
|
+
${header}`;
|
|
5787
|
+
blocks.push(sep);
|
|
5788
|
+
local_cursor += sep.length;
|
|
5789
|
+
is_first_block = false;
|
|
5628
5790
|
}
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5791
|
+
for (const item of iter_block_items(container)) {
|
|
5792
|
+
if (!is_first_block) local_cursor += 2;
|
|
5793
|
+
const block_start = local_cursor;
|
|
5794
|
+
if (item.constructor.name === "FootnoteItem") {
|
|
5795
|
+
const fn_text = _extract_blocks(
|
|
5796
|
+
item,
|
|
5797
|
+
comments_map,
|
|
5798
|
+
cleanView,
|
|
5799
|
+
block_start,
|
|
5800
|
+
paragraph_offsets
|
|
5801
|
+
);
|
|
5802
|
+
if (fn_text) {
|
|
5803
|
+
blocks.push(fn_text);
|
|
5804
|
+
local_cursor = block_start + fn_text.length;
|
|
5805
|
+
is_first_block = false;
|
|
5806
|
+
} else if (!is_first_block) {
|
|
5807
|
+
local_cursor -= 2;
|
|
5645
5808
|
}
|
|
5646
|
-
}
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
}
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
function walk(container) {
|
|
5663
|
-
for (const item of iter_block_items(container)) {
|
|
5664
|
-
const i_type = item.constructor.name;
|
|
5665
|
-
if (i_type === "FootnoteItem") {
|
|
5666
|
-
walk(item);
|
|
5667
|
-
} else if (item instanceof Paragraph) {
|
|
5668
|
-
paragraphs_and_tables.push(["p", item]);
|
|
5669
|
-
} else if (item instanceof Table) {
|
|
5670
|
-
paragraphs_and_tables.push(["t", item]);
|
|
5671
|
-
for (const row of item.rows) {
|
|
5672
|
-
for (const cell of row.cells) {
|
|
5673
|
-
if (seen_cells.has(cell._element)) {
|
|
5674
|
-
continue;
|
|
5675
|
-
}
|
|
5676
|
-
seen_cells.add(cell._element);
|
|
5677
|
-
walk(cell);
|
|
5678
|
-
}
|
|
5679
|
-
}
|
|
5809
|
+
} else if (item instanceof Paragraph) {
|
|
5810
|
+
let prefix = get_paragraph_prefix(item, style_cache, default_pstyle);
|
|
5811
|
+
if (is_first_para && container.constructor.name === "FootnoteItem") {
|
|
5812
|
+
prefix = `[^${container.note_type}-${container.id}]: ` + prefix;
|
|
5813
|
+
}
|
|
5814
|
+
const p_text = build_paragraph_text(
|
|
5815
|
+
item,
|
|
5816
|
+
comments_map,
|
|
5817
|
+
cleanView,
|
|
5818
|
+
style_cache,
|
|
5819
|
+
default_pstyle
|
|
5820
|
+
);
|
|
5821
|
+
const full_block = prefix + p_text;
|
|
5822
|
+
blocks.push(full_block);
|
|
5823
|
+
if (paragraph_offsets) {
|
|
5824
|
+
paragraph_offsets.set(item._element, [block_start, full_block.length]);
|
|
5680
5825
|
}
|
|
5826
|
+
local_cursor = block_start + full_block.length;
|
|
5827
|
+
is_first_para = false;
|
|
5828
|
+
is_first_block = false;
|
|
5829
|
+
} else if (item instanceof Table) {
|
|
5830
|
+
const table_text = extract_table(
|
|
5831
|
+
item,
|
|
5832
|
+
comments_map,
|
|
5833
|
+
cleanView,
|
|
5834
|
+
block_start,
|
|
5835
|
+
paragraph_offsets
|
|
5836
|
+
);
|
|
5837
|
+
if (table_text) {
|
|
5838
|
+
blocks.push(table_text);
|
|
5839
|
+
local_cursor = block_start + table_text.length;
|
|
5840
|
+
is_first_block = false;
|
|
5841
|
+
} else if (!is_first_block) {
|
|
5842
|
+
local_cursor -= 2;
|
|
5843
|
+
}
|
|
5844
|
+
is_first_para = false;
|
|
5681
5845
|
}
|
|
5682
5846
|
}
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5847
|
+
return blocks.join("\n\n");
|
|
5848
|
+
}
|
|
5849
|
+
function extract_table(table, comments_map, cleanView, cursor, paragraph_offsets) {
|
|
5850
|
+
const rows_text = [];
|
|
5851
|
+
let rows_processed = 0;
|
|
5852
|
+
let local_cursor = cursor;
|
|
5853
|
+
for (const row of table.rows) {
|
|
5854
|
+
const cell_texts = [];
|
|
5855
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
5856
|
+
const trPr = findChild(row._element, "w:trPr");
|
|
5857
|
+
const ins = trPr ? findChild(trPr, "w:ins") : null;
|
|
5858
|
+
const del_node = trPr ? findChild(trPr, "w:del") : null;
|
|
5859
|
+
if (cleanView && del_node) continue;
|
|
5860
|
+
const row_start = local_cursor + (rows_processed > 0 ? 1 : 0);
|
|
5861
|
+
const wrapper_prefix_len = !cleanView && ins ? 4 : !cleanView && del_node ? 4 : 0;
|
|
5862
|
+
let cell_cursor = row_start + wrapper_prefix_len;
|
|
5863
|
+
let first_cell = true;
|
|
5864
|
+
for (const cell of row.cells) {
|
|
5865
|
+
if (seen_cells.has(cell)) continue;
|
|
5866
|
+
seen_cells.add(cell);
|
|
5867
|
+
if (!first_cell) cell_cursor += 3;
|
|
5868
|
+
const cell_content = _extract_blocks(
|
|
5869
|
+
cell,
|
|
5870
|
+
comments_map,
|
|
5871
|
+
cleanView,
|
|
5872
|
+
cell_cursor,
|
|
5873
|
+
paragraph_offsets
|
|
5874
|
+
);
|
|
5875
|
+
cell_texts.push(cell_content);
|
|
5876
|
+
cell_cursor += cell_content.length;
|
|
5877
|
+
first_cell = false;
|
|
5693
5878
|
}
|
|
5694
|
-
|
|
5695
|
-
|
|
5879
|
+
let row_str = cell_texts.join(" | ");
|
|
5880
|
+
if (!cleanView) {
|
|
5881
|
+
if (ins) row_str = `{++ ${row_str} |Chg:${ins.getAttribute("w:id")}++}`;
|
|
5882
|
+
else if (del_node)
|
|
5883
|
+
row_str = `{-- ${row_str} |Chg:${del_node.getAttribute("w:id")}--}`;
|
|
5696
5884
|
}
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5885
|
+
rows_text.push(row_str);
|
|
5886
|
+
local_cursor = row_start + row_str.length;
|
|
5887
|
+
rows_processed++;
|
|
5700
5888
|
}
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5889
|
+
return rows_text.join("\n");
|
|
5890
|
+
}
|
|
5891
|
+
function build_paragraph_text(paragraph, comments_map, cleanView, style_cache, default_pstyle) {
|
|
5892
|
+
const parts = [];
|
|
5893
|
+
const active_ins = {};
|
|
5894
|
+
const active_del = {};
|
|
5895
|
+
const active_comments = /* @__PURE__ */ new Set();
|
|
5896
|
+
const active_fmt = {};
|
|
5897
|
+
const deferred_meta_states = [];
|
|
5898
|
+
let pending_text = "";
|
|
5899
|
+
let current_wrappers = ["", ""];
|
|
5900
|
+
let current_style = ["", ""];
|
|
5901
|
+
const items = Array.from(iter_paragraph_content(paragraph));
|
|
5902
|
+
const is_heading = is_heading_paragraph(
|
|
5903
|
+
paragraph,
|
|
5904
|
+
style_cache,
|
|
5905
|
+
default_pstyle
|
|
5906
|
+
);
|
|
5907
|
+
const native_heading = is_native_heading(
|
|
5908
|
+
paragraph,
|
|
5909
|
+
style_cache,
|
|
5910
|
+
default_pstyle
|
|
5911
|
+
);
|
|
5912
|
+
let leading_strip_active = is_heading;
|
|
5913
|
+
for (let i = 0; i < items.length; i++) {
|
|
5914
|
+
const item = items[i];
|
|
5915
|
+
if (item instanceof Run) {
|
|
5916
|
+
const [prefix, suffix] = get_run_style_markers(item, native_heading);
|
|
5917
|
+
const text = get_run_text(item);
|
|
5918
|
+
if (cleanView && Object.keys(active_del).length > 0) continue;
|
|
5919
|
+
if (leading_strip_active) {
|
|
5920
|
+
if (!text || !text.trim()) continue;
|
|
5921
|
+
leading_strip_active = false;
|
|
5726
5922
|
}
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5923
|
+
const seg = apply_formatting_to_segments(text, prefix, suffix);
|
|
5924
|
+
if (seg) {
|
|
5925
|
+
const new_wrappers = cleanView ? ["", ""] : _get_wrappers(active_ins, active_del, active_comments, active_fmt);
|
|
5926
|
+
const new_style = [prefix, suffix];
|
|
5927
|
+
if (pending_text && new_wrappers[0] === current_wrappers[0] && new_wrappers[1] === current_wrappers[1]) {
|
|
5928
|
+
if (new_style[0] === current_style[0] && new_style[1] === current_style[1] && current_style[0] !== "" && pending_text.endsWith(current_style[1]) && seg.startsWith(new_style[0])) {
|
|
5929
|
+
pending_text = pending_text.slice(0, -current_style[1].length) + seg.slice(new_style[0].length);
|
|
5930
|
+
} else {
|
|
5931
|
+
pending_text += seg;
|
|
5932
|
+
}
|
|
5933
|
+
current_style = new_style;
|
|
5934
|
+
} else {
|
|
5935
|
+
if (pending_text)
|
|
5936
|
+
parts.push(
|
|
5937
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
5938
|
+
);
|
|
5939
|
+
pending_text = seg;
|
|
5940
|
+
current_wrappers = new_wrappers;
|
|
5941
|
+
current_style = new_style;
|
|
5942
|
+
}
|
|
5943
|
+
if (!cleanView) {
|
|
5944
|
+
const has_meta = Object.keys(active_ins).length > 0 || Object.keys(active_del).length > 0 || active_comments.size > 0 || Object.keys(active_fmt).length > 0;
|
|
5945
|
+
if (has_meta) {
|
|
5946
|
+
deferred_meta_states.push([
|
|
5947
|
+
{ ...active_ins },
|
|
5948
|
+
{ ...active_del },
|
|
5949
|
+
new Set(active_comments),
|
|
5950
|
+
{ ...active_fmt }
|
|
5951
|
+
]);
|
|
5952
|
+
}
|
|
5953
|
+
let should_defer = false;
|
|
5954
|
+
const has_any_meta = Object.keys(active_ins).length > 0 || Object.keys(active_del).length > 0 || Object.keys(active_fmt).length > 0 || active_comments.size > 0;
|
|
5955
|
+
if (has_any_meta) {
|
|
5956
|
+
let j = i + 1;
|
|
5957
|
+
let next_has_meta = false;
|
|
5958
|
+
let temp_ins = Object.keys(active_ins).length;
|
|
5959
|
+
let temp_del = Object.keys(active_del).length;
|
|
5960
|
+
let temp_fmt = Object.keys(active_fmt).length;
|
|
5961
|
+
const temp_comments = new Set(active_comments);
|
|
5962
|
+
while (j < items.length) {
|
|
5963
|
+
const next_item = items[j];
|
|
5964
|
+
if (next_item instanceof Run) {
|
|
5965
|
+
if (!get_run_text(next_item)) {
|
|
5966
|
+
j++;
|
|
5967
|
+
continue;
|
|
5968
|
+
}
|
|
5969
|
+
if (temp_ins > 0 || temp_del > 0 || temp_fmt > 0 || temp_comments.size > 0)
|
|
5970
|
+
next_has_meta = true;
|
|
5971
|
+
break;
|
|
5972
|
+
} else {
|
|
5973
|
+
const ev = next_item;
|
|
5974
|
+
if (ev.type === "ins_start") temp_ins++;
|
|
5975
|
+
else if (ev.type === "ins_end")
|
|
5976
|
+
temp_ins = Math.max(0, temp_ins - 1);
|
|
5977
|
+
else if (ev.type === "del_start") temp_del++;
|
|
5978
|
+
else if (ev.type === "del_end")
|
|
5979
|
+
temp_del = Math.max(0, temp_del - 1);
|
|
5980
|
+
else if (ev.type === "fmt_start") temp_fmt++;
|
|
5981
|
+
else if (ev.type === "fmt_end")
|
|
5982
|
+
temp_fmt = Math.max(0, temp_fmt - 1);
|
|
5983
|
+
else if (ev.type === "start") temp_comments.add(ev.id);
|
|
5984
|
+
else if (ev.type === "end") temp_comments.delete(ev.id);
|
|
5985
|
+
}
|
|
5986
|
+
j++;
|
|
5987
|
+
}
|
|
5988
|
+
if (next_has_meta) should_defer = true;
|
|
5989
|
+
}
|
|
5990
|
+
if (!should_defer && deferred_meta_states.length > 0) {
|
|
5991
|
+
const meta_block = _build_merged_meta_block(
|
|
5992
|
+
deferred_meta_states,
|
|
5993
|
+
comments_map
|
|
5994
|
+
);
|
|
5995
|
+
if (meta_block) {
|
|
5996
|
+
if (pending_text) {
|
|
5997
|
+
parts.push(
|
|
5998
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
5999
|
+
);
|
|
6000
|
+
pending_text = "";
|
|
6001
|
+
current_wrappers = ["", ""];
|
|
6002
|
+
current_style = ["", ""];
|
|
6003
|
+
}
|
|
6004
|
+
parts.push(`{>>${meta_block}<<}`);
|
|
6005
|
+
}
|
|
6006
|
+
deferred_meta_states.length = 0;
|
|
6007
|
+
}
|
|
6008
|
+
}
|
|
5730
6009
|
}
|
|
5731
|
-
}
|
|
5732
|
-
const footnote_ids = _collect_footnote_ids_fast(owned);
|
|
5733
|
-
let para_offset;
|
|
5734
|
-
if (paragraph_offsets instanceof Map) {
|
|
5735
|
-
para_offset = paragraph_offsets.get(paragraph._element);
|
|
5736
6010
|
} else {
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
}
|
|
5755
|
-
function _heading_passes_quality_filter_fast(paragraph, projected_body, paragraph_offsets) {
|
|
5756
|
-
const style = _determine_heading_style(paragraph);
|
|
5757
|
-
if (style !== "(heuristic)") return true;
|
|
5758
|
-
const text = _heading_text_fast(paragraph, projected_body, paragraph_offsets);
|
|
5759
|
-
if (!text) return false;
|
|
5760
|
-
const words = text.match(/\w+/g) || [];
|
|
5761
|
-
return words.length >= _HEURISTIC_MIN_WORDS;
|
|
5762
|
-
}
|
|
5763
|
-
function _heading_text_fast(paragraph, projected_body, paragraph_offsets) {
|
|
5764
|
-
let offset;
|
|
5765
|
-
if (paragraph_offsets instanceof Map) {
|
|
5766
|
-
offset = paragraph_offsets.get(paragraph._element);
|
|
5767
|
-
} else {
|
|
5768
|
-
offset = paragraph_offsets[paragraph._element];
|
|
5769
|
-
}
|
|
5770
|
-
if (offset === void 0) {
|
|
5771
|
-
return "";
|
|
5772
|
-
}
|
|
5773
|
-
const [start, length] = offset;
|
|
5774
|
-
const raw = projected_body.substring(start, start + length);
|
|
5775
|
-
let cleaned = _strip_critic_markup(raw);
|
|
5776
|
-
cleaned = _strip_inline_formatting(cleaned);
|
|
5777
|
-
cleaned = cleaned.replace(/^#+\s+/, "");
|
|
5778
|
-
return cleaned.trim();
|
|
5779
|
-
}
|
|
5780
|
-
function _collect_footnote_ids_fast(owned_items) {
|
|
5781
|
-
const seen = /* @__PURE__ */ new Set();
|
|
5782
|
-
const ordered = [];
|
|
5783
|
-
for (const [kind, item] of owned_items) {
|
|
5784
|
-
if (kind !== "p") continue;
|
|
5785
|
-
for (const event of iter_paragraph_content(item)) {
|
|
5786
|
-
if (!("type" in event)) continue;
|
|
5787
|
-
let fn_id = "";
|
|
5788
|
-
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
5789
|
-
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
5790
|
-
else continue;
|
|
5791
|
-
if (!seen.has(fn_id)) {
|
|
5792
|
-
seen.add(fn_id);
|
|
5793
|
-
ordered.push(fn_id);
|
|
6011
|
+
const ev = item;
|
|
6012
|
+
leading_strip_active = false;
|
|
6013
|
+
if (![
|
|
6014
|
+
"ins_start",
|
|
6015
|
+
"ins_end",
|
|
6016
|
+
"del_start",
|
|
6017
|
+
"del_end",
|
|
6018
|
+
"fmt_start",
|
|
6019
|
+
"fmt_end"
|
|
6020
|
+
].includes(ev.type)) {
|
|
6021
|
+
if (pending_text) {
|
|
6022
|
+
parts.push(
|
|
6023
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6024
|
+
);
|
|
6025
|
+
pending_text = "";
|
|
6026
|
+
current_wrappers = ["", ""];
|
|
6027
|
+
current_style = ["", ""];
|
|
6028
|
+
}
|
|
5794
6029
|
}
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
removed_comment_lines = [];
|
|
5812
|
-
kept_comment_lines = [];
|
|
5813
|
-
metadata_lines = [];
|
|
5814
|
-
structural_lines = [];
|
|
5815
|
-
warnings = [];
|
|
5816
|
-
status = "clean";
|
|
5817
|
-
blocked_reason = null;
|
|
5818
|
-
constructor(filename, mode = "full", author = null) {
|
|
5819
|
-
this.filename = filename;
|
|
5820
|
-
this.mode = mode;
|
|
5821
|
-
this.author = author;
|
|
5822
|
-
}
|
|
5823
|
-
add_transform_lines(lines) {
|
|
5824
|
-
for (const line of lines) {
|
|
5825
|
-
const lower = line.toLowerCase();
|
|
5826
|
-
if (lower.includes("tracked change") || lower.includes("insertion") || lower.includes("deletion") || lower.includes("accepted")) {
|
|
5827
|
-
this.change_lines.push(line);
|
|
5828
|
-
} else if (lower.includes("comment") || lower.includes("[open]") || lower.includes("[resolved]")) {
|
|
5829
|
-
if (lower.includes("kept") || lower.includes("visible")) {
|
|
5830
|
-
this.kept_comment_lines.push(line);
|
|
5831
|
-
} else {
|
|
5832
|
-
this.removed_comment_lines.push(line);
|
|
6030
|
+
if (ev.type === "start") active_comments.add(ev.id);
|
|
6031
|
+
else if (ev.type === "end") active_comments.delete(ev.id);
|
|
6032
|
+
else if (ev.type === "ins_start") active_ins[ev.id] = ev;
|
|
6033
|
+
else if (ev.type === "ins_end") delete active_ins[ev.id];
|
|
6034
|
+
else if (ev.type === "del_start") active_del[ev.id] = ev;
|
|
6035
|
+
else if (ev.type === "del_end") delete active_del[ev.id];
|
|
6036
|
+
else if (ev.type === "fmt_start") active_fmt[ev.id] = ev;
|
|
6037
|
+
else if (ev.type === "fmt_end") delete active_fmt[ev.id];
|
|
6038
|
+
else if (ev.type === "footnote" || ev.type === "endnote") {
|
|
6039
|
+
if (pending_text) {
|
|
6040
|
+
parts.push(
|
|
6041
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6042
|
+
);
|
|
6043
|
+
pending_text = "";
|
|
6044
|
+
current_wrappers = ["", ""];
|
|
6045
|
+
current_style = ["", ""];
|
|
5833
6046
|
}
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
6047
|
+
parts.push(`[^${ev.type === "footnote" ? "fn" : "en"}-${ev.id}]`);
|
|
6048
|
+
} else if (ev.type === "hyperlink_start") {
|
|
6049
|
+
if (pending_text) {
|
|
6050
|
+
parts.push(
|
|
6051
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6052
|
+
);
|
|
6053
|
+
pending_text = "";
|
|
6054
|
+
current_wrappers = ["", ""];
|
|
6055
|
+
current_style = ["", ""];
|
|
6056
|
+
}
|
|
6057
|
+
parts.push("[");
|
|
6058
|
+
} else if (ev.type === "hyperlink_end") {
|
|
6059
|
+
if (pending_text) {
|
|
6060
|
+
parts.push(
|
|
6061
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6062
|
+
);
|
|
6063
|
+
pending_text = "";
|
|
6064
|
+
current_wrappers = ["", ""];
|
|
6065
|
+
current_style = ["", ""];
|
|
6066
|
+
}
|
|
6067
|
+
parts.push(`](${ev.date})`);
|
|
6068
|
+
} else if (ev.type === "xref_start") {
|
|
6069
|
+
if (pending_text) {
|
|
6070
|
+
parts.push(
|
|
6071
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6072
|
+
);
|
|
6073
|
+
pending_text = "";
|
|
6074
|
+
current_wrappers = ["", ""];
|
|
6075
|
+
current_style = ["", ""];
|
|
6076
|
+
}
|
|
6077
|
+
parts.push("[~");
|
|
6078
|
+
} else if (ev.type === "xref_end") {
|
|
6079
|
+
if (pending_text) {
|
|
6080
|
+
parts.push(
|
|
6081
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6082
|
+
);
|
|
6083
|
+
pending_text = "";
|
|
6084
|
+
current_wrappers = ["", ""];
|
|
6085
|
+
current_style = ["", ""];
|
|
6086
|
+
}
|
|
6087
|
+
parts.push(`~](#${ev.id})`);
|
|
6088
|
+
} else if (ev.type === "bookmark") {
|
|
6089
|
+
if (pending_text) {
|
|
6090
|
+
parts.push(
|
|
6091
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6092
|
+
);
|
|
6093
|
+
pending_text = "";
|
|
6094
|
+
current_wrappers = ["", ""];
|
|
6095
|
+
current_style = ["", ""];
|
|
6096
|
+
}
|
|
6097
|
+
parts.push(`{#${ev.id}}`);
|
|
5840
6098
|
}
|
|
5841
6099
|
}
|
|
5842
6100
|
}
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
const
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
if (
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
6101
|
+
if (pending_text)
|
|
6102
|
+
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
6103
|
+
if (deferred_meta_states.length > 0) {
|
|
6104
|
+
const meta_block = _build_merged_meta_block(
|
|
6105
|
+
deferred_meta_states,
|
|
6106
|
+
comments_map
|
|
6107
|
+
);
|
|
6108
|
+
if (meta_block) parts.push(`{>>${meta_block}<<}`);
|
|
6109
|
+
}
|
|
6110
|
+
return parts.join("");
|
|
6111
|
+
}
|
|
6112
|
+
function _get_wrappers(ins, del, comments, fmt) {
|
|
6113
|
+
if (Object.keys(del).length > 0) return ["{--", "--}"];
|
|
6114
|
+
if (Object.keys(ins).length > 0) return ["{++", "++}"];
|
|
6115
|
+
if (comments.size > 0 || Object.keys(fmt).length > 0) return ["{==", "==}"];
|
|
6116
|
+
return ["", ""];
|
|
6117
|
+
}
|
|
6118
|
+
function _build_merged_meta_block(states_list, comments_map) {
|
|
6119
|
+
const change_lines = [];
|
|
6120
|
+
const comment_lines = [];
|
|
6121
|
+
const seen_sigs = /* @__PURE__ */ new Set();
|
|
6122
|
+
for (const [ins_map, del_map, comments_set, fmt_map] of states_list) {
|
|
6123
|
+
let render_comment2 = function(cid) {
|
|
6124
|
+
if (!comments_map[cid]) return;
|
|
6125
|
+
const sig = `Com:${cid}`;
|
|
6126
|
+
if (seen_sigs.has(sig)) return;
|
|
6127
|
+
const data = comments_map[cid];
|
|
6128
|
+
let header = `[${sig}] ${data.author}`;
|
|
6129
|
+
if (data.date) header += ` @ ${data.date}`;
|
|
6130
|
+
if (data.resolved) header += `(RESOLVED)`;
|
|
6131
|
+
comment_lines.push(`${header}: ${data.text}`);
|
|
6132
|
+
seen_sigs.add(sig);
|
|
6133
|
+
if (children_map[cid]) {
|
|
6134
|
+
const children = children_map[cid].sort(
|
|
6135
|
+
(a, b) => (comments_map[a]?.date || "").localeCompare(
|
|
6136
|
+
comments_map[b]?.date || ""
|
|
6137
|
+
)
|
|
6138
|
+
);
|
|
6139
|
+
for (const child_id of children) {
|
|
6140
|
+
render_comment2(child_id);
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
};
|
|
6144
|
+
var render_comment = render_comment2;
|
|
6145
|
+
for (const [uid, meta] of Object.entries(
|
|
6146
|
+
ins_map
|
|
6147
|
+
)) {
|
|
6148
|
+
const sig = `Chg:${uid}`;
|
|
6149
|
+
if (!seen_sigs.has(sig)) {
|
|
6150
|
+
change_lines.push(`[${sig} insert] ${meta.author || "Unknown"}`);
|
|
6151
|
+
seen_sigs.add(sig);
|
|
5865
6152
|
}
|
|
5866
|
-
if (this.author) lines.push(` Author on all markup: "${this.author}"`);
|
|
5867
|
-
}
|
|
5868
|
-
if (this.change_lines.length > 0) {
|
|
5869
|
-
lines.push("");
|
|
5870
|
-
lines.push("TRACKED CHANGES");
|
|
5871
|
-
for (const cl of this.change_lines) lines.push(` ${cl}`);
|
|
5872
|
-
}
|
|
5873
|
-
if (this.removed_comment_lines.length > 0) {
|
|
5874
|
-
lines.push("");
|
|
5875
|
-
lines.push("COMMENTS (stripped)");
|
|
5876
|
-
for (const cl of this.removed_comment_lines) lines.push(` ${cl}`);
|
|
5877
6153
|
}
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
6154
|
+
for (const [uid, meta] of Object.entries(
|
|
6155
|
+
del_map
|
|
6156
|
+
)) {
|
|
6157
|
+
const sig = `Chg:${uid}`;
|
|
6158
|
+
if (!seen_sigs.has(sig)) {
|
|
6159
|
+
change_lines.push(`[${sig} delete] ${meta.author || "Unknown"}`);
|
|
6160
|
+
seen_sigs.add(sig);
|
|
6161
|
+
}
|
|
5882
6162
|
}
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
6163
|
+
for (const [uid, meta] of Object.entries(
|
|
6164
|
+
fmt_map
|
|
6165
|
+
)) {
|
|
6166
|
+
const sig = `Chg:${uid}`;
|
|
6167
|
+
if (!seen_sigs.has(sig)) {
|
|
6168
|
+
change_lines.push(`[${sig} format] ${meta.author || "Unknown"}`);
|
|
6169
|
+
seen_sigs.add(sig);
|
|
6170
|
+
}
|
|
5887
6171
|
}
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
6172
|
+
const children_map = {};
|
|
6173
|
+
for (const [c_id, data] of Object.entries(
|
|
6174
|
+
comments_map
|
|
6175
|
+
)) {
|
|
6176
|
+
const p_id = data.parent_id;
|
|
6177
|
+
if (p_id) {
|
|
6178
|
+
if (!children_map[p_id]) children_map[p_id] = [];
|
|
6179
|
+
children_map[p_id].push(c_id);
|
|
6180
|
+
}
|
|
5892
6181
|
}
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
lines.push(`Result: CLEAN WITH WARNINGS (${this.warnings.length} warning${this.warnings.length > 1 ? "s" : ""})`);
|
|
5897
|
-
} else {
|
|
5898
|
-
lines.push(`Result: CLEAN (${this.tracked_changes_found} changes resolved, ${this.comments_removed} comments removed)`);
|
|
6182
|
+
const sorted_ids = Array.from(comments_set).sort();
|
|
6183
|
+
for (const c_id of sorted_ids) {
|
|
6184
|
+
render_comment2(c_id);
|
|
5899
6185
|
}
|
|
5900
|
-
lines.push(sep);
|
|
5901
|
-
return lines.join("\n");
|
|
5902
6186
|
}
|
|
5903
|
-
|
|
6187
|
+
return [...change_lines, ...comment_lines].join("\n");
|
|
6188
|
+
}
|
|
5904
6189
|
|
|
5905
|
-
// src/
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
if (tag === localName || tag.endsWith(":" + localName)) {
|
|
5912
|
-
result.push(all[i]);
|
|
5913
|
-
}
|
|
6190
|
+
// src/outline.ts
|
|
6191
|
+
var _HEADING_PREFIX_RE = /^(#{1,6}) /;
|
|
6192
|
+
var _HEURISTIC_MIN_WORDS = 3;
|
|
6193
|
+
function extract_outline(doc, projected_body, body_pages, body_page_offsets, paragraph_offsets = null) {
|
|
6194
|
+
if (body_pages.length !== body_page_offsets.length) {
|
|
6195
|
+
throw new Error("body_pages and body_page_offsets length mismatch");
|
|
5914
6196
|
}
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
function coalesce_runs(doc) {
|
|
5918
|
-
let count = 0;
|
|
5919
|
-
function areRunsIdentical(rPr1, rPr2) {
|
|
5920
|
-
const xml1 = rPr1 ? rPr1.toString() : "";
|
|
5921
|
-
const xml2 = rPr2 ? rPr2.toString() : "";
|
|
5922
|
-
return xml1 === xml2;
|
|
6197
|
+
if (paragraph_offsets) {
|
|
6198
|
+
return _extract_outline_fast(doc, projected_body, body_page_offsets, paragraph_offsets);
|
|
5923
6199
|
}
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
}
|
|
5933
|
-
return false;
|
|
6200
|
+
const comments_map = extract_comments_data(doc.pkg);
|
|
6201
|
+
const block_records = _walk_doc_body(doc, comments_map);
|
|
6202
|
+
const heading_indices = [];
|
|
6203
|
+
for (let idx = 0; idx < block_records.length; idx++) {
|
|
6204
|
+
const rec = block_records[idx];
|
|
6205
|
+
if (!(rec.is_paragraph && _is_heading(rec.item))) continue;
|
|
6206
|
+
if (!_heading_passes_quality_filter(rec.item, comments_map)) continue;
|
|
6207
|
+
heading_indices.push(idx);
|
|
5934
6208
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
continue;
|
|
5975
|
-
}
|
|
5976
|
-
}
|
|
5977
|
-
}
|
|
5978
|
-
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(curr.tagName)) {
|
|
5979
|
-
coalesceContainer(curr);
|
|
5980
|
-
}
|
|
5981
|
-
i++;
|
|
6209
|
+
if (heading_indices.length === 0) return [];
|
|
6210
|
+
const nodes = [];
|
|
6211
|
+
for (let h_pos = 0; h_pos < heading_indices.length; h_pos++) {
|
|
6212
|
+
const rec_idx = heading_indices[h_pos];
|
|
6213
|
+
const rec = block_records[rec_idx];
|
|
6214
|
+
const paragraph = rec.item;
|
|
6215
|
+
const level = _heading_level(paragraph);
|
|
6216
|
+
const text = _heading_text(paragraph, comments_map);
|
|
6217
|
+
const style = _determine_heading_style(paragraph);
|
|
6218
|
+
const owned_end = _find_owned_end(
|
|
6219
|
+
block_records,
|
|
6220
|
+
heading_indices,
|
|
6221
|
+
h_pos,
|
|
6222
|
+
level
|
|
6223
|
+
);
|
|
6224
|
+
const owned_blocks = block_records.slice(rec_idx + 1, owned_end);
|
|
6225
|
+
const has_table = _direct_has_table(block_records, rec_idx + 1, owned_end);
|
|
6226
|
+
const footnote_ids = _collect_footnote_ids(owned_blocks);
|
|
6227
|
+
const page_num = _offset_to_page(rec.start_offset, body_page_offsets);
|
|
6228
|
+
nodes.push({ level, text, page: page_num, style, has_table, footnote_ids });
|
|
6229
|
+
}
|
|
6230
|
+
return nodes;
|
|
6231
|
+
}
|
|
6232
|
+
function _direct_has_table(block_records, range_start, range_end) {
|
|
6233
|
+
for (let idx = range_start; idx < range_end; idx++) {
|
|
6234
|
+
const rec = block_records[idx];
|
|
6235
|
+
if (rec.is_paragraph && _is_heading(rec.item)) return false;
|
|
6236
|
+
if (rec.is_table) return true;
|
|
6237
|
+
}
|
|
6238
|
+
return false;
|
|
6239
|
+
}
|
|
6240
|
+
function _walk_doc_body(doc, comments_map) {
|
|
6241
|
+
const parts = Array.from(iter_document_parts(doc));
|
|
6242
|
+
let body_start_offset = 0;
|
|
6243
|
+
let body_part = null;
|
|
6244
|
+
for (const part of parts) {
|
|
6245
|
+
if (part === doc) {
|
|
6246
|
+
body_part = part;
|
|
6247
|
+
break;
|
|
5982
6248
|
}
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
if (
|
|
5986
|
-
|
|
5987
|
-
}
|
|
6249
|
+
const part_text = _project_part(part, comments_map);
|
|
6250
|
+
if (part_text) {
|
|
6251
|
+
if (body_start_offset > 0) body_start_offset += 2;
|
|
6252
|
+
body_start_offset += part_text.length;
|
|
5988
6253
|
}
|
|
5989
6254
|
}
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
}
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
const
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6255
|
+
if (!body_part) {
|
|
6256
|
+
body_part = doc;
|
|
6257
|
+
body_start_offset = 0;
|
|
6258
|
+
} else {
|
|
6259
|
+
if (body_start_offset > 0) body_start_offset += 2;
|
|
6260
|
+
}
|
|
6261
|
+
const records = [];
|
|
6262
|
+
let cursor = body_start_offset;
|
|
6263
|
+
let is_first_block = true;
|
|
6264
|
+
for (const item of iter_block_items(body_part)) {
|
|
6265
|
+
if (item instanceof Paragraph) {
|
|
6266
|
+
const prefix = get_paragraph_prefix(item);
|
|
6267
|
+
const p_text = build_paragraph_text(item, comments_map, false);
|
|
6268
|
+
const block_len = (prefix + p_text).length;
|
|
6269
|
+
if (!is_first_block) cursor += 2;
|
|
6270
|
+
records.push({
|
|
6271
|
+
item,
|
|
6272
|
+
is_paragraph: true,
|
|
6273
|
+
is_table: false,
|
|
6274
|
+
start_offset: cursor,
|
|
6275
|
+
projected_length: block_len
|
|
6276
|
+
});
|
|
6277
|
+
cursor += block_len;
|
|
6278
|
+
is_first_block = false;
|
|
6279
|
+
} else if (item instanceof Table) {
|
|
6280
|
+
const table_text = extract_table(item, comments_map, false, 0);
|
|
6281
|
+
const block_len = table_text ? table_text.length : 0;
|
|
6282
|
+
if (!is_first_block) cursor += 2;
|
|
6283
|
+
const table_start = cursor;
|
|
6284
|
+
records.push({
|
|
6285
|
+
item,
|
|
6286
|
+
is_paragraph: false,
|
|
6287
|
+
is_table: true,
|
|
6288
|
+
start_offset: table_start,
|
|
6289
|
+
projected_length: block_len
|
|
6290
|
+
});
|
|
6291
|
+
_record_table_inner_blocks_lite(item, table_start, records, comments_map);
|
|
6292
|
+
cursor += block_len;
|
|
6293
|
+
is_first_block = false;
|
|
6004
6294
|
}
|
|
6005
6295
|
}
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6296
|
+
return records;
|
|
6297
|
+
}
|
|
6298
|
+
function _compute_inner_block_offset(table, target_paragraph, table_start_offset, comments_map) {
|
|
6299
|
+
const target_el = target_paragraph._element;
|
|
6300
|
+
let cursor = table_start_offset;
|
|
6301
|
+
let rows_processed = 0;
|
|
6302
|
+
for (const row of table.rows) {
|
|
6303
|
+
if (rows_processed > 0) cursor += 1;
|
|
6304
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
6305
|
+
let cells_in_row = 0;
|
|
6306
|
+
for (const cell of row.cells) {
|
|
6307
|
+
if (seen_cells.has(cell)) continue;
|
|
6308
|
+
seen_cells.add(cell);
|
|
6309
|
+
if (cells_in_row > 0) cursor += 3;
|
|
6310
|
+
const [new_cursor, found] = _walk_cell_for_offset(
|
|
6311
|
+
cell,
|
|
6312
|
+
target_el,
|
|
6313
|
+
cursor,
|
|
6314
|
+
comments_map
|
|
6315
|
+
);
|
|
6316
|
+
if (found) return new_cursor;
|
|
6317
|
+
cursor = new_cursor;
|
|
6318
|
+
cells_in_row++;
|
|
6011
6319
|
}
|
|
6320
|
+
rows_processed++;
|
|
6012
6321
|
}
|
|
6013
|
-
return
|
|
6322
|
+
return table_start_offset;
|
|
6014
6323
|
}
|
|
6015
|
-
function
|
|
6016
|
-
let
|
|
6017
|
-
|
|
6018
|
-
const
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
if (
|
|
6022
|
-
|
|
6023
|
-
|
|
6324
|
+
function _walk_cell_for_offset(cell, target_el, cell_start_cursor, comments_map) {
|
|
6325
|
+
let cursor = cell_start_cursor;
|
|
6326
|
+
let is_first_block = true;
|
|
6327
|
+
for (const inner_item of iter_block_items(cell)) {
|
|
6328
|
+
if (!is_first_block) cursor += 2;
|
|
6329
|
+
if (inner_item instanceof Paragraph) {
|
|
6330
|
+
if (inner_item._element === target_el) return [cursor, true];
|
|
6331
|
+
const prefix = get_paragraph_prefix(inner_item);
|
|
6332
|
+
const p_text = build_paragraph_text(inner_item, comments_map, false);
|
|
6333
|
+
cursor += (prefix + p_text).length;
|
|
6334
|
+
} else if (inner_item instanceof Table) {
|
|
6335
|
+
const nested_offset = _compute_inner_block_offset(
|
|
6336
|
+
inner_item,
|
|
6337
|
+
new Paragraph(target_el, null),
|
|
6338
|
+
cursor,
|
|
6339
|
+
comments_map
|
|
6340
|
+
);
|
|
6341
|
+
if (nested_offset !== cursor) {
|
|
6342
|
+
if (_element_is_descendant(target_el, inner_item._element))
|
|
6343
|
+
return [nested_offset, true];
|
|
6024
6344
|
}
|
|
6345
|
+
const table_text = extract_table(inner_item, comments_map, false, 0);
|
|
6346
|
+
cursor += table_text ? table_text.length : 0;
|
|
6025
6347
|
}
|
|
6348
|
+
is_first_block = false;
|
|
6026
6349
|
}
|
|
6027
|
-
return
|
|
6350
|
+
return [cursor, false];
|
|
6028
6351
|
}
|
|
6029
|
-
function
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6352
|
+
function _element_is_descendant(target_el, ancestor_el) {
|
|
6353
|
+
let cur = target_el.parentNode;
|
|
6354
|
+
while (cur) {
|
|
6355
|
+
if (cur === ancestor_el) return true;
|
|
6356
|
+
cur = cur.parentNode;
|
|
6357
|
+
}
|
|
6358
|
+
return false;
|
|
6033
6359
|
}
|
|
6034
|
-
function
|
|
6035
|
-
|
|
6036
|
-
for (const
|
|
6037
|
-
const
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6360
|
+
function _record_table_inner_blocks_lite(table, inherited_offset, records, comments_map) {
|
|
6361
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
6362
|
+
for (const row of table.rows) {
|
|
6363
|
+
for (const cell of row.cells) {
|
|
6364
|
+
if (seen_cells.has(cell)) continue;
|
|
6365
|
+
seen_cells.add(cell);
|
|
6366
|
+
for (const inner_item of iter_block_items(cell)) {
|
|
6367
|
+
if (inner_item instanceof Paragraph) {
|
|
6368
|
+
const true_offset = _is_heading(inner_item) ? _compute_inner_block_offset(
|
|
6369
|
+
table,
|
|
6370
|
+
inner_item,
|
|
6371
|
+
inherited_offset,
|
|
6372
|
+
comments_map
|
|
6373
|
+
) : inherited_offset;
|
|
6374
|
+
records.push({
|
|
6375
|
+
item: inner_item,
|
|
6376
|
+
is_paragraph: true,
|
|
6377
|
+
is_table: false,
|
|
6378
|
+
start_offset: true_offset,
|
|
6379
|
+
projected_length: 0
|
|
6380
|
+
});
|
|
6381
|
+
} else if (inner_item instanceof Table) {
|
|
6382
|
+
records.push({
|
|
6383
|
+
item: inner_item,
|
|
6384
|
+
is_paragraph: false,
|
|
6385
|
+
is_table: true,
|
|
6386
|
+
start_offset: inherited_offset,
|
|
6387
|
+
projected_length: 0
|
|
6388
|
+
});
|
|
6389
|
+
_record_table_inner_blocks_lite(
|
|
6390
|
+
inner_item,
|
|
6391
|
+
inherited_offset,
|
|
6392
|
+
records,
|
|
6393
|
+
comments_map
|
|
6394
|
+
);
|
|
6395
|
+
}
|
|
6042
6396
|
}
|
|
6043
6397
|
}
|
|
6044
6398
|
}
|
|
6045
|
-
return count ? [`Empty property elements: ${count} removed`] : [];
|
|
6046
6399
|
}
|
|
6047
|
-
function
|
|
6048
|
-
|
|
6049
|
-
const
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6400
|
+
function _project_part(part, comments_map) {
|
|
6401
|
+
const blocks = [];
|
|
6402
|
+
const c_type = part.constructor.name;
|
|
6403
|
+
if (c_type === "NotesPart") {
|
|
6404
|
+
const header = part.note_type === "fn" ? "## Footnotes" : "## Endnotes";
|
|
6405
|
+
blocks.push(`---
|
|
6406
|
+
${header}`);
|
|
6407
|
+
}
|
|
6408
|
+
let is_first_para = true;
|
|
6409
|
+
for (const item of iter_block_items(part)) {
|
|
6410
|
+
if (item.constructor.name === "FootnoteItem") {
|
|
6411
|
+
const fn_text = _project_part(item, comments_map);
|
|
6412
|
+
if (fn_text) blocks.push(fn_text);
|
|
6413
|
+
} else if (item instanceof Paragraph) {
|
|
6414
|
+
let prefix = get_paragraph_prefix(item);
|
|
6415
|
+
if (is_first_para && c_type === "FootnoteItem")
|
|
6416
|
+
prefix = `[^${part.note_type}-${part.id}]: ${prefix}`;
|
|
6417
|
+
const p_text = build_paragraph_text(item, comments_map, false);
|
|
6418
|
+
blocks.push(prefix + p_text);
|
|
6419
|
+
is_first_para = false;
|
|
6420
|
+
} else if (item instanceof Table) {
|
|
6421
|
+
const table_text = extract_table(item, comments_map, false, 0);
|
|
6422
|
+
if (table_text) blocks.push(table_text);
|
|
6423
|
+
is_first_para = false;
|
|
6057
6424
|
}
|
|
6058
6425
|
}
|
|
6059
|
-
return
|
|
6426
|
+
return blocks.join("\n\n");
|
|
6060
6427
|
}
|
|
6061
|
-
function
|
|
6062
|
-
|
|
6063
|
-
const del = findAllDescendants(doc.element, "w:del").length;
|
|
6064
|
-
const fmt = findAllDescendants(doc.element, "w:rPrChange").length + findAllDescendants(doc.element, "w:pPrChange").length + findAllDescendants(doc.element, "w:sectPrChange").length;
|
|
6065
|
-
return [ins, del, fmt];
|
|
6428
|
+
function _is_heading(paragraph) {
|
|
6429
|
+
return _HEADING_PREFIX_RE.test(get_paragraph_prefix(paragraph));
|
|
6066
6430
|
}
|
|
6067
|
-
function
|
|
6068
|
-
const
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
}
|
|
6075
|
-
return authors;
|
|
6431
|
+
function _heading_passes_quality_filter(paragraph, comments_map) {
|
|
6432
|
+
const style = _determine_heading_style(paragraph);
|
|
6433
|
+
if (style !== "(heuristic)") return true;
|
|
6434
|
+
const text = _heading_text(paragraph, comments_map);
|
|
6435
|
+
if (!text) return false;
|
|
6436
|
+
const word_count = (text.match(/\w+/g) || []).length;
|
|
6437
|
+
return word_count >= _HEURISTIC_MIN_WORDS;
|
|
6076
6438
|
}
|
|
6077
|
-
function
|
|
6078
|
-
const
|
|
6079
|
-
|
|
6080
|
-
for (const t of ts) if (t.textContent) texts.push(t.textContent);
|
|
6081
|
-
const dts = findAllDescendants(el, "w:delText");
|
|
6082
|
-
for (const dt of dts) if (dt.textContent) texts.push(dt.textContent);
|
|
6083
|
-
return texts.join("");
|
|
6439
|
+
function _heading_level(paragraph) {
|
|
6440
|
+
const match = _HEADING_PREFIX_RE.exec(get_paragraph_prefix(paragraph));
|
|
6441
|
+
return match ? Math.min(match[1].length, 6) : 1;
|
|
6084
6442
|
}
|
|
6085
|
-
function
|
|
6086
|
-
const
|
|
6087
|
-
|
|
6088
|
-
|
|
6443
|
+
function _heading_text(paragraph, comments_map) {
|
|
6444
|
+
const p_text = build_paragraph_text(paragraph, comments_map, false);
|
|
6445
|
+
let cleaned = _strip_critic_markup(p_text);
|
|
6446
|
+
cleaned = _strip_inline_formatting(cleaned);
|
|
6447
|
+
return cleaned.trim();
|
|
6089
6448
|
}
|
|
6090
|
-
function
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
}
|
|
6098
|
-
for (const del of delEls) {
|
|
6099
|
-
const text = _getElementText(del).trim();
|
|
6100
|
-
if (text) lines.push(` Accepted deletion of: "${_truncate(text, 60)}"`);
|
|
6101
|
-
}
|
|
6102
|
-
const engine = new RedlineEngine(doc);
|
|
6103
|
-
engine.accept_all_revisions();
|
|
6104
|
-
for (const tag of ["w:rPrChange", "w:pPrChange", "w:sectPrChange"]) {
|
|
6105
|
-
for (const el of findAllDescendants(doc.element, tag)) {
|
|
6106
|
-
el.parentNode?.removeChild(el);
|
|
6107
|
-
}
|
|
6108
|
-
}
|
|
6109
|
-
const total = insEls.length + delEls.length;
|
|
6110
|
-
if (total) {
|
|
6111
|
-
return [`Tracked changes auto-accepted: ${total}`].concat(lines);
|
|
6112
|
-
}
|
|
6113
|
-
return [];
|
|
6449
|
+
function _strip_critic_markup(text) {
|
|
6450
|
+
if (!text) return "";
|
|
6451
|
+
text = text.replace(/\{--[\s\S]*?--\}/g, "");
|
|
6452
|
+
text = text.replace(/\{>>[\s\S]*?<<\}/g, "");
|
|
6453
|
+
text = text.replace(/\{\+\+([\s\S]*?)\+\+\}/g, "$1");
|
|
6454
|
+
text = text.replace(/\{==([\s\S]*?)==\}/g, "$1");
|
|
6455
|
+
return text;
|
|
6114
6456
|
}
|
|
6115
|
-
function
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
if (info.resolved) resolvedCount++;
|
|
6122
|
-
else openCount++;
|
|
6123
|
-
comments.push({ id: cId, ...info });
|
|
6124
|
-
}
|
|
6125
|
-
return { total: comments.length, open: openCount, resolved: resolvedCount, comments };
|
|
6457
|
+
function _strip_inline_formatting(text) {
|
|
6458
|
+
if (!text) return "";
|
|
6459
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
6460
|
+
text = text.replace(/__(.+?)__/g, "$1");
|
|
6461
|
+
text = text.replace(/(?<!\w)_(\S(?:.*?\S)?)_(?!\w)/g, "$1");
|
|
6462
|
+
return text;
|
|
6126
6463
|
}
|
|
6127
|
-
function
|
|
6128
|
-
const
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
const
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
const
|
|
6135
|
-
|
|
6136
|
-
cm.deleteComment(cId);
|
|
6137
|
-
}
|
|
6138
|
-
for (const tag of ["w:commentRangeStart", "w:commentRangeEnd"]) {
|
|
6139
|
-
for (const el of findAllDescendants(doc.element, tag)) {
|
|
6140
|
-
el.parentNode?.removeChild(el);
|
|
6141
|
-
}
|
|
6464
|
+
function _determine_heading_style(paragraph) {
|
|
6465
|
+
const [style_cache, default_pstyle] = _get_style_cache(
|
|
6466
|
+
paragraph._parent.part || paragraph._parent
|
|
6467
|
+
);
|
|
6468
|
+
const pPr = findChild(paragraph._element, "w:pPr");
|
|
6469
|
+
let style_id = default_pstyle;
|
|
6470
|
+
if (pPr) {
|
|
6471
|
+
const pStyle = findChild(pPr, "w:pStyle");
|
|
6472
|
+
if (pStyle) style_id = pStyle.getAttribute("w:val") || default_pstyle;
|
|
6142
6473
|
}
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
(c) => c.nodeType === 1 && c.tagName !== "w:rPr" && c.tagName !== "rPr"
|
|
6150
|
-
);
|
|
6151
|
-
if (nonRprChildren.length <= 1) {
|
|
6152
|
-
parent.parentNode?.removeChild(parent);
|
|
6153
|
-
} else {
|
|
6154
|
-
parent.removeChild(ref);
|
|
6155
|
-
}
|
|
6156
|
-
} else {
|
|
6157
|
-
parent.removeChild(ref);
|
|
6158
|
-
}
|
|
6474
|
+
let outline_level = null;
|
|
6475
|
+
let outline_level_from_style = false;
|
|
6476
|
+
if (pPr) {
|
|
6477
|
+
const oLvl = findChild(pPr, "w:outlineLvl");
|
|
6478
|
+
if (oLvl && /^\d+$/.test(oLvl.getAttribute("w:val") || "")) {
|
|
6479
|
+
outline_level = parseInt(oLvl.getAttribute("w:val"), 10);
|
|
6159
6480
|
}
|
|
6160
6481
|
}
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
}
|
|
6165
|
-
function eject_comment_parts(doc) {
|
|
6166
|
-
const pkg = doc.pkg;
|
|
6167
|
-
const comment_partnames = /* @__PURE__ */ new Set();
|
|
6168
|
-
for (const part of pkg.parts) {
|
|
6169
|
-
if (part.partname.toLowerCase().includes("comments")) {
|
|
6170
|
-
comment_partnames.add(part.partname);
|
|
6171
|
-
const withSlash = part.partname.startsWith("/") ? part.partname : "/" + part.partname;
|
|
6172
|
-
const withoutSlash = part.partname.startsWith("/") ? part.partname.substring(1) : part.partname;
|
|
6173
|
-
comment_partnames.add(withSlash);
|
|
6174
|
-
comment_partnames.add(withoutSlash);
|
|
6175
|
-
}
|
|
6482
|
+
if (outline_level === null && style_id && style_cache && style_cache[style_id]) {
|
|
6483
|
+
outline_level = style_cache[style_id].outline_level;
|
|
6484
|
+
outline_level_from_style = true;
|
|
6176
6485
|
}
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
if (target.toLowerCase().includes("comments")) {
|
|
6185
|
-
toRemove.push(rel);
|
|
6186
|
-
const sourcePath = part.partname.replace("/_rels/", "/").replace(".rels", "");
|
|
6187
|
-
const sourcePart = pkg.getPartByPath(sourcePath);
|
|
6188
|
-
if (sourcePart) {
|
|
6189
|
-
const relId = rel.getAttribute("Id");
|
|
6190
|
-
if (relId) sourcePart.rels.delete(relId);
|
|
6191
|
-
}
|
|
6192
|
-
}
|
|
6193
|
-
}
|
|
6194
|
-
for (const relEl of toRemove) {
|
|
6195
|
-
relEl.parentNode?.removeChild(relEl);
|
|
6196
|
-
}
|
|
6486
|
+
const style_name = style_id && style_cache && style_cache[style_id] ? style_cache[style_id].name : style_id;
|
|
6487
|
+
let normalized_style_name = style_name;
|
|
6488
|
+
if (normalized_style_name && typeof normalized_style_name === "string") {
|
|
6489
|
+
if (normalized_style_name.toLowerCase().startsWith("heading")) {
|
|
6490
|
+
normalized_style_name = normalized_style_name.replace(/^heading/i, "Heading");
|
|
6491
|
+
} else if (normalized_style_name.toLowerCase() === "title") {
|
|
6492
|
+
normalized_style_name = "Title";
|
|
6197
6493
|
}
|
|
6198
6494
|
}
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
for (const override of overrides) {
|
|
6204
|
-
const partName = override.getAttribute("PartName") || "";
|
|
6205
|
-
if (comment_partnames.has(partName) || partName.toLowerCase().includes("comments")) {
|
|
6206
|
-
toRemove.push(override);
|
|
6207
|
-
}
|
|
6208
|
-
}
|
|
6209
|
-
for (const overrideEl of toRemove) {
|
|
6210
|
-
overrideEl.parentNode?.removeChild(overrideEl);
|
|
6495
|
+
if (outline_level_from_style && outline_level !== null) {
|
|
6496
|
+
const is_heading_or_title = normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title");
|
|
6497
|
+
if (!is_heading_or_title) {
|
|
6498
|
+
outline_level = null;
|
|
6211
6499
|
}
|
|
6212
6500
|
}
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
delete pkg.unzipped[key];
|
|
6501
|
+
if (outline_level !== null && outline_level >= 0 && outline_level <= 8) {
|
|
6502
|
+
if (normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title")) {
|
|
6503
|
+
return normalized_style_name;
|
|
6217
6504
|
}
|
|
6505
|
+
return "(outline_level)";
|
|
6218
6506
|
}
|
|
6507
|
+
if (normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title"))
|
|
6508
|
+
return normalized_style_name;
|
|
6509
|
+
if (normalized_style_name && /Heading[ ]?([1-6])(?![0-9])/.test(normalized_style_name))
|
|
6510
|
+
return normalized_style_name;
|
|
6511
|
+
return "(heuristic)";
|
|
6219
6512
|
}
|
|
6220
|
-
function
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
for (const c of comments) {
|
|
6226
|
-
const author = c.getAttribute("w:author");
|
|
6227
|
-
if (author) {
|
|
6228
|
-
original.add(author);
|
|
6229
|
-
c.setAttribute("w:author", newAuthor);
|
|
6230
|
-
}
|
|
6231
|
-
if (c.hasAttribute("w:initials")) {
|
|
6232
|
-
const initials = newAuthor.split(" ").filter(Boolean).map((p) => p[0]).join("").toUpperCase();
|
|
6233
|
-
c.setAttribute("w:initials", initials);
|
|
6234
|
-
}
|
|
6513
|
+
function _find_owned_end(block_records, heading_indices, current_h_pos, current_level) {
|
|
6514
|
+
for (let next_h_pos = current_h_pos + 1; next_h_pos < heading_indices.length; next_h_pos++) {
|
|
6515
|
+
const next_idx = heading_indices[next_h_pos];
|
|
6516
|
+
if (_heading_level(block_records[next_idx].item) <= current_level)
|
|
6517
|
+
return next_idx;
|
|
6235
6518
|
}
|
|
6236
|
-
return
|
|
6519
|
+
return block_records.length;
|
|
6237
6520
|
}
|
|
6238
|
-
function
|
|
6239
|
-
const
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6521
|
+
function _collect_footnote_ids(owned_blocks) {
|
|
6522
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6523
|
+
const ordered = [];
|
|
6524
|
+
for (const rec of owned_blocks) {
|
|
6525
|
+
if (!rec.is_paragraph) continue;
|
|
6526
|
+
for (const event of iter_paragraph_content(rec.item)) {
|
|
6527
|
+
if (!("type" in event)) continue;
|
|
6528
|
+
let fn_id = "";
|
|
6529
|
+
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
6530
|
+
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
6531
|
+
else continue;
|
|
6532
|
+
if (!seen.has(fn_id)) {
|
|
6533
|
+
seen.add(fn_id);
|
|
6534
|
+
ordered.push(fn_id);
|
|
6246
6535
|
}
|
|
6247
6536
|
}
|
|
6248
6537
|
}
|
|
6249
|
-
return
|
|
6538
|
+
return ordered;
|
|
6250
6539
|
}
|
|
6251
|
-
function
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
for (
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6540
|
+
function _offset_to_page(offset, body_page_offsets) {
|
|
6541
|
+
if (!body_page_offsets || body_page_offsets.length === 0) return 1;
|
|
6542
|
+
let page = 1;
|
|
6543
|
+
for (let i = 0; i < body_page_offsets.length; i++) {
|
|
6544
|
+
if (offset >= body_page_offsets[i]) page = i + 1;
|
|
6545
|
+
else break;
|
|
6546
|
+
}
|
|
6547
|
+
return page;
|
|
6548
|
+
}
|
|
6549
|
+
function _extract_outline_fast(doc, projected_body, body_page_offsets, paragraph_offsets) {
|
|
6550
|
+
const paragraphs_and_tables = [];
|
|
6551
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
6552
|
+
function walk(container) {
|
|
6553
|
+
for (const item of iter_block_items(container)) {
|
|
6554
|
+
const i_type = item.constructor.name;
|
|
6555
|
+
if (i_type === "FootnoteItem") {
|
|
6556
|
+
walk(item);
|
|
6557
|
+
} else if (item instanceof Paragraph) {
|
|
6558
|
+
paragraphs_and_tables.push(["p", item]);
|
|
6559
|
+
} else if (item instanceof Table) {
|
|
6560
|
+
paragraphs_and_tables.push(["t", item]);
|
|
6561
|
+
for (const row of item.rows) {
|
|
6562
|
+
for (const cell of row.cells) {
|
|
6563
|
+
if (seen_cells.has(cell._element)) {
|
|
6564
|
+
continue;
|
|
6565
|
+
}
|
|
6566
|
+
seen_cells.add(cell._element);
|
|
6567
|
+
walk(cell);
|
|
6568
|
+
}
|
|
6569
|
+
}
|
|
6259
6570
|
}
|
|
6260
6571
|
}
|
|
6261
6572
|
}
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6573
|
+
walk(doc);
|
|
6574
|
+
const heading_indices = [];
|
|
6575
|
+
for (let idx = 0; idx < paragraphs_and_tables.length; idx++) {
|
|
6576
|
+
const [kind, item] = paragraphs_and_tables[idx];
|
|
6577
|
+
if (kind !== "p") continue;
|
|
6578
|
+
let hasOffset = false;
|
|
6579
|
+
if (paragraph_offsets instanceof Map) {
|
|
6580
|
+
hasOffset = paragraph_offsets.has(item._element);
|
|
6581
|
+
} else {
|
|
6582
|
+
hasOffset = item._element in paragraph_offsets;
|
|
6583
|
+
}
|
|
6584
|
+
if (!hasOffset) {
|
|
6585
|
+
continue;
|
|
6586
|
+
}
|
|
6587
|
+
if (!_is_heading(item)) continue;
|
|
6588
|
+
if (!_heading_passes_quality_filter_fast(item, projected_body, paragraph_offsets)) continue;
|
|
6589
|
+
heading_indices.push(idx);
|
|
6590
|
+
}
|
|
6591
|
+
if (heading_indices.length === 0) return [];
|
|
6592
|
+
const nodes = [];
|
|
6593
|
+
for (let h_pos = 0; h_pos < heading_indices.length; h_pos++) {
|
|
6594
|
+
const item_idx = heading_indices[h_pos];
|
|
6595
|
+
const paragraph = paragraphs_and_tables[item_idx][1];
|
|
6596
|
+
const level = _heading_level(paragraph);
|
|
6597
|
+
const text = _heading_text_fast(paragraph, projected_body, paragraph_offsets);
|
|
6598
|
+
const style = _determine_heading_style(paragraph);
|
|
6599
|
+
let owned_end = item_idx;
|
|
6600
|
+
for (let next_h_pos = h_pos + 1; next_h_pos < heading_indices.length; next_h_pos++) {
|
|
6601
|
+
const next_idx = heading_indices[next_h_pos];
|
|
6602
|
+
const next_paragraph = paragraphs_and_tables[next_idx][1];
|
|
6603
|
+
if (_heading_level(next_paragraph) <= level) {
|
|
6604
|
+
owned_end = next_idx;
|
|
6605
|
+
break;
|
|
6273
6606
|
}
|
|
6274
|
-
}
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6607
|
+
}
|
|
6608
|
+
if (owned_end === item_idx) {
|
|
6609
|
+
owned_end = paragraphs_and_tables.length;
|
|
6610
|
+
}
|
|
6611
|
+
const owned = paragraphs_and_tables.slice(item_idx + 1, owned_end);
|
|
6612
|
+
let has_table = false;
|
|
6613
|
+
for (const [kind2, item2] of owned) {
|
|
6614
|
+
if (kind2 === "p" && _is_heading(item2)) {
|
|
6615
|
+
break;
|
|
6280
6616
|
}
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
if (c.textContent && parseInt(c.textContent) > 1) {
|
|
6285
|
-
lines.push(`Revision count: ${c.textContent} \u2192 1`);
|
|
6286
|
-
c.textContent = "1";
|
|
6617
|
+
if (kind2 === "t") {
|
|
6618
|
+
has_table = true;
|
|
6619
|
+
break;
|
|
6287
6620
|
}
|
|
6288
|
-
});
|
|
6289
|
-
}
|
|
6290
|
-
const appPart = doc.pkg.getPartByPath("docProps/app.xml");
|
|
6291
|
-
if (appPart) {
|
|
6292
|
-
const docEl = appPart._element;
|
|
6293
|
-
const intFields = ["TotalTime", "Words", "Characters", "Paragraphs", "Lines", "CharactersWithSpaces"];
|
|
6294
|
-
for (const f of intFields) {
|
|
6295
|
-
findDescendantsByLocalName(docEl, f).forEach((el) => {
|
|
6296
|
-
if (el.textContent && el.textContent !== "0") {
|
|
6297
|
-
if (f === "TotalTime") lines.push(`Total editing time: ${el.textContent} minutes`);
|
|
6298
|
-
el.textContent = "0";
|
|
6299
|
-
}
|
|
6300
|
-
});
|
|
6301
6621
|
}
|
|
6302
|
-
const
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
}
|
|
6309
|
-
});
|
|
6622
|
+
const footnote_ids = _collect_footnote_ids_fast(owned);
|
|
6623
|
+
let para_offset;
|
|
6624
|
+
if (paragraph_offsets instanceof Map) {
|
|
6625
|
+
para_offset = paragraph_offsets.get(paragraph._element);
|
|
6626
|
+
} else {
|
|
6627
|
+
para_offset = paragraph_offsets[paragraph._element];
|
|
6310
6628
|
}
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
let modified = false;
|
|
6316
|
-
const epoch = "1970-01-01T00:00:00Z";
|
|
6317
|
-
const corePart = doc.pkg.getPartByPath("docProps/core.xml");
|
|
6318
|
-
if (corePart) {
|
|
6319
|
-
for (const tag of ["created", "modified", "lastPrinted"]) {
|
|
6320
|
-
findDescendantsByLocalName(corePart._element, tag).forEach((el) => {
|
|
6321
|
-
if (el.textContent && el.textContent !== epoch) {
|
|
6322
|
-
el.textContent = epoch;
|
|
6323
|
-
modified = true;
|
|
6324
|
-
}
|
|
6325
|
-
});
|
|
6629
|
+
let page_num = 1;
|
|
6630
|
+
if (para_offset !== void 0) {
|
|
6631
|
+
const [start_offset] = para_offset;
|
|
6632
|
+
page_num = _offset_to_page(start_offset, body_page_offsets);
|
|
6326
6633
|
}
|
|
6634
|
+
nodes.push({
|
|
6635
|
+
level,
|
|
6636
|
+
text,
|
|
6637
|
+
page: page_num,
|
|
6638
|
+
style,
|
|
6639
|
+
has_table,
|
|
6640
|
+
footnote_ids
|
|
6641
|
+
});
|
|
6327
6642
|
}
|
|
6328
|
-
return
|
|
6643
|
+
return nodes;
|
|
6329
6644
|
}
|
|
6330
|
-
function
|
|
6331
|
-
const
|
|
6332
|
-
if (
|
|
6333
|
-
const
|
|
6334
|
-
|
|
6335
|
-
const
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
}
|
|
6343
|
-
|
|
6344
|
-
if (rootRels) removeRelationsTo(rootRels);
|
|
6345
|
-
const docRels = doc.pkg.getOrCreateRelsPart(doc.part.partname);
|
|
6346
|
-
if (docRels) removeRelationsTo(docRels);
|
|
6347
|
-
for (const sdtPr of findAllDescendants(doc.element, "w:sdtPr")) {
|
|
6348
|
-
findChildren(sdtPr, "w:dataBinding").forEach((b) => sdtPr.removeChild(b));
|
|
6645
|
+
function _heading_passes_quality_filter_fast(paragraph, projected_body, paragraph_offsets) {
|
|
6646
|
+
const style = _determine_heading_style(paragraph);
|
|
6647
|
+
if (style !== "(heuristic)") return true;
|
|
6648
|
+
const text = _heading_text_fast(paragraph, projected_body, paragraph_offsets);
|
|
6649
|
+
if (!text) return false;
|
|
6650
|
+
const words = text.match(/\w+/g) || [];
|
|
6651
|
+
return words.length >= _HEURISTIC_MIN_WORDS;
|
|
6652
|
+
}
|
|
6653
|
+
function _heading_text_fast(paragraph, projected_body, paragraph_offsets) {
|
|
6654
|
+
let offset;
|
|
6655
|
+
if (paragraph_offsets instanceof Map) {
|
|
6656
|
+
offset = paragraph_offsets.get(paragraph._element);
|
|
6657
|
+
} else {
|
|
6658
|
+
offset = paragraph_offsets[paragraph._element];
|
|
6349
6659
|
}
|
|
6350
|
-
|
|
6660
|
+
if (offset === void 0) {
|
|
6661
|
+
return "";
|
|
6662
|
+
}
|
|
6663
|
+
const [start, length] = offset;
|
|
6664
|
+
const raw = projected_body.substring(start, start + length);
|
|
6665
|
+
let cleaned = _strip_critic_markup(raw);
|
|
6666
|
+
cleaned = _strip_inline_formatting(cleaned);
|
|
6667
|
+
cleaned = cleaned.replace(/^#+\s+/, "");
|
|
6668
|
+
return cleaned.trim();
|
|
6351
6669
|
}
|
|
6352
|
-
function
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
if (
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6670
|
+
function _collect_footnote_ids_fast(owned_items) {
|
|
6671
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6672
|
+
const ordered = [];
|
|
6673
|
+
for (const [kind, item] of owned_items) {
|
|
6674
|
+
if (kind !== "p") continue;
|
|
6675
|
+
for (const event of iter_paragraph_content(item)) {
|
|
6676
|
+
if (!("type" in event)) continue;
|
|
6677
|
+
let fn_id = "";
|
|
6678
|
+
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
6679
|
+
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
6680
|
+
else continue;
|
|
6681
|
+
if (!seen.has(fn_id)) {
|
|
6682
|
+
seen.add(fn_id);
|
|
6683
|
+
ordered.push(fn_id);
|
|
6362
6684
|
}
|
|
6363
6685
|
}
|
|
6364
6686
|
}
|
|
6365
|
-
return
|
|
6687
|
+
return ordered;
|
|
6366
6688
|
}
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6689
|
+
|
|
6690
|
+
// src/sanitize/report.ts
|
|
6691
|
+
var SanitizeReport = class {
|
|
6692
|
+
filename;
|
|
6693
|
+
mode;
|
|
6694
|
+
author;
|
|
6695
|
+
tracked_changes_found = 0;
|
|
6696
|
+
tracked_changes_accepted = 0;
|
|
6697
|
+
tracked_changes_kept = 0;
|
|
6698
|
+
change_lines = [];
|
|
6699
|
+
comments_removed = 0;
|
|
6700
|
+
comments_kept = 0;
|
|
6701
|
+
removed_comment_lines = [];
|
|
6702
|
+
kept_comment_lines = [];
|
|
6703
|
+
metadata_lines = [];
|
|
6704
|
+
structural_lines = [];
|
|
6705
|
+
warnings = [];
|
|
6706
|
+
status = "clean";
|
|
6707
|
+
blocked_reason = null;
|
|
6708
|
+
constructor(filename, mode = "full", author = null) {
|
|
6709
|
+
this.filename = filename;
|
|
6710
|
+
this.mode = mode;
|
|
6711
|
+
this.author = author;
|
|
6712
|
+
}
|
|
6713
|
+
add_transform_lines(lines) {
|
|
6714
|
+
for (const line of lines) {
|
|
6715
|
+
const lower = line.toLowerCase();
|
|
6716
|
+
if (lower.includes("tracked change") || lower.includes("insertion") || lower.includes("deletion") || lower.includes("accepted")) {
|
|
6717
|
+
this.change_lines.push(line);
|
|
6718
|
+
} else if (lower.includes("comment") || lower.includes("[open]") || lower.includes("[resolved]")) {
|
|
6719
|
+
if (lower.includes("kept") || lower.includes("visible")) {
|
|
6720
|
+
this.kept_comment_lines.push(line);
|
|
6721
|
+
} else {
|
|
6722
|
+
this.removed_comment_lines.push(line);
|
|
6378
6723
|
}
|
|
6724
|
+
} else if (lower.includes("author") || lower.includes("template") || lower.includes("company") || lower.includes("manager") || lower.includes("metadata") || lower.includes("timestamp") || lower.includes("custom xml") || lower.includes("last modified by") || lower.includes("revision count") || lower.includes("last printed")) {
|
|
6725
|
+
this.metadata_lines.push(line);
|
|
6726
|
+
} else if (lower.includes("hyperlink") || lower.includes("warning")) {
|
|
6727
|
+
this.warnings.push(line);
|
|
6728
|
+
} else {
|
|
6729
|
+
this.structural_lines.push(line);
|
|
6379
6730
|
}
|
|
6380
6731
|
}
|
|
6381
6732
|
}
|
|
6382
|
-
|
|
6383
|
-
|
|
6733
|
+
render() {
|
|
6734
|
+
const sep = "\u2550".repeat(50);
|
|
6735
|
+
const lines = [sep, `Finalization Report: ${this.filename}`];
|
|
6736
|
+
const flags = [];
|
|
6737
|
+
if (this.mode === "keep-markup") flags.push("--keep-markup");
|
|
6738
|
+
if (this.author) flags.push(`--author "${this.author}"`);
|
|
6739
|
+
if (this.tracked_changes_accepted > 0) flags.push("--accept-all");
|
|
6740
|
+
if (flags.length > 0) lines.push(flags.join(" "));
|
|
6741
|
+
lines.push(sep);
|
|
6742
|
+
if (this.status === "blocked") {
|
|
6743
|
+
lines.push("");
|
|
6744
|
+
lines.push(`BLOCKED: ${this.blocked_reason}`);
|
|
6745
|
+
lines.push(sep);
|
|
6746
|
+
return lines.join("\n");
|
|
6747
|
+
}
|
|
6748
|
+
if (this.mode === "keep-markup" && (this.tracked_changes_kept > 0 || this.comments_kept > 0)) {
|
|
6749
|
+
lines.push("");
|
|
6750
|
+
lines.push("VISIBLE TO COUNTERPARTY");
|
|
6751
|
+
if (this.tracked_changes_kept > 0) lines.push(` Tracked changes: ${this.tracked_changes_kept}`);
|
|
6752
|
+
if (this.comments_kept > 0) {
|
|
6753
|
+
lines.push(` Open comments: ${this.comments_kept}`);
|
|
6754
|
+
for (const cl of this.kept_comment_lines) lines.push(` ${cl}`);
|
|
6755
|
+
}
|
|
6756
|
+
if (this.author) lines.push(` Author on all markup: "${this.author}"`);
|
|
6757
|
+
}
|
|
6758
|
+
if (this.change_lines.length > 0) {
|
|
6759
|
+
lines.push("");
|
|
6760
|
+
lines.push("TRACKED CHANGES");
|
|
6761
|
+
for (const cl of this.change_lines) lines.push(` ${cl}`);
|
|
6762
|
+
}
|
|
6763
|
+
if (this.removed_comment_lines.length > 0) {
|
|
6764
|
+
lines.push("");
|
|
6765
|
+
lines.push("COMMENTS (stripped)");
|
|
6766
|
+
for (const cl of this.removed_comment_lines) lines.push(` ${cl}`);
|
|
6767
|
+
}
|
|
6768
|
+
if (this.metadata_lines.length > 0) {
|
|
6769
|
+
lines.push("");
|
|
6770
|
+
lines.push("METADATA");
|
|
6771
|
+
for (const ml of this.metadata_lines) lines.push(` ${ml}`);
|
|
6772
|
+
}
|
|
6773
|
+
if (this.structural_lines.length > 0) {
|
|
6774
|
+
lines.push("");
|
|
6775
|
+
lines.push("STRUCTURAL & PROTECTION");
|
|
6776
|
+
for (const sl of this.structural_lines) lines.push(` ${sl}`);
|
|
6777
|
+
}
|
|
6778
|
+
if (this.warnings.length > 0) {
|
|
6779
|
+
lines.push("");
|
|
6780
|
+
lines.push("WARNINGS");
|
|
6781
|
+
for (const w of this.warnings) lines.push(` \u26A0 ${w}`);
|
|
6782
|
+
}
|
|
6783
|
+
lines.push("");
|
|
6784
|
+
lines.push(sep);
|
|
6785
|
+
if (this.warnings.length > 0) {
|
|
6786
|
+
lines.push(`Result: CLEAN WITH WARNINGS (${this.warnings.length} warning${this.warnings.length > 1 ? "s" : ""})`);
|
|
6787
|
+
} else {
|
|
6788
|
+
lines.push(`Result: CLEAN (${this.tracked_changes_found} changes resolved, ${this.comments_removed} comments removed)`);
|
|
6789
|
+
}
|
|
6790
|
+
lines.push(sep);
|
|
6791
|
+
return lines.join("\n");
|
|
6792
|
+
}
|
|
6793
|
+
};
|
|
6384
6794
|
|
|
6385
6795
|
// src/sanitize/core.ts
|
|
6386
6796
|
async function finalize_document(doc, options) {
|