wordinweb 0.3.4 → 0.3.6
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/{chunk-PZIJZDQ7.js → chunk-WPQ6GPG4.js} +971 -187
- package/dist/chunk-WPQ6GPG4.js.map +1 -0
- package/dist/collab.d.ts +2 -2
- package/dist/collab.js +1 -1
- package/dist/{index-DudQb7f0.d.ts → index-BWmOQ4GG.d.ts} +40 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-PZIJZDQ7.js.map +0 -1
|
@@ -2131,6 +2131,22 @@ function serializeXml(el26, declaration = false) {
|
|
|
2131
2131
|
write2(el26);
|
|
2132
2132
|
return out.join("");
|
|
2133
2133
|
}
|
|
2134
|
+
function isEmptyPlaceholder(e) {
|
|
2135
|
+
return !!e.omitWhenEmpty && Object.keys(e.attrs).length === 0 && e.text.length === 0 && e.children.length === 1 && localName(e.children[0].name) === "t" && e.children[0].children.length === 0 && e.children[0].text.length === 0 && Object.keys(e.children[0].attrs).every((key) => key === "xml:space" && e.children[0].attrs[key] === "preserve");
|
|
2136
|
+
}
|
|
2137
|
+
function sameXml(a, b) {
|
|
2138
|
+
if (a.name !== b.name) return false;
|
|
2139
|
+
const ak = Object.keys(a.attrs);
|
|
2140
|
+
if (ak.length !== Object.keys(b.attrs).length) return false;
|
|
2141
|
+
for (const k of ak) if (a.attrs[k] !== b.attrs[k]) return false;
|
|
2142
|
+
if (a.children.length === 0 && b.children.length === 0) return a.text === b.text;
|
|
2143
|
+
const ac = a.children.filter((c) => !isEmptyPlaceholder(c));
|
|
2144
|
+
const bc = b.children.filter((c) => !isEmptyPlaceholder(c));
|
|
2145
|
+
if (ac.length !== bc.length) return false;
|
|
2146
|
+
if (ac.length === 0) return (a.children.length > 0 ? "" : a.text) === (b.children.length > 0 ? "" : b.text);
|
|
2147
|
+
for (let i = 0; i < ac.length; i++) if (!sameXml(ac[i], bc[i])) return false;
|
|
2148
|
+
return true;
|
|
2149
|
+
}
|
|
2134
2150
|
function cloneXml(el26) {
|
|
2135
2151
|
return {
|
|
2136
2152
|
name: el26.name,
|
|
@@ -2176,6 +2192,12 @@ var Package = class _Package {
|
|
|
2176
2192
|
static from(data) {
|
|
2177
2193
|
return new _Package(data instanceof Uint8Array ? data : new Uint8Array(data));
|
|
2178
2194
|
}
|
|
2195
|
+
/** A package over an already-unzipped entry map (DocxDocument.clone). */
|
|
2196
|
+
static fromFiles(files) {
|
|
2197
|
+
const pkg = Object.create(_Package.prototype);
|
|
2198
|
+
pkg.files = files;
|
|
2199
|
+
return pkg;
|
|
2200
|
+
}
|
|
2179
2201
|
has(name) {
|
|
2180
2202
|
return this.normalize(name) in this.files;
|
|
2181
2203
|
}
|
|
@@ -2198,6 +2220,13 @@ var Package = class _Package {
|
|
|
2198
2220
|
return name.startsWith("/") ? name.slice(1) : name;
|
|
2199
2221
|
}
|
|
2200
2222
|
};
|
|
2223
|
+
function relativePartPath(fromPart, toPart) {
|
|
2224
|
+
const fromDir = fromPart.split("/").slice(0, -1);
|
|
2225
|
+
const to = toPart.split("/");
|
|
2226
|
+
let i = 0;
|
|
2227
|
+
while (i < fromDir.length && i < to.length - 1 && fromDir[i] === to[i]) i++;
|
|
2228
|
+
return [...fromDir.slice(i).map(() => ".."), ...to.slice(i)].join("/");
|
|
2229
|
+
}
|
|
2201
2230
|
function resolvePartPath(sourcePart, target) {
|
|
2202
2231
|
if (target.startsWith("/")) return target.slice(1);
|
|
2203
2232
|
const baseDir = sourcePart.includes("/") ? sourcePart.slice(0, sourcePart.lastIndexOf("/")) : "";
|
|
@@ -6355,6 +6384,10 @@ function parseCell(tc, ctx) {
|
|
|
6355
6384
|
if (tcW && attr(tcW, "type") === "dxa") {
|
|
6356
6385
|
const w = intAttr(tcW, "w");
|
|
6357
6386
|
if (w) props.width = twipsToPx(w);
|
|
6387
|
+
} else if (tcW && attr(tcW, "type") === "pct") {
|
|
6388
|
+
const raw = attr(tcW, "w");
|
|
6389
|
+
const num = raw !== void 0 ? parseFloat(raw) : NaN;
|
|
6390
|
+
if (Number.isFinite(num) && num > 0) props.widthPct = raw.trim().endsWith("%") ? num / 100 : num / 5e3;
|
|
6358
6391
|
} else if (tcW && attr(tcW, "type") === "auto") {
|
|
6359
6392
|
props.widthAuto = true;
|
|
6360
6393
|
}
|
|
@@ -8794,6 +8827,10 @@ var _DocxDocument = class _DocxDocument {
|
|
|
8794
8827
|
/** The header part roots, in package order. A watermark is authored into
|
|
8795
8828
|
* every one of them: Word paints it on every page, and a document with a
|
|
8796
8829
|
* first-page or even-page header has more than one header part. */
|
|
8830
|
+
/** The root of the header or footer part at `target`, if this document has one. */
|
|
8831
|
+
headerFooterRoot(target) {
|
|
8832
|
+
return this.hfParts.find((p) => p.target === target)?.root;
|
|
8833
|
+
}
|
|
8797
8834
|
headerRoots() {
|
|
8798
8835
|
return this.hfParts.filter((p) => p.isHeader).map((p) => p.root);
|
|
8799
8836
|
}
|
|
@@ -8957,6 +8994,22 @@ var _DocxDocument = class _DocxDocument {
|
|
|
8957
8994
|
this.saveJournal = null;
|
|
8958
8995
|
}
|
|
8959
8996
|
}
|
|
8997
|
+
/**
|
|
8998
|
+
* An independent copy: what `DocxDocument.load(doc.save())` gives, without
|
|
8999
|
+
* zipping the package and unzipping it again. compareDocuments starts from a
|
|
9000
|
+
* copy of the revised document, and the round trip through the zip was a
|
|
9001
|
+
* third of its time on a document that had not changed at all.
|
|
9002
|
+
*/
|
|
9003
|
+
clone() {
|
|
9004
|
+
const journal = [];
|
|
9005
|
+
this.saveJournal = journal;
|
|
9006
|
+
try {
|
|
9007
|
+
return new _DocxDocument(Package.fromFiles(this.buildPackageFiles()));
|
|
9008
|
+
} finally {
|
|
9009
|
+
for (let i = journal.length - 1; i >= 0; i--) journal[i]();
|
|
9010
|
+
this.saveJournal = null;
|
|
9011
|
+
}
|
|
9012
|
+
}
|
|
8960
9013
|
saveAsync() {
|
|
8961
9014
|
const journal = [];
|
|
8962
9015
|
this.saveJournal = journal;
|
|
@@ -9158,8 +9211,91 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9158
9211
|
return this.relsRoot;
|
|
9159
9212
|
}
|
|
9160
9213
|
/** Register an external hyperlink relationship and return its rId. */
|
|
9161
|
-
|
|
9162
|
-
|
|
9214
|
+
/**
|
|
9215
|
+
* Copy the part that `source`'s main-document relationship `relId` points
|
|
9216
|
+
* at into this package, with every part it references in turn (a chart's
|
|
9217
|
+
* embedded workbook, SmartArt's layout, style and colour parts) and their
|
|
9218
|
+
* content types, and add a relationship to it. Returns the new
|
|
9219
|
+
* relationship id, or null when the source has no such internal part.
|
|
9220
|
+
*
|
|
9221
|
+
* compareDocuments needs this for struck content: a removed picture keeps
|
|
9222
|
+
* its w:drawing, and without its image part the result's reject-all
|
|
9223
|
+
* brought the picture back as an empty frame.
|
|
9224
|
+
*/
|
|
9225
|
+
adoptRelFrom(source, relId, scope = {}) {
|
|
9226
|
+
const rel = source.relationshipsOf(scope.from).get(relId);
|
|
9227
|
+
if (!rel || rel.external) return null;
|
|
9228
|
+
const copied = /* @__PURE__ */ new Map();
|
|
9229
|
+
const sameBytes = (path2, bytes) => {
|
|
9230
|
+
const have = this.pkg.binary(path2);
|
|
9231
|
+
return !!have && have.length === bytes.length && have.every((b, i) => b === bytes[i]);
|
|
9232
|
+
};
|
|
9233
|
+
const copyPart = (from) => {
|
|
9234
|
+
const known = copied.get(from);
|
|
9235
|
+
if (known) return known;
|
|
9236
|
+
const bytes = source.pkg.binary(from);
|
|
9237
|
+
if (!bytes) return null;
|
|
9238
|
+
let to = from;
|
|
9239
|
+
for (let n = 2; this.pkg.has(to) && !sameBytes(to, bytes); n++) to = from.replace(/(\.[^./]+)?$/, `-${n}$1`);
|
|
9240
|
+
copied.set(from, to);
|
|
9241
|
+
if (this.pkg.has(to)) return to;
|
|
9242
|
+
this.pkg.raw()[to] = bytes;
|
|
9243
|
+
this.adoptContentType(source, from, to);
|
|
9244
|
+
const relsXml = source.pkg.text(relsPathFor(from));
|
|
9245
|
+
if (relsXml) {
|
|
9246
|
+
const root = parseXml(relsXml);
|
|
9247
|
+
for (const r of root.children) {
|
|
9248
|
+
if (r.attrs.TargetMode === "External" || !r.attrs.Target) continue;
|
|
9249
|
+
const sub = copyPart(resolvePartPath(from, r.attrs.Target));
|
|
9250
|
+
if (sub) r.attrs.Target = relativePartPath(to, sub);
|
|
9251
|
+
}
|
|
9252
|
+
this.pkg.raw()[relsPathFor(to)] = strToU82(serializeXml(root, true));
|
|
9253
|
+
}
|
|
9254
|
+
return to;
|
|
9255
|
+
};
|
|
9256
|
+
const part = copyPart(rel.target);
|
|
9257
|
+
if (!part) return null;
|
|
9258
|
+
const owner = this.relsOwnerOf(scope.into);
|
|
9259
|
+
const rels = owner.relsRoot;
|
|
9260
|
+
let maxId = 0;
|
|
9261
|
+
for (const r of rels.children) {
|
|
9262
|
+
const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
|
|
9263
|
+
if (m) maxId = Math.max(maxId, parseInt(m[1], 10));
|
|
9264
|
+
}
|
|
9265
|
+
const id = `rId${maxId + 1}`;
|
|
9266
|
+
rels.children.push({
|
|
9267
|
+
name: "Relationship",
|
|
9268
|
+
attrs: { Id: id, Type: rel.type, Target: relativePartPath(owner.partPath, part) },
|
|
9269
|
+
children: [],
|
|
9270
|
+
text: ""
|
|
9271
|
+
});
|
|
9272
|
+
owner.relationships.set(id, { id, type: rel.type, target: part, external: false });
|
|
9273
|
+
return id;
|
|
9274
|
+
}
|
|
9275
|
+
/** Give a part copied from `source` the content type it had there. */
|
|
9276
|
+
adoptContentType(source, from, to) {
|
|
9277
|
+
const types = this.contentTypesRoot;
|
|
9278
|
+
const theirs = source.contentTypesRoot;
|
|
9279
|
+
if (!types || !theirs) return;
|
|
9280
|
+
const override = theirs.children.find((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${from}`);
|
|
9281
|
+
const prefixEnd = types.name.indexOf(":") + 1;
|
|
9282
|
+
const prefix = prefixEnd > 0 ? types.name.slice(0, prefixEnd) : "";
|
|
9283
|
+
if (override) {
|
|
9284
|
+
if (!types.children.some((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${to}`)) {
|
|
9285
|
+
types.children.push({ name: `${prefix}Override`, attrs: { PartName: `/${to}`, ContentType: override.attrs.ContentType }, children: [], text: "" });
|
|
9286
|
+
}
|
|
9287
|
+
return;
|
|
9288
|
+
}
|
|
9289
|
+
const ext = from.slice(from.lastIndexOf(".") + 1).toLowerCase();
|
|
9290
|
+
const byExt = (root) => root.children.find((c) => localName(c.name) === "Default" && c.attrs.Extension?.toLowerCase() === ext);
|
|
9291
|
+
const fallback = byExt(theirs);
|
|
9292
|
+
if (fallback && !byExt(types)) {
|
|
9293
|
+
types.children.unshift({ name: `${prefix}Default`, attrs: { Extension: ext, ContentType: fallback.attrs.ContentType }, children: [], text: "" });
|
|
9294
|
+
}
|
|
9295
|
+
}
|
|
9296
|
+
addHyperlinkRel(url, ownerPart) {
|
|
9297
|
+
const owner = this.relsOwnerOf(ownerPart);
|
|
9298
|
+
const rels = owner.relsRoot;
|
|
9163
9299
|
let maxId = 0;
|
|
9164
9300
|
for (const r of rels.children) {
|
|
9165
9301
|
const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
|
|
@@ -9177,7 +9313,7 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9177
9313
|
children: [],
|
|
9178
9314
|
text: ""
|
|
9179
9315
|
});
|
|
9180
|
-
|
|
9316
|
+
owner.relationships.set(id, { id, type: "hyperlink", target: url, external: true });
|
|
9181
9317
|
return id;
|
|
9182
9318
|
}
|
|
9183
9319
|
/** Retarget an existing external relationship (hyperlink href edit). */
|
|
@@ -9189,6 +9325,23 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9189
9325
|
if (el26) el26.attrs["Target"] = url;
|
|
9190
9326
|
return true;
|
|
9191
9327
|
}
|
|
9328
|
+
/** The relationships owned by `part` (a header or footer part), or by the
|
|
9329
|
+
* document part when null; a header or footer gets a rels root on demand. */
|
|
9330
|
+
relsOwnerOf(part) {
|
|
9331
|
+
const owner = part ? this.hfParts.find((p) => p.target === part) : void 0;
|
|
9332
|
+
if (!owner) return { relsRoot: this.ensureRelsRoot(), relationships: this.documentRels, partPath: this.docPart };
|
|
9333
|
+
owner.relsRoot ?? (owner.relsRoot = {
|
|
9334
|
+
name: "Relationships",
|
|
9335
|
+
attrs: { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" },
|
|
9336
|
+
children: [],
|
|
9337
|
+
text: ""
|
|
9338
|
+
});
|
|
9339
|
+
return { relsRoot: owner.relsRoot, relationships: owner.rels, partPath: owner.target };
|
|
9340
|
+
}
|
|
9341
|
+
/** The relationships of `part` (a header or footer), or of the document part. */
|
|
9342
|
+
relationshipsOf(part) {
|
|
9343
|
+
return (part ? this.hfParts.find((p) => p.target === part)?.rels : void 0) ?? this.documentRels;
|
|
9344
|
+
}
|
|
9192
9345
|
/** The relationships of the part that holds `source`: a header or footer
|
|
9193
9346
|
* part when the element is inside one, else the document part. */
|
|
9194
9347
|
imageRelsOwner(source) {
|
|
@@ -10556,7 +10709,11 @@ var WORD_FONT_METRICS = {
|
|
|
10556
10709
|
// lineGap 99 / upem 2048 = 1.2734em (14.0pt at 11pt - phase23's sub-bullet
|
|
10557
10710
|
// lists run 14.0pt/line among 13.5pt Calibri text).
|
|
10558
10711
|
symbol: { asc: 1.005371, desc: 0.219727, gap: 0.04834 },
|
|
10559
|
-
|
|
10712
|
+
// A Symbol bullet LABEL (the engine's metrics face) sizes its line without
|
|
10713
|
+
// the lineGap: Word runs an 11pt bullet at 13.5pt among 11pt Times New
|
|
10714
|
+
// Roman (wild-termsheet-behinddoc) and a 10pt one at 12.25pt, both
|
|
10715
|
+
// asc + desc; with the gap they would be 14.0pt and 12.75pt.
|
|
10716
|
+
symbolmt: { asc: 1.005371, desc: 0.219727, gap: 0 },
|
|
10560
10717
|
"pingfang sc": { asc: 2.27, desc: 0.7627, gap: 0 },
|
|
10561
10718
|
"songti sc": { asc: 2.27, desc: 0.7627, gap: 0 },
|
|
10562
10719
|
"heiti sc": { asc: 2.27, desc: 0.7627, gap: 0 },
|
|
@@ -10583,13 +10740,19 @@ function quantizeQuarterPt(px) {
|
|
|
10583
10740
|
return Math.round(px / QUARTER_PT_PX) * QUARTER_PT_PX;
|
|
10584
10741
|
}
|
|
10585
10742
|
var FACE_SUFFIX = /\s+(bold italic|italic bold|bold|italic)$/i;
|
|
10743
|
+
var WORD_REPLACES = {
|
|
10744
|
+
"liberation serif": "Times New Roman",
|
|
10745
|
+
"liberation sans": "Arial",
|
|
10746
|
+
"liberation mono": "Courier New"
|
|
10747
|
+
};
|
|
10586
10748
|
function normalizeFamily(family, bold, italic) {
|
|
10587
10749
|
if (family === "\u5B8B\u4F53") family = "SimSun";
|
|
10750
|
+
const replaced = (f) => WORD_REPLACES[f.toLowerCase()] ?? f;
|
|
10588
10751
|
const m = FACE_SUFFIX.exec(family);
|
|
10589
|
-
if (!m) return { family, bold, italic };
|
|
10752
|
+
if (!m) return { family: replaced(family), bold, italic };
|
|
10590
10753
|
const suffix = m[1].toLowerCase();
|
|
10591
10754
|
return {
|
|
10592
|
-
family: family.slice(0, m.index).trim() || family,
|
|
10755
|
+
family: replaced(family.slice(0, m.index).trim() || family),
|
|
10593
10756
|
bold: bold || suffix.includes("bold"),
|
|
10594
10757
|
italic: italic || suffix.includes("italic")
|
|
10595
10758
|
};
|
|
@@ -18641,7 +18804,9 @@ var Engine = class {
|
|
|
18641
18804
|
if (tbl.props.layout === "fixed") return base;
|
|
18642
18805
|
const gridTotal = tbl.grid.reduce((a, b) => a + b, 0);
|
|
18643
18806
|
const target = base.reduce((a, b) => a + b, 0);
|
|
18644
|
-
const cellsDeclareWidths = tbl.rows.some(
|
|
18807
|
+
const cellsDeclareWidths = tbl.rows.some(
|
|
18808
|
+
(r) => r.cells.some((c) => c.props.width !== void 0 || c.props.widthPct !== void 0)
|
|
18809
|
+
);
|
|
18645
18810
|
const containsHyperlink = tbl.rows.some(
|
|
18646
18811
|
(row) => row.cells.some(
|
|
18647
18812
|
(cell) => cell.blocks.some(
|
|
@@ -19216,15 +19381,20 @@ var Engine = class {
|
|
|
19216
19381
|
while (out.length < nCols) out.push(bw(fallback));
|
|
19217
19382
|
return out;
|
|
19218
19383
|
};
|
|
19384
|
+
const colTopPads = (r) => {
|
|
19385
|
+
const fallback = this.cellMarginsOf(tbl, false).top ?? 0;
|
|
19386
|
+
return rows[r].cells.flatMap((c) => Array(Math.max(1, c.props.gridSpan)).fill(c.props.margins?.top ?? fallback));
|
|
19387
|
+
};
|
|
19219
19388
|
const boundary = (k) => {
|
|
19220
19389
|
if (k === 0) return Math.max(0, ...colWidths(0, "top", tb?.top));
|
|
19221
19390
|
if (k === rows.length)
|
|
19222
19391
|
return Math.max(0, ...colWidths(rows.length - 1, "bottom", tb?.bottom));
|
|
19223
19392
|
const above = colWidths(k - 1, "bottom", tb?.insideH);
|
|
19224
19393
|
const below = colWidths(k, "top", tb?.insideH);
|
|
19394
|
+
const topPads = colTopPads(k);
|
|
19225
19395
|
const n = Math.max(above.length, below.length);
|
|
19226
19396
|
let w = 0;
|
|
19227
|
-
for (let i = 0; i < n; i++) w = Math.max(w,
|
|
19397
|
+
for (let i = 0; i < n; i++) w = Math.max(w, below[i] ?? 0, (above[i] ?? 0) - (topPads[i] ?? 0));
|
|
19228
19398
|
return w;
|
|
19229
19399
|
};
|
|
19230
19400
|
return { top: boundary(ri), bottom: boundary(ri + 1) };
|
|
@@ -19800,7 +19970,14 @@ var Engine = class {
|
|
|
19800
19970
|
if (boundaryPara === void 0) continue;
|
|
19801
19971
|
const above = keptTops.filter((t) => paraOfTop(part.keep, t) === boundaryPara);
|
|
19802
19972
|
const below = restTops.filter((t) => paraOfTop(part.rest, t) === boundaryPara);
|
|
19803
|
-
if (
|
|
19973
|
+
if (above.length === 0) continue;
|
|
19974
|
+
if (above.length === 1 && below.length > 1) {
|
|
19975
|
+
const moved2 = part.keep.filter((it) => topOf(it) >= above[0] - 0.01);
|
|
19976
|
+
part.keep = part.keep.filter((it) => topOf(it) < above[0] - 0.01);
|
|
19977
|
+
part.rest = [...moved2, ...part.rest];
|
|
19978
|
+
continue;
|
|
19979
|
+
}
|
|
19980
|
+
if (below.length !== 1) continue;
|
|
19804
19981
|
const moveTop = above.length >= 3 ? above[above.length - 1] : above[0];
|
|
19805
19982
|
const moved = part.keep.filter((it) => topOf(it) >= moveTop - 0.01);
|
|
19806
19983
|
part.keep = part.keep.filter((it) => topOf(it) < moveTop - 0.01);
|
|
@@ -21423,6 +21600,8 @@ function renderPageShell(page, zoom, options) {
|
|
|
21423
21600
|
if (options.pageShadow !== false) {
|
|
21424
21601
|
el26.style.boxShadow = "var(--dxw-page-shadow, 0 1px 3px rgba(0,0,0,.28), 0 4px 14px rgba(0,0,0,.12))";
|
|
21425
21602
|
}
|
|
21603
|
+
el26.dataset.pageWidth = String(page.width);
|
|
21604
|
+
el26.dataset.pageHeight = String(page.height);
|
|
21426
21605
|
el26.dataset.bodyTop = String(page.bodyTop);
|
|
21427
21606
|
el26.dataset.bodyBottom = String(page.bodyBottom);
|
|
21428
21607
|
el26.dataset.columnBands = JSON.stringify(page.columnBands);
|
|
@@ -21739,9 +21918,21 @@ function trackPaint(work) {
|
|
|
21739
21918
|
async function paintsSettled() {
|
|
21740
21919
|
while (pendingPaints.size > 0) await Promise.allSettled([...pendingPaints]);
|
|
21741
21920
|
}
|
|
21742
|
-
function printStylesheet(pageWidthPx, pageHeightPx) {
|
|
21921
|
+
function printStylesheet(pageWidthPx, pageHeightPx, root) {
|
|
21922
|
+
const inches = (px) => `${Math.round(px / 96 * 1e3) / 1e3}in`;
|
|
21923
|
+
const sizes = /* @__PURE__ */ new Set();
|
|
21924
|
+
for (const el26 of root ? Array.from(root.querySelectorAll(".dxw-page[data-page-width]")) : []) {
|
|
21925
|
+
sizes.add(`${el26.dataset.pageWidth}x${el26.dataset.pageHeight}`);
|
|
21926
|
+
}
|
|
21927
|
+
const named = [...sizes].map((key) => {
|
|
21928
|
+
const [w, h] = key.split("x");
|
|
21929
|
+
const name = `dxw-${key.replace(/\./g, "_")}`;
|
|
21930
|
+
return `@page ${name} { size: ${inches(Number(w))} ${inches(Number(h))}; margin: 0; }
|
|
21931
|
+
.dxw-page[data-page-width="${w}"][data-page-height="${h}"] { page: ${name}; }`;
|
|
21932
|
+
});
|
|
21743
21933
|
return `
|
|
21744
|
-
@page { size: ${pageWidthPx
|
|
21934
|
+
@page { size: ${inches(pageWidthPx)} ${inches(pageHeightPx)}; margin: 0; }
|
|
21935
|
+
${named.join("\n ")}
|
|
21745
21936
|
html, body { margin: 0; padding: 0; }
|
|
21746
21937
|
.dxw-pages { display: block !important; padding: 0 !important; gap: 0 !important; }
|
|
21747
21938
|
.dxw-pages, .dxw-pages * {
|
|
@@ -21752,13 +21943,25 @@ function printStylesheet(pageWidthPx, pageHeightPx) {
|
|
|
21752
21943
|
.dxw-comment-card, .dxw-hf-marker { display: none !important; }
|
|
21753
21944
|
`;
|
|
21754
21945
|
}
|
|
21946
|
+
function pruneBlankSpans(root) {
|
|
21947
|
+
let removed = 0;
|
|
21948
|
+
for (const el26 of Array.from(root.querySelectorAll(".dxw-page span"))) {
|
|
21949
|
+
if (el26.childElementCount > 0 || el26.textContent?.trim()) continue;
|
|
21950
|
+
if (el26.style.textDecoration.includes("underline")) continue;
|
|
21951
|
+
el26.remove();
|
|
21952
|
+
removed++;
|
|
21953
|
+
}
|
|
21954
|
+
return removed;
|
|
21955
|
+
}
|
|
21755
21956
|
function buildPrintHtml(root, pageWidthPx, pageHeightPx) {
|
|
21756
21957
|
const head = [`<base href="${document.location.href}">`];
|
|
21757
21958
|
for (const node of Array.from(document.head.querySelectorAll("style, link[rel=stylesheet]"))) {
|
|
21758
21959
|
head.push(node.outerHTML);
|
|
21759
21960
|
}
|
|
21760
|
-
head.push(`<style>${printStylesheet(pageWidthPx, pageHeightPx)}</style>`);
|
|
21761
|
-
|
|
21961
|
+
head.push(`<style>${printStylesheet(pageWidthPx, pageHeightPx, root)}</style>`);
|
|
21962
|
+
const pages = root.cloneNode(true);
|
|
21963
|
+
pruneBlankSpans(pages);
|
|
21964
|
+
return `<!doctype html><html><head>${head.join("")}</head><body>${pages.outerHTML}</body></html>`;
|
|
21762
21965
|
}
|
|
21763
21966
|
function printPages(root, pageWidthPx, pageHeightPx) {
|
|
21764
21967
|
const iframe = document.createElement("iframe");
|
|
@@ -23805,7 +24008,9 @@ function revisionWrapper(sample, kind, content, meta) {
|
|
|
23805
24008
|
}
|
|
23806
24009
|
function markDeletedText(el26) {
|
|
23807
24010
|
const walk2 = (e) => {
|
|
24011
|
+
if (localName(e.name) === "txbxContent") return;
|
|
23808
24012
|
if (localName(e.name) === "t") e.name = e.name.replace(/t$/, "delText");
|
|
24013
|
+
else if (localName(e.name) === "instrText") e.name = e.name.replace(/instrText$/, "delInstrText");
|
|
23809
24014
|
for (const c of e.children) walk2(c);
|
|
23810
24015
|
};
|
|
23811
24016
|
walk2(el26);
|
|
@@ -24064,7 +24269,8 @@ function ensurePPrRPr(pEl) {
|
|
|
24064
24269
|
let rPr = pPr.children.find((c) => localName(c.name) === "rPr");
|
|
24065
24270
|
if (!rPr) {
|
|
24066
24271
|
rPr = el2(`${w}rPr`);
|
|
24067
|
-
pPr.children.
|
|
24272
|
+
const at = pPr.children.findIndex((c) => localName(c.name) === "sectPr" || localName(c.name) === "pPrChange");
|
|
24273
|
+
pPr.children.splice(at === -1 ? pPr.children.length : at, 0, rPr);
|
|
24068
24274
|
}
|
|
24069
24275
|
return rPr;
|
|
24070
24276
|
}
|
|
@@ -24224,6 +24430,8 @@ function convertDelTextToText(el26) {
|
|
|
24224
24430
|
const walk2 = (e) => {
|
|
24225
24431
|
if (localName(e.name) === "delText") {
|
|
24226
24432
|
e.name = e.name.replace(/delText$/, "t");
|
|
24433
|
+
} else if (localName(e.name) === "delInstrText") {
|
|
24434
|
+
e.name = e.name.replace(/delInstrText$/, "instrText");
|
|
24227
24435
|
}
|
|
24228
24436
|
for (const c of e.children) walk2(c);
|
|
24229
24437
|
};
|
|
@@ -24245,7 +24453,7 @@ function removeEl(doc, el26) {
|
|
|
24245
24453
|
parent.children.splice(idx, 1);
|
|
24246
24454
|
return true;
|
|
24247
24455
|
}
|
|
24248
|
-
var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo"];
|
|
24456
|
+
var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo", "fldSimple"];
|
|
24249
24457
|
function removeRevisionAndEmptyHost(doc, el26) {
|
|
24250
24458
|
const parent = doc.findParentOf(el26);
|
|
24251
24459
|
if (!removeEl(doc, el26)) return false;
|
|
@@ -24254,26 +24462,57 @@ function removeRevisionAndEmptyHost(doc, el26) {
|
|
|
24254
24462
|
}
|
|
24255
24463
|
return true;
|
|
24256
24464
|
}
|
|
24257
|
-
function joinWithNext(doc, pEl) {
|
|
24465
|
+
function joinWithNext(doc, pEl, gone) {
|
|
24258
24466
|
const parent = doc.findParentOf(pEl);
|
|
24259
24467
|
if (!parent) return false;
|
|
24260
24468
|
const idx = parent.children.indexOf(pEl);
|
|
24261
|
-
|
|
24262
|
-
|
|
24469
|
+
let at = idx + 1;
|
|
24470
|
+
while (at < parent.children.length && MARKERS.includes(localName(parent.children[at].name))) at++;
|
|
24471
|
+
const next = parent.children[at];
|
|
24472
|
+
if (!next || localName(next.name) !== "p") {
|
|
24473
|
+
const goes = gone === "del" ? ["pPr", "del", "moveFrom", "proofErr", ...MOVE_RANGES] : ["pPr", "ins", "moveTo", "proofErr", ...MOVE_RANGES];
|
|
24474
|
+
const stays = MARKERS.filter((m) => m !== "proofErr");
|
|
24475
|
+
const removable = (c) => goes.includes(localName(c.name)) || stays.includes(localName(c.name)) || isEmptyPlaceholder(c) || RUN_CONTAINERS.includes(localName(c.name)) && c.children.every(removable);
|
|
24476
|
+
if (!pEl.children.every(removable)) return false;
|
|
24477
|
+
parent.children.splice(idx, 1, ...pEl.children.filter((c) => stays.includes(localName(c.name))));
|
|
24478
|
+
dropEmptiedControl(doc, parent);
|
|
24479
|
+
return true;
|
|
24480
|
+
}
|
|
24481
|
+
const between = parent.children.slice(idx + 1, at);
|
|
24263
24482
|
const moved = next.children.filter((c) => localName(c.name) !== "pPr");
|
|
24264
|
-
pEl.children.push(...moved);
|
|
24265
|
-
parent.children.splice(idx + 1,
|
|
24483
|
+
pEl.children.push(...between, ...moved);
|
|
24484
|
+
parent.children.splice(idx + 1, at - idx);
|
|
24485
|
+
carrySectionBreak(pEl, next);
|
|
24266
24486
|
return true;
|
|
24267
24487
|
}
|
|
24488
|
+
var MARKERS = ["bookmarkStart", "bookmarkEnd", "commentRangeStart", "commentRangeEnd", "permStart", "permEnd", "proofErr"];
|
|
24489
|
+
function carrySectionBreak(pEl, next) {
|
|
24490
|
+
const pPrOf = (p) => p.children.find((c) => localName(c.name) === "pPr");
|
|
24491
|
+
const sectOf = (p) => pPrOf(p)?.children.find((c) => localName(c.name) === "sectPr");
|
|
24492
|
+
const mine = sectOf(pEl);
|
|
24493
|
+
const theirs = sectOf(next);
|
|
24494
|
+
if (!mine && !theirs) return;
|
|
24495
|
+
let pPr = pPrOf(pEl);
|
|
24496
|
+
if (!pPr) {
|
|
24497
|
+
pPr = { name: "w:pPr", attrs: {}, children: [], text: "" };
|
|
24498
|
+
pEl.children.unshift(pPr);
|
|
24499
|
+
}
|
|
24500
|
+
pPr.children = pPr.children.filter((c) => c !== mine);
|
|
24501
|
+
if (theirs) {
|
|
24502
|
+
const at = pPr.children.findIndex((c) => localName(c.name) === "pPrChange");
|
|
24503
|
+
pPr.children.splice(at === -1 ? pPr.children.length : at, 0, theirs);
|
|
24504
|
+
}
|
|
24505
|
+
if (pPr.children.length === 0) pEl.children.splice(pEl.children.indexOf(pPr), 1);
|
|
24506
|
+
}
|
|
24268
24507
|
function collectRevisions(doc) {
|
|
24269
24508
|
const out = [];
|
|
24270
24509
|
const walk2 = (el26) => {
|
|
24271
24510
|
const ln = localName(el26.name);
|
|
24272
24511
|
if (ln === "pPr") return;
|
|
24273
|
-
if (ln === "ins" || ln === "del") {
|
|
24512
|
+
if (ln === "ins" || ln === "del" || ln === "moveTo" || ln === "moveFrom") {
|
|
24274
24513
|
out.push({
|
|
24275
24514
|
el: el26,
|
|
24276
|
-
kind: ln === "ins" ? "insertion" : "deletion",
|
|
24515
|
+
kind: ln === "ins" || ln === "moveTo" ? "insertion" : "deletion",
|
|
24277
24516
|
author: attrByLocal(el26, "author")
|
|
24278
24517
|
});
|
|
24279
24518
|
}
|
|
@@ -24307,10 +24546,10 @@ function collectRevisions(doc) {
|
|
|
24307
24546
|
const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
|
|
24308
24547
|
for (const c of rPr?.children ?? []) {
|
|
24309
24548
|
const cn = localName(c.name);
|
|
24310
|
-
if (cn === "ins" || cn === "del") {
|
|
24549
|
+
if (cn === "ins" || cn === "del" || cn === "moveTo" || cn === "moveFrom") {
|
|
24311
24550
|
out.push({
|
|
24312
24551
|
el: c,
|
|
24313
|
-
kind: cn === "ins" ? "markInsertion" : "markDeletion",
|
|
24552
|
+
kind: cn === "ins" || cn === "moveTo" ? "markInsertion" : "markDeletion",
|
|
24314
24553
|
paragraph: el26,
|
|
24315
24554
|
author: attrByLocal(c, "author")
|
|
24316
24555
|
});
|
|
@@ -24332,7 +24571,7 @@ function applyAccept(doc, ref) {
|
|
|
24332
24571
|
case "markDeletion":
|
|
24333
24572
|
if (!ref.paragraph) return false;
|
|
24334
24573
|
removeMarkRevision(ref);
|
|
24335
|
-
return joinWithNext(doc, ref.paragraph);
|
|
24574
|
+
return joinWithNext(doc, ref.paragraph, "del");
|
|
24336
24575
|
case "rowInsertion":
|
|
24337
24576
|
return removeRowRevision(doc, ref);
|
|
24338
24577
|
case "rowDeletion":
|
|
@@ -24403,7 +24642,7 @@ function applyReject(doc, ref) {
|
|
|
24403
24642
|
case "markInsertion":
|
|
24404
24643
|
if (!ref.paragraph) return false;
|
|
24405
24644
|
removeMarkRevision(ref);
|
|
24406
|
-
return joinWithNext(doc, ref.paragraph);
|
|
24645
|
+
return joinWithNext(doc, ref.paragraph, "ins");
|
|
24407
24646
|
case "markDeletion":
|
|
24408
24647
|
return removeMarkRevision(ref);
|
|
24409
24648
|
case "rowInsertion":
|
|
@@ -24454,11 +24693,23 @@ function applyToAll(doc, apply, author) {
|
|
|
24454
24693
|
for (const part of held) notes.add(part);
|
|
24455
24694
|
}
|
|
24456
24695
|
if (n > 0) {
|
|
24696
|
+
dropSettledMoveRanges(doc);
|
|
24457
24697
|
doc.markNotePartsChanged([...notes]);
|
|
24458
24698
|
doc.refresh();
|
|
24459
24699
|
}
|
|
24460
24700
|
return n;
|
|
24461
24701
|
}
|
|
24702
|
+
var MOVE_RANGES = ["moveFromRangeStart", "moveFromRangeEnd", "moveToRangeStart", "moveToRangeEnd"];
|
|
24703
|
+
function dropSettledMoveRanges(doc) {
|
|
24704
|
+
const roots = doc.revisionRoots();
|
|
24705
|
+
const has = (el26) => ["moveFrom", "moveTo"].includes(localName(el26.name)) || el26.children.some(has);
|
|
24706
|
+
if (roots.some(has)) return;
|
|
24707
|
+
const strip = (el26) => {
|
|
24708
|
+
el26.children = el26.children.filter((c) => !MOVE_RANGES.includes(localName(c.name)));
|
|
24709
|
+
for (const c of el26.children) strip(c);
|
|
24710
|
+
};
|
|
24711
|
+
for (const root of roots) strip(root);
|
|
24712
|
+
}
|
|
24462
24713
|
function removeMarkRevision(ref) {
|
|
24463
24714
|
const pPr = ref.paragraph?.children.find((c) => localName(c.name) === "pPr");
|
|
24464
24715
|
const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
|
|
@@ -24490,9 +24741,24 @@ function removeRow(doc, ref) {
|
|
|
24490
24741
|
const idx = tbl.children.indexOf(ref.row);
|
|
24491
24742
|
if (idx === -1) return false;
|
|
24492
24743
|
tbl.children.splice(idx, 1);
|
|
24493
|
-
if (!tbl.children.some((c) => localName(c.name) === "tr"))
|
|
24744
|
+
if (!tbl.children.some((c) => localName(c.name) === "tr")) {
|
|
24745
|
+
const holder = doc.findParentOf(tbl);
|
|
24746
|
+
removeEl(doc, tbl);
|
|
24747
|
+
if (holder) dropEmptiedControl(doc, holder);
|
|
24748
|
+
}
|
|
24494
24749
|
return true;
|
|
24495
24750
|
}
|
|
24751
|
+
function dropEmptiedControl(doc, container) {
|
|
24752
|
+
const ln = localName(container.name);
|
|
24753
|
+
const control = ln === "sdtContent" ? doc.findParentOf(container) : ln === "customXml" ? container : void 0;
|
|
24754
|
+
if (!control) return;
|
|
24755
|
+
const left = container.children.filter((c) => localName(c.name) !== "customXmlPr");
|
|
24756
|
+
if (left.some((c) => !MARKERS.includes(localName(c.name)))) return;
|
|
24757
|
+
const holder = doc.findParentOf(control);
|
|
24758
|
+
if (!holder) return;
|
|
24759
|
+
holder.children.splice(holder.children.indexOf(control), 1, ...left.filter((c) => localName(c.name) !== "proofErr"));
|
|
24760
|
+
dropEmptiedControl(doc, holder);
|
|
24761
|
+
}
|
|
24496
24762
|
|
|
24497
24763
|
// ../core/src/edit/blocks.ts
|
|
24498
24764
|
function el3(name, attrs = {}, children3 = [], text = "") {
|
|
@@ -27792,7 +28058,7 @@ function sortTableRows(doc, tblEl, colIdx, order, compare, hasHeader = false) {
|
|
|
27792
28058
|
return tc ? cellText(tc).trim() : "";
|
|
27793
28059
|
};
|
|
27794
28060
|
const numeric2 = (s) => parseFloat(s.replace(/[^0-9.eE+-]/g, ""));
|
|
27795
|
-
const
|
|
28061
|
+
const compareRows2 = (a, b) => {
|
|
27796
28062
|
const ka = keyOf2(a);
|
|
27797
28063
|
const kb = keyOf2(b);
|
|
27798
28064
|
let d;
|
|
@@ -27808,7 +28074,7 @@ function sortTableRows(doc, tblEl, colIdx, order, compare, hasHeader = false) {
|
|
|
27808
28074
|
}
|
|
27809
28075
|
return order === "asc" ? d : -d;
|
|
27810
28076
|
};
|
|
27811
|
-
const sorted = [...body].sort(
|
|
28077
|
+
const sorted = [...body].sort(compareRows2);
|
|
27812
28078
|
if (sorted.every((tr, i) => tr === body[i])) return false;
|
|
27813
28079
|
const slots = body.map((tr) => tblEl.children.indexOf(tr));
|
|
27814
28080
|
sorted.forEach((tr, i) => {
|
|
@@ -42987,6 +43253,15 @@ var COMPARE_TUNING = {
|
|
|
42987
43253
|
shortParagraphTokens: 5,
|
|
42988
43254
|
/** Levenshtein ratio a short paragraph pair must reach instead of minDice. */
|
|
42989
43255
|
minShortRatio: 0.8,
|
|
43256
|
+
/**
|
|
43257
|
+
* Or the share of words a short pair has in common (Dice over words), which
|
|
43258
|
+
* a one-word edit in a short heading or list item keeps at half or more
|
|
43259
|
+
* ("Bullet one" / "Bullet updated", "Alignment and flow" / "Alignment and
|
|
43260
|
+
* flow as amended") while its character ratio falls under minShortRatio.
|
|
43261
|
+
* Below it, those came out struck and reinserted whole: 99% of the compare
|
|
43262
|
+
* benchmark's whole-paragraph misses were paragraphs under five words.
|
|
43263
|
+
*/
|
|
43264
|
+
minShortWordDice: 0.5,
|
|
42990
43265
|
/**
|
|
42991
43266
|
* ABSOLUTE floor for the coalescing pass: a matched run shorter than this,
|
|
42992
43267
|
* sitting between two changes in the inner word diff, is absorbed into the
|
|
@@ -43005,15 +43280,19 @@ var COMPARE_TUNING = {
|
|
|
43005
43280
|
* next to 1) while a rewritten clause becomes one (3 is small next to 7).
|
|
43006
43281
|
*
|
|
43007
43282
|
* Both numbers were settled against test/compare-corpus.test.ts; see the
|
|
43008
|
-
* calibration note there for what each fixture pins down.
|
|
43283
|
+
* calibration note there for what each fixture pins down. The ratio was 2
|
|
43284
|
+
* while spaces rode on words: "hardware," and "hardware " differed, so the
|
|
43285
|
+
* rewritten sentence of fixture 3 shared only three words. Matched exactly,
|
|
43286
|
+
* it shares four ("shortage of test hardware") beside seven changed, which
|
|
43287
|
+
* 1.5 absorbs (6 < 7) and 2 does not.
|
|
43009
43288
|
*/
|
|
43010
|
-
coalesceRatio:
|
|
43289
|
+
coalesceRatio: 1.5
|
|
43011
43290
|
};
|
|
43012
43291
|
function normalizeText(s) {
|
|
43013
43292
|
return s.replace(/\s+/g, " ").trim();
|
|
43014
43293
|
}
|
|
43015
43294
|
function wordTokens(s) {
|
|
43016
|
-
return s.match(
|
|
43295
|
+
return s.match(/[\p{L}\p{M}\p{N}]+(?:['\u2019][\p{L}\p{M}\p{N}]+)*|[^\s\p{L}\p{M}\p{N}]|\s+/gu) ?? [];
|
|
43017
43296
|
}
|
|
43018
43297
|
function similarityTokens(s) {
|
|
43019
43298
|
const n = normalizeText(s).toLowerCase();
|
|
@@ -43059,14 +43338,16 @@ function similarity(a, b) {
|
|
|
43059
43338
|
const nb = normalizeText(b).toLowerCase();
|
|
43060
43339
|
const longest = Math.max(na.length, nb.length);
|
|
43061
43340
|
if (longest === 0) return 1;
|
|
43062
|
-
|
|
43341
|
+
const ratio = 1 - levenshtein(na, nb) / longest;
|
|
43342
|
+
const words2 = dice(new Set(ta), new Set(tb));
|
|
43343
|
+
return Math.max(words2, ratio >= COMPARE_TUNING.minShortRatio ? ratio : 0);
|
|
43063
43344
|
}
|
|
43064
43345
|
return dice(bigrams(ta), bigrams(tb));
|
|
43065
43346
|
}
|
|
43066
43347
|
function similarityFloor(a, b) {
|
|
43067
43348
|
const la = similarityTokens(a).length;
|
|
43068
43349
|
const lb = similarityTokens(b).length;
|
|
43069
|
-
return isShortPair(la, lb) ? COMPARE_TUNING.
|
|
43350
|
+
return isShortPair(la, lb) ? COMPARE_TUNING.minShortWordDice : COMPARE_TUNING.minDice;
|
|
43070
43351
|
}
|
|
43071
43352
|
function isShortPair(la, lb) {
|
|
43072
43353
|
return Math.min(la, lb) < COMPARE_TUNING.shortParagraphTokens;
|
|
@@ -43077,6 +43358,25 @@ var PASSENGERS = /* @__PURE__ */ new Set(["proofErr", "bookmarkStart", "bookmark
|
|
|
43077
43358
|
function isPassenger(ln) {
|
|
43078
43359
|
return PASSENGERS.has(ln);
|
|
43079
43360
|
}
|
|
43361
|
+
var WORD_CHAR = /[\p{L}\p{M}\p{N}]/u;
|
|
43362
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["hyperlink", "smartTag", "fldSimple", "customXml", "dir", "bdo", "sdt"]);
|
|
43363
|
+
var CONTAINER_PROPS = /* @__PURE__ */ new Set(["pPr", "sdtPr", "sdtEndPr", "smartTagPr", "customXmlPr"]);
|
|
43364
|
+
function atomKey(atom, noteKey) {
|
|
43365
|
+
const ln = localName(atom.name);
|
|
43366
|
+
if (ln === "footnoteReference" || ln === "endnoteReference") {
|
|
43367
|
+
const { "w:id": _id, ...rest } = atom.attrs;
|
|
43368
|
+
return serializeXml({ ...atom, attrs: rest }) + (noteKey ? "\0" + noteKey(atom) : "");
|
|
43369
|
+
}
|
|
43370
|
+
const strip = (e) => localName(e.name) === "txbxContent" ? { ...e, children: [], text: "" } : { ...e, children: e.children.map(strip) };
|
|
43371
|
+
return serializeXml(hasTextBox(atom) ? strip(atom) : atom);
|
|
43372
|
+
}
|
|
43373
|
+
function hasTextBox(el26) {
|
|
43374
|
+
return localName(el26.name) === "txbxContent" || el26.children.some(hasTextBox);
|
|
43375
|
+
}
|
|
43376
|
+
function textBoxesIn(el26) {
|
|
43377
|
+
if (localName(el26.name) === "txbxContent") return [el26];
|
|
43378
|
+
return el26.children.flatMap(textBoxesIn);
|
|
43379
|
+
}
|
|
43080
43380
|
function readParagraph(pEl, options = {}) {
|
|
43081
43381
|
const keyOf2 = options.whitespace === false ? (word) => word.trimEnd() : (word) => word;
|
|
43082
43382
|
const tokens = [];
|
|
@@ -43089,31 +43389,80 @@ function readParagraph(pEl, options = {}) {
|
|
|
43089
43389
|
}
|
|
43090
43390
|
tokens.push(token);
|
|
43091
43391
|
};
|
|
43092
|
-
|
|
43093
|
-
|
|
43094
|
-
|
|
43095
|
-
if (
|
|
43096
|
-
|
|
43097
|
-
|
|
43098
|
-
|
|
43099
|
-
|
|
43100
|
-
|
|
43101
|
-
|
|
43102
|
-
|
|
43103
|
-
|
|
43104
|
-
|
|
43105
|
-
|
|
43106
|
-
|
|
43107
|
-
|
|
43108
|
-
|
|
43109
|
-
|
|
43110
|
-
|
|
43111
|
-
|
|
43392
|
+
let segment = [];
|
|
43393
|
+
let segmentPath = [];
|
|
43394
|
+
const flush = () => {
|
|
43395
|
+
if (segment.length === 0) return;
|
|
43396
|
+
const text = segment.map((p) => p.text).join("");
|
|
43397
|
+
const cuts = [];
|
|
43398
|
+
let base = 0;
|
|
43399
|
+
for (let i = 0; i + 1 < segment.length; i++) {
|
|
43400
|
+
base += segment[i].text.length;
|
|
43401
|
+
if (WORD_CHAR.test(text[base - 1] ?? "") && WORD_CHAR.test(text[base] ?? "") && runPropsKey(segment[i].run) !== runPropsKey(segment[i + 1].run)) {
|
|
43402
|
+
cuts.push(base);
|
|
43403
|
+
}
|
|
43404
|
+
}
|
|
43405
|
+
const words2 = [];
|
|
43406
|
+
let from = 0;
|
|
43407
|
+
for (const cut of [...cuts, text.length]) {
|
|
43408
|
+
words2.push(...wordTokens(text.slice(from, cut)));
|
|
43409
|
+
from = cut;
|
|
43410
|
+
}
|
|
43411
|
+
let at = 0;
|
|
43412
|
+
for (const word of words2) {
|
|
43413
|
+
const end = at + word.length;
|
|
43414
|
+
const pieces = [];
|
|
43415
|
+
let base2 = 0;
|
|
43416
|
+
for (const p of segment) {
|
|
43417
|
+
const from2 = Math.max(at, base2);
|
|
43418
|
+
const to = Math.min(end, base2 + p.text.length);
|
|
43419
|
+
if (from2 < to) pieces.push({ run: p.run, t: p.t, start: from2 - base2, end: to - base2, text: text.slice(from2, to) });
|
|
43420
|
+
base2 += p.text.length;
|
|
43421
|
+
}
|
|
43422
|
+
const first = pieces[0];
|
|
43423
|
+
push({ key: "w\0" + keyOf2(word), text: word, run: first.run, t: first.t, start: first.start, end: first.end, pieces, path: segmentPath });
|
|
43424
|
+
at = end;
|
|
43425
|
+
}
|
|
43426
|
+
segment = [];
|
|
43427
|
+
};
|
|
43428
|
+
const walk2 = (kids, path2) => {
|
|
43429
|
+
for (const cEl of kids) {
|
|
43430
|
+
const ln = localName(cEl.name);
|
|
43431
|
+
if (CONTAINER_PROPS.has(ln)) continue;
|
|
43432
|
+
if (PASSENGERS.has(ln)) {
|
|
43433
|
+
flush();
|
|
43434
|
+
pending.push(cEl);
|
|
43435
|
+
continue;
|
|
43436
|
+
}
|
|
43437
|
+
if (CONTAINERS.has(ln)) {
|
|
43438
|
+
const inner = ln === "sdt" ? cEl.children.find((c) => localName(c.name) === "sdtContent") : cEl;
|
|
43439
|
+
if (!inner) continue;
|
|
43440
|
+
flush();
|
|
43441
|
+
walk2(inner.children, [...path2, cEl]);
|
|
43442
|
+
flush();
|
|
43112
43443
|
continue;
|
|
43113
43444
|
}
|
|
43114
|
-
|
|
43445
|
+
if (ln !== "r") {
|
|
43446
|
+
flush();
|
|
43447
|
+
inlineDiffable = false;
|
|
43448
|
+
continue;
|
|
43449
|
+
}
|
|
43450
|
+
if (segment.length > 0 && segmentPath !== path2) flush();
|
|
43451
|
+
segmentPath = path2;
|
|
43452
|
+
for (const rc of cEl.children) {
|
|
43453
|
+
const rln = localName(rc.name);
|
|
43454
|
+
if (rln === "rPr") continue;
|
|
43455
|
+
if (rln === "t") {
|
|
43456
|
+
segment.push({ run: cEl, t: rc, text: rc.text });
|
|
43457
|
+
continue;
|
|
43458
|
+
}
|
|
43459
|
+
flush();
|
|
43460
|
+
push({ key: "a\0" + atomKey(rc, options.noteKey), text: "", run: cEl, atom: rc, path: path2 });
|
|
43461
|
+
}
|
|
43115
43462
|
}
|
|
43116
|
-
}
|
|
43463
|
+
};
|
|
43464
|
+
walk2(pEl.children, []);
|
|
43465
|
+
flush();
|
|
43117
43466
|
return { tokens, tail: pending, text: paragraphText3(pEl), inlineDiffable };
|
|
43118
43467
|
}
|
|
43119
43468
|
function paragraphText3(el26) {
|
|
@@ -43130,31 +43479,41 @@ function paragraphText3(el26) {
|
|
|
43130
43479
|
}
|
|
43131
43480
|
return out;
|
|
43132
43481
|
}
|
|
43133
|
-
function
|
|
43482
|
+
function sliceItems(tokens, from, to, deleted) {
|
|
43134
43483
|
const out = [];
|
|
43135
|
-
let
|
|
43136
|
-
|
|
43137
|
-
|
|
43138
|
-
if (
|
|
43139
|
-
const run = token.run;
|
|
43484
|
+
let open = null;
|
|
43485
|
+
let text = null;
|
|
43486
|
+
const runFor = (run, path2) => {
|
|
43487
|
+
if (open?.run === run) return open.el;
|
|
43140
43488
|
const rPr = run.children.find((c) => localName(c.name) === "rPr");
|
|
43141
|
-
const
|
|
43142
|
-
|
|
43143
|
-
|
|
43144
|
-
|
|
43145
|
-
|
|
43146
|
-
|
|
43489
|
+
const el26 = { name: run.name, attrs: { ...run.attrs }, children: rPr ? [cloneXml(rPr)] : [], text: "" };
|
|
43490
|
+
out.push({ el: el26, path: path2 });
|
|
43491
|
+
open = { run, el: el26 };
|
|
43492
|
+
text = null;
|
|
43493
|
+
return el26;
|
|
43494
|
+
};
|
|
43495
|
+
for (let i = from; i < to; i++) {
|
|
43496
|
+
const token = tokens[i];
|
|
43497
|
+
if (!deleted && token.lead) {
|
|
43498
|
+
out.push(...token.lead.map((el26) => ({ el: cloneXml(el26), path: token.path })));
|
|
43499
|
+
open = null;
|
|
43500
|
+
text = null;
|
|
43501
|
+
}
|
|
43502
|
+
if (token.atom) {
|
|
43503
|
+
runFor(token.run, token.path).children.push(cloneXml(token.atom));
|
|
43504
|
+
text = null;
|
|
43505
|
+
continue;
|
|
43506
|
+
}
|
|
43507
|
+
for (const piece of token.pieces ?? []) {
|
|
43508
|
+
const el26 = runFor(piece.run, token.path);
|
|
43509
|
+
if (text && text.t === piece.t) {
|
|
43510
|
+
text.el.text += piece.text;
|
|
43147
43511
|
continue;
|
|
43148
43512
|
}
|
|
43149
|
-
const
|
|
43150
|
-
|
|
43151
|
-
|
|
43152
|
-
text += tokens[i].text;
|
|
43153
|
-
i++;
|
|
43154
|
-
}
|
|
43155
|
-
children3.push(textElement2(t, text, deleted));
|
|
43513
|
+
const te = textElement2(piece.t, piece.text, deleted);
|
|
43514
|
+
el26.children.push(te);
|
|
43515
|
+
text = { t: piece.t, el: te };
|
|
43156
43516
|
}
|
|
43157
|
-
out.push({ name: run.name, attrs: { ...run.attrs }, children: children3, text: "" });
|
|
43158
43517
|
}
|
|
43159
43518
|
return out;
|
|
43160
43519
|
}
|
|
@@ -43214,6 +43573,7 @@ function lowestOccurrenceRegion(a, b, aLo, aHi, bLo, bHi) {
|
|
|
43214
43573
|
sawCommon = true;
|
|
43215
43574
|
if (candidates.length > MAX_CHAIN2) continue;
|
|
43216
43575
|
sawUsable = true;
|
|
43576
|
+
if (best !== void 0 && candidates.length > bestCost) continue;
|
|
43217
43577
|
for (const i of candidates) {
|
|
43218
43578
|
let aStart = i;
|
|
43219
43579
|
let bStart = j;
|
|
@@ -43226,12 +43586,12 @@ function lowestOccurrenceRegion(a, b, aLo, aHi, bLo, bHi) {
|
|
|
43226
43586
|
aEnd++;
|
|
43227
43587
|
}
|
|
43228
43588
|
const len = aEnd - aStart;
|
|
43229
|
-
let cost =
|
|
43589
|
+
let cost = candidates.length;
|
|
43230
43590
|
for (let k = aStart; k < aEnd; k++) {
|
|
43231
|
-
cost = Math.
|
|
43591
|
+
cost = Math.min(cost, positions.get(a[k])?.length ?? cost);
|
|
43232
43592
|
}
|
|
43233
43593
|
const offset = Math.abs((aStart - aLo) / (aSpan || 1) - (bStart - bLo) / (bSpan || 1));
|
|
43234
|
-
const better =
|
|
43594
|
+
const better = best !== void 0 && (len > best.len || cost < bestCost || len === best.len && cost === bestCost && offset < bestOffset);
|
|
43235
43595
|
if (best === void 0 || better) {
|
|
43236
43596
|
best = { aStart, bStart, len };
|
|
43237
43597
|
bestCost = cost;
|
|
@@ -43260,46 +43620,232 @@ function lcsFallback(a, b, aLo, aHi, bLo, bHi, out) {
|
|
|
43260
43620
|
|
|
43261
43621
|
// ../core/src/edit/compare/index.ts
|
|
43262
43622
|
function compareDocuments(original, revised, options) {
|
|
43263
|
-
const
|
|
43623
|
+
const note = options.onNote ?? (() => {
|
|
43624
|
+
});
|
|
43625
|
+
const accepted = (doc, which) => {
|
|
43626
|
+
if (collectRevisions(doc).length === 0) return doc;
|
|
43627
|
+
const copy = doc.clone();
|
|
43628
|
+
acceptAllRevisions(copy);
|
|
43629
|
+
note({ code: "existing-revisions-accepted", detail: `the ${which} document held tracked changes; it was compared as if they were accepted` });
|
|
43630
|
+
return copy;
|
|
43631
|
+
};
|
|
43632
|
+
const merged = accepted(revised, "revised");
|
|
43633
|
+
return compareInto(accepted(original, "original"), merged === revised ? revised.clone() : merged, options);
|
|
43634
|
+
}
|
|
43635
|
+
function compareInto(original, merged, options) {
|
|
43636
|
+
let issued = 0;
|
|
43264
43637
|
const ctx = {
|
|
43265
43638
|
doc: merged,
|
|
43266
43639
|
original,
|
|
43267
43640
|
meta: {
|
|
43268
43641
|
author: options.author,
|
|
43269
43642
|
date: options.date,
|
|
43270
|
-
nextId: () =>
|
|
43643
|
+
nextId: () => {
|
|
43644
|
+
issued++;
|
|
43645
|
+
return merged.nextRevisionId();
|
|
43646
|
+
}
|
|
43271
43647
|
},
|
|
43272
43648
|
formatting: options.formatting !== false,
|
|
43273
43649
|
whitespace: options.whitespace !== false,
|
|
43274
43650
|
note: options.onNote ?? (() => {
|
|
43275
|
-
})
|
|
43651
|
+
}),
|
|
43652
|
+
scope: { from: null, into: null },
|
|
43653
|
+
noteIds: { footnote: /* @__PURE__ */ new Map(), endnote: /* @__PURE__ */ new Map() }
|
|
43276
43654
|
};
|
|
43277
43655
|
const originalBody = child(original.docRoot, "body");
|
|
43278
43656
|
const mergedBody = child(merged.docRoot, "body");
|
|
43279
43657
|
if (!originalBody || !mergedBody) throw new Error("document.xml has no w:body");
|
|
43280
|
-
reportUncomparedParts(original,
|
|
43658
|
+
reportUncomparedParts(original, merged, ctx);
|
|
43659
|
+
const headerFooterPairs = pairHeadersFooters(original, merged);
|
|
43660
|
+
compareNotes("footnote", original, merged, ctx);
|
|
43661
|
+
compareNotes("endnote", original, merged, ctx);
|
|
43281
43662
|
const tail = mergedBody.children.filter((c) => localName(c.name) === "sectPr");
|
|
43282
43663
|
const entries = alignBlocks(blockList(originalBody, "sectPr"), blockList(mergedBody, "sectPr"), ctx);
|
|
43283
43664
|
applyParagraphMarks(entries, ctx);
|
|
43284
43665
|
mergedBody.children = [...entries.map((e) => e.el), ...tail];
|
|
43285
|
-
merged
|
|
43666
|
+
giveStruckReferencesTheirOwnNotes("footnote", merged, ctx);
|
|
43667
|
+
giveStruckReferencesTheirOwnNotes("endnote", merged, ctx);
|
|
43668
|
+
for (const [from, into] of headerFooterPairs) {
|
|
43669
|
+
const oRoot = original.headerFooterRoot(from);
|
|
43670
|
+
const mRoot = merged.headerFooterRoot(into);
|
|
43671
|
+
if (oRoot && mRoot) compareContainer(oRoot, mRoot, { ...ctx, scope: { from, into } });
|
|
43672
|
+
}
|
|
43673
|
+
if (issued > 0) merged.refresh();
|
|
43286
43674
|
return merged;
|
|
43287
43675
|
}
|
|
43676
|
+
function compareContainer(oRoot, mRoot, ctx) {
|
|
43677
|
+
const o = blockList(oRoot);
|
|
43678
|
+
const m = blockList(mRoot);
|
|
43679
|
+
if (o.length === m.length && o.every((el26, i) => sameXml(el26, m[i]))) return;
|
|
43680
|
+
const entries = alignBlocks(o, m, ctx);
|
|
43681
|
+
applyParagraphMarks(entries, ctx);
|
|
43682
|
+
const blocks = new Set(m);
|
|
43683
|
+
mRoot.children = [...mRoot.children.filter((c) => !blocks.has(c) && !["p", "tbl", "sdt"].includes(localName(c.name))), ...entries.map((e) => e.el)];
|
|
43684
|
+
}
|
|
43685
|
+
function pairHeadersFooters(original, merged) {
|
|
43686
|
+
const refs = (doc) => {
|
|
43687
|
+
const out = /* @__PURE__ */ new Map();
|
|
43688
|
+
const body = child(doc.docRoot, "body");
|
|
43689
|
+
const sects = (body?.children ?? []).flatMap(
|
|
43690
|
+
(c) => localName(c.name) === "sectPr" ? [c] : localName(c.name) === "p" ? [child(child(c, "pPr"), "sectPr")].filter((x) => !!x) : []
|
|
43691
|
+
);
|
|
43692
|
+
sects.forEach((sp, i) => {
|
|
43693
|
+
for (const ref of sp.children) {
|
|
43694
|
+
const ln = localName(ref.name);
|
|
43695
|
+
if (ln !== "headerReference" && ln !== "footerReference") continue;
|
|
43696
|
+
const target = doc.documentRels.get(ref.attrs["r:id"])?.target;
|
|
43697
|
+
if (target) out.set(`${i}:${ln}:${ref.attrs["w:type"] ?? "default"}`, target);
|
|
43698
|
+
}
|
|
43699
|
+
});
|
|
43700
|
+
return out;
|
|
43701
|
+
};
|
|
43702
|
+
const mine = refs(merged);
|
|
43703
|
+
const pairs = [];
|
|
43704
|
+
const done = /* @__PURE__ */ new Set();
|
|
43705
|
+
for (const [key, from] of refs(original)) {
|
|
43706
|
+
const into = mine.get(key);
|
|
43707
|
+
if (!into || done.has(into)) continue;
|
|
43708
|
+
done.add(into);
|
|
43709
|
+
pairs.push([from, into]);
|
|
43710
|
+
}
|
|
43711
|
+
return pairs;
|
|
43712
|
+
}
|
|
43713
|
+
function compareNotes(kind, original, merged, ctx) {
|
|
43714
|
+
const tree = (doc, create = false) => kind === "footnote" ? doc.footnotesTree(create) : doc.endnotesTree(create);
|
|
43715
|
+
const oRoot = tree(original);
|
|
43716
|
+
const mRoot = tree(merged);
|
|
43717
|
+
if (!oRoot && !mRoot) return;
|
|
43718
|
+
const refName = `${kind}Reference`;
|
|
43719
|
+
const inOrder = (doc, root) => {
|
|
43720
|
+
if (!root) return [];
|
|
43721
|
+
const byId = new Map(
|
|
43722
|
+
root.children.filter((c) => localName(c.name) === kind && !c.attrs["w:type"]).map((c) => [c.attrs["w:id"], c])
|
|
43723
|
+
);
|
|
43724
|
+
const out = [];
|
|
43725
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43726
|
+
const walk2 = (e) => {
|
|
43727
|
+
if (localName(e.name) === refName) {
|
|
43728
|
+
const id = e.attrs["w:id"];
|
|
43729
|
+
const note = byId.get(id);
|
|
43730
|
+
if (note && !seen.has(id)) {
|
|
43731
|
+
seen.add(id);
|
|
43732
|
+
out.push(note);
|
|
43733
|
+
}
|
|
43734
|
+
}
|
|
43735
|
+
for (const c of e.children) walk2(c);
|
|
43736
|
+
};
|
|
43737
|
+
walk2(doc.docRoot);
|
|
43738
|
+
return out;
|
|
43739
|
+
};
|
|
43740
|
+
const oNotes = inOrder(original, oRoot);
|
|
43741
|
+
const rNotes = inOrder(merged, mRoot);
|
|
43742
|
+
const text = (n) => normalizeText(paragraphText3(n));
|
|
43743
|
+
const pairs = [];
|
|
43744
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
43745
|
+
rNotes.forEach((n, j) => {
|
|
43746
|
+
const list = waiting.get(text(n));
|
|
43747
|
+
if (list) list.push(j);
|
|
43748
|
+
else waiting.set(text(n), [j]);
|
|
43749
|
+
});
|
|
43750
|
+
oNotes.forEach((n, i) => {
|
|
43751
|
+
const j = waiting.get(text(n))?.shift();
|
|
43752
|
+
if (j !== void 0) pairs.push([i, j]);
|
|
43753
|
+
});
|
|
43754
|
+
const exactO = new Set(pairs.map((p) => p[0]));
|
|
43755
|
+
const exactR = new Set(pairs.map((p) => p[1]));
|
|
43756
|
+
const oRest = oNotes.map((_, i) => i).filter((i) => !exactO.has(i));
|
|
43757
|
+
const rRest = rNotes.map((_, j) => j).filter((j) => !exactR.has(j));
|
|
43758
|
+
let oAt = 0;
|
|
43759
|
+
let rAt = 0;
|
|
43760
|
+
const rest = histogramMatch(oRest.map((i) => text(oNotes[i])), rRest.map((j) => text(rNotes[j])));
|
|
43761
|
+
for (const a of [...rest, { a: oRest.length, b: rRest.length }]) {
|
|
43762
|
+
if (a.a - oAt === 1 && a.b - rAt === 1) pairs.push([oRest[oAt], rRest[rAt]]);
|
|
43763
|
+
if (a.a < oRest.length && a.b < rRest.length) pairs.push([oRest[a.a], rRest[a.b]]);
|
|
43764
|
+
oAt = a.a + 1;
|
|
43765
|
+
rAt = a.b + 1;
|
|
43766
|
+
}
|
|
43767
|
+
const ids = ctx.noteIds[kind];
|
|
43768
|
+
const pairedO = new Set(pairs.map((p) => p[0]));
|
|
43769
|
+
const pairedR = new Set(pairs.map((p) => p[1]));
|
|
43770
|
+
let changed = false;
|
|
43771
|
+
for (const [i, j] of pairs) {
|
|
43772
|
+
ids.set(oNotes[i].attrs["w:id"], rNotes[j].attrs["w:id"]);
|
|
43773
|
+
const before = ctx.meta.nextId;
|
|
43774
|
+
let touched = false;
|
|
43775
|
+
compareContainer(oNotes[i], rNotes[j], { ...ctx, meta: { ...ctx.meta, nextId: () => (touched = true, before()) } });
|
|
43776
|
+
changed || (changed = touched);
|
|
43777
|
+
}
|
|
43778
|
+
rNotes.forEach((note, j) => {
|
|
43779
|
+
if (pairedR.has(j)) return;
|
|
43780
|
+
for (const block of blockList(note)) if (localName(block.name) === "p") wrapContainerContent(block, "ins", ctx);
|
|
43781
|
+
changed = true;
|
|
43782
|
+
});
|
|
43783
|
+
const removed = oNotes.filter((_, i) => !pairedO.has(i));
|
|
43784
|
+
if (removed.length > 0) {
|
|
43785
|
+
const root = tree(merged, true);
|
|
43786
|
+
if (root) {
|
|
43787
|
+
let next = 1 + Math.max(0, ...root.children.map((c) => Number(c.attrs["w:id"]) || 0));
|
|
43788
|
+
for (const o of removed) {
|
|
43789
|
+
const copy = cloneXml(o);
|
|
43790
|
+
copy.attrs["w:id"] = String(next);
|
|
43791
|
+
ids.set(o.attrs["w:id"], String(next++));
|
|
43792
|
+
for (const block of blockList(copy)) {
|
|
43793
|
+
if (localName(block.name) !== "p") continue;
|
|
43794
|
+
adoptFromOriginal(block, { ...ctx, scope: { from: null, into: null } });
|
|
43795
|
+
wrapContainerContent(block, "del", ctx);
|
|
43796
|
+
}
|
|
43797
|
+
root.children.push(copy);
|
|
43798
|
+
}
|
|
43799
|
+
changed = true;
|
|
43800
|
+
}
|
|
43801
|
+
}
|
|
43802
|
+
if (changed) merged.markNotePartsChanged([kind === "footnote" ? "footnotes" : "endnotes"]);
|
|
43803
|
+
}
|
|
43804
|
+
function giveStruckReferencesTheirOwnNotes(kind, merged, ctx) {
|
|
43805
|
+
const root = kind === "footnote" ? merged.footnotesTree() : merged.endnotesTree();
|
|
43806
|
+
if (!root) return;
|
|
43807
|
+
const refName = `${kind}Reference`;
|
|
43808
|
+
const refs = /* @__PURE__ */ new Map();
|
|
43809
|
+
const walk2 = (e, struck) => {
|
|
43810
|
+
if (localName(e.name) === refName) {
|
|
43811
|
+
const list = refs.get(e.attrs["w:id"]) ?? [];
|
|
43812
|
+
list.push({ ref: e, struck });
|
|
43813
|
+
refs.set(e.attrs["w:id"], list);
|
|
43814
|
+
}
|
|
43815
|
+
for (const c of e.children) walk2(c, struck || localName(c.name) === "del" || rowDeleted(c));
|
|
43816
|
+
};
|
|
43817
|
+
const rowDeleted = (tr) => localName(tr.name) === "tr" && !!child(child(tr, "trPr"), "del");
|
|
43818
|
+
walk2(merged.docRoot, false);
|
|
43819
|
+
let next = 1 + Math.max(0, ...root.children.map((c) => Number(c.attrs["w:id"]) || 0));
|
|
43820
|
+
let changed = false;
|
|
43821
|
+
for (const [id, list] of refs) {
|
|
43822
|
+
if (list.length < 2) continue;
|
|
43823
|
+
const note = root.children.find((c) => localName(c.name) === kind && c.attrs["w:id"] === id);
|
|
43824
|
+
if (!note) continue;
|
|
43825
|
+
const keep = list.find((r) => !r.struck) ?? list[0];
|
|
43826
|
+
for (const r of list) {
|
|
43827
|
+
if (r === keep || !r.struck) continue;
|
|
43828
|
+
const copy = cloneXml(note);
|
|
43829
|
+
copy.attrs["w:id"] = String(next);
|
|
43830
|
+
r.ref.attrs["w:id"] = String(next++);
|
|
43831
|
+
for (const block of blockList(copy)) if (localName(block.name) === "p") wrapContainerContent(block, "del", ctx);
|
|
43832
|
+
root.children.push(copy);
|
|
43833
|
+
changed = true;
|
|
43834
|
+
}
|
|
43835
|
+
}
|
|
43836
|
+
if (changed) merged.markNotePartsChanged([kind === "footnote" ? "footnotes" : "endnotes"]);
|
|
43837
|
+
}
|
|
43288
43838
|
function blockList(container, ...skip) {
|
|
43289
43839
|
return container.children.filter((c) => !skip.includes(localName(c.name)));
|
|
43290
43840
|
}
|
|
43291
43841
|
function reportUncomparedParts(original, revised, ctx) {
|
|
43292
43842
|
const parts = [
|
|
43293
43843
|
["styles.xml", original.stylesTree()],
|
|
43294
|
-
["numbering.xml", original.numberingTree()]
|
|
43295
|
-
["footnotes.xml", original.footnotesTree()],
|
|
43296
|
-
["endnotes.xml", original.endnotesTree()]
|
|
43844
|
+
["numbering.xml", original.numberingTree()]
|
|
43297
43845
|
];
|
|
43298
43846
|
const revisedTrees = {
|
|
43299
43847
|
"styles.xml": revised.stylesTree(),
|
|
43300
|
-
"numbering.xml": revised.numberingTree()
|
|
43301
|
-
"footnotes.xml": revised.footnotesTree(),
|
|
43302
|
-
"endnotes.xml": revised.endnotesTree()
|
|
43848
|
+
"numbering.xml": revised.numberingTree()
|
|
43303
43849
|
};
|
|
43304
43850
|
for (const [name, tree] of parts) {
|
|
43305
43851
|
const other = revisedTrees[name];
|
|
@@ -43309,13 +43855,12 @@ function reportUncomparedParts(original, revised, ctx) {
|
|
|
43309
43855
|
ctx.note({ code: "part-not-compared", detail: `${name} differs; the result carries the revised document's` });
|
|
43310
43856
|
}
|
|
43311
43857
|
}
|
|
43312
|
-
const hf = (doc) => [...doc.headerRoots(), ...doc.footerRoots()].map((root) => serializeXml(root)).join("");
|
|
43313
|
-
if (hf(original) !== hf(revised)) {
|
|
43314
|
-
ctx.note({ code: "part-not-compared", detail: "headers/footers differ; the result carries the revised document's" });
|
|
43315
|
-
}
|
|
43316
43858
|
}
|
|
43317
43859
|
function alignBlocks(oBlocks, rBlocks, ctx) {
|
|
43318
|
-
const anchors = histogramMatch(
|
|
43860
|
+
const anchors = histogramMatch(
|
|
43861
|
+
oBlocks.map((el26) => blockFingerprint(el26, ctx.original)),
|
|
43862
|
+
rBlocks.map((el26) => blockFingerprint(el26, ctx.doc))
|
|
43863
|
+
);
|
|
43319
43864
|
const entries = [];
|
|
43320
43865
|
let oAt = 0;
|
|
43321
43866
|
let rAt = 0;
|
|
@@ -43329,19 +43874,23 @@ function alignBlocks(oBlocks, rBlocks, ctx) {
|
|
|
43329
43874
|
}
|
|
43330
43875
|
return entries;
|
|
43331
43876
|
}
|
|
43332
|
-
function blockFingerprint(el26) {
|
|
43877
|
+
function blockFingerprint(el26, doc) {
|
|
43333
43878
|
const ln = localName(el26.name);
|
|
43334
43879
|
if (ln === "p") {
|
|
43335
43880
|
const pPr = child(el26, "pPr");
|
|
43336
43881
|
const style = child(pPr, "pStyle")?.attrs["w:val"] ?? "";
|
|
43337
43882
|
const numPr = child(pPr, "numPr");
|
|
43338
|
-
|
|
43883
|
+
const jc = child(pPr, "jc")?.attrs["w:val"] ?? "";
|
|
43884
|
+
const sect = sectionKey(el26, doc);
|
|
43885
|
+
return `P\0${normalizeText(paragraphText3(el26))}\0${style}\0${numPr ? serializeXml(numPr) : ""}\0${jc}\0${sect}`;
|
|
43339
43886
|
}
|
|
43340
43887
|
if (ln === "tbl") return `T\0${normalizeText(paragraphText3(el26))}`;
|
|
43341
43888
|
return `X\0${serializeXml(el26)}`;
|
|
43342
43889
|
}
|
|
43343
43890
|
function emitGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, entries, ctx) {
|
|
43344
|
-
const
|
|
43891
|
+
const before = entries[entries.length - 1];
|
|
43892
|
+
const unanchored = (!before || !isParagraph(before.el)) && (rHi >= rBlocks.length || !isParagraph(rBlocks[rHi]));
|
|
43893
|
+
const pairings = resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, oLo === 0 && rLo === 0 || unanchored);
|
|
43345
43894
|
let oAt = oLo;
|
|
43346
43895
|
let rAt = rLo;
|
|
43347
43896
|
for (const pairing of [...pairings, { kind: "pair", o: oHi, r: rHi, other: -1 }]) {
|
|
@@ -43373,16 +43922,43 @@ function emitGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, entries, ctx) {
|
|
|
43373
43922
|
}
|
|
43374
43923
|
}
|
|
43375
43924
|
}
|
|
43925
|
+
var GAP_PAIR_CAP = 200 * 200;
|
|
43926
|
+
var GAP_WINDOW = 50;
|
|
43376
43927
|
function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = false) {
|
|
43377
43928
|
const out = [];
|
|
43378
43929
|
const oUsed = /* @__PURE__ */ new Set();
|
|
43379
43930
|
const rUsed = /* @__PURE__ */ new Set();
|
|
43380
|
-
const
|
|
43931
|
+
const placed = [];
|
|
43932
|
+
const crosses = (o, r) => {
|
|
43933
|
+
let lo = 0;
|
|
43934
|
+
let hi = placed.length;
|
|
43935
|
+
while (lo < hi) {
|
|
43936
|
+
const mid = lo + hi >> 1;
|
|
43937
|
+
if (placed[mid].o < o) lo = mid + 1;
|
|
43938
|
+
else hi = mid;
|
|
43939
|
+
}
|
|
43940
|
+
return lo > 0 && placed[lo - 1].r >= r || lo < placed.length && placed[lo].r <= r;
|
|
43941
|
+
};
|
|
43942
|
+
const accept = (p) => {
|
|
43943
|
+
out.push(p);
|
|
43944
|
+
let at = placed.length;
|
|
43945
|
+
while (at > 0 && placed[at - 1].o > p.o) at--;
|
|
43946
|
+
placed.splice(at, 0, p);
|
|
43947
|
+
};
|
|
43948
|
+
const oLen = oHi - oLo;
|
|
43949
|
+
const rLen = rHi - rLo;
|
|
43950
|
+
const windowed = oLen * rLen > GAP_PAIR_CAP;
|
|
43951
|
+
const span = (i) => {
|
|
43952
|
+
if (!windowed) return [rLo, rHi];
|
|
43953
|
+
const centre = rLo + Math.round((i - oLo) * rLen / Math.max(1, oLen));
|
|
43954
|
+
return [Math.max(rLo, centre - GAP_WINDOW), Math.min(rHi, centre + GAP_WINDOW + 1)];
|
|
43955
|
+
};
|
|
43381
43956
|
for (let i = oLo; i < oHi; i++) {
|
|
43382
|
-
|
|
43957
|
+
const [jLo, jHi] = span(i);
|
|
43958
|
+
for (let j = jLo; j + 1 < jHi; j++) {
|
|
43383
43959
|
if (oUsed.has(i) || rUsed.has(j) || rUsed.has(j + 1) || crosses(i, j)) continue;
|
|
43384
43960
|
if (concatMatches([oBlocks[i]], [rBlocks[j], rBlocks[j + 1]], ctx)) {
|
|
43385
|
-
|
|
43961
|
+
accept({ kind: "split", o: i, r: j, other: j + 1 });
|
|
43386
43962
|
oUsed.add(i);
|
|
43387
43963
|
rUsed.add(j);
|
|
43388
43964
|
rUsed.add(j + 1);
|
|
@@ -43390,10 +43966,11 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43390
43966
|
}
|
|
43391
43967
|
}
|
|
43392
43968
|
for (let i = oLo; i + 1 < oHi; i++) {
|
|
43393
|
-
|
|
43969
|
+
const [jLo, jHi] = span(i);
|
|
43970
|
+
for (let j = jLo; j < jHi; j++) {
|
|
43394
43971
|
if (oUsed.has(i) || oUsed.has(i + 1) || rUsed.has(j) || crosses(i, j)) continue;
|
|
43395
43972
|
if (concatMatches([oBlocks[i], oBlocks[i + 1]], [rBlocks[j]], ctx)) {
|
|
43396
|
-
|
|
43973
|
+
accept({ kind: "merge", o: i, r: j, other: i + 1 });
|
|
43397
43974
|
oUsed.add(i);
|
|
43398
43975
|
oUsed.add(i + 1);
|
|
43399
43976
|
rUsed.add(j);
|
|
@@ -43403,9 +43980,10 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43403
43980
|
const scored = [];
|
|
43404
43981
|
for (let i = oLo; i < oHi; i++) {
|
|
43405
43982
|
if (oUsed.has(i)) continue;
|
|
43406
|
-
|
|
43983
|
+
const [jLo, jHi] = span(i);
|
|
43984
|
+
for (let j = jLo; j < jHi; j++) {
|
|
43407
43985
|
if (rUsed.has(j)) continue;
|
|
43408
|
-
if (
|
|
43986
|
+
if (!pairable(oBlocks[i], rBlocks[j], ctx)) continue;
|
|
43409
43987
|
const a = paragraphText3(oBlocks[i]);
|
|
43410
43988
|
const b = paragraphText3(rBlocks[j]);
|
|
43411
43989
|
const score = similarity(a, b);
|
|
@@ -43416,30 +43994,74 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43416
43994
|
for (const cand of scored) {
|
|
43417
43995
|
if (oUsed.has(cand.o) || rUsed.has(cand.r)) continue;
|
|
43418
43996
|
if (crosses(cand.o, cand.r)) continue;
|
|
43419
|
-
|
|
43997
|
+
accept({ kind: "pair", o: cand.o, r: cand.r, other: -1 });
|
|
43420
43998
|
oUsed.add(cand.o);
|
|
43421
43999
|
rUsed.add(cand.r);
|
|
43422
44000
|
}
|
|
44001
|
+
const settled = [...out].sort((a, b) => a.o - b.o);
|
|
44002
|
+
let oFrom = oLo;
|
|
44003
|
+
let rFrom = rLo;
|
|
44004
|
+
for (const p of [...settled, { kind: "pair", o: oHi, r: rHi, other: -1 }]) {
|
|
44005
|
+
const os = [];
|
|
44006
|
+
const rs = [];
|
|
44007
|
+
for (let i = oFrom; i < p.o; i++) if (!oUsed.has(i)) os.push(i);
|
|
44008
|
+
for (let j = rFrom; j < p.r; j++) if (!rUsed.has(j)) rs.push(j);
|
|
44009
|
+
if (os.length === 1 && rs.length === 1 && sameShape2(oBlocks[os[0]], rBlocks[rs[0]], ctx)) {
|
|
44010
|
+
accept({ kind: "pair", o: os[0], r: rs[0], other: -1 });
|
|
44011
|
+
oUsed.add(os[0]);
|
|
44012
|
+
rUsed.add(rs[0]);
|
|
44013
|
+
}
|
|
44014
|
+
oFrom = p.kind === "merge" ? p.other + 1 : p.o + 1;
|
|
44015
|
+
rFrom = p.kind === "split" ? p.other + 1 : p.r + 1;
|
|
44016
|
+
}
|
|
43423
44017
|
if (needsAnchor && out.length === 0) {
|
|
43424
44018
|
for (let i = oLo; i < oHi && out.length === 0; i++) {
|
|
43425
44019
|
if (oUsed.has(i)) continue;
|
|
43426
44020
|
for (let j = rLo; j < rHi; j++) {
|
|
43427
|
-
if (rUsed.has(j) ||
|
|
43428
|
-
|
|
44021
|
+
if (rUsed.has(j) || !pairable(oBlocks[i], rBlocks[j], ctx)) continue;
|
|
44022
|
+
accept({ kind: "pair", o: i, r: j, other: -1 });
|
|
43429
44023
|
break;
|
|
43430
44024
|
}
|
|
43431
44025
|
}
|
|
43432
44026
|
}
|
|
43433
44027
|
return out.sort((a, b) => a.o - b.o);
|
|
43434
44028
|
}
|
|
44029
|
+
function pairable(a, b, ctx) {
|
|
44030
|
+
if (localName(a.name) !== localName(b.name)) return false;
|
|
44031
|
+
if (!isParagraph(a)) return true;
|
|
44032
|
+
return sectionKey(a, ctx.original) === sectionKey(b, ctx.doc);
|
|
44033
|
+
}
|
|
44034
|
+
function sectionKey(el26, doc) {
|
|
44035
|
+
const sectPr = child(child(el26, "pPr"), "sectPr");
|
|
44036
|
+
if (!sectPr) return "";
|
|
44037
|
+
return serializeXml(sectPr).replace(/r:id="([^"]*)"/g, (_, id) => `r:id="${doc.documentRels.get(id)?.target ?? id}"`);
|
|
44038
|
+
}
|
|
44039
|
+
function sameShape2(a, b, ctx) {
|
|
44040
|
+
if (isParagraph(a) && isParagraph(b)) return pairable(a, b, ctx);
|
|
44041
|
+
if (localName(a.name) === localName(b.name) && ["tbl", "sdt", "customXml"].includes(localName(a.name))) return true;
|
|
44042
|
+
const cells = (tr) => tr.children.filter((c) => localName(c.name) === "tc").length;
|
|
44043
|
+
return localName(a.name) === "tr" && localName(b.name) === "tr" && cells(a) === cells(b);
|
|
44044
|
+
}
|
|
44045
|
+
function readOptions(ctx, original) {
|
|
44046
|
+
const noteKey = (ref) => {
|
|
44047
|
+
const kind = localName(ref.name) === "footnoteReference" ? "footnote" : "endnote";
|
|
44048
|
+
const id = ref.attrs["w:id"];
|
|
44049
|
+
return original ? `${ctx.noteIds[kind].get(id) ?? `original ${id}`}` : id;
|
|
44050
|
+
};
|
|
44051
|
+
return { whitespace: ctx.whitespace, noteKey };
|
|
44052
|
+
}
|
|
43435
44053
|
function concatMatches(oEls, rEls, ctx) {
|
|
43436
44054
|
const key = (els) => {
|
|
43437
44055
|
const parts = [];
|
|
43438
44056
|
for (const el26 of els) {
|
|
43439
44057
|
if (localName(el26.name) !== "p") return null;
|
|
43440
|
-
|
|
44058
|
+
if (child(child(el26, "pPr"), "sectPr")) return null;
|
|
44059
|
+
const content = readParagraph(el26, readOptions(ctx, oEls.includes(el26)));
|
|
43441
44060
|
if (!content.inlineDiffable) return null;
|
|
43442
|
-
for (const token of content.tokens)
|
|
44061
|
+
for (const token of content.tokens) {
|
|
44062
|
+
const props = token.pieces ? token.pieces.map((p) => `${p.text.length}:${runPropsKey(p.run)}`).join("|") : runPropsKey(token.run);
|
|
44063
|
+
parts.push(token.key + "" + props + "" + token.path.map((c) => serializeXml({ ...c, children: c.children.filter((k) => /Pr$/.test(localName(k.name))) })).join(">"));
|
|
44064
|
+
}
|
|
43443
44065
|
}
|
|
43444
44066
|
return parts.join("");
|
|
43445
44067
|
};
|
|
@@ -43451,14 +44073,24 @@ function emitMatched(oEl, rEl, ctx) {
|
|
|
43451
44073
|
const ln = localName(oEl.name);
|
|
43452
44074
|
if (ln === "p") comparePair(oEl, rEl, ctx);
|
|
43453
44075
|
else if (ln === "tbl") compareTable(oEl, rEl, ctx);
|
|
44076
|
+
else if (ln === "sdt") {
|
|
44077
|
+
const o = child(oEl, "sdtContent");
|
|
44078
|
+
const r = child(rEl, "sdtContent");
|
|
44079
|
+
if (o && r) compareContainer(o, r, ctx);
|
|
44080
|
+
} else if (ln === "customXml") compareContainer(oEl, rEl, ctx);
|
|
43454
44081
|
return { el: rEl, cls: "kept" };
|
|
43455
44082
|
}
|
|
43456
44083
|
var REL_ATTRS = ["id", "embed", "link", "pict", "dm", "lo", "qs", "cs"];
|
|
43457
44084
|
function adoptFromOriginal(root, ctx) {
|
|
43458
|
-
const mine = ctx.doc.
|
|
43459
|
-
const theirs = ctx.original.
|
|
44085
|
+
const mine = ctx.doc.relationshipsOf(ctx.scope.into);
|
|
44086
|
+
const theirs = ctx.original.relationshipsOf(ctx.scope.from);
|
|
43460
44087
|
const remapped = /* @__PURE__ */ new Map();
|
|
43461
44088
|
const walk2 = (el26) => {
|
|
44089
|
+
const ln = localName(el26.name);
|
|
44090
|
+
if (ln === "footnoteReference" || ln === "endnoteReference") {
|
|
44091
|
+
const to = ctx.noteIds[ln === "footnoteReference" ? "footnote" : "endnote"].get(el26.attrs["w:id"]);
|
|
44092
|
+
if (to !== void 0) el26.attrs["w:id"] = to;
|
|
44093
|
+
}
|
|
43462
44094
|
for (const key of Object.keys(el26.attrs)) {
|
|
43463
44095
|
if (!key.startsWith("r:") || !REL_ATTRS.includes(localName(key))) continue;
|
|
43464
44096
|
const id = el26.attrs[key];
|
|
@@ -43468,7 +44100,7 @@ function adoptFromOriginal(root, ctx) {
|
|
|
43468
44100
|
if (here && here.target === source.target && here.type === source.type) continue;
|
|
43469
44101
|
let replacement = remapped.get(id);
|
|
43470
44102
|
if (replacement === void 0) {
|
|
43471
|
-
replacement = source.external ? ctx.doc.addHyperlinkRel(source.target) :
|
|
44103
|
+
replacement = source.external ? ctx.doc.addHyperlinkRel(source.target, ctx.scope.into) : ctx.doc.adoptRelFrom(ctx.original, id, ctx.scope);
|
|
43472
44104
|
if (replacement === null) {
|
|
43473
44105
|
ctx.note({
|
|
43474
44106
|
code: "unresolved-relationship",
|
|
@@ -43487,32 +44119,31 @@ function adoptFromOriginal(root, ctx) {
|
|
|
43487
44119
|
function emitDeleted(el26, ctx) {
|
|
43488
44120
|
const clone = cloneXml(el26);
|
|
43489
44121
|
adoptFromOriginal(clone, ctx);
|
|
43490
|
-
|
|
43491
|
-
if (ln === "p") {
|
|
43492
|
-
wrapContainerContent(clone, "del", ctx);
|
|
43493
|
-
} else if (ln === "tbl") {
|
|
43494
|
-
for (const row of clone.children.filter((c) => localName(c.name) === "tr")) {
|
|
43495
|
-
markRowRevision(row, "del", ctx.meta);
|
|
43496
|
-
wrapRowContent(row, "del", ctx);
|
|
43497
|
-
}
|
|
43498
|
-
} else if (!isPassenger(ln)) {
|
|
43499
|
-
ctx.note({ code: "untracked-block", detail: `a <${clone.name}> block was removed and is not expressible as a revision` });
|
|
43500
|
-
}
|
|
44122
|
+
markWholeBlock(clone, "del", ctx);
|
|
43501
44123
|
return { el: clone, cls: "deleted" };
|
|
43502
44124
|
}
|
|
43503
44125
|
function emitInserted(el26, ctx) {
|
|
44126
|
+
markWholeBlock(el26, "ins", ctx);
|
|
44127
|
+
return { el: el26, cls: "inserted" };
|
|
44128
|
+
}
|
|
44129
|
+
function markWholeBlock(el26, kind, ctx) {
|
|
43504
44130
|
const ln = localName(el26.name);
|
|
43505
44131
|
if (ln === "p") {
|
|
43506
|
-
wrapContainerContent(el26,
|
|
44132
|
+
wrapContainerContent(el26, kind, ctx);
|
|
43507
44133
|
} else if (ln === "tbl") {
|
|
43508
44134
|
for (const row of el26.children.filter((c) => localName(c.name) === "tr")) {
|
|
43509
|
-
markRowRevision(row,
|
|
43510
|
-
wrapRowContent(row,
|
|
44135
|
+
markRowRevision(row, kind, ctx.meta);
|
|
44136
|
+
wrapRowContent(row, kind, ctx);
|
|
44137
|
+
}
|
|
44138
|
+
} else if (ln === "sdt" || ln === "customXml") {
|
|
44139
|
+
const inner = ln === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44140
|
+
for (const block of inner?.children ?? []) {
|
|
44141
|
+
markWholeBlock(block, kind, ctx);
|
|
44142
|
+
if (isParagraph(block)) markParagraphGlyph(block, kind, ctx.meta);
|
|
43511
44143
|
}
|
|
43512
|
-
} else if (!isPassenger(ln)) {
|
|
43513
|
-
ctx.note({ code: "untracked-block", detail: `a <${el26.name}> block was added and is not expressible as a revision` });
|
|
44144
|
+
} else if (!isPassenger(ln) && ln !== "sdtPr" && ln !== "sdtEndPr" && ln !== "customXmlPr") {
|
|
44145
|
+
ctx.note({ code: "untracked-block", detail: `a <${el26.name}> block was ${kind === "ins" ? "added" : "removed"} and is not expressible as a revision` });
|
|
43514
44146
|
}
|
|
43515
|
-
return { el: el26, cls: "inserted" };
|
|
43516
44147
|
}
|
|
43517
44148
|
function wrapRowContent(row, kind, ctx) {
|
|
43518
44149
|
for (const cell of row.children.filter((c) => localName(c.name) === "tc")) {
|
|
@@ -43522,7 +44153,8 @@ function wrapRowContent(row, kind, ctx) {
|
|
|
43522
44153
|
}
|
|
43523
44154
|
}
|
|
43524
44155
|
function wrapContainerContent(container, kind, ctx) {
|
|
43525
|
-
const NESTED = ["hyperlink", "smartTag", "dir", "bdo"];
|
|
44156
|
+
const NESTED = ["hyperlink", "smartTag", "dir", "bdo", "fldSimple"];
|
|
44157
|
+
const WRAPPABLE = ["r", "sdt", "customXml"];
|
|
43526
44158
|
const out = [];
|
|
43527
44159
|
let group = [];
|
|
43528
44160
|
const flush = () => {
|
|
@@ -43533,7 +44165,7 @@ function wrapContainerContent(container, kind, ctx) {
|
|
|
43533
44165
|
};
|
|
43534
44166
|
for (const c of container.children) {
|
|
43535
44167
|
const ln = localName(c.name);
|
|
43536
|
-
if (ln
|
|
44168
|
+
if (WRAPPABLE.includes(ln)) {
|
|
43537
44169
|
group.push(c);
|
|
43538
44170
|
continue;
|
|
43539
44171
|
}
|
|
@@ -43551,10 +44183,10 @@ function wrapContainerContent(container, kind, ctx) {
|
|
|
43551
44183
|
container.children = out;
|
|
43552
44184
|
}
|
|
43553
44185
|
function comparePair(oEl, rEl, ctx) {
|
|
43554
|
-
if (
|
|
44186
|
+
if (sameXml(oEl, rEl)) return;
|
|
43555
44187
|
if (ctx.formatting) alignParagraphProps(rEl, child(rEl, "pPr"), child(oEl, "pPr"), ctx);
|
|
43556
|
-
const oc = readParagraph(oEl,
|
|
43557
|
-
const rc = readParagraph(rEl,
|
|
44188
|
+
const oc = readParagraph(oEl, readOptions(ctx, true));
|
|
44189
|
+
const rc = readParagraph(rEl, readOptions(ctx, false));
|
|
43558
44190
|
if (!oc.inlineDiffable || !rc.inlineDiffable) {
|
|
43559
44191
|
if (oc.text !== rc.text || serializeXml(stripProps(oEl)) !== serializeXml(stripProps(rEl))) {
|
|
43560
44192
|
const deleted = cloneXml(oEl);
|
|
@@ -43569,70 +44201,180 @@ function comparePair(oEl, rEl, ctx) {
|
|
|
43569
44201
|
];
|
|
43570
44202
|
ctx.note({
|
|
43571
44203
|
code: "coarse-paragraph",
|
|
43572
|
-
detail: "a paragraph holding
|
|
44204
|
+
detail: "a paragraph holding inline math or other content a word diff cannot rebuild changed; struck and reinserted whole"
|
|
43573
44205
|
});
|
|
43574
44206
|
}
|
|
43575
44207
|
return;
|
|
43576
44208
|
}
|
|
43577
44209
|
const segments = wordSegments(oc.tokens, rc.tokens);
|
|
43578
44210
|
const changed = segments.some((s) => !s.equal);
|
|
43579
|
-
|
|
43580
|
-
|
|
43581
|
-
return;
|
|
43582
|
-
}
|
|
44211
|
+
const flat = oc.tokens.every((t) => t.path.length === 0) && rc.tokens.every((t) => t.path.length === 0);
|
|
44212
|
+
if (!changed && (!ctx.formatting || flat && alignRunPropsInPlace(oEl, rEl, ctx))) return;
|
|
43583
44213
|
const pPr = child(rEl, "pPr");
|
|
43584
|
-
const
|
|
44214
|
+
const items = [];
|
|
44215
|
+
const counterpart = /* @__PURE__ */ new Map();
|
|
43585
44216
|
for (const seg of segments) {
|
|
43586
44217
|
if (seg.equal) {
|
|
43587
|
-
|
|
44218
|
+
for (const it of emitEqualSpan(oc.tokens, rc.tokens, seg, ctx, counterpart)) items.push({ ...it, kind: null });
|
|
43588
44219
|
continue;
|
|
43589
44220
|
}
|
|
43590
44221
|
if (seg.aEnd > seg.aStart) {
|
|
43591
|
-
const
|
|
43592
|
-
|
|
43593
|
-
|
|
44222
|
+
for (const it of sliceItems(oc.tokens, seg.aStart, seg.aEnd, true)) {
|
|
44223
|
+
adoptFromOriginal(it.el, ctx);
|
|
44224
|
+
markDeletedText(it.el);
|
|
44225
|
+
items.push({ ...it, kind: "del" });
|
|
44226
|
+
}
|
|
43594
44227
|
}
|
|
43595
44228
|
if (seg.bEnd > seg.bStart) {
|
|
43596
|
-
|
|
44229
|
+
for (const it of sliceItems(rc.tokens, seg.bStart, seg.bEnd, false)) items.push({ ...it, kind: "ins" });
|
|
44230
|
+
}
|
|
44231
|
+
}
|
|
44232
|
+
for (const it of items) if (it.kind === "del") it.path = it.path.map((c) => counterpart.get(c) ?? c);
|
|
44233
|
+
for (const el26 of rc.tail) items.push({ el: cloneXml(el26), path: [], kind: null });
|
|
44234
|
+
const fromOriginal = new Set(oc.tokens.flatMap((t) => t.path));
|
|
44235
|
+
rEl.children = [...pPr ? [pPr] : [], ...nestItems(items, 0, rEl, ctx, fromOriginal)];
|
|
44236
|
+
}
|
|
44237
|
+
var WRAPPABLE_CONTAINERS = /* @__PURE__ */ new Set(["sdt", "customXml"]);
|
|
44238
|
+
function compareTextBoxes(oAtom, rAtom, ctx) {
|
|
44239
|
+
const theirs = textBoxesIn(oAtom);
|
|
44240
|
+
const mine = textBoxesIn(rAtom);
|
|
44241
|
+
for (let i = 0; i < Math.min(theirs.length, mine.length); i++) {
|
|
44242
|
+
if (sameXml(theirs[i], mine[i])) continue;
|
|
44243
|
+
const entries = alignBlocks(blockList(theirs[i]), blockList(mine[i]), ctx);
|
|
44244
|
+
applyParagraphMarks(entries, ctx);
|
|
44245
|
+
mine[i].children = entries.map((e) => e.el);
|
|
44246
|
+
}
|
|
44247
|
+
}
|
|
44248
|
+
function nestItems(items, depth, rEl, ctx, fromOriginal) {
|
|
44249
|
+
const out = [];
|
|
44250
|
+
let i = 0;
|
|
44251
|
+
while (i < items.length) {
|
|
44252
|
+
const c = items[i].path[depth];
|
|
44253
|
+
let j = i + 1;
|
|
44254
|
+
if (c) {
|
|
44255
|
+
while (j < items.length && items[j].path[depth] === c) j++;
|
|
44256
|
+
const group = items.slice(i, j);
|
|
44257
|
+
const kind = group[0].kind;
|
|
44258
|
+
if (kind && WRAPPABLE_CONTAINERS.has(localName(c.name)) && group.every((it) => it.kind === kind)) {
|
|
44259
|
+
const plain = group.map((it) => ({ ...it, kind: null }));
|
|
44260
|
+
out.push(revisionWrapper(rEl, kind, [containerShell(c, nestItems(plain, depth + 1, rEl, ctx, fromOriginal), fromOriginal.has(c), ctx)], ctx.meta));
|
|
44261
|
+
} else {
|
|
44262
|
+
out.push(containerShell(c, nestItems(group, depth + 1, rEl, ctx, fromOriginal), fromOriginal.has(c), ctx));
|
|
44263
|
+
}
|
|
44264
|
+
} else {
|
|
44265
|
+
const kind = items[i].kind;
|
|
44266
|
+
while (j < items.length && !items[j].path[depth] && items[j].kind === kind) j++;
|
|
44267
|
+
const els = items.slice(i, j).map((it) => it.el);
|
|
44268
|
+
if (kind) out.push(revisionWrapper(rEl, kind, els, ctx.meta));
|
|
44269
|
+
else out.push(...els);
|
|
43597
44270
|
}
|
|
44271
|
+
i = j;
|
|
43598
44272
|
}
|
|
43599
|
-
out
|
|
43600
|
-
|
|
44273
|
+
return out;
|
|
44274
|
+
}
|
|
44275
|
+
function containerShell(c, content, fromOriginal, ctx) {
|
|
44276
|
+
const isSdt = localName(c.name) === "sdt";
|
|
44277
|
+
const inner = isSdt ? c.children.find((k) => localName(k.name) === "sdtContent") : void 0;
|
|
44278
|
+
const shell = {
|
|
44279
|
+
name: c.name,
|
|
44280
|
+
attrs: { ...c.attrs },
|
|
44281
|
+
// Property children only (sdtPr, smartTagPr, ...): the content is new.
|
|
44282
|
+
children: c.children.filter((k) => /Pr$/.test(localName(k.name))).map(cloneXml),
|
|
44283
|
+
text: ""
|
|
44284
|
+
};
|
|
44285
|
+
if (fromOriginal) adoptFromOriginal(shell, ctx);
|
|
44286
|
+
if (isSdt) shell.children.push({ name: inner?.name ?? c.name.replace(/sdt$/, "sdtContent"), attrs: { ...inner?.attrs ?? {} }, children: content, text: "" });
|
|
44287
|
+
else shell.children.push(...content);
|
|
44288
|
+
return shell;
|
|
43601
44289
|
}
|
|
43602
44290
|
function stripProps(el26) {
|
|
43603
44291
|
return { ...el26, children: el26.children.filter((c) => localName(c.name) !== "pPr") };
|
|
43604
44292
|
}
|
|
43605
|
-
function emitEqualSpan(oTokens, rTokens, seg, ctx) {
|
|
44293
|
+
function emitEqualSpan(oTokens, rTokens, seg, ctx, counterpart) {
|
|
44294
|
+
const units = [];
|
|
44295
|
+
const pieceAt = (pieces, offset) => {
|
|
44296
|
+
let base = 0;
|
|
44297
|
+
for (const p of pieces) {
|
|
44298
|
+
if (offset < base + p.text.length) return p;
|
|
44299
|
+
base += p.text.length;
|
|
44300
|
+
}
|
|
44301
|
+
return pieces[pieces.length - 1];
|
|
44302
|
+
};
|
|
44303
|
+
for (let k = 0; k < seg.bEnd - seg.bStart; k++) {
|
|
44304
|
+
const o = oTokens[seg.aStart + k];
|
|
44305
|
+
const r = rTokens[seg.bStart + k];
|
|
44306
|
+
for (let d = 0; d < Math.min(o.path.length, r.path.length); d++) {
|
|
44307
|
+
if (localName(o.path[d].name) === localName(r.path[d].name)) counterpart.set(o.path[d], r.path[d]);
|
|
44308
|
+
}
|
|
44309
|
+
if (r.atom || !r.pieces || !o.pieces || o.text !== r.text) {
|
|
44310
|
+
if (r.atom) units.push({ token: r, run: r.run, text: "", atom: r.atom, oAtom: o.atom, was: o.run });
|
|
44311
|
+
else for (const p of r.pieces ?? []) units.push({ token: r, run: p.run, t: p.t, text: p.text, was: o.run });
|
|
44312
|
+
continue;
|
|
44313
|
+
}
|
|
44314
|
+
const cuts = /* @__PURE__ */ new Set([0, r.text.length]);
|
|
44315
|
+
for (const pieces of [o.pieces, r.pieces]) {
|
|
44316
|
+
let base = 0;
|
|
44317
|
+
for (const p of pieces) cuts.add(base += p.text.length);
|
|
44318
|
+
}
|
|
44319
|
+
const sorted = [...cuts].sort((x, y) => x - y);
|
|
44320
|
+
for (let c = 0; c + 1 < sorted.length; c++) {
|
|
44321
|
+
const from = sorted[c];
|
|
44322
|
+
const to = sorted[c + 1];
|
|
44323
|
+
if (from === to) continue;
|
|
44324
|
+
const rp = pieceAt(r.pieces, from);
|
|
44325
|
+
units.push({ token: r, run: rp.run, t: rp.t, text: r.text.slice(from, to), was: pieceAt(o.pieces, from).run });
|
|
44326
|
+
}
|
|
44327
|
+
}
|
|
43606
44328
|
const out = [];
|
|
43607
|
-
let
|
|
43608
|
-
|
|
43609
|
-
|
|
43610
|
-
|
|
43611
|
-
|
|
43612
|
-
|
|
43613
|
-
|
|
44329
|
+
let open = null;
|
|
44330
|
+
let text = null;
|
|
44331
|
+
const close = () => {
|
|
44332
|
+
if (open && ctx.formatting && open.was !== runPropsKey(open.run)) swapRunProps(open.el, open.prev, ctx);
|
|
44333
|
+
open = null;
|
|
44334
|
+
text = null;
|
|
44335
|
+
};
|
|
44336
|
+
let lastToken = null;
|
|
44337
|
+
for (const u of units) {
|
|
44338
|
+
if (u.token !== lastToken && u.token.lead) {
|
|
44339
|
+
close();
|
|
44340
|
+
out.push(...u.token.lead.map((el26) => ({ el: cloneXml(el26), path: u.token.path })));
|
|
44341
|
+
}
|
|
44342
|
+
lastToken = u.token;
|
|
44343
|
+
const was = runPropsKey(u.was);
|
|
44344
|
+
if (!open || open.run !== u.run || open.was !== was) {
|
|
44345
|
+
close();
|
|
44346
|
+
const rPr = u.run.children.find((c) => localName(c.name) === "rPr");
|
|
44347
|
+
const el26 = { name: u.run.name, attrs: { ...u.run.attrs }, children: rPr ? [cloneXml(rPr)] : [], text: "" };
|
|
44348
|
+
out.push({ el: el26, path: u.token.path });
|
|
44349
|
+
open = { run: u.run, was, el: el26, prev: child(u.was, "rPr") };
|
|
44350
|
+
}
|
|
44351
|
+
if (u.atom) {
|
|
44352
|
+
const atom = cloneXml(u.atom);
|
|
44353
|
+
if (u.oAtom && !sameXml(u.oAtom, u.atom)) compareTextBoxes(u.oAtom, atom, ctx);
|
|
44354
|
+
open.el.children.push(atom);
|
|
44355
|
+
text = null;
|
|
44356
|
+
continue;
|
|
43614
44357
|
}
|
|
43615
|
-
|
|
43616
|
-
|
|
43617
|
-
|
|
43618
|
-
if (localName(el26.name) === "r") {
|
|
43619
|
-
swapRunProps(el26, child(oTokens[seg.aStart + (at - seg.bStart)].run, "rPr"), ctx);
|
|
43620
|
-
}
|
|
43621
|
-
}
|
|
44358
|
+
if (text && text.t === u.t) {
|
|
44359
|
+
text.el.text += u.text;
|
|
44360
|
+
continue;
|
|
43622
44361
|
}
|
|
43623
|
-
|
|
43624
|
-
|
|
44362
|
+
const te = { name: u.t.name, attrs: { ...u.t.attrs, "xml:space": "preserve" }, children: [], text: u.text };
|
|
44363
|
+
open.el.children.push(te);
|
|
44364
|
+
text = { t: u.t, el: te };
|
|
43625
44365
|
}
|
|
44366
|
+
close();
|
|
43626
44367
|
return out;
|
|
43627
44368
|
}
|
|
43628
44369
|
function alignRunPropsInPlace(oEl, rEl, ctx) {
|
|
43629
44370
|
const oRuns = oEl.children.filter((c) => localName(c.name) === "r");
|
|
43630
44371
|
const rRuns = rEl.children.filter((c) => localName(c.name) === "r");
|
|
43631
|
-
if (oRuns.length !== rRuns.length) return;
|
|
44372
|
+
if (oRuns.length !== rRuns.length) return false;
|
|
43632
44373
|
for (let i = 0; i < rRuns.length; i++) {
|
|
43633
|
-
if (paragraphText3(oRuns[i]) !== paragraphText3(rRuns[i])) return;
|
|
44374
|
+
if (paragraphText3(oRuns[i]) !== paragraphText3(rRuns[i])) return false;
|
|
43634
44375
|
}
|
|
43635
44376
|
for (let i = 0; i < rRuns.length; i++) swapRunProps(rRuns[i], child(oRuns[i], "rPr"), ctx);
|
|
44377
|
+
return true;
|
|
43636
44378
|
}
|
|
43637
44379
|
function wordSegments(a, b) {
|
|
43638
44380
|
const raw = [];
|
|
@@ -43661,8 +44403,8 @@ function wordSegments(a, b) {
|
|
|
43661
44403
|
for (let i = 1; i + 1 < segments.length; i++) {
|
|
43662
44404
|
const seg = segments[i];
|
|
43663
44405
|
if (!seg.equal) continue;
|
|
43664
|
-
const len = seg.
|
|
43665
|
-
const beside = Math.min(changeSize(segments[i - 1]), changeSize(segments[i + 1]));
|
|
44406
|
+
const len = words(a, seg.aStart, seg.aEnd);
|
|
44407
|
+
const beside = Math.min(changeSize(segments[i - 1], a, b), changeSize(segments[i + 1], a, b));
|
|
43666
44408
|
if (len < COMPARE_TUNING.coalesceWindow || len * COMPARE_TUNING.coalesceRatio < beside) {
|
|
43667
44409
|
seg.equal = false;
|
|
43668
44410
|
absorbed = true;
|
|
@@ -43686,17 +44428,26 @@ function mergeChanges(segments) {
|
|
|
43686
44428
|
}
|
|
43687
44429
|
return out;
|
|
43688
44430
|
}
|
|
43689
|
-
function changeSize(seg) {
|
|
43690
|
-
return Math.max(
|
|
44431
|
+
function changeSize(seg, a, b) {
|
|
44432
|
+
return Math.max(words(a, seg.aStart, seg.aEnd), words(b, seg.bStart, seg.bEnd));
|
|
44433
|
+
}
|
|
44434
|
+
function words(tokens, from, to) {
|
|
44435
|
+
let n = 0;
|
|
44436
|
+
for (let i = from; i < to; i++) if (tokens[i].atom || !/^\s+$/.test(tokens[i].text)) n++;
|
|
44437
|
+
return n;
|
|
43691
44438
|
}
|
|
43692
44439
|
function compareTable(oTbl, rTbl, ctx) {
|
|
43693
|
-
if (
|
|
44440
|
+
if (sameXml(oTbl, rTbl)) return;
|
|
43694
44441
|
if (ctx.formatting) {
|
|
43695
44442
|
swapProps(rTbl, "tblPr", recordTableFormatChange, child(oTbl, "tblPr"), ctx);
|
|
43696
44443
|
alignTableGrid(oTbl, rTbl, ctx);
|
|
43697
44444
|
}
|
|
43698
|
-
|
|
43699
|
-
|
|
44445
|
+
compareRows(oTbl, rTbl, ctx);
|
|
44446
|
+
}
|
|
44447
|
+
var ROW_LEVEL = ["tr", "sdt", "customXml"];
|
|
44448
|
+
function compareRows(oParent, rParent, ctx) {
|
|
44449
|
+
const oRows = oParent.children.filter((c) => ROW_LEVEL.includes(localName(c.name)));
|
|
44450
|
+
const rRows = rParent.children.filter((c) => ROW_LEVEL.includes(localName(c.name)));
|
|
43700
44451
|
const anchors = histogramMatch(oRows.map(rowFingerprint), rRows.map(rowFingerprint));
|
|
43701
44452
|
const rows = [];
|
|
43702
44453
|
let oAt = 0;
|
|
@@ -43721,24 +44472,41 @@ function compareTable(oTbl, rTbl, ctx) {
|
|
|
43721
44472
|
oAt = anchor.a + 1;
|
|
43722
44473
|
rAt = anchor.b + 1;
|
|
43723
44474
|
}
|
|
43724
|
-
|
|
44475
|
+
rParent.children = [...rParent.children.filter((c) => !ROW_LEVEL.includes(localName(c.name))), ...rows];
|
|
43725
44476
|
}
|
|
43726
44477
|
function rowFingerprint(tr) {
|
|
44478
|
+
if (localName(tr.name) !== "tr") return `X\0${serializeXml(tr)}`;
|
|
43727
44479
|
const cells = tr.children.filter((c) => localName(c.name) === "tc");
|
|
43728
44480
|
return `${cells.length}\0${normalizeText(paragraphText3(tr))}`;
|
|
43729
44481
|
}
|
|
43730
|
-
function
|
|
43731
|
-
|
|
43732
|
-
|
|
43733
|
-
|
|
44482
|
+
function rowsIn(el26) {
|
|
44483
|
+
if (localName(el26.name) === "tr") return [el26];
|
|
44484
|
+
const inner = localName(el26.name) === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44485
|
+
return (inner?.children ?? []).filter((c) => ROW_LEVEL.includes(localName(c.name))).flatMap(rowsIn);
|
|
44486
|
+
}
|
|
44487
|
+
function deletedRow(el26, ctx) {
|
|
44488
|
+
const clone = cloneXml(el26);
|
|
44489
|
+
for (const tr of rowsIn(clone)) {
|
|
44490
|
+
markRowRevision(tr, "del", ctx.meta);
|
|
44491
|
+
wrapRowContent(tr, "del", ctx);
|
|
44492
|
+
}
|
|
43734
44493
|
return clone;
|
|
43735
44494
|
}
|
|
43736
|
-
function insertedRow(
|
|
43737
|
-
|
|
43738
|
-
|
|
43739
|
-
|
|
44495
|
+
function insertedRow(el26, ctx) {
|
|
44496
|
+
for (const tr of rowsIn(el26)) {
|
|
44497
|
+
markRowRevision(tr, "ins", ctx.meta);
|
|
44498
|
+
wrapRowContent(tr, "ins", ctx);
|
|
44499
|
+
}
|
|
44500
|
+
return el26;
|
|
43740
44501
|
}
|
|
43741
44502
|
function compareRow(oTr, rTr, ctx) {
|
|
44503
|
+
if (localName(oTr.name) !== "tr") {
|
|
44504
|
+
const inner = (el26) => localName(el26.name) === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44505
|
+
const o = inner(oTr);
|
|
44506
|
+
const r = inner(rTr);
|
|
44507
|
+
if (o && r) compareRows(o, r, ctx);
|
|
44508
|
+
return [rTr];
|
|
44509
|
+
}
|
|
43742
44510
|
const oCells = oTr.children.filter((c) => localName(c.name) === "tc");
|
|
43743
44511
|
const rCells = rTr.children.filter((c) => localName(c.name) === "tc");
|
|
43744
44512
|
if (oCells.length !== rCells.length) {
|
|
@@ -43761,7 +44529,7 @@ function compareRow(oTr, rTr, ctx) {
|
|
|
43761
44529
|
function alignTableGrid(oTbl, rTbl, ctx) {
|
|
43762
44530
|
const oGrid = child(oTbl, "tblGrid");
|
|
43763
44531
|
const rGrid = child(rTbl, "tblGrid");
|
|
43764
|
-
if (!oGrid || !rGrid ||
|
|
44532
|
+
if (!oGrid || !rGrid || sameXml(oGrid, rGrid)) return;
|
|
43765
44533
|
const live = rGrid.children.map(cloneXml);
|
|
43766
44534
|
rGrid.children = oGrid.children.map(cloneXml);
|
|
43767
44535
|
recordTableGridChange(rTbl, ctx.meta);
|
|
@@ -43848,7 +44616,22 @@ function applyParagraphMarks(entries, ctx) {
|
|
|
43848
44616
|
}
|
|
43849
44617
|
}
|
|
43850
44618
|
function markRun(entries, start, end, ctx) {
|
|
43851
|
-
|
|
44619
|
+
let at = start;
|
|
44620
|
+
while (at <= end) {
|
|
44621
|
+
if (!isParagraph(entries[at].el)) {
|
|
44622
|
+
at++;
|
|
44623
|
+
continue;
|
|
44624
|
+
}
|
|
44625
|
+
let last = at;
|
|
44626
|
+
while (last + 1 <= end && isParagraph(entries[last + 1].el)) last++;
|
|
44627
|
+
markParagraphRun(entries, at, last, ctx);
|
|
44628
|
+
at = last + 1;
|
|
44629
|
+
}
|
|
44630
|
+
}
|
|
44631
|
+
function markParagraphRun(entries, start, end, ctx) {
|
|
44632
|
+
const endsSection = (el26) => isParagraph(el26) && !!child(child(el26, "pPr"), "sectPr");
|
|
44633
|
+
const before = start > 0 ? entries[start - 1].el : void 0;
|
|
44634
|
+
const shifted = !!before && isParagraph(before) && !endsSection(before) && !entries.slice(start, end + 1).some((e) => endsSection(e.el));
|
|
43852
44635
|
if (shifted) {
|
|
43853
44636
|
for (let i = start; i <= end; i++) {
|
|
43854
44637
|
if (!isParagraph(entries[i].el) || !isParagraph(entries[i - 1].el)) continue;
|
|
@@ -45335,9 +46118,10 @@ async function renderForPrint(bytes, container) {
|
|
|
45335
46118
|
const doc = DocxDocument.load(bytes);
|
|
45336
46119
|
const layout = layoutDocument(doc);
|
|
45337
46120
|
renderToDom(doc, layout, container, { zoom: 1, pageGap: 0, pageShadow: false, comments: false });
|
|
46121
|
+
pruneBlankSpans(container);
|
|
45338
46122
|
const first = layout.pages[0];
|
|
45339
46123
|
const style = document.createElement("style");
|
|
45340
|
-
style.textContent = printStylesheet(first?.width ?? 816, first?.height ?? 1056);
|
|
46124
|
+
style.textContent = printStylesheet(first?.width ?? 816, first?.height ?? 1056, container);
|
|
45341
46125
|
document.head.append(style);
|
|
45342
46126
|
await paintsSettled();
|
|
45343
46127
|
await Promise.all(Array.from(container.querySelectorAll("img"), (img) => img.decode().catch(() => void 0)));
|
|
@@ -55791,4 +56575,4 @@ export {
|
|
|
55791
56575
|
cfb/cfb.js:
|
|
55792
56576
|
(*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com *)
|
|
55793
56577
|
*/
|
|
55794
|
-
//# sourceMappingURL=chunk-
|
|
56578
|
+
//# sourceMappingURL=chunk-WPQ6GPG4.js.map
|