@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.js
CHANGED
|
@@ -1282,7 +1282,12 @@ ${header}`;
|
|
|
1282
1282
|
this._add_virtual_text(prefix, current, item);
|
|
1283
1283
|
current += prefix.length;
|
|
1284
1284
|
}
|
|
1285
|
-
current = this._map_paragraph_content(
|
|
1285
|
+
current = this._map_paragraph_content(
|
|
1286
|
+
item,
|
|
1287
|
+
current,
|
|
1288
|
+
style_cache,
|
|
1289
|
+
default_pstyle
|
|
1290
|
+
);
|
|
1286
1291
|
is_first_para = false;
|
|
1287
1292
|
previous_item = item;
|
|
1288
1293
|
} else if (item instanceof Table) {
|
|
@@ -1355,7 +1360,13 @@ ${header}`;
|
|
|
1355
1360
|
}
|
|
1356
1361
|
_map_paragraph_content(paragraph, start_offset, style_cache, default_pstyle) {
|
|
1357
1362
|
let current = start_offset;
|
|
1358
|
-
const span = {
|
|
1363
|
+
const span = {
|
|
1364
|
+
start: current,
|
|
1365
|
+
end: current,
|
|
1366
|
+
text: "",
|
|
1367
|
+
run: null,
|
|
1368
|
+
paragraph
|
|
1369
|
+
};
|
|
1359
1370
|
this.spans.push(span);
|
|
1360
1371
|
const active_ids = /* @__PURE__ */ new Set();
|
|
1361
1372
|
const active_ins = {};
|
|
@@ -1373,7 +1384,7 @@ ${header}`;
|
|
|
1373
1384
|
this._add_virtual_text(s_tok, current, paragraph);
|
|
1374
1385
|
current += s_tok.length;
|
|
1375
1386
|
}
|
|
1376
|
-
for (const [kind, txt, r_obj, i_id, d_id] of pending_runs) {
|
|
1387
|
+
for (const [kind, txt, r_obj, i_id, d_id, c_ids] of pending_runs) {
|
|
1377
1388
|
if (kind === "virtual") {
|
|
1378
1389
|
this._add_virtual_text(txt, current, paragraph, active_hyperlink_id);
|
|
1379
1390
|
} else {
|
|
@@ -1385,7 +1396,8 @@ ${header}`;
|
|
|
1385
1396
|
paragraph,
|
|
1386
1397
|
ins_id: i_id || void 0,
|
|
1387
1398
|
del_id: d_id || void 0,
|
|
1388
|
-
hyperlink_id: active_hyperlink_id || void 0
|
|
1399
|
+
hyperlink_id: active_hyperlink_id || void 0,
|
|
1400
|
+
comment_ids: c_ids.length > 0 ? c_ids : void 0
|
|
1389
1401
|
};
|
|
1390
1402
|
this.spans.push(s);
|
|
1391
1403
|
this._text_chunks.push(txt);
|
|
@@ -1399,8 +1411,16 @@ ${header}`;
|
|
|
1399
1411
|
pending_runs = [];
|
|
1400
1412
|
};
|
|
1401
1413
|
const items = Array.from(iter_paragraph_content(paragraph));
|
|
1402
|
-
const is_heading = is_heading_paragraph(
|
|
1403
|
-
|
|
1414
|
+
const is_heading = is_heading_paragraph(
|
|
1415
|
+
paragraph,
|
|
1416
|
+
style_cache,
|
|
1417
|
+
default_pstyle
|
|
1418
|
+
);
|
|
1419
|
+
const native_heading = is_native_heading(
|
|
1420
|
+
paragraph,
|
|
1421
|
+
style_cache,
|
|
1422
|
+
default_pstyle
|
|
1423
|
+
);
|
|
1404
1424
|
let leading_strip_active = is_heading;
|
|
1405
1425
|
for (let i = 0; i < items.length; i++) {
|
|
1406
1426
|
const item = items[i];
|
|
@@ -1435,7 +1455,12 @@ ${header}`;
|
|
|
1435
1455
|
const curr_ins_id = Object.keys(active_ins).pop() || null;
|
|
1436
1456
|
const curr_del_id = Object.keys(active_del).pop() || null;
|
|
1437
1457
|
if (full_seg_text && !(this.clean_view && curr_del_id) && !(this.original_view && curr_ins_id)) {
|
|
1438
|
-
const new_wrappers = this.clean_view || this.original_view ? ["", ""] : this._get_wrappers(
|
|
1458
|
+
const new_wrappers = this.clean_view || this.original_view ? ["", ""] : this._get_wrappers(
|
|
1459
|
+
curr_ins_id,
|
|
1460
|
+
curr_del_id,
|
|
1461
|
+
active_ids,
|
|
1462
|
+
active_fmt
|
|
1463
|
+
);
|
|
1439
1464
|
const new_style = [prefix, suffix];
|
|
1440
1465
|
if (pending_runs.length > 0 && new_wrappers[0] === current_wrappers[0] && new_wrappers[1] === current_wrappers[1]) {
|
|
1441
1466
|
let skip_leading_prefix = false;
|
|
@@ -1443,36 +1468,58 @@ ${header}`;
|
|
|
1443
1468
|
pending_runs.pop();
|
|
1444
1469
|
skip_leading_prefix = true;
|
|
1445
1470
|
}
|
|
1471
|
+
const curr_comment_ids = Array.from(active_ids);
|
|
1446
1472
|
for (const [kind, txt, r_obj] of run_parts) {
|
|
1447
1473
|
if (skip_leading_prefix && kind === "virtual" && txt === new_style[0]) {
|
|
1448
1474
|
skip_leading_prefix = false;
|
|
1449
1475
|
continue;
|
|
1450
1476
|
}
|
|
1451
|
-
pending_runs.push([
|
|
1477
|
+
pending_runs.push([
|
|
1478
|
+
kind,
|
|
1479
|
+
txt,
|
|
1480
|
+
r_obj,
|
|
1481
|
+
curr_ins_id,
|
|
1482
|
+
curr_del_id,
|
|
1483
|
+
curr_comment_ids
|
|
1484
|
+
]);
|
|
1452
1485
|
}
|
|
1453
1486
|
current_style = new_style;
|
|
1454
1487
|
} else {
|
|
1455
1488
|
flush_pending_runs();
|
|
1456
1489
|
current_wrappers = new_wrappers;
|
|
1457
1490
|
current_style = new_style;
|
|
1491
|
+
const curr_comment_ids = Array.from(active_ids);
|
|
1458
1492
|
for (const [kind, txt, r_obj] of run_parts) {
|
|
1459
|
-
pending_runs.push([
|
|
1493
|
+
pending_runs.push([
|
|
1494
|
+
kind,
|
|
1495
|
+
txt,
|
|
1496
|
+
r_obj,
|
|
1497
|
+
curr_ins_id,
|
|
1498
|
+
curr_del_id,
|
|
1499
|
+
curr_comment_ids
|
|
1500
|
+
]);
|
|
1460
1501
|
}
|
|
1461
1502
|
}
|
|
1462
1503
|
}
|
|
1463
1504
|
if (!this.clean_view && !this.original_view) {
|
|
1464
1505
|
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;
|
|
1465
1506
|
if (has_meta) {
|
|
1466
|
-
deferred_meta_states.push([
|
|
1507
|
+
deferred_meta_states.push([
|
|
1508
|
+
{ ...active_ins },
|
|
1509
|
+
{ ...active_del },
|
|
1510
|
+
new Set(active_ids),
|
|
1511
|
+
{ ...active_fmt }
|
|
1512
|
+
]);
|
|
1467
1513
|
}
|
|
1468
1514
|
let should_defer = false;
|
|
1469
|
-
const
|
|
1470
|
-
if (
|
|
1515
|
+
const has_any_meta = curr_ins_id !== null || curr_del_id !== null || Object.keys(active_fmt).length > 0 || active_ids.size > 0;
|
|
1516
|
+
if (has_any_meta) {
|
|
1471
1517
|
let j = i + 1;
|
|
1472
|
-
let
|
|
1518
|
+
let next_has_meta = false;
|
|
1473
1519
|
let temp_ins_count = Object.keys(active_ins).length;
|
|
1474
1520
|
let temp_del_count = Object.keys(active_del).length;
|
|
1475
1521
|
let temp_fmt_count = Object.keys(active_fmt).length;
|
|
1522
|
+
const temp_comment_ids = new Set(active_ids);
|
|
1476
1523
|
while (j < items.length) {
|
|
1477
1524
|
const next_item = items[j];
|
|
1478
1525
|
if (next_item instanceof Run) {
|
|
@@ -1480,22 +1527,27 @@ ${header}`;
|
|
|
1480
1527
|
j++;
|
|
1481
1528
|
continue;
|
|
1482
1529
|
}
|
|
1483
|
-
if (temp_ins_count > 0 || temp_del_count > 0 || temp_fmt_count > 0) {
|
|
1484
|
-
|
|
1530
|
+
if (temp_ins_count > 0 || temp_del_count > 0 || temp_fmt_count > 0 || temp_comment_ids.size > 0) {
|
|
1531
|
+
next_has_meta = true;
|
|
1485
1532
|
}
|
|
1486
1533
|
break;
|
|
1487
1534
|
} else {
|
|
1488
1535
|
const ev = next_item;
|
|
1489
1536
|
if (ev.type === "ins_start") temp_ins_count++;
|
|
1490
|
-
else if (ev.type === "ins_end")
|
|
1537
|
+
else if (ev.type === "ins_end")
|
|
1538
|
+
temp_ins_count = Math.max(0, temp_ins_count - 1);
|
|
1491
1539
|
else if (ev.type === "del_start") temp_del_count++;
|
|
1492
|
-
else if (ev.type === "del_end")
|
|
1540
|
+
else if (ev.type === "del_end")
|
|
1541
|
+
temp_del_count = Math.max(0, temp_del_count - 1);
|
|
1493
1542
|
else if (ev.type === "fmt_start") temp_fmt_count++;
|
|
1494
|
-
else if (ev.type === "fmt_end")
|
|
1543
|
+
else if (ev.type === "fmt_end")
|
|
1544
|
+
temp_fmt_count = Math.max(0, temp_fmt_count - 1);
|
|
1545
|
+
else if (ev.type === "start") temp_comment_ids.add(ev.id);
|
|
1546
|
+
else if (ev.type === "end") temp_comment_ids.delete(ev.id);
|
|
1495
1547
|
}
|
|
1496
1548
|
j++;
|
|
1497
1549
|
}
|
|
1498
|
-
if (
|
|
1550
|
+
if (next_has_meta) should_defer = true;
|
|
1499
1551
|
}
|
|
1500
1552
|
if (!should_defer && deferred_meta_states.length > 0) {
|
|
1501
1553
|
const meta_block = this._build_merged_meta_block(deferred_meta_states);
|
|
@@ -1584,7 +1636,8 @@ ${header}`;
|
|
|
1584
1636
|
_get_wrappers(ins_id, del_id, active_ids, active_fmt) {
|
|
1585
1637
|
if (del_id) return ["{--", "--}"];
|
|
1586
1638
|
if (ins_id) return ["{++", "++}"];
|
|
1587
|
-
if (active_ids.size > 0 || Object.keys(active_fmt).length > 0)
|
|
1639
|
+
if (active_ids.size > 0 || Object.keys(active_fmt).length > 0)
|
|
1640
|
+
return ["{==", "==}"];
|
|
1588
1641
|
return ["", ""];
|
|
1589
1642
|
}
|
|
1590
1643
|
_build_merged_meta_block(states_list) {
|
|
@@ -1592,7 +1645,9 @@ ${header}`;
|
|
|
1592
1645
|
const comment_lines = [];
|
|
1593
1646
|
const seen_sigs = /* @__PURE__ */ new Set();
|
|
1594
1647
|
for (const [ins_map, del_map, comments_set, fmt_map] of states_list) {
|
|
1595
|
-
for (const [uid, meta] of Object.entries(
|
|
1648
|
+
for (const [uid, meta] of Object.entries(
|
|
1649
|
+
ins_map
|
|
1650
|
+
)) {
|
|
1596
1651
|
const sig = `Chg:${uid}`;
|
|
1597
1652
|
if (!seen_sigs.has(sig)) {
|
|
1598
1653
|
const auth = meta.author || "Unknown";
|
|
@@ -1600,7 +1655,9 @@ ${header}`;
|
|
|
1600
1655
|
seen_sigs.add(sig);
|
|
1601
1656
|
}
|
|
1602
1657
|
}
|
|
1603
|
-
for (const [uid, meta] of Object.entries(
|
|
1658
|
+
for (const [uid, meta] of Object.entries(
|
|
1659
|
+
del_map
|
|
1660
|
+
)) {
|
|
1604
1661
|
const sig = `Chg:${uid}`;
|
|
1605
1662
|
if (!seen_sigs.has(sig)) {
|
|
1606
1663
|
const auth = meta.author || "Unknown";
|
|
@@ -1608,7 +1665,9 @@ ${header}`;
|
|
|
1608
1665
|
seen_sigs.add(sig);
|
|
1609
1666
|
}
|
|
1610
1667
|
}
|
|
1611
|
-
for (const [uid, meta] of Object.entries(
|
|
1668
|
+
for (const [uid, meta] of Object.entries(
|
|
1669
|
+
fmt_map
|
|
1670
|
+
)) {
|
|
1612
1671
|
const sig = `Chg:${uid}`;
|
|
1613
1672
|
if (!seen_sigs.has(sig)) {
|
|
1614
1673
|
const auth = meta.author || "Unknown";
|
|
@@ -1682,7 +1741,16 @@ ${header}`;
|
|
|
1682
1741
|
if (remaining) parts.push(escapeRegExp(remaining));
|
|
1683
1742
|
return parts.join("");
|
|
1684
1743
|
}
|
|
1685
|
-
find_match_index(target_text) {
|
|
1744
|
+
find_match_index(target_text, is_regex = false) {
|
|
1745
|
+
if (is_regex) {
|
|
1746
|
+
try {
|
|
1747
|
+
const pattern = new RegExp(target_text);
|
|
1748
|
+
const match = pattern.exec(this.full_text);
|
|
1749
|
+
if (match) return [match.index, match[0].length];
|
|
1750
|
+
} catch (e) {
|
|
1751
|
+
}
|
|
1752
|
+
return [-1, 0];
|
|
1753
|
+
}
|
|
1686
1754
|
let start_idx = this.full_text.indexOf(target_text);
|
|
1687
1755
|
if (start_idx !== -1) return [start_idx, target_text.length];
|
|
1688
1756
|
const norm_full = this._replace_smart_quotes(this.full_text);
|
|
@@ -1702,22 +1770,41 @@ ${header}`;
|
|
|
1702
1770
|
}
|
|
1703
1771
|
return [-1, 0];
|
|
1704
1772
|
}
|
|
1705
|
-
find_all_match_indices(target_text) {
|
|
1773
|
+
find_all_match_indices(target_text, is_regex = false) {
|
|
1706
1774
|
if (!target_text) return [];
|
|
1775
|
+
if (is_regex) {
|
|
1776
|
+
try {
|
|
1777
|
+
const pattern = new RegExp(target_text, "g");
|
|
1778
|
+
const matches2 = [...this.full_text.matchAll(pattern)];
|
|
1779
|
+
if (matches2.length > 0)
|
|
1780
|
+
return matches2.map((m) => [m.index, m[0].length]);
|
|
1781
|
+
} catch (e) {
|
|
1782
|
+
}
|
|
1783
|
+
return [];
|
|
1784
|
+
}
|
|
1707
1785
|
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1708
|
-
let matches = [
|
|
1786
|
+
let matches = [
|
|
1787
|
+
...this.full_text.matchAll(new RegExp(escapeRegExp(target_text), "g"))
|
|
1788
|
+
];
|
|
1709
1789
|
if (matches.length > 0) return matches.map((m) => [m.index, m[0].length]);
|
|
1710
1790
|
const norm_full = this._replace_smart_quotes(this.full_text);
|
|
1711
1791
|
const norm_target = this._replace_smart_quotes(target_text);
|
|
1712
|
-
matches = [
|
|
1792
|
+
matches = [
|
|
1793
|
+
...norm_full.matchAll(new RegExp(escapeRegExp(norm_target), "g"))
|
|
1794
|
+
];
|
|
1713
1795
|
if (matches.length > 0) return matches.map((m) => [m.index, m[0].length]);
|
|
1714
1796
|
const stripped_target = this._strip_markdown_formatting(target_text);
|
|
1715
|
-
matches = [
|
|
1797
|
+
matches = [
|
|
1798
|
+
...this.full_text.matchAll(
|
|
1799
|
+
new RegExp(escapeRegExp(stripped_target), "g")
|
|
1800
|
+
)
|
|
1801
|
+
];
|
|
1716
1802
|
if (matches.length > 0) return matches.map((m) => [m.index, m[0].length]);
|
|
1717
1803
|
try {
|
|
1718
1804
|
const pattern = new RegExp(this._make_fuzzy_regex(target_text), "g");
|
|
1719
1805
|
matches = [...this.full_text.matchAll(pattern)];
|
|
1720
|
-
if (matches.length > 0)
|
|
1806
|
+
if (matches.length > 0)
|
|
1807
|
+
return matches.map((m) => [m.index, m[0].length]);
|
|
1721
1808
|
} catch (e) {
|
|
1722
1809
|
}
|
|
1723
1810
|
return [];
|
|
@@ -1728,14 +1815,22 @@ ${header}`;
|
|
|
1728
1815
|
return this._resolve_runs_at_range(start_idx, start_idx + length);
|
|
1729
1816
|
}
|
|
1730
1817
|
find_target_runs_by_index(start_index, length, rebuild_map = true) {
|
|
1731
|
-
return this._resolve_runs_at_range(
|
|
1818
|
+
return this._resolve_runs_at_range(
|
|
1819
|
+
start_index,
|
|
1820
|
+
start_index + length,
|
|
1821
|
+
rebuild_map
|
|
1822
|
+
);
|
|
1732
1823
|
}
|
|
1733
1824
|
get_virtual_spans_in_range(start_index, length) {
|
|
1734
1825
|
const end_index = start_index + length;
|
|
1735
|
-
return this.spans.filter(
|
|
1826
|
+
return this.spans.filter(
|
|
1827
|
+
(s) => s.run === null && s.text === "\n\n" && s.start >= start_index && s.end <= end_index
|
|
1828
|
+
);
|
|
1736
1829
|
}
|
|
1737
1830
|
_resolve_runs_at_range(start_idx, end_idx, rebuild_map = true) {
|
|
1738
|
-
const affected_spans = this.spans.filter(
|
|
1831
|
+
const affected_spans = this.spans.filter(
|
|
1832
|
+
(s) => s.end > start_idx && s.start < end_idx
|
|
1833
|
+
);
|
|
1739
1834
|
if (affected_spans.length === 0) return [];
|
|
1740
1835
|
const working_runs = affected_spans.filter((s) => s.run !== null).map((s) => s.run);
|
|
1741
1836
|
if (working_runs.length === 0) return [];
|
|
@@ -1746,7 +1841,10 @@ ${header}`;
|
|
|
1746
1841
|
const local_start = start_idx - first_real_span.start;
|
|
1747
1842
|
if (local_start > 0) {
|
|
1748
1843
|
const idx_in_working = 0;
|
|
1749
|
-
const [, right_run] = this._split_run_at_index(
|
|
1844
|
+
const [, right_run] = this._split_run_at_index(
|
|
1845
|
+
working_runs[idx_in_working],
|
|
1846
|
+
local_start
|
|
1847
|
+
);
|
|
1750
1848
|
working_runs[idx_in_working] = right_run;
|
|
1751
1849
|
dom_modified = true;
|
|
1752
1850
|
start_split_adjustment = local_start;
|
|
@@ -1783,7 +1881,9 @@ ${header}`;
|
|
|
1783
1881
|
if (preceding[i].paragraph) return [null, preceding[i].paragraph];
|
|
1784
1882
|
}
|
|
1785
1883
|
}
|
|
1786
|
-
const containing = this.spans.filter(
|
|
1884
|
+
const containing = this.spans.filter(
|
|
1885
|
+
(s) => s.start < index && index < s.end
|
|
1886
|
+
);
|
|
1787
1887
|
if (containing.length > 0) {
|
|
1788
1888
|
const span = containing[0];
|
|
1789
1889
|
if (span.run === null) {
|
|
@@ -1806,10 +1906,12 @@ ${header}`;
|
|
|
1806
1906
|
const preceding_gap = this.spans.filter((s) => s.end < index);
|
|
1807
1907
|
if (preceding_gap.length > 0) {
|
|
1808
1908
|
for (let i = preceding_gap.length - 1; i >= 0; i--) {
|
|
1809
|
-
if (preceding_gap[i].run)
|
|
1909
|
+
if (preceding_gap[i].run)
|
|
1910
|
+
return [preceding_gap[i].run, preceding_gap[i].paragraph];
|
|
1810
1911
|
}
|
|
1811
1912
|
for (let i = preceding_gap.length - 1; i >= 0; i--) {
|
|
1812
|
-
if (preceding_gap[i].paragraph)
|
|
1913
|
+
if (preceding_gap[i].paragraph)
|
|
1914
|
+
return [null, preceding_gap[i].paragraph];
|
|
1813
1915
|
}
|
|
1814
1916
|
}
|
|
1815
1917
|
return [null, null];
|
|
@@ -1822,7 +1924,10 @@ ${header}`;
|
|
|
1822
1924
|
const new_r_element = run._element.cloneNode(true);
|
|
1823
1925
|
this._set_run_text_elements(new_r_element, right_text);
|
|
1824
1926
|
if (run._element.parentNode) {
|
|
1825
|
-
run._element.parentNode.insertBefore(
|
|
1927
|
+
run._element.parentNode.insertBefore(
|
|
1928
|
+
new_r_element,
|
|
1929
|
+
run._element.nextSibling
|
|
1930
|
+
);
|
|
1826
1931
|
}
|
|
1827
1932
|
const new_run = new Run(new_r_element, run._parent);
|
|
1828
1933
|
return [run, new_run];
|
|
@@ -1849,7 +1954,9 @@ ${header}`;
|
|
|
1849
1954
|
}
|
|
1850
1955
|
}
|
|
1851
1956
|
get_context_at_range(start_idx, end_idx) {
|
|
1852
|
-
const real_spans = this.spans.filter(
|
|
1957
|
+
const real_spans = this.spans.filter(
|
|
1958
|
+
(s) => s.run && s.end > start_idx && s.start < end_idx
|
|
1959
|
+
);
|
|
1853
1960
|
if (real_spans.length > 0) return real_spans[0];
|
|
1854
1961
|
return null;
|
|
1855
1962
|
}
|
|
@@ -2201,6 +2308,193 @@ function create_word_patch_diff(original_text, modified_text, original_path = "O
|
|
|
2201
2308
|
return output.join("\n");
|
|
2202
2309
|
}
|
|
2203
2310
|
|
|
2311
|
+
// src/pagination.ts
|
|
2312
|
+
var PAGE_TARGET_CHARS = 19e3;
|
|
2313
|
+
var APPENDIX_MARKER = "<!-- READONLY_BOUNDARY_START -->";
|
|
2314
|
+
var _CRITIC_TOKENS = {
|
|
2315
|
+
"{++": "++}",
|
|
2316
|
+
"{--": "--}",
|
|
2317
|
+
"{==": "==}",
|
|
2318
|
+
"{>>": "<<}"
|
|
2319
|
+
};
|
|
2320
|
+
var _CHG_ID_PATTERN = /\bChg:(\d+)\b/g;
|
|
2321
|
+
function split_structural_appendix(markdown) {
|
|
2322
|
+
if (!markdown) return ["", ""];
|
|
2323
|
+
const idx = markdown.indexOf(APPENDIX_MARKER);
|
|
2324
|
+
if (idx === -1) return [markdown, ""];
|
|
2325
|
+
const line_start = markdown.lastIndexOf("\n", idx) + 1;
|
|
2326
|
+
const body = markdown.substring(0, line_start).trimEnd();
|
|
2327
|
+
const appendix = markdown.substring(line_start);
|
|
2328
|
+
return [body, appendix];
|
|
2329
|
+
}
|
|
2330
|
+
function paginate(markdown_body, structural_appendix = "") {
|
|
2331
|
+
if (!markdown_body) {
|
|
2332
|
+
const appendix_clean = structural_appendix ? structural_appendix.trim() : "";
|
|
2333
|
+
const content = appendix_clean;
|
|
2334
|
+
return {
|
|
2335
|
+
pages: [{
|
|
2336
|
+
page: 1,
|
|
2337
|
+
total_pages: 1,
|
|
2338
|
+
has_next: false,
|
|
2339
|
+
has_prev: false,
|
|
2340
|
+
tracked_change_count: _count_tracked_changes(content),
|
|
2341
|
+
page_content: content
|
|
2342
|
+
}],
|
|
2343
|
+
total_pages: 1,
|
|
2344
|
+
body_pages: [""],
|
|
2345
|
+
body_page_offsets: [0]
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
const block_records = _tokenize_into_atomic_blocks(markdown_body);
|
|
2349
|
+
const [body_pages, body_page_offsets] = _assemble_pages(block_records);
|
|
2350
|
+
let final_pages;
|
|
2351
|
+
if (structural_appendix && structural_appendix.trim()) {
|
|
2352
|
+
const appendix = structural_appendix.trim();
|
|
2353
|
+
final_pages = body_pages.map((bp) => bp ? `${bp}
|
|
2354
|
+
|
|
2355
|
+
${appendix}` : appendix);
|
|
2356
|
+
} else {
|
|
2357
|
+
final_pages = [...body_pages];
|
|
2358
|
+
}
|
|
2359
|
+
const total = final_pages.length;
|
|
2360
|
+
const page_infos = final_pages.map((content, i) => ({
|
|
2361
|
+
page: i + 1,
|
|
2362
|
+
total_pages: total,
|
|
2363
|
+
has_next: i + 1 < total,
|
|
2364
|
+
has_prev: i + 1 > 1,
|
|
2365
|
+
tracked_change_count: _count_tracked_changes(content),
|
|
2366
|
+
page_content: content
|
|
2367
|
+
}));
|
|
2368
|
+
return {
|
|
2369
|
+
pages: page_infos,
|
|
2370
|
+
total_pages: total,
|
|
2371
|
+
body_pages,
|
|
2372
|
+
body_page_offsets
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
function _tokenize_into_atomic_blocks(markdown_body) {
|
|
2376
|
+
const raw_blocks = _split_on_safe_paragraph_breaks(markdown_body);
|
|
2377
|
+
return _merge_footnote_sections(raw_blocks);
|
|
2378
|
+
}
|
|
2379
|
+
function _split_on_safe_paragraph_breaks(text) {
|
|
2380
|
+
const counters = { "++}": 0, "--}": 0, "==}": 0, "<<}": 0 };
|
|
2381
|
+
const blocks = [];
|
|
2382
|
+
let block_start = 0;
|
|
2383
|
+
let i = 0;
|
|
2384
|
+
const n = text.length;
|
|
2385
|
+
while (i < n) {
|
|
2386
|
+
let matched_open = false;
|
|
2387
|
+
for (const [open_tok, close_tok] of Object.entries(_CRITIC_TOKENS)) {
|
|
2388
|
+
if (text.startsWith(open_tok, i)) {
|
|
2389
|
+
counters[close_tok]++;
|
|
2390
|
+
i += open_tok.length;
|
|
2391
|
+
matched_open = true;
|
|
2392
|
+
break;
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
if (matched_open) continue;
|
|
2396
|
+
let matched_close = false;
|
|
2397
|
+
for (const close_tok of Object.values(_CRITIC_TOKENS)) {
|
|
2398
|
+
if (text.startsWith(close_tok, i)) {
|
|
2399
|
+
if (counters[close_tok] > 0) counters[close_tok]--;
|
|
2400
|
+
i += close_tok.length;
|
|
2401
|
+
matched_close = true;
|
|
2402
|
+
break;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
if (matched_close) continue;
|
|
2406
|
+
if (text[i] === "\n" && i + 1 < n && text[i + 1] === "\n") {
|
|
2407
|
+
if (Object.values(counters).every((c) => c === 0)) {
|
|
2408
|
+
const block_text = text.substring(block_start, i);
|
|
2409
|
+
if (block_text) blocks.push([block_text, block_start]);
|
|
2410
|
+
let j = i;
|
|
2411
|
+
while (j < n && text[j] === "\n") j++;
|
|
2412
|
+
i = j;
|
|
2413
|
+
block_start = i;
|
|
2414
|
+
continue;
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
i++;
|
|
2418
|
+
}
|
|
2419
|
+
if (block_start < n) {
|
|
2420
|
+
const block_text = text.substring(block_start, n);
|
|
2421
|
+
if (block_text) blocks.push([block_text, block_start]);
|
|
2422
|
+
}
|
|
2423
|
+
return blocks;
|
|
2424
|
+
}
|
|
2425
|
+
function _merge_footnote_sections(blocks) {
|
|
2426
|
+
if (!blocks.length) return blocks;
|
|
2427
|
+
const merged = [];
|
|
2428
|
+
let i = 0;
|
|
2429
|
+
while (i < blocks.length) {
|
|
2430
|
+
const [block_text, block_offset] = blocks[i];
|
|
2431
|
+
const stripped = block_text.trimStart();
|
|
2432
|
+
const is_section_header = stripped.startsWith("## Footnotes") || stripped.startsWith("## Endnotes");
|
|
2433
|
+
if (!is_section_header) {
|
|
2434
|
+
merged.push([block_text, block_offset]);
|
|
2435
|
+
i++;
|
|
2436
|
+
continue;
|
|
2437
|
+
}
|
|
2438
|
+
let accumulated_text = block_text;
|
|
2439
|
+
let j = i + 1;
|
|
2440
|
+
while (j < blocks.length) {
|
|
2441
|
+
const [next_text] = blocks[j];
|
|
2442
|
+
const next_stripped = next_text.trimStart();
|
|
2443
|
+
if (next_stripped.startsWith("[^fn-") || next_stripped.startsWith("[^en-")) {
|
|
2444
|
+
accumulated_text = `${accumulated_text}
|
|
2445
|
+
|
|
2446
|
+
${next_text}`;
|
|
2447
|
+
j++;
|
|
2448
|
+
} else {
|
|
2449
|
+
break;
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
merged.push([accumulated_text, block_offset]);
|
|
2453
|
+
i = j;
|
|
2454
|
+
}
|
|
2455
|
+
return merged;
|
|
2456
|
+
}
|
|
2457
|
+
function _assemble_pages(block_records) {
|
|
2458
|
+
if (!block_records.length) return [[""], [0]];
|
|
2459
|
+
const pages = [];
|
|
2460
|
+
const page_starts = [];
|
|
2461
|
+
let current_blocks = [];
|
|
2462
|
+
let current_size = 0;
|
|
2463
|
+
let current_start = -1;
|
|
2464
|
+
const flush_current = () => {
|
|
2465
|
+
if (current_blocks.length > 0) {
|
|
2466
|
+
pages.push(current_blocks.join("\n\n"));
|
|
2467
|
+
page_starts.push(current_start);
|
|
2468
|
+
}
|
|
2469
|
+
current_blocks = [];
|
|
2470
|
+
current_size = 0;
|
|
2471
|
+
current_start = -1;
|
|
2472
|
+
};
|
|
2473
|
+
for (const [block_text, block_offset] of block_records) {
|
|
2474
|
+
const block_size = block_text.length;
|
|
2475
|
+
const added_size = block_size + (current_blocks.length > 0 ? 2 : 0);
|
|
2476
|
+
if (current_blocks.length > 0 && current_size + added_size > PAGE_TARGET_CHARS) {
|
|
2477
|
+
flush_current();
|
|
2478
|
+
}
|
|
2479
|
+
if (current_blocks.length === 0 && block_size > PAGE_TARGET_CHARS) {
|
|
2480
|
+
pages.push(block_text);
|
|
2481
|
+
page_starts.push(block_offset);
|
|
2482
|
+
continue;
|
|
2483
|
+
}
|
|
2484
|
+
if (current_blocks.length === 0) current_start = block_offset;
|
|
2485
|
+
current_blocks.push(block_text);
|
|
2486
|
+
current_size += current_size > 0 ? added_size : block_size;
|
|
2487
|
+
}
|
|
2488
|
+
flush_current();
|
|
2489
|
+
if (!pages.length) return [[""], [0]];
|
|
2490
|
+
return [pages, page_starts];
|
|
2491
|
+
}
|
|
2492
|
+
function _count_tracked_changes(page_content) {
|
|
2493
|
+
const matches = [...page_content.matchAll(_CHG_ID_PATTERN)];
|
|
2494
|
+
const distinct = new Set(matches.map((m) => m[1]));
|
|
2495
|
+
return distinct.size;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2204
2498
|
// src/markup.ts
|
|
2205
2499
|
var AMBIGUITY_EXAMPLES_CAP = 5;
|
|
2206
2500
|
var AMBIGUITY_CONTEXT_CHARS = 50;
|
|
@@ -2680,7 +2974,8 @@ var RedlineEngine = class {
|
|
|
2680
2974
|
start_idx + length,
|
|
2681
2975
|
start_idx + length + 30
|
|
2682
2976
|
);
|
|
2683
|
-
const
|
|
2977
|
+
const insertion = new_text ? `{++${new_text}++}` : "";
|
|
2978
|
+
const critic_markup = `${context_before}{--${target_text}--}${insertion}${context_after}`;
|
|
2684
2979
|
let clean_text = critic_markup;
|
|
2685
2980
|
clean_text = clean_text.replace(/\{>>.*?<<\}/gs, "");
|
|
2686
2981
|
clean_text = clean_text.replace(/\{--.*?--\}/gs, "");
|
|
@@ -2698,6 +2993,37 @@ var RedlineEngine = class {
|
|
|
2698
2993
|
}
|
|
2699
2994
|
return maxId;
|
|
2700
2995
|
}
|
|
2996
|
+
_get_heading_path_and_page(start_idx, text, page_offsets) {
|
|
2997
|
+
let page = 1;
|
|
2998
|
+
for (let i = 0; i < page_offsets.length; i++) {
|
|
2999
|
+
if (start_idx >= page_offsets[i]) {
|
|
3000
|
+
page = i + 1;
|
|
3001
|
+
} else {
|
|
3002
|
+
break;
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
const textBefore = text.substring(0, start_idx);
|
|
3006
|
+
const lines = textBefore.split("\n");
|
|
3007
|
+
const path = [];
|
|
3008
|
+
let current_level = 999;
|
|
3009
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3010
|
+
const line = lines[i];
|
|
3011
|
+
const m = line.match(/^(#{1,6})\s+(.*)/);
|
|
3012
|
+
if (m) {
|
|
3013
|
+
const level = m[1].length;
|
|
3014
|
+
if (level < current_level) {
|
|
3015
|
+
let cleanHeading = m[2].replace(/\*\*|__|[*_]/g, "").replace(/\{#[^}]+\}/g, "").trim();
|
|
3016
|
+
if (cleanHeading.length > 80) {
|
|
3017
|
+
cleanHeading = cleanHeading.substring(0, 80) + "...";
|
|
3018
|
+
}
|
|
3019
|
+
path.unshift(cleanHeading);
|
|
3020
|
+
current_level = level;
|
|
3021
|
+
if (level === 1) break;
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
return [path.join(" > "), page];
|
|
3026
|
+
}
|
|
2701
3027
|
accept_all_revisions() {
|
|
2702
3028
|
const parts_to_process = [this.doc.element];
|
|
2703
3029
|
for (const part of this.doc.pkg.parts) {
|
|
@@ -3396,13 +3722,15 @@ var RedlineEngine = class {
|
|
|
3396
3722
|
for (let i = 0; i < edits.length; i++) {
|
|
3397
3723
|
const edit = edits[i];
|
|
3398
3724
|
if (!edit.target_text) continue;
|
|
3399
|
-
|
|
3725
|
+
const is_regex = edit.regex || false;
|
|
3726
|
+
const match_mode = edit.match_mode || "strict";
|
|
3727
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
3400
3728
|
let activeText = this.mapper.full_text;
|
|
3401
3729
|
let target_mapper = this.mapper;
|
|
3402
3730
|
if (matches.length === 0) {
|
|
3403
3731
|
if (!this.clean_mapper)
|
|
3404
3732
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
3405
|
-
matches = this.clean_mapper.find_all_match_indices(edit.target_text);
|
|
3733
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
3406
3734
|
if (matches.length > 0) {
|
|
3407
3735
|
activeText = this.clean_mapper.full_text;
|
|
3408
3736
|
target_mapper = this.clean_mapper;
|
|
@@ -3424,7 +3752,7 @@ var RedlineEngine = class {
|
|
|
3424
3752
|
if (!this.original_mapper) {
|
|
3425
3753
|
this.original_mapper = new DocumentMapper(this.doc, false, true);
|
|
3426
3754
|
}
|
|
3427
|
-
const orig_matches = this.original_mapper.find_all_match_indices(edit.target_text);
|
|
3755
|
+
const orig_matches = this.original_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
3428
3756
|
if (orig_matches.length > 0) {
|
|
3429
3757
|
is_deleted_text = true;
|
|
3430
3758
|
for (const [start, length] of orig_matches) {
|
|
@@ -3461,7 +3789,7 @@ var RedlineEngine = class {
|
|
|
3461
3789
|
"${edit.target_text}"`
|
|
3462
3790
|
);
|
|
3463
3791
|
}
|
|
3464
|
-
} else if (matches.length > 1) {
|
|
3792
|
+
} else if (matches.length > 1 && match_mode === "strict") {
|
|
3465
3793
|
const positions = matches.map(([start, length]) => [
|
|
3466
3794
|
start,
|
|
3467
3795
|
start + length
|
|
@@ -3519,6 +3847,14 @@ var RedlineEngine = class {
|
|
|
3519
3847
|
if (auth && auth !== this.author) nestedAuthors.add(auth);
|
|
3520
3848
|
}
|
|
3521
3849
|
}
|
|
3850
|
+
if (s.comment_ids) {
|
|
3851
|
+
for (const cid of s.comment_ids) {
|
|
3852
|
+
const c_data = this.mapper.comments_map[cid];
|
|
3853
|
+
if (c_data && c_data.author && c_data.author !== this.author) {
|
|
3854
|
+
nestedAuthors.add(c_data.author);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3522
3858
|
}
|
|
3523
3859
|
if (nestedAuthors.size > 0) {
|
|
3524
3860
|
errors.push(
|
|
@@ -3649,6 +3985,9 @@ var RedlineEngine = class {
|
|
|
3649
3985
|
if (this.clean_mapper) this.clean_mapper["_build_map"]();
|
|
3650
3986
|
}
|
|
3651
3987
|
}
|
|
3988
|
+
const [body_text] = split_structural_appendix(this.mapper.full_text);
|
|
3989
|
+
const pag_res = paginate(body_text, "");
|
|
3990
|
+
const page_offsets = pag_res.body_page_offsets;
|
|
3652
3991
|
const edits_reports = [];
|
|
3653
3992
|
let applied_edits = 0;
|
|
3654
3993
|
let skipped_edits = 0;
|
|
@@ -3672,9 +4011,8 @@ var RedlineEngine = class {
|
|
|
3672
4011
|
});
|
|
3673
4012
|
continue;
|
|
3674
4013
|
}
|
|
3675
|
-
const res = this.apply_edits([edit]);
|
|
3676
|
-
|
|
3677
|
-
if (applied > 0) {
|
|
4014
|
+
const res = this.apply_edits([edit], page_offsets);
|
|
4015
|
+
if (edit._applied_status) {
|
|
3678
4016
|
applied_edits++;
|
|
3679
4017
|
const previews = this._build_edit_context_previews(edit);
|
|
3680
4018
|
edits_reports.push({
|
|
@@ -3684,7 +4022,11 @@ var RedlineEngine = class {
|
|
|
3684
4022
|
warning,
|
|
3685
4023
|
error: null,
|
|
3686
4024
|
critic_markup: previews[0],
|
|
3687
|
-
clean_text: previews[1]
|
|
4025
|
+
clean_text: previews[1],
|
|
4026
|
+
pages: edit._pages || [],
|
|
4027
|
+
heading_path: edit._heading_path || "",
|
|
4028
|
+
occurrences_modified: edit._occurrences_modified || 0,
|
|
4029
|
+
match_mode: edit.match_mode || "strict"
|
|
3688
4030
|
});
|
|
3689
4031
|
} else {
|
|
3690
4032
|
skipped_edits++;
|
|
@@ -3706,9 +4048,9 @@ var RedlineEngine = class {
|
|
|
3706
4048
|
throw new BatchValidationError(errors);
|
|
3707
4049
|
}
|
|
3708
4050
|
const cloned_edits = edits.map((e) => JSON.parse(JSON.stringify(e)));
|
|
3709
|
-
const res = this.apply_edits(cloned_edits);
|
|
3710
|
-
applied_edits =
|
|
3711
|
-
skipped_edits =
|
|
4051
|
+
const res = this.apply_edits(cloned_edits, page_offsets);
|
|
4052
|
+
applied_edits = cloned_edits.filter((e) => e._applied_status).length;
|
|
4053
|
+
skipped_edits = cloned_edits.length - applied_edits;
|
|
3712
4054
|
for (const edit of cloned_edits) {
|
|
3713
4055
|
const success = edit._applied_status || false;
|
|
3714
4056
|
const error_msg = edit._error_msg || null;
|
|
@@ -3729,7 +4071,11 @@ var RedlineEngine = class {
|
|
|
3729
4071
|
warning,
|
|
3730
4072
|
error: error_msg,
|
|
3731
4073
|
critic_markup,
|
|
3732
|
-
clean_text
|
|
4074
|
+
clean_text,
|
|
4075
|
+
pages: edit._pages || [],
|
|
4076
|
+
heading_path: edit._heading_path || "",
|
|
4077
|
+
occurrences_modified: edit._occurrences_modified || 0,
|
|
4078
|
+
match_mode: edit.match_mode || "strict"
|
|
3733
4079
|
});
|
|
3734
4080
|
}
|
|
3735
4081
|
}
|
|
@@ -3745,9 +4091,13 @@ var RedlineEngine = class {
|
|
|
3745
4091
|
version: "1.10.0"
|
|
3746
4092
|
};
|
|
3747
4093
|
}
|
|
3748
|
-
apply_edits(edits) {
|
|
4094
|
+
apply_edits(edits, page_offsets = []) {
|
|
3749
4095
|
let applied = 0;
|
|
3750
4096
|
let skipped = 0;
|
|
4097
|
+
if (!page_offsets || page_offsets.length === 0) {
|
|
4098
|
+
const [body_text] = split_structural_appendix(this.mapper.full_text);
|
|
4099
|
+
page_offsets = paginate(body_text, "").body_page_offsets;
|
|
4100
|
+
}
|
|
3751
4101
|
const resolved_edits = [];
|
|
3752
4102
|
for (const edit of edits) {
|
|
3753
4103
|
edit._applied_status = false;
|
|
@@ -3849,6 +4199,19 @@ var RedlineEngine = class {
|
|
|
3849
4199
|
const parent = edit._parent_edit_ref;
|
|
3850
4200
|
if (parent) {
|
|
3851
4201
|
parent._applied_status = true;
|
|
4202
|
+
parent._occurrences_modified = (parent._occurrences_modified || 0) + 1;
|
|
4203
|
+
const [path, page] = this._get_heading_path_and_page(start, this.mapper.full_text, page_offsets);
|
|
4204
|
+
const pages = parent._pages || [];
|
|
4205
|
+
if (!pages.includes(page)) pages.unshift(page);
|
|
4206
|
+
parent._pages = pages;
|
|
4207
|
+
parent._heading_path = path;
|
|
4208
|
+
} else {
|
|
4209
|
+
edit._occurrences_modified = (edit._occurrences_modified || 0) + 1;
|
|
4210
|
+
const [path, page] = this._get_heading_path_and_page(start, this.mapper.full_text, page_offsets);
|
|
4211
|
+
const pages = edit._pages || [];
|
|
4212
|
+
if (!pages.includes(page)) pages.unshift(page);
|
|
4213
|
+
edit._pages = pages;
|
|
4214
|
+
edit._heading_path = path;
|
|
3852
4215
|
}
|
|
3853
4216
|
} else {
|
|
3854
4217
|
skipped++;
|
|
@@ -4001,96 +4364,192 @@ var RedlineEngine = class {
|
|
|
4001
4364
|
}
|
|
4002
4365
|
return false;
|
|
4003
4366
|
}
|
|
4004
|
-
/**
|
|
4005
|
-
* Returns the first match of `target_text` in the raw mapper that is NOT
|
|
4006
|
-
* entirely contained within a tracked deletion (<w:del>). Tracked-deleted
|
|
4007
|
-
* copies are not live, editable text, so an edit must resolve to a live
|
|
4008
|
-
* occurrence even when a dead copy appears earlier in the document
|
|
4009
|
-
* (BUG-23-5). Falls back to the plain first match when no live copy is
|
|
4010
|
-
* found (e.g. fuzzy/normalized matches the span filter cannot align).
|
|
4011
|
-
*/
|
|
4012
|
-
_first_live_match(target_text) {
|
|
4013
|
-
const all = this.mapper.find_all_match_indices(target_text);
|
|
4014
|
-
if (all.length <= 1) {
|
|
4015
|
-
return this.mapper.find_match_index(target_text);
|
|
4016
|
-
}
|
|
4017
|
-
for (const [start, length] of all) {
|
|
4018
|
-
const realSpans = this.mapper.spans.filter(
|
|
4019
|
-
(s) => s.run !== null && s.end > start && s.start < start + length
|
|
4020
|
-
);
|
|
4021
|
-
if (realSpans.length === 0) return [start, length];
|
|
4022
|
-
if (realSpans.some((s) => !s.del_id)) return [start, length];
|
|
4023
|
-
}
|
|
4024
|
-
return this.mapper.find_match_index(target_text);
|
|
4025
|
-
}
|
|
4026
4367
|
_pre_resolve_heuristic_edit(edit) {
|
|
4027
4368
|
if (!edit.target_text) return null;
|
|
4028
|
-
|
|
4369
|
+
const is_regex = edit.regex || false;
|
|
4370
|
+
const match_mode = edit.match_mode || "strict";
|
|
4371
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4029
4372
|
let use_clean_map = false;
|
|
4030
|
-
if (
|
|
4373
|
+
if (matches.length === 0) {
|
|
4031
4374
|
if (!this.clean_mapper)
|
|
4032
4375
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
);
|
|
4036
|
-
if (start_idx !== -1) use_clean_map = true;
|
|
4376
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4377
|
+
if (matches.length > 0) use_clean_map = true;
|
|
4037
4378
|
else return null;
|
|
4038
4379
|
}
|
|
4039
4380
|
const active_mapper = use_clean_map ? this.clean_mapper : this.mapper;
|
|
4040
|
-
|
|
4041
|
-
const
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4381
|
+
let live_matches = [];
|
|
4382
|
+
for (const [s, match_len] of matches) {
|
|
4383
|
+
const realSpans = active_mapper.spans.filter(
|
|
4384
|
+
(span) => span.run !== null && span.end > s && span.start < s + match_len
|
|
4385
|
+
);
|
|
4386
|
+
if (realSpans.length === 0 || realSpans.some((span) => !span.del_id)) {
|
|
4387
|
+
live_matches.push([s, match_len]);
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
if (live_matches.length === 0) return null;
|
|
4391
|
+
if (match_mode === "strict" || match_mode === "first") {
|
|
4392
|
+
live_matches = live_matches.slice(0, 1);
|
|
4393
|
+
}
|
|
4394
|
+
const all_sub_edits = [];
|
|
4395
|
+
for (const [start_idx, match_len] of live_matches) {
|
|
4396
|
+
const actual_doc_text = active_mapper.full_text.substring(
|
|
4397
|
+
start_idx,
|
|
4398
|
+
start_idx + match_len
|
|
4399
|
+
);
|
|
4400
|
+
let current_effective_new_text = edit.new_text || "";
|
|
4401
|
+
if (is_regex && current_effective_new_text) {
|
|
4402
|
+
try {
|
|
4403
|
+
current_effective_new_text = actual_doc_text.replace(new RegExp(edit.target_text), current_effective_new_text);
|
|
4404
|
+
} catch (e) {
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
const [edit_target_clean, edit_target_style] = this._parse_markdown_style(edit.target_text);
|
|
4408
|
+
const [edit_new_clean, edit_new_style] = this._parse_markdown_style(current_effective_new_text);
|
|
4409
|
+
if (edit_target_style !== edit_new_style) {
|
|
4410
|
+
const [actual_clean] = this._parse_markdown_style(actual_doc_text);
|
|
4411
|
+
const final_target2 = actual_clean;
|
|
4412
|
+
const final_new2 = edit_new_clean;
|
|
4413
|
+
const style_op = final_target2 === final_new2 ? "STYLE_ONLY" : "STYLE_AND_TEXT";
|
|
4414
|
+
const prefix_offset = actual_doc_text.indexOf(actual_clean);
|
|
4415
|
+
const effective_start_idx2 = start_idx + (prefix_offset !== -1 ? prefix_offset : 0);
|
|
4416
|
+
const resolved_style = edit_new_style !== null ? edit_new_style : "Normal";
|
|
4417
|
+
all_sub_edits.push({
|
|
4418
|
+
type: "modify",
|
|
4419
|
+
target_text: final_target2,
|
|
4420
|
+
new_text: final_new2,
|
|
4421
|
+
comment: edit.comment,
|
|
4422
|
+
_match_start_index: effective_start_idx2,
|
|
4423
|
+
_internal_op: style_op,
|
|
4424
|
+
_new_style: resolved_style,
|
|
4425
|
+
_active_mapper_ref: active_mapper
|
|
4426
|
+
});
|
|
4427
|
+
continue;
|
|
4428
|
+
}
|
|
4429
|
+
if (actual_doc_text === current_effective_new_text || edit.target_text === current_effective_new_text) {
|
|
4430
|
+
all_sub_edits.push({
|
|
4431
|
+
type: "modify",
|
|
4432
|
+
target_text: actual_doc_text,
|
|
4433
|
+
new_text: actual_doc_text,
|
|
4434
|
+
comment: edit.comment,
|
|
4435
|
+
_match_start_index: start_idx,
|
|
4436
|
+
_internal_op: "COMMENT_ONLY",
|
|
4437
|
+
_active_mapper_ref: active_mapper
|
|
4438
|
+
});
|
|
4439
|
+
continue;
|
|
4440
|
+
}
|
|
4441
|
+
let effective_op = "";
|
|
4442
|
+
let final_target = "";
|
|
4443
|
+
let final_new = "";
|
|
4444
|
+
let effective_start_idx = start_idx;
|
|
4445
|
+
if (current_effective_new_text.startsWith(actual_doc_text)) {
|
|
4446
|
+
effective_op = "INSERTION";
|
|
4447
|
+
final_new = current_effective_new_text.substring(actual_doc_text.length);
|
|
4448
|
+
effective_start_idx = start_idx + match_len;
|
|
4449
|
+
} else {
|
|
4450
|
+
const [prefix_len, suffix_len] = trim_common_context(
|
|
4451
|
+
actual_doc_text,
|
|
4452
|
+
current_effective_new_text
|
|
4453
|
+
);
|
|
4454
|
+
const t_end = actual_doc_text.length - suffix_len;
|
|
4455
|
+
const n_end = current_effective_new_text.length - suffix_len;
|
|
4456
|
+
final_target = actual_doc_text.substring(prefix_len, t_end);
|
|
4457
|
+
final_new = current_effective_new_text.substring(prefix_len, n_end);
|
|
4458
|
+
effective_start_idx = start_idx + prefix_len;
|
|
4459
|
+
if (!final_target && final_new) effective_op = "INSERTION";
|
|
4460
|
+
else if (final_target && !final_new) effective_op = "DELETION";
|
|
4461
|
+
else if (final_target && final_new) effective_op = "MODIFICATION";
|
|
4462
|
+
else effective_op = "COMMENT_ONLY";
|
|
4463
|
+
}
|
|
4464
|
+
all_sub_edits.push({
|
|
4047
4465
|
type: "modify",
|
|
4048
|
-
target_text:
|
|
4049
|
-
new_text:
|
|
4466
|
+
target_text: final_target,
|
|
4467
|
+
new_text: final_new,
|
|
4050
4468
|
comment: edit.comment,
|
|
4051
|
-
_match_start_index:
|
|
4052
|
-
_internal_op:
|
|
4469
|
+
_match_start_index: effective_start_idx,
|
|
4470
|
+
_internal_op: effective_op,
|
|
4053
4471
|
_active_mapper_ref: active_mapper
|
|
4054
|
-
};
|
|
4472
|
+
});
|
|
4055
4473
|
}
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
let effective_start_idx = start_idx;
|
|
4060
|
-
if (effective_new_text.startsWith(actual_doc_text)) {
|
|
4061
|
-
effective_op = "INSERTION";
|
|
4062
|
-
final_new = effective_new_text.substring(actual_doc_text.length);
|
|
4063
|
-
effective_start_idx = start_idx + match_len;
|
|
4064
|
-
} else {
|
|
4065
|
-
const [prefix_len, suffix_len] = trim_common_context(
|
|
4066
|
-
actual_doc_text,
|
|
4067
|
-
effective_new_text
|
|
4068
|
-
);
|
|
4069
|
-
const t_end = actual_doc_text.length - suffix_len;
|
|
4070
|
-
const n_end = effective_new_text.length - suffix_len;
|
|
4071
|
-
final_target = actual_doc_text.substring(prefix_len, t_end);
|
|
4072
|
-
final_new = effective_new_text.substring(prefix_len, n_end);
|
|
4073
|
-
effective_start_idx = start_idx + prefix_len;
|
|
4074
|
-
if (!final_target && final_new) effective_op = "INSERTION";
|
|
4075
|
-
else if (final_target && !final_new) effective_op = "DELETION";
|
|
4076
|
-
else if (final_target && final_new) effective_op = "MODIFICATION";
|
|
4077
|
-
else effective_op = "COMMENT_ONLY";
|
|
4078
|
-
}
|
|
4079
|
-
return {
|
|
4080
|
-
type: "modify",
|
|
4081
|
-
target_text: final_target,
|
|
4082
|
-
new_text: final_new,
|
|
4083
|
-
comment: edit.comment,
|
|
4084
|
-
_match_start_index: effective_start_idx,
|
|
4085
|
-
_internal_op: effective_op,
|
|
4086
|
-
_active_mapper_ref: active_mapper
|
|
4087
|
-
};
|
|
4474
|
+
if (all_sub_edits.length === 0) return null;
|
|
4475
|
+
if (match_mode === "all" || all_sub_edits.length > 1) return all_sub_edits;
|
|
4476
|
+
return all_sub_edits[0];
|
|
4088
4477
|
}
|
|
4089
4478
|
_apply_single_edit_indexed(edit, orig_new, rebuild_map) {
|
|
4090
4479
|
let op = edit._internal_op;
|
|
4091
4480
|
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
4092
4481
|
const start_idx = edit._resolved_start_idx !== void 0 && edit._resolved_start_idx !== null ? edit._resolved_start_idx : edit._match_start_index || 0;
|
|
4093
4482
|
const length = edit.target_text ? edit.target_text.length : 0;
|
|
4483
|
+
if (op === "STYLE_ONLY" || op === "STYLE_AND_TEXT") {
|
|
4484
|
+
const [anchor_run, anchor_para] = active_mapper.get_insertion_anchor(
|
|
4485
|
+
start_idx,
|
|
4486
|
+
rebuild_map
|
|
4487
|
+
);
|
|
4488
|
+
let target_para_el = null;
|
|
4489
|
+
if (anchor_para) {
|
|
4490
|
+
target_para_el = anchor_para._element;
|
|
4491
|
+
} else if (anchor_run) {
|
|
4492
|
+
let walker = anchor_run._element;
|
|
4493
|
+
while (walker && walker.tagName !== "w:p") {
|
|
4494
|
+
walker = walker.parentNode;
|
|
4495
|
+
}
|
|
4496
|
+
target_para_el = walker;
|
|
4497
|
+
}
|
|
4498
|
+
if (target_para_el && edit._new_style) {
|
|
4499
|
+
this._set_paragraph_style(target_para_el, edit._new_style);
|
|
4500
|
+
}
|
|
4501
|
+
if (op === "STYLE_ONLY") {
|
|
4502
|
+
if (edit.comment) {
|
|
4503
|
+
const target_runs2 = active_mapper.find_target_runs_by_index(
|
|
4504
|
+
start_idx,
|
|
4505
|
+
length,
|
|
4506
|
+
rebuild_map
|
|
4507
|
+
);
|
|
4508
|
+
if (target_runs2.length > 0) {
|
|
4509
|
+
const first_el = target_runs2[0]._element;
|
|
4510
|
+
const last_el = target_runs2[target_runs2.length - 1]._element;
|
|
4511
|
+
let start_p = first_el;
|
|
4512
|
+
while (start_p && start_p.tagName !== "w:p")
|
|
4513
|
+
start_p = start_p.parentNode;
|
|
4514
|
+
let end_p = last_el;
|
|
4515
|
+
while (end_p && end_p.tagName !== "w:p")
|
|
4516
|
+
end_p = end_p.parentNode;
|
|
4517
|
+
if (start_p && end_p) {
|
|
4518
|
+
const ascend_to_paragraph_child = (el, p) => {
|
|
4519
|
+
let cur = el;
|
|
4520
|
+
while (cur.parentNode && cur.parentNode !== p) {
|
|
4521
|
+
cur = cur.parentNode;
|
|
4522
|
+
}
|
|
4523
|
+
return cur;
|
|
4524
|
+
};
|
|
4525
|
+
const first_anchor = ascend_to_paragraph_child(first_el, start_p);
|
|
4526
|
+
const last_anchor = ascend_to_paragraph_child(last_el, end_p);
|
|
4527
|
+
if (start_p === end_p) {
|
|
4528
|
+
this._attach_comment(start_p, first_anchor, last_anchor, edit.comment);
|
|
4529
|
+
} else {
|
|
4530
|
+
this._attach_comment_spanning(
|
|
4531
|
+
start_p,
|
|
4532
|
+
first_anchor,
|
|
4533
|
+
end_p,
|
|
4534
|
+
last_anchor,
|
|
4535
|
+
edit.comment
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
return true;
|
|
4542
|
+
}
|
|
4543
|
+
if (edit.target_text && edit.new_text) {
|
|
4544
|
+
op = "MODIFICATION";
|
|
4545
|
+
} else if (!edit.target_text && edit.new_text) {
|
|
4546
|
+
op = "INSERTION";
|
|
4547
|
+
} else if (edit.target_text && !edit.new_text) {
|
|
4548
|
+
op = "DELETION";
|
|
4549
|
+
} else {
|
|
4550
|
+
op = "COMMENT_ONLY";
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4094
4553
|
const del_id = ["DELETION", "MODIFICATION"].includes(op) ? this._getNextId() : null;
|
|
4095
4554
|
const ins_id = ["INSERTION", "MODIFICATION"].includes(op) ? this._getNextId() : null;
|
|
4096
4555
|
if (op === "COMMENT_ONLY") {
|
|
@@ -4473,1863 +4932,1814 @@ var RedlineEngine = class {
|
|
|
4473
4932
|
}
|
|
4474
4933
|
};
|
|
4475
4934
|
|
|
4476
|
-
// src/
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
}
|
|
4485
|
-
var _CHG_ID_PATTERN = /\bChg:(\d+)\b/g;
|
|
4486
|
-
function split_structural_appendix(markdown) {
|
|
4487
|
-
if (!markdown) return ["", ""];
|
|
4488
|
-
const idx = markdown.indexOf(APPENDIX_MARKER);
|
|
4489
|
-
if (idx === -1) return [markdown, ""];
|
|
4490
|
-
const line_start = markdown.lastIndexOf("\n", idx) + 1;
|
|
4491
|
-
const body = markdown.substring(0, line_start).trimEnd();
|
|
4492
|
-
const appendix = markdown.substring(line_start);
|
|
4493
|
-
return [body, appendix];
|
|
4494
|
-
}
|
|
4495
|
-
function paginate(markdown_body, structural_appendix = "") {
|
|
4496
|
-
if (!markdown_body) {
|
|
4497
|
-
const appendix_clean = structural_appendix ? structural_appendix.trim() : "";
|
|
4498
|
-
const content = appendix_clean;
|
|
4499
|
-
return {
|
|
4500
|
-
pages: [{
|
|
4501
|
-
page: 1,
|
|
4502
|
-
total_pages: 1,
|
|
4503
|
-
has_next: false,
|
|
4504
|
-
has_prev: false,
|
|
4505
|
-
tracked_change_count: _count_tracked_changes(content),
|
|
4506
|
-
page_content: content
|
|
4507
|
-
}],
|
|
4508
|
-
total_pages: 1,
|
|
4509
|
-
body_pages: [""],
|
|
4510
|
-
body_page_offsets: [0]
|
|
4511
|
-
};
|
|
4512
|
-
}
|
|
4513
|
-
const block_records = _tokenize_into_atomic_blocks(markdown_body);
|
|
4514
|
-
const [body_pages, body_page_offsets] = _assemble_pages(block_records);
|
|
4515
|
-
let final_pages;
|
|
4516
|
-
if (structural_appendix && structural_appendix.trim()) {
|
|
4517
|
-
const appendix = structural_appendix.trim();
|
|
4518
|
-
final_pages = body_pages.map((bp) => bp ? `${bp}
|
|
4519
|
-
|
|
4520
|
-
${appendix}` : appendix);
|
|
4521
|
-
} else {
|
|
4522
|
-
final_pages = [...body_pages];
|
|
4935
|
+
// src/sanitize/transforms.ts
|
|
4936
|
+
function findDescendantsByLocalName(element, localName) {
|
|
4937
|
+
const result = [];
|
|
4938
|
+
const all = element.getElementsByTagName("*");
|
|
4939
|
+
for (let i = 0; i < all.length; i++) {
|
|
4940
|
+
const tag = all[i].tagName;
|
|
4941
|
+
if (tag === localName || tag.endsWith(":" + localName)) {
|
|
4942
|
+
result.push(all[i]);
|
|
4943
|
+
}
|
|
4523
4944
|
}
|
|
4524
|
-
|
|
4525
|
-
const page_infos = final_pages.map((content, i) => ({
|
|
4526
|
-
page: i + 1,
|
|
4527
|
-
total_pages: total,
|
|
4528
|
-
has_next: i + 1 < total,
|
|
4529
|
-
has_prev: i + 1 > 1,
|
|
4530
|
-
tracked_change_count: _count_tracked_changes(content),
|
|
4531
|
-
page_content: content
|
|
4532
|
-
}));
|
|
4533
|
-
return {
|
|
4534
|
-
pages: page_infos,
|
|
4535
|
-
total_pages: total,
|
|
4536
|
-
body_pages,
|
|
4537
|
-
body_page_offsets
|
|
4538
|
-
};
|
|
4539
|
-
}
|
|
4540
|
-
function _tokenize_into_atomic_blocks(markdown_body) {
|
|
4541
|
-
const raw_blocks = _split_on_safe_paragraph_breaks(markdown_body);
|
|
4542
|
-
return _merge_footnote_sections(raw_blocks);
|
|
4945
|
+
return result;
|
|
4543
4946
|
}
|
|
4544
|
-
function
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4947
|
+
function coalesce_runs(doc) {
|
|
4948
|
+
let count = 0;
|
|
4949
|
+
function areRunsIdentical(rPr1, rPr2) {
|
|
4950
|
+
const xml1 = rPr1 ? rPr1.toString() : "";
|
|
4951
|
+
const xml2 = rPr2 ? rPr2.toString() : "";
|
|
4952
|
+
return xml1 === xml2;
|
|
4953
|
+
}
|
|
4954
|
+
function hasSpecialContent(run) {
|
|
4955
|
+
const safeTags = ["w:t", "w:tab", "w:br", "w:cr", "w:delText", "w:rPr"];
|
|
4956
|
+
for (let i = 0; i < run.childNodes.length; i++) {
|
|
4957
|
+
const child = run.childNodes[i];
|
|
4958
|
+
if (child.nodeType === 1) {
|
|
4959
|
+
const tag = child.tagName;
|
|
4960
|
+
if (!safeTags.includes(tag)) return true;
|
|
4558
4961
|
}
|
|
4559
4962
|
}
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4963
|
+
return false;
|
|
4964
|
+
}
|
|
4965
|
+
function coalesceContainer(container) {
|
|
4966
|
+
const children = Array.from(container.childNodes).filter((n) => n.nodeType === 1);
|
|
4967
|
+
let i = 0;
|
|
4968
|
+
while (i < children.length - 1) {
|
|
4969
|
+
const curr = children[i];
|
|
4970
|
+
const nxt = children[i + 1];
|
|
4971
|
+
if (curr.tagName === "w:r" && nxt.tagName === "w:r") {
|
|
4972
|
+
if (!hasSpecialContent(curr) && !hasSpecialContent(nxt)) {
|
|
4973
|
+
const rPr1 = findChild(curr, "w:rPr");
|
|
4974
|
+
const rPr2 = findChild(nxt, "w:rPr");
|
|
4975
|
+
if (areRunsIdentical(rPr1, rPr2)) {
|
|
4976
|
+
let last_t = null;
|
|
4977
|
+
for (let c = 0; c < curr.childNodes.length; c++) {
|
|
4978
|
+
const child = curr.childNodes[c];
|
|
4979
|
+
if (child.nodeType === 1 && (child.tagName === "w:t" || child.tagName === "w:delText")) {
|
|
4980
|
+
last_t = child;
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
const nxtChildren = Array.from(nxt.childNodes).filter((n) => n.nodeType === 1);
|
|
4984
|
+
for (const child of nxtChildren) {
|
|
4985
|
+
if (child.tagName === "w:rPr") continue;
|
|
4986
|
+
if ((child.tagName === "w:t" || child.tagName === "w:delText") && last_t && last_t.tagName === child.tagName) {
|
|
4987
|
+
const t1 = last_t.textContent || "";
|
|
4988
|
+
const t2 = child.textContent || "";
|
|
4989
|
+
const combined = t1 + t2;
|
|
4990
|
+
last_t.textContent = combined;
|
|
4991
|
+
if (combined.trim() !== combined) {
|
|
4992
|
+
last_t.setAttribute("xml:space", "preserve");
|
|
4993
|
+
}
|
|
4994
|
+
} else {
|
|
4995
|
+
curr.appendChild(child);
|
|
4996
|
+
if (child.tagName === "w:t" || child.tagName === "w:delText") {
|
|
4997
|
+
last_t = child;
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
container.removeChild(nxt);
|
|
5002
|
+
children.splice(i + 1, 1);
|
|
5003
|
+
count++;
|
|
5004
|
+
continue;
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(curr.tagName)) {
|
|
5009
|
+
coalesceContainer(curr);
|
|
4568
5010
|
}
|
|
5011
|
+
i++;
|
|
4569
5012
|
}
|
|
4570
|
-
if (
|
|
4571
|
-
|
|
4572
|
-
if (
|
|
4573
|
-
|
|
4574
|
-
if (block_text) blocks.push([block_text, block_start]);
|
|
4575
|
-
let j = i;
|
|
4576
|
-
while (j < n && text[j] === "\n") j++;
|
|
4577
|
-
i = j;
|
|
4578
|
-
block_start = i;
|
|
4579
|
-
continue;
|
|
5013
|
+
if (children.length > 0) {
|
|
5014
|
+
const last = children[children.length - 1];
|
|
5015
|
+
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(last.tagName)) {
|
|
5016
|
+
coalesceContainer(last);
|
|
4580
5017
|
}
|
|
4581
5018
|
}
|
|
4582
|
-
i++;
|
|
4583
|
-
}
|
|
4584
|
-
if (block_start < n) {
|
|
4585
|
-
const block_text = text.substring(block_start, n);
|
|
4586
|
-
if (block_text) blocks.push([block_text, block_start]);
|
|
4587
5019
|
}
|
|
4588
|
-
|
|
5020
|
+
const paragraphs = findAllDescendants(doc.element, "w:p");
|
|
5021
|
+
for (const p of paragraphs) coalesceContainer(p);
|
|
5022
|
+
return count ? [`Adjacent identical runs coalesced: ${count}`] : [];
|
|
4589
5023
|
}
|
|
4590
|
-
function
|
|
4591
|
-
|
|
4592
|
-
const
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
const
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
merged.push([block_text, block_offset]);
|
|
4600
|
-
i++;
|
|
4601
|
-
continue;
|
|
4602
|
-
}
|
|
4603
|
-
let accumulated_text = block_text;
|
|
4604
|
-
let j = i + 1;
|
|
4605
|
-
while (j < blocks.length) {
|
|
4606
|
-
const [next_text] = blocks[j];
|
|
4607
|
-
const next_stripped = next_text.trimStart();
|
|
4608
|
-
if (next_stripped.startsWith("[^fn-") || next_stripped.startsWith("[^en-")) {
|
|
4609
|
-
accumulated_text = `${accumulated_text}
|
|
4610
|
-
|
|
4611
|
-
${next_text}`;
|
|
4612
|
-
j++;
|
|
4613
|
-
} else {
|
|
4614
|
-
break;
|
|
5024
|
+
function strip_rsid(doc) {
|
|
5025
|
+
let count = 0;
|
|
5026
|
+
const rsidAttrs = ["w:rsidR", "w:rsidRPr", "w:rsidRDefault", "w:rsidP", "w:rsidDel", "w:rsidSect", "w:rsidTr"];
|
|
5027
|
+
const all = doc.element.getElementsByTagName("*");
|
|
5028
|
+
for (let i = 0; i < all.length; i++) {
|
|
5029
|
+
for (const attr of rsidAttrs) {
|
|
5030
|
+
if (all[i].hasAttribute(attr)) {
|
|
5031
|
+
all[i].removeAttribute(attr);
|
|
5032
|
+
count++;
|
|
4615
5033
|
}
|
|
4616
5034
|
}
|
|
4617
|
-
merged.push([accumulated_text, block_offset]);
|
|
4618
|
-
i = j;
|
|
4619
5035
|
}
|
|
4620
|
-
|
|
5036
|
+
const rsidsElements = findAllDescendants(doc.element, "w:rsids");
|
|
5037
|
+
for (const el of rsidsElements) {
|
|
5038
|
+
if (el.parentNode) {
|
|
5039
|
+
el.parentNode.removeChild(el);
|
|
5040
|
+
count++;
|
|
5041
|
+
}
|
|
5042
|
+
}
|
|
5043
|
+
return count ? [`rsid attributes: ${count} removed`] : [];
|
|
4621
5044
|
}
|
|
4622
|
-
function
|
|
4623
|
-
|
|
4624
|
-
const
|
|
4625
|
-
const
|
|
4626
|
-
let
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
page_starts.push(current_start);
|
|
5045
|
+
function strip_para_ids(doc) {
|
|
5046
|
+
let count = 0;
|
|
5047
|
+
const attrs = ["w14:paraId", "w14:textId"];
|
|
5048
|
+
const all = doc.element.getElementsByTagName("*");
|
|
5049
|
+
for (let i = 0; i < all.length; i++) {
|
|
5050
|
+
for (const attr of attrs) {
|
|
5051
|
+
if (all[i].hasAttribute(attr)) {
|
|
5052
|
+
all[i].removeAttribute(attr);
|
|
5053
|
+
count++;
|
|
5054
|
+
}
|
|
4633
5055
|
}
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
5056
|
+
}
|
|
5057
|
+
return count ? [`Paragraph/text IDs: ${count} removed`] : [];
|
|
5058
|
+
}
|
|
5059
|
+
function strip_proof_errors(doc) {
|
|
5060
|
+
const elements = findAllDescendants(doc.element, "w:proofErr");
|
|
5061
|
+
elements.forEach((el) => el.parentNode?.removeChild(el));
|
|
5062
|
+
return elements.length ? [`Spell check markers: ${elements.length} removed`] : [];
|
|
5063
|
+
}
|
|
5064
|
+
function strip_empty_properties(doc) {
|
|
5065
|
+
let count = 0;
|
|
5066
|
+
for (const tag of ["w:rPr", "w:pPr"]) {
|
|
5067
|
+
const elements = findAllDescendants(doc.element, tag);
|
|
5068
|
+
for (const el of elements) {
|
|
5069
|
+
if (el.childNodes.length === 0 || el.childNodes.length === 1 && el.childNodes[0].nodeType === 3 && !el.childNodes[0].textContent?.trim()) {
|
|
5070
|
+
el.parentNode?.removeChild(el);
|
|
5071
|
+
count++;
|
|
5072
|
+
}
|
|
4643
5073
|
}
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
5074
|
+
}
|
|
5075
|
+
return count ? [`Empty property elements: ${count} removed`] : [];
|
|
5076
|
+
}
|
|
5077
|
+
function strip_hidden_text(doc) {
|
|
5078
|
+
let count = 0;
|
|
5079
|
+
const elements = findAllDescendants(doc.element, "w:rPr");
|
|
5080
|
+
for (const rPr of elements) {
|
|
5081
|
+
if (findChild(rPr, "w:vanish") || findChild(rPr, "w:webHidden")) {
|
|
5082
|
+
const run = rPr.parentNode;
|
|
5083
|
+
if (run && run.tagName === "w:r" && run.parentNode) {
|
|
5084
|
+
run.parentNode.removeChild(run);
|
|
5085
|
+
count++;
|
|
5086
|
+
}
|
|
4648
5087
|
}
|
|
4649
|
-
if (current_blocks.length === 0) current_start = block_offset;
|
|
4650
|
-
current_blocks.push(block_text);
|
|
4651
|
-
current_size += current_size > 0 ? added_size : block_size;
|
|
4652
5088
|
}
|
|
4653
|
-
|
|
4654
|
-
if (!pages.length) return [[""], [0]];
|
|
4655
|
-
return [pages, page_starts];
|
|
5089
|
+
return count ? [`Hidden text runs: ${count} removed`] : [];
|
|
4656
5090
|
}
|
|
4657
|
-
function
|
|
4658
|
-
const
|
|
4659
|
-
const
|
|
4660
|
-
|
|
5091
|
+
function count_tracked_changes(doc) {
|
|
5092
|
+
const ins = findAllDescendants(doc.element, "w:ins").length;
|
|
5093
|
+
const del = findAllDescendants(doc.element, "w:del").length;
|
|
5094
|
+
const fmt = findAllDescendants(doc.element, "w:rPrChange").length + findAllDescendants(doc.element, "w:pPrChange").length + findAllDescendants(doc.element, "w:sectPrChange").length;
|
|
5095
|
+
return [ins, del, fmt];
|
|
4661
5096
|
}
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
if (a.length > b.length) {
|
|
4670
|
-
const temp = a;
|
|
4671
|
-
a = b;
|
|
4672
|
-
b = temp;
|
|
5097
|
+
function get_track_change_authors(doc) {
|
|
5098
|
+
const authors = /* @__PURE__ */ new Set();
|
|
5099
|
+
for (const tag of ["w:ins", "w:del", "w:rPrChange", "w:pPrChange", "w:sectPrChange"]) {
|
|
5100
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5101
|
+
const author = el.getAttribute("w:author");
|
|
5102
|
+
if (author) authors.add(author);
|
|
5103
|
+
}
|
|
4673
5104
|
}
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
5105
|
+
return authors;
|
|
5106
|
+
}
|
|
5107
|
+
function _getElementText(el) {
|
|
5108
|
+
const texts = [];
|
|
5109
|
+
const ts = findAllDescendants(el, "w:t");
|
|
5110
|
+
for (const t of ts) if (t.textContent) texts.push(t.textContent);
|
|
5111
|
+
const dts = findAllDescendants(el, "w:delText");
|
|
5112
|
+
for (const dt of dts) if (dt.textContent) texts.push(dt.textContent);
|
|
5113
|
+
return texts.join("");
|
|
5114
|
+
}
|
|
5115
|
+
function _truncate(text, maxLen = 60) {
|
|
5116
|
+
const clean = text.replace(/\n/g, " ").trim();
|
|
5117
|
+
if (clean.length <= maxLen) return clean;
|
|
5118
|
+
return clean.substring(0, maxLen - 3) + "...";
|
|
5119
|
+
}
|
|
5120
|
+
function accept_all_tracked_changes(doc) {
|
|
5121
|
+
const lines = [];
|
|
5122
|
+
const insEls = findAllDescendants(doc.element, "w:ins");
|
|
5123
|
+
const delEls = findAllDescendants(doc.element, "w:del");
|
|
5124
|
+
for (const ins of insEls) {
|
|
5125
|
+
const text = _getElementText(ins).trim();
|
|
5126
|
+
if (text) lines.push(` Accepted insertion: "${_truncate(text, 60)}"`);
|
|
5127
|
+
}
|
|
5128
|
+
for (const del of delEls) {
|
|
5129
|
+
const text = _getElementText(del).trim();
|
|
5130
|
+
if (text) lines.push(` Accepted deletion of: "${_truncate(text, 60)}"`);
|
|
5131
|
+
}
|
|
5132
|
+
const engine = new RedlineEngine(doc);
|
|
5133
|
+
engine.accept_all_revisions();
|
|
5134
|
+
for (const tag of ["w:rPrChange", "w:pPrChange", "w:sectPrChange"]) {
|
|
5135
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5136
|
+
el.parentNode?.removeChild(el);
|
|
4687
5137
|
}
|
|
4688
|
-
if (minInRow > maxDist) return maxDist + 1;
|
|
4689
|
-
row = newRow;
|
|
4690
5138
|
}
|
|
4691
|
-
|
|
5139
|
+
const total = insEls.length + delEls.length;
|
|
5140
|
+
if (total) {
|
|
5141
|
+
return [`Tracked changes auto-accepted: ${total}`].concat(lines);
|
|
5142
|
+
}
|
|
5143
|
+
return [];
|
|
4692
5144
|
}
|
|
4693
|
-
function
|
|
4694
|
-
|
|
4695
|
-
const
|
|
4696
|
-
|
|
4697
|
-
|
|
5145
|
+
function get_comments_summary(doc) {
|
|
5146
|
+
const data = extract_comments_data(doc.pkg);
|
|
5147
|
+
const comments = [];
|
|
5148
|
+
let openCount = 0;
|
|
5149
|
+
let resolvedCount = 0;
|
|
5150
|
+
for (const [cId, info] of Object.entries(data)) {
|
|
5151
|
+
if (info.resolved) resolvedCount++;
|
|
5152
|
+
else openCount++;
|
|
5153
|
+
comments.push({ id: cId, ...info });
|
|
4698
5154
|
}
|
|
4699
|
-
return
|
|
5155
|
+
return { total: comments.length, open: openCount, resolved: resolvedCount, comments };
|
|
4700
5156
|
}
|
|
4701
|
-
function
|
|
4702
|
-
const
|
|
4703
|
-
const
|
|
4704
|
-
|
|
4705
|
-
const
|
|
4706
|
-
const
|
|
4707
|
-
const
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
const
|
|
4714
|
-
|
|
4715
|
-
const inline_matches = text.matchAll(inline_re);
|
|
4716
|
-
for (const m of inline_matches) {
|
|
4717
|
-
extracted_terms.push(m[1].trim());
|
|
4718
|
-
}
|
|
4719
|
-
for (const term of extracted_terms) {
|
|
4720
|
-
if (definitions[term]) duplicates.add(term);
|
|
4721
|
-
else definitions[term] = { count: 0 };
|
|
5157
|
+
function remove_all_comments(doc) {
|
|
5158
|
+
const data = extract_comments_data(doc.pkg);
|
|
5159
|
+
const keys = Object.keys(data);
|
|
5160
|
+
if (keys.length === 0) return [];
|
|
5161
|
+
const lines = [];
|
|
5162
|
+
const cm = new CommentsManager(doc);
|
|
5163
|
+
for (const [cId, info] of Object.entries(data)) {
|
|
5164
|
+
const status = info.resolved ? "[Resolved]" : "[Open]";
|
|
5165
|
+
lines.push(` ${status} "${_truncate(info.text || "", 60)}" (${info.author || "Unknown"})`);
|
|
5166
|
+
cm.deleteComment(cId);
|
|
5167
|
+
}
|
|
5168
|
+
for (const tag of ["w:commentRangeStart", "w:commentRangeEnd"]) {
|
|
5169
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5170
|
+
el.parentNode?.removeChild(el);
|
|
4722
5171
|
}
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
5172
|
+
}
|
|
5173
|
+
const refs = findAllDescendants(doc.element, "w:commentReference");
|
|
5174
|
+
for (const ref of refs) {
|
|
5175
|
+
const parent = ref.parentNode;
|
|
5176
|
+
if (parent) {
|
|
5177
|
+
if (parent.tagName === "w:r" || parent.tagName.endsWith(":r")) {
|
|
5178
|
+
const nonRprChildren = Array.from(parent.childNodes).filter(
|
|
5179
|
+
(c) => c.nodeType === 1 && c.tagName !== "w:rPr" && c.tagName !== "rPr"
|
|
5180
|
+
);
|
|
5181
|
+
if (nonRprChildren.length <= 1) {
|
|
5182
|
+
parent.parentNode?.removeChild(parent);
|
|
5183
|
+
} else {
|
|
5184
|
+
parent.removeChild(ref);
|
|
4732
5185
|
}
|
|
5186
|
+
} else {
|
|
5187
|
+
parent.removeChild(ref);
|
|
4733
5188
|
}
|
|
4734
|
-
let target = null;
|
|
4735
|
-
if (node.tagName === "w:fldSimple") {
|
|
4736
|
-
const instr = node.getAttribute("w:instr") || "";
|
|
4737
|
-
const parts = instr.trim().split(/\s+/);
|
|
4738
|
-
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
4739
|
-
} else if (node.tagName === "w:instrText") {
|
|
4740
|
-
const instr = node.textContent || "";
|
|
4741
|
-
const parts = instr.trim().split(/\s+/);
|
|
4742
|
-
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
4743
|
-
}
|
|
4744
|
-
if (target) raw_references.push([target, short_text]);
|
|
4745
5189
|
}
|
|
4746
5190
|
}
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
5191
|
+
const resolvedCount = Object.values(data).filter((c) => c.resolved).length;
|
|
5192
|
+
const openCount = Object.values(data).filter((c) => !c.resolved).length;
|
|
5193
|
+
return [`Comments removed: ${keys.length} (${resolvedCount} resolved, ${openCount} open)`].concat(lines);
|
|
5194
|
+
}
|
|
5195
|
+
function eject_comment_parts(doc) {
|
|
5196
|
+
const pkg = doc.pkg;
|
|
5197
|
+
const comment_partnames = /* @__PURE__ */ new Set();
|
|
5198
|
+
for (const part of pkg.parts) {
|
|
5199
|
+
if (part.partname.toLowerCase().includes("comments")) {
|
|
5200
|
+
comment_partnames.add(part.partname);
|
|
5201
|
+
const withSlash = part.partname.startsWith("/") ? part.partname : "/" + part.partname;
|
|
5202
|
+
const withoutSlash = part.partname.startsWith("/") ? part.partname.substring(1) : part.partname;
|
|
5203
|
+
comment_partnames.add(withSlash);
|
|
5204
|
+
comment_partnames.add(withoutSlash);
|
|
4750
5205
|
}
|
|
4751
5206
|
}
|
|
4752
|
-
|
|
4753
|
-
const
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
5207
|
+
if (comment_partnames.size === 0) return;
|
|
5208
|
+
for (const part of pkg.parts) {
|
|
5209
|
+
if (part.partname.endsWith(".rels")) {
|
|
5210
|
+
const rels = findAllDescendants(part._element, "Relationship");
|
|
5211
|
+
const toRemove = [];
|
|
5212
|
+
for (const rel of rels) {
|
|
5213
|
+
const target = rel.getAttribute("Target") || "";
|
|
5214
|
+
if (target.toLowerCase().includes("comments")) {
|
|
5215
|
+
toRemove.push(rel);
|
|
5216
|
+
const sourcePath = part.partname.replace("/_rels/", "/").replace(".rels", "");
|
|
5217
|
+
const sourcePart = pkg.getPartByPath(sourcePath);
|
|
5218
|
+
if (sourcePart) {
|
|
5219
|
+
const relId = rel.getAttribute("Id");
|
|
5220
|
+
if (relId) sourcePart.rels.delete(relId);
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
for (const relEl of toRemove) {
|
|
5225
|
+
relEl.parentNode?.removeChild(relEl);
|
|
4767
5226
|
}
|
|
4768
5227
|
}
|
|
4769
5228
|
}
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
"A",
|
|
4779
|
-
"An",
|
|
4780
|
-
"Any",
|
|
4781
|
-
"All",
|
|
4782
|
-
"Some",
|
|
4783
|
-
"No",
|
|
4784
|
-
"Every",
|
|
4785
|
-
"Each",
|
|
4786
|
-
"As",
|
|
4787
|
-
"In",
|
|
4788
|
-
"Of",
|
|
4789
|
-
"For",
|
|
4790
|
-
"To",
|
|
4791
|
-
"On",
|
|
4792
|
-
"By",
|
|
4793
|
-
"With"
|
|
4794
|
-
]);
|
|
4795
|
-
const all_cap_pattern = /\b[A-Z][a-zA-Z]*(?:\s+[A-Z][a-zA-Z]*)*\b/g;
|
|
4796
|
-
const all_caps = new Set(base_text.match(all_cap_pattern) || []);
|
|
4797
|
-
const valid_terms = new Set(Object.keys(definitions));
|
|
4798
|
-
const terms_by_first_letter = {};
|
|
4799
|
-
for (const term of valid_terms) {
|
|
4800
|
-
const fl = term[0].toLowerCase();
|
|
4801
|
-
if (!terms_by_first_letter[fl]) terms_by_first_letter[fl] = [];
|
|
4802
|
-
terms_by_first_letter[fl].push(term);
|
|
4803
|
-
}
|
|
4804
|
-
const candidates_by_term = {};
|
|
4805
|
-
for (const raw_candidate of all_caps) {
|
|
4806
|
-
let candidate = raw_candidate.trim();
|
|
4807
|
-
const words = candidate.split(/\s+/);
|
|
4808
|
-
while (words.length > 0) {
|
|
4809
|
-
const first = words[0];
|
|
4810
|
-
const title = first.charAt(0).toUpperCase() + first.slice(1).toLowerCase();
|
|
4811
|
-
if (stop_words.has(title)) words.shift();
|
|
4812
|
-
else break;
|
|
4813
|
-
}
|
|
4814
|
-
candidate = words.join(" ");
|
|
4815
|
-
if (candidate.length < 4) continue;
|
|
4816
|
-
if (valid_terms.has(candidate)) continue;
|
|
4817
|
-
const first_letter = candidate[0].toLowerCase();
|
|
4818
|
-
let candidate_terms = terms_by_first_letter[first_letter] || [];
|
|
4819
|
-
if (candidate.length > 5) {
|
|
4820
|
-
for (const [k, v] of Object.entries(terms_by_first_letter)) {
|
|
4821
|
-
if (k !== first_letter) candidate_terms = candidate_terms.concat(v);
|
|
5229
|
+
const ctPart = pkg.getPartByPath("[Content_Types].xml");
|
|
5230
|
+
if (ctPart) {
|
|
5231
|
+
const overrides = findAllDescendants(ctPart._element, "Override");
|
|
5232
|
+
const toRemove = [];
|
|
5233
|
+
for (const override of overrides) {
|
|
5234
|
+
const partName = override.getAttribute("PartName") || "";
|
|
5235
|
+
if (comment_partnames.has(partName) || partName.toLowerCase().includes("comments")) {
|
|
5236
|
+
toRemove.push(override);
|
|
4822
5237
|
}
|
|
4823
5238
|
}
|
|
4824
|
-
for (const
|
|
4825
|
-
|
|
4826
|
-
if (candidate === term + "s" || candidate === term + "es") continue;
|
|
4827
|
-
if (term === candidate + "s" || term === candidate + "es") continue;
|
|
4828
|
-
const dist = boundedLevenshtein(candidate, term, 2);
|
|
4829
|
-
if (dist === 0 || dist > 2) continue;
|
|
4830
|
-
if (term.length <= 5) {
|
|
4831
|
-
if (dist > 1) continue;
|
|
4832
|
-
if (candidate[0].toLowerCase() !== term[0].toLowerCase()) continue;
|
|
4833
|
-
}
|
|
4834
|
-
if (!candidates_by_term[term]) candidates_by_term[term] = [];
|
|
4835
|
-
if (!candidates_by_term[term].includes(candidate)) candidates_by_term[term].push(candidate);
|
|
5239
|
+
for (const overrideEl of toRemove) {
|
|
5240
|
+
overrideEl.parentNode?.removeChild(overrideEl);
|
|
4836
5241
|
}
|
|
4837
5242
|
}
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
function diag_sort_key(msg) {
|
|
4844
|
-
if (msg.startsWith("[Error]")) return 0;
|
|
4845
|
-
if (msg.startsWith("[Warning]")) return 1;
|
|
4846
|
-
return 2;
|
|
5243
|
+
pkg.parts = pkg.parts.filter((p) => !p.partname.toLowerCase().includes("comments"));
|
|
5244
|
+
for (const key of Object.keys(pkg.unzipped)) {
|
|
5245
|
+
if (key.toLowerCase().includes("comments")) {
|
|
5246
|
+
delete pkg.unzipped[key];
|
|
5247
|
+
}
|
|
4847
5248
|
}
|
|
4848
|
-
diagnostics.sort((a, b) => {
|
|
4849
|
-
const keyA = diag_sort_key(a);
|
|
4850
|
-
const keyB = diag_sort_key(b);
|
|
4851
|
-
if (keyA !== keyB) return keyA - keyB;
|
|
4852
|
-
return a.localeCompare(b);
|
|
4853
|
-
});
|
|
4854
|
-
return [definitions, diagnostics, raw_anchors];
|
|
4855
5249
|
}
|
|
4856
|
-
function
|
|
4857
|
-
const
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
if (Object.keys(defs).length > 0) {
|
|
4867
|
-
has_content = true;
|
|
4868
|
-
lines.push("\n## Defined Terms");
|
|
4869
|
-
for (const [term, data] of Object.entries(defs)) {
|
|
4870
|
-
lines.push(`- "${term}" \u2014 used ${data.count} times.`);
|
|
5250
|
+
function replace_comment_authors(doc, newAuthor) {
|
|
5251
|
+
const cm = new CommentsManager(doc);
|
|
5252
|
+
if (!cm.commentsPart) return [];
|
|
5253
|
+
const original = /* @__PURE__ */ new Set();
|
|
5254
|
+
const comments = findAllDescendants(cm.commentsPart._element, "w:comment");
|
|
5255
|
+
for (const c of comments) {
|
|
5256
|
+
const author = c.getAttribute("w:author");
|
|
5257
|
+
if (author) {
|
|
5258
|
+
original.add(author);
|
|
5259
|
+
c.setAttribute("w:author", newAuthor);
|
|
4871
5260
|
}
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
lines.push("\n## Semantic Diagnostics");
|
|
4876
|
-
for (const diag of diagnostics) {
|
|
4877
|
-
lines.push(`- ${diag}`);
|
|
5261
|
+
if (c.hasAttribute("w:initials")) {
|
|
5262
|
+
const initials = newAuthor.split(" ").filter(Boolean).map((p) => p[0]).join("").toUpperCase();
|
|
5263
|
+
c.setAttribute("w:initials", initials);
|
|
4878
5264
|
}
|
|
4879
5265
|
}
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
5266
|
+
return original.size ? [`Comment authors replaced: ${Array.from(original).sort().join(", ")} \u2192 "${newAuthor}"`] : [];
|
|
5267
|
+
}
|
|
5268
|
+
function replace_change_authors(doc, newAuthor) {
|
|
5269
|
+
const original = /* @__PURE__ */ new Set();
|
|
5270
|
+
for (const tag of ["w:ins", "w:del", "w:rPrChange", "w:pPrChange"]) {
|
|
5271
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5272
|
+
const author = el.getAttribute("w:author");
|
|
5273
|
+
if (author) {
|
|
5274
|
+
original.add(author);
|
|
5275
|
+
el.setAttribute("w:author", newAuthor);
|
|
4887
5276
|
}
|
|
4888
5277
|
}
|
|
4889
5278
|
}
|
|
4890
|
-
|
|
4891
|
-
return lines.join("\n");
|
|
4892
|
-
}
|
|
4893
|
-
return "";
|
|
4894
|
-
}
|
|
4895
|
-
|
|
4896
|
-
// src/ingest.ts
|
|
4897
|
-
async function extractTextFromBuffer(buffer, cleanView = false) {
|
|
4898
|
-
const doc = await DocumentObject.load(buffer);
|
|
4899
|
-
return _extractTextFromDoc(doc, cleanView);
|
|
5279
|
+
return original.size ? [`Track change authors replaced: ${Array.from(original).sort().join(", ")} \u2192 "${newAuthor}"`] : [];
|
|
4900
5280
|
}
|
|
4901
|
-
function
|
|
4902
|
-
|
|
4903
|
-
const
|
|
4904
|
-
const
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
if (full_text.length > 0) cursor += 2;
|
|
4911
|
-
full_text.push(part_text);
|
|
4912
|
-
cursor += part_text.length;
|
|
5281
|
+
function normalize_change_dates(doc) {
|
|
5282
|
+
let count = 0;
|
|
5283
|
+
const fixed = "2025-01-01T00:00:00Z";
|
|
5284
|
+
for (const tag of ["w:ins", "w:del", "w:rPrChange", "w:pPrChange"]) {
|
|
5285
|
+
for (const el of findAllDescendants(doc.element, tag)) {
|
|
5286
|
+
if (el.hasAttribute("w:date")) {
|
|
5287
|
+
el.setAttribute("w:date", fixed);
|
|
5288
|
+
count++;
|
|
5289
|
+
}
|
|
4913
5290
|
}
|
|
4914
5291
|
}
|
|
4915
|
-
|
|
4916
|
-
if (includeAppendix) {
|
|
4917
|
-
const appendix = build_structural_appendix(doc, base_text);
|
|
4918
|
-
if (appendix) base_text += appendix;
|
|
4919
|
-
}
|
|
4920
|
-
if (return_paragraph_offsets) {
|
|
4921
|
-
return { text: base_text, paragraph_offsets };
|
|
4922
|
-
}
|
|
4923
|
-
return base_text;
|
|
5292
|
+
return count ? [`Track change timestamps: ${count} normalized`] : [];
|
|
4924
5293
|
}
|
|
4925
|
-
function
|
|
4926
|
-
const
|
|
4927
|
-
const
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
const sep = `---
|
|
4935
|
-
${header}`;
|
|
4936
|
-
blocks.push(sep);
|
|
4937
|
-
local_cursor += sep.length;
|
|
4938
|
-
is_first_block = false;
|
|
4939
|
-
}
|
|
4940
|
-
for (const item of iter_block_items(container)) {
|
|
4941
|
-
if (!is_first_block) local_cursor += 2;
|
|
4942
|
-
const block_start = local_cursor;
|
|
4943
|
-
if (item.constructor.name === "FootnoteItem") {
|
|
4944
|
-
const fn_text = _extract_blocks(item, comments_map, cleanView, block_start, paragraph_offsets);
|
|
4945
|
-
if (fn_text) {
|
|
4946
|
-
blocks.push(fn_text);
|
|
4947
|
-
local_cursor = block_start + fn_text.length;
|
|
4948
|
-
is_first_block = false;
|
|
4949
|
-
} else if (!is_first_block) {
|
|
4950
|
-
local_cursor -= 2;
|
|
4951
|
-
}
|
|
4952
|
-
} else if (item instanceof Paragraph) {
|
|
4953
|
-
let prefix = get_paragraph_prefix(item, style_cache, default_pstyle);
|
|
4954
|
-
if (is_first_para && container.constructor.name === "FootnoteItem") {
|
|
4955
|
-
prefix = `[^${container.note_type}-${container.id}]: ` + prefix;
|
|
5294
|
+
function scrub_doc_properties(doc) {
|
|
5295
|
+
const lines = [];
|
|
5296
|
+
const corePart = doc.pkg.getPartByPath("docProps/core.xml");
|
|
5297
|
+
if (corePart) {
|
|
5298
|
+
const creators = findDescendantsByLocalName(corePart._element, "creator");
|
|
5299
|
+
creators.forEach((c) => {
|
|
5300
|
+
if (c.textContent) {
|
|
5301
|
+
lines.push(`Author: ${c.textContent}`);
|
|
5302
|
+
c.textContent = "";
|
|
4956
5303
|
}
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
if (
|
|
4961
|
-
|
|
5304
|
+
});
|
|
5305
|
+
const modifiers = findDescendantsByLocalName(corePart._element, "lastModifiedBy");
|
|
5306
|
+
modifiers.forEach((c) => {
|
|
5307
|
+
if (c.textContent) {
|
|
5308
|
+
lines.push(`Last modified by: ${c.textContent}`);
|
|
5309
|
+
c.textContent = "";
|
|
4962
5310
|
}
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
blocks.push(table_text);
|
|
4970
|
-
local_cursor = block_start + table_text.length;
|
|
4971
|
-
is_first_block = false;
|
|
4972
|
-
} else if (!is_first_block) {
|
|
4973
|
-
local_cursor -= 2;
|
|
5311
|
+
});
|
|
5312
|
+
const revisions = findDescendantsByLocalName(corePart._element, "revision");
|
|
5313
|
+
revisions.forEach((c) => {
|
|
5314
|
+
if (c.textContent && parseInt(c.textContent) > 1) {
|
|
5315
|
+
lines.push(`Revision count: ${c.textContent} \u2192 1`);
|
|
5316
|
+
c.textContent = "1";
|
|
4974
5317
|
}
|
|
4975
|
-
|
|
5318
|
+
});
|
|
5319
|
+
}
|
|
5320
|
+
const appPart = doc.pkg.getPartByPath("docProps/app.xml");
|
|
5321
|
+
if (appPart) {
|
|
5322
|
+
const docEl = appPart._element;
|
|
5323
|
+
const intFields = ["TotalTime", "Words", "Characters", "Paragraphs", "Lines", "CharactersWithSpaces"];
|
|
5324
|
+
for (const f of intFields) {
|
|
5325
|
+
findDescendantsByLocalName(docEl, f).forEach((el) => {
|
|
5326
|
+
if (el.textContent && el.textContent !== "0") {
|
|
5327
|
+
if (f === "TotalTime") lines.push(`Total editing time: ${el.textContent} minutes`);
|
|
5328
|
+
el.textContent = "0";
|
|
5329
|
+
}
|
|
5330
|
+
});
|
|
5331
|
+
}
|
|
5332
|
+
const strFields = ["Template", "Manager", "Company"];
|
|
5333
|
+
for (const f of strFields) {
|
|
5334
|
+
findDescendantsByLocalName(docEl, f).forEach((el) => {
|
|
5335
|
+
if (el.textContent) {
|
|
5336
|
+
lines.push(`${f}: ${el.textContent}`);
|
|
5337
|
+
el.textContent = "";
|
|
5338
|
+
}
|
|
5339
|
+
});
|
|
4976
5340
|
}
|
|
4977
5341
|
}
|
|
4978
|
-
return
|
|
5342
|
+
return lines.length ? ["Metadata scrubbed:", ...lines.map((l) => ` ${l}`)] : [];
|
|
4979
5343
|
}
|
|
4980
|
-
function
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
const
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
const wrapper_prefix_len = !cleanView && ins ? 4 : !cleanView && del_node ? 4 : 0;
|
|
4993
|
-
let cell_cursor = row_start + wrapper_prefix_len;
|
|
4994
|
-
let first_cell = true;
|
|
4995
|
-
for (const cell of row.cells) {
|
|
4996
|
-
if (seen_cells.has(cell)) continue;
|
|
4997
|
-
seen_cells.add(cell);
|
|
4998
|
-
if (!first_cell) cell_cursor += 3;
|
|
4999
|
-
const cell_content = _extract_blocks(cell, comments_map, cleanView, cell_cursor, paragraph_offsets);
|
|
5000
|
-
cell_texts.push(cell_content);
|
|
5001
|
-
cell_cursor += cell_content.length;
|
|
5002
|
-
first_cell = false;
|
|
5344
|
+
function scrub_timestamps(doc) {
|
|
5345
|
+
let modified = false;
|
|
5346
|
+
const epoch = "1970-01-01T00:00:00Z";
|
|
5347
|
+
const corePart = doc.pkg.getPartByPath("docProps/core.xml");
|
|
5348
|
+
if (corePart) {
|
|
5349
|
+
for (const tag of ["created", "modified", "lastPrinted"]) {
|
|
5350
|
+
findDescendantsByLocalName(corePart._element, tag).forEach((el) => {
|
|
5351
|
+
if (el.textContent && el.textContent !== epoch) {
|
|
5352
|
+
el.textContent = epoch;
|
|
5353
|
+
modified = true;
|
|
5354
|
+
}
|
|
5355
|
+
});
|
|
5003
5356
|
}
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5357
|
+
}
|
|
5358
|
+
return modified ? ["Timestamps normalized to epoch"] : [];
|
|
5359
|
+
}
|
|
5360
|
+
function strip_custom_xml(doc) {
|
|
5361
|
+
const customParts = doc.pkg.parts.filter((p) => p.partname.includes("/customXml"));
|
|
5362
|
+
if (customParts.length === 0) return [];
|
|
5363
|
+
const partnames = new Set(customParts.map((p) => p.partname));
|
|
5364
|
+
doc.pkg.parts = doc.pkg.parts.filter((p) => !partnames.has(p.partname));
|
|
5365
|
+
const removeRelationsTo = (relsPart) => {
|
|
5366
|
+
const toRemove = [];
|
|
5367
|
+
for (const rel of findAllDescendants(relsPart._element, "Relationship")) {
|
|
5368
|
+
const target = rel.getAttribute("Target");
|
|
5369
|
+
if (target && target.includes("customXml")) toRemove.push(rel);
|
|
5008
5370
|
}
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5371
|
+
toRemove.forEach((r) => r.parentNode?.removeChild(r));
|
|
5372
|
+
};
|
|
5373
|
+
const rootRels = doc.pkg.getPartByPath("_rels/.rels");
|
|
5374
|
+
if (rootRels) removeRelationsTo(rootRels);
|
|
5375
|
+
const docRels = doc.pkg.getOrCreateRelsPart(doc.part.partname);
|
|
5376
|
+
if (docRels) removeRelationsTo(docRels);
|
|
5377
|
+
for (const sdtPr of findAllDescendants(doc.element, "w:sdtPr")) {
|
|
5378
|
+
findChildren(sdtPr, "w:dataBinding").forEach((b) => sdtPr.removeChild(b));
|
|
5012
5379
|
}
|
|
5013
|
-
return
|
|
5380
|
+
return [`Custom XML parts: ${customParts.length} removed`];
|
|
5014
5381
|
}
|
|
5015
|
-
function
|
|
5016
|
-
|
|
5017
|
-
const
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
const items = Array.from(iter_paragraph_content(paragraph));
|
|
5026
|
-
const is_heading = is_heading_paragraph(paragraph, style_cache, default_pstyle);
|
|
5027
|
-
const native_heading = is_native_heading(paragraph, style_cache, default_pstyle);
|
|
5028
|
-
let leading_strip_active = is_heading;
|
|
5029
|
-
for (let i = 0; i < items.length; i++) {
|
|
5030
|
-
const item = items[i];
|
|
5031
|
-
if (item instanceof Run) {
|
|
5032
|
-
const [prefix, suffix] = get_run_style_markers(item, native_heading);
|
|
5033
|
-
const text = get_run_text(item);
|
|
5034
|
-
if (cleanView && Object.keys(active_del).length > 0) continue;
|
|
5035
|
-
if (leading_strip_active) {
|
|
5036
|
-
if (!text || !text.trim()) continue;
|
|
5037
|
-
leading_strip_active = false;
|
|
5038
|
-
}
|
|
5039
|
-
const seg = apply_formatting_to_segments(text, prefix, suffix);
|
|
5040
|
-
if (seg) {
|
|
5041
|
-
const new_wrappers = cleanView ? ["", ""] : _get_wrappers(active_ins, active_del, active_comments, active_fmt);
|
|
5042
|
-
const new_style = [prefix, suffix];
|
|
5043
|
-
if (pending_text && new_wrappers[0] === current_wrappers[0] && new_wrappers[1] === current_wrappers[1]) {
|
|
5044
|
-
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])) {
|
|
5045
|
-
pending_text = pending_text.slice(0, -current_style[1].length) + seg.slice(new_style[0].length);
|
|
5046
|
-
} else {
|
|
5047
|
-
pending_text += seg;
|
|
5048
|
-
}
|
|
5049
|
-
current_style = new_style;
|
|
5050
|
-
} else {
|
|
5051
|
-
if (pending_text) parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5052
|
-
pending_text = seg;
|
|
5053
|
-
current_wrappers = new_wrappers;
|
|
5054
|
-
current_style = new_style;
|
|
5055
|
-
}
|
|
5056
|
-
if (!cleanView) {
|
|
5057
|
-
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;
|
|
5058
|
-
if (has_meta) {
|
|
5059
|
-
deferred_meta_states.push([{ ...active_ins }, { ...active_del }, new Set(active_comments), { ...active_fmt }]);
|
|
5060
|
-
}
|
|
5061
|
-
let should_defer = false;
|
|
5062
|
-
const is_redline = Object.keys(active_ins).length > 0 || Object.keys(active_del).length > 0 || Object.keys(active_fmt).length > 0;
|
|
5063
|
-
if (is_redline) {
|
|
5064
|
-
let j = i + 1;
|
|
5065
|
-
let next_is_redline = false;
|
|
5066
|
-
let temp_ins = Object.keys(active_ins).length;
|
|
5067
|
-
let temp_del = Object.keys(active_del).length;
|
|
5068
|
-
let temp_fmt = Object.keys(active_fmt).length;
|
|
5069
|
-
while (j < items.length) {
|
|
5070
|
-
const next_item = items[j];
|
|
5071
|
-
if (next_item instanceof Run) {
|
|
5072
|
-
if (!get_run_text(next_item)) {
|
|
5073
|
-
j++;
|
|
5074
|
-
continue;
|
|
5075
|
-
}
|
|
5076
|
-
if (temp_ins > 0 || temp_del > 0 || temp_fmt > 0) next_is_redline = true;
|
|
5077
|
-
break;
|
|
5078
|
-
} else {
|
|
5079
|
-
const ev = next_item;
|
|
5080
|
-
if (ev.type === "ins_start") temp_ins++;
|
|
5081
|
-
else if (ev.type === "ins_end") temp_ins = Math.max(0, temp_ins - 1);
|
|
5082
|
-
else if (ev.type === "del_start") temp_del++;
|
|
5083
|
-
else if (ev.type === "del_end") temp_del = Math.max(0, temp_del - 1);
|
|
5084
|
-
else if (ev.type === "fmt_start") temp_fmt++;
|
|
5085
|
-
else if (ev.type === "fmt_end") temp_fmt = Math.max(0, temp_fmt - 1);
|
|
5086
|
-
}
|
|
5087
|
-
j++;
|
|
5088
|
-
}
|
|
5089
|
-
if (next_is_redline) should_defer = true;
|
|
5090
|
-
}
|
|
5091
|
-
if (!should_defer && deferred_meta_states.length > 0) {
|
|
5092
|
-
const meta_block = _build_merged_meta_block(deferred_meta_states, comments_map);
|
|
5093
|
-
if (meta_block) {
|
|
5094
|
-
if (pending_text) {
|
|
5095
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5096
|
-
pending_text = "";
|
|
5097
|
-
current_wrappers = ["", ""];
|
|
5098
|
-
current_style = ["", ""];
|
|
5099
|
-
}
|
|
5100
|
-
parts.push(`{>>${meta_block}<<}`);
|
|
5101
|
-
}
|
|
5102
|
-
deferred_meta_states.length = 0;
|
|
5103
|
-
}
|
|
5104
|
-
}
|
|
5105
|
-
}
|
|
5106
|
-
} else {
|
|
5107
|
-
const ev = item;
|
|
5108
|
-
leading_strip_active = false;
|
|
5109
|
-
if (!["ins_start", "ins_end", "del_start", "del_end", "fmt_start", "fmt_end"].includes(ev.type)) {
|
|
5110
|
-
if (pending_text) {
|
|
5111
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5112
|
-
pending_text = "";
|
|
5113
|
-
current_wrappers = ["", ""];
|
|
5114
|
-
current_style = ["", ""];
|
|
5115
|
-
}
|
|
5116
|
-
}
|
|
5117
|
-
if (ev.type === "start") active_comments.add(ev.id);
|
|
5118
|
-
else if (ev.type === "end") active_comments.delete(ev.id);
|
|
5119
|
-
else if (ev.type === "ins_start") active_ins[ev.id] = ev;
|
|
5120
|
-
else if (ev.type === "ins_end") delete active_ins[ev.id];
|
|
5121
|
-
else if (ev.type === "del_start") active_del[ev.id] = ev;
|
|
5122
|
-
else if (ev.type === "del_end") delete active_del[ev.id];
|
|
5123
|
-
else if (ev.type === "fmt_start") active_fmt[ev.id] = ev;
|
|
5124
|
-
else if (ev.type === "fmt_end") delete active_fmt[ev.id];
|
|
5125
|
-
else if (ev.type === "footnote" || ev.type === "endnote") {
|
|
5126
|
-
if (pending_text) {
|
|
5127
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5128
|
-
pending_text = "";
|
|
5129
|
-
current_wrappers = ["", ""];
|
|
5130
|
-
current_style = ["", ""];
|
|
5131
|
-
}
|
|
5132
|
-
parts.push(`[^${ev.type === "footnote" ? "fn" : "en"}-${ev.id}]`);
|
|
5133
|
-
} else if (ev.type === "hyperlink_start") {
|
|
5134
|
-
if (pending_text) {
|
|
5135
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5136
|
-
pending_text = "";
|
|
5137
|
-
current_wrappers = ["", ""];
|
|
5138
|
-
current_style = ["", ""];
|
|
5139
|
-
}
|
|
5140
|
-
parts.push("[");
|
|
5141
|
-
} else if (ev.type === "hyperlink_end") {
|
|
5142
|
-
if (pending_text) {
|
|
5143
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5144
|
-
pending_text = "";
|
|
5145
|
-
current_wrappers = ["", ""];
|
|
5146
|
-
current_style = ["", ""];
|
|
5147
|
-
}
|
|
5148
|
-
parts.push(`](${ev.date})`);
|
|
5149
|
-
} else if (ev.type === "xref_start") {
|
|
5150
|
-
if (pending_text) {
|
|
5151
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5152
|
-
pending_text = "";
|
|
5153
|
-
current_wrappers = ["", ""];
|
|
5154
|
-
current_style = ["", ""];
|
|
5155
|
-
}
|
|
5156
|
-
parts.push("[~");
|
|
5157
|
-
} else if (ev.type === "xref_end") {
|
|
5158
|
-
if (pending_text) {
|
|
5159
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5160
|
-
pending_text = "";
|
|
5161
|
-
current_wrappers = ["", ""];
|
|
5162
|
-
current_style = ["", ""];
|
|
5163
|
-
}
|
|
5164
|
-
parts.push(`~](#${ev.id})`);
|
|
5165
|
-
} else if (ev.type === "bookmark") {
|
|
5166
|
-
if (pending_text) {
|
|
5167
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5168
|
-
pending_text = "";
|
|
5169
|
-
current_wrappers = ["", ""];
|
|
5170
|
-
current_style = ["", ""];
|
|
5171
|
-
}
|
|
5172
|
-
parts.push(`{#${ev.id}}`);
|
|
5382
|
+
function strip_image_alt_text(doc) {
|
|
5383
|
+
let count = 0;
|
|
5384
|
+
for (const docPr of findDescendantsByLocalName(doc.element, "docPr")) {
|
|
5385
|
+
const descr = docPr.getAttribute("descr");
|
|
5386
|
+
if (descr) {
|
|
5387
|
+
const isShort = descr.length < 10;
|
|
5388
|
+
const isFile = descr.includes(".") && descr.length < 60;
|
|
5389
|
+
if (isShort || isFile) {
|
|
5390
|
+
docPr.removeAttribute("descr");
|
|
5391
|
+
count++;
|
|
5173
5392
|
}
|
|
5174
5393
|
}
|
|
5175
5394
|
}
|
|
5176
|
-
|
|
5177
|
-
if (deferred_meta_states.length > 0) {
|
|
5178
|
-
const meta_block = _build_merged_meta_block(deferred_meta_states, comments_map);
|
|
5179
|
-
if (meta_block) parts.push(`{>>${meta_block}<<}`);
|
|
5180
|
-
}
|
|
5181
|
-
return parts.join("");
|
|
5395
|
+
return count ? [`Image alt text: ${count} auto-generated descriptions removed`] : [];
|
|
5182
5396
|
}
|
|
5183
|
-
function
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
let render_comment2 = function(cid) {
|
|
5195
|
-
if (!comments_map[cid]) return;
|
|
5196
|
-
const sig = `Com:${cid}`;
|
|
5197
|
-
if (seen_sigs.has(sig)) return;
|
|
5198
|
-
const data = comments_map[cid];
|
|
5199
|
-
let header = `[${sig}] ${data.author}`;
|
|
5200
|
-
if (data.date) header += ` @ ${data.date}`;
|
|
5201
|
-
if (data.resolved) header += `(RESOLVED)`;
|
|
5202
|
-
comment_lines.push(`${header}: ${data.text}`);
|
|
5203
|
-
seen_sigs.add(sig);
|
|
5204
|
-
if (children_map[cid]) {
|
|
5205
|
-
const children = children_map[cid].sort((a, b) => (comments_map[a]?.date || "").localeCompare(comments_map[b]?.date || ""));
|
|
5206
|
-
for (const child_id of children) {
|
|
5207
|
-
render_comment2(child_id);
|
|
5397
|
+
function audit_hyperlinks(doc) {
|
|
5398
|
+
const internal = ["sharepoint.com", "onedrive.com", ".internal", "intranet", "localhost", "10.", "192.168.", "172.16."];
|
|
5399
|
+
const warnings = [];
|
|
5400
|
+
const docRels = doc.pkg.getOrCreateRelsPart(doc.part.partname);
|
|
5401
|
+
for (const rel of findAllDescendants(docRels._element, "Relationship")) {
|
|
5402
|
+
if (rel.getAttribute("TargetMode") === "External") {
|
|
5403
|
+
const url = rel.getAttribute("Target") || "";
|
|
5404
|
+
for (const pattern of internal) {
|
|
5405
|
+
if (url.toLowerCase().includes(pattern.toLowerCase())) {
|
|
5406
|
+
warnings.push(`Hyperlink targets internal URL: ${_truncate(url, 80)}`);
|
|
5407
|
+
break;
|
|
5208
5408
|
}
|
|
5209
5409
|
}
|
|
5210
|
-
};
|
|
5211
|
-
var render_comment = render_comment2;
|
|
5212
|
-
for (const [uid, meta] of Object.entries(ins_map)) {
|
|
5213
|
-
const sig = `Chg:${uid}`;
|
|
5214
|
-
if (!seen_sigs.has(sig)) {
|
|
5215
|
-
change_lines.push(`[${sig} insert] ${meta.author || "Unknown"}`);
|
|
5216
|
-
seen_sigs.add(sig);
|
|
5217
|
-
}
|
|
5218
|
-
}
|
|
5219
|
-
for (const [uid, meta] of Object.entries(del_map)) {
|
|
5220
|
-
const sig = `Chg:${uid}`;
|
|
5221
|
-
if (!seen_sigs.has(sig)) {
|
|
5222
|
-
change_lines.push(`[${sig} delete] ${meta.author || "Unknown"}`);
|
|
5223
|
-
seen_sigs.add(sig);
|
|
5224
|
-
}
|
|
5225
|
-
}
|
|
5226
|
-
for (const [uid, meta] of Object.entries(fmt_map)) {
|
|
5227
|
-
const sig = `Chg:${uid}`;
|
|
5228
|
-
if (!seen_sigs.has(sig)) {
|
|
5229
|
-
change_lines.push(`[${sig} format] ${meta.author || "Unknown"}`);
|
|
5230
|
-
seen_sigs.add(sig);
|
|
5231
|
-
}
|
|
5232
|
-
}
|
|
5233
|
-
const children_map = {};
|
|
5234
|
-
for (const [c_id, data] of Object.entries(comments_map)) {
|
|
5235
|
-
const p_id = data.parent_id;
|
|
5236
|
-
if (p_id) {
|
|
5237
|
-
if (!children_map[p_id]) children_map[p_id] = [];
|
|
5238
|
-
children_map[p_id].push(c_id);
|
|
5239
|
-
}
|
|
5240
|
-
}
|
|
5241
|
-
const sorted_ids = Array.from(comments_set).sort();
|
|
5242
|
-
for (const c_id of sorted_ids) {
|
|
5243
|
-
render_comment2(c_id);
|
|
5244
5410
|
}
|
|
5245
5411
|
}
|
|
5246
|
-
return
|
|
5412
|
+
return warnings;
|
|
5247
5413
|
}
|
|
5248
5414
|
|
|
5249
|
-
// src/
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
if (
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
const comments_map = extract_comments_data(doc.pkg);
|
|
5260
|
-
const block_records = _walk_doc_body(doc, comments_map);
|
|
5261
|
-
const heading_indices = [];
|
|
5262
|
-
for (let idx = 0; idx < block_records.length; idx++) {
|
|
5263
|
-
const rec = block_records[idx];
|
|
5264
|
-
if (!(rec.is_paragraph && _is_heading(rec.item))) continue;
|
|
5265
|
-
if (!_heading_passes_quality_filter(rec.item, comments_map)) continue;
|
|
5266
|
-
heading_indices.push(idx);
|
|
5415
|
+
// src/domain.ts
|
|
5416
|
+
function boundedLevenshtein(a, b, maxDist = 2) {
|
|
5417
|
+
if (a === b) return 0;
|
|
5418
|
+
if (Math.abs(a.length - b.length) > maxDist) return maxDist + 1;
|
|
5419
|
+
if (a.length === 0) return b.length <= maxDist ? b.length : maxDist + 1;
|
|
5420
|
+
if (b.length === 0) return a.length <= maxDist ? a.length : maxDist + 1;
|
|
5421
|
+
if (a.length > b.length) {
|
|
5422
|
+
const temp = a;
|
|
5423
|
+
a = b;
|
|
5424
|
+
b = temp;
|
|
5267
5425
|
}
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
h_pos,
|
|
5281
|
-
level
|
|
5282
|
-
);
|
|
5283
|
-
const owned_blocks = block_records.slice(rec_idx + 1, owned_end);
|
|
5284
|
-
const has_table = _direct_has_table(block_records, rec_idx + 1, owned_end);
|
|
5285
|
-
const footnote_ids = _collect_footnote_ids(owned_blocks);
|
|
5286
|
-
const page_num = _offset_to_page(rec.start_offset, body_page_offsets);
|
|
5287
|
-
nodes.push({ level, text, page: page_num, style, has_table, footnote_ids });
|
|
5426
|
+
let row = Array.from({ length: a.length + 1 }, (_, i) => i);
|
|
5427
|
+
for (let i = 1; i <= b.length; i++) {
|
|
5428
|
+
const newRow = [i];
|
|
5429
|
+
let minInRow = i;
|
|
5430
|
+
for (let j = 1; j <= a.length; j++) {
|
|
5431
|
+
const cost = a[j - 1] === b[i - 1] ? 0 : 1;
|
|
5432
|
+
const val = Math.min(row[j] + 1, newRow[j - 1] + 1, row[j - 1] + cost);
|
|
5433
|
+
newRow.push(val);
|
|
5434
|
+
if (val < minInRow) minInRow = val;
|
|
5435
|
+
}
|
|
5436
|
+
if (minInRow > maxDist) return maxDist + 1;
|
|
5437
|
+
row = newRow;
|
|
5288
5438
|
}
|
|
5289
|
-
return
|
|
5439
|
+
return row[a.length] <= maxDist ? row[a.length] : maxDist + 1;
|
|
5290
5440
|
}
|
|
5291
|
-
function
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5441
|
+
function _get_paragraph_text(p) {
|
|
5442
|
+
let text = "";
|
|
5443
|
+
const runs = findAllDescendants(p._element, "w:r");
|
|
5444
|
+
for (const r of runs) {
|
|
5445
|
+
text += get_run_text(new Run(r, p));
|
|
5296
5446
|
}
|
|
5297
|
-
return
|
|
5447
|
+
return text;
|
|
5298
5448
|
}
|
|
5299
|
-
function
|
|
5300
|
-
const
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5449
|
+
function extract_all_domain_metadata(doc, base_text) {
|
|
5450
|
+
const definitions = {};
|
|
5451
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
5452
|
+
const raw_anchors = {};
|
|
5453
|
+
const raw_references = [];
|
|
5454
|
+
const leading_re = /^(?:[\d.\-()a-zA-Z]+\s*)?["“]([A-Z][A-Za-z0-9\s\-&'’]{1,60})["”]/;
|
|
5455
|
+
const inline_re = /\([^)]*?["“]([A-Z][A-Za-z0-9\s\-&'’]{1,60})["”][^)]*?\)/g;
|
|
5456
|
+
for (const item of iter_block_items(doc)) {
|
|
5457
|
+
if (!(item instanceof Paragraph)) continue;
|
|
5458
|
+
const text = _get_paragraph_text(item).trim();
|
|
5459
|
+
if (!text) continue;
|
|
5460
|
+
const extracted_terms = [];
|
|
5461
|
+
const leading_match = text.match(leading_re);
|
|
5462
|
+
if (leading_match) extracted_terms.push(leading_match[1].trim());
|
|
5463
|
+
const inline_matches = text.matchAll(inline_re);
|
|
5464
|
+
for (const m of inline_matches) {
|
|
5465
|
+
extracted_terms.push(m[1].trim());
|
|
5307
5466
|
}
|
|
5308
|
-
const
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5467
|
+
for (const term of extracted_terms) {
|
|
5468
|
+
if (definitions[term]) duplicates.add(term);
|
|
5469
|
+
else definitions[term] = { count: 0 };
|
|
5470
|
+
}
|
|
5471
|
+
const short_text = text.length > 60 ? text.substring(0, 60) + "..." : text;
|
|
5472
|
+
const nodes = findAllDescendants(item._element, "*");
|
|
5473
|
+
for (const node of nodes) {
|
|
5474
|
+
if (node.tagName === "w:bookmarkStart") {
|
|
5475
|
+
const b_name = node.getAttribute("w:name");
|
|
5476
|
+
if (b_name && (!b_name.startsWith("_") || b_name.startsWith("_Ref"))) {
|
|
5477
|
+
if (!raw_anchors[b_name]) {
|
|
5478
|
+
raw_anchors[b_name] = {
|
|
5479
|
+
anchored_to: short_text,
|
|
5480
|
+
referenced_from: []
|
|
5481
|
+
};
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
}
|
|
5485
|
+
let target = null;
|
|
5486
|
+
if (node.tagName === "w:fldSimple") {
|
|
5487
|
+
const instr = node.getAttribute("w:instr") || "";
|
|
5488
|
+
const parts = instr.trim().split(/\s+/);
|
|
5489
|
+
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
5490
|
+
} else if (node.tagName === "w:instrText") {
|
|
5491
|
+
const instr = node.textContent || "";
|
|
5492
|
+
const parts = instr.trim().split(/\s+/);
|
|
5493
|
+
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
5494
|
+
}
|
|
5495
|
+
if (target) raw_references.push([target, short_text]);
|
|
5312
5496
|
}
|
|
5313
5497
|
}
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
if (body_start_offset > 0) body_start_offset += 2;
|
|
5498
|
+
for (const [target, ref_text] of raw_references) {
|
|
5499
|
+
if (raw_anchors[target]) {
|
|
5500
|
+
raw_anchors[target].referenced_from.push(ref_text);
|
|
5501
|
+
}
|
|
5319
5502
|
}
|
|
5320
|
-
const
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
}
|
|
5336
|
-
cursor += block_len;
|
|
5337
|
-
is_first_block = false;
|
|
5338
|
-
} else if (item instanceof Table) {
|
|
5339
|
-
const table_text = extract_table(item, comments_map, false, 0);
|
|
5340
|
-
const block_len = table_text ? table_text.length : 0;
|
|
5341
|
-
if (!is_first_block) cursor += 2;
|
|
5342
|
-
const table_start = cursor;
|
|
5343
|
-
records.push({
|
|
5344
|
-
item,
|
|
5345
|
-
is_paragraph: false,
|
|
5346
|
-
is_table: true,
|
|
5347
|
-
start_offset: table_start,
|
|
5348
|
-
projected_length: block_len
|
|
5349
|
-
});
|
|
5350
|
-
_record_table_inner_blocks_lite(item, table_start, records, comments_map);
|
|
5351
|
-
cursor += block_len;
|
|
5352
|
-
is_first_block = false;
|
|
5503
|
+
const diagnostics = [];
|
|
5504
|
+
const def_keys = Object.keys(definitions);
|
|
5505
|
+
if (def_keys.length > 0) {
|
|
5506
|
+
const sorted_terms = def_keys.sort((a, b) => b.length - a.length);
|
|
5507
|
+
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5508
|
+
const alt = sorted_terms.map(escapeRegExp).join("|");
|
|
5509
|
+
const usage_pattern = new RegExp(`(?<!["\u201C])\\b(${alt})\\b(?![\u201D"])`, "g");
|
|
5510
|
+
for (const m of base_text.matchAll(usage_pattern)) {
|
|
5511
|
+
const matched_term = m[1];
|
|
5512
|
+
if (definitions[matched_term]) definitions[matched_term].count++;
|
|
5513
|
+
}
|
|
5514
|
+
for (const term of def_keys) {
|
|
5515
|
+
if (definitions[term].count === 0) {
|
|
5516
|
+
delete definitions[term];
|
|
5517
|
+
duplicates.delete(term);
|
|
5518
|
+
}
|
|
5353
5519
|
}
|
|
5354
5520
|
}
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
let cursor = table_start_offset;
|
|
5360
|
-
let rows_processed = 0;
|
|
5361
|
-
for (const row of table.rows) {
|
|
5362
|
-
if (rows_processed > 0) cursor += 1;
|
|
5363
|
-
const seen_cells = /* @__PURE__ */ new Set();
|
|
5364
|
-
let cells_in_row = 0;
|
|
5365
|
-
for (const cell of row.cells) {
|
|
5366
|
-
if (seen_cells.has(cell)) continue;
|
|
5367
|
-
seen_cells.add(cell);
|
|
5368
|
-
if (cells_in_row > 0) cursor += 3;
|
|
5369
|
-
const [new_cursor, found] = _walk_cell_for_offset(
|
|
5370
|
-
cell,
|
|
5371
|
-
target_el,
|
|
5372
|
-
cursor,
|
|
5373
|
-
comments_map
|
|
5374
|
-
);
|
|
5375
|
-
if (found) return new_cursor;
|
|
5376
|
-
cursor = new_cursor;
|
|
5377
|
-
cells_in_row++;
|
|
5378
|
-
}
|
|
5379
|
-
rows_processed++;
|
|
5521
|
+
for (const term of duplicates) {
|
|
5522
|
+
diagnostics.push(
|
|
5523
|
+
`[Error] Duplicate Definition: '${term}' is defined multiple times.`
|
|
5524
|
+
);
|
|
5380
5525
|
}
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5526
|
+
const stop_words = /* @__PURE__ */ new Set([
|
|
5527
|
+
"The",
|
|
5528
|
+
"This",
|
|
5529
|
+
"That",
|
|
5530
|
+
"Such",
|
|
5531
|
+
"A",
|
|
5532
|
+
"An",
|
|
5533
|
+
"Any",
|
|
5534
|
+
"All",
|
|
5535
|
+
"Some",
|
|
5536
|
+
"No",
|
|
5537
|
+
"Every",
|
|
5538
|
+
"Each",
|
|
5539
|
+
"As",
|
|
5540
|
+
"In",
|
|
5541
|
+
"Of",
|
|
5542
|
+
"For",
|
|
5543
|
+
"To",
|
|
5544
|
+
"On",
|
|
5545
|
+
"By",
|
|
5546
|
+
"With"
|
|
5547
|
+
]);
|
|
5548
|
+
const all_cap_pattern = /\b[A-Z][a-zA-Z]*(?:\s+[A-Z][a-zA-Z]*)*\b/g;
|
|
5549
|
+
const all_caps = new Set(base_text.match(all_cap_pattern) || []);
|
|
5550
|
+
const valid_terms = new Set(Object.keys(definitions));
|
|
5551
|
+
const terms_by_first_letter = {};
|
|
5552
|
+
for (const term of valid_terms) {
|
|
5553
|
+
const fl = term[0].toLowerCase();
|
|
5554
|
+
if (!terms_by_first_letter[fl]) terms_by_first_letter[fl] = [];
|
|
5555
|
+
terms_by_first_letter[fl].push(term);
|
|
5556
|
+
}
|
|
5557
|
+
const candidates_by_term = {};
|
|
5558
|
+
for (const raw_candidate of all_caps) {
|
|
5559
|
+
let candidate = raw_candidate.trim();
|
|
5560
|
+
const words = candidate.split(/\s+/);
|
|
5561
|
+
while (words.length > 0) {
|
|
5562
|
+
const first = words[0];
|
|
5563
|
+
const title = first.charAt(0).toUpperCase() + first.slice(1).toLowerCase();
|
|
5564
|
+
if (stop_words.has(title)) words.shift();
|
|
5565
|
+
else break;
|
|
5566
|
+
}
|
|
5567
|
+
candidate = words.join(" ");
|
|
5568
|
+
if (candidate.length < 4) continue;
|
|
5569
|
+
if (valid_terms.has(candidate)) continue;
|
|
5570
|
+
const first_letter = candidate[0].toLowerCase();
|
|
5571
|
+
let candidate_terms = terms_by_first_letter[first_letter] || [];
|
|
5572
|
+
if (candidate.length > 5) {
|
|
5573
|
+
for (const [k, v] of Object.entries(terms_by_first_letter)) {
|
|
5574
|
+
if (k !== first_letter) candidate_terms = candidate_terms.concat(v);
|
|
5403
5575
|
}
|
|
5404
|
-
const table_text = extract_table(inner_item, comments_map, false, 0);
|
|
5405
|
-
cursor += table_text ? table_text.length : 0;
|
|
5406
5576
|
}
|
|
5407
|
-
|
|
5577
|
+
for (const term of candidate_terms) {
|
|
5578
|
+
if (Math.abs(candidate.length - term.length) > 2) continue;
|
|
5579
|
+
if (candidate === term + "s" || candidate === term + "es") continue;
|
|
5580
|
+
if (term === candidate + "s" || term === candidate + "es") continue;
|
|
5581
|
+
const dist = boundedLevenshtein(candidate, term, 2);
|
|
5582
|
+
if (dist === 0 || dist > 2) continue;
|
|
5583
|
+
if (term.length <= 5) {
|
|
5584
|
+
if (dist > 1) continue;
|
|
5585
|
+
if (candidate[0].toLowerCase() !== term[0].toLowerCase()) continue;
|
|
5586
|
+
}
|
|
5587
|
+
if (!candidates_by_term[term]) candidates_by_term[term] = [];
|
|
5588
|
+
if (!candidates_by_term[term].includes(candidate))
|
|
5589
|
+
candidates_by_term[term].push(candidate);
|
|
5590
|
+
}
|
|
5408
5591
|
}
|
|
5409
|
-
|
|
5592
|
+
for (const [term, candidates] of Object.entries(candidates_by_term)) {
|
|
5593
|
+
candidates.sort();
|
|
5594
|
+
const c_str = candidates.map((c) => `'${c}'`).join(", ");
|
|
5595
|
+
diagnostics.push(`[Info] Possible Typos for '${term}': Found ${c_str}`);
|
|
5596
|
+
}
|
|
5597
|
+
function diag_sort_key(msg) {
|
|
5598
|
+
if (msg.startsWith("[Error]")) return 0;
|
|
5599
|
+
if (msg.startsWith("[Warning]")) return 1;
|
|
5600
|
+
return 2;
|
|
5601
|
+
}
|
|
5602
|
+
diagnostics.sort((a, b) => {
|
|
5603
|
+
const keyA = diag_sort_key(a);
|
|
5604
|
+
const keyB = diag_sort_key(b);
|
|
5605
|
+
if (keyA !== keyB) return keyA - keyB;
|
|
5606
|
+
return a.localeCompare(b);
|
|
5607
|
+
});
|
|
5608
|
+
return [definitions, diagnostics, raw_anchors];
|
|
5410
5609
|
}
|
|
5411
|
-
function
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5610
|
+
function extract_document_settings_warnings(doc) {
|
|
5611
|
+
const warnings = [];
|
|
5612
|
+
const settingsPart = doc.pkg.getPartByPath("word/settings.xml");
|
|
5613
|
+
if (!settingsPart) return warnings;
|
|
5614
|
+
const isTruthy = (el) => {
|
|
5615
|
+
if (!el.hasAttribute("w:val")) return true;
|
|
5616
|
+
const val = (el.getAttribute("w:val") || "").toLowerCase();
|
|
5617
|
+
return val !== "0" && val !== "false" && val !== "off";
|
|
5618
|
+
};
|
|
5619
|
+
const removePersonal = findDescendantsByLocalName(
|
|
5620
|
+
settingsPart._element,
|
|
5621
|
+
"removePersonalInformation"
|
|
5622
|
+
);
|
|
5623
|
+
if (removePersonal.length > 0 && isTruthy(removePersonal[0])) {
|
|
5624
|
+
warnings.push(
|
|
5625
|
+
"[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."
|
|
5626
|
+
);
|
|
5416
5627
|
}
|
|
5417
|
-
|
|
5628
|
+
const removeDateTime = findDescendantsByLocalName(
|
|
5629
|
+
settingsPart._element,
|
|
5630
|
+
"removeDateAndTime"
|
|
5631
|
+
);
|
|
5632
|
+
if (removeDateTime.length > 0 && isTruthy(removeDateTime[0])) {
|
|
5633
|
+
warnings.push(
|
|
5634
|
+
"[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."
|
|
5635
|
+
);
|
|
5636
|
+
}
|
|
5637
|
+
return warnings;
|
|
5418
5638
|
}
|
|
5419
|
-
function
|
|
5420
|
-
const
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
projected_length: 0
|
|
5439
|
-
});
|
|
5440
|
-
} else if (inner_item instanceof Table) {
|
|
5441
|
-
records.push({
|
|
5442
|
-
item: inner_item,
|
|
5443
|
-
is_paragraph: false,
|
|
5444
|
-
is_table: true,
|
|
5445
|
-
start_offset: inherited_offset,
|
|
5446
|
-
projected_length: 0
|
|
5447
|
-
});
|
|
5448
|
-
_record_table_inner_blocks_lite(
|
|
5449
|
-
inner_item,
|
|
5450
|
-
inherited_offset,
|
|
5451
|
-
records,
|
|
5452
|
-
comments_map
|
|
5453
|
-
);
|
|
5454
|
-
}
|
|
5455
|
-
}
|
|
5639
|
+
function build_structural_appendix(doc, base_text) {
|
|
5640
|
+
const [defs, diagnostics, anchors] = extract_all_domain_metadata(
|
|
5641
|
+
doc,
|
|
5642
|
+
base_text
|
|
5643
|
+
);
|
|
5644
|
+
const settings_warnings = extract_document_settings_warnings(doc);
|
|
5645
|
+
const lines = [
|
|
5646
|
+
"\n\n---",
|
|
5647
|
+
"",
|
|
5648
|
+
"<!-- READONLY_BOUNDARY_START -->",
|
|
5649
|
+
"# Document Structure (Read-Only)",
|
|
5650
|
+
"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."
|
|
5651
|
+
];
|
|
5652
|
+
let has_content = false;
|
|
5653
|
+
if (settings_warnings.length > 0) {
|
|
5654
|
+
has_content = true;
|
|
5655
|
+
lines.push("\n## Document Settings");
|
|
5656
|
+
for (const warning of settings_warnings) {
|
|
5657
|
+
lines.push(`- ${warning}`);
|
|
5456
5658
|
}
|
|
5457
5659
|
}
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
blocks.push(`---
|
|
5465
|
-
${header}`);
|
|
5660
|
+
if (Object.keys(defs).length > 0) {
|
|
5661
|
+
has_content = true;
|
|
5662
|
+
lines.push("\n## Defined Terms");
|
|
5663
|
+
for (const [term, data] of Object.entries(defs)) {
|
|
5664
|
+
lines.push(`- "${term}" \u2014 used ${data.count} times.`);
|
|
5665
|
+
}
|
|
5466
5666
|
}
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
} else if (item instanceof Paragraph) {
|
|
5473
|
-
let prefix = get_paragraph_prefix(item);
|
|
5474
|
-
if (is_first_para && c_type === "FootnoteItem")
|
|
5475
|
-
prefix = `[^${part.note_type}-${part.id}]: ${prefix}`;
|
|
5476
|
-
const p_text = build_paragraph_text(item, comments_map, false);
|
|
5477
|
-
blocks.push(prefix + p_text);
|
|
5478
|
-
is_first_para = false;
|
|
5479
|
-
} else if (item instanceof Table) {
|
|
5480
|
-
const table_text = extract_table(item, comments_map, false, 0);
|
|
5481
|
-
if (table_text) blocks.push(table_text);
|
|
5482
|
-
is_first_para = false;
|
|
5667
|
+
if (diagnostics.length > 0) {
|
|
5668
|
+
has_content = true;
|
|
5669
|
+
lines.push("\n## Semantic Diagnostics");
|
|
5670
|
+
for (const diag of diagnostics) {
|
|
5671
|
+
lines.push(`- ${diag}`);
|
|
5483
5672
|
}
|
|
5484
5673
|
}
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
}
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5674
|
+
if (Object.keys(anchors).length > 0) {
|
|
5675
|
+
has_content = true;
|
|
5676
|
+
lines.push("\n## Named Anchors");
|
|
5677
|
+
for (const [b_name, data] of Object.entries(anchors)) {
|
|
5678
|
+
lines.push(`- ${b_name} \u2192 Anchored to: "${data.anchored_to}"`);
|
|
5679
|
+
for (const ref of data.referenced_from) {
|
|
5680
|
+
lines.push(` - Referenced from: "${ref}"`);
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
}
|
|
5684
|
+
if (has_content) {
|
|
5685
|
+
return lines.join("\n");
|
|
5686
|
+
}
|
|
5687
|
+
return "";
|
|
5497
5688
|
}
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5689
|
+
|
|
5690
|
+
// src/ingest.ts
|
|
5691
|
+
async function extractTextFromBuffer(buffer, cleanView = false) {
|
|
5692
|
+
const doc = await DocumentObject.load(buffer);
|
|
5693
|
+
return _extractTextFromDoc(doc, cleanView);
|
|
5501
5694
|
}
|
|
5502
|
-
function
|
|
5503
|
-
const
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
text = text.replace(/(?<!\w)_(\S(?:.*?\S)?)_(?!\w)/g, "$1");
|
|
5521
|
-
return text;
|
|
5522
|
-
}
|
|
5523
|
-
function _determine_heading_style(paragraph) {
|
|
5524
|
-
const [style_cache, default_pstyle] = _get_style_cache(
|
|
5525
|
-
paragraph._parent.part || paragraph._parent
|
|
5526
|
-
);
|
|
5527
|
-
const pPr = findChild(paragraph._element, "w:pPr");
|
|
5528
|
-
let style_id = default_pstyle;
|
|
5529
|
-
if (pPr) {
|
|
5530
|
-
const pStyle = findChild(pPr, "w:pStyle");
|
|
5531
|
-
if (pStyle) style_id = pStyle.getAttribute("w:val") || default_pstyle;
|
|
5532
|
-
}
|
|
5533
|
-
let outline_level = null;
|
|
5534
|
-
let outline_level_from_style = false;
|
|
5535
|
-
if (pPr) {
|
|
5536
|
-
const oLvl = findChild(pPr, "w:outlineLvl");
|
|
5537
|
-
if (oLvl && /^\d+$/.test(oLvl.getAttribute("w:val") || "")) {
|
|
5538
|
-
outline_level = parseInt(oLvl.getAttribute("w:val"), 10);
|
|
5539
|
-
}
|
|
5540
|
-
}
|
|
5541
|
-
if (outline_level === null && style_id && style_cache && style_cache[style_id]) {
|
|
5542
|
-
outline_level = style_cache[style_id].outline_level;
|
|
5543
|
-
outline_level_from_style = true;
|
|
5544
|
-
}
|
|
5545
|
-
const style_name = style_id && style_cache && style_cache[style_id] ? style_cache[style_id].name : style_id;
|
|
5546
|
-
let normalized_style_name = style_name;
|
|
5547
|
-
if (normalized_style_name && typeof normalized_style_name === "string") {
|
|
5548
|
-
if (normalized_style_name.toLowerCase().startsWith("heading")) {
|
|
5549
|
-
normalized_style_name = normalized_style_name.replace(/^heading/i, "Heading");
|
|
5550
|
-
} else if (normalized_style_name.toLowerCase() === "title") {
|
|
5551
|
-
normalized_style_name = "Title";
|
|
5695
|
+
function _extractTextFromDoc(doc, cleanView = false, includeAppendix = true, return_paragraph_offsets = false) {
|
|
5696
|
+
const comments_map = extract_comments_data(doc.pkg);
|
|
5697
|
+
const full_text = [];
|
|
5698
|
+
const paragraph_offsets = /* @__PURE__ */ new Map();
|
|
5699
|
+
let cursor = 0;
|
|
5700
|
+
for (const part of iter_document_parts(doc)) {
|
|
5701
|
+
const part_cursor = full_text.length > 0 ? cursor + 2 : cursor;
|
|
5702
|
+
const part_text = _extract_blocks(
|
|
5703
|
+
part,
|
|
5704
|
+
comments_map,
|
|
5705
|
+
cleanView,
|
|
5706
|
+
part_cursor,
|
|
5707
|
+
return_paragraph_offsets ? paragraph_offsets : void 0
|
|
5708
|
+
);
|
|
5709
|
+
if (part_text) {
|
|
5710
|
+
if (full_text.length > 0) cursor += 2;
|
|
5711
|
+
full_text.push(part_text);
|
|
5712
|
+
cursor += part_text.length;
|
|
5552
5713
|
}
|
|
5553
5714
|
}
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
}
|
|
5715
|
+
let base_text = full_text.join("\n\n");
|
|
5716
|
+
if (includeAppendix) {
|
|
5717
|
+
const appendix = build_structural_appendix(doc, base_text);
|
|
5718
|
+
if (appendix) base_text += appendix;
|
|
5559
5719
|
}
|
|
5560
|
-
if (
|
|
5561
|
-
|
|
5562
|
-
return normalized_style_name;
|
|
5563
|
-
}
|
|
5564
|
-
return "(outline_level)";
|
|
5720
|
+
if (return_paragraph_offsets) {
|
|
5721
|
+
return { text: base_text, paragraph_offsets };
|
|
5565
5722
|
}
|
|
5566
|
-
|
|
5567
|
-
return normalized_style_name;
|
|
5568
|
-
if (normalized_style_name && /Heading[ ]?([1-6])(?![0-9])/.test(normalized_style_name))
|
|
5569
|
-
return normalized_style_name;
|
|
5570
|
-
return "(heuristic)";
|
|
5723
|
+
return base_text;
|
|
5571
5724
|
}
|
|
5572
|
-
function
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5725
|
+
function _extract_blocks(container, comments_map, cleanView, cursor, paragraph_offsets) {
|
|
5726
|
+
const part = container.part || container;
|
|
5727
|
+
const [style_cache, default_pstyle] = _get_style_cache(part);
|
|
5728
|
+
const blocks = [];
|
|
5729
|
+
let local_cursor = cursor;
|
|
5730
|
+
let is_first_block = true;
|
|
5731
|
+
let is_first_para = true;
|
|
5732
|
+
if (container.constructor && container.constructor.name === "NotesPart") {
|
|
5733
|
+
const header = container.note_type === "fn" ? "## Footnotes" : "## Endnotes";
|
|
5734
|
+
const sep = `---
|
|
5735
|
+
${header}`;
|
|
5736
|
+
blocks.push(sep);
|
|
5737
|
+
local_cursor += sep.length;
|
|
5738
|
+
is_first_block = false;
|
|
5577
5739
|
}
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5740
|
+
for (const item of iter_block_items(container)) {
|
|
5741
|
+
if (!is_first_block) local_cursor += 2;
|
|
5742
|
+
const block_start = local_cursor;
|
|
5743
|
+
if (item.constructor.name === "FootnoteItem") {
|
|
5744
|
+
const fn_text = _extract_blocks(
|
|
5745
|
+
item,
|
|
5746
|
+
comments_map,
|
|
5747
|
+
cleanView,
|
|
5748
|
+
block_start,
|
|
5749
|
+
paragraph_offsets
|
|
5750
|
+
);
|
|
5751
|
+
if (fn_text) {
|
|
5752
|
+
blocks.push(fn_text);
|
|
5753
|
+
local_cursor = block_start + fn_text.length;
|
|
5754
|
+
is_first_block = false;
|
|
5755
|
+
} else if (!is_first_block) {
|
|
5756
|
+
local_cursor -= 2;
|
|
5594
5757
|
}
|
|
5595
|
-
}
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
}
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
function walk(container) {
|
|
5612
|
-
for (const item of iter_block_items(container)) {
|
|
5613
|
-
const i_type = item.constructor.name;
|
|
5614
|
-
if (i_type === "FootnoteItem") {
|
|
5615
|
-
walk(item);
|
|
5616
|
-
} else if (item instanceof Paragraph) {
|
|
5617
|
-
paragraphs_and_tables.push(["p", item]);
|
|
5618
|
-
} else if (item instanceof Table) {
|
|
5619
|
-
paragraphs_and_tables.push(["t", item]);
|
|
5620
|
-
for (const row of item.rows) {
|
|
5621
|
-
for (const cell of row.cells) {
|
|
5622
|
-
if (seen_cells.has(cell._element)) {
|
|
5623
|
-
continue;
|
|
5624
|
-
}
|
|
5625
|
-
seen_cells.add(cell._element);
|
|
5626
|
-
walk(cell);
|
|
5627
|
-
}
|
|
5628
|
-
}
|
|
5758
|
+
} else if (item instanceof Paragraph) {
|
|
5759
|
+
let prefix = get_paragraph_prefix(item, style_cache, default_pstyle);
|
|
5760
|
+
if (is_first_para && container.constructor.name === "FootnoteItem") {
|
|
5761
|
+
prefix = `[^${container.note_type}-${container.id}]: ` + prefix;
|
|
5762
|
+
}
|
|
5763
|
+
const p_text = build_paragraph_text(
|
|
5764
|
+
item,
|
|
5765
|
+
comments_map,
|
|
5766
|
+
cleanView,
|
|
5767
|
+
style_cache,
|
|
5768
|
+
default_pstyle
|
|
5769
|
+
);
|
|
5770
|
+
const full_block = prefix + p_text;
|
|
5771
|
+
blocks.push(full_block);
|
|
5772
|
+
if (paragraph_offsets) {
|
|
5773
|
+
paragraph_offsets.set(item._element, [block_start, full_block.length]);
|
|
5629
5774
|
}
|
|
5775
|
+
local_cursor = block_start + full_block.length;
|
|
5776
|
+
is_first_para = false;
|
|
5777
|
+
is_first_block = false;
|
|
5778
|
+
} else if (item instanceof Table) {
|
|
5779
|
+
const table_text = extract_table(
|
|
5780
|
+
item,
|
|
5781
|
+
comments_map,
|
|
5782
|
+
cleanView,
|
|
5783
|
+
block_start,
|
|
5784
|
+
paragraph_offsets
|
|
5785
|
+
);
|
|
5786
|
+
if (table_text) {
|
|
5787
|
+
blocks.push(table_text);
|
|
5788
|
+
local_cursor = block_start + table_text.length;
|
|
5789
|
+
is_first_block = false;
|
|
5790
|
+
} else if (!is_first_block) {
|
|
5791
|
+
local_cursor -= 2;
|
|
5792
|
+
}
|
|
5793
|
+
is_first_para = false;
|
|
5630
5794
|
}
|
|
5631
5795
|
}
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5796
|
+
return blocks.join("\n\n");
|
|
5797
|
+
}
|
|
5798
|
+
function extract_table(table, comments_map, cleanView, cursor, paragraph_offsets) {
|
|
5799
|
+
const rows_text = [];
|
|
5800
|
+
let rows_processed = 0;
|
|
5801
|
+
let local_cursor = cursor;
|
|
5802
|
+
for (const row of table.rows) {
|
|
5803
|
+
const cell_texts = [];
|
|
5804
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
5805
|
+
const trPr = findChild(row._element, "w:trPr");
|
|
5806
|
+
const ins = trPr ? findChild(trPr, "w:ins") : null;
|
|
5807
|
+
const del_node = trPr ? findChild(trPr, "w:del") : null;
|
|
5808
|
+
if (cleanView && del_node) continue;
|
|
5809
|
+
const row_start = local_cursor + (rows_processed > 0 ? 1 : 0);
|
|
5810
|
+
const wrapper_prefix_len = !cleanView && ins ? 4 : !cleanView && del_node ? 4 : 0;
|
|
5811
|
+
let cell_cursor = row_start + wrapper_prefix_len;
|
|
5812
|
+
let first_cell = true;
|
|
5813
|
+
for (const cell of row.cells) {
|
|
5814
|
+
if (seen_cells.has(cell)) continue;
|
|
5815
|
+
seen_cells.add(cell);
|
|
5816
|
+
if (!first_cell) cell_cursor += 3;
|
|
5817
|
+
const cell_content = _extract_blocks(
|
|
5818
|
+
cell,
|
|
5819
|
+
comments_map,
|
|
5820
|
+
cleanView,
|
|
5821
|
+
cell_cursor,
|
|
5822
|
+
paragraph_offsets
|
|
5823
|
+
);
|
|
5824
|
+
cell_texts.push(cell_content);
|
|
5825
|
+
cell_cursor += cell_content.length;
|
|
5826
|
+
first_cell = false;
|
|
5642
5827
|
}
|
|
5643
|
-
|
|
5644
|
-
|
|
5828
|
+
let row_str = cell_texts.join(" | ");
|
|
5829
|
+
if (!cleanView) {
|
|
5830
|
+
if (ins) row_str = `{++ ${row_str} |Chg:${ins.getAttribute("w:id")}++}`;
|
|
5831
|
+
else if (del_node)
|
|
5832
|
+
row_str = `{-- ${row_str} |Chg:${del_node.getAttribute("w:id")}--}`;
|
|
5645
5833
|
}
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5834
|
+
rows_text.push(row_str);
|
|
5835
|
+
local_cursor = row_start + row_str.length;
|
|
5836
|
+
rows_processed++;
|
|
5649
5837
|
}
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5838
|
+
return rows_text.join("\n");
|
|
5839
|
+
}
|
|
5840
|
+
function build_paragraph_text(paragraph, comments_map, cleanView, style_cache, default_pstyle) {
|
|
5841
|
+
const parts = [];
|
|
5842
|
+
const active_ins = {};
|
|
5843
|
+
const active_del = {};
|
|
5844
|
+
const active_comments = /* @__PURE__ */ new Set();
|
|
5845
|
+
const active_fmt = {};
|
|
5846
|
+
const deferred_meta_states = [];
|
|
5847
|
+
let pending_text = "";
|
|
5848
|
+
let current_wrappers = ["", ""];
|
|
5849
|
+
let current_style = ["", ""];
|
|
5850
|
+
const items = Array.from(iter_paragraph_content(paragraph));
|
|
5851
|
+
const is_heading = is_heading_paragraph(
|
|
5852
|
+
paragraph,
|
|
5853
|
+
style_cache,
|
|
5854
|
+
default_pstyle
|
|
5855
|
+
);
|
|
5856
|
+
const native_heading = is_native_heading(
|
|
5857
|
+
paragraph,
|
|
5858
|
+
style_cache,
|
|
5859
|
+
default_pstyle
|
|
5860
|
+
);
|
|
5861
|
+
let leading_strip_active = is_heading;
|
|
5862
|
+
for (let i = 0; i < items.length; i++) {
|
|
5863
|
+
const item = items[i];
|
|
5864
|
+
if (item instanceof Run) {
|
|
5865
|
+
const [prefix, suffix] = get_run_style_markers(item, native_heading);
|
|
5866
|
+
const text = get_run_text(item);
|
|
5867
|
+
if (cleanView && Object.keys(active_del).length > 0) continue;
|
|
5868
|
+
if (leading_strip_active) {
|
|
5869
|
+
if (!text || !text.trim()) continue;
|
|
5870
|
+
leading_strip_active = false;
|
|
5675
5871
|
}
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5872
|
+
const seg = apply_formatting_to_segments(text, prefix, suffix);
|
|
5873
|
+
if (seg) {
|
|
5874
|
+
const new_wrappers = cleanView ? ["", ""] : _get_wrappers(active_ins, active_del, active_comments, active_fmt);
|
|
5875
|
+
const new_style = [prefix, suffix];
|
|
5876
|
+
if (pending_text && new_wrappers[0] === current_wrappers[0] && new_wrappers[1] === current_wrappers[1]) {
|
|
5877
|
+
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])) {
|
|
5878
|
+
pending_text = pending_text.slice(0, -current_style[1].length) + seg.slice(new_style[0].length);
|
|
5879
|
+
} else {
|
|
5880
|
+
pending_text += seg;
|
|
5881
|
+
}
|
|
5882
|
+
current_style = new_style;
|
|
5883
|
+
} else {
|
|
5884
|
+
if (pending_text)
|
|
5885
|
+
parts.push(
|
|
5886
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
5887
|
+
);
|
|
5888
|
+
pending_text = seg;
|
|
5889
|
+
current_wrappers = new_wrappers;
|
|
5890
|
+
current_style = new_style;
|
|
5891
|
+
}
|
|
5892
|
+
if (!cleanView) {
|
|
5893
|
+
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;
|
|
5894
|
+
if (has_meta) {
|
|
5895
|
+
deferred_meta_states.push([
|
|
5896
|
+
{ ...active_ins },
|
|
5897
|
+
{ ...active_del },
|
|
5898
|
+
new Set(active_comments),
|
|
5899
|
+
{ ...active_fmt }
|
|
5900
|
+
]);
|
|
5901
|
+
}
|
|
5902
|
+
let should_defer = false;
|
|
5903
|
+
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;
|
|
5904
|
+
if (has_any_meta) {
|
|
5905
|
+
let j = i + 1;
|
|
5906
|
+
let next_has_meta = false;
|
|
5907
|
+
let temp_ins = Object.keys(active_ins).length;
|
|
5908
|
+
let temp_del = Object.keys(active_del).length;
|
|
5909
|
+
let temp_fmt = Object.keys(active_fmt).length;
|
|
5910
|
+
const temp_comments = new Set(active_comments);
|
|
5911
|
+
while (j < items.length) {
|
|
5912
|
+
const next_item = items[j];
|
|
5913
|
+
if (next_item instanceof Run) {
|
|
5914
|
+
if (!get_run_text(next_item)) {
|
|
5915
|
+
j++;
|
|
5916
|
+
continue;
|
|
5917
|
+
}
|
|
5918
|
+
if (temp_ins > 0 || temp_del > 0 || temp_fmt > 0 || temp_comments.size > 0)
|
|
5919
|
+
next_has_meta = true;
|
|
5920
|
+
break;
|
|
5921
|
+
} else {
|
|
5922
|
+
const ev = next_item;
|
|
5923
|
+
if (ev.type === "ins_start") temp_ins++;
|
|
5924
|
+
else if (ev.type === "ins_end")
|
|
5925
|
+
temp_ins = Math.max(0, temp_ins - 1);
|
|
5926
|
+
else if (ev.type === "del_start") temp_del++;
|
|
5927
|
+
else if (ev.type === "del_end")
|
|
5928
|
+
temp_del = Math.max(0, temp_del - 1);
|
|
5929
|
+
else if (ev.type === "fmt_start") temp_fmt++;
|
|
5930
|
+
else if (ev.type === "fmt_end")
|
|
5931
|
+
temp_fmt = Math.max(0, temp_fmt - 1);
|
|
5932
|
+
else if (ev.type === "start") temp_comments.add(ev.id);
|
|
5933
|
+
else if (ev.type === "end") temp_comments.delete(ev.id);
|
|
5934
|
+
}
|
|
5935
|
+
j++;
|
|
5936
|
+
}
|
|
5937
|
+
if (next_has_meta) should_defer = true;
|
|
5938
|
+
}
|
|
5939
|
+
if (!should_defer && deferred_meta_states.length > 0) {
|
|
5940
|
+
const meta_block = _build_merged_meta_block(
|
|
5941
|
+
deferred_meta_states,
|
|
5942
|
+
comments_map
|
|
5943
|
+
);
|
|
5944
|
+
if (meta_block) {
|
|
5945
|
+
if (pending_text) {
|
|
5946
|
+
parts.push(
|
|
5947
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
5948
|
+
);
|
|
5949
|
+
pending_text = "";
|
|
5950
|
+
current_wrappers = ["", ""];
|
|
5951
|
+
current_style = ["", ""];
|
|
5952
|
+
}
|
|
5953
|
+
parts.push(`{>>${meta_block}<<}`);
|
|
5954
|
+
}
|
|
5955
|
+
deferred_meta_states.length = 0;
|
|
5956
|
+
}
|
|
5957
|
+
}
|
|
5679
5958
|
}
|
|
5680
|
-
}
|
|
5681
|
-
const footnote_ids = _collect_footnote_ids_fast(owned);
|
|
5682
|
-
let para_offset;
|
|
5683
|
-
if (paragraph_offsets instanceof Map) {
|
|
5684
|
-
para_offset = paragraph_offsets.get(paragraph._element);
|
|
5685
5959
|
} else {
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
}
|
|
5704
|
-
function _heading_passes_quality_filter_fast(paragraph, projected_body, paragraph_offsets) {
|
|
5705
|
-
const style = _determine_heading_style(paragraph);
|
|
5706
|
-
if (style !== "(heuristic)") return true;
|
|
5707
|
-
const text = _heading_text_fast(paragraph, projected_body, paragraph_offsets);
|
|
5708
|
-
if (!text) return false;
|
|
5709
|
-
const words = text.match(/\w+/g) || [];
|
|
5710
|
-
return words.length >= _HEURISTIC_MIN_WORDS;
|
|
5711
|
-
}
|
|
5712
|
-
function _heading_text_fast(paragraph, projected_body, paragraph_offsets) {
|
|
5713
|
-
let offset;
|
|
5714
|
-
if (paragraph_offsets instanceof Map) {
|
|
5715
|
-
offset = paragraph_offsets.get(paragraph._element);
|
|
5716
|
-
} else {
|
|
5717
|
-
offset = paragraph_offsets[paragraph._element];
|
|
5718
|
-
}
|
|
5719
|
-
if (offset === void 0) {
|
|
5720
|
-
return "";
|
|
5721
|
-
}
|
|
5722
|
-
const [start, length] = offset;
|
|
5723
|
-
const raw = projected_body.substring(start, start + length);
|
|
5724
|
-
let cleaned = _strip_critic_markup(raw);
|
|
5725
|
-
cleaned = _strip_inline_formatting(cleaned);
|
|
5726
|
-
cleaned = cleaned.replace(/^#+\s+/, "");
|
|
5727
|
-
return cleaned.trim();
|
|
5728
|
-
}
|
|
5729
|
-
function _collect_footnote_ids_fast(owned_items) {
|
|
5730
|
-
const seen = /* @__PURE__ */ new Set();
|
|
5731
|
-
const ordered = [];
|
|
5732
|
-
for (const [kind, item] of owned_items) {
|
|
5733
|
-
if (kind !== "p") continue;
|
|
5734
|
-
for (const event of iter_paragraph_content(item)) {
|
|
5735
|
-
if (!("type" in event)) continue;
|
|
5736
|
-
let fn_id = "";
|
|
5737
|
-
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
5738
|
-
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
5739
|
-
else continue;
|
|
5740
|
-
if (!seen.has(fn_id)) {
|
|
5741
|
-
seen.add(fn_id);
|
|
5742
|
-
ordered.push(fn_id);
|
|
5960
|
+
const ev = item;
|
|
5961
|
+
leading_strip_active = false;
|
|
5962
|
+
if (![
|
|
5963
|
+
"ins_start",
|
|
5964
|
+
"ins_end",
|
|
5965
|
+
"del_start",
|
|
5966
|
+
"del_end",
|
|
5967
|
+
"fmt_start",
|
|
5968
|
+
"fmt_end"
|
|
5969
|
+
].includes(ev.type)) {
|
|
5970
|
+
if (pending_text) {
|
|
5971
|
+
parts.push(
|
|
5972
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
5973
|
+
);
|
|
5974
|
+
pending_text = "";
|
|
5975
|
+
current_wrappers = ["", ""];
|
|
5976
|
+
current_style = ["", ""];
|
|
5977
|
+
}
|
|
5743
5978
|
}
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
removed_comment_lines = [];
|
|
5761
|
-
kept_comment_lines = [];
|
|
5762
|
-
metadata_lines = [];
|
|
5763
|
-
structural_lines = [];
|
|
5764
|
-
warnings = [];
|
|
5765
|
-
status = "clean";
|
|
5766
|
-
blocked_reason = null;
|
|
5767
|
-
constructor(filename, mode = "full", author = null) {
|
|
5768
|
-
this.filename = filename;
|
|
5769
|
-
this.mode = mode;
|
|
5770
|
-
this.author = author;
|
|
5771
|
-
}
|
|
5772
|
-
add_transform_lines(lines) {
|
|
5773
|
-
for (const line of lines) {
|
|
5774
|
-
const lower = line.toLowerCase();
|
|
5775
|
-
if (lower.includes("tracked change") || lower.includes("insertion") || lower.includes("deletion") || lower.includes("accepted")) {
|
|
5776
|
-
this.change_lines.push(line);
|
|
5777
|
-
} else if (lower.includes("comment") || lower.includes("[open]") || lower.includes("[resolved]")) {
|
|
5778
|
-
if (lower.includes("kept") || lower.includes("visible")) {
|
|
5779
|
-
this.kept_comment_lines.push(line);
|
|
5780
|
-
} else {
|
|
5781
|
-
this.removed_comment_lines.push(line);
|
|
5979
|
+
if (ev.type === "start") active_comments.add(ev.id);
|
|
5980
|
+
else if (ev.type === "end") active_comments.delete(ev.id);
|
|
5981
|
+
else if (ev.type === "ins_start") active_ins[ev.id] = ev;
|
|
5982
|
+
else if (ev.type === "ins_end") delete active_ins[ev.id];
|
|
5983
|
+
else if (ev.type === "del_start") active_del[ev.id] = ev;
|
|
5984
|
+
else if (ev.type === "del_end") delete active_del[ev.id];
|
|
5985
|
+
else if (ev.type === "fmt_start") active_fmt[ev.id] = ev;
|
|
5986
|
+
else if (ev.type === "fmt_end") delete active_fmt[ev.id];
|
|
5987
|
+
else if (ev.type === "footnote" || ev.type === "endnote") {
|
|
5988
|
+
if (pending_text) {
|
|
5989
|
+
parts.push(
|
|
5990
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
5991
|
+
);
|
|
5992
|
+
pending_text = "";
|
|
5993
|
+
current_wrappers = ["", ""];
|
|
5994
|
+
current_style = ["", ""];
|
|
5782
5995
|
}
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5996
|
+
parts.push(`[^${ev.type === "footnote" ? "fn" : "en"}-${ev.id}]`);
|
|
5997
|
+
} else if (ev.type === "hyperlink_start") {
|
|
5998
|
+
if (pending_text) {
|
|
5999
|
+
parts.push(
|
|
6000
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6001
|
+
);
|
|
6002
|
+
pending_text = "";
|
|
6003
|
+
current_wrappers = ["", ""];
|
|
6004
|
+
current_style = ["", ""];
|
|
6005
|
+
}
|
|
6006
|
+
parts.push("[");
|
|
6007
|
+
} else if (ev.type === "hyperlink_end") {
|
|
6008
|
+
if (pending_text) {
|
|
6009
|
+
parts.push(
|
|
6010
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6011
|
+
);
|
|
6012
|
+
pending_text = "";
|
|
6013
|
+
current_wrappers = ["", ""];
|
|
6014
|
+
current_style = ["", ""];
|
|
6015
|
+
}
|
|
6016
|
+
parts.push(`](${ev.date})`);
|
|
6017
|
+
} else if (ev.type === "xref_start") {
|
|
6018
|
+
if (pending_text) {
|
|
6019
|
+
parts.push(
|
|
6020
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6021
|
+
);
|
|
6022
|
+
pending_text = "";
|
|
6023
|
+
current_wrappers = ["", ""];
|
|
6024
|
+
current_style = ["", ""];
|
|
6025
|
+
}
|
|
6026
|
+
parts.push("[~");
|
|
6027
|
+
} else if (ev.type === "xref_end") {
|
|
6028
|
+
if (pending_text) {
|
|
6029
|
+
parts.push(
|
|
6030
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6031
|
+
);
|
|
6032
|
+
pending_text = "";
|
|
6033
|
+
current_wrappers = ["", ""];
|
|
6034
|
+
current_style = ["", ""];
|
|
6035
|
+
}
|
|
6036
|
+
parts.push(`~](#${ev.id})`);
|
|
6037
|
+
} else if (ev.type === "bookmark") {
|
|
6038
|
+
if (pending_text) {
|
|
6039
|
+
parts.push(
|
|
6040
|
+
`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`
|
|
6041
|
+
);
|
|
6042
|
+
pending_text = "";
|
|
6043
|
+
current_wrappers = ["", ""];
|
|
6044
|
+
current_style = ["", ""];
|
|
6045
|
+
}
|
|
6046
|
+
parts.push(`{#${ev.id}}`);
|
|
5789
6047
|
}
|
|
5790
6048
|
}
|
|
5791
6049
|
}
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
const
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
if (
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
6050
|
+
if (pending_text)
|
|
6051
|
+
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
6052
|
+
if (deferred_meta_states.length > 0) {
|
|
6053
|
+
const meta_block = _build_merged_meta_block(
|
|
6054
|
+
deferred_meta_states,
|
|
6055
|
+
comments_map
|
|
6056
|
+
);
|
|
6057
|
+
if (meta_block) parts.push(`{>>${meta_block}<<}`);
|
|
6058
|
+
}
|
|
6059
|
+
return parts.join("");
|
|
6060
|
+
}
|
|
6061
|
+
function _get_wrappers(ins, del, comments, fmt) {
|
|
6062
|
+
if (Object.keys(del).length > 0) return ["{--", "--}"];
|
|
6063
|
+
if (Object.keys(ins).length > 0) return ["{++", "++}"];
|
|
6064
|
+
if (comments.size > 0 || Object.keys(fmt).length > 0) return ["{==", "==}"];
|
|
6065
|
+
return ["", ""];
|
|
6066
|
+
}
|
|
6067
|
+
function _build_merged_meta_block(states_list, comments_map) {
|
|
6068
|
+
const change_lines = [];
|
|
6069
|
+
const comment_lines = [];
|
|
6070
|
+
const seen_sigs = /* @__PURE__ */ new Set();
|
|
6071
|
+
for (const [ins_map, del_map, comments_set, fmt_map] of states_list) {
|
|
6072
|
+
let render_comment2 = function(cid) {
|
|
6073
|
+
if (!comments_map[cid]) return;
|
|
6074
|
+
const sig = `Com:${cid}`;
|
|
6075
|
+
if (seen_sigs.has(sig)) return;
|
|
6076
|
+
const data = comments_map[cid];
|
|
6077
|
+
let header = `[${sig}] ${data.author}`;
|
|
6078
|
+
if (data.date) header += ` @ ${data.date}`;
|
|
6079
|
+
if (data.resolved) header += `(RESOLVED)`;
|
|
6080
|
+
comment_lines.push(`${header}: ${data.text}`);
|
|
6081
|
+
seen_sigs.add(sig);
|
|
6082
|
+
if (children_map[cid]) {
|
|
6083
|
+
const children = children_map[cid].sort(
|
|
6084
|
+
(a, b) => (comments_map[a]?.date || "").localeCompare(
|
|
6085
|
+
comments_map[b]?.date || ""
|
|
6086
|
+
)
|
|
6087
|
+
);
|
|
6088
|
+
for (const child_id of children) {
|
|
6089
|
+
render_comment2(child_id);
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
6092
|
+
};
|
|
6093
|
+
var render_comment = render_comment2;
|
|
6094
|
+
for (const [uid, meta] of Object.entries(
|
|
6095
|
+
ins_map
|
|
6096
|
+
)) {
|
|
6097
|
+
const sig = `Chg:${uid}`;
|
|
6098
|
+
if (!seen_sigs.has(sig)) {
|
|
6099
|
+
change_lines.push(`[${sig} insert] ${meta.author || "Unknown"}`);
|
|
6100
|
+
seen_sigs.add(sig);
|
|
5814
6101
|
}
|
|
5815
|
-
if (this.author) lines.push(` Author on all markup: "${this.author}"`);
|
|
5816
|
-
}
|
|
5817
|
-
if (this.change_lines.length > 0) {
|
|
5818
|
-
lines.push("");
|
|
5819
|
-
lines.push("TRACKED CHANGES");
|
|
5820
|
-
for (const cl of this.change_lines) lines.push(` ${cl}`);
|
|
5821
|
-
}
|
|
5822
|
-
if (this.removed_comment_lines.length > 0) {
|
|
5823
|
-
lines.push("");
|
|
5824
|
-
lines.push("COMMENTS (stripped)");
|
|
5825
|
-
for (const cl of this.removed_comment_lines) lines.push(` ${cl}`);
|
|
5826
6102
|
}
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
6103
|
+
for (const [uid, meta] of Object.entries(
|
|
6104
|
+
del_map
|
|
6105
|
+
)) {
|
|
6106
|
+
const sig = `Chg:${uid}`;
|
|
6107
|
+
if (!seen_sigs.has(sig)) {
|
|
6108
|
+
change_lines.push(`[${sig} delete] ${meta.author || "Unknown"}`);
|
|
6109
|
+
seen_sigs.add(sig);
|
|
6110
|
+
}
|
|
5831
6111
|
}
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
6112
|
+
for (const [uid, meta] of Object.entries(
|
|
6113
|
+
fmt_map
|
|
6114
|
+
)) {
|
|
6115
|
+
const sig = `Chg:${uid}`;
|
|
6116
|
+
if (!seen_sigs.has(sig)) {
|
|
6117
|
+
change_lines.push(`[${sig} format] ${meta.author || "Unknown"}`);
|
|
6118
|
+
seen_sigs.add(sig);
|
|
6119
|
+
}
|
|
5836
6120
|
}
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
6121
|
+
const children_map = {};
|
|
6122
|
+
for (const [c_id, data] of Object.entries(
|
|
6123
|
+
comments_map
|
|
6124
|
+
)) {
|
|
6125
|
+
const p_id = data.parent_id;
|
|
6126
|
+
if (p_id) {
|
|
6127
|
+
if (!children_map[p_id]) children_map[p_id] = [];
|
|
6128
|
+
children_map[p_id].push(c_id);
|
|
6129
|
+
}
|
|
5841
6130
|
}
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
lines.push(`Result: CLEAN WITH WARNINGS (${this.warnings.length} warning${this.warnings.length > 1 ? "s" : ""})`);
|
|
5846
|
-
} else {
|
|
5847
|
-
lines.push(`Result: CLEAN (${this.tracked_changes_found} changes resolved, ${this.comments_removed} comments removed)`);
|
|
6131
|
+
const sorted_ids = Array.from(comments_set).sort();
|
|
6132
|
+
for (const c_id of sorted_ids) {
|
|
6133
|
+
render_comment2(c_id);
|
|
5848
6134
|
}
|
|
5849
|
-
lines.push(sep);
|
|
5850
|
-
return lines.join("\n");
|
|
5851
6135
|
}
|
|
5852
|
-
|
|
6136
|
+
return [...change_lines, ...comment_lines].join("\n");
|
|
6137
|
+
}
|
|
5853
6138
|
|
|
5854
|
-
// src/
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
if (tag === localName || tag.endsWith(":" + localName)) {
|
|
5861
|
-
result.push(all[i]);
|
|
5862
|
-
}
|
|
6139
|
+
// src/outline.ts
|
|
6140
|
+
var _HEADING_PREFIX_RE = /^(#{1,6}) /;
|
|
6141
|
+
var _HEURISTIC_MIN_WORDS = 3;
|
|
6142
|
+
function extract_outline(doc, projected_body, body_pages, body_page_offsets, paragraph_offsets = null) {
|
|
6143
|
+
if (body_pages.length !== body_page_offsets.length) {
|
|
6144
|
+
throw new Error("body_pages and body_page_offsets length mismatch");
|
|
5863
6145
|
}
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
function coalesce_runs(doc) {
|
|
5867
|
-
let count = 0;
|
|
5868
|
-
function areRunsIdentical(rPr1, rPr2) {
|
|
5869
|
-
const xml1 = rPr1 ? rPr1.toString() : "";
|
|
5870
|
-
const xml2 = rPr2 ? rPr2.toString() : "";
|
|
5871
|
-
return xml1 === xml2;
|
|
6146
|
+
if (paragraph_offsets) {
|
|
6147
|
+
return _extract_outline_fast(doc, projected_body, body_page_offsets, paragraph_offsets);
|
|
5872
6148
|
}
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
}
|
|
5882
|
-
return false;
|
|
6149
|
+
const comments_map = extract_comments_data(doc.pkg);
|
|
6150
|
+
const block_records = _walk_doc_body(doc, comments_map);
|
|
6151
|
+
const heading_indices = [];
|
|
6152
|
+
for (let idx = 0; idx < block_records.length; idx++) {
|
|
6153
|
+
const rec = block_records[idx];
|
|
6154
|
+
if (!(rec.is_paragraph && _is_heading(rec.item))) continue;
|
|
6155
|
+
if (!_heading_passes_quality_filter(rec.item, comments_map)) continue;
|
|
6156
|
+
heading_indices.push(idx);
|
|
5883
6157
|
}
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
continue;
|
|
5924
|
-
}
|
|
5925
|
-
}
|
|
5926
|
-
}
|
|
5927
|
-
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(curr.tagName)) {
|
|
5928
|
-
coalesceContainer(curr);
|
|
5929
|
-
}
|
|
5930
|
-
i++;
|
|
6158
|
+
if (heading_indices.length === 0) return [];
|
|
6159
|
+
const nodes = [];
|
|
6160
|
+
for (let h_pos = 0; h_pos < heading_indices.length; h_pos++) {
|
|
6161
|
+
const rec_idx = heading_indices[h_pos];
|
|
6162
|
+
const rec = block_records[rec_idx];
|
|
6163
|
+
const paragraph = rec.item;
|
|
6164
|
+
const level = _heading_level(paragraph);
|
|
6165
|
+
const text = _heading_text(paragraph, comments_map);
|
|
6166
|
+
const style = _determine_heading_style(paragraph);
|
|
6167
|
+
const owned_end = _find_owned_end(
|
|
6168
|
+
block_records,
|
|
6169
|
+
heading_indices,
|
|
6170
|
+
h_pos,
|
|
6171
|
+
level
|
|
6172
|
+
);
|
|
6173
|
+
const owned_blocks = block_records.slice(rec_idx + 1, owned_end);
|
|
6174
|
+
const has_table = _direct_has_table(block_records, rec_idx + 1, owned_end);
|
|
6175
|
+
const footnote_ids = _collect_footnote_ids(owned_blocks);
|
|
6176
|
+
const page_num = _offset_to_page(rec.start_offset, body_page_offsets);
|
|
6177
|
+
nodes.push({ level, text, page: page_num, style, has_table, footnote_ids });
|
|
6178
|
+
}
|
|
6179
|
+
return nodes;
|
|
6180
|
+
}
|
|
6181
|
+
function _direct_has_table(block_records, range_start, range_end) {
|
|
6182
|
+
for (let idx = range_start; idx < range_end; idx++) {
|
|
6183
|
+
const rec = block_records[idx];
|
|
6184
|
+
if (rec.is_paragraph && _is_heading(rec.item)) return false;
|
|
6185
|
+
if (rec.is_table) return true;
|
|
6186
|
+
}
|
|
6187
|
+
return false;
|
|
6188
|
+
}
|
|
6189
|
+
function _walk_doc_body(doc, comments_map) {
|
|
6190
|
+
const parts = Array.from(iter_document_parts(doc));
|
|
6191
|
+
let body_start_offset = 0;
|
|
6192
|
+
let body_part = null;
|
|
6193
|
+
for (const part of parts) {
|
|
6194
|
+
if (part === doc) {
|
|
6195
|
+
body_part = part;
|
|
6196
|
+
break;
|
|
5931
6197
|
}
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
if (
|
|
5935
|
-
|
|
5936
|
-
}
|
|
6198
|
+
const part_text = _project_part(part, comments_map);
|
|
6199
|
+
if (part_text) {
|
|
6200
|
+
if (body_start_offset > 0) body_start_offset += 2;
|
|
6201
|
+
body_start_offset += part_text.length;
|
|
5937
6202
|
}
|
|
5938
6203
|
}
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
}
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
const
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
6204
|
+
if (!body_part) {
|
|
6205
|
+
body_part = doc;
|
|
6206
|
+
body_start_offset = 0;
|
|
6207
|
+
} else {
|
|
6208
|
+
if (body_start_offset > 0) body_start_offset += 2;
|
|
6209
|
+
}
|
|
6210
|
+
const records = [];
|
|
6211
|
+
let cursor = body_start_offset;
|
|
6212
|
+
let is_first_block = true;
|
|
6213
|
+
for (const item of iter_block_items(body_part)) {
|
|
6214
|
+
if (item instanceof Paragraph) {
|
|
6215
|
+
const prefix = get_paragraph_prefix(item);
|
|
6216
|
+
const p_text = build_paragraph_text(item, comments_map, false);
|
|
6217
|
+
const block_len = (prefix + p_text).length;
|
|
6218
|
+
if (!is_first_block) cursor += 2;
|
|
6219
|
+
records.push({
|
|
6220
|
+
item,
|
|
6221
|
+
is_paragraph: true,
|
|
6222
|
+
is_table: false,
|
|
6223
|
+
start_offset: cursor,
|
|
6224
|
+
projected_length: block_len
|
|
6225
|
+
});
|
|
6226
|
+
cursor += block_len;
|
|
6227
|
+
is_first_block = false;
|
|
6228
|
+
} else if (item instanceof Table) {
|
|
6229
|
+
const table_text = extract_table(item, comments_map, false, 0);
|
|
6230
|
+
const block_len = table_text ? table_text.length : 0;
|
|
6231
|
+
if (!is_first_block) cursor += 2;
|
|
6232
|
+
const table_start = cursor;
|
|
6233
|
+
records.push({
|
|
6234
|
+
item,
|
|
6235
|
+
is_paragraph: false,
|
|
6236
|
+
is_table: true,
|
|
6237
|
+
start_offset: table_start,
|
|
6238
|
+
projected_length: block_len
|
|
6239
|
+
});
|
|
6240
|
+
_record_table_inner_blocks_lite(item, table_start, records, comments_map);
|
|
6241
|
+
cursor += block_len;
|
|
6242
|
+
is_first_block = false;
|
|
5953
6243
|
}
|
|
5954
6244
|
}
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
6245
|
+
return records;
|
|
6246
|
+
}
|
|
6247
|
+
function _compute_inner_block_offset(table, target_paragraph, table_start_offset, comments_map) {
|
|
6248
|
+
const target_el = target_paragraph._element;
|
|
6249
|
+
let cursor = table_start_offset;
|
|
6250
|
+
let rows_processed = 0;
|
|
6251
|
+
for (const row of table.rows) {
|
|
6252
|
+
if (rows_processed > 0) cursor += 1;
|
|
6253
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
6254
|
+
let cells_in_row = 0;
|
|
6255
|
+
for (const cell of row.cells) {
|
|
6256
|
+
if (seen_cells.has(cell)) continue;
|
|
6257
|
+
seen_cells.add(cell);
|
|
6258
|
+
if (cells_in_row > 0) cursor += 3;
|
|
6259
|
+
const [new_cursor, found] = _walk_cell_for_offset(
|
|
6260
|
+
cell,
|
|
6261
|
+
target_el,
|
|
6262
|
+
cursor,
|
|
6263
|
+
comments_map
|
|
6264
|
+
);
|
|
6265
|
+
if (found) return new_cursor;
|
|
6266
|
+
cursor = new_cursor;
|
|
6267
|
+
cells_in_row++;
|
|
5960
6268
|
}
|
|
6269
|
+
rows_processed++;
|
|
5961
6270
|
}
|
|
5962
|
-
return
|
|
6271
|
+
return table_start_offset;
|
|
5963
6272
|
}
|
|
5964
|
-
function
|
|
5965
|
-
let
|
|
5966
|
-
|
|
5967
|
-
const
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
if (
|
|
5971
|
-
|
|
5972
|
-
|
|
6273
|
+
function _walk_cell_for_offset(cell, target_el, cell_start_cursor, comments_map) {
|
|
6274
|
+
let cursor = cell_start_cursor;
|
|
6275
|
+
let is_first_block = true;
|
|
6276
|
+
for (const inner_item of iter_block_items(cell)) {
|
|
6277
|
+
if (!is_first_block) cursor += 2;
|
|
6278
|
+
if (inner_item instanceof Paragraph) {
|
|
6279
|
+
if (inner_item._element === target_el) return [cursor, true];
|
|
6280
|
+
const prefix = get_paragraph_prefix(inner_item);
|
|
6281
|
+
const p_text = build_paragraph_text(inner_item, comments_map, false);
|
|
6282
|
+
cursor += (prefix + p_text).length;
|
|
6283
|
+
} else if (inner_item instanceof Table) {
|
|
6284
|
+
const nested_offset = _compute_inner_block_offset(
|
|
6285
|
+
inner_item,
|
|
6286
|
+
new Paragraph(target_el, null),
|
|
6287
|
+
cursor,
|
|
6288
|
+
comments_map
|
|
6289
|
+
);
|
|
6290
|
+
if (nested_offset !== cursor) {
|
|
6291
|
+
if (_element_is_descendant(target_el, inner_item._element))
|
|
6292
|
+
return [nested_offset, true];
|
|
5973
6293
|
}
|
|
6294
|
+
const table_text = extract_table(inner_item, comments_map, false, 0);
|
|
6295
|
+
cursor += table_text ? table_text.length : 0;
|
|
5974
6296
|
}
|
|
6297
|
+
is_first_block = false;
|
|
5975
6298
|
}
|
|
5976
|
-
return
|
|
6299
|
+
return [cursor, false];
|
|
5977
6300
|
}
|
|
5978
|
-
function
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
6301
|
+
function _element_is_descendant(target_el, ancestor_el) {
|
|
6302
|
+
let cur = target_el.parentNode;
|
|
6303
|
+
while (cur) {
|
|
6304
|
+
if (cur === ancestor_el) return true;
|
|
6305
|
+
cur = cur.parentNode;
|
|
6306
|
+
}
|
|
6307
|
+
return false;
|
|
5982
6308
|
}
|
|
5983
|
-
function
|
|
5984
|
-
|
|
5985
|
-
for (const
|
|
5986
|
-
const
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
6309
|
+
function _record_table_inner_blocks_lite(table, inherited_offset, records, comments_map) {
|
|
6310
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
6311
|
+
for (const row of table.rows) {
|
|
6312
|
+
for (const cell of row.cells) {
|
|
6313
|
+
if (seen_cells.has(cell)) continue;
|
|
6314
|
+
seen_cells.add(cell);
|
|
6315
|
+
for (const inner_item of iter_block_items(cell)) {
|
|
6316
|
+
if (inner_item instanceof Paragraph) {
|
|
6317
|
+
const true_offset = _is_heading(inner_item) ? _compute_inner_block_offset(
|
|
6318
|
+
table,
|
|
6319
|
+
inner_item,
|
|
6320
|
+
inherited_offset,
|
|
6321
|
+
comments_map
|
|
6322
|
+
) : inherited_offset;
|
|
6323
|
+
records.push({
|
|
6324
|
+
item: inner_item,
|
|
6325
|
+
is_paragraph: true,
|
|
6326
|
+
is_table: false,
|
|
6327
|
+
start_offset: true_offset,
|
|
6328
|
+
projected_length: 0
|
|
6329
|
+
});
|
|
6330
|
+
} else if (inner_item instanceof Table) {
|
|
6331
|
+
records.push({
|
|
6332
|
+
item: inner_item,
|
|
6333
|
+
is_paragraph: false,
|
|
6334
|
+
is_table: true,
|
|
6335
|
+
start_offset: inherited_offset,
|
|
6336
|
+
projected_length: 0
|
|
6337
|
+
});
|
|
6338
|
+
_record_table_inner_blocks_lite(
|
|
6339
|
+
inner_item,
|
|
6340
|
+
inherited_offset,
|
|
6341
|
+
records,
|
|
6342
|
+
comments_map
|
|
6343
|
+
);
|
|
6344
|
+
}
|
|
5991
6345
|
}
|
|
5992
6346
|
}
|
|
5993
6347
|
}
|
|
5994
|
-
return count ? [`Empty property elements: ${count} removed`] : [];
|
|
5995
6348
|
}
|
|
5996
|
-
function
|
|
5997
|
-
|
|
5998
|
-
const
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6349
|
+
function _project_part(part, comments_map) {
|
|
6350
|
+
const blocks = [];
|
|
6351
|
+
const c_type = part.constructor.name;
|
|
6352
|
+
if (c_type === "NotesPart") {
|
|
6353
|
+
const header = part.note_type === "fn" ? "## Footnotes" : "## Endnotes";
|
|
6354
|
+
blocks.push(`---
|
|
6355
|
+
${header}`);
|
|
6356
|
+
}
|
|
6357
|
+
let is_first_para = true;
|
|
6358
|
+
for (const item of iter_block_items(part)) {
|
|
6359
|
+
if (item.constructor.name === "FootnoteItem") {
|
|
6360
|
+
const fn_text = _project_part(item, comments_map);
|
|
6361
|
+
if (fn_text) blocks.push(fn_text);
|
|
6362
|
+
} else if (item instanceof Paragraph) {
|
|
6363
|
+
let prefix = get_paragraph_prefix(item);
|
|
6364
|
+
if (is_first_para && c_type === "FootnoteItem")
|
|
6365
|
+
prefix = `[^${part.note_type}-${part.id}]: ${prefix}`;
|
|
6366
|
+
const p_text = build_paragraph_text(item, comments_map, false);
|
|
6367
|
+
blocks.push(prefix + p_text);
|
|
6368
|
+
is_first_para = false;
|
|
6369
|
+
} else if (item instanceof Table) {
|
|
6370
|
+
const table_text = extract_table(item, comments_map, false, 0);
|
|
6371
|
+
if (table_text) blocks.push(table_text);
|
|
6372
|
+
is_first_para = false;
|
|
6006
6373
|
}
|
|
6007
6374
|
}
|
|
6008
|
-
return
|
|
6375
|
+
return blocks.join("\n\n");
|
|
6009
6376
|
}
|
|
6010
|
-
function
|
|
6011
|
-
|
|
6012
|
-
const del = findAllDescendants(doc.element, "w:del").length;
|
|
6013
|
-
const fmt = findAllDescendants(doc.element, "w:rPrChange").length + findAllDescendants(doc.element, "w:pPrChange").length + findAllDescendants(doc.element, "w:sectPrChange").length;
|
|
6014
|
-
return [ins, del, fmt];
|
|
6377
|
+
function _is_heading(paragraph) {
|
|
6378
|
+
return _HEADING_PREFIX_RE.test(get_paragraph_prefix(paragraph));
|
|
6015
6379
|
}
|
|
6016
|
-
function
|
|
6017
|
-
const
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
}
|
|
6024
|
-
return authors;
|
|
6380
|
+
function _heading_passes_quality_filter(paragraph, comments_map) {
|
|
6381
|
+
const style = _determine_heading_style(paragraph);
|
|
6382
|
+
if (style !== "(heuristic)") return true;
|
|
6383
|
+
const text = _heading_text(paragraph, comments_map);
|
|
6384
|
+
if (!text) return false;
|
|
6385
|
+
const word_count = (text.match(/\w+/g) || []).length;
|
|
6386
|
+
return word_count >= _HEURISTIC_MIN_WORDS;
|
|
6025
6387
|
}
|
|
6026
|
-
function
|
|
6027
|
-
const
|
|
6028
|
-
|
|
6029
|
-
for (const t of ts) if (t.textContent) texts.push(t.textContent);
|
|
6030
|
-
const dts = findAllDescendants(el, "w:delText");
|
|
6031
|
-
for (const dt of dts) if (dt.textContent) texts.push(dt.textContent);
|
|
6032
|
-
return texts.join("");
|
|
6388
|
+
function _heading_level(paragraph) {
|
|
6389
|
+
const match = _HEADING_PREFIX_RE.exec(get_paragraph_prefix(paragraph));
|
|
6390
|
+
return match ? Math.min(match[1].length, 6) : 1;
|
|
6033
6391
|
}
|
|
6034
|
-
function
|
|
6035
|
-
const
|
|
6036
|
-
|
|
6037
|
-
|
|
6392
|
+
function _heading_text(paragraph, comments_map) {
|
|
6393
|
+
const p_text = build_paragraph_text(paragraph, comments_map, false);
|
|
6394
|
+
let cleaned = _strip_critic_markup(p_text);
|
|
6395
|
+
cleaned = _strip_inline_formatting(cleaned);
|
|
6396
|
+
return cleaned.trim();
|
|
6038
6397
|
}
|
|
6039
|
-
function
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
}
|
|
6047
|
-
for (const del of delEls) {
|
|
6048
|
-
const text = _getElementText(del).trim();
|
|
6049
|
-
if (text) lines.push(` Accepted deletion of: "${_truncate(text, 60)}"`);
|
|
6050
|
-
}
|
|
6051
|
-
const engine = new RedlineEngine(doc);
|
|
6052
|
-
engine.accept_all_revisions();
|
|
6053
|
-
for (const tag of ["w:rPrChange", "w:pPrChange", "w:sectPrChange"]) {
|
|
6054
|
-
for (const el of findAllDescendants(doc.element, tag)) {
|
|
6055
|
-
el.parentNode?.removeChild(el);
|
|
6056
|
-
}
|
|
6057
|
-
}
|
|
6058
|
-
const total = insEls.length + delEls.length;
|
|
6059
|
-
if (total) {
|
|
6060
|
-
return [`Tracked changes auto-accepted: ${total}`].concat(lines);
|
|
6061
|
-
}
|
|
6062
|
-
return [];
|
|
6398
|
+
function _strip_critic_markup(text) {
|
|
6399
|
+
if (!text) return "";
|
|
6400
|
+
text = text.replace(/\{--[\s\S]*?--\}/g, "");
|
|
6401
|
+
text = text.replace(/\{>>[\s\S]*?<<\}/g, "");
|
|
6402
|
+
text = text.replace(/\{\+\+([\s\S]*?)\+\+\}/g, "$1");
|
|
6403
|
+
text = text.replace(/\{==([\s\S]*?)==\}/g, "$1");
|
|
6404
|
+
return text;
|
|
6063
6405
|
}
|
|
6064
|
-
function
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
if (info.resolved) resolvedCount++;
|
|
6071
|
-
else openCount++;
|
|
6072
|
-
comments.push({ id: cId, ...info });
|
|
6073
|
-
}
|
|
6074
|
-
return { total: comments.length, open: openCount, resolved: resolvedCount, comments };
|
|
6406
|
+
function _strip_inline_formatting(text) {
|
|
6407
|
+
if (!text) return "";
|
|
6408
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
6409
|
+
text = text.replace(/__(.+?)__/g, "$1");
|
|
6410
|
+
text = text.replace(/(?<!\w)_(\S(?:.*?\S)?)_(?!\w)/g, "$1");
|
|
6411
|
+
return text;
|
|
6075
6412
|
}
|
|
6076
|
-
function
|
|
6077
|
-
const
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
const
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
const
|
|
6084
|
-
|
|
6085
|
-
cm.deleteComment(cId);
|
|
6086
|
-
}
|
|
6087
|
-
for (const tag of ["w:commentRangeStart", "w:commentRangeEnd"]) {
|
|
6088
|
-
for (const el of findAllDescendants(doc.element, tag)) {
|
|
6089
|
-
el.parentNode?.removeChild(el);
|
|
6090
|
-
}
|
|
6413
|
+
function _determine_heading_style(paragraph) {
|
|
6414
|
+
const [style_cache, default_pstyle] = _get_style_cache(
|
|
6415
|
+
paragraph._parent.part || paragraph._parent
|
|
6416
|
+
);
|
|
6417
|
+
const pPr = findChild(paragraph._element, "w:pPr");
|
|
6418
|
+
let style_id = default_pstyle;
|
|
6419
|
+
if (pPr) {
|
|
6420
|
+
const pStyle = findChild(pPr, "w:pStyle");
|
|
6421
|
+
if (pStyle) style_id = pStyle.getAttribute("w:val") || default_pstyle;
|
|
6091
6422
|
}
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
(c) => c.nodeType === 1 && c.tagName !== "w:rPr" && c.tagName !== "rPr"
|
|
6099
|
-
);
|
|
6100
|
-
if (nonRprChildren.length <= 1) {
|
|
6101
|
-
parent.parentNode?.removeChild(parent);
|
|
6102
|
-
} else {
|
|
6103
|
-
parent.removeChild(ref);
|
|
6104
|
-
}
|
|
6105
|
-
} else {
|
|
6106
|
-
parent.removeChild(ref);
|
|
6107
|
-
}
|
|
6423
|
+
let outline_level = null;
|
|
6424
|
+
let outline_level_from_style = false;
|
|
6425
|
+
if (pPr) {
|
|
6426
|
+
const oLvl = findChild(pPr, "w:outlineLvl");
|
|
6427
|
+
if (oLvl && /^\d+$/.test(oLvl.getAttribute("w:val") || "")) {
|
|
6428
|
+
outline_level = parseInt(oLvl.getAttribute("w:val"), 10);
|
|
6108
6429
|
}
|
|
6109
6430
|
}
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
}
|
|
6114
|
-
function eject_comment_parts(doc) {
|
|
6115
|
-
const pkg = doc.pkg;
|
|
6116
|
-
const comment_partnames = /* @__PURE__ */ new Set();
|
|
6117
|
-
for (const part of pkg.parts) {
|
|
6118
|
-
if (part.partname.toLowerCase().includes("comments")) {
|
|
6119
|
-
comment_partnames.add(part.partname);
|
|
6120
|
-
const withSlash = part.partname.startsWith("/") ? part.partname : "/" + part.partname;
|
|
6121
|
-
const withoutSlash = part.partname.startsWith("/") ? part.partname.substring(1) : part.partname;
|
|
6122
|
-
comment_partnames.add(withSlash);
|
|
6123
|
-
comment_partnames.add(withoutSlash);
|
|
6124
|
-
}
|
|
6431
|
+
if (outline_level === null && style_id && style_cache && style_cache[style_id]) {
|
|
6432
|
+
outline_level = style_cache[style_id].outline_level;
|
|
6433
|
+
outline_level_from_style = true;
|
|
6125
6434
|
}
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
if (target.toLowerCase().includes("comments")) {
|
|
6134
|
-
toRemove.push(rel);
|
|
6135
|
-
const sourcePath = part.partname.replace("/_rels/", "/").replace(".rels", "");
|
|
6136
|
-
const sourcePart = pkg.getPartByPath(sourcePath);
|
|
6137
|
-
if (sourcePart) {
|
|
6138
|
-
const relId = rel.getAttribute("Id");
|
|
6139
|
-
if (relId) sourcePart.rels.delete(relId);
|
|
6140
|
-
}
|
|
6141
|
-
}
|
|
6142
|
-
}
|
|
6143
|
-
for (const relEl of toRemove) {
|
|
6144
|
-
relEl.parentNode?.removeChild(relEl);
|
|
6145
|
-
}
|
|
6435
|
+
const style_name = style_id && style_cache && style_cache[style_id] ? style_cache[style_id].name : style_id;
|
|
6436
|
+
let normalized_style_name = style_name;
|
|
6437
|
+
if (normalized_style_name && typeof normalized_style_name === "string") {
|
|
6438
|
+
if (normalized_style_name.toLowerCase().startsWith("heading")) {
|
|
6439
|
+
normalized_style_name = normalized_style_name.replace(/^heading/i, "Heading");
|
|
6440
|
+
} else if (normalized_style_name.toLowerCase() === "title") {
|
|
6441
|
+
normalized_style_name = "Title";
|
|
6146
6442
|
}
|
|
6147
6443
|
}
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
for (const override of overrides) {
|
|
6153
|
-
const partName = override.getAttribute("PartName") || "";
|
|
6154
|
-
if (comment_partnames.has(partName) || partName.toLowerCase().includes("comments")) {
|
|
6155
|
-
toRemove.push(override);
|
|
6156
|
-
}
|
|
6157
|
-
}
|
|
6158
|
-
for (const overrideEl of toRemove) {
|
|
6159
|
-
overrideEl.parentNode?.removeChild(overrideEl);
|
|
6444
|
+
if (outline_level_from_style && outline_level !== null) {
|
|
6445
|
+
const is_heading_or_title = normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title");
|
|
6446
|
+
if (!is_heading_or_title) {
|
|
6447
|
+
outline_level = null;
|
|
6160
6448
|
}
|
|
6161
6449
|
}
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
delete pkg.unzipped[key];
|
|
6450
|
+
if (outline_level !== null && outline_level >= 0 && outline_level <= 8) {
|
|
6451
|
+
if (normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title")) {
|
|
6452
|
+
return normalized_style_name;
|
|
6166
6453
|
}
|
|
6454
|
+
return "(outline_level)";
|
|
6167
6455
|
}
|
|
6456
|
+
if (normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title"))
|
|
6457
|
+
return normalized_style_name;
|
|
6458
|
+
if (normalized_style_name && /Heading[ ]?([1-6])(?![0-9])/.test(normalized_style_name))
|
|
6459
|
+
return normalized_style_name;
|
|
6460
|
+
return "(heuristic)";
|
|
6168
6461
|
}
|
|
6169
|
-
function
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
for (const c of comments) {
|
|
6175
|
-
const author = c.getAttribute("w:author");
|
|
6176
|
-
if (author) {
|
|
6177
|
-
original.add(author);
|
|
6178
|
-
c.setAttribute("w:author", newAuthor);
|
|
6179
|
-
}
|
|
6180
|
-
if (c.hasAttribute("w:initials")) {
|
|
6181
|
-
const initials = newAuthor.split(" ").filter(Boolean).map((p) => p[0]).join("").toUpperCase();
|
|
6182
|
-
c.setAttribute("w:initials", initials);
|
|
6183
|
-
}
|
|
6462
|
+
function _find_owned_end(block_records, heading_indices, current_h_pos, current_level) {
|
|
6463
|
+
for (let next_h_pos = current_h_pos + 1; next_h_pos < heading_indices.length; next_h_pos++) {
|
|
6464
|
+
const next_idx = heading_indices[next_h_pos];
|
|
6465
|
+
if (_heading_level(block_records[next_idx].item) <= current_level)
|
|
6466
|
+
return next_idx;
|
|
6184
6467
|
}
|
|
6185
|
-
return
|
|
6468
|
+
return block_records.length;
|
|
6186
6469
|
}
|
|
6187
|
-
function
|
|
6188
|
-
const
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6470
|
+
function _collect_footnote_ids(owned_blocks) {
|
|
6471
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6472
|
+
const ordered = [];
|
|
6473
|
+
for (const rec of owned_blocks) {
|
|
6474
|
+
if (!rec.is_paragraph) continue;
|
|
6475
|
+
for (const event of iter_paragraph_content(rec.item)) {
|
|
6476
|
+
if (!("type" in event)) continue;
|
|
6477
|
+
let fn_id = "";
|
|
6478
|
+
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
6479
|
+
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
6480
|
+
else continue;
|
|
6481
|
+
if (!seen.has(fn_id)) {
|
|
6482
|
+
seen.add(fn_id);
|
|
6483
|
+
ordered.push(fn_id);
|
|
6195
6484
|
}
|
|
6196
6485
|
}
|
|
6197
6486
|
}
|
|
6198
|
-
return
|
|
6487
|
+
return ordered;
|
|
6199
6488
|
}
|
|
6200
|
-
function
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
for (
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6489
|
+
function _offset_to_page(offset, body_page_offsets) {
|
|
6490
|
+
if (!body_page_offsets || body_page_offsets.length === 0) return 1;
|
|
6491
|
+
let page = 1;
|
|
6492
|
+
for (let i = 0; i < body_page_offsets.length; i++) {
|
|
6493
|
+
if (offset >= body_page_offsets[i]) page = i + 1;
|
|
6494
|
+
else break;
|
|
6495
|
+
}
|
|
6496
|
+
return page;
|
|
6497
|
+
}
|
|
6498
|
+
function _extract_outline_fast(doc, projected_body, body_page_offsets, paragraph_offsets) {
|
|
6499
|
+
const paragraphs_and_tables = [];
|
|
6500
|
+
const seen_cells = /* @__PURE__ */ new Set();
|
|
6501
|
+
function walk(container) {
|
|
6502
|
+
for (const item of iter_block_items(container)) {
|
|
6503
|
+
const i_type = item.constructor.name;
|
|
6504
|
+
if (i_type === "FootnoteItem") {
|
|
6505
|
+
walk(item);
|
|
6506
|
+
} else if (item instanceof Paragraph) {
|
|
6507
|
+
paragraphs_and_tables.push(["p", item]);
|
|
6508
|
+
} else if (item instanceof Table) {
|
|
6509
|
+
paragraphs_and_tables.push(["t", item]);
|
|
6510
|
+
for (const row of item.rows) {
|
|
6511
|
+
for (const cell of row.cells) {
|
|
6512
|
+
if (seen_cells.has(cell._element)) {
|
|
6513
|
+
continue;
|
|
6514
|
+
}
|
|
6515
|
+
seen_cells.add(cell._element);
|
|
6516
|
+
walk(cell);
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
6208
6519
|
}
|
|
6209
6520
|
}
|
|
6210
6521
|
}
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6522
|
+
walk(doc);
|
|
6523
|
+
const heading_indices = [];
|
|
6524
|
+
for (let idx = 0; idx < paragraphs_and_tables.length; idx++) {
|
|
6525
|
+
const [kind, item] = paragraphs_and_tables[idx];
|
|
6526
|
+
if (kind !== "p") continue;
|
|
6527
|
+
let hasOffset = false;
|
|
6528
|
+
if (paragraph_offsets instanceof Map) {
|
|
6529
|
+
hasOffset = paragraph_offsets.has(item._element);
|
|
6530
|
+
} else {
|
|
6531
|
+
hasOffset = item._element in paragraph_offsets;
|
|
6532
|
+
}
|
|
6533
|
+
if (!hasOffset) {
|
|
6534
|
+
continue;
|
|
6535
|
+
}
|
|
6536
|
+
if (!_is_heading(item)) continue;
|
|
6537
|
+
if (!_heading_passes_quality_filter_fast(item, projected_body, paragraph_offsets)) continue;
|
|
6538
|
+
heading_indices.push(idx);
|
|
6539
|
+
}
|
|
6540
|
+
if (heading_indices.length === 0) return [];
|
|
6541
|
+
const nodes = [];
|
|
6542
|
+
for (let h_pos = 0; h_pos < heading_indices.length; h_pos++) {
|
|
6543
|
+
const item_idx = heading_indices[h_pos];
|
|
6544
|
+
const paragraph = paragraphs_and_tables[item_idx][1];
|
|
6545
|
+
const level = _heading_level(paragraph);
|
|
6546
|
+
const text = _heading_text_fast(paragraph, projected_body, paragraph_offsets);
|
|
6547
|
+
const style = _determine_heading_style(paragraph);
|
|
6548
|
+
let owned_end = item_idx;
|
|
6549
|
+
for (let next_h_pos = h_pos + 1; next_h_pos < heading_indices.length; next_h_pos++) {
|
|
6550
|
+
const next_idx = heading_indices[next_h_pos];
|
|
6551
|
+
const next_paragraph = paragraphs_and_tables[next_idx][1];
|
|
6552
|
+
if (_heading_level(next_paragraph) <= level) {
|
|
6553
|
+
owned_end = next_idx;
|
|
6554
|
+
break;
|
|
6222
6555
|
}
|
|
6223
|
-
}
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6556
|
+
}
|
|
6557
|
+
if (owned_end === item_idx) {
|
|
6558
|
+
owned_end = paragraphs_and_tables.length;
|
|
6559
|
+
}
|
|
6560
|
+
const owned = paragraphs_and_tables.slice(item_idx + 1, owned_end);
|
|
6561
|
+
let has_table = false;
|
|
6562
|
+
for (const [kind2, item2] of owned) {
|
|
6563
|
+
if (kind2 === "p" && _is_heading(item2)) {
|
|
6564
|
+
break;
|
|
6229
6565
|
}
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
if (c.textContent && parseInt(c.textContent) > 1) {
|
|
6234
|
-
lines.push(`Revision count: ${c.textContent} \u2192 1`);
|
|
6235
|
-
c.textContent = "1";
|
|
6566
|
+
if (kind2 === "t") {
|
|
6567
|
+
has_table = true;
|
|
6568
|
+
break;
|
|
6236
6569
|
}
|
|
6237
|
-
});
|
|
6238
|
-
}
|
|
6239
|
-
const appPart = doc.pkg.getPartByPath("docProps/app.xml");
|
|
6240
|
-
if (appPart) {
|
|
6241
|
-
const docEl = appPart._element;
|
|
6242
|
-
const intFields = ["TotalTime", "Words", "Characters", "Paragraphs", "Lines", "CharactersWithSpaces"];
|
|
6243
|
-
for (const f of intFields) {
|
|
6244
|
-
findDescendantsByLocalName(docEl, f).forEach((el) => {
|
|
6245
|
-
if (el.textContent && el.textContent !== "0") {
|
|
6246
|
-
if (f === "TotalTime") lines.push(`Total editing time: ${el.textContent} minutes`);
|
|
6247
|
-
el.textContent = "0";
|
|
6248
|
-
}
|
|
6249
|
-
});
|
|
6250
6570
|
}
|
|
6251
|
-
const
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
}
|
|
6258
|
-
});
|
|
6571
|
+
const footnote_ids = _collect_footnote_ids_fast(owned);
|
|
6572
|
+
let para_offset;
|
|
6573
|
+
if (paragraph_offsets instanceof Map) {
|
|
6574
|
+
para_offset = paragraph_offsets.get(paragraph._element);
|
|
6575
|
+
} else {
|
|
6576
|
+
para_offset = paragraph_offsets[paragraph._element];
|
|
6259
6577
|
}
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
let modified = false;
|
|
6265
|
-
const epoch = "1970-01-01T00:00:00Z";
|
|
6266
|
-
const corePart = doc.pkg.getPartByPath("docProps/core.xml");
|
|
6267
|
-
if (corePart) {
|
|
6268
|
-
for (const tag of ["created", "modified", "lastPrinted"]) {
|
|
6269
|
-
findDescendantsByLocalName(corePart._element, tag).forEach((el) => {
|
|
6270
|
-
if (el.textContent && el.textContent !== epoch) {
|
|
6271
|
-
el.textContent = epoch;
|
|
6272
|
-
modified = true;
|
|
6273
|
-
}
|
|
6274
|
-
});
|
|
6578
|
+
let page_num = 1;
|
|
6579
|
+
if (para_offset !== void 0) {
|
|
6580
|
+
const [start_offset] = para_offset;
|
|
6581
|
+
page_num = _offset_to_page(start_offset, body_page_offsets);
|
|
6275
6582
|
}
|
|
6583
|
+
nodes.push({
|
|
6584
|
+
level,
|
|
6585
|
+
text,
|
|
6586
|
+
page: page_num,
|
|
6587
|
+
style,
|
|
6588
|
+
has_table,
|
|
6589
|
+
footnote_ids
|
|
6590
|
+
});
|
|
6276
6591
|
}
|
|
6277
|
-
return
|
|
6592
|
+
return nodes;
|
|
6278
6593
|
}
|
|
6279
|
-
function
|
|
6280
|
-
const
|
|
6281
|
-
if (
|
|
6282
|
-
const
|
|
6283
|
-
|
|
6284
|
-
const
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
}
|
|
6292
|
-
|
|
6293
|
-
if (rootRels) removeRelationsTo(rootRels);
|
|
6294
|
-
const docRels = doc.pkg.getOrCreateRelsPart(doc.part.partname);
|
|
6295
|
-
if (docRels) removeRelationsTo(docRels);
|
|
6296
|
-
for (const sdtPr of findAllDescendants(doc.element, "w:sdtPr")) {
|
|
6297
|
-
findChildren(sdtPr, "w:dataBinding").forEach((b) => sdtPr.removeChild(b));
|
|
6594
|
+
function _heading_passes_quality_filter_fast(paragraph, projected_body, paragraph_offsets) {
|
|
6595
|
+
const style = _determine_heading_style(paragraph);
|
|
6596
|
+
if (style !== "(heuristic)") return true;
|
|
6597
|
+
const text = _heading_text_fast(paragraph, projected_body, paragraph_offsets);
|
|
6598
|
+
if (!text) return false;
|
|
6599
|
+
const words = text.match(/\w+/g) || [];
|
|
6600
|
+
return words.length >= _HEURISTIC_MIN_WORDS;
|
|
6601
|
+
}
|
|
6602
|
+
function _heading_text_fast(paragraph, projected_body, paragraph_offsets) {
|
|
6603
|
+
let offset;
|
|
6604
|
+
if (paragraph_offsets instanceof Map) {
|
|
6605
|
+
offset = paragraph_offsets.get(paragraph._element);
|
|
6606
|
+
} else {
|
|
6607
|
+
offset = paragraph_offsets[paragraph._element];
|
|
6298
6608
|
}
|
|
6299
|
-
|
|
6609
|
+
if (offset === void 0) {
|
|
6610
|
+
return "";
|
|
6611
|
+
}
|
|
6612
|
+
const [start, length] = offset;
|
|
6613
|
+
const raw = projected_body.substring(start, start + length);
|
|
6614
|
+
let cleaned = _strip_critic_markup(raw);
|
|
6615
|
+
cleaned = _strip_inline_formatting(cleaned);
|
|
6616
|
+
cleaned = cleaned.replace(/^#+\s+/, "");
|
|
6617
|
+
return cleaned.trim();
|
|
6300
6618
|
}
|
|
6301
|
-
function
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
if (
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6619
|
+
function _collect_footnote_ids_fast(owned_items) {
|
|
6620
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6621
|
+
const ordered = [];
|
|
6622
|
+
for (const [kind, item] of owned_items) {
|
|
6623
|
+
if (kind !== "p") continue;
|
|
6624
|
+
for (const event of iter_paragraph_content(item)) {
|
|
6625
|
+
if (!("type" in event)) continue;
|
|
6626
|
+
let fn_id = "";
|
|
6627
|
+
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
6628
|
+
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
6629
|
+
else continue;
|
|
6630
|
+
if (!seen.has(fn_id)) {
|
|
6631
|
+
seen.add(fn_id);
|
|
6632
|
+
ordered.push(fn_id);
|
|
6311
6633
|
}
|
|
6312
6634
|
}
|
|
6313
6635
|
}
|
|
6314
|
-
return
|
|
6636
|
+
return ordered;
|
|
6315
6637
|
}
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6638
|
+
|
|
6639
|
+
// src/sanitize/report.ts
|
|
6640
|
+
var SanitizeReport = class {
|
|
6641
|
+
filename;
|
|
6642
|
+
mode;
|
|
6643
|
+
author;
|
|
6644
|
+
tracked_changes_found = 0;
|
|
6645
|
+
tracked_changes_accepted = 0;
|
|
6646
|
+
tracked_changes_kept = 0;
|
|
6647
|
+
change_lines = [];
|
|
6648
|
+
comments_removed = 0;
|
|
6649
|
+
comments_kept = 0;
|
|
6650
|
+
removed_comment_lines = [];
|
|
6651
|
+
kept_comment_lines = [];
|
|
6652
|
+
metadata_lines = [];
|
|
6653
|
+
structural_lines = [];
|
|
6654
|
+
warnings = [];
|
|
6655
|
+
status = "clean";
|
|
6656
|
+
blocked_reason = null;
|
|
6657
|
+
constructor(filename, mode = "full", author = null) {
|
|
6658
|
+
this.filename = filename;
|
|
6659
|
+
this.mode = mode;
|
|
6660
|
+
this.author = author;
|
|
6661
|
+
}
|
|
6662
|
+
add_transform_lines(lines) {
|
|
6663
|
+
for (const line of lines) {
|
|
6664
|
+
const lower = line.toLowerCase();
|
|
6665
|
+
if (lower.includes("tracked change") || lower.includes("insertion") || lower.includes("deletion") || lower.includes("accepted")) {
|
|
6666
|
+
this.change_lines.push(line);
|
|
6667
|
+
} else if (lower.includes("comment") || lower.includes("[open]") || lower.includes("[resolved]")) {
|
|
6668
|
+
if (lower.includes("kept") || lower.includes("visible")) {
|
|
6669
|
+
this.kept_comment_lines.push(line);
|
|
6670
|
+
} else {
|
|
6671
|
+
this.removed_comment_lines.push(line);
|
|
6327
6672
|
}
|
|
6673
|
+
} 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")) {
|
|
6674
|
+
this.metadata_lines.push(line);
|
|
6675
|
+
} else if (lower.includes("hyperlink") || lower.includes("warning")) {
|
|
6676
|
+
this.warnings.push(line);
|
|
6677
|
+
} else {
|
|
6678
|
+
this.structural_lines.push(line);
|
|
6328
6679
|
}
|
|
6329
6680
|
}
|
|
6330
6681
|
}
|
|
6331
|
-
|
|
6332
|
-
|
|
6682
|
+
render() {
|
|
6683
|
+
const sep = "\u2550".repeat(50);
|
|
6684
|
+
const lines = [sep, `Finalization Report: ${this.filename}`];
|
|
6685
|
+
const flags = [];
|
|
6686
|
+
if (this.mode === "keep-markup") flags.push("--keep-markup");
|
|
6687
|
+
if (this.author) flags.push(`--author "${this.author}"`);
|
|
6688
|
+
if (this.tracked_changes_accepted > 0) flags.push("--accept-all");
|
|
6689
|
+
if (flags.length > 0) lines.push(flags.join(" "));
|
|
6690
|
+
lines.push(sep);
|
|
6691
|
+
if (this.status === "blocked") {
|
|
6692
|
+
lines.push("");
|
|
6693
|
+
lines.push(`BLOCKED: ${this.blocked_reason}`);
|
|
6694
|
+
lines.push(sep);
|
|
6695
|
+
return lines.join("\n");
|
|
6696
|
+
}
|
|
6697
|
+
if (this.mode === "keep-markup" && (this.tracked_changes_kept > 0 || this.comments_kept > 0)) {
|
|
6698
|
+
lines.push("");
|
|
6699
|
+
lines.push("VISIBLE TO COUNTERPARTY");
|
|
6700
|
+
if (this.tracked_changes_kept > 0) lines.push(` Tracked changes: ${this.tracked_changes_kept}`);
|
|
6701
|
+
if (this.comments_kept > 0) {
|
|
6702
|
+
lines.push(` Open comments: ${this.comments_kept}`);
|
|
6703
|
+
for (const cl of this.kept_comment_lines) lines.push(` ${cl}`);
|
|
6704
|
+
}
|
|
6705
|
+
if (this.author) lines.push(` Author on all markup: "${this.author}"`);
|
|
6706
|
+
}
|
|
6707
|
+
if (this.change_lines.length > 0) {
|
|
6708
|
+
lines.push("");
|
|
6709
|
+
lines.push("TRACKED CHANGES");
|
|
6710
|
+
for (const cl of this.change_lines) lines.push(` ${cl}`);
|
|
6711
|
+
}
|
|
6712
|
+
if (this.removed_comment_lines.length > 0) {
|
|
6713
|
+
lines.push("");
|
|
6714
|
+
lines.push("COMMENTS (stripped)");
|
|
6715
|
+
for (const cl of this.removed_comment_lines) lines.push(` ${cl}`);
|
|
6716
|
+
}
|
|
6717
|
+
if (this.metadata_lines.length > 0) {
|
|
6718
|
+
lines.push("");
|
|
6719
|
+
lines.push("METADATA");
|
|
6720
|
+
for (const ml of this.metadata_lines) lines.push(` ${ml}`);
|
|
6721
|
+
}
|
|
6722
|
+
if (this.structural_lines.length > 0) {
|
|
6723
|
+
lines.push("");
|
|
6724
|
+
lines.push("STRUCTURAL & PROTECTION");
|
|
6725
|
+
for (const sl of this.structural_lines) lines.push(` ${sl}`);
|
|
6726
|
+
}
|
|
6727
|
+
if (this.warnings.length > 0) {
|
|
6728
|
+
lines.push("");
|
|
6729
|
+
lines.push("WARNINGS");
|
|
6730
|
+
for (const w of this.warnings) lines.push(` \u26A0 ${w}`);
|
|
6731
|
+
}
|
|
6732
|
+
lines.push("");
|
|
6733
|
+
lines.push(sep);
|
|
6734
|
+
if (this.warnings.length > 0) {
|
|
6735
|
+
lines.push(`Result: CLEAN WITH WARNINGS (${this.warnings.length} warning${this.warnings.length > 1 ? "s" : ""})`);
|
|
6736
|
+
} else {
|
|
6737
|
+
lines.push(`Result: CLEAN (${this.tracked_changes_found} changes resolved, ${this.comments_removed} comments removed)`);
|
|
6738
|
+
}
|
|
6739
|
+
lines.push(sep);
|
|
6740
|
+
return lines.join("\n");
|
|
6741
|
+
}
|
|
6742
|
+
};
|
|
6333
6743
|
|
|
6334
6744
|
// src/sanitize/core.ts
|
|
6335
6745
|
async function finalize_document(doc, options) {
|