wordinweb 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QU3LB5BC.js → chunk-WPQ6GPG4.js} +939 -179
- package/dist/chunk-WPQ6GPG4.js.map +1 -0
- package/dist/collab.d.ts +2 -2
- package/dist/collab.js +1 -1
- package/dist/{index-BunX2duN.d.ts → index-BWmOQ4GG.d.ts} +37 -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("/")) : "";
|
|
@@ -8798,6 +8827,10 @@ var _DocxDocument = class _DocxDocument {
|
|
|
8798
8827
|
/** The header part roots, in package order. A watermark is authored into
|
|
8799
8828
|
* every one of them: Word paints it on every page, and a document with a
|
|
8800
8829
|
* first-page or even-page header has more than one header part. */
|
|
8830
|
+
/** The root of the header or footer part at `target`, if this document has one. */
|
|
8831
|
+
headerFooterRoot(target) {
|
|
8832
|
+
return this.hfParts.find((p) => p.target === target)?.root;
|
|
8833
|
+
}
|
|
8801
8834
|
headerRoots() {
|
|
8802
8835
|
return this.hfParts.filter((p) => p.isHeader).map((p) => p.root);
|
|
8803
8836
|
}
|
|
@@ -8961,6 +8994,22 @@ var _DocxDocument = class _DocxDocument {
|
|
|
8961
8994
|
this.saveJournal = null;
|
|
8962
8995
|
}
|
|
8963
8996
|
}
|
|
8997
|
+
/**
|
|
8998
|
+
* An independent copy: what `DocxDocument.load(doc.save())` gives, without
|
|
8999
|
+
* zipping the package and unzipping it again. compareDocuments starts from a
|
|
9000
|
+
* copy of the revised document, and the round trip through the zip was a
|
|
9001
|
+
* third of its time on a document that had not changed at all.
|
|
9002
|
+
*/
|
|
9003
|
+
clone() {
|
|
9004
|
+
const journal = [];
|
|
9005
|
+
this.saveJournal = journal;
|
|
9006
|
+
try {
|
|
9007
|
+
return new _DocxDocument(Package.fromFiles(this.buildPackageFiles()));
|
|
9008
|
+
} finally {
|
|
9009
|
+
for (let i = journal.length - 1; i >= 0; i--) journal[i]();
|
|
9010
|
+
this.saveJournal = null;
|
|
9011
|
+
}
|
|
9012
|
+
}
|
|
8964
9013
|
saveAsync() {
|
|
8965
9014
|
const journal = [];
|
|
8966
9015
|
this.saveJournal = journal;
|
|
@@ -9162,8 +9211,91 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9162
9211
|
return this.relsRoot;
|
|
9163
9212
|
}
|
|
9164
9213
|
/** Register an external hyperlink relationship and return its rId. */
|
|
9165
|
-
|
|
9166
|
-
|
|
9214
|
+
/**
|
|
9215
|
+
* Copy the part that `source`'s main-document relationship `relId` points
|
|
9216
|
+
* at into this package, with every part it references in turn (a chart's
|
|
9217
|
+
* embedded workbook, SmartArt's layout, style and colour parts) and their
|
|
9218
|
+
* content types, and add a relationship to it. Returns the new
|
|
9219
|
+
* relationship id, or null when the source has no such internal part.
|
|
9220
|
+
*
|
|
9221
|
+
* compareDocuments needs this for struck content: a removed picture keeps
|
|
9222
|
+
* its w:drawing, and without its image part the result's reject-all
|
|
9223
|
+
* brought the picture back as an empty frame.
|
|
9224
|
+
*/
|
|
9225
|
+
adoptRelFrom(source, relId, scope = {}) {
|
|
9226
|
+
const rel = source.relationshipsOf(scope.from).get(relId);
|
|
9227
|
+
if (!rel || rel.external) return null;
|
|
9228
|
+
const copied = /* @__PURE__ */ new Map();
|
|
9229
|
+
const sameBytes = (path2, bytes) => {
|
|
9230
|
+
const have = this.pkg.binary(path2);
|
|
9231
|
+
return !!have && have.length === bytes.length && have.every((b, i) => b === bytes[i]);
|
|
9232
|
+
};
|
|
9233
|
+
const copyPart = (from) => {
|
|
9234
|
+
const known = copied.get(from);
|
|
9235
|
+
if (known) return known;
|
|
9236
|
+
const bytes = source.pkg.binary(from);
|
|
9237
|
+
if (!bytes) return null;
|
|
9238
|
+
let to = from;
|
|
9239
|
+
for (let n = 2; this.pkg.has(to) && !sameBytes(to, bytes); n++) to = from.replace(/(\.[^./]+)?$/, `-${n}$1`);
|
|
9240
|
+
copied.set(from, to);
|
|
9241
|
+
if (this.pkg.has(to)) return to;
|
|
9242
|
+
this.pkg.raw()[to] = bytes;
|
|
9243
|
+
this.adoptContentType(source, from, to);
|
|
9244
|
+
const relsXml = source.pkg.text(relsPathFor(from));
|
|
9245
|
+
if (relsXml) {
|
|
9246
|
+
const root = parseXml(relsXml);
|
|
9247
|
+
for (const r of root.children) {
|
|
9248
|
+
if (r.attrs.TargetMode === "External" || !r.attrs.Target) continue;
|
|
9249
|
+
const sub = copyPart(resolvePartPath(from, r.attrs.Target));
|
|
9250
|
+
if (sub) r.attrs.Target = relativePartPath(to, sub);
|
|
9251
|
+
}
|
|
9252
|
+
this.pkg.raw()[relsPathFor(to)] = strToU82(serializeXml(root, true));
|
|
9253
|
+
}
|
|
9254
|
+
return to;
|
|
9255
|
+
};
|
|
9256
|
+
const part = copyPart(rel.target);
|
|
9257
|
+
if (!part) return null;
|
|
9258
|
+
const owner = this.relsOwnerOf(scope.into);
|
|
9259
|
+
const rels = owner.relsRoot;
|
|
9260
|
+
let maxId = 0;
|
|
9261
|
+
for (const r of rels.children) {
|
|
9262
|
+
const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
|
|
9263
|
+
if (m) maxId = Math.max(maxId, parseInt(m[1], 10));
|
|
9264
|
+
}
|
|
9265
|
+
const id = `rId${maxId + 1}`;
|
|
9266
|
+
rels.children.push({
|
|
9267
|
+
name: "Relationship",
|
|
9268
|
+
attrs: { Id: id, Type: rel.type, Target: relativePartPath(owner.partPath, part) },
|
|
9269
|
+
children: [],
|
|
9270
|
+
text: ""
|
|
9271
|
+
});
|
|
9272
|
+
owner.relationships.set(id, { id, type: rel.type, target: part, external: false });
|
|
9273
|
+
return id;
|
|
9274
|
+
}
|
|
9275
|
+
/** Give a part copied from `source` the content type it had there. */
|
|
9276
|
+
adoptContentType(source, from, to) {
|
|
9277
|
+
const types = this.contentTypesRoot;
|
|
9278
|
+
const theirs = source.contentTypesRoot;
|
|
9279
|
+
if (!types || !theirs) return;
|
|
9280
|
+
const override = theirs.children.find((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${from}`);
|
|
9281
|
+
const prefixEnd = types.name.indexOf(":") + 1;
|
|
9282
|
+
const prefix = prefixEnd > 0 ? types.name.slice(0, prefixEnd) : "";
|
|
9283
|
+
if (override) {
|
|
9284
|
+
if (!types.children.some((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${to}`)) {
|
|
9285
|
+
types.children.push({ name: `${prefix}Override`, attrs: { PartName: `/${to}`, ContentType: override.attrs.ContentType }, children: [], text: "" });
|
|
9286
|
+
}
|
|
9287
|
+
return;
|
|
9288
|
+
}
|
|
9289
|
+
const ext = from.slice(from.lastIndexOf(".") + 1).toLowerCase();
|
|
9290
|
+
const byExt = (root) => root.children.find((c) => localName(c.name) === "Default" && c.attrs.Extension?.toLowerCase() === ext);
|
|
9291
|
+
const fallback = byExt(theirs);
|
|
9292
|
+
if (fallback && !byExt(types)) {
|
|
9293
|
+
types.children.unshift({ name: `${prefix}Default`, attrs: { Extension: ext, ContentType: fallback.attrs.ContentType }, children: [], text: "" });
|
|
9294
|
+
}
|
|
9295
|
+
}
|
|
9296
|
+
addHyperlinkRel(url, ownerPart) {
|
|
9297
|
+
const owner = this.relsOwnerOf(ownerPart);
|
|
9298
|
+
const rels = owner.relsRoot;
|
|
9167
9299
|
let maxId = 0;
|
|
9168
9300
|
for (const r of rels.children) {
|
|
9169
9301
|
const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
|
|
@@ -9181,7 +9313,7 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9181
9313
|
children: [],
|
|
9182
9314
|
text: ""
|
|
9183
9315
|
});
|
|
9184
|
-
|
|
9316
|
+
owner.relationships.set(id, { id, type: "hyperlink", target: url, external: true });
|
|
9185
9317
|
return id;
|
|
9186
9318
|
}
|
|
9187
9319
|
/** Retarget an existing external relationship (hyperlink href edit). */
|
|
@@ -9193,6 +9325,23 @@ var _DocxDocument = class _DocxDocument {
|
|
|
9193
9325
|
if (el26) el26.attrs["Target"] = url;
|
|
9194
9326
|
return true;
|
|
9195
9327
|
}
|
|
9328
|
+
/** The relationships owned by `part` (a header or footer part), or by the
|
|
9329
|
+
* document part when null; a header or footer gets a rels root on demand. */
|
|
9330
|
+
relsOwnerOf(part) {
|
|
9331
|
+
const owner = part ? this.hfParts.find((p) => p.target === part) : void 0;
|
|
9332
|
+
if (!owner) return { relsRoot: this.ensureRelsRoot(), relationships: this.documentRels, partPath: this.docPart };
|
|
9333
|
+
owner.relsRoot ?? (owner.relsRoot = {
|
|
9334
|
+
name: "Relationships",
|
|
9335
|
+
attrs: { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" },
|
|
9336
|
+
children: [],
|
|
9337
|
+
text: ""
|
|
9338
|
+
});
|
|
9339
|
+
return { relsRoot: owner.relsRoot, relationships: owner.rels, partPath: owner.target };
|
|
9340
|
+
}
|
|
9341
|
+
/** The relationships of `part` (a header or footer), or of the document part. */
|
|
9342
|
+
relationshipsOf(part) {
|
|
9343
|
+
return (part ? this.hfParts.find((p) => p.target === part)?.rels : void 0) ?? this.documentRels;
|
|
9344
|
+
}
|
|
9196
9345
|
/** The relationships of the part that holds `source`: a header or footer
|
|
9197
9346
|
* part when the element is inside one, else the document part. */
|
|
9198
9347
|
imageRelsOwner(source) {
|
|
@@ -19232,15 +19381,20 @@ var Engine = class {
|
|
|
19232
19381
|
while (out.length < nCols) out.push(bw(fallback));
|
|
19233
19382
|
return out;
|
|
19234
19383
|
};
|
|
19384
|
+
const colTopPads = (r) => {
|
|
19385
|
+
const fallback = this.cellMarginsOf(tbl, false).top ?? 0;
|
|
19386
|
+
return rows[r].cells.flatMap((c) => Array(Math.max(1, c.props.gridSpan)).fill(c.props.margins?.top ?? fallback));
|
|
19387
|
+
};
|
|
19235
19388
|
const boundary = (k) => {
|
|
19236
19389
|
if (k === 0) return Math.max(0, ...colWidths(0, "top", tb?.top));
|
|
19237
19390
|
if (k === rows.length)
|
|
19238
19391
|
return Math.max(0, ...colWidths(rows.length - 1, "bottom", tb?.bottom));
|
|
19239
19392
|
const above = colWidths(k - 1, "bottom", tb?.insideH);
|
|
19240
19393
|
const below = colWidths(k, "top", tb?.insideH);
|
|
19394
|
+
const topPads = colTopPads(k);
|
|
19241
19395
|
const n = Math.max(above.length, below.length);
|
|
19242
19396
|
let w = 0;
|
|
19243
|
-
for (let i = 0; i < n; i++) w = Math.max(w,
|
|
19397
|
+
for (let i = 0; i < n; i++) w = Math.max(w, below[i] ?? 0, (above[i] ?? 0) - (topPads[i] ?? 0));
|
|
19244
19398
|
return w;
|
|
19245
19399
|
};
|
|
19246
19400
|
return { top: boundary(ri), bottom: boundary(ri + 1) };
|
|
@@ -21446,6 +21600,8 @@ function renderPageShell(page, zoom, options) {
|
|
|
21446
21600
|
if (options.pageShadow !== false) {
|
|
21447
21601
|
el26.style.boxShadow = "var(--dxw-page-shadow, 0 1px 3px rgba(0,0,0,.28), 0 4px 14px rgba(0,0,0,.12))";
|
|
21448
21602
|
}
|
|
21603
|
+
el26.dataset.pageWidth = String(page.width);
|
|
21604
|
+
el26.dataset.pageHeight = String(page.height);
|
|
21449
21605
|
el26.dataset.bodyTop = String(page.bodyTop);
|
|
21450
21606
|
el26.dataset.bodyBottom = String(page.bodyBottom);
|
|
21451
21607
|
el26.dataset.columnBands = JSON.stringify(page.columnBands);
|
|
@@ -21762,9 +21918,21 @@ function trackPaint(work) {
|
|
|
21762
21918
|
async function paintsSettled() {
|
|
21763
21919
|
while (pendingPaints.size > 0) await Promise.allSettled([...pendingPaints]);
|
|
21764
21920
|
}
|
|
21765
|
-
function printStylesheet(pageWidthPx, pageHeightPx) {
|
|
21921
|
+
function printStylesheet(pageWidthPx, pageHeightPx, root) {
|
|
21922
|
+
const inches = (px) => `${Math.round(px / 96 * 1e3) / 1e3}in`;
|
|
21923
|
+
const sizes = /* @__PURE__ */ new Set();
|
|
21924
|
+
for (const el26 of root ? Array.from(root.querySelectorAll(".dxw-page[data-page-width]")) : []) {
|
|
21925
|
+
sizes.add(`${el26.dataset.pageWidth}x${el26.dataset.pageHeight}`);
|
|
21926
|
+
}
|
|
21927
|
+
const named = [...sizes].map((key) => {
|
|
21928
|
+
const [w, h] = key.split("x");
|
|
21929
|
+
const name = `dxw-${key.replace(/\./g, "_")}`;
|
|
21930
|
+
return `@page ${name} { size: ${inches(Number(w))} ${inches(Number(h))}; margin: 0; }
|
|
21931
|
+
.dxw-page[data-page-width="${w}"][data-page-height="${h}"] { page: ${name}; }`;
|
|
21932
|
+
});
|
|
21766
21933
|
return `
|
|
21767
|
-
@page { size: ${pageWidthPx
|
|
21934
|
+
@page { size: ${inches(pageWidthPx)} ${inches(pageHeightPx)}; margin: 0; }
|
|
21935
|
+
${named.join("\n ")}
|
|
21768
21936
|
html, body { margin: 0; padding: 0; }
|
|
21769
21937
|
.dxw-pages { display: block !important; padding: 0 !important; gap: 0 !important; }
|
|
21770
21938
|
.dxw-pages, .dxw-pages * {
|
|
@@ -21775,13 +21943,25 @@ function printStylesheet(pageWidthPx, pageHeightPx) {
|
|
|
21775
21943
|
.dxw-comment-card, .dxw-hf-marker { display: none !important; }
|
|
21776
21944
|
`;
|
|
21777
21945
|
}
|
|
21946
|
+
function pruneBlankSpans(root) {
|
|
21947
|
+
let removed = 0;
|
|
21948
|
+
for (const el26 of Array.from(root.querySelectorAll(".dxw-page span"))) {
|
|
21949
|
+
if (el26.childElementCount > 0 || el26.textContent?.trim()) continue;
|
|
21950
|
+
if (el26.style.textDecoration.includes("underline")) continue;
|
|
21951
|
+
el26.remove();
|
|
21952
|
+
removed++;
|
|
21953
|
+
}
|
|
21954
|
+
return removed;
|
|
21955
|
+
}
|
|
21778
21956
|
function buildPrintHtml(root, pageWidthPx, pageHeightPx) {
|
|
21779
21957
|
const head = [`<base href="${document.location.href}">`];
|
|
21780
21958
|
for (const node of Array.from(document.head.querySelectorAll("style, link[rel=stylesheet]"))) {
|
|
21781
21959
|
head.push(node.outerHTML);
|
|
21782
21960
|
}
|
|
21783
|
-
head.push(`<style>${printStylesheet(pageWidthPx, pageHeightPx)}</style>`);
|
|
21784
|
-
|
|
21961
|
+
head.push(`<style>${printStylesheet(pageWidthPx, pageHeightPx, root)}</style>`);
|
|
21962
|
+
const pages = root.cloneNode(true);
|
|
21963
|
+
pruneBlankSpans(pages);
|
|
21964
|
+
return `<!doctype html><html><head>${head.join("")}</head><body>${pages.outerHTML}</body></html>`;
|
|
21785
21965
|
}
|
|
21786
21966
|
function printPages(root, pageWidthPx, pageHeightPx) {
|
|
21787
21967
|
const iframe = document.createElement("iframe");
|
|
@@ -23828,7 +24008,9 @@ function revisionWrapper(sample, kind, content, meta) {
|
|
|
23828
24008
|
}
|
|
23829
24009
|
function markDeletedText(el26) {
|
|
23830
24010
|
const walk2 = (e) => {
|
|
24011
|
+
if (localName(e.name) === "txbxContent") return;
|
|
23831
24012
|
if (localName(e.name) === "t") e.name = e.name.replace(/t$/, "delText");
|
|
24013
|
+
else if (localName(e.name) === "instrText") e.name = e.name.replace(/instrText$/, "delInstrText");
|
|
23832
24014
|
for (const c of e.children) walk2(c);
|
|
23833
24015
|
};
|
|
23834
24016
|
walk2(el26);
|
|
@@ -24087,7 +24269,8 @@ function ensurePPrRPr(pEl) {
|
|
|
24087
24269
|
let rPr = pPr.children.find((c) => localName(c.name) === "rPr");
|
|
24088
24270
|
if (!rPr) {
|
|
24089
24271
|
rPr = el2(`${w}rPr`);
|
|
24090
|
-
pPr.children.
|
|
24272
|
+
const at = pPr.children.findIndex((c) => localName(c.name) === "sectPr" || localName(c.name) === "pPrChange");
|
|
24273
|
+
pPr.children.splice(at === -1 ? pPr.children.length : at, 0, rPr);
|
|
24091
24274
|
}
|
|
24092
24275
|
return rPr;
|
|
24093
24276
|
}
|
|
@@ -24247,6 +24430,8 @@ function convertDelTextToText(el26) {
|
|
|
24247
24430
|
const walk2 = (e) => {
|
|
24248
24431
|
if (localName(e.name) === "delText") {
|
|
24249
24432
|
e.name = e.name.replace(/delText$/, "t");
|
|
24433
|
+
} else if (localName(e.name) === "delInstrText") {
|
|
24434
|
+
e.name = e.name.replace(/delInstrText$/, "instrText");
|
|
24250
24435
|
}
|
|
24251
24436
|
for (const c of e.children) walk2(c);
|
|
24252
24437
|
};
|
|
@@ -24268,7 +24453,7 @@ function removeEl(doc, el26) {
|
|
|
24268
24453
|
parent.children.splice(idx, 1);
|
|
24269
24454
|
return true;
|
|
24270
24455
|
}
|
|
24271
|
-
var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo"];
|
|
24456
|
+
var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo", "fldSimple"];
|
|
24272
24457
|
function removeRevisionAndEmptyHost(doc, el26) {
|
|
24273
24458
|
const parent = doc.findParentOf(el26);
|
|
24274
24459
|
if (!removeEl(doc, el26)) return false;
|
|
@@ -24277,26 +24462,57 @@ function removeRevisionAndEmptyHost(doc, el26) {
|
|
|
24277
24462
|
}
|
|
24278
24463
|
return true;
|
|
24279
24464
|
}
|
|
24280
|
-
function joinWithNext(doc, pEl) {
|
|
24465
|
+
function joinWithNext(doc, pEl, gone) {
|
|
24281
24466
|
const parent = doc.findParentOf(pEl);
|
|
24282
24467
|
if (!parent) return false;
|
|
24283
24468
|
const idx = parent.children.indexOf(pEl);
|
|
24284
|
-
|
|
24285
|
-
|
|
24469
|
+
let at = idx + 1;
|
|
24470
|
+
while (at < parent.children.length && MARKERS.includes(localName(parent.children[at].name))) at++;
|
|
24471
|
+
const next = parent.children[at];
|
|
24472
|
+
if (!next || localName(next.name) !== "p") {
|
|
24473
|
+
const goes = gone === "del" ? ["pPr", "del", "moveFrom", "proofErr", ...MOVE_RANGES] : ["pPr", "ins", "moveTo", "proofErr", ...MOVE_RANGES];
|
|
24474
|
+
const stays = MARKERS.filter((m) => m !== "proofErr");
|
|
24475
|
+
const removable = (c) => goes.includes(localName(c.name)) || stays.includes(localName(c.name)) || isEmptyPlaceholder(c) || RUN_CONTAINERS.includes(localName(c.name)) && c.children.every(removable);
|
|
24476
|
+
if (!pEl.children.every(removable)) return false;
|
|
24477
|
+
parent.children.splice(idx, 1, ...pEl.children.filter((c) => stays.includes(localName(c.name))));
|
|
24478
|
+
dropEmptiedControl(doc, parent);
|
|
24479
|
+
return true;
|
|
24480
|
+
}
|
|
24481
|
+
const between = parent.children.slice(idx + 1, at);
|
|
24286
24482
|
const moved = next.children.filter((c) => localName(c.name) !== "pPr");
|
|
24287
|
-
pEl.children.push(...moved);
|
|
24288
|
-
parent.children.splice(idx + 1,
|
|
24483
|
+
pEl.children.push(...between, ...moved);
|
|
24484
|
+
parent.children.splice(idx + 1, at - idx);
|
|
24485
|
+
carrySectionBreak(pEl, next);
|
|
24289
24486
|
return true;
|
|
24290
24487
|
}
|
|
24488
|
+
var MARKERS = ["bookmarkStart", "bookmarkEnd", "commentRangeStart", "commentRangeEnd", "permStart", "permEnd", "proofErr"];
|
|
24489
|
+
function carrySectionBreak(pEl, next) {
|
|
24490
|
+
const pPrOf = (p) => p.children.find((c) => localName(c.name) === "pPr");
|
|
24491
|
+
const sectOf = (p) => pPrOf(p)?.children.find((c) => localName(c.name) === "sectPr");
|
|
24492
|
+
const mine = sectOf(pEl);
|
|
24493
|
+
const theirs = sectOf(next);
|
|
24494
|
+
if (!mine && !theirs) return;
|
|
24495
|
+
let pPr = pPrOf(pEl);
|
|
24496
|
+
if (!pPr) {
|
|
24497
|
+
pPr = { name: "w:pPr", attrs: {}, children: [], text: "" };
|
|
24498
|
+
pEl.children.unshift(pPr);
|
|
24499
|
+
}
|
|
24500
|
+
pPr.children = pPr.children.filter((c) => c !== mine);
|
|
24501
|
+
if (theirs) {
|
|
24502
|
+
const at = pPr.children.findIndex((c) => localName(c.name) === "pPrChange");
|
|
24503
|
+
pPr.children.splice(at === -1 ? pPr.children.length : at, 0, theirs);
|
|
24504
|
+
}
|
|
24505
|
+
if (pPr.children.length === 0) pEl.children.splice(pEl.children.indexOf(pPr), 1);
|
|
24506
|
+
}
|
|
24291
24507
|
function collectRevisions(doc) {
|
|
24292
24508
|
const out = [];
|
|
24293
24509
|
const walk2 = (el26) => {
|
|
24294
24510
|
const ln = localName(el26.name);
|
|
24295
24511
|
if (ln === "pPr") return;
|
|
24296
|
-
if (ln === "ins" || ln === "del") {
|
|
24512
|
+
if (ln === "ins" || ln === "del" || ln === "moveTo" || ln === "moveFrom") {
|
|
24297
24513
|
out.push({
|
|
24298
24514
|
el: el26,
|
|
24299
|
-
kind: ln === "ins" ? "insertion" : "deletion",
|
|
24515
|
+
kind: ln === "ins" || ln === "moveTo" ? "insertion" : "deletion",
|
|
24300
24516
|
author: attrByLocal(el26, "author")
|
|
24301
24517
|
});
|
|
24302
24518
|
}
|
|
@@ -24330,10 +24546,10 @@ function collectRevisions(doc) {
|
|
|
24330
24546
|
const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
|
|
24331
24547
|
for (const c of rPr?.children ?? []) {
|
|
24332
24548
|
const cn = localName(c.name);
|
|
24333
|
-
if (cn === "ins" || cn === "del") {
|
|
24549
|
+
if (cn === "ins" || cn === "del" || cn === "moveTo" || cn === "moveFrom") {
|
|
24334
24550
|
out.push({
|
|
24335
24551
|
el: c,
|
|
24336
|
-
kind: cn === "ins" ? "markInsertion" : "markDeletion",
|
|
24552
|
+
kind: cn === "ins" || cn === "moveTo" ? "markInsertion" : "markDeletion",
|
|
24337
24553
|
paragraph: el26,
|
|
24338
24554
|
author: attrByLocal(c, "author")
|
|
24339
24555
|
});
|
|
@@ -24355,7 +24571,7 @@ function applyAccept(doc, ref) {
|
|
|
24355
24571
|
case "markDeletion":
|
|
24356
24572
|
if (!ref.paragraph) return false;
|
|
24357
24573
|
removeMarkRevision(ref);
|
|
24358
|
-
return joinWithNext(doc, ref.paragraph);
|
|
24574
|
+
return joinWithNext(doc, ref.paragraph, "del");
|
|
24359
24575
|
case "rowInsertion":
|
|
24360
24576
|
return removeRowRevision(doc, ref);
|
|
24361
24577
|
case "rowDeletion":
|
|
@@ -24426,7 +24642,7 @@ function applyReject(doc, ref) {
|
|
|
24426
24642
|
case "markInsertion":
|
|
24427
24643
|
if (!ref.paragraph) return false;
|
|
24428
24644
|
removeMarkRevision(ref);
|
|
24429
|
-
return joinWithNext(doc, ref.paragraph);
|
|
24645
|
+
return joinWithNext(doc, ref.paragraph, "ins");
|
|
24430
24646
|
case "markDeletion":
|
|
24431
24647
|
return removeMarkRevision(ref);
|
|
24432
24648
|
case "rowInsertion":
|
|
@@ -24477,11 +24693,23 @@ function applyToAll(doc, apply, author) {
|
|
|
24477
24693
|
for (const part of held) notes.add(part);
|
|
24478
24694
|
}
|
|
24479
24695
|
if (n > 0) {
|
|
24696
|
+
dropSettledMoveRanges(doc);
|
|
24480
24697
|
doc.markNotePartsChanged([...notes]);
|
|
24481
24698
|
doc.refresh();
|
|
24482
24699
|
}
|
|
24483
24700
|
return n;
|
|
24484
24701
|
}
|
|
24702
|
+
var MOVE_RANGES = ["moveFromRangeStart", "moveFromRangeEnd", "moveToRangeStart", "moveToRangeEnd"];
|
|
24703
|
+
function dropSettledMoveRanges(doc) {
|
|
24704
|
+
const roots = doc.revisionRoots();
|
|
24705
|
+
const has = (el26) => ["moveFrom", "moveTo"].includes(localName(el26.name)) || el26.children.some(has);
|
|
24706
|
+
if (roots.some(has)) return;
|
|
24707
|
+
const strip = (el26) => {
|
|
24708
|
+
el26.children = el26.children.filter((c) => !MOVE_RANGES.includes(localName(c.name)));
|
|
24709
|
+
for (const c of el26.children) strip(c);
|
|
24710
|
+
};
|
|
24711
|
+
for (const root of roots) strip(root);
|
|
24712
|
+
}
|
|
24485
24713
|
function removeMarkRevision(ref) {
|
|
24486
24714
|
const pPr = ref.paragraph?.children.find((c) => localName(c.name) === "pPr");
|
|
24487
24715
|
const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
|
|
@@ -24513,9 +24741,24 @@ function removeRow(doc, ref) {
|
|
|
24513
24741
|
const idx = tbl.children.indexOf(ref.row);
|
|
24514
24742
|
if (idx === -1) return false;
|
|
24515
24743
|
tbl.children.splice(idx, 1);
|
|
24516
|
-
if (!tbl.children.some((c) => localName(c.name) === "tr"))
|
|
24744
|
+
if (!tbl.children.some((c) => localName(c.name) === "tr")) {
|
|
24745
|
+
const holder = doc.findParentOf(tbl);
|
|
24746
|
+
removeEl(doc, tbl);
|
|
24747
|
+
if (holder) dropEmptiedControl(doc, holder);
|
|
24748
|
+
}
|
|
24517
24749
|
return true;
|
|
24518
24750
|
}
|
|
24751
|
+
function dropEmptiedControl(doc, container) {
|
|
24752
|
+
const ln = localName(container.name);
|
|
24753
|
+
const control = ln === "sdtContent" ? doc.findParentOf(container) : ln === "customXml" ? container : void 0;
|
|
24754
|
+
if (!control) return;
|
|
24755
|
+
const left = container.children.filter((c) => localName(c.name) !== "customXmlPr");
|
|
24756
|
+
if (left.some((c) => !MARKERS.includes(localName(c.name)))) return;
|
|
24757
|
+
const holder = doc.findParentOf(control);
|
|
24758
|
+
if (!holder) return;
|
|
24759
|
+
holder.children.splice(holder.children.indexOf(control), 1, ...left.filter((c) => localName(c.name) !== "proofErr"));
|
|
24760
|
+
dropEmptiedControl(doc, holder);
|
|
24761
|
+
}
|
|
24519
24762
|
|
|
24520
24763
|
// ../core/src/edit/blocks.ts
|
|
24521
24764
|
function el3(name, attrs = {}, children3 = [], text = "") {
|
|
@@ -27815,7 +28058,7 @@ function sortTableRows(doc, tblEl, colIdx, order, compare, hasHeader = false) {
|
|
|
27815
28058
|
return tc ? cellText(tc).trim() : "";
|
|
27816
28059
|
};
|
|
27817
28060
|
const numeric2 = (s) => parseFloat(s.replace(/[^0-9.eE+-]/g, ""));
|
|
27818
|
-
const
|
|
28061
|
+
const compareRows2 = (a, b) => {
|
|
27819
28062
|
const ka = keyOf2(a);
|
|
27820
28063
|
const kb = keyOf2(b);
|
|
27821
28064
|
let d;
|
|
@@ -27831,7 +28074,7 @@ function sortTableRows(doc, tblEl, colIdx, order, compare, hasHeader = false) {
|
|
|
27831
28074
|
}
|
|
27832
28075
|
return order === "asc" ? d : -d;
|
|
27833
28076
|
};
|
|
27834
|
-
const sorted = [...body].sort(
|
|
28077
|
+
const sorted = [...body].sort(compareRows2);
|
|
27835
28078
|
if (sorted.every((tr, i) => tr === body[i])) return false;
|
|
27836
28079
|
const slots = body.map((tr) => tblEl.children.indexOf(tr));
|
|
27837
28080
|
sorted.forEach((tr, i) => {
|
|
@@ -43010,6 +43253,15 @@ var COMPARE_TUNING = {
|
|
|
43010
43253
|
shortParagraphTokens: 5,
|
|
43011
43254
|
/** Levenshtein ratio a short paragraph pair must reach instead of minDice. */
|
|
43012
43255
|
minShortRatio: 0.8,
|
|
43256
|
+
/**
|
|
43257
|
+
* Or the share of words a short pair has in common (Dice over words), which
|
|
43258
|
+
* a one-word edit in a short heading or list item keeps at half or more
|
|
43259
|
+
* ("Bullet one" / "Bullet updated", "Alignment and flow" / "Alignment and
|
|
43260
|
+
* flow as amended") while its character ratio falls under minShortRatio.
|
|
43261
|
+
* Below it, those came out struck and reinserted whole: 99% of the compare
|
|
43262
|
+
* benchmark's whole-paragraph misses were paragraphs under five words.
|
|
43263
|
+
*/
|
|
43264
|
+
minShortWordDice: 0.5,
|
|
43013
43265
|
/**
|
|
43014
43266
|
* ABSOLUTE floor for the coalescing pass: a matched run shorter than this,
|
|
43015
43267
|
* sitting between two changes in the inner word diff, is absorbed into the
|
|
@@ -43028,15 +43280,19 @@ var COMPARE_TUNING = {
|
|
|
43028
43280
|
* next to 1) while a rewritten clause becomes one (3 is small next to 7).
|
|
43029
43281
|
*
|
|
43030
43282
|
* Both numbers were settled against test/compare-corpus.test.ts; see the
|
|
43031
|
-
* calibration note there for what each fixture pins down.
|
|
43283
|
+
* calibration note there for what each fixture pins down. The ratio was 2
|
|
43284
|
+
* while spaces rode on words: "hardware," and "hardware " differed, so the
|
|
43285
|
+
* rewritten sentence of fixture 3 shared only three words. Matched exactly,
|
|
43286
|
+
* it shares four ("shortage of test hardware") beside seven changed, which
|
|
43287
|
+
* 1.5 absorbs (6 < 7) and 2 does not.
|
|
43032
43288
|
*/
|
|
43033
|
-
coalesceRatio:
|
|
43289
|
+
coalesceRatio: 1.5
|
|
43034
43290
|
};
|
|
43035
43291
|
function normalizeText(s) {
|
|
43036
43292
|
return s.replace(/\s+/g, " ").trim();
|
|
43037
43293
|
}
|
|
43038
43294
|
function wordTokens(s) {
|
|
43039
|
-
return s.match(
|
|
43295
|
+
return s.match(/[\p{L}\p{M}\p{N}]+(?:['\u2019][\p{L}\p{M}\p{N}]+)*|[^\s\p{L}\p{M}\p{N}]|\s+/gu) ?? [];
|
|
43040
43296
|
}
|
|
43041
43297
|
function similarityTokens(s) {
|
|
43042
43298
|
const n = normalizeText(s).toLowerCase();
|
|
@@ -43082,14 +43338,16 @@ function similarity(a, b) {
|
|
|
43082
43338
|
const nb = normalizeText(b).toLowerCase();
|
|
43083
43339
|
const longest = Math.max(na.length, nb.length);
|
|
43084
43340
|
if (longest === 0) return 1;
|
|
43085
|
-
|
|
43341
|
+
const ratio = 1 - levenshtein(na, nb) / longest;
|
|
43342
|
+
const words2 = dice(new Set(ta), new Set(tb));
|
|
43343
|
+
return Math.max(words2, ratio >= COMPARE_TUNING.minShortRatio ? ratio : 0);
|
|
43086
43344
|
}
|
|
43087
43345
|
return dice(bigrams(ta), bigrams(tb));
|
|
43088
43346
|
}
|
|
43089
43347
|
function similarityFloor(a, b) {
|
|
43090
43348
|
const la = similarityTokens(a).length;
|
|
43091
43349
|
const lb = similarityTokens(b).length;
|
|
43092
|
-
return isShortPair(la, lb) ? COMPARE_TUNING.
|
|
43350
|
+
return isShortPair(la, lb) ? COMPARE_TUNING.minShortWordDice : COMPARE_TUNING.minDice;
|
|
43093
43351
|
}
|
|
43094
43352
|
function isShortPair(la, lb) {
|
|
43095
43353
|
return Math.min(la, lb) < COMPARE_TUNING.shortParagraphTokens;
|
|
@@ -43100,6 +43358,25 @@ var PASSENGERS = /* @__PURE__ */ new Set(["proofErr", "bookmarkStart", "bookmark
|
|
|
43100
43358
|
function isPassenger(ln) {
|
|
43101
43359
|
return PASSENGERS.has(ln);
|
|
43102
43360
|
}
|
|
43361
|
+
var WORD_CHAR = /[\p{L}\p{M}\p{N}]/u;
|
|
43362
|
+
var CONTAINERS = /* @__PURE__ */ new Set(["hyperlink", "smartTag", "fldSimple", "customXml", "dir", "bdo", "sdt"]);
|
|
43363
|
+
var CONTAINER_PROPS = /* @__PURE__ */ new Set(["pPr", "sdtPr", "sdtEndPr", "smartTagPr", "customXmlPr"]);
|
|
43364
|
+
function atomKey(atom, noteKey) {
|
|
43365
|
+
const ln = localName(atom.name);
|
|
43366
|
+
if (ln === "footnoteReference" || ln === "endnoteReference") {
|
|
43367
|
+
const { "w:id": _id, ...rest } = atom.attrs;
|
|
43368
|
+
return serializeXml({ ...atom, attrs: rest }) + (noteKey ? "\0" + noteKey(atom) : "");
|
|
43369
|
+
}
|
|
43370
|
+
const strip = (e) => localName(e.name) === "txbxContent" ? { ...e, children: [], text: "" } : { ...e, children: e.children.map(strip) };
|
|
43371
|
+
return serializeXml(hasTextBox(atom) ? strip(atom) : atom);
|
|
43372
|
+
}
|
|
43373
|
+
function hasTextBox(el26) {
|
|
43374
|
+
return localName(el26.name) === "txbxContent" || el26.children.some(hasTextBox);
|
|
43375
|
+
}
|
|
43376
|
+
function textBoxesIn(el26) {
|
|
43377
|
+
if (localName(el26.name) === "txbxContent") return [el26];
|
|
43378
|
+
return el26.children.flatMap(textBoxesIn);
|
|
43379
|
+
}
|
|
43103
43380
|
function readParagraph(pEl, options = {}) {
|
|
43104
43381
|
const keyOf2 = options.whitespace === false ? (word) => word.trimEnd() : (word) => word;
|
|
43105
43382
|
const tokens = [];
|
|
@@ -43112,31 +43389,80 @@ function readParagraph(pEl, options = {}) {
|
|
|
43112
43389
|
}
|
|
43113
43390
|
tokens.push(token);
|
|
43114
43391
|
};
|
|
43115
|
-
|
|
43116
|
-
|
|
43117
|
-
|
|
43118
|
-
if (
|
|
43119
|
-
|
|
43120
|
-
|
|
43121
|
-
|
|
43122
|
-
|
|
43123
|
-
|
|
43124
|
-
|
|
43125
|
-
|
|
43126
|
-
|
|
43127
|
-
|
|
43128
|
-
|
|
43129
|
-
|
|
43130
|
-
|
|
43131
|
-
|
|
43132
|
-
|
|
43133
|
-
|
|
43134
|
-
|
|
43392
|
+
let segment = [];
|
|
43393
|
+
let segmentPath = [];
|
|
43394
|
+
const flush = () => {
|
|
43395
|
+
if (segment.length === 0) return;
|
|
43396
|
+
const text = segment.map((p) => p.text).join("");
|
|
43397
|
+
const cuts = [];
|
|
43398
|
+
let base = 0;
|
|
43399
|
+
for (let i = 0; i + 1 < segment.length; i++) {
|
|
43400
|
+
base += segment[i].text.length;
|
|
43401
|
+
if (WORD_CHAR.test(text[base - 1] ?? "") && WORD_CHAR.test(text[base] ?? "") && runPropsKey(segment[i].run) !== runPropsKey(segment[i + 1].run)) {
|
|
43402
|
+
cuts.push(base);
|
|
43403
|
+
}
|
|
43404
|
+
}
|
|
43405
|
+
const words2 = [];
|
|
43406
|
+
let from = 0;
|
|
43407
|
+
for (const cut of [...cuts, text.length]) {
|
|
43408
|
+
words2.push(...wordTokens(text.slice(from, cut)));
|
|
43409
|
+
from = cut;
|
|
43410
|
+
}
|
|
43411
|
+
let at = 0;
|
|
43412
|
+
for (const word of words2) {
|
|
43413
|
+
const end = at + word.length;
|
|
43414
|
+
const pieces = [];
|
|
43415
|
+
let base2 = 0;
|
|
43416
|
+
for (const p of segment) {
|
|
43417
|
+
const from2 = Math.max(at, base2);
|
|
43418
|
+
const to = Math.min(end, base2 + p.text.length);
|
|
43419
|
+
if (from2 < to) pieces.push({ run: p.run, t: p.t, start: from2 - base2, end: to - base2, text: text.slice(from2, to) });
|
|
43420
|
+
base2 += p.text.length;
|
|
43421
|
+
}
|
|
43422
|
+
const first = pieces[0];
|
|
43423
|
+
push({ key: "w\0" + keyOf2(word), text: word, run: first.run, t: first.t, start: first.start, end: first.end, pieces, path: segmentPath });
|
|
43424
|
+
at = end;
|
|
43425
|
+
}
|
|
43426
|
+
segment = [];
|
|
43427
|
+
};
|
|
43428
|
+
const walk2 = (kids, path2) => {
|
|
43429
|
+
for (const cEl of kids) {
|
|
43430
|
+
const ln = localName(cEl.name);
|
|
43431
|
+
if (CONTAINER_PROPS.has(ln)) continue;
|
|
43432
|
+
if (PASSENGERS.has(ln)) {
|
|
43433
|
+
flush();
|
|
43434
|
+
pending.push(cEl);
|
|
43435
|
+
continue;
|
|
43436
|
+
}
|
|
43437
|
+
if (CONTAINERS.has(ln)) {
|
|
43438
|
+
const inner = ln === "sdt" ? cEl.children.find((c) => localName(c.name) === "sdtContent") : cEl;
|
|
43439
|
+
if (!inner) continue;
|
|
43440
|
+
flush();
|
|
43441
|
+
walk2(inner.children, [...path2, cEl]);
|
|
43442
|
+
flush();
|
|
43135
43443
|
continue;
|
|
43136
43444
|
}
|
|
43137
|
-
|
|
43445
|
+
if (ln !== "r") {
|
|
43446
|
+
flush();
|
|
43447
|
+
inlineDiffable = false;
|
|
43448
|
+
continue;
|
|
43449
|
+
}
|
|
43450
|
+
if (segment.length > 0 && segmentPath !== path2) flush();
|
|
43451
|
+
segmentPath = path2;
|
|
43452
|
+
for (const rc of cEl.children) {
|
|
43453
|
+
const rln = localName(rc.name);
|
|
43454
|
+
if (rln === "rPr") continue;
|
|
43455
|
+
if (rln === "t") {
|
|
43456
|
+
segment.push({ run: cEl, t: rc, text: rc.text });
|
|
43457
|
+
continue;
|
|
43458
|
+
}
|
|
43459
|
+
flush();
|
|
43460
|
+
push({ key: "a\0" + atomKey(rc, options.noteKey), text: "", run: cEl, atom: rc, path: path2 });
|
|
43461
|
+
}
|
|
43138
43462
|
}
|
|
43139
|
-
}
|
|
43463
|
+
};
|
|
43464
|
+
walk2(pEl.children, []);
|
|
43465
|
+
flush();
|
|
43140
43466
|
return { tokens, tail: pending, text: paragraphText3(pEl), inlineDiffable };
|
|
43141
43467
|
}
|
|
43142
43468
|
function paragraphText3(el26) {
|
|
@@ -43153,31 +43479,41 @@ function paragraphText3(el26) {
|
|
|
43153
43479
|
}
|
|
43154
43480
|
return out;
|
|
43155
43481
|
}
|
|
43156
|
-
function
|
|
43482
|
+
function sliceItems(tokens, from, to, deleted) {
|
|
43157
43483
|
const out = [];
|
|
43158
|
-
let
|
|
43159
|
-
|
|
43160
|
-
|
|
43161
|
-
if (
|
|
43162
|
-
const run = token.run;
|
|
43484
|
+
let open = null;
|
|
43485
|
+
let text = null;
|
|
43486
|
+
const runFor = (run, path2) => {
|
|
43487
|
+
if (open?.run === run) return open.el;
|
|
43163
43488
|
const rPr = run.children.find((c) => localName(c.name) === "rPr");
|
|
43164
|
-
const
|
|
43165
|
-
|
|
43166
|
-
|
|
43167
|
-
|
|
43168
|
-
|
|
43169
|
-
|
|
43489
|
+
const el26 = { name: run.name, attrs: { ...run.attrs }, children: rPr ? [cloneXml(rPr)] : [], text: "" };
|
|
43490
|
+
out.push({ el: el26, path: path2 });
|
|
43491
|
+
open = { run, el: el26 };
|
|
43492
|
+
text = null;
|
|
43493
|
+
return el26;
|
|
43494
|
+
};
|
|
43495
|
+
for (let i = from; i < to; i++) {
|
|
43496
|
+
const token = tokens[i];
|
|
43497
|
+
if (!deleted && token.lead) {
|
|
43498
|
+
out.push(...token.lead.map((el26) => ({ el: cloneXml(el26), path: token.path })));
|
|
43499
|
+
open = null;
|
|
43500
|
+
text = null;
|
|
43501
|
+
}
|
|
43502
|
+
if (token.atom) {
|
|
43503
|
+
runFor(token.run, token.path).children.push(cloneXml(token.atom));
|
|
43504
|
+
text = null;
|
|
43505
|
+
continue;
|
|
43506
|
+
}
|
|
43507
|
+
for (const piece of token.pieces ?? []) {
|
|
43508
|
+
const el26 = runFor(piece.run, token.path);
|
|
43509
|
+
if (text && text.t === piece.t) {
|
|
43510
|
+
text.el.text += piece.text;
|
|
43170
43511
|
continue;
|
|
43171
43512
|
}
|
|
43172
|
-
const
|
|
43173
|
-
|
|
43174
|
-
|
|
43175
|
-
text += tokens[i].text;
|
|
43176
|
-
i++;
|
|
43177
|
-
}
|
|
43178
|
-
children3.push(textElement2(t, text, deleted));
|
|
43513
|
+
const te = textElement2(piece.t, piece.text, deleted);
|
|
43514
|
+
el26.children.push(te);
|
|
43515
|
+
text = { t: piece.t, el: te };
|
|
43179
43516
|
}
|
|
43180
|
-
out.push({ name: run.name, attrs: { ...run.attrs }, children: children3, text: "" });
|
|
43181
43517
|
}
|
|
43182
43518
|
return out;
|
|
43183
43519
|
}
|
|
@@ -43284,46 +43620,232 @@ function lcsFallback(a, b, aLo, aHi, bLo, bHi, out) {
|
|
|
43284
43620
|
|
|
43285
43621
|
// ../core/src/edit/compare/index.ts
|
|
43286
43622
|
function compareDocuments(original, revised, options) {
|
|
43287
|
-
const
|
|
43623
|
+
const note = options.onNote ?? (() => {
|
|
43624
|
+
});
|
|
43625
|
+
const accepted = (doc, which) => {
|
|
43626
|
+
if (collectRevisions(doc).length === 0) return doc;
|
|
43627
|
+
const copy = doc.clone();
|
|
43628
|
+
acceptAllRevisions(copy);
|
|
43629
|
+
note({ code: "existing-revisions-accepted", detail: `the ${which} document held tracked changes; it was compared as if they were accepted` });
|
|
43630
|
+
return copy;
|
|
43631
|
+
};
|
|
43632
|
+
const merged = accepted(revised, "revised");
|
|
43633
|
+
return compareInto(accepted(original, "original"), merged === revised ? revised.clone() : merged, options);
|
|
43634
|
+
}
|
|
43635
|
+
function compareInto(original, merged, options) {
|
|
43636
|
+
let issued = 0;
|
|
43288
43637
|
const ctx = {
|
|
43289
43638
|
doc: merged,
|
|
43290
43639
|
original,
|
|
43291
43640
|
meta: {
|
|
43292
43641
|
author: options.author,
|
|
43293
43642
|
date: options.date,
|
|
43294
|
-
nextId: () =>
|
|
43643
|
+
nextId: () => {
|
|
43644
|
+
issued++;
|
|
43645
|
+
return merged.nextRevisionId();
|
|
43646
|
+
}
|
|
43295
43647
|
},
|
|
43296
43648
|
formatting: options.formatting !== false,
|
|
43297
43649
|
whitespace: options.whitespace !== false,
|
|
43298
43650
|
note: options.onNote ?? (() => {
|
|
43299
|
-
})
|
|
43651
|
+
}),
|
|
43652
|
+
scope: { from: null, into: null },
|
|
43653
|
+
noteIds: { footnote: /* @__PURE__ */ new Map(), endnote: /* @__PURE__ */ new Map() }
|
|
43300
43654
|
};
|
|
43301
43655
|
const originalBody = child(original.docRoot, "body");
|
|
43302
43656
|
const mergedBody = child(merged.docRoot, "body");
|
|
43303
43657
|
if (!originalBody || !mergedBody) throw new Error("document.xml has no w:body");
|
|
43304
|
-
reportUncomparedParts(original,
|
|
43658
|
+
reportUncomparedParts(original, merged, ctx);
|
|
43659
|
+
const headerFooterPairs = pairHeadersFooters(original, merged);
|
|
43660
|
+
compareNotes("footnote", original, merged, ctx);
|
|
43661
|
+
compareNotes("endnote", original, merged, ctx);
|
|
43305
43662
|
const tail = mergedBody.children.filter((c) => localName(c.name) === "sectPr");
|
|
43306
43663
|
const entries = alignBlocks(blockList(originalBody, "sectPr"), blockList(mergedBody, "sectPr"), ctx);
|
|
43307
43664
|
applyParagraphMarks(entries, ctx);
|
|
43308
43665
|
mergedBody.children = [...entries.map((e) => e.el), ...tail];
|
|
43309
|
-
merged
|
|
43666
|
+
giveStruckReferencesTheirOwnNotes("footnote", merged, ctx);
|
|
43667
|
+
giveStruckReferencesTheirOwnNotes("endnote", merged, ctx);
|
|
43668
|
+
for (const [from, into] of headerFooterPairs) {
|
|
43669
|
+
const oRoot = original.headerFooterRoot(from);
|
|
43670
|
+
const mRoot = merged.headerFooterRoot(into);
|
|
43671
|
+
if (oRoot && mRoot) compareContainer(oRoot, mRoot, { ...ctx, scope: { from, into } });
|
|
43672
|
+
}
|
|
43673
|
+
if (issued > 0) merged.refresh();
|
|
43310
43674
|
return merged;
|
|
43311
43675
|
}
|
|
43676
|
+
function compareContainer(oRoot, mRoot, ctx) {
|
|
43677
|
+
const o = blockList(oRoot);
|
|
43678
|
+
const m = blockList(mRoot);
|
|
43679
|
+
if (o.length === m.length && o.every((el26, i) => sameXml(el26, m[i]))) return;
|
|
43680
|
+
const entries = alignBlocks(o, m, ctx);
|
|
43681
|
+
applyParagraphMarks(entries, ctx);
|
|
43682
|
+
const blocks = new Set(m);
|
|
43683
|
+
mRoot.children = [...mRoot.children.filter((c) => !blocks.has(c) && !["p", "tbl", "sdt"].includes(localName(c.name))), ...entries.map((e) => e.el)];
|
|
43684
|
+
}
|
|
43685
|
+
function pairHeadersFooters(original, merged) {
|
|
43686
|
+
const refs = (doc) => {
|
|
43687
|
+
const out = /* @__PURE__ */ new Map();
|
|
43688
|
+
const body = child(doc.docRoot, "body");
|
|
43689
|
+
const sects = (body?.children ?? []).flatMap(
|
|
43690
|
+
(c) => localName(c.name) === "sectPr" ? [c] : localName(c.name) === "p" ? [child(child(c, "pPr"), "sectPr")].filter((x) => !!x) : []
|
|
43691
|
+
);
|
|
43692
|
+
sects.forEach((sp, i) => {
|
|
43693
|
+
for (const ref of sp.children) {
|
|
43694
|
+
const ln = localName(ref.name);
|
|
43695
|
+
if (ln !== "headerReference" && ln !== "footerReference") continue;
|
|
43696
|
+
const target = doc.documentRels.get(ref.attrs["r:id"])?.target;
|
|
43697
|
+
if (target) out.set(`${i}:${ln}:${ref.attrs["w:type"] ?? "default"}`, target);
|
|
43698
|
+
}
|
|
43699
|
+
});
|
|
43700
|
+
return out;
|
|
43701
|
+
};
|
|
43702
|
+
const mine = refs(merged);
|
|
43703
|
+
const pairs = [];
|
|
43704
|
+
const done = /* @__PURE__ */ new Set();
|
|
43705
|
+
for (const [key, from] of refs(original)) {
|
|
43706
|
+
const into = mine.get(key);
|
|
43707
|
+
if (!into || done.has(into)) continue;
|
|
43708
|
+
done.add(into);
|
|
43709
|
+
pairs.push([from, into]);
|
|
43710
|
+
}
|
|
43711
|
+
return pairs;
|
|
43712
|
+
}
|
|
43713
|
+
function compareNotes(kind, original, merged, ctx) {
|
|
43714
|
+
const tree = (doc, create = false) => kind === "footnote" ? doc.footnotesTree(create) : doc.endnotesTree(create);
|
|
43715
|
+
const oRoot = tree(original);
|
|
43716
|
+
const mRoot = tree(merged);
|
|
43717
|
+
if (!oRoot && !mRoot) return;
|
|
43718
|
+
const refName = `${kind}Reference`;
|
|
43719
|
+
const inOrder = (doc, root) => {
|
|
43720
|
+
if (!root) return [];
|
|
43721
|
+
const byId = new Map(
|
|
43722
|
+
root.children.filter((c) => localName(c.name) === kind && !c.attrs["w:type"]).map((c) => [c.attrs["w:id"], c])
|
|
43723
|
+
);
|
|
43724
|
+
const out = [];
|
|
43725
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43726
|
+
const walk2 = (e) => {
|
|
43727
|
+
if (localName(e.name) === refName) {
|
|
43728
|
+
const id = e.attrs["w:id"];
|
|
43729
|
+
const note = byId.get(id);
|
|
43730
|
+
if (note && !seen.has(id)) {
|
|
43731
|
+
seen.add(id);
|
|
43732
|
+
out.push(note);
|
|
43733
|
+
}
|
|
43734
|
+
}
|
|
43735
|
+
for (const c of e.children) walk2(c);
|
|
43736
|
+
};
|
|
43737
|
+
walk2(doc.docRoot);
|
|
43738
|
+
return out;
|
|
43739
|
+
};
|
|
43740
|
+
const oNotes = inOrder(original, oRoot);
|
|
43741
|
+
const rNotes = inOrder(merged, mRoot);
|
|
43742
|
+
const text = (n) => normalizeText(paragraphText3(n));
|
|
43743
|
+
const pairs = [];
|
|
43744
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
43745
|
+
rNotes.forEach((n, j) => {
|
|
43746
|
+
const list = waiting.get(text(n));
|
|
43747
|
+
if (list) list.push(j);
|
|
43748
|
+
else waiting.set(text(n), [j]);
|
|
43749
|
+
});
|
|
43750
|
+
oNotes.forEach((n, i) => {
|
|
43751
|
+
const j = waiting.get(text(n))?.shift();
|
|
43752
|
+
if (j !== void 0) pairs.push([i, j]);
|
|
43753
|
+
});
|
|
43754
|
+
const exactO = new Set(pairs.map((p) => p[0]));
|
|
43755
|
+
const exactR = new Set(pairs.map((p) => p[1]));
|
|
43756
|
+
const oRest = oNotes.map((_, i) => i).filter((i) => !exactO.has(i));
|
|
43757
|
+
const rRest = rNotes.map((_, j) => j).filter((j) => !exactR.has(j));
|
|
43758
|
+
let oAt = 0;
|
|
43759
|
+
let rAt = 0;
|
|
43760
|
+
const rest = histogramMatch(oRest.map((i) => text(oNotes[i])), rRest.map((j) => text(rNotes[j])));
|
|
43761
|
+
for (const a of [...rest, { a: oRest.length, b: rRest.length }]) {
|
|
43762
|
+
if (a.a - oAt === 1 && a.b - rAt === 1) pairs.push([oRest[oAt], rRest[rAt]]);
|
|
43763
|
+
if (a.a < oRest.length && a.b < rRest.length) pairs.push([oRest[a.a], rRest[a.b]]);
|
|
43764
|
+
oAt = a.a + 1;
|
|
43765
|
+
rAt = a.b + 1;
|
|
43766
|
+
}
|
|
43767
|
+
const ids = ctx.noteIds[kind];
|
|
43768
|
+
const pairedO = new Set(pairs.map((p) => p[0]));
|
|
43769
|
+
const pairedR = new Set(pairs.map((p) => p[1]));
|
|
43770
|
+
let changed = false;
|
|
43771
|
+
for (const [i, j] of pairs) {
|
|
43772
|
+
ids.set(oNotes[i].attrs["w:id"], rNotes[j].attrs["w:id"]);
|
|
43773
|
+
const before = ctx.meta.nextId;
|
|
43774
|
+
let touched = false;
|
|
43775
|
+
compareContainer(oNotes[i], rNotes[j], { ...ctx, meta: { ...ctx.meta, nextId: () => (touched = true, before()) } });
|
|
43776
|
+
changed || (changed = touched);
|
|
43777
|
+
}
|
|
43778
|
+
rNotes.forEach((note, j) => {
|
|
43779
|
+
if (pairedR.has(j)) return;
|
|
43780
|
+
for (const block of blockList(note)) if (localName(block.name) === "p") wrapContainerContent(block, "ins", ctx);
|
|
43781
|
+
changed = true;
|
|
43782
|
+
});
|
|
43783
|
+
const removed = oNotes.filter((_, i) => !pairedO.has(i));
|
|
43784
|
+
if (removed.length > 0) {
|
|
43785
|
+
const root = tree(merged, true);
|
|
43786
|
+
if (root) {
|
|
43787
|
+
let next = 1 + Math.max(0, ...root.children.map((c) => Number(c.attrs["w:id"]) || 0));
|
|
43788
|
+
for (const o of removed) {
|
|
43789
|
+
const copy = cloneXml(o);
|
|
43790
|
+
copy.attrs["w:id"] = String(next);
|
|
43791
|
+
ids.set(o.attrs["w:id"], String(next++));
|
|
43792
|
+
for (const block of blockList(copy)) {
|
|
43793
|
+
if (localName(block.name) !== "p") continue;
|
|
43794
|
+
adoptFromOriginal(block, { ...ctx, scope: { from: null, into: null } });
|
|
43795
|
+
wrapContainerContent(block, "del", ctx);
|
|
43796
|
+
}
|
|
43797
|
+
root.children.push(copy);
|
|
43798
|
+
}
|
|
43799
|
+
changed = true;
|
|
43800
|
+
}
|
|
43801
|
+
}
|
|
43802
|
+
if (changed) merged.markNotePartsChanged([kind === "footnote" ? "footnotes" : "endnotes"]);
|
|
43803
|
+
}
|
|
43804
|
+
function giveStruckReferencesTheirOwnNotes(kind, merged, ctx) {
|
|
43805
|
+
const root = kind === "footnote" ? merged.footnotesTree() : merged.endnotesTree();
|
|
43806
|
+
if (!root) return;
|
|
43807
|
+
const refName = `${kind}Reference`;
|
|
43808
|
+
const refs = /* @__PURE__ */ new Map();
|
|
43809
|
+
const walk2 = (e, struck) => {
|
|
43810
|
+
if (localName(e.name) === refName) {
|
|
43811
|
+
const list = refs.get(e.attrs["w:id"]) ?? [];
|
|
43812
|
+
list.push({ ref: e, struck });
|
|
43813
|
+
refs.set(e.attrs["w:id"], list);
|
|
43814
|
+
}
|
|
43815
|
+
for (const c of e.children) walk2(c, struck || localName(c.name) === "del" || rowDeleted(c));
|
|
43816
|
+
};
|
|
43817
|
+
const rowDeleted = (tr) => localName(tr.name) === "tr" && !!child(child(tr, "trPr"), "del");
|
|
43818
|
+
walk2(merged.docRoot, false);
|
|
43819
|
+
let next = 1 + Math.max(0, ...root.children.map((c) => Number(c.attrs["w:id"]) || 0));
|
|
43820
|
+
let changed = false;
|
|
43821
|
+
for (const [id, list] of refs) {
|
|
43822
|
+
if (list.length < 2) continue;
|
|
43823
|
+
const note = root.children.find((c) => localName(c.name) === kind && c.attrs["w:id"] === id);
|
|
43824
|
+
if (!note) continue;
|
|
43825
|
+
const keep = list.find((r) => !r.struck) ?? list[0];
|
|
43826
|
+
for (const r of list) {
|
|
43827
|
+
if (r === keep || !r.struck) continue;
|
|
43828
|
+
const copy = cloneXml(note);
|
|
43829
|
+
copy.attrs["w:id"] = String(next);
|
|
43830
|
+
r.ref.attrs["w:id"] = String(next++);
|
|
43831
|
+
for (const block of blockList(copy)) if (localName(block.name) === "p") wrapContainerContent(block, "del", ctx);
|
|
43832
|
+
root.children.push(copy);
|
|
43833
|
+
changed = true;
|
|
43834
|
+
}
|
|
43835
|
+
}
|
|
43836
|
+
if (changed) merged.markNotePartsChanged([kind === "footnote" ? "footnotes" : "endnotes"]);
|
|
43837
|
+
}
|
|
43312
43838
|
function blockList(container, ...skip) {
|
|
43313
43839
|
return container.children.filter((c) => !skip.includes(localName(c.name)));
|
|
43314
43840
|
}
|
|
43315
43841
|
function reportUncomparedParts(original, revised, ctx) {
|
|
43316
43842
|
const parts = [
|
|
43317
43843
|
["styles.xml", original.stylesTree()],
|
|
43318
|
-
["numbering.xml", original.numberingTree()]
|
|
43319
|
-
["footnotes.xml", original.footnotesTree()],
|
|
43320
|
-
["endnotes.xml", original.endnotesTree()]
|
|
43844
|
+
["numbering.xml", original.numberingTree()]
|
|
43321
43845
|
];
|
|
43322
43846
|
const revisedTrees = {
|
|
43323
43847
|
"styles.xml": revised.stylesTree(),
|
|
43324
|
-
"numbering.xml": revised.numberingTree()
|
|
43325
|
-
"footnotes.xml": revised.footnotesTree(),
|
|
43326
|
-
"endnotes.xml": revised.endnotesTree()
|
|
43848
|
+
"numbering.xml": revised.numberingTree()
|
|
43327
43849
|
};
|
|
43328
43850
|
for (const [name, tree] of parts) {
|
|
43329
43851
|
const other = revisedTrees[name];
|
|
@@ -43333,13 +43855,12 @@ function reportUncomparedParts(original, revised, ctx) {
|
|
|
43333
43855
|
ctx.note({ code: "part-not-compared", detail: `${name} differs; the result carries the revised document's` });
|
|
43334
43856
|
}
|
|
43335
43857
|
}
|
|
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
43858
|
}
|
|
43341
43859
|
function alignBlocks(oBlocks, rBlocks, ctx) {
|
|
43342
|
-
const anchors = histogramMatch(
|
|
43860
|
+
const anchors = histogramMatch(
|
|
43861
|
+
oBlocks.map((el26) => blockFingerprint(el26, ctx.original)),
|
|
43862
|
+
rBlocks.map((el26) => blockFingerprint(el26, ctx.doc))
|
|
43863
|
+
);
|
|
43343
43864
|
const entries = [];
|
|
43344
43865
|
let oAt = 0;
|
|
43345
43866
|
let rAt = 0;
|
|
@@ -43353,19 +43874,23 @@ function alignBlocks(oBlocks, rBlocks, ctx) {
|
|
|
43353
43874
|
}
|
|
43354
43875
|
return entries;
|
|
43355
43876
|
}
|
|
43356
|
-
function blockFingerprint(el26) {
|
|
43877
|
+
function blockFingerprint(el26, doc) {
|
|
43357
43878
|
const ln = localName(el26.name);
|
|
43358
43879
|
if (ln === "p") {
|
|
43359
43880
|
const pPr = child(el26, "pPr");
|
|
43360
43881
|
const style = child(pPr, "pStyle")?.attrs["w:val"] ?? "";
|
|
43361
43882
|
const numPr = child(pPr, "numPr");
|
|
43362
|
-
|
|
43883
|
+
const jc = child(pPr, "jc")?.attrs["w:val"] ?? "";
|
|
43884
|
+
const sect = sectionKey(el26, doc);
|
|
43885
|
+
return `P\0${normalizeText(paragraphText3(el26))}\0${style}\0${numPr ? serializeXml(numPr) : ""}\0${jc}\0${sect}`;
|
|
43363
43886
|
}
|
|
43364
43887
|
if (ln === "tbl") return `T\0${normalizeText(paragraphText3(el26))}`;
|
|
43365
43888
|
return `X\0${serializeXml(el26)}`;
|
|
43366
43889
|
}
|
|
43367
43890
|
function emitGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, entries, ctx) {
|
|
43368
|
-
const
|
|
43891
|
+
const before = entries[entries.length - 1];
|
|
43892
|
+
const unanchored = (!before || !isParagraph(before.el)) && (rHi >= rBlocks.length || !isParagraph(rBlocks[rHi]));
|
|
43893
|
+
const pairings = resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, oLo === 0 && rLo === 0 || unanchored);
|
|
43369
43894
|
let oAt = oLo;
|
|
43370
43895
|
let rAt = rLo;
|
|
43371
43896
|
for (const pairing of [...pairings, { kind: "pair", o: oHi, r: rHi, other: -1 }]) {
|
|
@@ -43397,16 +43922,43 @@ function emitGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, entries, ctx) {
|
|
|
43397
43922
|
}
|
|
43398
43923
|
}
|
|
43399
43924
|
}
|
|
43925
|
+
var GAP_PAIR_CAP = 200 * 200;
|
|
43926
|
+
var GAP_WINDOW = 50;
|
|
43400
43927
|
function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = false) {
|
|
43401
43928
|
const out = [];
|
|
43402
43929
|
const oUsed = /* @__PURE__ */ new Set();
|
|
43403
43930
|
const rUsed = /* @__PURE__ */ new Set();
|
|
43404
|
-
const
|
|
43931
|
+
const placed = [];
|
|
43932
|
+
const crosses = (o, r) => {
|
|
43933
|
+
let lo = 0;
|
|
43934
|
+
let hi = placed.length;
|
|
43935
|
+
while (lo < hi) {
|
|
43936
|
+
const mid = lo + hi >> 1;
|
|
43937
|
+
if (placed[mid].o < o) lo = mid + 1;
|
|
43938
|
+
else hi = mid;
|
|
43939
|
+
}
|
|
43940
|
+
return lo > 0 && placed[lo - 1].r >= r || lo < placed.length && placed[lo].r <= r;
|
|
43941
|
+
};
|
|
43942
|
+
const accept = (p) => {
|
|
43943
|
+
out.push(p);
|
|
43944
|
+
let at = placed.length;
|
|
43945
|
+
while (at > 0 && placed[at - 1].o > p.o) at--;
|
|
43946
|
+
placed.splice(at, 0, p);
|
|
43947
|
+
};
|
|
43948
|
+
const oLen = oHi - oLo;
|
|
43949
|
+
const rLen = rHi - rLo;
|
|
43950
|
+
const windowed = oLen * rLen > GAP_PAIR_CAP;
|
|
43951
|
+
const span = (i) => {
|
|
43952
|
+
if (!windowed) return [rLo, rHi];
|
|
43953
|
+
const centre = rLo + Math.round((i - oLo) * rLen / Math.max(1, oLen));
|
|
43954
|
+
return [Math.max(rLo, centre - GAP_WINDOW), Math.min(rHi, centre + GAP_WINDOW + 1)];
|
|
43955
|
+
};
|
|
43405
43956
|
for (let i = oLo; i < oHi; i++) {
|
|
43406
|
-
|
|
43957
|
+
const [jLo, jHi] = span(i);
|
|
43958
|
+
for (let j = jLo; j + 1 < jHi; j++) {
|
|
43407
43959
|
if (oUsed.has(i) || rUsed.has(j) || rUsed.has(j + 1) || crosses(i, j)) continue;
|
|
43408
43960
|
if (concatMatches([oBlocks[i]], [rBlocks[j], rBlocks[j + 1]], ctx)) {
|
|
43409
|
-
|
|
43961
|
+
accept({ kind: "split", o: i, r: j, other: j + 1 });
|
|
43410
43962
|
oUsed.add(i);
|
|
43411
43963
|
rUsed.add(j);
|
|
43412
43964
|
rUsed.add(j + 1);
|
|
@@ -43414,10 +43966,11 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43414
43966
|
}
|
|
43415
43967
|
}
|
|
43416
43968
|
for (let i = oLo; i + 1 < oHi; i++) {
|
|
43417
|
-
|
|
43969
|
+
const [jLo, jHi] = span(i);
|
|
43970
|
+
for (let j = jLo; j < jHi; j++) {
|
|
43418
43971
|
if (oUsed.has(i) || oUsed.has(i + 1) || rUsed.has(j) || crosses(i, j)) continue;
|
|
43419
43972
|
if (concatMatches([oBlocks[i], oBlocks[i + 1]], [rBlocks[j]], ctx)) {
|
|
43420
|
-
|
|
43973
|
+
accept({ kind: "merge", o: i, r: j, other: i + 1 });
|
|
43421
43974
|
oUsed.add(i);
|
|
43422
43975
|
oUsed.add(i + 1);
|
|
43423
43976
|
rUsed.add(j);
|
|
@@ -43427,9 +43980,10 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43427
43980
|
const scored = [];
|
|
43428
43981
|
for (let i = oLo; i < oHi; i++) {
|
|
43429
43982
|
if (oUsed.has(i)) continue;
|
|
43430
|
-
|
|
43983
|
+
const [jLo, jHi] = span(i);
|
|
43984
|
+
for (let j = jLo; j < jHi; j++) {
|
|
43431
43985
|
if (rUsed.has(j)) continue;
|
|
43432
|
-
if (
|
|
43986
|
+
if (!pairable(oBlocks[i], rBlocks[j], ctx)) continue;
|
|
43433
43987
|
const a = paragraphText3(oBlocks[i]);
|
|
43434
43988
|
const b = paragraphText3(rBlocks[j]);
|
|
43435
43989
|
const score = similarity(a, b);
|
|
@@ -43440,30 +43994,74 @@ function resolveGap(oBlocks, rBlocks, oLo, oHi, rLo, rHi, ctx, needsAnchor = fal
|
|
|
43440
43994
|
for (const cand of scored) {
|
|
43441
43995
|
if (oUsed.has(cand.o) || rUsed.has(cand.r)) continue;
|
|
43442
43996
|
if (crosses(cand.o, cand.r)) continue;
|
|
43443
|
-
|
|
43997
|
+
accept({ kind: "pair", o: cand.o, r: cand.r, other: -1 });
|
|
43444
43998
|
oUsed.add(cand.o);
|
|
43445
43999
|
rUsed.add(cand.r);
|
|
43446
44000
|
}
|
|
44001
|
+
const settled = [...out].sort((a, b) => a.o - b.o);
|
|
44002
|
+
let oFrom = oLo;
|
|
44003
|
+
let rFrom = rLo;
|
|
44004
|
+
for (const p of [...settled, { kind: "pair", o: oHi, r: rHi, other: -1 }]) {
|
|
44005
|
+
const os = [];
|
|
44006
|
+
const rs = [];
|
|
44007
|
+
for (let i = oFrom; i < p.o; i++) if (!oUsed.has(i)) os.push(i);
|
|
44008
|
+
for (let j = rFrom; j < p.r; j++) if (!rUsed.has(j)) rs.push(j);
|
|
44009
|
+
if (os.length === 1 && rs.length === 1 && sameShape2(oBlocks[os[0]], rBlocks[rs[0]], ctx)) {
|
|
44010
|
+
accept({ kind: "pair", o: os[0], r: rs[0], other: -1 });
|
|
44011
|
+
oUsed.add(os[0]);
|
|
44012
|
+
rUsed.add(rs[0]);
|
|
44013
|
+
}
|
|
44014
|
+
oFrom = p.kind === "merge" ? p.other + 1 : p.o + 1;
|
|
44015
|
+
rFrom = p.kind === "split" ? p.other + 1 : p.r + 1;
|
|
44016
|
+
}
|
|
43447
44017
|
if (needsAnchor && out.length === 0) {
|
|
43448
44018
|
for (let i = oLo; i < oHi && out.length === 0; i++) {
|
|
43449
44019
|
if (oUsed.has(i)) continue;
|
|
43450
44020
|
for (let j = rLo; j < rHi; j++) {
|
|
43451
|
-
if (rUsed.has(j) ||
|
|
43452
|
-
|
|
44021
|
+
if (rUsed.has(j) || !pairable(oBlocks[i], rBlocks[j], ctx)) continue;
|
|
44022
|
+
accept({ kind: "pair", o: i, r: j, other: -1 });
|
|
43453
44023
|
break;
|
|
43454
44024
|
}
|
|
43455
44025
|
}
|
|
43456
44026
|
}
|
|
43457
44027
|
return out.sort((a, b) => a.o - b.o);
|
|
43458
44028
|
}
|
|
44029
|
+
function pairable(a, b, ctx) {
|
|
44030
|
+
if (localName(a.name) !== localName(b.name)) return false;
|
|
44031
|
+
if (!isParagraph(a)) return true;
|
|
44032
|
+
return sectionKey(a, ctx.original) === sectionKey(b, ctx.doc);
|
|
44033
|
+
}
|
|
44034
|
+
function sectionKey(el26, doc) {
|
|
44035
|
+
const sectPr = child(child(el26, "pPr"), "sectPr");
|
|
44036
|
+
if (!sectPr) return "";
|
|
44037
|
+
return serializeXml(sectPr).replace(/r:id="([^"]*)"/g, (_, id) => `r:id="${doc.documentRels.get(id)?.target ?? id}"`);
|
|
44038
|
+
}
|
|
44039
|
+
function sameShape2(a, b, ctx) {
|
|
44040
|
+
if (isParagraph(a) && isParagraph(b)) return pairable(a, b, ctx);
|
|
44041
|
+
if (localName(a.name) === localName(b.name) && ["tbl", "sdt", "customXml"].includes(localName(a.name))) return true;
|
|
44042
|
+
const cells = (tr) => tr.children.filter((c) => localName(c.name) === "tc").length;
|
|
44043
|
+
return localName(a.name) === "tr" && localName(b.name) === "tr" && cells(a) === cells(b);
|
|
44044
|
+
}
|
|
44045
|
+
function readOptions(ctx, original) {
|
|
44046
|
+
const noteKey = (ref) => {
|
|
44047
|
+
const kind = localName(ref.name) === "footnoteReference" ? "footnote" : "endnote";
|
|
44048
|
+
const id = ref.attrs["w:id"];
|
|
44049
|
+
return original ? `${ctx.noteIds[kind].get(id) ?? `original ${id}`}` : id;
|
|
44050
|
+
};
|
|
44051
|
+
return { whitespace: ctx.whitespace, noteKey };
|
|
44052
|
+
}
|
|
43459
44053
|
function concatMatches(oEls, rEls, ctx) {
|
|
43460
44054
|
const key = (els) => {
|
|
43461
44055
|
const parts = [];
|
|
43462
44056
|
for (const el26 of els) {
|
|
43463
44057
|
if (localName(el26.name) !== "p") return null;
|
|
43464
|
-
|
|
44058
|
+
if (child(child(el26, "pPr"), "sectPr")) return null;
|
|
44059
|
+
const content = readParagraph(el26, readOptions(ctx, oEls.includes(el26)));
|
|
43465
44060
|
if (!content.inlineDiffable) return null;
|
|
43466
|
-
for (const token of content.tokens)
|
|
44061
|
+
for (const token of content.tokens) {
|
|
44062
|
+
const props = token.pieces ? token.pieces.map((p) => `${p.text.length}:${runPropsKey(p.run)}`).join("|") : runPropsKey(token.run);
|
|
44063
|
+
parts.push(token.key + "" + props + "" + token.path.map((c) => serializeXml({ ...c, children: c.children.filter((k) => /Pr$/.test(localName(k.name))) })).join(">"));
|
|
44064
|
+
}
|
|
43467
44065
|
}
|
|
43468
44066
|
return parts.join("");
|
|
43469
44067
|
};
|
|
@@ -43475,14 +44073,24 @@ function emitMatched(oEl, rEl, ctx) {
|
|
|
43475
44073
|
const ln = localName(oEl.name);
|
|
43476
44074
|
if (ln === "p") comparePair(oEl, rEl, ctx);
|
|
43477
44075
|
else if (ln === "tbl") compareTable(oEl, rEl, ctx);
|
|
44076
|
+
else if (ln === "sdt") {
|
|
44077
|
+
const o = child(oEl, "sdtContent");
|
|
44078
|
+
const r = child(rEl, "sdtContent");
|
|
44079
|
+
if (o && r) compareContainer(o, r, ctx);
|
|
44080
|
+
} else if (ln === "customXml") compareContainer(oEl, rEl, ctx);
|
|
43478
44081
|
return { el: rEl, cls: "kept" };
|
|
43479
44082
|
}
|
|
43480
44083
|
var REL_ATTRS = ["id", "embed", "link", "pict", "dm", "lo", "qs", "cs"];
|
|
43481
44084
|
function adoptFromOriginal(root, ctx) {
|
|
43482
|
-
const mine = ctx.doc.
|
|
43483
|
-
const theirs = ctx.original.
|
|
44085
|
+
const mine = ctx.doc.relationshipsOf(ctx.scope.into);
|
|
44086
|
+
const theirs = ctx.original.relationshipsOf(ctx.scope.from);
|
|
43484
44087
|
const remapped = /* @__PURE__ */ new Map();
|
|
43485
44088
|
const walk2 = (el26) => {
|
|
44089
|
+
const ln = localName(el26.name);
|
|
44090
|
+
if (ln === "footnoteReference" || ln === "endnoteReference") {
|
|
44091
|
+
const to = ctx.noteIds[ln === "footnoteReference" ? "footnote" : "endnote"].get(el26.attrs["w:id"]);
|
|
44092
|
+
if (to !== void 0) el26.attrs["w:id"] = to;
|
|
44093
|
+
}
|
|
43486
44094
|
for (const key of Object.keys(el26.attrs)) {
|
|
43487
44095
|
if (!key.startsWith("r:") || !REL_ATTRS.includes(localName(key))) continue;
|
|
43488
44096
|
const id = el26.attrs[key];
|
|
@@ -43492,7 +44100,7 @@ function adoptFromOriginal(root, ctx) {
|
|
|
43492
44100
|
if (here && here.target === source.target && here.type === source.type) continue;
|
|
43493
44101
|
let replacement = remapped.get(id);
|
|
43494
44102
|
if (replacement === void 0) {
|
|
43495
|
-
replacement = source.external ? ctx.doc.addHyperlinkRel(source.target) :
|
|
44103
|
+
replacement = source.external ? ctx.doc.addHyperlinkRel(source.target, ctx.scope.into) : ctx.doc.adoptRelFrom(ctx.original, id, ctx.scope);
|
|
43496
44104
|
if (replacement === null) {
|
|
43497
44105
|
ctx.note({
|
|
43498
44106
|
code: "unresolved-relationship",
|
|
@@ -43511,32 +44119,31 @@ function adoptFromOriginal(root, ctx) {
|
|
|
43511
44119
|
function emitDeleted(el26, ctx) {
|
|
43512
44120
|
const clone = cloneXml(el26);
|
|
43513
44121
|
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
|
-
}
|
|
44122
|
+
markWholeBlock(clone, "del", ctx);
|
|
43525
44123
|
return { el: clone, cls: "deleted" };
|
|
43526
44124
|
}
|
|
43527
44125
|
function emitInserted(el26, ctx) {
|
|
44126
|
+
markWholeBlock(el26, "ins", ctx);
|
|
44127
|
+
return { el: el26, cls: "inserted" };
|
|
44128
|
+
}
|
|
44129
|
+
function markWholeBlock(el26, kind, ctx) {
|
|
43528
44130
|
const ln = localName(el26.name);
|
|
43529
44131
|
if (ln === "p") {
|
|
43530
|
-
wrapContainerContent(el26,
|
|
44132
|
+
wrapContainerContent(el26, kind, ctx);
|
|
43531
44133
|
} else if (ln === "tbl") {
|
|
43532
44134
|
for (const row of el26.children.filter((c) => localName(c.name) === "tr")) {
|
|
43533
|
-
markRowRevision(row,
|
|
43534
|
-
wrapRowContent(row,
|
|
44135
|
+
markRowRevision(row, kind, ctx.meta);
|
|
44136
|
+
wrapRowContent(row, kind, ctx);
|
|
44137
|
+
}
|
|
44138
|
+
} else if (ln === "sdt" || ln === "customXml") {
|
|
44139
|
+
const inner = ln === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44140
|
+
for (const block of inner?.children ?? []) {
|
|
44141
|
+
markWholeBlock(block, kind, ctx);
|
|
44142
|
+
if (isParagraph(block)) markParagraphGlyph(block, kind, ctx.meta);
|
|
43535
44143
|
}
|
|
43536
|
-
} else if (!isPassenger(ln)) {
|
|
43537
|
-
ctx.note({ code: "untracked-block", detail: `a <${el26.name}> block was added and is not expressible as a revision` });
|
|
44144
|
+
} else if (!isPassenger(ln) && ln !== "sdtPr" && ln !== "sdtEndPr" && ln !== "customXmlPr") {
|
|
44145
|
+
ctx.note({ code: "untracked-block", detail: `a <${el26.name}> block was ${kind === "ins" ? "added" : "removed"} and is not expressible as a revision` });
|
|
43538
44146
|
}
|
|
43539
|
-
return { el: el26, cls: "inserted" };
|
|
43540
44147
|
}
|
|
43541
44148
|
function wrapRowContent(row, kind, ctx) {
|
|
43542
44149
|
for (const cell of row.children.filter((c) => localName(c.name) === "tc")) {
|
|
@@ -43546,7 +44153,8 @@ function wrapRowContent(row, kind, ctx) {
|
|
|
43546
44153
|
}
|
|
43547
44154
|
}
|
|
43548
44155
|
function wrapContainerContent(container, kind, ctx) {
|
|
43549
|
-
const NESTED = ["hyperlink", "smartTag", "dir", "bdo"];
|
|
44156
|
+
const NESTED = ["hyperlink", "smartTag", "dir", "bdo", "fldSimple"];
|
|
44157
|
+
const WRAPPABLE = ["r", "sdt", "customXml"];
|
|
43550
44158
|
const out = [];
|
|
43551
44159
|
let group = [];
|
|
43552
44160
|
const flush = () => {
|
|
@@ -43557,7 +44165,7 @@ function wrapContainerContent(container, kind, ctx) {
|
|
|
43557
44165
|
};
|
|
43558
44166
|
for (const c of container.children) {
|
|
43559
44167
|
const ln = localName(c.name);
|
|
43560
|
-
if (ln
|
|
44168
|
+
if (WRAPPABLE.includes(ln)) {
|
|
43561
44169
|
group.push(c);
|
|
43562
44170
|
continue;
|
|
43563
44171
|
}
|
|
@@ -43575,10 +44183,10 @@ function wrapContainerContent(container, kind, ctx) {
|
|
|
43575
44183
|
container.children = out;
|
|
43576
44184
|
}
|
|
43577
44185
|
function comparePair(oEl, rEl, ctx) {
|
|
43578
|
-
if (
|
|
44186
|
+
if (sameXml(oEl, rEl)) return;
|
|
43579
44187
|
if (ctx.formatting) alignParagraphProps(rEl, child(rEl, "pPr"), child(oEl, "pPr"), ctx);
|
|
43580
|
-
const oc = readParagraph(oEl,
|
|
43581
|
-
const rc = readParagraph(rEl,
|
|
44188
|
+
const oc = readParagraph(oEl, readOptions(ctx, true));
|
|
44189
|
+
const rc = readParagraph(rEl, readOptions(ctx, false));
|
|
43582
44190
|
if (!oc.inlineDiffable || !rc.inlineDiffable) {
|
|
43583
44191
|
if (oc.text !== rc.text || serializeXml(stripProps(oEl)) !== serializeXml(stripProps(rEl))) {
|
|
43584
44192
|
const deleted = cloneXml(oEl);
|
|
@@ -43593,70 +44201,180 @@ function comparePair(oEl, rEl, ctx) {
|
|
|
43593
44201
|
];
|
|
43594
44202
|
ctx.note({
|
|
43595
44203
|
code: "coarse-paragraph",
|
|
43596
|
-
detail: "a paragraph holding
|
|
44204
|
+
detail: "a paragraph holding inline math or other content a word diff cannot rebuild changed; struck and reinserted whole"
|
|
43597
44205
|
});
|
|
43598
44206
|
}
|
|
43599
44207
|
return;
|
|
43600
44208
|
}
|
|
43601
44209
|
const segments = wordSegments(oc.tokens, rc.tokens);
|
|
43602
44210
|
const changed = segments.some((s) => !s.equal);
|
|
43603
|
-
|
|
43604
|
-
|
|
43605
|
-
return;
|
|
43606
|
-
}
|
|
44211
|
+
const flat = oc.tokens.every((t) => t.path.length === 0) && rc.tokens.every((t) => t.path.length === 0);
|
|
44212
|
+
if (!changed && (!ctx.formatting || flat && alignRunPropsInPlace(oEl, rEl, ctx))) return;
|
|
43607
44213
|
const pPr = child(rEl, "pPr");
|
|
43608
|
-
const
|
|
44214
|
+
const items = [];
|
|
44215
|
+
const counterpart = /* @__PURE__ */ new Map();
|
|
43609
44216
|
for (const seg of segments) {
|
|
43610
44217
|
if (seg.equal) {
|
|
43611
|
-
|
|
44218
|
+
for (const it of emitEqualSpan(oc.tokens, rc.tokens, seg, ctx, counterpart)) items.push({ ...it, kind: null });
|
|
43612
44219
|
continue;
|
|
43613
44220
|
}
|
|
43614
44221
|
if (seg.aEnd > seg.aStart) {
|
|
43615
|
-
const
|
|
43616
|
-
|
|
43617
|
-
|
|
44222
|
+
for (const it of sliceItems(oc.tokens, seg.aStart, seg.aEnd, true)) {
|
|
44223
|
+
adoptFromOriginal(it.el, ctx);
|
|
44224
|
+
markDeletedText(it.el);
|
|
44225
|
+
items.push({ ...it, kind: "del" });
|
|
44226
|
+
}
|
|
43618
44227
|
}
|
|
43619
44228
|
if (seg.bEnd > seg.bStart) {
|
|
43620
|
-
|
|
44229
|
+
for (const it of sliceItems(rc.tokens, seg.bStart, seg.bEnd, false)) items.push({ ...it, kind: "ins" });
|
|
43621
44230
|
}
|
|
43622
44231
|
}
|
|
43623
|
-
|
|
43624
|
-
|
|
44232
|
+
for (const it of items) if (it.kind === "del") it.path = it.path.map((c) => counterpart.get(c) ?? c);
|
|
44233
|
+
for (const el26 of rc.tail) items.push({ el: cloneXml(el26), path: [], kind: null });
|
|
44234
|
+
const fromOriginal = new Set(oc.tokens.flatMap((t) => t.path));
|
|
44235
|
+
rEl.children = [...pPr ? [pPr] : [], ...nestItems(items, 0, rEl, ctx, fromOriginal)];
|
|
44236
|
+
}
|
|
44237
|
+
var WRAPPABLE_CONTAINERS = /* @__PURE__ */ new Set(["sdt", "customXml"]);
|
|
44238
|
+
function compareTextBoxes(oAtom, rAtom, ctx) {
|
|
44239
|
+
const theirs = textBoxesIn(oAtom);
|
|
44240
|
+
const mine = textBoxesIn(rAtom);
|
|
44241
|
+
for (let i = 0; i < Math.min(theirs.length, mine.length); i++) {
|
|
44242
|
+
if (sameXml(theirs[i], mine[i])) continue;
|
|
44243
|
+
const entries = alignBlocks(blockList(theirs[i]), blockList(mine[i]), ctx);
|
|
44244
|
+
applyParagraphMarks(entries, ctx);
|
|
44245
|
+
mine[i].children = entries.map((e) => e.el);
|
|
44246
|
+
}
|
|
44247
|
+
}
|
|
44248
|
+
function nestItems(items, depth, rEl, ctx, fromOriginal) {
|
|
44249
|
+
const out = [];
|
|
44250
|
+
let i = 0;
|
|
44251
|
+
while (i < items.length) {
|
|
44252
|
+
const c = items[i].path[depth];
|
|
44253
|
+
let j = i + 1;
|
|
44254
|
+
if (c) {
|
|
44255
|
+
while (j < items.length && items[j].path[depth] === c) j++;
|
|
44256
|
+
const group = items.slice(i, j);
|
|
44257
|
+
const kind = group[0].kind;
|
|
44258
|
+
if (kind && WRAPPABLE_CONTAINERS.has(localName(c.name)) && group.every((it) => it.kind === kind)) {
|
|
44259
|
+
const plain = group.map((it) => ({ ...it, kind: null }));
|
|
44260
|
+
out.push(revisionWrapper(rEl, kind, [containerShell(c, nestItems(plain, depth + 1, rEl, ctx, fromOriginal), fromOriginal.has(c), ctx)], ctx.meta));
|
|
44261
|
+
} else {
|
|
44262
|
+
out.push(containerShell(c, nestItems(group, depth + 1, rEl, ctx, fromOriginal), fromOriginal.has(c), ctx));
|
|
44263
|
+
}
|
|
44264
|
+
} else {
|
|
44265
|
+
const kind = items[i].kind;
|
|
44266
|
+
while (j < items.length && !items[j].path[depth] && items[j].kind === kind) j++;
|
|
44267
|
+
const els = items.slice(i, j).map((it) => it.el);
|
|
44268
|
+
if (kind) out.push(revisionWrapper(rEl, kind, els, ctx.meta));
|
|
44269
|
+
else out.push(...els);
|
|
44270
|
+
}
|
|
44271
|
+
i = j;
|
|
44272
|
+
}
|
|
44273
|
+
return out;
|
|
44274
|
+
}
|
|
44275
|
+
function containerShell(c, content, fromOriginal, ctx) {
|
|
44276
|
+
const isSdt = localName(c.name) === "sdt";
|
|
44277
|
+
const inner = isSdt ? c.children.find((k) => localName(k.name) === "sdtContent") : void 0;
|
|
44278
|
+
const shell = {
|
|
44279
|
+
name: c.name,
|
|
44280
|
+
attrs: { ...c.attrs },
|
|
44281
|
+
// Property children only (sdtPr, smartTagPr, ...): the content is new.
|
|
44282
|
+
children: c.children.filter((k) => /Pr$/.test(localName(k.name))).map(cloneXml),
|
|
44283
|
+
text: ""
|
|
44284
|
+
};
|
|
44285
|
+
if (fromOriginal) adoptFromOriginal(shell, ctx);
|
|
44286
|
+
if (isSdt) shell.children.push({ name: inner?.name ?? c.name.replace(/sdt$/, "sdtContent"), attrs: { ...inner?.attrs ?? {} }, children: content, text: "" });
|
|
44287
|
+
else shell.children.push(...content);
|
|
44288
|
+
return shell;
|
|
43625
44289
|
}
|
|
43626
44290
|
function stripProps(el26) {
|
|
43627
44291
|
return { ...el26, children: el26.children.filter((c) => localName(c.name) !== "pPr") };
|
|
43628
44292
|
}
|
|
43629
|
-
function emitEqualSpan(oTokens, rTokens, seg, ctx) {
|
|
44293
|
+
function emitEqualSpan(oTokens, rTokens, seg, ctx, counterpart) {
|
|
44294
|
+
const units = [];
|
|
44295
|
+
const pieceAt = (pieces, offset) => {
|
|
44296
|
+
let base = 0;
|
|
44297
|
+
for (const p of pieces) {
|
|
44298
|
+
if (offset < base + p.text.length) return p;
|
|
44299
|
+
base += p.text.length;
|
|
44300
|
+
}
|
|
44301
|
+
return pieces[pieces.length - 1];
|
|
44302
|
+
};
|
|
44303
|
+
for (let k = 0; k < seg.bEnd - seg.bStart; k++) {
|
|
44304
|
+
const o = oTokens[seg.aStart + k];
|
|
44305
|
+
const r = rTokens[seg.bStart + k];
|
|
44306
|
+
for (let d = 0; d < Math.min(o.path.length, r.path.length); d++) {
|
|
44307
|
+
if (localName(o.path[d].name) === localName(r.path[d].name)) counterpart.set(o.path[d], r.path[d]);
|
|
44308
|
+
}
|
|
44309
|
+
if (r.atom || !r.pieces || !o.pieces || o.text !== r.text) {
|
|
44310
|
+
if (r.atom) units.push({ token: r, run: r.run, text: "", atom: r.atom, oAtom: o.atom, was: o.run });
|
|
44311
|
+
else for (const p of r.pieces ?? []) units.push({ token: r, run: p.run, t: p.t, text: p.text, was: o.run });
|
|
44312
|
+
continue;
|
|
44313
|
+
}
|
|
44314
|
+
const cuts = /* @__PURE__ */ new Set([0, r.text.length]);
|
|
44315
|
+
for (const pieces of [o.pieces, r.pieces]) {
|
|
44316
|
+
let base = 0;
|
|
44317
|
+
for (const p of pieces) cuts.add(base += p.text.length);
|
|
44318
|
+
}
|
|
44319
|
+
const sorted = [...cuts].sort((x, y) => x - y);
|
|
44320
|
+
for (let c = 0; c + 1 < sorted.length; c++) {
|
|
44321
|
+
const from = sorted[c];
|
|
44322
|
+
const to = sorted[c + 1];
|
|
44323
|
+
if (from === to) continue;
|
|
44324
|
+
const rp = pieceAt(r.pieces, from);
|
|
44325
|
+
units.push({ token: r, run: rp.run, t: rp.t, text: r.text.slice(from, to), was: pieceAt(o.pieces, from).run });
|
|
44326
|
+
}
|
|
44327
|
+
}
|
|
43630
44328
|
const out = [];
|
|
43631
|
-
let
|
|
43632
|
-
|
|
43633
|
-
|
|
43634
|
-
|
|
43635
|
-
|
|
43636
|
-
|
|
43637
|
-
|
|
44329
|
+
let open = null;
|
|
44330
|
+
let text = null;
|
|
44331
|
+
const close = () => {
|
|
44332
|
+
if (open && ctx.formatting && open.was !== runPropsKey(open.run)) swapRunProps(open.el, open.prev, ctx);
|
|
44333
|
+
open = null;
|
|
44334
|
+
text = null;
|
|
44335
|
+
};
|
|
44336
|
+
let lastToken = null;
|
|
44337
|
+
for (const u of units) {
|
|
44338
|
+
if (u.token !== lastToken && u.token.lead) {
|
|
44339
|
+
close();
|
|
44340
|
+
out.push(...u.token.lead.map((el26) => ({ el: cloneXml(el26), path: u.token.path })));
|
|
44341
|
+
}
|
|
44342
|
+
lastToken = u.token;
|
|
44343
|
+
const was = runPropsKey(u.was);
|
|
44344
|
+
if (!open || open.run !== u.run || open.was !== was) {
|
|
44345
|
+
close();
|
|
44346
|
+
const rPr = u.run.children.find((c) => localName(c.name) === "rPr");
|
|
44347
|
+
const el26 = { name: u.run.name, attrs: { ...u.run.attrs }, children: rPr ? [cloneXml(rPr)] : [], text: "" };
|
|
44348
|
+
out.push({ el: el26, path: u.token.path });
|
|
44349
|
+
open = { run: u.run, was, el: el26, prev: child(u.was, "rPr") };
|
|
44350
|
+
}
|
|
44351
|
+
if (u.atom) {
|
|
44352
|
+
const atom = cloneXml(u.atom);
|
|
44353
|
+
if (u.oAtom && !sameXml(u.oAtom, u.atom)) compareTextBoxes(u.oAtom, atom, ctx);
|
|
44354
|
+
open.el.children.push(atom);
|
|
44355
|
+
text = null;
|
|
44356
|
+
continue;
|
|
43638
44357
|
}
|
|
43639
|
-
|
|
43640
|
-
|
|
43641
|
-
|
|
43642
|
-
if (localName(el26.name) === "r") {
|
|
43643
|
-
swapRunProps(el26, child(oTokens[seg.aStart + (at - seg.bStart)].run, "rPr"), ctx);
|
|
43644
|
-
}
|
|
43645
|
-
}
|
|
44358
|
+
if (text && text.t === u.t) {
|
|
44359
|
+
text.el.text += u.text;
|
|
44360
|
+
continue;
|
|
43646
44361
|
}
|
|
43647
|
-
|
|
43648
|
-
|
|
44362
|
+
const te = { name: u.t.name, attrs: { ...u.t.attrs, "xml:space": "preserve" }, children: [], text: u.text };
|
|
44363
|
+
open.el.children.push(te);
|
|
44364
|
+
text = { t: u.t, el: te };
|
|
43649
44365
|
}
|
|
44366
|
+
close();
|
|
43650
44367
|
return out;
|
|
43651
44368
|
}
|
|
43652
44369
|
function alignRunPropsInPlace(oEl, rEl, ctx) {
|
|
43653
44370
|
const oRuns = oEl.children.filter((c) => localName(c.name) === "r");
|
|
43654
44371
|
const rRuns = rEl.children.filter((c) => localName(c.name) === "r");
|
|
43655
|
-
if (oRuns.length !== rRuns.length) return;
|
|
44372
|
+
if (oRuns.length !== rRuns.length) return false;
|
|
43656
44373
|
for (let i = 0; i < rRuns.length; i++) {
|
|
43657
|
-
if (paragraphText3(oRuns[i]) !== paragraphText3(rRuns[i])) return;
|
|
44374
|
+
if (paragraphText3(oRuns[i]) !== paragraphText3(rRuns[i])) return false;
|
|
43658
44375
|
}
|
|
43659
44376
|
for (let i = 0; i < rRuns.length; i++) swapRunProps(rRuns[i], child(oRuns[i], "rPr"), ctx);
|
|
44377
|
+
return true;
|
|
43660
44378
|
}
|
|
43661
44379
|
function wordSegments(a, b) {
|
|
43662
44380
|
const raw = [];
|
|
@@ -43685,8 +44403,8 @@ function wordSegments(a, b) {
|
|
|
43685
44403
|
for (let i = 1; i + 1 < segments.length; i++) {
|
|
43686
44404
|
const seg = segments[i];
|
|
43687
44405
|
if (!seg.equal) continue;
|
|
43688
|
-
const len = seg.
|
|
43689
|
-
const beside = Math.min(changeSize(segments[i - 1]), changeSize(segments[i + 1]));
|
|
44406
|
+
const len = words(a, seg.aStart, seg.aEnd);
|
|
44407
|
+
const beside = Math.min(changeSize(segments[i - 1], a, b), changeSize(segments[i + 1], a, b));
|
|
43690
44408
|
if (len < COMPARE_TUNING.coalesceWindow || len * COMPARE_TUNING.coalesceRatio < beside) {
|
|
43691
44409
|
seg.equal = false;
|
|
43692
44410
|
absorbed = true;
|
|
@@ -43710,17 +44428,26 @@ function mergeChanges(segments) {
|
|
|
43710
44428
|
}
|
|
43711
44429
|
return out;
|
|
43712
44430
|
}
|
|
43713
|
-
function changeSize(seg) {
|
|
43714
|
-
return Math.max(
|
|
44431
|
+
function changeSize(seg, a, b) {
|
|
44432
|
+
return Math.max(words(a, seg.aStart, seg.aEnd), words(b, seg.bStart, seg.bEnd));
|
|
44433
|
+
}
|
|
44434
|
+
function words(tokens, from, to) {
|
|
44435
|
+
let n = 0;
|
|
44436
|
+
for (let i = from; i < to; i++) if (tokens[i].atom || !/^\s+$/.test(tokens[i].text)) n++;
|
|
44437
|
+
return n;
|
|
43715
44438
|
}
|
|
43716
44439
|
function compareTable(oTbl, rTbl, ctx) {
|
|
43717
|
-
if (
|
|
44440
|
+
if (sameXml(oTbl, rTbl)) return;
|
|
43718
44441
|
if (ctx.formatting) {
|
|
43719
44442
|
swapProps(rTbl, "tblPr", recordTableFormatChange, child(oTbl, "tblPr"), ctx);
|
|
43720
44443
|
alignTableGrid(oTbl, rTbl, ctx);
|
|
43721
44444
|
}
|
|
43722
|
-
|
|
43723
|
-
|
|
44445
|
+
compareRows(oTbl, rTbl, ctx);
|
|
44446
|
+
}
|
|
44447
|
+
var ROW_LEVEL = ["tr", "sdt", "customXml"];
|
|
44448
|
+
function compareRows(oParent, rParent, ctx) {
|
|
44449
|
+
const oRows = oParent.children.filter((c) => ROW_LEVEL.includes(localName(c.name)));
|
|
44450
|
+
const rRows = rParent.children.filter((c) => ROW_LEVEL.includes(localName(c.name)));
|
|
43724
44451
|
const anchors = histogramMatch(oRows.map(rowFingerprint), rRows.map(rowFingerprint));
|
|
43725
44452
|
const rows = [];
|
|
43726
44453
|
let oAt = 0;
|
|
@@ -43745,24 +44472,41 @@ function compareTable(oTbl, rTbl, ctx) {
|
|
|
43745
44472
|
oAt = anchor.a + 1;
|
|
43746
44473
|
rAt = anchor.b + 1;
|
|
43747
44474
|
}
|
|
43748
|
-
|
|
44475
|
+
rParent.children = [...rParent.children.filter((c) => !ROW_LEVEL.includes(localName(c.name))), ...rows];
|
|
43749
44476
|
}
|
|
43750
44477
|
function rowFingerprint(tr) {
|
|
44478
|
+
if (localName(tr.name) !== "tr") return `X\0${serializeXml(tr)}`;
|
|
43751
44479
|
const cells = tr.children.filter((c) => localName(c.name) === "tc");
|
|
43752
44480
|
return `${cells.length}\0${normalizeText(paragraphText3(tr))}`;
|
|
43753
44481
|
}
|
|
43754
|
-
function
|
|
43755
|
-
|
|
43756
|
-
|
|
43757
|
-
|
|
44482
|
+
function rowsIn(el26) {
|
|
44483
|
+
if (localName(el26.name) === "tr") return [el26];
|
|
44484
|
+
const inner = localName(el26.name) === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44485
|
+
return (inner?.children ?? []).filter((c) => ROW_LEVEL.includes(localName(c.name))).flatMap(rowsIn);
|
|
44486
|
+
}
|
|
44487
|
+
function deletedRow(el26, ctx) {
|
|
44488
|
+
const clone = cloneXml(el26);
|
|
44489
|
+
for (const tr of rowsIn(clone)) {
|
|
44490
|
+
markRowRevision(tr, "del", ctx.meta);
|
|
44491
|
+
wrapRowContent(tr, "del", ctx);
|
|
44492
|
+
}
|
|
43758
44493
|
return clone;
|
|
43759
44494
|
}
|
|
43760
|
-
function insertedRow(
|
|
43761
|
-
|
|
43762
|
-
|
|
43763
|
-
|
|
44495
|
+
function insertedRow(el26, ctx) {
|
|
44496
|
+
for (const tr of rowsIn(el26)) {
|
|
44497
|
+
markRowRevision(tr, "ins", ctx.meta);
|
|
44498
|
+
wrapRowContent(tr, "ins", ctx);
|
|
44499
|
+
}
|
|
44500
|
+
return el26;
|
|
43764
44501
|
}
|
|
43765
44502
|
function compareRow(oTr, rTr, ctx) {
|
|
44503
|
+
if (localName(oTr.name) !== "tr") {
|
|
44504
|
+
const inner = (el26) => localName(el26.name) === "sdt" ? child(el26, "sdtContent") : el26;
|
|
44505
|
+
const o = inner(oTr);
|
|
44506
|
+
const r = inner(rTr);
|
|
44507
|
+
if (o && r) compareRows(o, r, ctx);
|
|
44508
|
+
return [rTr];
|
|
44509
|
+
}
|
|
43766
44510
|
const oCells = oTr.children.filter((c) => localName(c.name) === "tc");
|
|
43767
44511
|
const rCells = rTr.children.filter((c) => localName(c.name) === "tc");
|
|
43768
44512
|
if (oCells.length !== rCells.length) {
|
|
@@ -43785,7 +44529,7 @@ function compareRow(oTr, rTr, ctx) {
|
|
|
43785
44529
|
function alignTableGrid(oTbl, rTbl, ctx) {
|
|
43786
44530
|
const oGrid = child(oTbl, "tblGrid");
|
|
43787
44531
|
const rGrid = child(rTbl, "tblGrid");
|
|
43788
|
-
if (!oGrid || !rGrid ||
|
|
44532
|
+
if (!oGrid || !rGrid || sameXml(oGrid, rGrid)) return;
|
|
43789
44533
|
const live = rGrid.children.map(cloneXml);
|
|
43790
44534
|
rGrid.children = oGrid.children.map(cloneXml);
|
|
43791
44535
|
recordTableGridChange(rTbl, ctx.meta);
|
|
@@ -43872,7 +44616,22 @@ function applyParagraphMarks(entries, ctx) {
|
|
|
43872
44616
|
}
|
|
43873
44617
|
}
|
|
43874
44618
|
function markRun(entries, start, end, ctx) {
|
|
43875
|
-
|
|
44619
|
+
let at = start;
|
|
44620
|
+
while (at <= end) {
|
|
44621
|
+
if (!isParagraph(entries[at].el)) {
|
|
44622
|
+
at++;
|
|
44623
|
+
continue;
|
|
44624
|
+
}
|
|
44625
|
+
let last = at;
|
|
44626
|
+
while (last + 1 <= end && isParagraph(entries[last + 1].el)) last++;
|
|
44627
|
+
markParagraphRun(entries, at, last, ctx);
|
|
44628
|
+
at = last + 1;
|
|
44629
|
+
}
|
|
44630
|
+
}
|
|
44631
|
+
function markParagraphRun(entries, start, end, ctx) {
|
|
44632
|
+
const endsSection = (el26) => isParagraph(el26) && !!child(child(el26, "pPr"), "sectPr");
|
|
44633
|
+
const before = start > 0 ? entries[start - 1].el : void 0;
|
|
44634
|
+
const shifted = !!before && isParagraph(before) && !endsSection(before) && !entries.slice(start, end + 1).some((e) => endsSection(e.el));
|
|
43876
44635
|
if (shifted) {
|
|
43877
44636
|
for (let i = start; i <= end; i++) {
|
|
43878
44637
|
if (!isParagraph(entries[i].el) || !isParagraph(entries[i - 1].el)) continue;
|
|
@@ -45359,9 +46118,10 @@ async function renderForPrint(bytes, container) {
|
|
|
45359
46118
|
const doc = DocxDocument.load(bytes);
|
|
45360
46119
|
const layout = layoutDocument(doc);
|
|
45361
46120
|
renderToDom(doc, layout, container, { zoom: 1, pageGap: 0, pageShadow: false, comments: false });
|
|
46121
|
+
pruneBlankSpans(container);
|
|
45362
46122
|
const first = layout.pages[0];
|
|
45363
46123
|
const style = document.createElement("style");
|
|
45364
|
-
style.textContent = printStylesheet(first?.width ?? 816, first?.height ?? 1056);
|
|
46124
|
+
style.textContent = printStylesheet(first?.width ?? 816, first?.height ?? 1056, container);
|
|
45365
46125
|
document.head.append(style);
|
|
45366
46126
|
await paintsSettled();
|
|
45367
46127
|
await Promise.all(Array.from(container.querySelectorAll("img"), (img) => img.decode().catch(() => void 0)));
|
|
@@ -55815,4 +56575,4 @@ export {
|
|
|
55815
56575
|
cfb/cfb.js:
|
|
55816
56576
|
(*! crc32.js (C) 2014-present SheetJS -- http://sheetjs.com *)
|
|
55817
56577
|
*/
|
|
55818
|
-
//# sourceMappingURL=chunk-
|
|
56578
|
+
//# sourceMappingURL=chunk-WPQ6GPG4.js.map
|