@adeu/core 1.13.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 +2215 -1789
- 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 +2215 -1789
- 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.bugs.test.ts +20 -0
- package/src/engine.qa.test.ts +162 -0
- package/src/engine.ts +302 -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(
|
|
@@ -3567,6 +3903,22 @@ var RedlineEngine = class {
|
|
|
3567
3903
|
return errors;
|
|
3568
3904
|
}
|
|
3569
3905
|
process_batch(changes, dry_run = false) {
|
|
3906
|
+
if (Array.isArray(changes)) {
|
|
3907
|
+
changes = changes.map((item) => {
|
|
3908
|
+
if (typeof item === "string") {
|
|
3909
|
+
try {
|
|
3910
|
+
const parsed = JSON.parse(item);
|
|
3911
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
3912
|
+
return parsed;
|
|
3913
|
+
}
|
|
3914
|
+
return item;
|
|
3915
|
+
} catch {
|
|
3916
|
+
return item;
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
return item;
|
|
3920
|
+
});
|
|
3921
|
+
}
|
|
3570
3922
|
if (dry_run) {
|
|
3571
3923
|
const baselines = /* @__PURE__ */ new Map();
|
|
3572
3924
|
for (const part of this.doc.pkg.parts) {
|
|
@@ -3633,6 +3985,9 @@ var RedlineEngine = class {
|
|
|
3633
3985
|
if (this.clean_mapper) this.clean_mapper["_build_map"]();
|
|
3634
3986
|
}
|
|
3635
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;
|
|
3636
3991
|
const edits_reports = [];
|
|
3637
3992
|
let applied_edits = 0;
|
|
3638
3993
|
let skipped_edits = 0;
|
|
@@ -3656,9 +4011,8 @@ var RedlineEngine = class {
|
|
|
3656
4011
|
});
|
|
3657
4012
|
continue;
|
|
3658
4013
|
}
|
|
3659
|
-
const res = this.apply_edits([edit]);
|
|
3660
|
-
|
|
3661
|
-
if (applied > 0) {
|
|
4014
|
+
const res = this.apply_edits([edit], page_offsets);
|
|
4015
|
+
if (edit._applied_status) {
|
|
3662
4016
|
applied_edits++;
|
|
3663
4017
|
const previews = this._build_edit_context_previews(edit);
|
|
3664
4018
|
edits_reports.push({
|
|
@@ -3668,7 +4022,11 @@ var RedlineEngine = class {
|
|
|
3668
4022
|
warning,
|
|
3669
4023
|
error: null,
|
|
3670
4024
|
critic_markup: previews[0],
|
|
3671
|
-
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"
|
|
3672
4030
|
});
|
|
3673
4031
|
} else {
|
|
3674
4032
|
skipped_edits++;
|
|
@@ -3690,9 +4048,9 @@ var RedlineEngine = class {
|
|
|
3690
4048
|
throw new BatchValidationError(errors);
|
|
3691
4049
|
}
|
|
3692
4050
|
const cloned_edits = edits.map((e) => JSON.parse(JSON.stringify(e)));
|
|
3693
|
-
const res = this.apply_edits(cloned_edits);
|
|
3694
|
-
applied_edits =
|
|
3695
|
-
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;
|
|
3696
4054
|
for (const edit of cloned_edits) {
|
|
3697
4055
|
const success = edit._applied_status || false;
|
|
3698
4056
|
const error_msg = edit._error_msg || null;
|
|
@@ -3713,7 +4071,11 @@ var RedlineEngine = class {
|
|
|
3713
4071
|
warning,
|
|
3714
4072
|
error: error_msg,
|
|
3715
4073
|
critic_markup,
|
|
3716
|
-
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"
|
|
3717
4079
|
});
|
|
3718
4080
|
}
|
|
3719
4081
|
}
|
|
@@ -3729,9 +4091,13 @@ var RedlineEngine = class {
|
|
|
3729
4091
|
version: "1.10.0"
|
|
3730
4092
|
};
|
|
3731
4093
|
}
|
|
3732
|
-
apply_edits(edits) {
|
|
4094
|
+
apply_edits(edits, page_offsets = []) {
|
|
3733
4095
|
let applied = 0;
|
|
3734
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
|
+
}
|
|
3735
4101
|
const resolved_edits = [];
|
|
3736
4102
|
for (const edit of edits) {
|
|
3737
4103
|
edit._applied_status = false;
|
|
@@ -3833,6 +4199,19 @@ var RedlineEngine = class {
|
|
|
3833
4199
|
const parent = edit._parent_edit_ref;
|
|
3834
4200
|
if (parent) {
|
|
3835
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;
|
|
3836
4215
|
}
|
|
3837
4216
|
} else {
|
|
3838
4217
|
skipped++;
|
|
@@ -3985,96 +4364,192 @@ var RedlineEngine = class {
|
|
|
3985
4364
|
}
|
|
3986
4365
|
return false;
|
|
3987
4366
|
}
|
|
3988
|
-
/**
|
|
3989
|
-
* Returns the first match of `target_text` in the raw mapper that is NOT
|
|
3990
|
-
* entirely contained within a tracked deletion (<w:del>). Tracked-deleted
|
|
3991
|
-
* copies are not live, editable text, so an edit must resolve to a live
|
|
3992
|
-
* occurrence even when a dead copy appears earlier in the document
|
|
3993
|
-
* (BUG-23-5). Falls back to the plain first match when no live copy is
|
|
3994
|
-
* found (e.g. fuzzy/normalized matches the span filter cannot align).
|
|
3995
|
-
*/
|
|
3996
|
-
_first_live_match(target_text) {
|
|
3997
|
-
const all = this.mapper.find_all_match_indices(target_text);
|
|
3998
|
-
if (all.length <= 1) {
|
|
3999
|
-
return this.mapper.find_match_index(target_text);
|
|
4000
|
-
}
|
|
4001
|
-
for (const [start, length] of all) {
|
|
4002
|
-
const realSpans = this.mapper.spans.filter(
|
|
4003
|
-
(s) => s.run !== null && s.end > start && s.start < start + length
|
|
4004
|
-
);
|
|
4005
|
-
if (realSpans.length === 0) return [start, length];
|
|
4006
|
-
if (realSpans.some((s) => !s.del_id)) return [start, length];
|
|
4007
|
-
}
|
|
4008
|
-
return this.mapper.find_match_index(target_text);
|
|
4009
|
-
}
|
|
4010
4367
|
_pre_resolve_heuristic_edit(edit) {
|
|
4011
4368
|
if (!edit.target_text) return null;
|
|
4012
|
-
|
|
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);
|
|
4013
4372
|
let use_clean_map = false;
|
|
4014
|
-
if (
|
|
4373
|
+
if (matches.length === 0) {
|
|
4015
4374
|
if (!this.clean_mapper)
|
|
4016
4375
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
);
|
|
4020
|
-
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;
|
|
4021
4378
|
else return null;
|
|
4022
4379
|
}
|
|
4023
4380
|
const active_mapper = use_clean_map ? this.clean_mapper : this.mapper;
|
|
4024
|
-
|
|
4025
|
-
const
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
);
|
|
4029
|
-
if (actual_doc_text === effective_new_text || edit.target_text === effective_new_text) {
|
|
4030
|
-
return {
|
|
4031
|
-
type: "modify",
|
|
4032
|
-
target_text: actual_doc_text,
|
|
4033
|
-
new_text: actual_doc_text,
|
|
4034
|
-
comment: edit.comment,
|
|
4035
|
-
_match_start_index: start_idx,
|
|
4036
|
-
_internal_op: "COMMENT_ONLY",
|
|
4037
|
-
_active_mapper_ref: active_mapper
|
|
4038
|
-
};
|
|
4039
|
-
}
|
|
4040
|
-
let effective_op = "";
|
|
4041
|
-
let final_target = "";
|
|
4042
|
-
let final_new = "";
|
|
4043
|
-
let effective_start_idx = start_idx;
|
|
4044
|
-
if (effective_new_text.startsWith(actual_doc_text)) {
|
|
4045
|
-
effective_op = "INSERTION";
|
|
4046
|
-
final_new = effective_new_text.substring(actual_doc_text.length);
|
|
4047
|
-
effective_start_idx = start_idx + match_len;
|
|
4048
|
-
} else {
|
|
4049
|
-
const [prefix_len, suffix_len] = trim_common_context(
|
|
4050
|
-
actual_doc_text,
|
|
4051
|
-
effective_new_text
|
|
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
|
|
4052
4385
|
);
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
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({
|
|
4465
|
+
type: "modify",
|
|
4466
|
+
target_text: final_target,
|
|
4467
|
+
new_text: final_new,
|
|
4468
|
+
comment: edit.comment,
|
|
4469
|
+
_match_start_index: effective_start_idx,
|
|
4470
|
+
_internal_op: effective_op,
|
|
4471
|
+
_active_mapper_ref: active_mapper
|
|
4472
|
+
});
|
|
4473
|
+
}
|
|
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];
|
|
4477
|
+
}
|
|
4478
|
+
_apply_single_edit_indexed(edit, orig_new, rebuild_map) {
|
|
4479
|
+
let op = edit._internal_op;
|
|
4480
|
+
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
4481
|
+
const start_idx = edit._resolved_start_idx !== void 0 && edit._resolved_start_idx !== null ? edit._resolved_start_idx : edit._match_start_index || 0;
|
|
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
|
+
}
|
|
4062
4552
|
}
|
|
4063
|
-
return {
|
|
4064
|
-
type: "modify",
|
|
4065
|
-
target_text: final_target,
|
|
4066
|
-
new_text: final_new,
|
|
4067
|
-
comment: edit.comment,
|
|
4068
|
-
_match_start_index: effective_start_idx,
|
|
4069
|
-
_internal_op: effective_op,
|
|
4070
|
-
_active_mapper_ref: active_mapper
|
|
4071
|
-
};
|
|
4072
|
-
}
|
|
4073
|
-
_apply_single_edit_indexed(edit, orig_new, rebuild_map) {
|
|
4074
|
-
let op = edit._internal_op;
|
|
4075
|
-
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
4076
|
-
const start_idx = edit._resolved_start_idx !== void 0 && edit._resolved_start_idx !== null ? edit._resolved_start_idx : edit._match_start_index || 0;
|
|
4077
|
-
const length = edit.target_text ? edit.target_text.length : 0;
|
|
4078
4553
|
const del_id = ["DELETION", "MODIFICATION"].includes(op) ? this._getNextId() : null;
|
|
4079
4554
|
const ins_id = ["INSERTION", "MODIFICATION"].includes(op) ? this._getNextId() : null;
|
|
4080
4555
|
if (op === "COMMENT_ONLY") {
|
|
@@ -4457,1863 +4932,1814 @@ var RedlineEngine = class {
|
|
|
4457
4932
|
}
|
|
4458
4933
|
};
|
|
4459
4934
|
|
|
4460
|
-
// src/
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
}
|
|
4469
|
-
var _CHG_ID_PATTERN = /\bChg:(\d+)\b/g;
|
|
4470
|
-
function split_structural_appendix(markdown) {
|
|
4471
|
-
if (!markdown) return ["", ""];
|
|
4472
|
-
const idx = markdown.indexOf(APPENDIX_MARKER);
|
|
4473
|
-
if (idx === -1) return [markdown, ""];
|
|
4474
|
-
const line_start = markdown.lastIndexOf("\n", idx) + 1;
|
|
4475
|
-
const body = markdown.substring(0, line_start).trimEnd();
|
|
4476
|
-
const appendix = markdown.substring(line_start);
|
|
4477
|
-
return [body, appendix];
|
|
4478
|
-
}
|
|
4479
|
-
function paginate(markdown_body, structural_appendix = "") {
|
|
4480
|
-
if (!markdown_body) {
|
|
4481
|
-
const appendix_clean = structural_appendix ? structural_appendix.trim() : "";
|
|
4482
|
-
const content = appendix_clean;
|
|
4483
|
-
return {
|
|
4484
|
-
pages: [{
|
|
4485
|
-
page: 1,
|
|
4486
|
-
total_pages: 1,
|
|
4487
|
-
has_next: false,
|
|
4488
|
-
has_prev: false,
|
|
4489
|
-
tracked_change_count: _count_tracked_changes(content),
|
|
4490
|
-
page_content: content
|
|
4491
|
-
}],
|
|
4492
|
-
total_pages: 1,
|
|
4493
|
-
body_pages: [""],
|
|
4494
|
-
body_page_offsets: [0]
|
|
4495
|
-
};
|
|
4496
|
-
}
|
|
4497
|
-
const block_records = _tokenize_into_atomic_blocks(markdown_body);
|
|
4498
|
-
const [body_pages, body_page_offsets] = _assemble_pages(block_records);
|
|
4499
|
-
let final_pages;
|
|
4500
|
-
if (structural_appendix && structural_appendix.trim()) {
|
|
4501
|
-
const appendix = structural_appendix.trim();
|
|
4502
|
-
final_pages = body_pages.map((bp) => bp ? `${bp}
|
|
4503
|
-
|
|
4504
|
-
${appendix}` : appendix);
|
|
4505
|
-
} else {
|
|
4506
|
-
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
|
+
}
|
|
4507
4944
|
}
|
|
4508
|
-
|
|
4509
|
-
const page_infos = final_pages.map((content, i) => ({
|
|
4510
|
-
page: i + 1,
|
|
4511
|
-
total_pages: total,
|
|
4512
|
-
has_next: i + 1 < total,
|
|
4513
|
-
has_prev: i + 1 > 1,
|
|
4514
|
-
tracked_change_count: _count_tracked_changes(content),
|
|
4515
|
-
page_content: content
|
|
4516
|
-
}));
|
|
4517
|
-
return {
|
|
4518
|
-
pages: page_infos,
|
|
4519
|
-
total_pages: total,
|
|
4520
|
-
body_pages,
|
|
4521
|
-
body_page_offsets
|
|
4522
|
-
};
|
|
4523
|
-
}
|
|
4524
|
-
function _tokenize_into_atomic_blocks(markdown_body) {
|
|
4525
|
-
const raw_blocks = _split_on_safe_paragraph_breaks(markdown_body);
|
|
4526
|
-
return _merge_footnote_sections(raw_blocks);
|
|
4945
|
+
return result;
|
|
4527
4946
|
}
|
|
4528
|
-
function
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
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;
|
|
4542
4961
|
}
|
|
4543
4962
|
}
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
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
|
+
}
|
|
4552
5007
|
}
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
if (text[i] === "\n" && i + 1 < n && text[i + 1] === "\n") {
|
|
4556
|
-
if (Object.values(counters).every((c) => c === 0)) {
|
|
4557
|
-
const block_text = text.substring(block_start, i);
|
|
4558
|
-
if (block_text) blocks.push([block_text, block_start]);
|
|
4559
|
-
let j = i;
|
|
4560
|
-
while (j < n && text[j] === "\n") j++;
|
|
4561
|
-
i = j;
|
|
4562
|
-
block_start = i;
|
|
4563
|
-
continue;
|
|
5008
|
+
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(curr.tagName)) {
|
|
5009
|
+
coalesceContainer(curr);
|
|
4564
5010
|
}
|
|
4565
|
-
}
|
|
4566
|
-
i++;
|
|
4567
|
-
}
|
|
4568
|
-
if (block_start < n) {
|
|
4569
|
-
const block_text = text.substring(block_start, n);
|
|
4570
|
-
if (block_text) blocks.push([block_text, block_start]);
|
|
4571
|
-
}
|
|
4572
|
-
return blocks;
|
|
4573
|
-
}
|
|
4574
|
-
function _merge_footnote_sections(blocks) {
|
|
4575
|
-
if (!blocks.length) return blocks;
|
|
4576
|
-
const merged = [];
|
|
4577
|
-
let i = 0;
|
|
4578
|
-
while (i < blocks.length) {
|
|
4579
|
-
const [block_text, block_offset] = blocks[i];
|
|
4580
|
-
const stripped = block_text.trimStart();
|
|
4581
|
-
const is_section_header = stripped.startsWith("## Footnotes") || stripped.startsWith("## Endnotes");
|
|
4582
|
-
if (!is_section_header) {
|
|
4583
|
-
merged.push([block_text, block_offset]);
|
|
4584
5011
|
i++;
|
|
4585
|
-
continue;
|
|
4586
5012
|
}
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
const next_stripped = next_text.trimStart();
|
|
4592
|
-
if (next_stripped.startsWith("[^fn-") || next_stripped.startsWith("[^en-")) {
|
|
4593
|
-
accumulated_text = `${accumulated_text}
|
|
4594
|
-
|
|
4595
|
-
${next_text}`;
|
|
4596
|
-
j++;
|
|
4597
|
-
} else {
|
|
4598
|
-
break;
|
|
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);
|
|
4599
5017
|
}
|
|
4600
5018
|
}
|
|
4601
|
-
merged.push([accumulated_text, block_offset]);
|
|
4602
|
-
i = j;
|
|
4603
5019
|
}
|
|
4604
|
-
|
|
5020
|
+
const paragraphs = findAllDescendants(doc.element, "w:p");
|
|
5021
|
+
for (const p of paragraphs) coalesceContainer(p);
|
|
5022
|
+
return count ? [`Adjacent identical runs coalesced: ${count}`] : [];
|
|
4605
5023
|
}
|
|
4606
|
-
function
|
|
4607
|
-
|
|
4608
|
-
const
|
|
4609
|
-
const
|
|
4610
|
-
let
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
page_starts.push(current_start);
|
|
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++;
|
|
5033
|
+
}
|
|
4617
5034
|
}
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
const added_size = block_size + (current_blocks.length > 0 ? 2 : 0);
|
|
4625
|
-
if (current_blocks.length > 0 && current_size + added_size > PAGE_TARGET_CHARS) {
|
|
4626
|
-
flush_current();
|
|
5035
|
+
}
|
|
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++;
|
|
4627
5041
|
}
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
5042
|
+
}
|
|
5043
|
+
return count ? [`rsid attributes: ${count} removed`] : [];
|
|
5044
|
+
}
|
|
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
|
+
}
|
|
4632
5055
|
}
|
|
4633
|
-
if (current_blocks.length === 0) current_start = block_offset;
|
|
4634
|
-
current_blocks.push(block_text);
|
|
4635
|
-
current_size += current_size > 0 ? added_size : block_size;
|
|
4636
5056
|
}
|
|
4637
|
-
|
|
4638
|
-
if (!pages.length) return [[""], [0]];
|
|
4639
|
-
return [pages, page_starts];
|
|
5057
|
+
return count ? [`Paragraph/text IDs: ${count} removed`] : [];
|
|
4640
5058
|
}
|
|
4641
|
-
function
|
|
4642
|
-
const
|
|
4643
|
-
|
|
4644
|
-
return
|
|
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`] : [];
|
|
4645
5063
|
}
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
b = temp;
|
|
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
|
+
}
|
|
5073
|
+
}
|
|
4657
5074
|
}
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
if (val < minInRow) minInRow = val;
|
|
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
|
+
}
|
|
4671
5087
|
}
|
|
4672
|
-
if (minInRow > maxDist) return maxDist + 1;
|
|
4673
|
-
row = newRow;
|
|
4674
5088
|
}
|
|
4675
|
-
return
|
|
5089
|
+
return count ? [`Hidden text runs: ${count} removed`] : [];
|
|
4676
5090
|
}
|
|
4677
|
-
function
|
|
4678
|
-
|
|
4679
|
-
const
|
|
4680
|
-
|
|
4681
|
-
|
|
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];
|
|
5096
|
+
}
|
|
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
|
+
}
|
|
4682
5104
|
}
|
|
4683
|
-
return
|
|
5105
|
+
return authors;
|
|
4684
5106
|
}
|
|
4685
|
-
function
|
|
4686
|
-
const
|
|
4687
|
-
const
|
|
4688
|
-
const
|
|
4689
|
-
const
|
|
4690
|
-
const
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
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);
|
|
4702
5137
|
}
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
5138
|
+
}
|
|
5139
|
+
const total = insEls.length + delEls.length;
|
|
5140
|
+
if (total) {
|
|
5141
|
+
return [`Tracked changes auto-accepted: ${total}`].concat(lines);
|
|
5142
|
+
}
|
|
5143
|
+
return [];
|
|
5144
|
+
}
|
|
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 });
|
|
5154
|
+
}
|
|
5155
|
+
return { total: comments.length, open: openCount, resolved: resolvedCount, comments };
|
|
5156
|
+
}
|
|
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);
|
|
4706
5171
|
}
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
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);
|
|
4716
5185
|
}
|
|
5186
|
+
} else {
|
|
5187
|
+
parent.removeChild(ref);
|
|
4717
5188
|
}
|
|
4718
|
-
let target = null;
|
|
4719
|
-
if (node.tagName === "w:fldSimple") {
|
|
4720
|
-
const instr = node.getAttribute("w:instr") || "";
|
|
4721
|
-
const parts = instr.trim().split(/\s+/);
|
|
4722
|
-
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
4723
|
-
} else if (node.tagName === "w:instrText") {
|
|
4724
|
-
const instr = node.textContent || "";
|
|
4725
|
-
const parts = instr.trim().split(/\s+/);
|
|
4726
|
-
if (parts.length > 1 && parts[0] === "REF") target = parts[1];
|
|
4727
|
-
}
|
|
4728
|
-
if (target) raw_references.push([target, short_text]);
|
|
4729
|
-
}
|
|
4730
|
-
}
|
|
4731
|
-
for (const [target, ref_text] of raw_references) {
|
|
4732
|
-
if (raw_anchors[target]) {
|
|
4733
|
-
raw_anchors[target].referenced_from.push(ref_text);
|
|
4734
5189
|
}
|
|
4735
5190
|
}
|
|
4736
|
-
const
|
|
4737
|
-
const
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
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);
|
|
4746
5205
|
}
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
5206
|
+
}
|
|
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);
|
|
4751
5226
|
}
|
|
4752
5227
|
}
|
|
4753
5228
|
}
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
"A",
|
|
4763
|
-
"An",
|
|
4764
|
-
"Any",
|
|
4765
|
-
"All",
|
|
4766
|
-
"Some",
|
|
4767
|
-
"No",
|
|
4768
|
-
"Every",
|
|
4769
|
-
"Each",
|
|
4770
|
-
"As",
|
|
4771
|
-
"In",
|
|
4772
|
-
"Of",
|
|
4773
|
-
"For",
|
|
4774
|
-
"To",
|
|
4775
|
-
"On",
|
|
4776
|
-
"By",
|
|
4777
|
-
"With"
|
|
4778
|
-
]);
|
|
4779
|
-
const all_cap_pattern = /\b[A-Z][a-zA-Z]*(?:\s+[A-Z][a-zA-Z]*)*\b/g;
|
|
4780
|
-
const all_caps = new Set(base_text.match(all_cap_pattern) || []);
|
|
4781
|
-
const valid_terms = new Set(Object.keys(definitions));
|
|
4782
|
-
const terms_by_first_letter = {};
|
|
4783
|
-
for (const term of valid_terms) {
|
|
4784
|
-
const fl = term[0].toLowerCase();
|
|
4785
|
-
if (!terms_by_first_letter[fl]) terms_by_first_letter[fl] = [];
|
|
4786
|
-
terms_by_first_letter[fl].push(term);
|
|
4787
|
-
}
|
|
4788
|
-
const candidates_by_term = {};
|
|
4789
|
-
for (const raw_candidate of all_caps) {
|
|
4790
|
-
let candidate = raw_candidate.trim();
|
|
4791
|
-
const words = candidate.split(/\s+/);
|
|
4792
|
-
while (words.length > 0) {
|
|
4793
|
-
const first = words[0];
|
|
4794
|
-
const title = first.charAt(0).toUpperCase() + first.slice(1).toLowerCase();
|
|
4795
|
-
if (stop_words.has(title)) words.shift();
|
|
4796
|
-
else break;
|
|
4797
|
-
}
|
|
4798
|
-
candidate = words.join(" ");
|
|
4799
|
-
if (candidate.length < 4) continue;
|
|
4800
|
-
if (valid_terms.has(candidate)) continue;
|
|
4801
|
-
const first_letter = candidate[0].toLowerCase();
|
|
4802
|
-
let candidate_terms = terms_by_first_letter[first_letter] || [];
|
|
4803
|
-
if (candidate.length > 5) {
|
|
4804
|
-
for (const [k, v] of Object.entries(terms_by_first_letter)) {
|
|
4805
|
-
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);
|
|
4806
5237
|
}
|
|
4807
5238
|
}
|
|
4808
|
-
for (const
|
|
4809
|
-
|
|
4810
|
-
if (candidate === term + "s" || candidate === term + "es") continue;
|
|
4811
|
-
if (term === candidate + "s" || term === candidate + "es") continue;
|
|
4812
|
-
const dist = boundedLevenshtein(candidate, term, 2);
|
|
4813
|
-
if (dist === 0 || dist > 2) continue;
|
|
4814
|
-
if (term.length <= 5) {
|
|
4815
|
-
if (dist > 1) continue;
|
|
4816
|
-
if (candidate[0].toLowerCase() !== term[0].toLowerCase()) continue;
|
|
4817
|
-
}
|
|
4818
|
-
if (!candidates_by_term[term]) candidates_by_term[term] = [];
|
|
4819
|
-
if (!candidates_by_term[term].includes(candidate)) candidates_by_term[term].push(candidate);
|
|
5239
|
+
for (const overrideEl of toRemove) {
|
|
5240
|
+
overrideEl.parentNode?.removeChild(overrideEl);
|
|
4820
5241
|
}
|
|
4821
5242
|
}
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
function diag_sort_key(msg) {
|
|
4828
|
-
if (msg.startsWith("[Error]")) return 0;
|
|
4829
|
-
if (msg.startsWith("[Warning]")) return 1;
|
|
4830
|
-
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
|
+
}
|
|
4831
5248
|
}
|
|
4832
|
-
diagnostics.sort((a, b) => {
|
|
4833
|
-
const keyA = diag_sort_key(a);
|
|
4834
|
-
const keyB = diag_sort_key(b);
|
|
4835
|
-
if (keyA !== keyB) return keyA - keyB;
|
|
4836
|
-
return a.localeCompare(b);
|
|
4837
|
-
});
|
|
4838
|
-
return [definitions, diagnostics, raw_anchors];
|
|
4839
5249
|
}
|
|
4840
|
-
function
|
|
4841
|
-
const
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
if (Object.keys(defs).length > 0) {
|
|
4851
|
-
has_content = true;
|
|
4852
|
-
lines.push("\n## Defined Terms");
|
|
4853
|
-
for (const [term, data] of Object.entries(defs)) {
|
|
4854
|
-
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);
|
|
4855
5260
|
}
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
lines.push("\n## Semantic Diagnostics");
|
|
4860
|
-
for (const diag of diagnostics) {
|
|
4861
|
-
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);
|
|
4862
5264
|
}
|
|
4863
5265
|
}
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
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);
|
|
4871
5276
|
}
|
|
4872
5277
|
}
|
|
4873
5278
|
}
|
|
4874
|
-
|
|
4875
|
-
return lines.join("\n");
|
|
4876
|
-
}
|
|
4877
|
-
return "";
|
|
4878
|
-
}
|
|
4879
|
-
|
|
4880
|
-
// src/ingest.ts
|
|
4881
|
-
async function extractTextFromBuffer(buffer, cleanView = false) {
|
|
4882
|
-
const doc = await DocumentObject.load(buffer);
|
|
4883
|
-
return _extractTextFromDoc(doc, cleanView);
|
|
5279
|
+
return original.size ? [`Track change authors replaced: ${Array.from(original).sort().join(", ")} \u2192 "${newAuthor}"`] : [];
|
|
4884
5280
|
}
|
|
4885
|
-
function
|
|
4886
|
-
|
|
4887
|
-
const
|
|
4888
|
-
const
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
if (full_text.length > 0) cursor += 2;
|
|
4895
|
-
full_text.push(part_text);
|
|
4896
|
-
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
|
+
}
|
|
4897
5290
|
}
|
|
4898
5291
|
}
|
|
4899
|
-
|
|
4900
|
-
if (includeAppendix) {
|
|
4901
|
-
const appendix = build_structural_appendix(doc, base_text);
|
|
4902
|
-
if (appendix) base_text += appendix;
|
|
4903
|
-
}
|
|
4904
|
-
if (return_paragraph_offsets) {
|
|
4905
|
-
return { text: base_text, paragraph_offsets };
|
|
4906
|
-
}
|
|
4907
|
-
return base_text;
|
|
5292
|
+
return count ? [`Track change timestamps: ${count} normalized`] : [];
|
|
4908
5293
|
}
|
|
4909
|
-
function
|
|
4910
|
-
const
|
|
4911
|
-
const
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
const sep = `---
|
|
4919
|
-
${header}`;
|
|
4920
|
-
blocks.push(sep);
|
|
4921
|
-
local_cursor += sep.length;
|
|
4922
|
-
is_first_block = false;
|
|
4923
|
-
}
|
|
4924
|
-
for (const item of iter_block_items(container)) {
|
|
4925
|
-
if (!is_first_block) local_cursor += 2;
|
|
4926
|
-
const block_start = local_cursor;
|
|
4927
|
-
if (item.constructor.name === "FootnoteItem") {
|
|
4928
|
-
const fn_text = _extract_blocks(item, comments_map, cleanView, block_start, paragraph_offsets);
|
|
4929
|
-
if (fn_text) {
|
|
4930
|
-
blocks.push(fn_text);
|
|
4931
|
-
local_cursor = block_start + fn_text.length;
|
|
4932
|
-
is_first_block = false;
|
|
4933
|
-
} else if (!is_first_block) {
|
|
4934
|
-
local_cursor -= 2;
|
|
4935
|
-
}
|
|
4936
|
-
} else if (item instanceof Paragraph) {
|
|
4937
|
-
let prefix = get_paragraph_prefix(item, style_cache, default_pstyle);
|
|
4938
|
-
if (is_first_para && container.constructor.name === "FootnoteItem") {
|
|
4939
|
-
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 = "";
|
|
4940
5303
|
}
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
if (
|
|
4945
|
-
|
|
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 = "";
|
|
4946
5310
|
}
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
blocks.push(table_text);
|
|
4954
|
-
local_cursor = block_start + table_text.length;
|
|
4955
|
-
is_first_block = false;
|
|
4956
|
-
} else if (!is_first_block) {
|
|
4957
|
-
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";
|
|
4958
5317
|
}
|
|
4959
|
-
|
|
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
|
+
});
|
|
4960
5340
|
}
|
|
4961
5341
|
}
|
|
4962
|
-
return
|
|
5342
|
+
return lines.length ? ["Metadata scrubbed:", ...lines.map((l) => ` ${l}`)] : [];
|
|
4963
5343
|
}
|
|
4964
|
-
function
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
const
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
const wrapper_prefix_len = !cleanView && ins ? 4 : !cleanView && del_node ? 4 : 0;
|
|
4977
|
-
let cell_cursor = row_start + wrapper_prefix_len;
|
|
4978
|
-
let first_cell = true;
|
|
4979
|
-
for (const cell of row.cells) {
|
|
4980
|
-
if (seen_cells.has(cell)) continue;
|
|
4981
|
-
seen_cells.add(cell);
|
|
4982
|
-
if (!first_cell) cell_cursor += 3;
|
|
4983
|
-
const cell_content = _extract_blocks(cell, comments_map, cleanView, cell_cursor, paragraph_offsets);
|
|
4984
|
-
cell_texts.push(cell_content);
|
|
4985
|
-
cell_cursor += cell_content.length;
|
|
4986
|
-
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
|
+
});
|
|
4987
5356
|
}
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
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);
|
|
4992
5370
|
}
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
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));
|
|
4996
5379
|
}
|
|
4997
|
-
return
|
|
5380
|
+
return [`Custom XML parts: ${customParts.length} removed`];
|
|
4998
5381
|
}
|
|
4999
|
-
function
|
|
5000
|
-
|
|
5001
|
-
const
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
const items = Array.from(iter_paragraph_content(paragraph));
|
|
5010
|
-
const is_heading = is_heading_paragraph(paragraph, style_cache, default_pstyle);
|
|
5011
|
-
const native_heading = is_native_heading(paragraph, style_cache, default_pstyle);
|
|
5012
|
-
let leading_strip_active = is_heading;
|
|
5013
|
-
for (let i = 0; i < items.length; i++) {
|
|
5014
|
-
const item = items[i];
|
|
5015
|
-
if (item instanceof Run) {
|
|
5016
|
-
const [prefix, suffix] = get_run_style_markers(item, native_heading);
|
|
5017
|
-
const text = get_run_text(item);
|
|
5018
|
-
if (cleanView && Object.keys(active_del).length > 0) continue;
|
|
5019
|
-
if (leading_strip_active) {
|
|
5020
|
-
if (!text || !text.trim()) continue;
|
|
5021
|
-
leading_strip_active = false;
|
|
5022
|
-
}
|
|
5023
|
-
const seg = apply_formatting_to_segments(text, prefix, suffix);
|
|
5024
|
-
if (seg) {
|
|
5025
|
-
const new_wrappers = cleanView ? ["", ""] : _get_wrappers(active_ins, active_del, active_comments, active_fmt);
|
|
5026
|
-
const new_style = [prefix, suffix];
|
|
5027
|
-
if (pending_text && new_wrappers[0] === current_wrappers[0] && new_wrappers[1] === current_wrappers[1]) {
|
|
5028
|
-
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])) {
|
|
5029
|
-
pending_text = pending_text.slice(0, -current_style[1].length) + seg.slice(new_style[0].length);
|
|
5030
|
-
} else {
|
|
5031
|
-
pending_text += seg;
|
|
5032
|
-
}
|
|
5033
|
-
current_style = new_style;
|
|
5034
|
-
} else {
|
|
5035
|
-
if (pending_text) parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5036
|
-
pending_text = seg;
|
|
5037
|
-
current_wrappers = new_wrappers;
|
|
5038
|
-
current_style = new_style;
|
|
5039
|
-
}
|
|
5040
|
-
if (!cleanView) {
|
|
5041
|
-
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;
|
|
5042
|
-
if (has_meta) {
|
|
5043
|
-
deferred_meta_states.push([{ ...active_ins }, { ...active_del }, new Set(active_comments), { ...active_fmt }]);
|
|
5044
|
-
}
|
|
5045
|
-
let should_defer = false;
|
|
5046
|
-
const is_redline = Object.keys(active_ins).length > 0 || Object.keys(active_del).length > 0 || Object.keys(active_fmt).length > 0;
|
|
5047
|
-
if (is_redline) {
|
|
5048
|
-
let j = i + 1;
|
|
5049
|
-
let next_is_redline = false;
|
|
5050
|
-
let temp_ins = Object.keys(active_ins).length;
|
|
5051
|
-
let temp_del = Object.keys(active_del).length;
|
|
5052
|
-
let temp_fmt = Object.keys(active_fmt).length;
|
|
5053
|
-
while (j < items.length) {
|
|
5054
|
-
const next_item = items[j];
|
|
5055
|
-
if (next_item instanceof Run) {
|
|
5056
|
-
if (!get_run_text(next_item)) {
|
|
5057
|
-
j++;
|
|
5058
|
-
continue;
|
|
5059
|
-
}
|
|
5060
|
-
if (temp_ins > 0 || temp_del > 0 || temp_fmt > 0) next_is_redline = true;
|
|
5061
|
-
break;
|
|
5062
|
-
} else {
|
|
5063
|
-
const ev = next_item;
|
|
5064
|
-
if (ev.type === "ins_start") temp_ins++;
|
|
5065
|
-
else if (ev.type === "ins_end") temp_ins = Math.max(0, temp_ins - 1);
|
|
5066
|
-
else if (ev.type === "del_start") temp_del++;
|
|
5067
|
-
else if (ev.type === "del_end") temp_del = Math.max(0, temp_del - 1);
|
|
5068
|
-
else if (ev.type === "fmt_start") temp_fmt++;
|
|
5069
|
-
else if (ev.type === "fmt_end") temp_fmt = Math.max(0, temp_fmt - 1);
|
|
5070
|
-
}
|
|
5071
|
-
j++;
|
|
5072
|
-
}
|
|
5073
|
-
if (next_is_redline) should_defer = true;
|
|
5074
|
-
}
|
|
5075
|
-
if (!should_defer && deferred_meta_states.length > 0) {
|
|
5076
|
-
const meta_block = _build_merged_meta_block(deferred_meta_states, comments_map);
|
|
5077
|
-
if (meta_block) {
|
|
5078
|
-
if (pending_text) {
|
|
5079
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5080
|
-
pending_text = "";
|
|
5081
|
-
current_wrappers = ["", ""];
|
|
5082
|
-
current_style = ["", ""];
|
|
5083
|
-
}
|
|
5084
|
-
parts.push(`{>>${meta_block}<<}`);
|
|
5085
|
-
}
|
|
5086
|
-
deferred_meta_states.length = 0;
|
|
5087
|
-
}
|
|
5088
|
-
}
|
|
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++;
|
|
5089
5392
|
}
|
|
5090
|
-
}
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5393
|
+
}
|
|
5394
|
+
}
|
|
5395
|
+
return count ? [`Image alt text: ${count} auto-generated descriptions removed`] : [];
|
|
5396
|
+
}
|
|
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;
|
|
5099
5408
|
}
|
|
5100
5409
|
}
|
|
5101
|
-
if (ev.type === "start") active_comments.add(ev.id);
|
|
5102
|
-
else if (ev.type === "end") active_comments.delete(ev.id);
|
|
5103
|
-
else if (ev.type === "ins_start") active_ins[ev.id] = ev;
|
|
5104
|
-
else if (ev.type === "ins_end") delete active_ins[ev.id];
|
|
5105
|
-
else if (ev.type === "del_start") active_del[ev.id] = ev;
|
|
5106
|
-
else if (ev.type === "del_end") delete active_del[ev.id];
|
|
5107
|
-
else if (ev.type === "fmt_start") active_fmt[ev.id] = ev;
|
|
5108
|
-
else if (ev.type === "fmt_end") delete active_fmt[ev.id];
|
|
5109
|
-
else if (ev.type === "footnote" || ev.type === "endnote") {
|
|
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
|
-
parts.push(`[^${ev.type === "footnote" ? "fn" : "en"}-${ev.id}]`);
|
|
5117
|
-
} else if (ev.type === "hyperlink_start") {
|
|
5118
|
-
if (pending_text) {
|
|
5119
|
-
parts.push(`${current_wrappers[0]}${pending_text}${current_wrappers[1]}`);
|
|
5120
|
-
pending_text = "";
|
|
5121
|
-
current_wrappers = ["", ""];
|
|
5122
|
-
current_style = ["", ""];
|
|
5123
|
-
}
|
|
5124
|
-
parts.push("[");
|
|
5125
|
-
} else if (ev.type === "hyperlink_end") {
|
|
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.date})`);
|
|
5133
|
-
} else if (ev.type === "xref_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 === "xref_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.id})`);
|
|
5149
|
-
} else if (ev.type === "bookmark") {
|
|
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(`{#${ev.id}}`);
|
|
5157
|
-
}
|
|
5158
5410
|
}
|
|
5159
5411
|
}
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5412
|
+
return warnings;
|
|
5413
|
+
}
|
|
5414
|
+
|
|
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;
|
|
5164
5425
|
}
|
|
5165
|
-
|
|
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;
|
|
5438
|
+
}
|
|
5439
|
+
return row[a.length] <= maxDist ? row[a.length] : maxDist + 1;
|
|
5166
5440
|
}
|
|
5167
|
-
function
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
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));
|
|
5446
|
+
}
|
|
5447
|
+
return text;
|
|
5172
5448
|
}
|
|
5173
|
-
function
|
|
5174
|
-
const
|
|
5175
|
-
const
|
|
5176
|
-
const
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
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());
|
|
5466
|
+
}
|
|
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
|
+
}
|
|
5192
5483
|
}
|
|
5193
5484
|
}
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
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];
|
|
5201
5494
|
}
|
|
5495
|
+
if (target) raw_references.push([target, short_text]);
|
|
5202
5496
|
}
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
seen_sigs.add(sig);
|
|
5208
|
-
}
|
|
5497
|
+
}
|
|
5498
|
+
for (const [target, ref_text] of raw_references) {
|
|
5499
|
+
if (raw_anchors[target]) {
|
|
5500
|
+
raw_anchors[target].referenced_from.push(ref_text);
|
|
5209
5501
|
}
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5502
|
+
}
|
|
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);
|
|
5215
5518
|
}
|
|
5216
5519
|
}
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5520
|
+
}
|
|
5521
|
+
for (const term of duplicates) {
|
|
5522
|
+
diagnostics.push(
|
|
5523
|
+
`[Error] Duplicate Definition: '${term}' is defined multiple times.`
|
|
5524
|
+
);
|
|
5525
|
+
}
|
|
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);
|
|
5223
5575
|
}
|
|
5224
5576
|
}
|
|
5225
|
-
const
|
|
5226
|
-
|
|
5227
|
-
|
|
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);
|
|
5228
5590
|
}
|
|
5229
5591
|
}
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
var _HEADING_PREFIX_RE = /^(#{1,6}) /;
|
|
5235
|
-
var _HEURISTIC_MIN_WORDS = 3;
|
|
5236
|
-
function extract_outline(doc, projected_body, body_pages, body_page_offsets, paragraph_offsets = null) {
|
|
5237
|
-
if (body_pages.length !== body_page_offsets.length) {
|
|
5238
|
-
throw new Error("body_pages and body_page_offsets length mismatch");
|
|
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}`);
|
|
5239
5596
|
}
|
|
5240
|
-
|
|
5241
|
-
|
|
5597
|
+
function diag_sort_key(msg) {
|
|
5598
|
+
if (msg.startsWith("[Error]")) return 0;
|
|
5599
|
+
if (msg.startsWith("[Warning]")) return 1;
|
|
5600
|
+
return 2;
|
|
5242
5601
|
}
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
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];
|
|
5609
|
+
}
|
|
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
|
+
);
|
|
5251
5627
|
}
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
const text = _heading_text(paragraph, comments_map);
|
|
5260
|
-
const style = _determine_heading_style(paragraph);
|
|
5261
|
-
const owned_end = _find_owned_end(
|
|
5262
|
-
block_records,
|
|
5263
|
-
heading_indices,
|
|
5264
|
-
h_pos,
|
|
5265
|
-
level
|
|
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."
|
|
5266
5635
|
);
|
|
5267
|
-
const owned_blocks = block_records.slice(rec_idx + 1, owned_end);
|
|
5268
|
-
const has_table = _direct_has_table(block_records, rec_idx + 1, owned_end);
|
|
5269
|
-
const footnote_ids = _collect_footnote_ids(owned_blocks);
|
|
5270
|
-
const page_num = _offset_to_page(rec.start_offset, body_page_offsets);
|
|
5271
|
-
nodes.push({ level, text, page: page_num, style, has_table, footnote_ids });
|
|
5272
5636
|
}
|
|
5273
|
-
return
|
|
5637
|
+
return warnings;
|
|
5274
5638
|
}
|
|
5275
|
-
function
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
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}`);
|
|
5658
|
+
}
|
|
5280
5659
|
}
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
let body_part = null;
|
|
5287
|
-
for (const part of parts) {
|
|
5288
|
-
if (part === doc) {
|
|
5289
|
-
body_part = part;
|
|
5290
|
-
break;
|
|
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.`);
|
|
5291
5665
|
}
|
|
5292
|
-
|
|
5666
|
+
}
|
|
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}`);
|
|
5672
|
+
}
|
|
5673
|
+
}
|
|
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 "";
|
|
5688
|
+
}
|
|
5689
|
+
|
|
5690
|
+
// src/ingest.ts
|
|
5691
|
+
async function extractTextFromBuffer(buffer, cleanView = false) {
|
|
5692
|
+
const doc = await DocumentObject.load(buffer);
|
|
5693
|
+
return _extractTextFromDoc(doc, cleanView);
|
|
5694
|
+
}
|
|
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
|
+
);
|
|
5293
5709
|
if (part_text) {
|
|
5294
|
-
if (
|
|
5295
|
-
|
|
5710
|
+
if (full_text.length > 0) cursor += 2;
|
|
5711
|
+
full_text.push(part_text);
|
|
5712
|
+
cursor += part_text.length;
|
|
5296
5713
|
}
|
|
5297
5714
|
}
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
if (body_start_offset > 0) body_start_offset += 2;
|
|
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;
|
|
5303
5719
|
}
|
|
5304
|
-
|
|
5305
|
-
|
|
5720
|
+
if (return_paragraph_offsets) {
|
|
5721
|
+
return { text: base_text, paragraph_offsets };
|
|
5722
|
+
}
|
|
5723
|
+
return base_text;
|
|
5724
|
+
}
|
|
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;
|
|
5306
5730
|
let is_first_block = true;
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
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;
|
|
5739
|
+
}
|
|
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(
|
|
5314
5745
|
item,
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
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;
|
|
5757
|
+
}
|
|
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]);
|
|
5774
|
+
}
|
|
5775
|
+
local_cursor = block_start + full_block.length;
|
|
5776
|
+
is_first_para = false;
|
|
5321
5777
|
is_first_block = false;
|
|
5322
5778
|
} else if (item instanceof Table) {
|
|
5323
|
-
const table_text = extract_table(
|
|
5324
|
-
const block_len = table_text ? table_text.length : 0;
|
|
5325
|
-
if (!is_first_block) cursor += 2;
|
|
5326
|
-
const table_start = cursor;
|
|
5327
|
-
records.push({
|
|
5779
|
+
const table_text = extract_table(
|
|
5328
5780
|
item,
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
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;
|
|
5337
5794
|
}
|
|
5338
5795
|
}
|
|
5339
|
-
return
|
|
5796
|
+
return blocks.join("\n\n");
|
|
5340
5797
|
}
|
|
5341
|
-
function
|
|
5342
|
-
const
|
|
5343
|
-
let cursor = table_start_offset;
|
|
5798
|
+
function extract_table(table, comments_map, cleanView, cursor, paragraph_offsets) {
|
|
5799
|
+
const rows_text = [];
|
|
5344
5800
|
let rows_processed = 0;
|
|
5801
|
+
let local_cursor = cursor;
|
|
5345
5802
|
for (const row of table.rows) {
|
|
5346
|
-
|
|
5803
|
+
const cell_texts = [];
|
|
5347
5804
|
const seen_cells = /* @__PURE__ */ new Set();
|
|
5348
|
-
|
|
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;
|
|
5349
5813
|
for (const cell of row.cells) {
|
|
5350
5814
|
if (seen_cells.has(cell)) continue;
|
|
5351
5815
|
seen_cells.add(cell);
|
|
5352
|
-
if (
|
|
5353
|
-
const
|
|
5816
|
+
if (!first_cell) cell_cursor += 3;
|
|
5817
|
+
const cell_content = _extract_blocks(
|
|
5354
5818
|
cell,
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5819
|
+
comments_map,
|
|
5820
|
+
cleanView,
|
|
5821
|
+
cell_cursor,
|
|
5822
|
+
paragraph_offsets
|
|
5358
5823
|
);
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5824
|
+
cell_texts.push(cell_content);
|
|
5825
|
+
cell_cursor += cell_content.length;
|
|
5826
|
+
first_cell = false;
|
|
5827
|
+
}
|
|
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")}--}`;
|
|
5362
5833
|
}
|
|
5834
|
+
rows_text.push(row_str);
|
|
5835
|
+
local_cursor = row_start + row_str.length;
|
|
5363
5836
|
rows_processed++;
|
|
5364
5837
|
}
|
|
5365
|
-
return
|
|
5838
|
+
return rows_text.join("\n");
|
|
5366
5839
|
}
|
|
5367
|
-
function
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
comments_map
|
|
5383
|
-
);
|
|
5384
|
-
if (nested_offset !== cursor) {
|
|
5385
|
-
if (_element_is_descendant(target_el, inner_item._element))
|
|
5386
|
-
return [nested_offset, true];
|
|
5387
|
-
}
|
|
5388
|
-
const table_text = extract_table(inner_item, comments_map, false, 0);
|
|
5389
|
-
cursor += table_text ? table_text.length : 0;
|
|
5390
|
-
}
|
|
5391
|
-
is_first_block = false;
|
|
5392
|
-
}
|
|
5393
|
-
return [cursor, false];
|
|
5394
|
-
}
|
|
5395
|
-
function _element_is_descendant(target_el, ancestor_el) {
|
|
5396
|
-
let cur = target_el.parentNode;
|
|
5397
|
-
while (cur) {
|
|
5398
|
-
if (cur === ancestor_el) return true;
|
|
5399
|
-
cur = cur.parentNode;
|
|
5400
|
-
}
|
|
5401
|
-
return false;
|
|
5402
|
-
}
|
|
5403
|
-
function _record_table_inner_blocks_lite(table, inherited_offset, records, comments_map) {
|
|
5404
|
-
const seen_cells = /* @__PURE__ */ new Set();
|
|
5405
|
-
for (const row of table.rows) {
|
|
5406
|
-
for (const cell of row.cells) {
|
|
5407
|
-
if (seen_cells.has(cell)) continue;
|
|
5408
|
-
seen_cells.add(cell);
|
|
5409
|
-
for (const inner_item of iter_block_items(cell)) {
|
|
5410
|
-
if (inner_item instanceof Paragraph) {
|
|
5411
|
-
const true_offset = _is_heading(inner_item) ? _compute_inner_block_offset(
|
|
5412
|
-
table,
|
|
5413
|
-
inner_item,
|
|
5414
|
-
inherited_offset,
|
|
5415
|
-
comments_map
|
|
5416
|
-
) : inherited_offset;
|
|
5417
|
-
records.push({
|
|
5418
|
-
item: inner_item,
|
|
5419
|
-
is_paragraph: true,
|
|
5420
|
-
is_table: false,
|
|
5421
|
-
start_offset: true_offset,
|
|
5422
|
-
projected_length: 0
|
|
5423
|
-
});
|
|
5424
|
-
} else if (inner_item instanceof Table) {
|
|
5425
|
-
records.push({
|
|
5426
|
-
item: inner_item,
|
|
5427
|
-
is_paragraph: false,
|
|
5428
|
-
is_table: true,
|
|
5429
|
-
start_offset: inherited_offset,
|
|
5430
|
-
projected_length: 0
|
|
5431
|
-
});
|
|
5432
|
-
_record_table_inner_blocks_lite(
|
|
5433
|
-
inner_item,
|
|
5434
|
-
inherited_offset,
|
|
5435
|
-
records,
|
|
5436
|
-
comments_map
|
|
5437
|
-
);
|
|
5438
|
-
}
|
|
5439
|
-
}
|
|
5440
|
-
}
|
|
5441
|
-
}
|
|
5442
|
-
}
|
|
5443
|
-
function _project_part(part, comments_map) {
|
|
5444
|
-
const blocks = [];
|
|
5445
|
-
const c_type = part.constructor.name;
|
|
5446
|
-
if (c_type === "NotesPart") {
|
|
5447
|
-
const header = part.note_type === "fn" ? "## Footnotes" : "## Endnotes";
|
|
5448
|
-
blocks.push(`---
|
|
5449
|
-
${header}`);
|
|
5450
|
-
}
|
|
5451
|
-
let is_first_para = true;
|
|
5452
|
-
for (const item of iter_block_items(part)) {
|
|
5453
|
-
if (item.constructor.name === "FootnoteItem") {
|
|
5454
|
-
const fn_text = _project_part(item, comments_map);
|
|
5455
|
-
if (fn_text) blocks.push(fn_text);
|
|
5456
|
-
} else if (item instanceof Paragraph) {
|
|
5457
|
-
let prefix = get_paragraph_prefix(item);
|
|
5458
|
-
if (is_first_para && c_type === "FootnoteItem")
|
|
5459
|
-
prefix = `[^${part.note_type}-${part.id}]: ${prefix}`;
|
|
5460
|
-
const p_text = build_paragraph_text(item, comments_map, false);
|
|
5461
|
-
blocks.push(prefix + p_text);
|
|
5462
|
-
is_first_para = false;
|
|
5463
|
-
} else if (item instanceof Table) {
|
|
5464
|
-
const table_text = extract_table(item, comments_map, false, 0);
|
|
5465
|
-
if (table_text) blocks.push(table_text);
|
|
5466
|
-
is_first_para = false;
|
|
5467
|
-
}
|
|
5468
|
-
}
|
|
5469
|
-
return blocks.join("\n\n");
|
|
5470
|
-
}
|
|
5471
|
-
function _is_heading(paragraph) {
|
|
5472
|
-
return _HEADING_PREFIX_RE.test(get_paragraph_prefix(paragraph));
|
|
5473
|
-
}
|
|
5474
|
-
function _heading_passes_quality_filter(paragraph, comments_map) {
|
|
5475
|
-
const style = _determine_heading_style(paragraph);
|
|
5476
|
-
if (style !== "(heuristic)") return true;
|
|
5477
|
-
const text = _heading_text(paragraph, comments_map);
|
|
5478
|
-
if (!text) return false;
|
|
5479
|
-
const word_count = (text.match(/\w+/g) || []).length;
|
|
5480
|
-
return word_count >= _HEURISTIC_MIN_WORDS;
|
|
5481
|
-
}
|
|
5482
|
-
function _heading_level(paragraph) {
|
|
5483
|
-
const match = _HEADING_PREFIX_RE.exec(get_paragraph_prefix(paragraph));
|
|
5484
|
-
return match ? Math.min(match[1].length, 6) : 1;
|
|
5485
|
-
}
|
|
5486
|
-
function _heading_text(paragraph, comments_map) {
|
|
5487
|
-
const p_text = build_paragraph_text(paragraph, comments_map, false);
|
|
5488
|
-
let cleaned = _strip_critic_markup(p_text);
|
|
5489
|
-
cleaned = _strip_inline_formatting(cleaned);
|
|
5490
|
-
return cleaned.trim();
|
|
5491
|
-
}
|
|
5492
|
-
function _strip_critic_markup(text) {
|
|
5493
|
-
if (!text) return "";
|
|
5494
|
-
text = text.replace(/\{--[\s\S]*?--\}/g, "");
|
|
5495
|
-
text = text.replace(/\{>>[\s\S]*?<<\}/g, "");
|
|
5496
|
-
text = text.replace(/\{\+\+([\s\S]*?)\+\+\}/g, "$1");
|
|
5497
|
-
text = text.replace(/\{==([\s\S]*?)==\}/g, "$1");
|
|
5498
|
-
return text;
|
|
5499
|
-
}
|
|
5500
|
-
function _strip_inline_formatting(text) {
|
|
5501
|
-
if (!text) return "";
|
|
5502
|
-
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
5503
|
-
text = text.replace(/__(.+?)__/g, "$1");
|
|
5504
|
-
text = text.replace(/(?<!\w)_(\S(?:.*?\S)?)_(?!\w)/g, "$1");
|
|
5505
|
-
return text;
|
|
5506
|
-
}
|
|
5507
|
-
function _determine_heading_style(paragraph) {
|
|
5508
|
-
const [style_cache, default_pstyle] = _get_style_cache(
|
|
5509
|
-
paragraph._parent.part || paragraph._parent
|
|
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
|
|
5510
5855
|
);
|
|
5511
|
-
const
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
let
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
outline_level = style_cache[style_id].outline_level;
|
|
5527
|
-
outline_level_from_style = true;
|
|
5528
|
-
}
|
|
5529
|
-
const style_name = style_id && style_cache && style_cache[style_id] ? style_cache[style_id].name : style_id;
|
|
5530
|
-
let normalized_style_name = style_name;
|
|
5531
|
-
if (normalized_style_name && typeof normalized_style_name === "string") {
|
|
5532
|
-
if (normalized_style_name.toLowerCase().startsWith("heading")) {
|
|
5533
|
-
normalized_style_name = normalized_style_name.replace(/^heading/i, "Heading");
|
|
5534
|
-
} else if (normalized_style_name.toLowerCase() === "title") {
|
|
5535
|
-
normalized_style_name = "Title";
|
|
5536
|
-
}
|
|
5537
|
-
}
|
|
5538
|
-
if (outline_level_from_style && outline_level !== null) {
|
|
5539
|
-
const is_heading_or_title = normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title");
|
|
5540
|
-
if (!is_heading_or_title) {
|
|
5541
|
-
outline_level = null;
|
|
5542
|
-
}
|
|
5543
|
-
}
|
|
5544
|
-
if (outline_level !== null && outline_level >= 0 && outline_level <= 8) {
|
|
5545
|
-
if (normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title")) {
|
|
5546
|
-
return normalized_style_name;
|
|
5547
|
-
}
|
|
5548
|
-
return "(outline_level)";
|
|
5549
|
-
}
|
|
5550
|
-
if (normalized_style_name && (normalized_style_name.startsWith("Heading") || normalized_style_name === "Title"))
|
|
5551
|
-
return normalized_style_name;
|
|
5552
|
-
if (normalized_style_name && /Heading[ ]?([1-6])(?![0-9])/.test(normalized_style_name))
|
|
5553
|
-
return normalized_style_name;
|
|
5554
|
-
return "(heuristic)";
|
|
5555
|
-
}
|
|
5556
|
-
function _find_owned_end(block_records, heading_indices, current_h_pos, current_level) {
|
|
5557
|
-
for (let next_h_pos = current_h_pos + 1; next_h_pos < heading_indices.length; next_h_pos++) {
|
|
5558
|
-
const next_idx = heading_indices[next_h_pos];
|
|
5559
|
-
if (_heading_level(block_records[next_idx].item) <= current_level)
|
|
5560
|
-
return next_idx;
|
|
5561
|
-
}
|
|
5562
|
-
return block_records.length;
|
|
5563
|
-
}
|
|
5564
|
-
function _collect_footnote_ids(owned_blocks) {
|
|
5565
|
-
const seen = /* @__PURE__ */ new Set();
|
|
5566
|
-
const ordered = [];
|
|
5567
|
-
for (const rec of owned_blocks) {
|
|
5568
|
-
if (!rec.is_paragraph) continue;
|
|
5569
|
-
for (const event of iter_paragraph_content(rec.item)) {
|
|
5570
|
-
if (!("type" in event)) continue;
|
|
5571
|
-
let fn_id = "";
|
|
5572
|
-
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
5573
|
-
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
5574
|
-
else continue;
|
|
5575
|
-
if (!seen.has(fn_id)) {
|
|
5576
|
-
seen.add(fn_id);
|
|
5577
|
-
ordered.push(fn_id);
|
|
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;
|
|
5578
5871
|
}
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
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++;
|
|
5608
5936
|
}
|
|
5609
|
-
|
|
5610
|
-
|
|
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;
|
|
5611
5956
|
}
|
|
5612
5957
|
}
|
|
5613
5958
|
}
|
|
5614
|
-
}
|
|
5615
|
-
}
|
|
5616
|
-
walk(doc);
|
|
5617
|
-
const heading_indices = [];
|
|
5618
|
-
for (let idx = 0; idx < paragraphs_and_tables.length; idx++) {
|
|
5619
|
-
const [kind, item] = paragraphs_and_tables[idx];
|
|
5620
|
-
if (kind !== "p") continue;
|
|
5621
|
-
let hasOffset = false;
|
|
5622
|
-
if (paragraph_offsets instanceof Map) {
|
|
5623
|
-
hasOffset = paragraph_offsets.has(item._element);
|
|
5624
5959
|
} else {
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
for (let next_h_pos = h_pos + 1; next_h_pos < heading_indices.length; next_h_pos++) {
|
|
5644
|
-
const next_idx = heading_indices[next_h_pos];
|
|
5645
|
-
const next_paragraph = paragraphs_and_tables[next_idx][1];
|
|
5646
|
-
if (_heading_level(next_paragraph) <= level) {
|
|
5647
|
-
owned_end = next_idx;
|
|
5648
|
-
break;
|
|
5649
|
-
}
|
|
5650
|
-
}
|
|
5651
|
-
if (owned_end === item_idx) {
|
|
5652
|
-
owned_end = paragraphs_and_tables.length;
|
|
5653
|
-
}
|
|
5654
|
-
const owned = paragraphs_and_tables.slice(item_idx + 1, owned_end);
|
|
5655
|
-
let has_table = false;
|
|
5656
|
-
for (const [kind2, item2] of owned) {
|
|
5657
|
-
if (kind2 === "p" && _is_heading(item2)) {
|
|
5658
|
-
break;
|
|
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
|
+
}
|
|
5659
5978
|
}
|
|
5660
|
-
if (
|
|
5661
|
-
|
|
5662
|
-
|
|
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 = ["", ""];
|
|
5995
|
+
}
|
|
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}}`);
|
|
5663
6047
|
}
|
|
5664
6048
|
}
|
|
5665
|
-
const footnote_ids = _collect_footnote_ids_fast(owned);
|
|
5666
|
-
let para_offset;
|
|
5667
|
-
if (paragraph_offsets instanceof Map) {
|
|
5668
|
-
para_offset = paragraph_offsets.get(paragraph._element);
|
|
5669
|
-
} else {
|
|
5670
|
-
para_offset = paragraph_offsets[paragraph._element];
|
|
5671
|
-
}
|
|
5672
|
-
let page_num = 1;
|
|
5673
|
-
if (para_offset !== void 0) {
|
|
5674
|
-
const [start_offset] = para_offset;
|
|
5675
|
-
page_num = _offset_to_page(start_offset, body_page_offsets);
|
|
5676
|
-
}
|
|
5677
|
-
nodes.push({
|
|
5678
|
-
level,
|
|
5679
|
-
text,
|
|
5680
|
-
page: page_num,
|
|
5681
|
-
style,
|
|
5682
|
-
has_table,
|
|
5683
|
-
footnote_ids
|
|
5684
|
-
});
|
|
5685
|
-
}
|
|
5686
|
-
return nodes;
|
|
5687
|
-
}
|
|
5688
|
-
function _heading_passes_quality_filter_fast(paragraph, projected_body, paragraph_offsets) {
|
|
5689
|
-
const style = _determine_heading_style(paragraph);
|
|
5690
|
-
if (style !== "(heuristic)") return true;
|
|
5691
|
-
const text = _heading_text_fast(paragraph, projected_body, paragraph_offsets);
|
|
5692
|
-
if (!text) return false;
|
|
5693
|
-
const words = text.match(/\w+/g) || [];
|
|
5694
|
-
return words.length >= _HEURISTIC_MIN_WORDS;
|
|
5695
|
-
}
|
|
5696
|
-
function _heading_text_fast(paragraph, projected_body, paragraph_offsets) {
|
|
5697
|
-
let offset;
|
|
5698
|
-
if (paragraph_offsets instanceof Map) {
|
|
5699
|
-
offset = paragraph_offsets.get(paragraph._element);
|
|
5700
|
-
} else {
|
|
5701
|
-
offset = paragraph_offsets[paragraph._element];
|
|
5702
6049
|
}
|
|
5703
|
-
if (
|
|
5704
|
-
|
|
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}<<}`);
|
|
5705
6058
|
}
|
|
5706
|
-
|
|
5707
|
-
const raw = projected_body.substring(start, start + length);
|
|
5708
|
-
let cleaned = _strip_critic_markup(raw);
|
|
5709
|
-
cleaned = _strip_inline_formatting(cleaned);
|
|
5710
|
-
cleaned = cleaned.replace(/^#+\s+/, "");
|
|
5711
|
-
return cleaned.trim();
|
|
6059
|
+
return parts.join("");
|
|
5712
6060
|
}
|
|
5713
|
-
function
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
for (const event of iter_paragraph_content(item)) {
|
|
5719
|
-
if (!("type" in event)) continue;
|
|
5720
|
-
let fn_id = "";
|
|
5721
|
-
if (event.type === "footnote") fn_id = `fn-${event.id}`;
|
|
5722
|
-
else if (event.type === "endnote") fn_id = `en-${event.id}`;
|
|
5723
|
-
else continue;
|
|
5724
|
-
if (!seen.has(fn_id)) {
|
|
5725
|
-
seen.add(fn_id);
|
|
5726
|
-
ordered.push(fn_id);
|
|
5727
|
-
}
|
|
5728
|
-
}
|
|
5729
|
-
}
|
|
5730
|
-
return ordered;
|
|
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 ["", ""];
|
|
5731
6066
|
}
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
}
|
|
5765
|
-
|
|
5766
|
-
}
|
|
5767
|
-
} 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")) {
|
|
5768
|
-
this.metadata_lines.push(line);
|
|
5769
|
-
} else if (lower.includes("hyperlink") || lower.includes("warning")) {
|
|
5770
|
-
this.warnings.push(line);
|
|
5771
|
-
} else {
|
|
5772
|
-
this.structural_lines.push(line);
|
|
5773
|
-
}
|
|
5774
|
-
}
|
|
5775
|
-
}
|
|
5776
|
-
render() {
|
|
5777
|
-
const sep = "\u2550".repeat(50);
|
|
5778
|
-
const lines = [sep, `Finalization Report: ${this.filename}`];
|
|
5779
|
-
const flags = [];
|
|
5780
|
-
if (this.mode === "keep-markup") flags.push("--keep-markup");
|
|
5781
|
-
if (this.author) flags.push(`--author "${this.author}"`);
|
|
5782
|
-
if (this.tracked_changes_accepted > 0) flags.push("--accept-all");
|
|
5783
|
-
if (flags.length > 0) lines.push(flags.join(" "));
|
|
5784
|
-
lines.push(sep);
|
|
5785
|
-
if (this.status === "blocked") {
|
|
5786
|
-
lines.push("");
|
|
5787
|
-
lines.push(`BLOCKED: ${this.blocked_reason}`);
|
|
5788
|
-
lines.push(sep);
|
|
5789
|
-
return lines.join("\n");
|
|
5790
|
-
}
|
|
5791
|
-
if (this.mode === "keep-markup" && (this.tracked_changes_kept > 0 || this.comments_kept > 0)) {
|
|
5792
|
-
lines.push("");
|
|
5793
|
-
lines.push("VISIBLE TO COUNTERPARTY");
|
|
5794
|
-
if (this.tracked_changes_kept > 0) lines.push(` Tracked changes: ${this.tracked_changes_kept}`);
|
|
5795
|
-
if (this.comments_kept > 0) {
|
|
5796
|
-
lines.push(` Open comments: ${this.comments_kept}`);
|
|
5797
|
-
for (const cl of this.kept_comment_lines) lines.push(` ${cl}`);
|
|
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);
|
|
5798
6101
|
}
|
|
5799
|
-
if (this.author) lines.push(` Author on all markup: "${this.author}"`);
|
|
5800
|
-
}
|
|
5801
|
-
if (this.change_lines.length > 0) {
|
|
5802
|
-
lines.push("");
|
|
5803
|
-
lines.push("TRACKED CHANGES");
|
|
5804
|
-
for (const cl of this.change_lines) lines.push(` ${cl}`);
|
|
5805
|
-
}
|
|
5806
|
-
if (this.removed_comment_lines.length > 0) {
|
|
5807
|
-
lines.push("");
|
|
5808
|
-
lines.push("COMMENTS (stripped)");
|
|
5809
|
-
for (const cl of this.removed_comment_lines) lines.push(` ${cl}`);
|
|
5810
6102
|
}
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
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
|
+
}
|
|
5815
6111
|
}
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
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
|
+
}
|
|
5820
6120
|
}
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
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
|
+
}
|
|
5825
6130
|
}
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
lines.push(`Result: CLEAN WITH WARNINGS (${this.warnings.length} warning${this.warnings.length > 1 ? "s" : ""})`);
|
|
5830
|
-
} else {
|
|
5831
|
-
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);
|
|
5832
6134
|
}
|
|
5833
|
-
lines.push(sep);
|
|
5834
|
-
return lines.join("\n");
|
|
5835
6135
|
}
|
|
5836
|
-
|
|
6136
|
+
return [...change_lines, ...comment_lines].join("\n");
|
|
6137
|
+
}
|
|
5837
6138
|
|
|
5838
|
-
// src/
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
if (tag === localName || tag.endsWith(":" + localName)) {
|
|
5845
|
-
result.push(all[i]);
|
|
5846
|
-
}
|
|
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");
|
|
5847
6145
|
}
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
function coalesce_runs(doc) {
|
|
5851
|
-
let count = 0;
|
|
5852
|
-
function areRunsIdentical(rPr1, rPr2) {
|
|
5853
|
-
const xml1 = rPr1 ? rPr1.toString() : "";
|
|
5854
|
-
const xml2 = rPr2 ? rPr2.toString() : "";
|
|
5855
|
-
return xml1 === xml2;
|
|
6146
|
+
if (paragraph_offsets) {
|
|
6147
|
+
return _extract_outline_fast(doc, projected_body, body_page_offsets, paragraph_offsets);
|
|
5856
6148
|
}
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
}
|
|
5866
|
-
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);
|
|
5867
6157
|
}
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
continue;
|
|
5908
|
-
}
|
|
5909
|
-
}
|
|
5910
|
-
}
|
|
5911
|
-
if (["w:ins", "w:del", "w:hyperlink", "w:sdt", "w:smartTag", "w:fldSimple", "w:sdtContent"].includes(curr.tagName)) {
|
|
5912
|
-
coalesceContainer(curr);
|
|
5913
|
-
}
|
|
5914
|
-
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;
|
|
5915
6197
|
}
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
if (
|
|
5919
|
-
|
|
5920
|
-
}
|
|
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;
|
|
5921
6202
|
}
|
|
5922
6203
|
}
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
}
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
const
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
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;
|
|
5937
6243
|
}
|
|
5938
6244
|
}
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
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++;
|
|
5944
6268
|
}
|
|
6269
|
+
rows_processed++;
|
|
5945
6270
|
}
|
|
5946
|
-
return
|
|
6271
|
+
return table_start_offset;
|
|
5947
6272
|
}
|
|
5948
|
-
function
|
|
5949
|
-
let
|
|
5950
|
-
|
|
5951
|
-
const
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
if (
|
|
5955
|
-
|
|
5956
|
-
|
|
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];
|
|
5957
6293
|
}
|
|
6294
|
+
const table_text = extract_table(inner_item, comments_map, false, 0);
|
|
6295
|
+
cursor += table_text ? table_text.length : 0;
|
|
5958
6296
|
}
|
|
6297
|
+
is_first_block = false;
|
|
5959
6298
|
}
|
|
5960
|
-
return
|
|
6299
|
+
return [cursor, false];
|
|
5961
6300
|
}
|
|
5962
|
-
function
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
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;
|
|
5966
6308
|
}
|
|
5967
|
-
function
|
|
5968
|
-
|
|
5969
|
-
for (const
|
|
5970
|
-
const
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
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
|
+
}
|
|
5975
6345
|
}
|
|
5976
6346
|
}
|
|
5977
6347
|
}
|
|
5978
|
-
return count ? [`Empty property elements: ${count} removed`] : [];
|
|
5979
6348
|
}
|
|
5980
|
-
function
|
|
5981
|
-
|
|
5982
|
-
const
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
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;
|
|
5990
6373
|
}
|
|
5991
6374
|
}
|
|
5992
|
-
return
|
|
6375
|
+
return blocks.join("\n\n");
|
|
5993
6376
|
}
|
|
5994
|
-
function
|
|
5995
|
-
|
|
5996
|
-
const del = findAllDescendants(doc.element, "w:del").length;
|
|
5997
|
-
const fmt = findAllDescendants(doc.element, "w:rPrChange").length + findAllDescendants(doc.element, "w:pPrChange").length + findAllDescendants(doc.element, "w:sectPrChange").length;
|
|
5998
|
-
return [ins, del, fmt];
|
|
6377
|
+
function _is_heading(paragraph) {
|
|
6378
|
+
return _HEADING_PREFIX_RE.test(get_paragraph_prefix(paragraph));
|
|
5999
6379
|
}
|
|
6000
|
-
function
|
|
6001
|
-
const
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
}
|
|
6008
|
-
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;
|
|
6009
6387
|
}
|
|
6010
|
-
function
|
|
6011
|
-
const
|
|
6012
|
-
|
|
6013
|
-
for (const t of ts) if (t.textContent) texts.push(t.textContent);
|
|
6014
|
-
const dts = findAllDescendants(el, "w:delText");
|
|
6015
|
-
for (const dt of dts) if (dt.textContent) texts.push(dt.textContent);
|
|
6016
|
-
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;
|
|
6017
6391
|
}
|
|
6018
|
-
function
|
|
6019
|
-
const
|
|
6020
|
-
|
|
6021
|
-
|
|
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();
|
|
6022
6397
|
}
|
|
6023
|
-
function
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
}
|
|
6031
|
-
for (const del of delEls) {
|
|
6032
|
-
const text = _getElementText(del).trim();
|
|
6033
|
-
if (text) lines.push(` Accepted deletion of: "${_truncate(text, 60)}"`);
|
|
6034
|
-
}
|
|
6035
|
-
const engine = new RedlineEngine(doc);
|
|
6036
|
-
engine.accept_all_revisions();
|
|
6037
|
-
for (const tag of ["w:rPrChange", "w:pPrChange", "w:sectPrChange"]) {
|
|
6038
|
-
for (const el of findAllDescendants(doc.element, tag)) {
|
|
6039
|
-
el.parentNode?.removeChild(el);
|
|
6040
|
-
}
|
|
6041
|
-
}
|
|
6042
|
-
const total = insEls.length + delEls.length;
|
|
6043
|
-
if (total) {
|
|
6044
|
-
return [`Tracked changes auto-accepted: ${total}`].concat(lines);
|
|
6045
|
-
}
|
|
6046
|
-
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;
|
|
6047
6405
|
}
|
|
6048
|
-
function
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
if (info.resolved) resolvedCount++;
|
|
6055
|
-
else openCount++;
|
|
6056
|
-
comments.push({ id: cId, ...info });
|
|
6057
|
-
}
|
|
6058
|
-
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;
|
|
6059
6412
|
}
|
|
6060
|
-
function
|
|
6061
|
-
const
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
const
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
const
|
|
6068
|
-
|
|
6069
|
-
cm.deleteComment(cId);
|
|
6070
|
-
}
|
|
6071
|
-
for (const tag of ["w:commentRangeStart", "w:commentRangeEnd"]) {
|
|
6072
|
-
for (const el of findAllDescendants(doc.element, tag)) {
|
|
6073
|
-
el.parentNode?.removeChild(el);
|
|
6074
|
-
}
|
|
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;
|
|
6075
6422
|
}
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
(c) => c.nodeType === 1 && c.tagName !== "w:rPr" && c.tagName !== "rPr"
|
|
6083
|
-
);
|
|
6084
|
-
if (nonRprChildren.length <= 1) {
|
|
6085
|
-
parent.parentNode?.removeChild(parent);
|
|
6086
|
-
} else {
|
|
6087
|
-
parent.removeChild(ref);
|
|
6088
|
-
}
|
|
6089
|
-
} else {
|
|
6090
|
-
parent.removeChild(ref);
|
|
6091
|
-
}
|
|
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);
|
|
6092
6429
|
}
|
|
6093
6430
|
}
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
}
|
|
6098
|
-
function eject_comment_parts(doc) {
|
|
6099
|
-
const pkg = doc.pkg;
|
|
6100
|
-
const comment_partnames = /* @__PURE__ */ new Set();
|
|
6101
|
-
for (const part of pkg.parts) {
|
|
6102
|
-
if (part.partname.toLowerCase().includes("comments")) {
|
|
6103
|
-
comment_partnames.add(part.partname);
|
|
6104
|
-
const withSlash = part.partname.startsWith("/") ? part.partname : "/" + part.partname;
|
|
6105
|
-
const withoutSlash = part.partname.startsWith("/") ? part.partname.substring(1) : part.partname;
|
|
6106
|
-
comment_partnames.add(withSlash);
|
|
6107
|
-
comment_partnames.add(withoutSlash);
|
|
6108
|
-
}
|
|
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;
|
|
6109
6434
|
}
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
if (target.toLowerCase().includes("comments")) {
|
|
6118
|
-
toRemove.push(rel);
|
|
6119
|
-
const sourcePath = part.partname.replace("/_rels/", "/").replace(".rels", "");
|
|
6120
|
-
const sourcePart = pkg.getPartByPath(sourcePath);
|
|
6121
|
-
if (sourcePart) {
|
|
6122
|
-
const relId = rel.getAttribute("Id");
|
|
6123
|
-
if (relId) sourcePart.rels.delete(relId);
|
|
6124
|
-
}
|
|
6125
|
-
}
|
|
6126
|
-
}
|
|
6127
|
-
for (const relEl of toRemove) {
|
|
6128
|
-
relEl.parentNode?.removeChild(relEl);
|
|
6129
|
-
}
|
|
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";
|
|
6130
6442
|
}
|
|
6131
6443
|
}
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
for (const override of overrides) {
|
|
6137
|
-
const partName = override.getAttribute("PartName") || "";
|
|
6138
|
-
if (comment_partnames.has(partName) || partName.toLowerCase().includes("comments")) {
|
|
6139
|
-
toRemove.push(override);
|
|
6140
|
-
}
|
|
6141
|
-
}
|
|
6142
|
-
for (const overrideEl of toRemove) {
|
|
6143
|
-
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;
|
|
6144
6448
|
}
|
|
6145
6449
|
}
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
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;
|
|
6150
6453
|
}
|
|
6454
|
+
return "(outline_level)";
|
|
6151
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)";
|
|
6152
6461
|
}
|
|
6153
|
-
function
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
for (const c of comments) {
|
|
6159
|
-
const author = c.getAttribute("w:author");
|
|
6160
|
-
if (author) {
|
|
6161
|
-
original.add(author);
|
|
6162
|
-
c.setAttribute("w:author", newAuthor);
|
|
6163
|
-
}
|
|
6164
|
-
if (c.hasAttribute("w:initials")) {
|
|
6165
|
-
const initials = newAuthor.split(" ").filter(Boolean).map((p) => p[0]).join("").toUpperCase();
|
|
6166
|
-
c.setAttribute("w:initials", initials);
|
|
6167
|
-
}
|
|
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;
|
|
6168
6467
|
}
|
|
6169
|
-
return
|
|
6468
|
+
return block_records.length;
|
|
6170
6469
|
}
|
|
6171
|
-
function
|
|
6172
|
-
const
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
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);
|
|
6179
6484
|
}
|
|
6180
6485
|
}
|
|
6181
6486
|
}
|
|
6182
|
-
return
|
|
6487
|
+
return ordered;
|
|
6183
6488
|
}
|
|
6184
|
-
function
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
for (
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
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
|
+
}
|
|
6192
6519
|
}
|
|
6193
6520
|
}
|
|
6194
6521
|
}
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
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;
|
|
6206
6555
|
}
|
|
6207
|
-
}
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
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;
|
|
6213
6565
|
}
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
if (c.textContent && parseInt(c.textContent) > 1) {
|
|
6218
|
-
lines.push(`Revision count: ${c.textContent} \u2192 1`);
|
|
6219
|
-
c.textContent = "1";
|
|
6566
|
+
if (kind2 === "t") {
|
|
6567
|
+
has_table = true;
|
|
6568
|
+
break;
|
|
6220
6569
|
}
|
|
6221
|
-
});
|
|
6222
|
-
}
|
|
6223
|
-
const appPart = doc.pkg.getPartByPath("docProps/app.xml");
|
|
6224
|
-
if (appPart) {
|
|
6225
|
-
const docEl = appPart._element;
|
|
6226
|
-
const intFields = ["TotalTime", "Words", "Characters", "Paragraphs", "Lines", "CharactersWithSpaces"];
|
|
6227
|
-
for (const f of intFields) {
|
|
6228
|
-
findDescendantsByLocalName(docEl, f).forEach((el) => {
|
|
6229
|
-
if (el.textContent && el.textContent !== "0") {
|
|
6230
|
-
if (f === "TotalTime") lines.push(`Total editing time: ${el.textContent} minutes`);
|
|
6231
|
-
el.textContent = "0";
|
|
6232
|
-
}
|
|
6233
|
-
});
|
|
6234
6570
|
}
|
|
6235
|
-
const
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
}
|
|
6242
|
-
});
|
|
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];
|
|
6243
6577
|
}
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
let modified = false;
|
|
6249
|
-
const epoch = "1970-01-01T00:00:00Z";
|
|
6250
|
-
const corePart = doc.pkg.getPartByPath("docProps/core.xml");
|
|
6251
|
-
if (corePart) {
|
|
6252
|
-
for (const tag of ["created", "modified", "lastPrinted"]) {
|
|
6253
|
-
findDescendantsByLocalName(corePart._element, tag).forEach((el) => {
|
|
6254
|
-
if (el.textContent && el.textContent !== epoch) {
|
|
6255
|
-
el.textContent = epoch;
|
|
6256
|
-
modified = true;
|
|
6257
|
-
}
|
|
6258
|
-
});
|
|
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);
|
|
6259
6582
|
}
|
|
6583
|
+
nodes.push({
|
|
6584
|
+
level,
|
|
6585
|
+
text,
|
|
6586
|
+
page: page_num,
|
|
6587
|
+
style,
|
|
6588
|
+
has_table,
|
|
6589
|
+
footnote_ids
|
|
6590
|
+
});
|
|
6260
6591
|
}
|
|
6261
|
-
return
|
|
6592
|
+
return nodes;
|
|
6262
6593
|
}
|
|
6263
|
-
function
|
|
6264
|
-
const
|
|
6265
|
-
if (
|
|
6266
|
-
const
|
|
6267
|
-
|
|
6268
|
-
const
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
}
|
|
6276
|
-
|
|
6277
|
-
if (rootRels) removeRelationsTo(rootRels);
|
|
6278
|
-
const docRels = doc.pkg.getOrCreateRelsPart(doc.part.partname);
|
|
6279
|
-
if (docRels) removeRelationsTo(docRels);
|
|
6280
|
-
for (const sdtPr of findAllDescendants(doc.element, "w:sdtPr")) {
|
|
6281
|
-
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];
|
|
6282
6608
|
}
|
|
6283
|
-
|
|
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();
|
|
6284
6618
|
}
|
|
6285
|
-
function
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
if (
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
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);
|
|
6295
6633
|
}
|
|
6296
6634
|
}
|
|
6297
6635
|
}
|
|
6298
|
-
return
|
|
6636
|
+
return ordered;
|
|
6299
6637
|
}
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
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);
|
|
6311
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);
|
|
6312
6679
|
}
|
|
6313
6680
|
}
|
|
6314
6681
|
}
|
|
6315
|
-
|
|
6316
|
-
|
|
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
|
+
};
|
|
6317
6743
|
|
|
6318
6744
|
// src/sanitize/core.ts
|
|
6319
6745
|
async function finalize_document(doc, options) {
|