wordinweb 0.3.5 → 0.3.7
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-QU3LB5BC.js → chunk-ULPL55MT.js} +1030 -202
- package/dist/chunk-ULPL55MT.js.map +1 -0
- package/dist/collab.d.ts +2 -2
- package/dist/collab.js +1 -1
- package/dist/{index-BunX2duN.d.ts → index-Ch_Ayax5.d.ts} +42 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-QU3LB5BC.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("/")) : "";
|
|
@@ -4432,6 +4461,11 @@ function parseParagraph(p, ctx) {
|
|
|
4432
4461
|
}
|
|
4433
4462
|
}
|
|
4434
4463
|
ensureCaretAnchor(p, para);
|
|
4464
|
+
if (ctx.revisionView === "markup" && pPr) {
|
|
4465
|
+
const markRPr = child(pPr, "rPr");
|
|
4466
|
+
if (markRPr && child(markRPr, "del")) para.markRevision = "del";
|
|
4467
|
+
else if (markRPr && child(markRPr, "ins")) para.markRevision = "ins";
|
|
4468
|
+
}
|
|
4435
4469
|
return para;
|
|
4436
4470
|
}
|
|
4437
4471
|
function hasVisibleContent(c) {
|
|
@@ -8798,6 +8832,10 @@ var _DocxDocument = class _DocxDocument {
|
|
|
8798
8832
|
/** The header part roots, in package order. A watermark is authored into
|
|
8799
8833
|
* every one of them: Word paints it on every page, and a document with a
|
|
8800
8834
|
* first-page or even-page header has more than one header part. */
|
|
8835
|
+
/** The root of the header or footer part at `target`, if this document has one. */
|
|
8836
|
+
headerFooterRoot(target) {
|
|
8837
|
+
return this.hfParts.find((p) => p.target === target)?.root;
|
|
8838
|
+
}
|
|
8801
8839
|
headerRoots() {
|
|
8802
8840
|
return this.hfParts.filter((p) => p.isHeader).map((p) => p.root);
|
|
8803
8841
|
}
|
|
@@ -8961,6 +8999,22 @@ var _DocxDocument = class _DocxDocument {
|
|
|
8961
8999
|
this.saveJournal = null;
|
|
8962
9000
|
}
|
|
8963
9001
|
}
|
|
9002
|
+
/**
|
|
9003
|
+
* An independent copy: what `DocxDocument.load(doc.save())` gives, without
|
|
9004
|
+
* zipping the package and unzipping it again. compareDocuments starts from a
|
|
9005
|
+
* copy of the revised document, and the round trip through the zip was a
|
|
9006
|
+
* third of its time on a document that had not changed at all.
|
|
9007
|
+
*/
|
|
9008
|
+
clone() {
|
|
9009
|
+
const journal = [];
|
|
9010
|
+
this.saveJournal = journal;
|
|
9011
|
+
try {
|
|
9012
|
+
return new _DocxDocument(Package.fromFiles(this.buildPackageFiles()));
|
|
9013
|
+
} finally {
|
|
9014
|
+
for (let i = journal.length - 1; i >= 0; i--) journal[i]();
|
|
9015
|
+
this.saveJournal = null;
|
|
9016
|
+
}
|
|
9017
|
+
}
|
|
8964
9018
|
saveAsync() {
|
|
8965
9019
|
const journal = [];
|
|
8966
9020
|
this.saveJournal = journal;
|
|
@@ -9162,8 +9216,91 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9162
9216
|
return this.relsRoot;
|
|
9163
9217
|
}
|
|
9164
9218
|
/** Register an external hyperlink relationship and return its rId. */
|
|
9165
|
-
|
|
9166
|
-
|
|
9219
|
+
/**
|
|
9220
|
+
* Copy the part that `source`'s main-document relationship `relId` points
|
|
9221
|
+
* at into this package, with every part it references in turn (a chart's
|
|
9222
|
+
* embedded workbook, SmartArt's layout, style and colour parts) and their
|
|
9223
|
+
* content types, and add a relationship to it. Returns the new
|
|
9224
|
+
* relationship id, or null when the source has no such internal part.
|
|
9225
|
+
*
|
|
9226
|
+
* compareDocuments needs this for struck content: a removed picture keeps
|
|
9227
|
+
* its w:drawing, and without its image part the result's reject-all
|
|
9228
|
+
* brought the picture back as an empty frame.
|
|
9229
|
+
*/
|
|
9230
|
+
adoptRelFrom(source, relId, scope = {}) {
|
|
9231
|
+
const rel = source.relationshipsOf(scope.from).get(relId);
|
|
9232
|
+
if (!rel || rel.external) return null;
|
|
9233
|
+
const copied = /* @__PURE__ */ new Map();
|
|
9234
|
+
const sameBytes = (path2, bytes) => {
|
|
9235
|
+
const have = this.pkg.binary(path2);
|
|
9236
|
+
return !!have && have.length === bytes.length && have.every((b, i) => b === bytes[i]);
|
|
9237
|
+
};
|
|
9238
|
+
const copyPart = (from) => {
|
|
9239
|
+
const known = copied.get(from);
|
|
9240
|
+
if (known) return known;
|
|
9241
|
+
const bytes = source.pkg.binary(from);
|
|
9242
|
+
if (!bytes) return null;
|
|
9243
|
+
let to = from;
|
|
9244
|
+
for (let n = 2; this.pkg.has(to) && !sameBytes(to, bytes); n++) to = from.replace(/(\.[^./]+)?$/, `-${n}$1`);
|
|
9245
|
+
copied.set(from, to);
|
|
9246
|
+
if (this.pkg.has(to)) return to;
|
|
9247
|
+
this.pkg.raw()[to] = bytes;
|
|
9248
|
+
this.adoptContentType(source, from, to);
|
|
9249
|
+
const relsXml = source.pkg.text(relsPathFor(from));
|
|
9250
|
+
if (relsXml) {
|
|
9251
|
+
const root = parseXml(relsXml);
|
|
9252
|
+
for (const r of root.children) {
|
|
9253
|
+
if (r.attrs.TargetMode === "External" || !r.attrs.Target) continue;
|
|
9254
|
+
const sub = copyPart(resolvePartPath(from, r.attrs.Target));
|
|
9255
|
+
if (sub) r.attrs.Target = relativePartPath(to, sub);
|
|
9256
|
+
}
|
|
9257
|
+
this.pkg.raw()[relsPathFor(to)] = strToU82(serializeXml(root, true));
|
|
9258
|
+
}
|
|
9259
|
+
return to;
|
|
9260
|
+
};
|
|
9261
|
+
const part = copyPart(rel.target);
|
|
9262
|
+
if (!part) return null;
|
|
9263
|
+
const owner = this.relsOwnerOf(scope.into);
|
|
9264
|
+
const rels = owner.relsRoot;
|
|
9265
|
+
let maxId = 0;
|
|
9266
|
+
for (const r of rels.children) {
|
|
9267
|
+
const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
|
|
9268
|
+
if (m) maxId = Math.max(maxId, parseInt(m[1], 10));
|
|
9269
|
+
}
|
|
9270
|
+
const id = `rId${maxId + 1}`;
|
|
9271
|
+
rels.children.push({
|
|
9272
|
+
name: "Relationship",
|
|
9273
|
+
attrs: { Id: id, Type: rel.type, Target: relativePartPath(owner.partPath, part) },
|
|
9274
|
+
children: [],
|
|
9275
|
+
text: ""
|
|
9276
|
+
});
|
|
9277
|
+
owner.relationships.set(id, { id, type: rel.type, target: part, external: false });
|
|
9278
|
+
return id;
|
|
9279
|
+
}
|
|
9280
|
+
/** Give a part copied from `source` the content type it had there. */
|
|
9281
|
+
adoptContentType(source, from, to) {
|
|
9282
|
+
const types = this.contentTypesRoot;
|
|
9283
|
+
const theirs = source.contentTypesRoot;
|
|
9284
|
+
if (!types || !theirs) return;
|
|
9285
|
+
const override = theirs.children.find((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${from}`);
|
|
9286
|
+
const prefixEnd = types.name.indexOf(":") + 1;
|
|
9287
|
+
const prefix = prefixEnd > 0 ? types.name.slice(0, prefixEnd) : "";
|
|
9288
|
+
if (override) {
|
|
9289
|
+
if (!types.children.some((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${to}`)) {
|
|
9290
|
+
types.children.push({ name: `${prefix}Override`, attrs: { PartName: `/${to}`, ContentType: override.attrs.ContentType }, children: [], text: "" });
|
|
9291
|
+
}
|
|
9292
|
+
return;
|
|
9293
|
+
}
|
|
9294
|
+
const ext = from.slice(from.lastIndexOf(".") + 1).toLowerCase();
|
|
9295
|
+
const byExt = (root) => root.children.find((c) => localName(c.name) === "Default" && c.attrs.Extension?.toLowerCase() === ext);
|
|
9296
|
+
const fallback = byExt(theirs);
|
|
9297
|
+
if (fallback && !byExt(types)) {
|
|
9298
|
+
types.children.unshift({ name: `${prefix}Default`, attrs: { Extension: ext, ContentType: fallback.attrs.ContentType }, children: [], text: "" });
|
|
9299
|
+
}
|
|
9300
|
+
}
|
|
9301
|
+
addHyperlinkRel(url, ownerPart) {
|
|
9302
|
+
const owner = this.relsOwnerOf(ownerPart);
|
|
9303
|
+
const rels = owner.relsRoot;
|
|
9167
9304
|
let maxId = 0;
|
|
9168
9305
|
for (const r of rels.children) {
|
|
9169
9306
|
const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
|
|
@@ -9181,7 +9318,7 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9181
9318
|
children: [],
|
|
9182
9319
|
text: ""
|
|
9183
9320
|
});
|
|
9184
|
-
|
|
9321
|
+
owner.relationships.set(id, { id, type: "hyperlink", target: url, external: true });
|
|
9185
9322
|
return id;
|
|
9186
9323
|
}
|
|
9187
9324
|
/** Retarget an existing external relationship (hyperlink href edit). */
|
|
@@ -9193,6 +9330,23 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9193
9330
|
if (el26) el26.attrs["Target"] = url;
|
|
9194
9331
|
return true;
|
|
9195
9332
|
}
|
|
9333
|
+
/** The relationships owned by `part` (a header or footer part), or by the
|
|
9334
|
+
* document part when null; a header or footer gets a rels root on demand. */
|
|
9335
|
+
relsOwnerOf(part) {
|
|
9336
|
+
const owner = part ? this.hfParts.find((p) => p.target === part) : void 0;
|
|
9337
|
+
if (!owner) return { relsRoot: this.ensureRelsRoot(), relationships: this.documentRels, partPath: this.docPart };
|
|
9338
|
+
owner.relsRoot ?? (owner.relsRoot = {
|
|
9339
|
+
name: "Relationships",
|
|
9340
|
+
attrs: { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" },
|
|
9341
|
+
children: [],
|
|
9342
|
+
text: ""
|
|
9343
|
+
});
|
|
9344
|
+
return { relsRoot: owner.relsRoot, relationships: owner.rels, partPath: owner.target };
|
|
9345
|
+
}
|
|
9346
|
+
/** The relationships of `part` (a header or footer), or of the document part. */
|
|
9347
|
+
relationshipsOf(part) {
|
|
9348
|
+
return (part ? this.hfParts.find((p) => p.target === part)?.rels : void 0) ?? this.documentRels;
|
|
9349
|
+
}
|
|
9196
9350
|
/** The relationships of the part that holds `source`: a header or footer
|
|
9197
9351
|
* part when the element is inside one, else the document part. */
|
|
9198
9352
|
imageRelsOwner(source) {
|
|
@@ -13702,6 +13856,20 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
|
|
|
13702
13856
|
if (childEl.type === "run") pushRun(childEl);
|
|
13703
13857
|
else for (const r of childEl.runs) pushRun(r, childEl.href ?? (childEl.anchor ? "#" + childEl.anchor : void 0));
|
|
13704
13858
|
}
|
|
13859
|
+
if (para.markRevision) {
|
|
13860
|
+
const last = para.children.at(-1);
|
|
13861
|
+
const lastRun = last?.type === "hyperlink" ? last.runs.at(-1) : last;
|
|
13862
|
+
const deleted = para.markRevision === "del";
|
|
13863
|
+
pushRun({
|
|
13864
|
+
type: "run",
|
|
13865
|
+
props: {
|
|
13866
|
+
...lastRun?.props ?? {},
|
|
13867
|
+
color: deleted ? "#D93025" : "#188038",
|
|
13868
|
+
...deleted ? { strike: true } : { underline: "single" }
|
|
13869
|
+
},
|
|
13870
|
+
content: [{ kind: "text", text: "\xB6" }]
|
|
13871
|
+
});
|
|
13872
|
+
}
|
|
13705
13873
|
if (gridPitch) {
|
|
13706
13874
|
for (let i = 0; i + 1 < atoms.length; i++) {
|
|
13707
13875
|
const punct = atoms[i];
|
|
@@ -19232,15 +19400,20 @@ var Engine = class {
|
|
|
19232
19400
|
while (out.length < nCols) out.push(bw(fallback));
|
|
19233
19401
|
return out;
|
|
19234
19402
|
};
|
|
19403
|
+
const colTopPads = (r) => {
|
|
19404
|
+
const fallback = this.cellMarginsOf(tbl, false).top ?? 0;
|
|
19405
|
+
return rows[r].cells.flatMap((c) => Array(Math.max(1, c.props.gridSpan)).fill(c.props.margins?.top ?? fallback));
|
|
19406
|
+
};
|
|
19235
19407
|
const boundary = (k) => {
|
|
19236
19408
|
if (k === 0) return Math.max(0, ...colWidths(0, "top", tb?.top));
|
|
19237
19409
|
if (k === rows.length)
|
|
19238
19410
|
return Math.max(0, ...colWidths(rows.length - 1, "bottom", tb?.bottom));
|
|
19239
19411
|
const above = colWidths(k - 1, "bottom", tb?.insideH);
|
|
19240
19412
|
const below = colWidths(k, "top", tb?.insideH);
|
|
19413
|
+
const topPads = colTopPads(k);
|
|
19241
19414
|
const n = Math.max(above.length, below.length);
|
|
19242
19415
|
let w = 0;
|
|
19243
|
-
for (let i = 0; i < n; i++) w = Math.max(w,
|
|
19416
|
+
for (let i = 0; i < n; i++) w = Math.max(w, below[i] ?? 0, (above[i] ?? 0) - (topPads[i] ?? 0));
|
|
19244
19417
|
return w;
|
|
19245
19418
|
};
|
|
19246
19419
|
return { top: boundary(ri), bottom: boundary(ri + 1) };
|
|
@@ -21446,6 +21619,8 @@ function renderPageShell(page, zoom, options) {
|
|
|
21446
21619
|
if (options.pageShadow !== false) {
|
|
21447
21620
|
el26.style.boxShadow = "var(--dxw-page-shadow, 0 1px 3px rgba(0,0,0,.28), 0 4px 14px rgba(0,0,0,.12))";
|
|
21448
21621
|
}
|
|
21622
|
+
el26.dataset.pageWidth = String(page.width);
|
|
21623
|
+
el26.dataset.pageHeight = String(page.height);
|
|
21449
21624
|
el26.dataset.bodyTop = String(page.bodyTop);
|
|
21450
21625
|
el26.dataset.bodyBottom = String(page.bodyBottom);
|
|
21451
21626
|
el26.dataset.columnBands = JSON.stringify(page.columnBands);
|
|
@@ -21762,9 +21937,21 @@ function trackPaint(work) {
|
|
|
21762
21937
|
async function paintsSettled() {
|
|
21763
21938
|
while (pendingPaints.size > 0) await Promise.allSettled([...pendingPaints]);
|
|
21764
21939
|
}
|
|
21765
|
-
function printStylesheet(pageWidthPx, pageHeightPx) {
|
|
21940
|
+
function printStylesheet(pageWidthPx, pageHeightPx, root) {
|
|
21941
|
+
const inches = (px) => `${Math.round(px / 96 * 1e3) / 1e3}in`;
|
|
21942
|
+
const sizes = /* @__PURE__ */ new Set();
|
|
21943
|
+
for (const el26 of root ? Array.from(root.querySelectorAll(".dxw-page[data-page-width]")) : []) {
|
|
21944
|
+
sizes.add(`${el26.dataset.pageWidth}x${el26.dataset.pageHeight}`);
|
|
21945
|
+
}
|
|
21946
|
+
const named = [...sizes].map((key) => {
|
|
21947
|
+
const [w, h] = key.split("x");
|
|
21948
|
+
const name = `dxw-${key.replace(/\./g, "_")}`;
|
|
21949
|
+
return `@page ${name} { size: ${inches(Number(w))} ${inches(Number(h))}; margin: 0; }
|
|
21950
|
+
.dxw-page[data-page-width="${w}"][data-page-height="${h}"] { page: ${name}; }`;
|
|
21951
|
+
});
|
|
21766
21952
|
return `
|
|
21767
|
-
@page { size: ${pageWidthPx
|
|
21953
|
+
@page { size: ${inches(pageWidthPx)} ${inches(pageHeightPx)}; margin: 0; }
|
|
21954
|
+
${named.join("\n ")}
|
|
21768
21955
|
html, body { margin: 0; padding: 0; }
|
|
21769
21956
|
.dxw-pages { display: block !important; padding: 0 !important; gap: 0 !important; }
|
|
21770
21957
|
.dxw-pages, .dxw-pages * {
|
|
@@ -21775,13 +21962,25 @@ function printStylesheet(pageWidthPx, pageHeightPx) {
|
|
|
21775
21962
|
.dxw-comment-card, .dxw-hf-marker { display: none !important; }
|
|
21776
21963
|
`;
|
|
21777
21964
|
}
|
|
21965
|
+
function pruneBlankSpans(root) {
|
|
21966
|
+
let removed = 0;
|
|
21967
|
+
for (const el26 of Array.from(root.querySelectorAll(".dxw-page span"))) {
|
|
21968
|
+
if (el26.childElementCount > 0 || el26.textContent?.trim()) continue;
|
|
21969
|
+
if (el26.style.textDecoration.includes("underline")) continue;
|
|
21970
|
+
el26.remove();
|
|
21971
|
+
removed++;
|
|
21972
|
+
}
|
|
21973
|
+
return removed;
|
|
21974
|
+
}
|
|
21778
21975
|
function buildPrintHtml(root, pageWidthPx, pageHeightPx) {
|
|
21779
21976
|
const head = [`<base href="${document.location.href}">`];
|
|
21780
21977
|
for (const node of Array.from(document.head.querySelectorAll("style, link[rel=stylesheet]"))) {
|
|
21781
21978
|
head.push(node.outerHTML);
|
|
21782
21979
|
}
|
|
21783
|
-
head.push(`<style>${printStylesheet(pageWidthPx, pageHeightPx)}</style>`);
|
|
21784
|
-
|
|
21980
|
+
head.push(`<style>${printStylesheet(pageWidthPx, pageHeightPx, root)}</style>`);
|
|
21981
|
+
const pages = root.cloneNode(true);
|
|
21982
|
+
pruneBlankSpans(pages);
|
|
21983
|
+
return `<!doctype html><html><head>${head.join("")}</head><body>${pages.outerHTML}</body></html>`;
|
|
21785
21984
|
}
|
|
21786
21985
|
function printPages(root, pageWidthPx, pageHeightPx) {
|
|
21787
21986
|
const iframe = document.createElement("iframe");
|
|
@@ -23828,7 +24027,9 @@ function revisionWrapper(sample, kind, content, meta) {
|
|
|
23828
24027
|
}
|
|
23829
24028
|
function markDeletedText(el26) {
|
|
23830
24029
|
const walk2 = (e) => {
|
|
24030
|
+
if (localName(e.name) === "txbxContent") return;
|
|
23831
24031
|
if (localName(e.name) === "t") e.name = e.name.replace(/t$/, "delText");
|
|
24032
|
+
else if (localName(e.name) === "instrText") e.name = e.name.replace(/instrText$/, "delInstrText");
|
|
23832
24033
|
for (const c of e.children) walk2(c);
|
|
23833
24034
|
};
|
|
23834
24035
|
walk2(el26);
|
|
@@ -24087,7 +24288,8 @@ function ensurePPrRPr(pEl) {
|
|
|
24087
24288
|
let rPr = pPr.children.find((c) => localName(c.name) === "rPr");
|
|
24088
24289
|
if (!rPr) {
|
|
24089
24290
|
rPr = el2(`${w}rPr`);
|
|
24090
|
-
pPr.children.
|
|
24291
|
+
const at = pPr.children.findIndex((c) => localName(c.name) === "sectPr" || localName(c.name) === "pPrChange");
|
|
24292
|
+
pPr.children.splice(at === -1 ? pPr.children.length : at, 0, rPr);
|
|
24091
24293
|
}
|
|
24092
24294
|
return rPr;
|
|
24093
24295
|
}
|
|
@@ -24247,6 +24449,8 @@ function convertDelTextToText(el26) {
|
|
|
24247
24449
|
const walk2 = (e) => {
|
|
24248
24450
|
if (localName(e.name) === "delText") {
|
|
24249
24451
|
e.name = e.name.replace(/delText$/, "t");
|
|
24452
|
+
} else if (localName(e.name) === "delInstrText") {
|
|
24453
|
+
e.name = e.name.replace(/delInstrText$/, "instrText");
|
|
24250
24454
|
}
|
|
24251
24455
|
for (const c of e.children) walk2(c);
|
|
24252
24456
|
};
|
|
@@ -24268,7 +24472,7 @@ function removeEl(doc, el26) {
|
|
|
24268
24472
|
parent.children.splice(idx, 1);
|
|
24269
24473
|
return true;
|
|
24270
24474
|
}
|
|
24271
|
-
var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo"];
|
|
24475
|
+
var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo", "fldSimple"];
|
|
24272
24476
|
function removeRevisionAndEmptyHost(doc, el26) {
|
|
24273
24477
|
const parent = doc.findParentOf(el26);
|
|
24274
24478
|
if (!removeEl(doc, el26)) return false;
|
|
@@ -24277,26 +24481,57 @@ function removeRevisionAndEmptyHost(doc, el26) {
|
|
|
24277
24481
|
}
|
|
24278
24482
|
return true;
|
|
24279
24483
|
}
|
|
24280
|
-
function joinWithNext(doc, pEl) {
|
|
24484
|
+
function joinWithNext(doc, pEl, gone) {
|
|
24281
24485
|
const parent = doc.findParentOf(pEl);
|
|
24282
24486
|
if (!parent) return false;
|
|
24283
24487
|
const idx = parent.children.indexOf(pEl);
|
|
24284
|
-
|
|
24285
|
-
|
|
24488
|
+
let at = idx + 1;
|
|
24489
|
+
while (at < parent.children.length && MARKERS.includes(localName(parent.children[at].name))) at++;
|
|
24490
|
+
const next = parent.children[at];
|
|
24491
|
+
if (!next || localName(next.name) !== "p") {
|
|
24492
|
+
const goes = gone === "del" ? ["pPr", "del", "moveFrom", "proofErr", ...MOVE_RANGES] : ["pPr", "ins", "moveTo", "proofErr", ...MOVE_RANGES];
|
|
24493
|
+
const stays = MARKERS.filter((m) => m !== "proofErr");
|
|
24494
|
+
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);
|
|
24495
|
+
if (!pEl.children.every(removable)) return false;
|
|
24496
|
+
parent.children.splice(idx, 1, ...pEl.children.filter((c) => stays.includes(localName(c.name))));
|
|
24497
|
+
dropEmptiedControl(doc, parent);
|
|
24498
|
+
return true;
|
|
24499
|
+
}
|
|
24500
|
+
const between = parent.children.slice(idx + 1, at);
|
|
24286
24501
|
const moved = next.children.filter((c) => localName(c.name) !== "pPr");
|
|
24287
|
-
pEl.children.push(...moved);
|
|
24288
|
-
parent.children.splice(idx + 1,
|
|
24502
|
+
pEl.children.push(...between, ...moved);
|
|
24503
|
+
parent.children.splice(idx + 1, at - idx);
|
|
24504
|
+
carrySectionBreak(pEl, next);
|
|
24289
24505
|
return true;
|
|
24290
24506
|
}
|
|
24507
|
+
var MARKERS = ["bookmarkStart", "bookmarkEnd", "commentRangeStart", "commentRangeEnd", "permStart", "permEnd", "proofErr"];
|
|
24508
|
+
function carrySectionBreak(pEl, next) {
|
|
24509
|
+
const pPrOf = (p) => p.children.find((c) => localName(c.name) === "pPr");
|
|
24510
|
+
const sectOf = (p) => pPrOf(p)?.children.find((c) => localName(c.name) === "sectPr");
|
|
24511
|
+
const mine = sectOf(pEl);
|
|
24512
|
+
const theirs = sectOf(next);
|
|
24513
|
+
if (!mine && !theirs) return;
|
|
24514
|
+
let pPr = pPrOf(pEl);
|
|
24515
|
+
if (!pPr) {
|
|
24516
|
+
pPr = { name: "w:pPr", attrs: {}, children: [], text: "" };
|
|
24517
|
+
pEl.children.unshift(pPr);
|
|
24518
|
+
}
|
|
24519
|
+
pPr.children = pPr.children.filter((c) => c !== mine);
|
|
24520
|
+
if (theirs) {
|
|
24521
|
+
const at = pPr.children.findIndex((c) => localName(c.name) === "pPrChange");
|
|
24522
|
+
pPr.children.splice(at === -1 ? pPr.children.length : at, 0, theirs);
|
|
24523
|
+
}
|
|
24524
|
+
if (pPr.children.length === 0) pEl.children.splice(pEl.children.indexOf(pPr), 1);
|
|
24525
|
+
}
|
|
24291
24526
|
function collectRevisions(doc) {
|
|
24292
24527
|
const out = [];
|
|
24293
24528
|
const walk2 = (el26) => {
|
|
24294
24529
|
const ln = localName(el26.name);
|
|
24295
24530
|
if (ln === "pPr") return;
|
|
24296
|
-
if (ln === "ins" || ln === "del") {
|
|
24531
|
+
if (ln === "ins" || ln === "del" || ln === "moveTo" || ln === "moveFrom") {
|
|
24297
24532
|
out.push({
|
|
24298
24533
|
el: el26,
|
|
24299
|
-
kind: ln === "ins" ? "insertion" : "deletion",
|
|
24534
|
+
kind: ln === "ins" || ln === "moveTo" ? "insertion" : "deletion",
|
|
24300
24535
|
author: attrByLocal(el26, "author")
|
|
24301
24536
|
});
|
|
24302
24537
|
}
|
|
@@ -24330,10 +24565,10 @@ function collectRevisions(doc) {
|
|
|
24330
24565
|
const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
|
|
24331
24566
|
for (const c of rPr?.children ?? []) {
|
|
24332
24567
|
const cn = localName(c.name);
|
|
24333
|
-
if (cn === "ins" || cn === "del") {
|
|
24568
|
+
if (cn === "ins" || cn === "del" || cn === "moveTo" || cn === "moveFrom") {
|
|
24334
24569
|
out.push({
|
|
24335
24570
|
el: c,
|
|
24336
|
-
kind: cn === "ins" ? "markInsertion" : "markDeletion",
|
|
24571
|
+
kind: cn === "ins" || cn === "moveTo" ? "markInsertion" : "markDeletion",
|
|
24337
24572
|
paragraph: el26,
|
|
24338
24573
|
author: attrByLocal(c, "author")
|
|
24339
24574
|
});
|
|
@@ -24355,7 +24590,7 @@ function applyAccept(doc, ref) {
|
|
|
24355
24590
|
case "markDeletion":
|
|
24356
24591
|
if (!ref.paragraph) return false;
|
|
24357
24592
|
removeMarkRevision(ref);
|
|
24358
|
-
return joinWithNext(doc, ref.paragraph);
|
|
24593
|
+
return joinWithNext(doc, ref.paragraph, "del");
|
|
24359
24594
|
case "rowInsertion":
|
|
24360
24595
|
return removeRowRevision(doc, ref);
|
|
24361
24596
|
case "rowDeletion":
|
|
@@ -24426,7 +24661,7 @@ function applyReject(doc, ref) {
|
|
|
24426
24661
|
case "markInsertion":
|
|
24427
24662
|
if (!ref.paragraph) return false;
|
|
24428
24663
|
removeMarkRevision(ref);
|
|
24429
|
-
return joinWithNext(doc, ref.paragraph);
|
|
24664
|
+
return joinWithNext(doc, ref.paragraph, "ins");
|
|
24430
24665
|
case "markDeletion":
|
|
24431
24666
|
return removeMarkRevision(ref);
|
|
24432
24667
|
case "rowInsertion":
|
|
@@ -24477,11 +24712,23 @@ function applyToAll(doc, apply, author) {
|
|
|
24477
24712
|
for (const part of held) notes.add(part);
|
|
24478
24713
|
}
|
|
24479
24714
|
if (n > 0) {
|
|
24715
|
+
dropSettledMoveRanges(doc);
|
|
24480
24716
|
doc.markNotePartsChanged([...notes]);
|
|
24481
24717
|
doc.refresh();
|
|
24482
24718
|
}
|
|
24483
24719
|
return n;
|
|
24484
24720
|
}
|
|
24721
|
+
var MOVE_RANGES = ["moveFromRangeStart", "moveFromRangeEnd", "moveToRangeStart", "moveToRangeEnd"];
|
|
24722
|
+
function dropSettledMoveRanges(doc) {
|
|
24723
|
+
const roots = doc.revisionRoots();
|
|
24724
|
+
const has = (el26) => ["moveFrom", "moveTo"].includes(localName(el26.name)) || el26.children.some(has);
|
|
24725
|
+
if (roots.some(has)) return;
|
|
24726
|
+
const strip = (el26) => {
|
|
24727
|
+
el26.children = el26.children.filter((c) => !MOVE_RANGES.includes(localName(c.name)));
|
|
24728
|
+
for (const c of el26.children) strip(c);
|
|
24729
|
+
};
|
|
24730
|
+
for (const root of roots) strip(root);
|
|
24731
|
+
}
|
|
24485
24732
|
function removeMarkRevision(ref) {
|
|
24486
24733
|
const pPr = ref.paragraph?.children.find((c) => localName(c.name) === "pPr");
|
|
24487
24734
|
const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
|
|
@@ -24513,9 +24760,24 @@ function removeRow(doc, ref) {
|
|
|
24513
24760
|
const idx = tbl.children.indexOf(ref.row);
|
|
24514
24761
|
if (idx === -1) return false;
|
|
24515
24762
|
tbl.children.splice(idx, 1);
|
|
24516
|
-
if (!tbl.children.some((c) => localName(c.name) === "tr"))
|
|
24763
|
+
if (!tbl.children.some((c) => localName(c.name) === "tr")) {
|
|
24764
|
+
const holder = doc.findParentOf(tbl);
|
|
24765
|
+
removeEl(doc, tbl);
|
|
24766
|
+
if (holder) dropEmptiedControl(doc, holder);
|
|
24767
|
+
}
|
|
24517
24768
|
return true;
|
|
24518
24769
|
}
|
|
24770
|
+
function dropEmptiedControl(doc, container) {
|
|
24771
|
+
const ln = localName(container.name);
|
|
24772
|
+
const control = ln === "sdtContent" ? doc.findParentOf(container) : ln === "customXml" ? container : void 0;
|
|
24773
|
+
if (!control) return;
|
|
24774
|
+
const left = container.children.filter((c) => localName(c.name) !== "customXmlPr");
|
|
24775
|
+
if (left.some((c) => !MARKERS.includes(localName(c.name)))) return;
|
|
24776
|
+
const holder = doc.findParentOf(control);
|
|
24777
|
+
if (!holder) return;
|
|
24778
|
+
holder.children.splice(holder.children.indexOf(control), 1, ...left.filter((c) => localName(c.name) !== "proofErr"));
|
|
24779
|
+
dropEmptiedControl(doc, holder);
|
|
24780
|
+
}
|
|
24519
24781
|
|
|
24520
24782
|
// ../core/src/edit/blocks.ts
|
|
24521
24783
|
function el3(name, attrs = {}, children3 = [], text = "") {
|
|
@@ -24859,8 +25121,32 @@ function mergeParagraphBackward(doc, pEl) {
|
|
|
24859
25121
|
const moved = pEl.children.filter((c) => localName(c.name) !== "pPr" && !isPlaceholderRun(c));
|
|
24860
25122
|
prev.children.push(...moved);
|
|
24861
25123
|
parent.children.splice(idx, 1);
|
|
25124
|
+
carryMarkRevision(prev, pEl);
|
|
24862
25125
|
return finishMerge(prev);
|
|
24863
25126
|
}
|
|
25127
|
+
function carryMarkRevision(survivor, gone) {
|
|
25128
|
+
const markOf = (p) => p.children.find((c) => localName(c.name) === "pPr")?.children.find((c) => localName(c.name) === "rPr");
|
|
25129
|
+
const isRev = (c) => localName(c.name) === "ins" || localName(c.name) === "del";
|
|
25130
|
+
const theirs = markOf(gone)?.children.filter(isRev) ?? [];
|
|
25131
|
+
let pPr = survivor.children.find((c) => localName(c.name) === "pPr");
|
|
25132
|
+
let rPr = markOf(survivor);
|
|
25133
|
+
if (!rPr && theirs.length === 0) return;
|
|
25134
|
+
if (!rPr) {
|
|
25135
|
+
const w = survivor.name.includes(":") ? survivor.name.slice(0, survivor.name.indexOf(":") + 1) : "";
|
|
25136
|
+
if (!pPr) {
|
|
25137
|
+
pPr = { name: `${w}pPr`, attrs: {}, children: [], text: "" };
|
|
25138
|
+
survivor.children.unshift(pPr);
|
|
25139
|
+
}
|
|
25140
|
+
rPr = { name: `${w}rPr`, attrs: {}, children: [], text: "" };
|
|
25141
|
+
const at = pPr.children.findIndex((c) => localName(c.name) === "sectPr" || localName(c.name) === "pPrChange");
|
|
25142
|
+
pPr.children.splice(at === -1 ? pPr.children.length : at, 0, rPr);
|
|
25143
|
+
}
|
|
25144
|
+
rPr.children = [...theirs, ...rPr.children.filter((c) => !isRev(c))];
|
|
25145
|
+
if (rPr.children.length === 0 && pPr) {
|
|
25146
|
+
pPr.children.splice(pPr.children.indexOf(rPr), 1);
|
|
25147
|
+
if (pPr.children.length === 0) survivor.children.splice(survivor.children.indexOf(pPr), 1);
|
|
25148
|
+
}
|
|
25149
|
+
}
|
|
24864
25150
|
function firstTextOf(pEl) {
|
|
24865
25151
|
const walk2 = (el26) => {
|
|
24866
25152
|
for (const c of el26.children) {
|
|
@@ -27815,7 +28101,7 @@ function sortTableRows(doc, tblEl, colIdx, order, compare, hasHeader = false) {
|
|
|
27815
28101
|
return tc ? cellText(tc).trim() : "";
|
|
27816
28102
|
};
|
|
27817
28103
|
const numeric2 = (s) => parseFloat(s.replace(/[^0-9.eE+-]/g, ""));
|
|
27818
|
-
const
|
|
28104
|
+
const compareRows2 = (a, b) => {
|
|
27819
28105
|
const ka = keyOf2(a);
|
|
27820
28106
|
const kb = keyOf2(b);
|
|
27821
28107
|
let d;
|
|
@@ -27831,7 +28117,7 @@ function sortTableRows(doc, tblEl, colIdx, order, compare, hasHeader = false) {
|
|
|
27831
28117
|
}
|
|
27832
28118
|
return order === "asc" ? d : -d;
|
|
27833
28119
|
};
|
|
27834
|
-
const sorted = [...body].sort(
|
|
28120
|
+
const sorted = [...body].sort(compareRows2);
|
|
27835
28121
|
if (sorted.every((tr, i) => tr === body[i])) return false;
|
|
27836
28122
|
const slots = body.map((tr) => tblEl.children.indexOf(tr));
|
|
27837
28123
|
sorted.forEach((tr, i) => {
|
|
@@ -41791,17 +42077,7 @@ var _DocxEditor = class _DocxEditor {
|
|
|
41791
42077
|
if (pEl && firstTextOf(pEl) === caret.t) {
|
|
41792
42078
|
this.host.history?.checkpoint();
|
|
41793
42079
|
if (this.clearListAtCaret()) return;
|
|
41794
|
-
|
|
41795
|
-
if (!prev) return;
|
|
41796
|
-
const junction = lastTextOf(prev);
|
|
41797
|
-
const mergeBlockId = this.host.onIntent && this.host.doc.stableIds ? this.host.doc.stableIds.idOf(pEl) : void 0;
|
|
41798
|
-
if (mergeParagraphBackward(this.host.doc, pEl)) {
|
|
41799
|
-
if (mergeBlockId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeBlockId });
|
|
41800
|
-
const target = junction && paragraphOf(this.host.doc, junction) ? junction : caret.t;
|
|
41801
|
-
this.caret = { t: target, run: caret.run, offset: target === junction ? target.text.length : 0 };
|
|
41802
|
-
invalidateParagraphSignature(paragraphOf(this.host.doc, target) ?? pEl);
|
|
41803
|
-
this.commit(false, "local", true);
|
|
41804
|
-
}
|
|
42080
|
+
this.mergeIntoPrevious(pEl);
|
|
41805
42081
|
return;
|
|
41806
42082
|
}
|
|
41807
42083
|
if (!this.stepToNeighbor(-1)) return;
|
|
@@ -41817,19 +42093,8 @@ var _DocxEditor = class _DocxEditor {
|
|
|
41817
42093
|
if (caret.offset >= caret.t.text.length) {
|
|
41818
42094
|
const pEl = paragraphOf(this.host.doc, caret.t);
|
|
41819
42095
|
if (pEl && lastTextOf(pEl) === caret.t) {
|
|
41820
|
-
const next = siblingParagraph(this.host.doc, pEl, 1);
|
|
41821
|
-
if (!next) return;
|
|
41822
42096
|
this.host.history?.checkpoint();
|
|
41823
|
-
|
|
41824
|
-
if (mergeParagraphBackward(this.host.doc, next)) {
|
|
41825
|
-
if (mergeNextId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeNextId });
|
|
41826
|
-
if (!paragraphOf(this.host.doc, caret.t)) {
|
|
41827
|
-
const t = firstTextOf(next);
|
|
41828
|
-
if (t) this.caret = { t, run: caret.run, offset: 0 };
|
|
41829
|
-
}
|
|
41830
|
-
invalidateParagraphSignature(paragraphOf(this.host.doc, this.caret?.t ?? caret.t) ?? pEl);
|
|
41831
|
-
this.commit(false, "local", true);
|
|
41832
|
-
}
|
|
42097
|
+
this.mergeNextInto(pEl);
|
|
41833
42098
|
return;
|
|
41834
42099
|
}
|
|
41835
42100
|
if (!this.stepToNeighbor(1)) return;
|
|
@@ -41847,6 +42112,44 @@ var _DocxEditor = class _DocxEditor {
|
|
|
41847
42112
|
emitDelete(blockId, runId, start, end) {
|
|
41848
42113
|
if (end > start) this.host.onIntent?.({ kind: "deleteText", blockId, runId, start, end });
|
|
41849
42114
|
}
|
|
42115
|
+
/** Backspace at the start of `pEl`: merge it into the previous paragraph. */
|
|
42116
|
+
mergeIntoPrevious(pEl) {
|
|
42117
|
+
const caret = this.caret;
|
|
42118
|
+
if (!caret) return;
|
|
42119
|
+
const prev = siblingParagraph(this.host.doc, pEl, -1);
|
|
42120
|
+
if (!prev) return;
|
|
42121
|
+
const junction = lastTextOf(prev);
|
|
42122
|
+
const mergeBlockId = this.host.onIntent && this.host.doc.stableIds ? this.host.doc.stableIds.idOf(pEl) : void 0;
|
|
42123
|
+
if (mergeParagraphBackward(this.host.doc, pEl)) {
|
|
42124
|
+
if (mergeBlockId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeBlockId });
|
|
42125
|
+
const target = junction && paragraphOf(this.host.doc, junction) ? junction : caret.t;
|
|
42126
|
+
this.caret = { t: target, run: caret.run, offset: target === junction ? target.text.length : 0 };
|
|
42127
|
+
invalidateParagraphSignature(paragraphOf(this.host.doc, target) ?? pEl);
|
|
42128
|
+
this.commit(false, "local", true);
|
|
42129
|
+
}
|
|
42130
|
+
}
|
|
42131
|
+
/** Delete at the end of `pEl`: merge the next paragraph into it. */
|
|
42132
|
+
mergeNextInto(pEl) {
|
|
42133
|
+
const caret = this.caret;
|
|
42134
|
+
if (!caret) return;
|
|
42135
|
+
const next = siblingParagraph(this.host.doc, pEl, 1);
|
|
42136
|
+
if (!next) return;
|
|
42137
|
+
const mergeNextId = this.host.onIntent && this.host.doc.stableIds ? this.host.doc.stableIds.idOf(next) : void 0;
|
|
42138
|
+
if (mergeParagraphBackward(this.host.doc, next)) {
|
|
42139
|
+
if (mergeNextId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeNextId });
|
|
42140
|
+
if (!paragraphOf(this.host.doc, caret.t)) {
|
|
42141
|
+
const t = firstTextOf(next);
|
|
42142
|
+
if (t) this.caret = { t, run: caret.run, offset: 0 };
|
|
42143
|
+
}
|
|
42144
|
+
invalidateParagraphSignature(paragraphOf(this.host.doc, this.caret?.t ?? caret.t) ?? pEl);
|
|
42145
|
+
this.commit(false, "local", true);
|
|
42146
|
+
}
|
|
42147
|
+
}
|
|
42148
|
+
/** True when `pEl`'s paragraph mark is a break this author suggested. */
|
|
42149
|
+
ownSuggestedBreak(pEl) {
|
|
42150
|
+
const mark = paragraphGlyphRevision(pEl, "ins");
|
|
42151
|
+
return !!mark && attr(mark, "author") === this.revisionAuthor;
|
|
42152
|
+
}
|
|
41850
42153
|
/** Backspace/Delete in suggesting mode: mark one character (or a paragraph
|
|
41851
42154
|
* mark at a boundary) as a tracked deletion instead of removing it. Skips over
|
|
41852
42155
|
* text already struck by this author so a repeated press keeps eating real
|
|
@@ -41867,6 +42170,10 @@ var _DocxEditor = class _DocxEditor {
|
|
|
41867
42170
|
if (this.suggestUnbulletAtCaret()) return;
|
|
41868
42171
|
const prev = siblingParagraph(doc, pEl, -1);
|
|
41869
42172
|
if (!prev) return;
|
|
42173
|
+
if (this.ownSuggestedBreak(prev)) {
|
|
42174
|
+
this.mergeIntoPrevious(pEl);
|
|
42175
|
+
return;
|
|
42176
|
+
}
|
|
41870
42177
|
if (paragraphGlyphRevision(prev, "del")) {
|
|
41871
42178
|
this.stepToNeighbor(-1);
|
|
41872
42179
|
this.positionCaret();
|
|
@@ -41896,6 +42203,10 @@ var _DocxEditor = class _DocxEditor {
|
|
|
41896
42203
|
if (caret.offset >= caret.t.text.length) {
|
|
41897
42204
|
const pEl = paragraphOf(doc, caret.t);
|
|
41898
42205
|
if (pEl && lastTextOf(pEl) === caret.t) {
|
|
42206
|
+
if (this.ownSuggestedBreak(pEl)) {
|
|
42207
|
+
this.mergeNextInto(pEl);
|
|
42208
|
+
return;
|
|
42209
|
+
}
|
|
41899
42210
|
if (paragraphGlyphRevision(pEl, "del")) {
|
|
41900
42211
|
this.stepToNeighbor(1);
|
|
41901
42212
|
this.positionCaret();
|
|
@@ -43010,6 +43321,15 @@ var COMPARE_TUNING = {
|
|
|
43010
43321
|
shortParagraphTokens: 5,
|
|
43011
43322
|
/** Levenshtein ratio a short paragraph pair must reach instead of minDice. */
|
|
43012
43323
|
minShortRatio: 0.8,
|
|
43324
|
+
/**
|
|
43325
|
+
* Or the share of words a short pair has in common (Dice over words), which
|
|
43326
|
+
* a one-word edit in a short heading or list item keeps at half or more
|
|
43327
|
+
* ("Bullet one" / "Bullet updated", "Alignment and flow" / "Alignment and
|
|
43328
|
+
* flow as amended") while its character ratio falls under minShortRatio.
|
|
43329
|
+
* Below it, those came out struck and reinserted whole: 99% of the compare
|
|
43330
|
+
* benchmark's whole-paragraph misses were paragraphs under five words.
|
|
43331
|
+
*/
|
|
43332
|
+
minShortWordDice: 0.5,
|
|
43013
43333
|
/**
|
|
43014
43334
|
* ABSOLUTE floor for the coalescing pass: a matched run shorter than this,
|
|
43015
43335
|
* sitting between two changes in the inner word diff, is absorbed into the
|
|
@@ -43028,15 +43348,19 @@ var COMPARE_TUNING = {
|
|
|
43028
43348
|
* next to 1) while a rewritten clause becomes one (3 is small next to 7).
|
|
43029
43349
|
*
|
|
43030
43350
|
* Both numbers were settled against test/compare-corpus.test.ts; see the
|
|
43031
|
-
* calibration note there for what each fixture pins down.
|
|
43351
|
+
* calibration note there for what each fixture pins down. The ratio was 2
|
|
43352
|
+
* while spaces rode on words: "hardware," and "hardware " differed, so the
|
|
43353
|
+
* rewritten sentence of fixture 3 shared only three words. Matched exactly,
|
|
43354
|
+
* it shares four ("shortage of test hardware") beside seven changed, which
|
|
43355
|
+
* 1.5 absorbs (6 < 7) and 2 does not.
|
|
43032
43356
|
*/
|
|
43033
|
-
coalesceRatio:
|
|
43357
|
+
coalesceRatio: 1.5
|
|
43034
43358
|
};
|
|
43035
43359
|
function normalizeText(s) {
|
|
43036
43360
|
return s.replace(/\s+/g, " ").trim();
|
|
43037
43361
|
}
|
|
43038
43362
|
function wordTokens(s) {
|
|
43039
|
-
return s.match(
|
|
43363
|
+
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) ?? [];
|
|
43040
43364
|
}
|
|
43041
43365
|
function similarityTokens(s) {
|
|
43042
43366
|
const n = normalizeText(s).toLowerCase();
|
|
@@ -43082,14 +43406,16 @@ function similarity(a, b) {
|
|
|
43082
43406
|
const nb = normalizeText(b).toLowerCase();
|
|
43083
43407
|
const longest = Math.max(na.length, nb.length);
|
|
43084
43408
|
if (longest === 0) return 1;
|
|
43085
|
-
|
|
43409
|
+
const ratio = 1 - levenshtein(na, nb) / longest;
|
|
43410
|
+
const words2 = dice(new Set(ta), new Set(tb));
|
|
43411
|
+
return Math.max(words2, ratio >= COMPARE_TUNING.minShortRatio ? ratio : 0);
|
|
43086
43412
|
}
|
|
43087
43413
|
return dice(bigrams(ta), bigrams(tb));
|
|
43088
43414
|
}
|
|
43089
43415
|
function similarityFloor(a, b) {
|
|
43090
43416
|
const la = similarityTokens(a).length;
|
|
43091
43417
|
const lb = similarityTokens(b).length;
|
|
43092
|
-
return isShortPair(la, lb) ? COMPARE_TUNING.
|
|
43418
|
+
return isShortPair(la, lb) ? COMPARE_TUNING.minShortWordDice : COMPARE_TUNING.minDice;
|
|
43093
43419
|
}
|
|
43094
43420
|
function isShortPair(la, lb) {
|
|
43095
43421
|
return Math.min(la, lb) < COMPARE_TUNING.shortParagraphTokens;
|
|
@@ -43100,6 +43426,25 @@ var PASSENGERS = /* @__PURE__ */ new Set(["proofErr", "bookmarkStart", "bookmark
|
|
|
43100
43426
|
function isPassenger(ln) {
|
|
43101
43427
|
return PASSENGERS.has(ln);
|
|
43102
43428
|
}
|
|
43429
|
+
var WORD_CHAR = /[\p{L}\p{M}\p{N}]/u;
|
|
43430
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["hyperlink", "smartTag", "fldSimple", "customXml", "dir", "bdo", "sdt"]);
|
|
43431
|
+
var CONTAINER_PROPS = /* @__PURE__ */ new Set(["pPr", "sdtPr", "sdtEndPr", "smartTagPr", "customXmlPr"]);
|
|
43432
|
+
function atomKey(atom, noteKey) {
|
|
43433
|
+
const ln = localName(atom.name);
|
|
43434
|
+
if (ln === "footnoteReference" || ln === "endnoteReference") {
|
|
43435
|
+
const { "w:id": _id, ...rest } = atom.attrs;
|
|
43436
|
+
return serializeXml({ ...atom, attrs: rest }) + (noteKey ? "\0" + noteKey(atom) : "");
|
|
43437
|
+
}
|
|
43438
|
+
const strip = (e) => localName(e.name) === "txbxContent" ? { ...e, children: [], text: "" } : { ...e, children: e.children.map(strip) };
|
|
43439
|
+
return serializeXml(hasTextBox(atom) ? strip(atom) : atom);
|
|
43440
|
+
}
|
|
43441
|
+
function hasTextBox(el26) {
|
|
43442
|
+
return localName(el26.name) === "txbxContent" || el26.children.some(hasTextBox);
|
|
43443
|
+
}
|
|
43444
|
+
function textBoxesIn(el26) {
|
|
43445
|
+
if (localName(el26.name) === "txbxContent") return [el26];
|
|
43446
|
+
return el26.children.flatMap(textBoxesIn);
|
|
43447
|
+
}
|
|
43103
43448
|
function readParagraph(pEl, options = {}) {
|
|
43104
43449
|
const keyOf2 = options.whitespace === false ? (word) => word.trimEnd() : (word) => word;
|
|
43105
43450
|
const tokens = [];
|
|
@@ -43112,31 +43457,80 @@ function readParagraph(pEl, options = {}) {
|
|
|
43112
43457
|
}
|
|
43113
43458
|
tokens.push(token);
|
|
43114
43459
|
};
|
|
43115
|
-
|
|
43116
|
-
|
|
43117
|
-
|
|
43118
|
-
if (
|
|
43119
|
-
|
|
43120
|
-
|
|
43121
|
-
|
|
43122
|
-
|
|
43123
|
-
|
|
43124
|
-
|
|
43125
|
-
|
|
43126
|
-
|
|
43127
|
-
|
|
43128
|
-
|
|
43129
|
-
|
|
43130
|
-
|
|
43131
|
-
|
|
43132
|
-
|
|
43133
|
-
|
|
43134
|
-
|
|
43460
|
+
let segment = [];
|
|
43461
|
+
let segmentPath = [];
|
|
43462
|
+
const flush = () => {
|
|
43463
|
+
if (segment.length === 0) return;
|
|
43464
|
+
const text = segment.map((p) => p.text).join("");
|
|
43465
|
+
const cuts = [];
|
|
43466
|
+
let base = 0;
|
|
43467
|
+
for (let i = 0; i + 1 < segment.length; i++) {
|
|
43468
|
+
base += segment[i].text.length;
|
|
43469
|
+
if (WORD_CHAR.test(text[base - 1] ?? "") && WORD_CHAR.test(text[base] ?? "") && runPropsKey(segment[i].run) !== runPropsKey(segment[i + 1].run)) {
|
|
43470
|
+
cuts.push(base);
|
|
43471
|
+
}
|
|
43472
|
+
}
|
|
43473
|
+
const words2 = [];
|
|
43474
|
+
let from = 0;
|
|
43475
|
+
for (const cut of [...cuts, text.length]) {
|
|
43476
|
+
words2.push(...wordTokens(text.slice(from, cut)));
|
|
43477
|
+
from = cut;
|
|
43478
|
+
}
|
|
43479
|
+
let at = 0;
|
|
43480
|
+
for (const word of words2) {
|
|
43481
|
+
const end = at + word.length;
|
|
43482
|
+
const pieces = [];
|
|
43483
|
+
let base2 = 0;
|
|
43484
|
+
for (const p of segment) {
|
|
43485
|
+
const from2 = Math.max(at, base2);
|
|
43486
|
+
const to = Math.min(end, base2 + p.text.length);
|
|
43487
|
+
if (from2 < to) pieces.push({ run: p.run, t: p.t, start: from2 - base2, end: to - base2, text: text.slice(from2, to) });
|
|
43488
|
+
base2 += p.text.length;
|
|
43489
|
+
}
|
|
43490
|
+
const first = pieces[0];
|
|
43491
|
+
push({ key: "w\0" + keyOf2(word), text: word, run: first.run, t: first.t, start: first.start, end: first.end, pieces, path: segmentPath });
|
|
43492
|
+
at = end;
|
|
43493
|
+
}
|
|
43494
|
+
segment = [];
|
|
43495
|
+
};
|
|
43496
|
+
const walk2 = (kids, path2) => {
|
|
43497
|
+
for (const cEl of kids) {
|
|
43498
|
+
const ln = localName(cEl.name);
|
|
43499
|
+
if (CONTAINER_PROPS.has(ln)) continue;
|
|
43500
|
+
if (PASSENGERS.has(ln)) {
|
|
43501
|
+
flush();
|
|
43502
|
+
pending.push(cEl);
|
|
43503
|
+
continue;
|
|
43504
|
+
}
|
|
43505
|
+
if (CONTAINERS.has(ln)) {
|
|
43506
|
+
const inner = ln === "sdt" ? cEl.children.find((c) => localName(c.name) === "sdtContent") : cEl;
|
|
43507
|
+
if (!inner) continue;
|
|
43508
|
+
flush();
|
|
43509
|
+
walk2(inner.children, [...path2, cEl]);
|
|
43510
|
+
flush();
|
|
43135
43511
|
continue;
|
|
43136
43512
|
}
|
|
43137
|
-
|
|
43513
|
+
if (ln !== "r") {
|
|
43514
|
+
flush();
|
|
43515
|
+
inlineDiffable = false;
|
|
43516
|
+
continue;
|
|
43517
|
+
}
|
|
43518
|
+
if (segment.length > 0 && segmentPath !== path2) flush();
|
|
43519
|
+
segmentPath = path2;
|
|
43520
|
+
for (const rc of cEl.children) {
|
|
43521
|
+
const rln = localName(rc.name);
|
|
43522
|
+
if (rln === "rPr") continue;
|
|
43523
|
+
if (rln === "t") {
|
|
43524
|
+
segment.push({ run: cEl, t: rc, text: rc.text });
|
|
43525
|
+
continue;
|
|
43526
|
+
}
|
|
43527
|
+
flush();
|
|
43528
|
+
push({ key: "a\0" + atomKey(rc, options.noteKey), text: "", run: cEl, atom: rc, path: path2 });
|
|
43529
|
+
}
|
|
43138
43530
|
}
|
|
43139
|
-
}
|
|
43531
|
+
};
|
|
43532
|
+
walk2(pEl.children, []);
|
|
43533
|
+
flush();
|
|
43140
43534
|
return { tokens, tail: pending, text: paragraphText3(pEl), inlineDiffable };
|
|
43141
43535
|
}
|
|
43142
43536
|
function paragraphText3(el26) {
|
|
@@ -43153,31 +43547,41 @@ function paragraphText3(el26) {
|
|
|
43153
43547
|
}
|
|
43154
43548
|
return out;
|
|
43155
43549
|
}
|
|
43156
|
-
function
|
|
43550
|
+
function sliceItems(tokens, from, to, deleted) {
|
|
43157
43551
|
const out = [];
|
|
43158
|
-
let
|
|
43159
|
-
|
|
43160
|
-
|
|
43161
|
-
if (
|
|
43162
|
-
const run = token.run;
|
|
43552
|
+
let open = null;
|
|
43553
|
+
let text = null;
|
|
43554
|
+
const runFor = (run, path2) => {
|
|
43555
|
+
if (open?.run === run) return open.el;
|
|
43163
43556
|
const rPr = run.children.find((c) => localName(c.name) === "rPr");
|
|
43164
|
-
const
|
|
43165
|
-
|
|
43166
|
-
|
|
43167
|
-
|
|
43168
|
-
|
|
43169
|
-
|
|
43557
|
+
const el26 = { name: run.name, attrs: { ...run.attrs }, children: rPr ? [cloneXml(rPr)] : [], text: "" };
|
|
43558
|
+
out.push({ el: el26, path: path2 });
|
|
43559
|
+
open = { run, el: el26 };
|
|
43560
|
+
text = null;
|
|
43561
|
+
return el26;
|
|
43562
|
+
};
|
|
43563
|
+
for (let i = from; i < to; i++) {
|
|
43564
|
+
const token = tokens[i];
|
|
43565
|
+
if (!deleted && token.lead) {
|
|
43566
|
+
out.push(...token.lead.map((el26) => ({ el: cloneXml(el26), path: token.path })));
|
|
43567
|
+
open = null;
|
|
43568
|
+
text = null;
|
|
43569
|
+
}
|
|
43570
|
+
if (token.atom) {
|
|
43571
|
+
runFor(token.run, token.path).children.push(cloneXml(token.atom));
|
|
43572
|
+
text = null;
|
|
43573
|
+
continue;
|
|
43574
|
+
}
|
|
43575
|
+
for (const piece of token.pieces ?? []) {
|
|
43576
|
+
const el26 = runFor(piece.run, token.path);
|
|
43577
|
+
if (text && text.t === piece.t) {
|
|
43578
|
+
text.el.text += piece.text;
|
|
43170
43579
|
continue;
|
|
43171
43580
|
}
|
|
43172
|
-
const
|
|
43173
|
-
|
|
43174
|
-
|
|
43175
|
-
text += tokens[i].text;
|
|
43176
|
-
i++;
|
|
43177
|
-
}
|
|
43178
|
-
children3.push(textElement2(t, text, deleted));
|
|
43581
|
+
const te = textElement2(piece.t, piece.text, deleted);
|
|
43582
|
+
el26.children.push(te);
|
|
43583
|
+
text = { t: piece.t, el: te };
|
|
43179
43584
|
}
|
|
43180
|
-
out.push({ name: run.name, attrs: { ...run.attrs }, children: children3, text: "" });
|
|
43181
43585
|
}
|
|
43182
43586
|
return out;
|
|
43183
43587
|
}
|
|
@@ -43284,46 +43688,232 @@ function lcsFallback(a, b, aLo, aHi, bLo, bHi, out) {
|
|
|
43284
43688
|
|
|
43285
43689
|
// ../core/src/edit/compare/index.ts
|
|
43286
43690
|
function compareDocuments(original, revised, options) {
|
|
43287
|
-
const
|
|
43691
|
+
const note = options.onNote ?? (() => {
|
|
43692
|
+
});
|
|
43693
|
+
const accepted = (doc, which) => {
|
|
43694
|
+
if (collectRevisions(doc).length === 0) return doc;
|
|
43695
|
+
const copy = doc.clone();
|
|
43696
|
+
acceptAllRevisions(copy);
|
|
43697
|
+
note({ code: "existing-revisions-accepted", detail: `the ${which} document held tracked changes; it was compared as if they were accepted` });
|
|
43698
|
+
return copy;
|
|
43699
|
+
};
|
|
43700
|
+
const merged = accepted(revised, "revised");
|
|
43701
|
+
return compareInto(accepted(original, "original"), merged === revised ? revised.clone() : merged, options);
|
|
43702
|
+
}
|
|
43703
|
+
function compareInto(original, merged, options) {
|
|
43704
|
+
let issued = 0;
|
|
43288
43705
|
const ctx = {
|
|
43289
43706
|
doc: merged,
|
|
43290
43707
|
original,
|
|
43291
43708
|
meta: {
|
|
43292
43709
|
author: options.author,
|
|
43293
43710
|
date: options.date,
|
|
43294
|
-
nextId: () =>
|
|
43711
|
+
nextId: () => {
|
|
43712
|
+
issued++;
|
|
43713
|
+
return merged.nextRevisionId();
|
|
43714
|
+
}
|
|
43295
43715
|
},
|
|
43296
43716
|
formatting: options.formatting !== false,
|
|
43297
43717
|
whitespace: options.whitespace !== false,
|
|
43298
43718
|
note: options.onNote ?? (() => {
|
|
43299
|
-
})
|
|
43719
|
+
}),
|
|
43720
|
+
scope: { from: null, into: null },
|
|
43721
|
+
noteIds: { footnote: /* @__PURE__ */ new Map(), endnote: /* @__PURE__ */ new Map() }
|
|
43300
43722
|
};
|
|
43301
43723
|
const originalBody = child(original.docRoot, "body");
|
|
43302
43724
|
const mergedBody = child(merged.docRoot, "body");
|
|
43303
43725
|
if (!originalBody || !mergedBody) throw new Error("document.xml has no w:body");
|
|
43304
|
-
reportUncomparedParts(original,
|
|
43726
|
+
reportUncomparedParts(original, merged, ctx);
|
|
43727
|
+
const headerFooterPairs = pairHeadersFooters(original, merged);
|
|
43728
|
+
compareNotes("footnote", original, merged, ctx);
|
|
43729
|
+
compareNotes("endnote", original, merged, ctx);
|
|
43305
43730
|
const tail = mergedBody.children.filter((c) => localName(c.name) === "sectPr");
|
|
43306
43731
|
const entries = alignBlocks(blockList(originalBody, "sectPr"), blockList(mergedBody, "sectPr"), ctx);
|
|
43307
43732
|
applyParagraphMarks(entries, ctx);
|
|
43308
43733
|
mergedBody.children = [...entries.map((e) => e.el), ...tail];
|
|
43309
|
-
merged
|
|
43734
|
+
giveStruckReferencesTheirOwnNotes("footnote", merged, ctx);
|
|
43735
|
+
giveStruckReferencesTheirOwnNotes("endnote", merged, ctx);
|
|
43736
|
+
for (const [from, into] of headerFooterPairs) {
|
|
43737
|
+
const oRoot = original.headerFooterRoot(from);
|
|
43738
|
+
const mRoot = merged.headerFooterRoot(into);
|
|
43739
|
+
if (oRoot && mRoot) compareContainer(oRoot, mRoot, { ...ctx, scope: { from, into } });
|
|
43740
|
+
}
|
|
43741
|
+
if (issued > 0) merged.refresh();
|
|
43310
43742
|
return merged;
|
|
43311
43743
|
}
|
|
43744
|
+
function compareContainer(oRoot, mRoot, ctx) {
|
|
43745
|
+
const o = blockList(oRoot);
|
|
43746
|
+
const m = blockList(mRoot);
|
|
43747
|
+
if (o.length === m.length && o.every((el26, i) => sameXml(el26, m[i]))) return;
|
|
43748
|
+
const entries = alignBlocks(o, m, ctx);
|
|
43749
|
+
applyParagraphMarks(entries, ctx);
|
|
43750
|
+
const blocks = new Set(m);
|
|
43751
|
+
mRoot.children = [...mRoot.children.filter((c) => !blocks.has(c) && !["p", "tbl", "sdt"].includes(localName(c.name))), ...entries.map((e) => e.el)];
|
|
43752
|
+
}
|
|
43753
|
+
function pairHeadersFooters(original, merged) {
|
|
43754
|
+
const refs = (doc) => {
|
|
43755
|
+
const out = /* @__PURE__ */ new Map();
|
|
43756
|
+
const body = child(doc.docRoot, "body");
|
|
43757
|
+
const sects = (body?.children ?? []).flatMap(
|
|
43758
|
+
(c) => localName(c.name) === "sectPr" ? [c] : localName(c.name) === "p" ? [child(child(c, "pPr"), "sectPr")].filter((x) => !!x) : []
|
|
43759
|
+
);
|
|
43760
|
+
sects.forEach((sp, i) => {
|
|
43761
|
+
for (const ref of sp.children) {
|
|
43762
|
+
const ln = localName(ref.name);
|
|
43763
|
+
if (ln !== "headerReference" && ln !== "footerReference") continue;
|
|
43764
|
+
const target = doc.documentRels.get(ref.attrs["r:id"])?.target;
|
|
43765
|
+
if (target) out.set(`${i}:${ln}:${ref.attrs["w:type"] ?? "default"}`, target);
|
|
43766
|
+
}
|
|
43767
|
+
});
|
|
43768
|
+
return out;
|
|
43769
|
+
};
|
|
43770
|
+
const mine = refs(merged);
|
|
43771
|
+
const pairs = [];
|
|
43772
|
+
const done = /* @__PURE__ */ new Set();
|
|
43773
|
+
for (const [key, from] of refs(original)) {
|
|
43774
|
+
const into = mine.get(key);
|
|
43775
|
+
if (!into || done.has(into)) continue;
|
|
43776
|
+
done.add(into);
|
|
43777
|
+
pairs.push([from, into]);
|
|
43778
|
+
}
|
|
43779
|
+
return pairs;
|
|
43780
|
+
}
|
|
43781
|
+
function compareNotes(kind, original, merged, ctx) {
|
|
43782
|
+
const tree = (doc, create = false) => kind === "footnote" ? doc.footnotesTree(create) : doc.endnotesTree(create);
|
|
43783
|
+
const oRoot = tree(original);
|
|
43784
|
+
const mRoot = tree(merged);
|
|
43785
|
+
if (!oRoot && !mRoot) return;
|
|
43786
|
+
const refName = `${kind}Reference`;
|
|
43787
|
+
const inOrder = (doc, root) => {
|
|
43788
|
+
if (!root) return [];
|
|
43789
|
+
const byId = new Map(
|
|
43790
|
+
root.children.filter((c) => localName(c.name) === kind && !c.attrs["w:type"]).map((c) => [c.attrs["w:id"], c])
|
|
43791
|
+
);
|
|
43792
|
+
const out = [];
|
|
43793
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43794
|
+
const walk2 = (e) => {
|
|
43795
|
+
if (localName(e.name) === refName) {
|
|
43796
|
+
const id = e.attrs["w:id"];
|
|
43797
|
+
const note = byId.get(id);
|
|
43798
|
+
if (note && !seen.has(id)) {
|
|
43799
|
+
seen.add(id);
|
|
43800
|
+
out.push(note);
|
|
43801
|
+
}
|
|
43802
|
+
}
|
|
43803
|
+
for (const c of e.children) walk2(c);
|
|
43804
|
+
};
|
|
43805
|
+
walk2(doc.docRoot);
|
|
43806
|
+
return out;
|
|
43807
|
+
};
|
|
43808
|
+
const oNotes = inOrder(original, oRoot);
|
|
43809
|
+
const rNotes = inOrder(merged, mRoot);
|
|
43810
|
+
const text = (n) => normalizeText(paragraphText3(n));
|
|
43811
|
+
const pairs = [];
|
|
43812
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
43813
|
+
rNotes.forEach((n, j) => {
|
|
43814
|
+
const list = waiting.get(text(n));
|
|
43815
|
+
if (list) list.push(j);
|
|
43816
|
+
else waiting.set(text(n), [j]);
|
|
43817
|
+
});
|
|
43818
|
+
oNotes.forEach((n, i) => {
|
|
43819
|
+
const j = waiting.get(text(n))?.shift();
|
|
43820
|
+
if (j !== void 0) pairs.push([i, j]);
|
|
43821
|
+
});
|
|
43822
|
+
const exactO = new Set(pairs.map((p) => p[0]));
|
|
43823
|
+
const exactR = new Set(pairs.map((p) => p[1]));
|
|
43824
|
+
const oRest = oNotes.map((_, i) => i).filter((i) => !exactO.has(i));
|
|
43825
|
+
const rRest = rNotes.map((_, j) => j).filter((j) => !exactR.has(j));
|
|
43826
|
+
let oAt = 0;
|
|
43827
|
+
let rAt = 0;
|
|
43828
|
+
const rest = histogramMatch(oRest.map((i) => text(oNotes[i])), rRest.map((j) => text(rNotes[j])));
|
|
43829
|
+
for (const a of [...rest, { a: oRest.length, b: rRest.length }]) {
|
|
43830
|
+
if (a.a - oAt === 1 && a.b - rAt === 1) pairs.push([oRest[oAt], rRest[rAt]]);
|
|
43831
|
+
if (a.a < oRest.length && a.b < rRest.length) pairs.push([oRest[a.a], rRest[a.b]]);
|
|
43832
|
+
oAt = a.a + 1;
|
|
43833
|
+
rAt = a.b + 1;
|
|
43834
|
+
}
|
|
43835
|
+
const ids = ctx.noteIds[kind];
|
|
43836
|
+
const pairedO = new Set(pairs.map((p) => p[0]));
|
|
43837
|
+
const pairedR = new Set(pairs.map((p) => p[1]));
|
|
43838
|
+
let changed = false;
|
|
43839
|
+
for (const [i, j] of pairs) {
|
|
43840
|
+
ids.set(oNotes[i].attrs["w:id"], rNotes[j].attrs["w:id"]);
|
|
43841
|
+
const before = ctx.meta.nextId;
|
|
43842
|
+
let touched = false;
|
|
43843
|
+
compareContainer(oNotes[i], rNotes[j], { ...ctx, meta: { ...ctx.meta, nextId: () => (touched = true, before()) } });
|
|
43844
|
+
changed || (changed = touched);
|
|
43845
|
+
}
|
|
43846
|
+
rNotes.forEach((note, j) => {
|
|
43847
|
+
if (pairedR.has(j)) return;
|
|
43848
|
+
for (const block of blockList(note)) if (localName(block.name) === "p") wrapContainerContent(block, "ins", ctx);
|
|
43849
|
+
changed = true;
|
|
43850
|
+
});
|
|
43851
|
+
const removed = oNotes.filter((_, i) => !pairedO.has(i));
|
|
43852
|
+
if (removed.length > 0) {
|
|
43853
|
+
const root = tree(merged, true);
|
|
43854
|
+
if (root) {
|
|
43855
|
+
let next = 1 + Math.max(0, ...root.children.map((c) => Number(c.attrs["w:id"]) || 0));
|
|
43856
|
+
for (const o of removed) {
|
|
43857
|
+
const copy = cloneXml(o);
|
|
43858
|
+
copy.attrs["w:id"] = String(next);
|
|
43859
|
+
ids.set(o.attrs["w:id"], String(next++));
|
|
43860
|
+
for (const block of blockList(copy)) {
|
|
43861
|
+
if (localName(block.name) !== "p") continue;
|
|
43862
|
+
adoptFromOriginal(block, { ...ctx, scope: { from: null, into: null } });
|
|
43863
|
+
wrapContainerContent(block, "del", ctx);
|
|
43864
|
+
}
|
|
43865
|
+
root.children.push(copy);
|
|
43866
|
+
}
|
|
43867
|
+
changed = true;
|
|
43868
|
+
}
|
|
43869
|
+
}
|
|
43870
|
+
if (changed) merged.markNotePartsChanged([kind === "footnote" ? "footnotes" : "endnotes"]);
|
|
43871
|
+
}
|
|
43872
|
+
function giveStruckReferencesTheirOwnNotes(kind, merged, ctx) {
|
|
43873
|
+
const root = kind === "footnote" ? merged.footnotesTree() : merged.endnotesTree();
|
|
43874
|
+
if (!root) return;
|
|
43875
|
+
const refName = `${kind}Reference`;
|
|
43876
|
+
const refs = /* @__PURE__ */ new Map();
|
|
43877
|
+
const walk2 = (e, struck) => {
|
|
43878
|
+
if (localName(e.name) === refName) {
|
|
43879
|
+
const list = refs.get(e.attrs["w:id"]) ?? [];
|
|
43880
|
+
list.push({ ref: e, struck });
|
|
43881
|
+
refs.set(e.attrs["w:id"], list);
|
|
43882
|
+
}
|
|
43883
|
+
for (const c of e.children) walk2(c, struck || localName(c.name) === "del" || rowDeleted(c));
|
|
43884
|
+
};
|
|
43885
|
+
const rowDeleted = (tr) => localName(tr.name) === "tr" && !!child(child(tr, "trPr"), "del");
|
|
43886
|
+
walk2(merged.docRoot, false);
|
|
43887
|
+
let next = 1 + Math.max(0, ...root.children.map((c) => Number(c.attrs["w:id"]) || 0));
|
|
43888
|
+
let changed = false;
|
|
43889
|
+
for (const [id, list] of refs) {
|
|
43890
|
+
if (list.length < 2) continue;
|
|
43891
|
+
const note = root.children.find((c) => localName(c.name) === kind && c.attrs["w:id"] === id);
|
|
43892
|
+
if (!note) continue;
|
|
43893
|
+
const keep = list.find((r) => !r.struck) ?? list[0];
|
|
43894
|
+
for (const r of list) {
|
|
43895
|
+
if (r === keep || !r.struck) continue;
|
|
43896
|
+
const copy = cloneXml(note);
|
|
43897
|
+
copy.attrs["w:id"] = String(next);
|
|
43898
|
+
r.ref.attrs["w:id"] = String(next++);
|
|
43899
|
+
for (const block of blockList(copy)) if (localName(block.name) === "p") wrapContainerContent(block, "del", ctx);
|
|
43900
|
+
root.children.push(copy);
|
|
43901
|
+
changed = true;
|
|
43902
|
+
}
|
|
43903
|
+
}
|
|
43904
|
+
if (changed) merged.markNotePartsChanged([kind === "footnote" ? "footnotes" : "endnotes"]);
|
|
43905
|
+
}
|
|
43312
43906
|
function blockList(container, ...skip) {
|
|
43313
43907
|
return container.children.filter((c) => !skip.includes(localName(c.name)));
|
|
43314
43908
|
}
|
|
43315
43909
|
function reportUncomparedParts(original, revised, ctx) {
|
|
43316
43910
|
const parts = [
|
|
43317
43911
|
["styles.xml", original.stylesTree()],
|
|
43318
|
-
["numbering.xml", original.numberingTree()]
|
|
43319
|
-
["footnotes.xml", original.footnotesTree()],
|
|
43320
|
-
["endnotes.xml", original.endnotesTree()]
|
|
43912
|
+
["numbering.xml", original.numberingTree()]
|
|
43321
43913
|
];
|
|
43322
43914
|
const revisedTrees = {
|
|
43323
43915
|
"styles.xml": revised.stylesTree(),
|
|
43324
|
-
"numbering.xml": revised.numberingTree()
|
|
43325
|
-
"footnotes.xml": revised.footnotesTree(),
|
|
43326
|
-
"endnotes.xml": revised.endnotesTree()
|
|
43916
|
+
"numbering.xml": revised.numberingTree()
|
|
43327
43917
|
};
|
|
43328
43918
|
for (const [name, tree] of parts) {
|
|
43329
43919
|
const other = revisedTrees[name];
|
|
@@ -43333,13 +43923,12 @@ function reportUncomparedParts(original, revised, ctx) {
|
|
|
43333
43923
|
ctx.note({ code: "part-not-compared", detail: `${name} differs; the result carries the revised document's` });
|
|
43334
43924
|
}
|
|
43335
43925
|
}
|
|
43336
|
-
const hf = (doc) => [...doc.headerRoots(), ...doc.footerRoots()].map((root) => serializeXml(root)).join("");
|
|
43337
|
-
if (hf(original) !== hf(revised)) {
|
|
43338
|
-
ctx.note({ code: "part-not-compared", detail: "headers/footers differ; the result carries the revised document's" });
|
|
43339
|
-
}
|
|
43340
43926
|
}
|
|
43341
43927
|
function alignBlocks(oBlocks, rBlocks, ctx) {
|
|
43342
|
-
const anchors = histogramMatch(
|
|
43928
|
+
const anchors = histogramMatch(
|
|
43929
|
+
oBlocks.map((el26) => blockFingerprint(el26, ctx.original)),
|
|
43930
|
+
rBlocks.map((el26) => blockFingerprint(el26, ctx.doc))
|
|
43931
|
+
);
|
|
43343
43932
|
const entries = [];
|
|
43344
43933
|
let oAt = 0;
|
|
43345
43934
|
let rAt = 0;
|
|
@@ -43353,19 +43942,23 @@ function alignBlocks(oBlocks, rBlocks, ctx) {
|
|
|
43353
43942
|
}
|
|
43354
43943
|
return entries;
|
|
43355
43944
|
}
|
|
43356
|
-
function blockFingerprint(el26) {
|
|
43945
|
+
function blockFingerprint(el26, doc) {
|
|
43357
43946
|
const ln = localName(el26.name);
|
|
43358
43947
|
if (ln === "p") {
|
|
43359
43948
|
const pPr = child(el26, "pPr");
|
|
43360
43949
|
const style = child(pPr, "pStyle")?.attrs["w:val"] ?? "";
|
|
43361
43950
|
const numPr = child(pPr, "numPr");
|
|
43362
|
-
|
|
43951
|
+
const jc = child(pPr, "jc")?.attrs["w:val"] ?? "";
|
|
43952
|
+
const sect = sectionKey(el26, doc);
|
|
43953
|
+
return `P\0${normalizeText(paragraphText3(el26))}\0${style}\0${numPr ? serializeXml(numPr) : ""}\0${jc}\0${sect}`;
|
|
43363
43954
|
}
|
|
43364
43955
|
if (ln === "tbl") return `T\0${normalizeText(paragraphText3(el26))}`;
|
|
43365
43956
|
return `X\0${serializeXml(el26)}`;
|
|
43366
43957
|
}
|
|
43367
43958
|
function emitGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, entries, ctx) {
|
|
43368
|
-
const
|
|
43959
|
+
const before = entries[entries.length - 1];
|
|
43960
|
+
const unanchored = (!before || !isParagraph(before.el)) && (rHi >= rBlocks.length || !isParagraph(rBlocks[rHi]));
|
|
43961
|
+
const pairings = resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, oLo === 0 && rLo === 0 || unanchored);
|
|
43369
43962
|
let oAt = oLo;
|
|
43370
43963
|
let rAt = rLo;
|
|
43371
43964
|
for (const pairing of [...pairings, { kind: "pair", o: oHi, r: rHi, other: -1 }]) {
|
|
@@ -43397,16 +43990,43 @@ function emitGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, entries, ctx) {
|
|
|
43397
43990
|
}
|
|
43398
43991
|
}
|
|
43399
43992
|
}
|
|
43993
|
+
var GAP_PAIR_CAP = 200 * 200;
|
|
43994
|
+
var GAP_WINDOW = 50;
|
|
43400
43995
|
function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = false) {
|
|
43401
43996
|
const out = [];
|
|
43402
43997
|
const oUsed = /* @__PURE__ */ new Set();
|
|
43403
43998
|
const rUsed = /* @__PURE__ */ new Set();
|
|
43404
|
-
const
|
|
43999
|
+
const placed = [];
|
|
44000
|
+
const crosses = (o, r) => {
|
|
44001
|
+
let lo = 0;
|
|
44002
|
+
let hi = placed.length;
|
|
44003
|
+
while (lo < hi) {
|
|
44004
|
+
const mid = lo + hi >> 1;
|
|
44005
|
+
if (placed[mid].o < o) lo = mid + 1;
|
|
44006
|
+
else hi = mid;
|
|
44007
|
+
}
|
|
44008
|
+
return lo > 0 && placed[lo - 1].r >= r || lo < placed.length && placed[lo].r <= r;
|
|
44009
|
+
};
|
|
44010
|
+
const accept = (p) => {
|
|
44011
|
+
out.push(p);
|
|
44012
|
+
let at = placed.length;
|
|
44013
|
+
while (at > 0 && placed[at - 1].o > p.o) at--;
|
|
44014
|
+
placed.splice(at, 0, p);
|
|
44015
|
+
};
|
|
44016
|
+
const oLen = oHi - oLo;
|
|
44017
|
+
const rLen = rHi - rLo;
|
|
44018
|
+
const windowed = oLen * rLen > GAP_PAIR_CAP;
|
|
44019
|
+
const span = (i) => {
|
|
44020
|
+
if (!windowed) return [rLo, rHi];
|
|
44021
|
+
const centre = rLo + Math.round((i - oLo) * rLen / Math.max(1, oLen));
|
|
44022
|
+
return [Math.max(rLo, centre - GAP_WINDOW), Math.min(rHi, centre + GAP_WINDOW + 1)];
|
|
44023
|
+
};
|
|
43405
44024
|
for (let i = oLo; i < oHi; i++) {
|
|
43406
|
-
|
|
44025
|
+
const [jLo, jHi] = span(i);
|
|
44026
|
+
for (let j = jLo; j + 1 < jHi; j++) {
|
|
43407
44027
|
if (oUsed.has(i) || rUsed.has(j) || rUsed.has(j + 1) || crosses(i, j)) continue;
|
|
43408
44028
|
if (concatMatches([oBlocks[i]], [rBlocks[j], rBlocks[j + 1]], ctx)) {
|
|
43409
|
-
|
|
44029
|
+
accept({ kind: "split", o: i, r: j, other: j + 1 });
|
|
43410
44030
|
oUsed.add(i);
|
|
43411
44031
|
rUsed.add(j);
|
|
43412
44032
|
rUsed.add(j + 1);
|
|
@@ -43414,10 +44034,11 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43414
44034
|
}
|
|
43415
44035
|
}
|
|
43416
44036
|
for (let i = oLo; i + 1 < oHi; i++) {
|
|
43417
|
-
|
|
44037
|
+
const [jLo, jHi] = span(i);
|
|
44038
|
+
for (let j = jLo; j < jHi; j++) {
|
|
43418
44039
|
if (oUsed.has(i) || oUsed.has(i + 1) || rUsed.has(j) || crosses(i, j)) continue;
|
|
43419
44040
|
if (concatMatches([oBlocks[i], oBlocks[i + 1]], [rBlocks[j]], ctx)) {
|
|
43420
|
-
|
|
44041
|
+
accept({ kind: "merge", o: i, r: j, other: i + 1 });
|
|
43421
44042
|
oUsed.add(i);
|
|
43422
44043
|
oUsed.add(i + 1);
|
|
43423
44044
|
rUsed.add(j);
|
|
@@ -43427,9 +44048,10 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43427
44048
|
const scored = [];
|
|
43428
44049
|
for (let i = oLo; i < oHi; i++) {
|
|
43429
44050
|
if (oUsed.has(i)) continue;
|
|
43430
|
-
|
|
44051
|
+
const [jLo, jHi] = span(i);
|
|
44052
|
+
for (let j = jLo; j < jHi; j++) {
|
|
43431
44053
|
if (rUsed.has(j)) continue;
|
|
43432
|
-
if (
|
|
44054
|
+
if (!pairable(oBlocks[i], rBlocks[j], ctx)) continue;
|
|
43433
44055
|
const a = paragraphText3(oBlocks[i]);
|
|
43434
44056
|
const b = paragraphText3(rBlocks[j]);
|
|
43435
44057
|
const score = similarity(a, b);
|
|
@@ -43440,30 +44062,74 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43440
44062
|
for (const cand of scored) {
|
|
43441
44063
|
if (oUsed.has(cand.o) || rUsed.has(cand.r)) continue;
|
|
43442
44064
|
if (crosses(cand.o, cand.r)) continue;
|
|
43443
|
-
|
|
44065
|
+
accept({ kind: "pair", o: cand.o, r: cand.r, other: -1 });
|
|
43444
44066
|
oUsed.add(cand.o);
|
|
43445
44067
|
rUsed.add(cand.r);
|
|
43446
44068
|
}
|
|
44069
|
+
const settled = [...out].sort((a, b) => a.o - b.o);
|
|
44070
|
+
let oFrom = oLo;
|
|
44071
|
+
let rFrom = rLo;
|
|
44072
|
+
for (const p of [...settled, { kind: "pair", o: oHi, r: rHi, other: -1 }]) {
|
|
44073
|
+
const os = [];
|
|
44074
|
+
const rs = [];
|
|
44075
|
+
for (let i = oFrom; i < p.o; i++) if (!oUsed.has(i)) os.push(i);
|
|
44076
|
+
for (let j = rFrom; j < p.r; j++) if (!rUsed.has(j)) rs.push(j);
|
|
44077
|
+
if (os.length === 1 && rs.length === 1 && sameShape2(oBlocks[os[0]], rBlocks[rs[0]], ctx)) {
|
|
44078
|
+
accept({ kind: "pair", o: os[0], r: rs[0], other: -1 });
|
|
44079
|
+
oUsed.add(os[0]);
|
|
44080
|
+
rUsed.add(rs[0]);
|
|
44081
|
+
}
|
|
44082
|
+
oFrom = p.kind === "merge" ? p.other + 1 : p.o + 1;
|
|
44083
|
+
rFrom = p.kind === "split" ? p.other + 1 : p.r + 1;
|
|
44084
|
+
}
|
|
43447
44085
|
if (needsAnchor && out.length === 0) {
|
|
43448
44086
|
for (let i = oLo; i < oHi && out.length === 0; i++) {
|
|
43449
44087
|
if (oUsed.has(i)) continue;
|
|
43450
44088
|
for (let j = rLo; j < rHi; j++) {
|
|
43451
|
-
if (rUsed.has(j) ||
|
|
43452
|
-
|
|
44089
|
+
if (rUsed.has(j) || !pairable(oBlocks[i], rBlocks[j], ctx)) continue;
|
|
44090
|
+
accept({ kind: "pair", o: i, r: j, other: -1 });
|
|
43453
44091
|
break;
|
|
43454
44092
|
}
|
|
43455
44093
|
}
|
|
43456
44094
|
}
|
|
43457
44095
|
return out.sort((a, b) => a.o - b.o);
|
|
43458
44096
|
}
|
|
44097
|
+
function pairable(a, b, ctx) {
|
|
44098
|
+
if (localName(a.name) !== localName(b.name)) return false;
|
|
44099
|
+
if (!isParagraph(a)) return true;
|
|
44100
|
+
return sectionKey(a, ctx.original) === sectionKey(b, ctx.doc);
|
|
44101
|
+
}
|
|
44102
|
+
function sectionKey(el26, doc) {
|
|
44103
|
+
const sectPr = child(child(el26, "pPr"), "sectPr");
|
|
44104
|
+
if (!sectPr) return "";
|
|
44105
|
+
return serializeXml(sectPr).replace(/r:id="([^"]*)"/g, (_, id) => `r:id="${doc.documentRels.get(id)?.target ?? id}"`);
|
|
44106
|
+
}
|
|
44107
|
+
function sameShape2(a, b, ctx) {
|
|
44108
|
+
if (isParagraph(a) && isParagraph(b)) return pairable(a, b, ctx);
|
|
44109
|
+
if (localName(a.name) === localName(b.name) && ["tbl", "sdt", "customXml"].includes(localName(a.name))) return true;
|
|
44110
|
+
const cells = (tr) => tr.children.filter((c) => localName(c.name) === "tc").length;
|
|
44111
|
+
return localName(a.name) === "tr" && localName(b.name) === "tr" && cells(a) === cells(b);
|
|
44112
|
+
}
|
|
44113
|
+
function readOptions(ctx, original) {
|
|
44114
|
+
const noteKey = (ref) => {
|
|
44115
|
+
const kind = localName(ref.name) === "footnoteReference" ? "footnote" : "endnote";
|
|
44116
|
+
const id = ref.attrs["w:id"];
|
|
44117
|
+
return original ? `${ctx.noteIds[kind].get(id) ?? `original ${id}`}` : id;
|
|
44118
|
+
};
|
|
44119
|
+
return { whitespace: ctx.whitespace, noteKey };
|
|
44120
|
+
}
|
|
43459
44121
|
function concatMatches(oEls, rEls, ctx) {
|
|
43460
44122
|
const key = (els) => {
|
|
43461
44123
|
const parts = [];
|
|
43462
44124
|
for (const el26 of els) {
|
|
43463
44125
|
if (localName(el26.name) !== "p") return null;
|
|
43464
|
-
|
|
44126
|
+
if (child(child(el26, "pPr"), "sectPr")) return null;
|
|
44127
|
+
const content = readParagraph(el26, readOptions(ctx, oEls.includes(el26)));
|
|
43465
44128
|
if (!content.inlineDiffable) return null;
|
|
43466
|
-
for (const token of content.tokens)
|
|
44129
|
+
for (const token of content.tokens) {
|
|
44130
|
+
const props = token.pieces ? token.pieces.map((p) => `${p.text.length}:${runPropsKey(p.run)}`).join("|") : runPropsKey(token.run);
|
|
44131
|
+
parts.push(token.key + "" + props + "" + token.path.map((c) => serializeXml({ ...c, children: c.children.filter((k) => /Pr$/.test(localName(k.name))) })).join(">"));
|
|
44132
|
+
}
|
|
43467
44133
|
}
|
|
43468
44134
|
return parts.join("");
|
|
43469
44135
|
};
|
|
@@ -43475,14 +44141,24 @@ function emitMatched(oEl, rEl, ctx) {
|
|
|
43475
44141
|
const ln = localName(oEl.name);
|
|
43476
44142
|
if (ln === "p") comparePair(oEl, rEl, ctx);
|
|
43477
44143
|
else if (ln === "tbl") compareTable(oEl, rEl, ctx);
|
|
44144
|
+
else if (ln === "sdt") {
|
|
44145
|
+
const o = child(oEl, "sdtContent");
|
|
44146
|
+
const r = child(rEl, "sdtContent");
|
|
44147
|
+
if (o && r) compareContainer(o, r, ctx);
|
|
44148
|
+
} else if (ln === "customXml") compareContainer(oEl, rEl, ctx);
|
|
43478
44149
|
return { el: rEl, cls: "kept" };
|
|
43479
44150
|
}
|
|
43480
44151
|
var REL_ATTRS = ["id", "embed", "link", "pict", "dm", "lo", "qs", "cs"];
|
|
43481
44152
|
function adoptFromOriginal(root, ctx) {
|
|
43482
|
-
const mine = ctx.doc.
|
|
43483
|
-
const theirs = ctx.original.
|
|
44153
|
+
const mine = ctx.doc.relationshipsOf(ctx.scope.into);
|
|
44154
|
+
const theirs = ctx.original.relationshipsOf(ctx.scope.from);
|
|
43484
44155
|
const remapped = /* @__PURE__ */ new Map();
|
|
43485
44156
|
const walk2 = (el26) => {
|
|
44157
|
+
const ln = localName(el26.name);
|
|
44158
|
+
if (ln === "footnoteReference" || ln === "endnoteReference") {
|
|
44159
|
+
const to = ctx.noteIds[ln === "footnoteReference" ? "footnote" : "endnote"].get(el26.attrs["w:id"]);
|
|
44160
|
+
if (to !== void 0) el26.attrs["w:id"] = to;
|
|
44161
|
+
}
|
|
43486
44162
|
for (const key of Object.keys(el26.attrs)) {
|
|
43487
44163
|
if (!key.startsWith("r:") || !REL_ATTRS.includes(localName(key))) continue;
|
|
43488
44164
|
const id = el26.attrs[key];
|
|
@@ -43492,7 +44168,7 @@ function adoptFromOriginal(root, ctx) {
|
|
|
43492
44168
|
if (here && here.target === source.target && here.type === source.type) continue;
|
|
43493
44169
|
let replacement = remapped.get(id);
|
|
43494
44170
|
if (replacement === void 0) {
|
|
43495
|
-
replacement = source.external ? ctx.doc.addHyperlinkRel(source.target) :
|
|
44171
|
+
replacement = source.external ? ctx.doc.addHyperlinkRel(source.target, ctx.scope.into) : ctx.doc.adoptRelFrom(ctx.original, id, ctx.scope);
|
|
43496
44172
|
if (replacement === null) {
|
|
43497
44173
|
ctx.note({
|
|
43498
44174
|
code: "unresolved-relationship",
|
|
@@ -43511,32 +44187,31 @@ function adoptFromOriginal(root, ctx) {
|
|
|
43511
44187
|
function emitDeleted(el26, ctx) {
|
|
43512
44188
|
const clone = cloneXml(el26);
|
|
43513
44189
|
adoptFromOriginal(clone, ctx);
|
|
43514
|
-
|
|
43515
|
-
if (ln === "p") {
|
|
43516
|
-
wrapContainerContent(clone, "del", ctx);
|
|
43517
|
-
} else if (ln === "tbl") {
|
|
43518
|
-
for (const row of clone.children.filter((c) => localName(c.name) === "tr")) {
|
|
43519
|
-
markRowRevision(row, "del", ctx.meta);
|
|
43520
|
-
wrapRowContent(row, "del", ctx);
|
|
43521
|
-
}
|
|
43522
|
-
} else if (!isPassenger(ln)) {
|
|
43523
|
-
ctx.note({ code: "untracked-block", detail: `a <${clone.name}> block was removed and is not expressible as a revision` });
|
|
43524
|
-
}
|
|
44190
|
+
markWholeBlock(clone, "del", ctx);
|
|
43525
44191
|
return { el: clone, cls: "deleted" };
|
|
43526
44192
|
}
|
|
43527
44193
|
function emitInserted(el26, ctx) {
|
|
44194
|
+
markWholeBlock(el26, "ins", ctx);
|
|
44195
|
+
return { el: el26, cls: "inserted" };
|
|
44196
|
+
}
|
|
44197
|
+
function markWholeBlock(el26, kind, ctx) {
|
|
43528
44198
|
const ln = localName(el26.name);
|
|
43529
44199
|
if (ln === "p") {
|
|
43530
|
-
wrapContainerContent(el26,
|
|
44200
|
+
wrapContainerContent(el26, kind, ctx);
|
|
43531
44201
|
} else if (ln === "tbl") {
|
|
43532
44202
|
for (const row of el26.children.filter((c) => localName(c.name) === "tr")) {
|
|
43533
|
-
markRowRevision(row,
|
|
43534
|
-
wrapRowContent(row,
|
|
44203
|
+
markRowRevision(row, kind, ctx.meta);
|
|
44204
|
+
wrapRowContent(row, kind, ctx);
|
|
43535
44205
|
}
|
|
43536
|
-
} else if (
|
|
43537
|
-
|
|
44206
|
+
} else if (ln === "sdt" || ln === "customXml") {
|
|
44207
|
+
const inner = ln === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44208
|
+
for (const block of inner?.children ?? []) {
|
|
44209
|
+
markWholeBlock(block, kind, ctx);
|
|
44210
|
+
if (isParagraph(block)) markParagraphGlyph(block, kind, ctx.meta);
|
|
44211
|
+
}
|
|
44212
|
+
} else if (!isPassenger(ln) && ln !== "sdtPr" && ln !== "sdtEndPr" && ln !== "customXmlPr") {
|
|
44213
|
+
ctx.note({ code: "untracked-block", detail: `a <${el26.name}> block was ${kind === "ins" ? "added" : "removed"} and is not expressible as a revision` });
|
|
43538
44214
|
}
|
|
43539
|
-
return { el: el26, cls: "inserted" };
|
|
43540
44215
|
}
|
|
43541
44216
|
function wrapRowContent(row, kind, ctx) {
|
|
43542
44217
|
for (const cell of row.children.filter((c) => localName(c.name) === "tc")) {
|
|
@@ -43546,7 +44221,8 @@ function wrapRowContent(row, kind, ctx) {
|
|
|
43546
44221
|
}
|
|
43547
44222
|
}
|
|
43548
44223
|
function wrapContainerContent(container, kind, ctx) {
|
|
43549
|
-
const NESTED = ["hyperlink", "smartTag", "dir", "bdo"];
|
|
44224
|
+
const NESTED = ["hyperlink", "smartTag", "dir", "bdo", "fldSimple"];
|
|
44225
|
+
const WRAPPABLE = ["r", "sdt", "customXml"];
|
|
43550
44226
|
const out = [];
|
|
43551
44227
|
let group = [];
|
|
43552
44228
|
const flush = () => {
|
|
@@ -43557,7 +44233,7 @@ function wrapContainerContent(container, kind, ctx) {
|
|
|
43557
44233
|
};
|
|
43558
44234
|
for (const c of container.children) {
|
|
43559
44235
|
const ln = localName(c.name);
|
|
43560
|
-
if (ln
|
|
44236
|
+
if (WRAPPABLE.includes(ln)) {
|
|
43561
44237
|
group.push(c);
|
|
43562
44238
|
continue;
|
|
43563
44239
|
}
|
|
@@ -43575,10 +44251,10 @@ function wrapContainerContent(container, kind, ctx) {
|
|
|
43575
44251
|
container.children = out;
|
|
43576
44252
|
}
|
|
43577
44253
|
function comparePair(oEl, rEl, ctx) {
|
|
43578
|
-
if (
|
|
44254
|
+
if (sameXml(oEl, rEl)) return;
|
|
43579
44255
|
if (ctx.formatting) alignParagraphProps(rEl, child(rEl, "pPr"), child(oEl, "pPr"), ctx);
|
|
43580
|
-
const oc = readParagraph(oEl,
|
|
43581
|
-
const rc = readParagraph(rEl,
|
|
44256
|
+
const oc = readParagraph(oEl, readOptions(ctx, true));
|
|
44257
|
+
const rc = readParagraph(rEl, readOptions(ctx, false));
|
|
43582
44258
|
if (!oc.inlineDiffable || !rc.inlineDiffable) {
|
|
43583
44259
|
if (oc.text !== rc.text || serializeXml(stripProps(oEl)) !== serializeXml(stripProps(rEl))) {
|
|
43584
44260
|
const deleted = cloneXml(oEl);
|
|
@@ -43593,70 +44269,180 @@ function comparePair(oEl, rEl, ctx) {
|
|
|
43593
44269
|
];
|
|
43594
44270
|
ctx.note({
|
|
43595
44271
|
code: "coarse-paragraph",
|
|
43596
|
-
detail: "a paragraph holding
|
|
44272
|
+
detail: "a paragraph holding inline math or other content a word diff cannot rebuild changed; struck and reinserted whole"
|
|
43597
44273
|
});
|
|
43598
44274
|
}
|
|
43599
44275
|
return;
|
|
43600
44276
|
}
|
|
43601
44277
|
const segments = wordSegments(oc.tokens, rc.tokens);
|
|
43602
44278
|
const changed = segments.some((s) => !s.equal);
|
|
43603
|
-
|
|
43604
|
-
|
|
43605
|
-
return;
|
|
43606
|
-
}
|
|
44279
|
+
const flat = oc.tokens.every((t) => t.path.length === 0) && rc.tokens.every((t) => t.path.length === 0);
|
|
44280
|
+
if (!changed && (!ctx.formatting || flat && alignRunPropsInPlace(oEl, rEl, ctx))) return;
|
|
43607
44281
|
const pPr = child(rEl, "pPr");
|
|
43608
|
-
const
|
|
44282
|
+
const items = [];
|
|
44283
|
+
const counterpart = /* @__PURE__ */ new Map();
|
|
43609
44284
|
for (const seg of segments) {
|
|
43610
44285
|
if (seg.equal) {
|
|
43611
|
-
|
|
44286
|
+
for (const it of emitEqualSpan(oc.tokens, rc.tokens, seg, ctx, counterpart)) items.push({ ...it, kind: null });
|
|
43612
44287
|
continue;
|
|
43613
44288
|
}
|
|
43614
44289
|
if (seg.aEnd > seg.aStart) {
|
|
43615
|
-
const
|
|
43616
|
-
|
|
43617
|
-
|
|
44290
|
+
for (const it of sliceItems(oc.tokens, seg.aStart, seg.aEnd, true)) {
|
|
44291
|
+
adoptFromOriginal(it.el, ctx);
|
|
44292
|
+
markDeletedText(it.el);
|
|
44293
|
+
items.push({ ...it, kind: "del" });
|
|
44294
|
+
}
|
|
43618
44295
|
}
|
|
43619
44296
|
if (seg.bEnd > seg.bStart) {
|
|
43620
|
-
|
|
44297
|
+
for (const it of sliceItems(rc.tokens, seg.bStart, seg.bEnd, false)) items.push({ ...it, kind: "ins" });
|
|
43621
44298
|
}
|
|
43622
44299
|
}
|
|
43623
|
-
|
|
43624
|
-
|
|
44300
|
+
for (const it of items) if (it.kind === "del") it.path = it.path.map((c) => counterpart.get(c) ?? c);
|
|
44301
|
+
for (const el26 of rc.tail) items.push({ el: cloneXml(el26), path: [], kind: null });
|
|
44302
|
+
const fromOriginal = new Set(oc.tokens.flatMap((t) => t.path));
|
|
44303
|
+
rEl.children = [...pPr ? [pPr] : [], ...nestItems(items, 0, rEl, ctx, fromOriginal)];
|
|
44304
|
+
}
|
|
44305
|
+
var WRAPPABLE_CONTAINERS = /* @__PURE__ */ new Set(["sdt", "customXml"]);
|
|
44306
|
+
function compareTextBoxes(oAtom, rAtom, ctx) {
|
|
44307
|
+
const theirs = textBoxesIn(oAtom);
|
|
44308
|
+
const mine = textBoxesIn(rAtom);
|
|
44309
|
+
for (let i = 0; i < Math.min(theirs.length, mine.length); i++) {
|
|
44310
|
+
if (sameXml(theirs[i], mine[i])) continue;
|
|
44311
|
+
const entries = alignBlocks(blockList(theirs[i]), blockList(mine[i]), ctx);
|
|
44312
|
+
applyParagraphMarks(entries, ctx);
|
|
44313
|
+
mine[i].children = entries.map((e) => e.el);
|
|
44314
|
+
}
|
|
44315
|
+
}
|
|
44316
|
+
function nestItems(items, depth, rEl, ctx, fromOriginal) {
|
|
44317
|
+
const out = [];
|
|
44318
|
+
let i = 0;
|
|
44319
|
+
while (i < items.length) {
|
|
44320
|
+
const c = items[i].path[depth];
|
|
44321
|
+
let j = i + 1;
|
|
44322
|
+
if (c) {
|
|
44323
|
+
while (j < items.length && items[j].path[depth] === c) j++;
|
|
44324
|
+
const group = items.slice(i, j);
|
|
44325
|
+
const kind = group[0].kind;
|
|
44326
|
+
if (kind && WRAPPABLE_CONTAINERS.has(localName(c.name)) && group.every((it) => it.kind === kind)) {
|
|
44327
|
+
const plain = group.map((it) => ({ ...it, kind: null }));
|
|
44328
|
+
out.push(revisionWrapper(rEl, kind, [containerShell(c, nestItems(plain, depth + 1, rEl, ctx, fromOriginal), fromOriginal.has(c), ctx)], ctx.meta));
|
|
44329
|
+
} else {
|
|
44330
|
+
out.push(containerShell(c, nestItems(group, depth + 1, rEl, ctx, fromOriginal), fromOriginal.has(c), ctx));
|
|
44331
|
+
}
|
|
44332
|
+
} else {
|
|
44333
|
+
const kind = items[i].kind;
|
|
44334
|
+
while (j < items.length && !items[j].path[depth] && items[j].kind === kind) j++;
|
|
44335
|
+
const els = items.slice(i, j).map((it) => it.el);
|
|
44336
|
+
if (kind) out.push(revisionWrapper(rEl, kind, els, ctx.meta));
|
|
44337
|
+
else out.push(...els);
|
|
44338
|
+
}
|
|
44339
|
+
i = j;
|
|
44340
|
+
}
|
|
44341
|
+
return out;
|
|
44342
|
+
}
|
|
44343
|
+
function containerShell(c, content, fromOriginal, ctx) {
|
|
44344
|
+
const isSdt = localName(c.name) === "sdt";
|
|
44345
|
+
const inner = isSdt ? c.children.find((k) => localName(k.name) === "sdtContent") : void 0;
|
|
44346
|
+
const shell = {
|
|
44347
|
+
name: c.name,
|
|
44348
|
+
attrs: { ...c.attrs },
|
|
44349
|
+
// Property children only (sdtPr, smartTagPr, ...): the content is new.
|
|
44350
|
+
children: c.children.filter((k) => /Pr$/.test(localName(k.name))).map(cloneXml),
|
|
44351
|
+
text: ""
|
|
44352
|
+
};
|
|
44353
|
+
if (fromOriginal) adoptFromOriginal(shell, ctx);
|
|
44354
|
+
if (isSdt) shell.children.push({ name: inner?.name ?? c.name.replace(/sdt$/, "sdtContent"), attrs: { ...inner?.attrs ?? {} }, children: content, text: "" });
|
|
44355
|
+
else shell.children.push(...content);
|
|
44356
|
+
return shell;
|
|
43625
44357
|
}
|
|
43626
44358
|
function stripProps(el26) {
|
|
43627
44359
|
return { ...el26, children: el26.children.filter((c) => localName(c.name) !== "pPr") };
|
|
43628
44360
|
}
|
|
43629
|
-
function emitEqualSpan(oTokens, rTokens, seg, ctx) {
|
|
44361
|
+
function emitEqualSpan(oTokens, rTokens, seg, ctx, counterpart) {
|
|
44362
|
+
const units = [];
|
|
44363
|
+
const pieceAt = (pieces, offset) => {
|
|
44364
|
+
let base = 0;
|
|
44365
|
+
for (const p of pieces) {
|
|
44366
|
+
if (offset < base + p.text.length) return p;
|
|
44367
|
+
base += p.text.length;
|
|
44368
|
+
}
|
|
44369
|
+
return pieces[pieces.length - 1];
|
|
44370
|
+
};
|
|
44371
|
+
for (let k = 0; k < seg.bEnd - seg.bStart; k++) {
|
|
44372
|
+
const o = oTokens[seg.aStart + k];
|
|
44373
|
+
const r = rTokens[seg.bStart + k];
|
|
44374
|
+
for (let d = 0; d < Math.min(o.path.length, r.path.length); d++) {
|
|
44375
|
+
if (localName(o.path[d].name) === localName(r.path[d].name)) counterpart.set(o.path[d], r.path[d]);
|
|
44376
|
+
}
|
|
44377
|
+
if (r.atom || !r.pieces || !o.pieces || o.text !== r.text) {
|
|
44378
|
+
if (r.atom) units.push({ token: r, run: r.run, text: "", atom: r.atom, oAtom: o.atom, was: o.run });
|
|
44379
|
+
else for (const p of r.pieces ?? []) units.push({ token: r, run: p.run, t: p.t, text: p.text, was: o.run });
|
|
44380
|
+
continue;
|
|
44381
|
+
}
|
|
44382
|
+
const cuts = /* @__PURE__ */ new Set([0, r.text.length]);
|
|
44383
|
+
for (const pieces of [o.pieces, r.pieces]) {
|
|
44384
|
+
let base = 0;
|
|
44385
|
+
for (const p of pieces) cuts.add(base += p.text.length);
|
|
44386
|
+
}
|
|
44387
|
+
const sorted = [...cuts].sort((x, y) => x - y);
|
|
44388
|
+
for (let c = 0; c + 1 < sorted.length; c++) {
|
|
44389
|
+
const from = sorted[c];
|
|
44390
|
+
const to = sorted[c + 1];
|
|
44391
|
+
if (from === to) continue;
|
|
44392
|
+
const rp = pieceAt(r.pieces, from);
|
|
44393
|
+
units.push({ token: r, run: rp.run, t: rp.t, text: r.text.slice(from, to), was: pieceAt(o.pieces, from).run });
|
|
44394
|
+
}
|
|
44395
|
+
}
|
|
43630
44396
|
const out = [];
|
|
43631
|
-
let
|
|
43632
|
-
|
|
43633
|
-
|
|
43634
|
-
|
|
43635
|
-
|
|
43636
|
-
|
|
43637
|
-
|
|
44397
|
+
let open = null;
|
|
44398
|
+
let text = null;
|
|
44399
|
+
const close = () => {
|
|
44400
|
+
if (open && ctx.formatting && open.was !== runPropsKey(open.run)) swapRunProps(open.el, open.prev, ctx);
|
|
44401
|
+
open = null;
|
|
44402
|
+
text = null;
|
|
44403
|
+
};
|
|
44404
|
+
let lastToken = null;
|
|
44405
|
+
for (const u of units) {
|
|
44406
|
+
if (u.token !== lastToken && u.token.lead) {
|
|
44407
|
+
close();
|
|
44408
|
+
out.push(...u.token.lead.map((el26) => ({ el: cloneXml(el26), path: u.token.path })));
|
|
44409
|
+
}
|
|
44410
|
+
lastToken = u.token;
|
|
44411
|
+
const was = runPropsKey(u.was);
|
|
44412
|
+
if (!open || open.run !== u.run || open.was !== was) {
|
|
44413
|
+
close();
|
|
44414
|
+
const rPr = u.run.children.find((c) => localName(c.name) === "rPr");
|
|
44415
|
+
const el26 = { name: u.run.name, attrs: { ...u.run.attrs }, children: rPr ? [cloneXml(rPr)] : [], text: "" };
|
|
44416
|
+
out.push({ el: el26, path: u.token.path });
|
|
44417
|
+
open = { run: u.run, was, el: el26, prev: child(u.was, "rPr") };
|
|
44418
|
+
}
|
|
44419
|
+
if (u.atom) {
|
|
44420
|
+
const atom = cloneXml(u.atom);
|
|
44421
|
+
if (u.oAtom && !sameXml(u.oAtom, u.atom)) compareTextBoxes(u.oAtom, atom, ctx);
|
|
44422
|
+
open.el.children.push(atom);
|
|
44423
|
+
text = null;
|
|
44424
|
+
continue;
|
|
43638
44425
|
}
|
|
43639
|
-
|
|
43640
|
-
|
|
43641
|
-
|
|
43642
|
-
if (localName(el26.name) === "r") {
|
|
43643
|
-
swapRunProps(el26, child(oTokens[seg.aStart + (at - seg.bStart)].run, "rPr"), ctx);
|
|
43644
|
-
}
|
|
43645
|
-
}
|
|
44426
|
+
if (text && text.t === u.t) {
|
|
44427
|
+
text.el.text += u.text;
|
|
44428
|
+
continue;
|
|
43646
44429
|
}
|
|
43647
|
-
|
|
43648
|
-
|
|
44430
|
+
const te = { name: u.t.name, attrs: { ...u.t.attrs, "xml:space": "preserve" }, children: [], text: u.text };
|
|
44431
|
+
open.el.children.push(te);
|
|
44432
|
+
text = { t: u.t, el: te };
|
|
43649
44433
|
}
|
|
44434
|
+
close();
|
|
43650
44435
|
return out;
|
|
43651
44436
|
}
|
|
43652
44437
|
function alignRunPropsInPlace(oEl, rEl, ctx) {
|
|
43653
44438
|
const oRuns = oEl.children.filter((c) => localName(c.name) === "r");
|
|
43654
44439
|
const rRuns = rEl.children.filter((c) => localName(c.name) === "r");
|
|
43655
|
-
if (oRuns.length !== rRuns.length) return;
|
|
44440
|
+
if (oRuns.length !== rRuns.length) return false;
|
|
43656
44441
|
for (let i = 0; i < rRuns.length; i++) {
|
|
43657
|
-
if (paragraphText3(oRuns[i]) !== paragraphText3(rRuns[i])) return;
|
|
44442
|
+
if (paragraphText3(oRuns[i]) !== paragraphText3(rRuns[i])) return false;
|
|
43658
44443
|
}
|
|
43659
44444
|
for (let i = 0; i < rRuns.length; i++) swapRunProps(rRuns[i], child(oRuns[i], "rPr"), ctx);
|
|
44445
|
+
return true;
|
|
43660
44446
|
}
|
|
43661
44447
|
function wordSegments(a, b) {
|
|
43662
44448
|
const raw = [];
|
|
@@ -43685,8 +44471,8 @@ function wordSegments(a, b) {
|
|
|
43685
44471
|
for (let i = 1; i + 1 < segments.length; i++) {
|
|
43686
44472
|
const seg = segments[i];
|
|
43687
44473
|
if (!seg.equal) continue;
|
|
43688
|
-
const len = seg.
|
|
43689
|
-
const beside = Math.min(changeSize(segments[i - 1]), changeSize(segments[i + 1]));
|
|
44474
|
+
const len = words(a, seg.aStart, seg.aEnd);
|
|
44475
|
+
const beside = Math.min(changeSize(segments[i - 1], a, b), changeSize(segments[i + 1], a, b));
|
|
43690
44476
|
if (len < COMPARE_TUNING.coalesceWindow || len * COMPARE_TUNING.coalesceRatio < beside) {
|
|
43691
44477
|
seg.equal = false;
|
|
43692
44478
|
absorbed = true;
|
|
@@ -43710,17 +44496,26 @@ function mergeChanges(segments) {
|
|
|
43710
44496
|
}
|
|
43711
44497
|
return out;
|
|
43712
44498
|
}
|
|
43713
|
-
function changeSize(seg) {
|
|
43714
|
-
return Math.max(
|
|
44499
|
+
function changeSize(seg, a, b) {
|
|
44500
|
+
return Math.max(words(a, seg.aStart, seg.aEnd), words(b, seg.bStart, seg.bEnd));
|
|
44501
|
+
}
|
|
44502
|
+
function words(tokens, from, to) {
|
|
44503
|
+
let n = 0;
|
|
44504
|
+
for (let i = from; i < to; i++) if (tokens[i].atom || !/^\s+$/.test(tokens[i].text)) n++;
|
|
44505
|
+
return n;
|
|
43715
44506
|
}
|
|
43716
44507
|
function compareTable(oTbl, rTbl, ctx) {
|
|
43717
|
-
if (
|
|
44508
|
+
if (sameXml(oTbl, rTbl)) return;
|
|
43718
44509
|
if (ctx.formatting) {
|
|
43719
44510
|
swapProps(rTbl, "tblPr", recordTableFormatChange, child(oTbl, "tblPr"), ctx);
|
|
43720
44511
|
alignTableGrid(oTbl, rTbl, ctx);
|
|
43721
44512
|
}
|
|
43722
|
-
|
|
43723
|
-
|
|
44513
|
+
compareRows(oTbl, rTbl, ctx);
|
|
44514
|
+
}
|
|
44515
|
+
var ROW_LEVEL = ["tr", "sdt", "customXml"];
|
|
44516
|
+
function compareRows(oParent, rParent, ctx) {
|
|
44517
|
+
const oRows = oParent.children.filter((c) => ROW_LEVEL.includes(localName(c.name)));
|
|
44518
|
+
const rRows = rParent.children.filter((c) => ROW_LEVEL.includes(localName(c.name)));
|
|
43724
44519
|
const anchors = histogramMatch(oRows.map(rowFingerprint), rRows.map(rowFingerprint));
|
|
43725
44520
|
const rows = [];
|
|
43726
44521
|
let oAt = 0;
|
|
@@ -43745,24 +44540,41 @@ function compareTable(oTbl, rTbl, ctx) {
|
|
|
43745
44540
|
oAt = anchor.a + 1;
|
|
43746
44541
|
rAt = anchor.b + 1;
|
|
43747
44542
|
}
|
|
43748
|
-
|
|
44543
|
+
rParent.children = [...rParent.children.filter((c) => !ROW_LEVEL.includes(localName(c.name))), ...rows];
|
|
43749
44544
|
}
|
|
43750
44545
|
function rowFingerprint(tr) {
|
|
44546
|
+
if (localName(tr.name) !== "tr") return `X\0${serializeXml(tr)}`;
|
|
43751
44547
|
const cells = tr.children.filter((c) => localName(c.name) === "tc");
|
|
43752
44548
|
return `${cells.length}\0${normalizeText(paragraphText3(tr))}`;
|
|
43753
44549
|
}
|
|
43754
|
-
function
|
|
43755
|
-
|
|
43756
|
-
|
|
43757
|
-
|
|
44550
|
+
function rowsIn(el26) {
|
|
44551
|
+
if (localName(el26.name) === "tr") return [el26];
|
|
44552
|
+
const inner = localName(el26.name) === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44553
|
+
return (inner?.children ?? []).filter((c) => ROW_LEVEL.includes(localName(c.name))).flatMap(rowsIn);
|
|
44554
|
+
}
|
|
44555
|
+
function deletedRow(el26, ctx) {
|
|
44556
|
+
const clone = cloneXml(el26);
|
|
44557
|
+
for (const tr of rowsIn(clone)) {
|
|
44558
|
+
markRowRevision(tr, "del", ctx.meta);
|
|
44559
|
+
wrapRowContent(tr, "del", ctx);
|
|
44560
|
+
}
|
|
43758
44561
|
return clone;
|
|
43759
44562
|
}
|
|
43760
|
-
function insertedRow(
|
|
43761
|
-
|
|
43762
|
-
|
|
43763
|
-
|
|
44563
|
+
function insertedRow(el26, ctx) {
|
|
44564
|
+
for (const tr of rowsIn(el26)) {
|
|
44565
|
+
markRowRevision(tr, "ins", ctx.meta);
|
|
44566
|
+
wrapRowContent(tr, "ins", ctx);
|
|
44567
|
+
}
|
|
44568
|
+
return el26;
|
|
43764
44569
|
}
|
|
43765
44570
|
function compareRow(oTr, rTr, ctx) {
|
|
44571
|
+
if (localName(oTr.name) !== "tr") {
|
|
44572
|
+
const inner = (el26) => localName(el26.name) === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44573
|
+
const o = inner(oTr);
|
|
44574
|
+
const r = inner(rTr);
|
|
44575
|
+
if (o && r) compareRows(o, r, ctx);
|
|
44576
|
+
return [rTr];
|
|
44577
|
+
}
|
|
43766
44578
|
const oCells = oTr.children.filter((c) => localName(c.name) === "tc");
|
|
43767
44579
|
const rCells = rTr.children.filter((c) => localName(c.name) === "tc");
|
|
43768
44580
|
if (oCells.length !== rCells.length) {
|
|
@@ -43785,7 +44597,7 @@ function compareRow(oTr, rTr, ctx) {
|
|
|
43785
44597
|
function alignTableGrid(oTbl, rTbl, ctx) {
|
|
43786
44598
|
const oGrid = child(oTbl, "tblGrid");
|
|
43787
44599
|
const rGrid = child(rTbl, "tblGrid");
|
|
43788
|
-
if (!oGrid || !rGrid ||
|
|
44600
|
+
if (!oGrid || !rGrid || sameXml(oGrid, rGrid)) return;
|
|
43789
44601
|
const live = rGrid.children.map(cloneXml);
|
|
43790
44602
|
rGrid.children = oGrid.children.map(cloneXml);
|
|
43791
44603
|
recordTableGridChange(rTbl, ctx.meta);
|
|
@@ -43872,7 +44684,22 @@ function applyParagraphMarks(entries, ctx) {
|
|
|
43872
44684
|
}
|
|
43873
44685
|
}
|
|
43874
44686
|
function markRun(entries, start, end, ctx) {
|
|
43875
|
-
|
|
44687
|
+
let at = start;
|
|
44688
|
+
while (at <= end) {
|
|
44689
|
+
if (!isParagraph(entries[at].el)) {
|
|
44690
|
+
at++;
|
|
44691
|
+
continue;
|
|
44692
|
+
}
|
|
44693
|
+
let last = at;
|
|
44694
|
+
while (last + 1 <= end && isParagraph(entries[last + 1].el)) last++;
|
|
44695
|
+
markParagraphRun(entries, at, last, ctx);
|
|
44696
|
+
at = last + 1;
|
|
44697
|
+
}
|
|
44698
|
+
}
|
|
44699
|
+
function markParagraphRun(entries, start, end, ctx) {
|
|
44700
|
+
const endsSection = (el26) => isParagraph(el26) && !!child(child(el26, "pPr"), "sectPr");
|
|
44701
|
+
const before = start > 0 ? entries[start - 1].el : void 0;
|
|
44702
|
+
const shifted = !!before && isParagraph(before) && !endsSection(before) && !entries.slice(start, end + 1).some((e) => endsSection(e.el));
|
|
43876
44703
|
if (shifted) {
|
|
43877
44704
|
for (let i = start; i <= end; i++) {
|
|
43878
44705
|
if (!isParagraph(entries[i].el) || !isParagraph(entries[i - 1].el)) continue;
|
|
@@ -45359,9 +46186,10 @@ async function renderForPrint(bytes, container) {
|
|
|
45359
46186
|
const doc = DocxDocument.load(bytes);
|
|
45360
46187
|
const layout = layoutDocument(doc);
|
|
45361
46188
|
renderToDom(doc, layout, container, { zoom: 1, pageGap: 0, pageShadow: false, comments: false });
|
|
46189
|
+
pruneBlankSpans(container);
|
|
45362
46190
|
const first = layout.pages[0];
|
|
45363
46191
|
const style = document.createElement("style");
|
|
45364
|
-
style.textContent = printStylesheet(first?.width ?? 816, first?.height ?? 1056);
|
|
46192
|
+
style.textContent = printStylesheet(first?.width ?? 816, first?.height ?? 1056, container);
|
|
45365
46193
|
document.head.append(style);
|
|
45366
46194
|
await paintsSettled();
|
|
45367
46195
|
await Promise.all(Array.from(container.querySelectorAll("img"), (img) => img.decode().catch(() => void 0)));
|
|
@@ -55815,4 +56643,4 @@ export {
|
|
|
55815
56643
|
cfb/cfb.js:
|
|
55816
56644
|
(*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com *)
|
|
55817
56645
|
*/
|
|
55818
|
-
//# sourceMappingURL=chunk-
|
|
56646
|
+
//# sourceMappingURL=chunk-ULPL55MT.js.map
|