@arbidocs/coedit 0.5.109 → 0.5.111
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/commands.d.cts +3 -65
- package/dist/commands.d.ts +3 -65
- package/dist/ooxml.cjs +120 -0
- package/dist/ooxml.cjs.map +1 -0
- package/dist/ooxml.d.cts +65 -0
- package/dist/ooxml.d.ts +65 -0
- package/dist/ooxml.js +107 -0
- package/dist/ooxml.js.map +1 -0
- package/package.json +6 -1
package/dist/commands.d.cts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { g as DocParagraph, l as ListItemInfo, k as LevelOf, R as ReferencesPort, a as CrossReferenceTarget, h as DocPoint, O as OoxmlNodeLike, b as DefinitionsPort, j as DocumentNumbering } from './port-BbN5OZHx.cjs';
|
|
2
2
|
export { B as BROKEN_CROSS_REFERENCE_STYLE, C as CROSS_REFERENCE_STYLE, D as DEFINED_TERM_STYLE, c as DocField, d as DocLink, e as DocMark, f as DocNote, i as DocRevision, F as FIELD_ATOM, L as LevelInfo, N as NumberingSource, W as WATCHED_STYLES, m as documentNumbering, n as engineLength, o as engineOffset, p as filterTargets, q as listTargets, r as paragraphNumber, s as readParagraphs, t as styleNames } from './port-BbN5OZHx.cjs';
|
|
3
3
|
import { OoxmlProperty, TabStopWrite, TreeDocOp } from '@docx-editor.dev/core/store';
|
|
4
|
+
import { ParsedDefinition, DefinitionText } from './ooxml.cjs';
|
|
5
|
+
export { DEFINITION_BOOKMARK_PREFIX, W_NAMESPACES, compareTerms, definitionBookmark, definitionIdOfBookmark, documentXml, docxPackage, esc, hyperlinkRelationship, parseDefinition, stylesXml, termKey } from './ooxml.cjs';
|
|
4
6
|
import { R as Revision } from './track-eYC94jTp.cjs';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -232,70 +234,6 @@ declare function fieldNodeIds(paragraph: OoxmlNodeLike): string[];
|
|
|
232
234
|
*/
|
|
233
235
|
declare function undoOps(root: OoxmlNodeLike, record: UndoRecord): TreeDocOp[] | null;
|
|
234
236
|
|
|
235
|
-
/**
|
|
236
|
-
* The minimal WordprocessingML a command writes: escaped text, a `word/document.xml` and
|
|
237
|
-
* `word/styles.xml` around it, and the `.docx` package that carries them (a paste fragment).
|
|
238
|
-
*/
|
|
239
|
-
declare const W_NAMESPACES = "xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"";
|
|
240
|
-
/** Text as XML character data or an attribute value. */
|
|
241
|
-
declare const esc: (text: string) => string;
|
|
242
|
-
/** A `word/document.xml` around body content (paragraphs, tables, then the section). */
|
|
243
|
-
declare const documentXml: (body: string) => string;
|
|
244
|
-
/** `word/styles.xml` holding the given style definitions. */
|
|
245
|
-
declare const stylesXml: (defaults: string, styles: string) => string;
|
|
246
|
-
/** A relationship to an external hyperlink target, for `w:hyperlink r:id`. */
|
|
247
|
-
declare const hyperlinkRelationship: (id: string, url: string) => string;
|
|
248
|
-
/** The minimal `.docx` package: a document, its styles, and its title and time. */
|
|
249
|
-
declare function docxPackage(parts: {
|
|
250
|
-
document: string;
|
|
251
|
-
styles: string;
|
|
252
|
-
title: string;
|
|
253
|
-
/** ISO time; default now. */
|
|
254
|
-
at?: string;
|
|
255
|
-
/** Further `word/document.xml` relationships (e.g. {@link hyperlinkRelationship}). */
|
|
256
|
-
relationships?: string;
|
|
257
|
-
}): Uint8Array;
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Defined terms in text: a definition's term read from the start of its text (`"Shares" means …`,
|
|
261
|
-
* `**Business Day** shall mean …`, `Completion: the date …`), the order of a glossary, and the hidden
|
|
262
|
-
* Word bookmark a term links to. Shared by the editor's Defined Terms and ARBI's headless tools.
|
|
263
|
-
*/
|
|
264
|
-
/** A definition's text split into its term and the rest. */
|
|
265
|
-
interface ParsedDefinition {
|
|
266
|
-
/** The term as written, without quotes or emphasis (`Business Day`). */
|
|
267
|
-
term: string;
|
|
268
|
-
/** What follows the term, its connecting word included (`means a day …`, `: the date …`). */
|
|
269
|
-
rest: string;
|
|
270
|
-
/** Just the meaning, the connecting word dropped (`a day …`). */
|
|
271
|
-
meaning: string;
|
|
272
|
-
}
|
|
273
|
-
/** A definition as the commands insert it: the id of its fact, its term and the rest. */
|
|
274
|
-
interface DefinitionText extends Pick<ParsedDefinition, 'term' | 'rest'> {
|
|
275
|
-
id: string;
|
|
276
|
-
}
|
|
277
|
-
/** The matching key of a term: case and whitespace folded, quotes and emphasis dropped. */
|
|
278
|
-
declare function termKey(term: string): string;
|
|
279
|
-
/**
|
|
280
|
-
* Read a definition's term from the start of its text, or `null` when it does not start with one.
|
|
281
|
-
*
|
|
282
|
-
* Optional straight or curly quotes and bold markers around the term are dropped; the term is what
|
|
283
|
-
* comes before "means", "shall mean", "includes", "has the meaning", a colon, or a dash. A quoted
|
|
284
|
-
* term is taken whole, so `"Long-Stop Date" means …` keeps its hyphen.
|
|
285
|
-
*/
|
|
286
|
-
declare function parseDefinition(text: string): ParsedDefinition | null;
|
|
287
|
-
/** Alphabetical order of terms, as a reader sorts a glossary (case and accents folded). */
|
|
288
|
-
declare const compareTerms: (a: string, b: string) => number;
|
|
289
|
-
/** Prefix of every bookmark a defined term links to: hidden in Word (leading underscore). */
|
|
290
|
-
declare const DEFINITION_BOOKMARK_PREFIX = "_dt_";
|
|
291
|
-
/**
|
|
292
|
-
* The Word bookmark of a definition: `_dt_` and the fact id in valid bookmark characters
|
|
293
|
-
* (`fct-AbC12345` → `_dt_fct_AbC12345`), at most 40 characters as Word requires.
|
|
294
|
-
*/
|
|
295
|
-
declare function definitionBookmark(factId: string): string;
|
|
296
|
-
/** The fact id a definition bookmark names, or `null` for any other bookmark. */
|
|
297
|
-
declare function definitionIdOfBookmark(name: string): string | null;
|
|
298
|
-
|
|
299
237
|
/**
|
|
300
238
|
* Defined-term and cross-reference content as WordprocessingML, and the clipboard fragment (a small
|
|
301
239
|
* `.docx` package) that carries it into a document. The engine has no "insert bookmark" command, but
|
|
@@ -580,4 +518,4 @@ declare function documentReader(view: DocumentTrees): DocumentReader;
|
|
|
580
518
|
/** Read a document from its bytes (a collaboration room's `readRoom`). Throws on a bad package. */
|
|
581
519
|
declare function readDocument(bytes: Uint8Array): DocumentReader;
|
|
582
520
|
|
|
583
|
-
export { AGENT_UNDO_MAP, type BrokenReference, CHARACTER_STYLES, type CrossReference, type CrossReferenceMode, CrossReferenceTarget, DEFINITIONS_HEADING,
|
|
521
|
+
export { AGENT_UNDO_MAP, type BrokenReference, CHARACTER_STYLES, type CrossReference, type CrossReferenceMode, CrossReferenceTarget, DEFINITIONS_HEADING, DefinitionText, DefinitionsPort, type DefinitionsSection, DocParagraph, DocPoint, DocumentNumbering, type DocumentReader, type DocumentTrees, type InsertTermResult, LevelOf, ListItemInfo, OoxmlNodeLike, type ParagraphState, ParsedDefinition, REF_BOOKMARK_PREFIX, ReferencesPort, type ReferencesReader, type RefreshResult, type ReplacedSpan, SIGNATURE_FIELDS_NAMESPACE, SIGNATURE_FIELD_KINDS, SIGNATURE_FIELD_LABELS, SIGNATURE_FIELD_TAG_PREFIX, type SectionState, type SignatureFieldKind, type SignatureFieldPayload, type SignatureFieldSpec, type StaleReference, type TermChange, TermEverywhereError, type TermEverywhereResult, type TermSkip, type TermSkipReason, type TermSpec, type TermUse, type UndoRecord, addDefinition, applyTermEverywhere, brokenCrossReferences, crossReferenceXml, crossReferences, definitionOfAnchor, definitionParagraphText, definitionParagraphXml, definitionTooltip, definitionsById, documentReader, documentReferenceTooltip, documentStyleIds, ensureTargetBookmark, fieldNodeIds, findDefinitionsSection, findTermUses, fragmentPackage, headingParagraphXml, insertCrossReference, insertCrossReferenceField, insertDefinedTerm, insertDocumentReference, insertionIndex, isDefinitionsTitle, isSignerEmail, markCrossReferenceTarget, newRefBookmark, newSignatureFieldKey, paragraphIndex, paragraphRunsXml, paragraphState, parseSignatureFieldTag, readDocument, refFieldXml, refInstruction, refResult, refTooltip, refreshCrossReferences, replaceSpan, sectionStates, signatureFieldControls, signatureFieldLabel, signatureFieldTag, signatureFieldWrite, staleCrossReferences, stylesUsed, tableOf, tableState, termLinkXml, undoOps, withoutArticle };
|
package/dist/commands.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { g as DocParagraph, l as ListItemInfo, k as LevelOf, R as ReferencesPort, a as CrossReferenceTarget, h as DocPoint, O as OoxmlNodeLike, b as DefinitionsPort, j as DocumentNumbering } from './port-BbN5OZHx.js';
|
|
2
2
|
export { B as BROKEN_CROSS_REFERENCE_STYLE, C as CROSS_REFERENCE_STYLE, D as DEFINED_TERM_STYLE, c as DocField, d as DocLink, e as DocMark, f as DocNote, i as DocRevision, F as FIELD_ATOM, L as LevelInfo, N as NumberingSource, W as WATCHED_STYLES, m as documentNumbering, n as engineLength, o as engineOffset, p as filterTargets, q as listTargets, r as paragraphNumber, s as readParagraphs, t as styleNames } from './port-BbN5OZHx.js';
|
|
3
3
|
import { OoxmlProperty, TabStopWrite, TreeDocOp } from '@docx-editor.dev/core/store';
|
|
4
|
+
import { ParsedDefinition, DefinitionText } from './ooxml.js';
|
|
5
|
+
export { DEFINITION_BOOKMARK_PREFIX, W_NAMESPACES, compareTerms, definitionBookmark, definitionIdOfBookmark, documentXml, docxPackage, esc, hyperlinkRelationship, parseDefinition, stylesXml, termKey } from './ooxml.js';
|
|
4
6
|
import { R as Revision } from './track-eYC94jTp.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -232,70 +234,6 @@ declare function fieldNodeIds(paragraph: OoxmlNodeLike): string[];
|
|
|
232
234
|
*/
|
|
233
235
|
declare function undoOps(root: OoxmlNodeLike, record: UndoRecord): TreeDocOp[] | null;
|
|
234
236
|
|
|
235
|
-
/**
|
|
236
|
-
* The minimal WordprocessingML a command writes: escaped text, a `word/document.xml` and
|
|
237
|
-
* `word/styles.xml` around it, and the `.docx` package that carries them (a paste fragment).
|
|
238
|
-
*/
|
|
239
|
-
declare const W_NAMESPACES = "xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"";
|
|
240
|
-
/** Text as XML character data or an attribute value. */
|
|
241
|
-
declare const esc: (text: string) => string;
|
|
242
|
-
/** A `word/document.xml` around body content (paragraphs, tables, then the section). */
|
|
243
|
-
declare const documentXml: (body: string) => string;
|
|
244
|
-
/** `word/styles.xml` holding the given style definitions. */
|
|
245
|
-
declare const stylesXml: (defaults: string, styles: string) => string;
|
|
246
|
-
/** A relationship to an external hyperlink target, for `w:hyperlink r:id`. */
|
|
247
|
-
declare const hyperlinkRelationship: (id: string, url: string) => string;
|
|
248
|
-
/** The minimal `.docx` package: a document, its styles, and its title and time. */
|
|
249
|
-
declare function docxPackage(parts: {
|
|
250
|
-
document: string;
|
|
251
|
-
styles: string;
|
|
252
|
-
title: string;
|
|
253
|
-
/** ISO time; default now. */
|
|
254
|
-
at?: string;
|
|
255
|
-
/** Further `word/document.xml` relationships (e.g. {@link hyperlinkRelationship}). */
|
|
256
|
-
relationships?: string;
|
|
257
|
-
}): Uint8Array;
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Defined terms in text: a definition's term read from the start of its text (`"Shares" means …`,
|
|
261
|
-
* `**Business Day** shall mean …`, `Completion: the date …`), the order of a glossary, and the hidden
|
|
262
|
-
* Word bookmark a term links to. Shared by the editor's Defined Terms and ARBI's headless tools.
|
|
263
|
-
*/
|
|
264
|
-
/** A definition's text split into its term and the rest. */
|
|
265
|
-
interface ParsedDefinition {
|
|
266
|
-
/** The term as written, without quotes or emphasis (`Business Day`). */
|
|
267
|
-
term: string;
|
|
268
|
-
/** What follows the term, its connecting word included (`means a day …`, `: the date …`). */
|
|
269
|
-
rest: string;
|
|
270
|
-
/** Just the meaning, the connecting word dropped (`a day …`). */
|
|
271
|
-
meaning: string;
|
|
272
|
-
}
|
|
273
|
-
/** A definition as the commands insert it: the id of its fact, its term and the rest. */
|
|
274
|
-
interface DefinitionText extends Pick<ParsedDefinition, 'term' | 'rest'> {
|
|
275
|
-
id: string;
|
|
276
|
-
}
|
|
277
|
-
/** The matching key of a term: case and whitespace folded, quotes and emphasis dropped. */
|
|
278
|
-
declare function termKey(term: string): string;
|
|
279
|
-
/**
|
|
280
|
-
* Read a definition's term from the start of its text, or `null` when it does not start with one.
|
|
281
|
-
*
|
|
282
|
-
* Optional straight or curly quotes and bold markers around the term are dropped; the term is what
|
|
283
|
-
* comes before "means", "shall mean", "includes", "has the meaning", a colon, or a dash. A quoted
|
|
284
|
-
* term is taken whole, so `"Long-Stop Date" means …` keeps its hyphen.
|
|
285
|
-
*/
|
|
286
|
-
declare function parseDefinition(text: string): ParsedDefinition | null;
|
|
287
|
-
/** Alphabetical order of terms, as a reader sorts a glossary (case and accents folded). */
|
|
288
|
-
declare const compareTerms: (a: string, b: string) => number;
|
|
289
|
-
/** Prefix of every bookmark a defined term links to: hidden in Word (leading underscore). */
|
|
290
|
-
declare const DEFINITION_BOOKMARK_PREFIX = "_dt_";
|
|
291
|
-
/**
|
|
292
|
-
* The Word bookmark of a definition: `_dt_` and the fact id in valid bookmark characters
|
|
293
|
-
* (`fct-AbC12345` → `_dt_fct_AbC12345`), at most 40 characters as Word requires.
|
|
294
|
-
*/
|
|
295
|
-
declare function definitionBookmark(factId: string): string;
|
|
296
|
-
/** The fact id a definition bookmark names, or `null` for any other bookmark. */
|
|
297
|
-
declare function definitionIdOfBookmark(name: string): string | null;
|
|
298
|
-
|
|
299
237
|
/**
|
|
300
238
|
* Defined-term and cross-reference content as WordprocessingML, and the clipboard fragment (a small
|
|
301
239
|
* `.docx` package) that carries it into a document. The engine has no "insert bookmark" command, but
|
|
@@ -580,4 +518,4 @@ declare function documentReader(view: DocumentTrees): DocumentReader;
|
|
|
580
518
|
/** Read a document from its bytes (a collaboration room's `readRoom`). Throws on a bad package. */
|
|
581
519
|
declare function readDocument(bytes: Uint8Array): DocumentReader;
|
|
582
520
|
|
|
583
|
-
export { AGENT_UNDO_MAP, type BrokenReference, CHARACTER_STYLES, type CrossReference, type CrossReferenceMode, CrossReferenceTarget, DEFINITIONS_HEADING,
|
|
521
|
+
export { AGENT_UNDO_MAP, type BrokenReference, CHARACTER_STYLES, type CrossReference, type CrossReferenceMode, CrossReferenceTarget, DEFINITIONS_HEADING, DefinitionText, DefinitionsPort, type DefinitionsSection, DocParagraph, DocPoint, DocumentNumbering, type DocumentReader, type DocumentTrees, type InsertTermResult, LevelOf, ListItemInfo, OoxmlNodeLike, type ParagraphState, ParsedDefinition, REF_BOOKMARK_PREFIX, ReferencesPort, type ReferencesReader, type RefreshResult, type ReplacedSpan, SIGNATURE_FIELDS_NAMESPACE, SIGNATURE_FIELD_KINDS, SIGNATURE_FIELD_LABELS, SIGNATURE_FIELD_TAG_PREFIX, type SectionState, type SignatureFieldKind, type SignatureFieldPayload, type SignatureFieldSpec, type StaleReference, type TermChange, TermEverywhereError, type TermEverywhereResult, type TermSkip, type TermSkipReason, type TermSpec, type TermUse, type UndoRecord, addDefinition, applyTermEverywhere, brokenCrossReferences, crossReferenceXml, crossReferences, definitionOfAnchor, definitionParagraphText, definitionParagraphXml, definitionTooltip, definitionsById, documentReader, documentReferenceTooltip, documentStyleIds, ensureTargetBookmark, fieldNodeIds, findDefinitionsSection, findTermUses, fragmentPackage, headingParagraphXml, insertCrossReference, insertCrossReferenceField, insertDefinedTerm, insertDocumentReference, insertionIndex, isDefinitionsTitle, isSignerEmail, markCrossReferenceTarget, newRefBookmark, newSignatureFieldKey, paragraphIndex, paragraphRunsXml, paragraphState, parseSignatureFieldTag, readDocument, refFieldXml, refInstruction, refResult, refTooltip, refreshCrossReferences, replaceSpan, sectionStates, signatureFieldControls, signatureFieldLabel, signatureFieldTag, signatureFieldWrite, staleCrossReferences, stylesUsed, tableOf, tableState, termLinkXml, undoOps, withoutArticle };
|
package/dist/ooxml.cjs
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fflate = require('fflate');
|
|
4
|
+
|
|
5
|
+
// src/commands/terms.ts
|
|
6
|
+
var QUOTES = `"\u201C\u201D\u201E\u201F'\u2018\u2019\u201A\u201B\xAB\xBB`;
|
|
7
|
+
var OPEN_CLOSE = {
|
|
8
|
+
'"': '"',
|
|
9
|
+
"\u201C": "\u201D",
|
|
10
|
+
"\u201E": "\u201C",
|
|
11
|
+
"\u201F": "\u201D",
|
|
12
|
+
"'": "'",
|
|
13
|
+
"\u2018": "\u2019",
|
|
14
|
+
"\u201A": "\u2018",
|
|
15
|
+
"\u201B": "\u2019",
|
|
16
|
+
"\xAB": "\xBB"
|
|
17
|
+
};
|
|
18
|
+
var EMPHASIS = /^(\*\*|__|\*|_)/;
|
|
19
|
+
var CONNECTORS = /^(?:shall\s+(?:have\s+the\s+meaning|mean|include)s?|ha(?:s|ve)\s+the\s+meanings?|means?\s+and\s+includes?|means|mean|includes|include|[:—–]|\s-\s|-\s)/i;
|
|
20
|
+
var MAX_TERM_LENGTH = 80;
|
|
21
|
+
var MAX_TERM_WORDS = 10;
|
|
22
|
+
function plain(text) {
|
|
23
|
+
return text.replace(/\\([\\`*_{}[\]()#+\-.!"'])/g, "$1").replace(/(\*\*|__)(.+?)\1/g, "$2").replace(/\s+/g, " ").trim();
|
|
24
|
+
}
|
|
25
|
+
function termKey(term) {
|
|
26
|
+
return plain(term).replace(new RegExp(`[${QUOTES}*_]`, "g"), "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
var validTerm = (term) => term.length > 0 && term.length <= MAX_TERM_LENGTH && term.split(/\s+/).length <= MAX_TERM_WORDS && /\p{L}/u.test(term);
|
|
29
|
+
function connectorAt(rest) {
|
|
30
|
+
const lead = rest.length - rest.trimStart().length;
|
|
31
|
+
const spaced = /^\s-\s/.exec(rest);
|
|
32
|
+
if (spaced) return spaced[0].length;
|
|
33
|
+
const match = CONNECTORS.exec(rest.slice(lead));
|
|
34
|
+
return match ? lead + match[0].length : 0;
|
|
35
|
+
}
|
|
36
|
+
function parseDefinition(text) {
|
|
37
|
+
let s = plain(text);
|
|
38
|
+
for (let i = 0; i < 3; i++) s = s.replace(EMPHASIS, "").trimStart();
|
|
39
|
+
let term;
|
|
40
|
+
let rest;
|
|
41
|
+
const open = s[0];
|
|
42
|
+
const close = open ? OPEN_CLOSE[open] : void 0;
|
|
43
|
+
if (close) {
|
|
44
|
+
const end = s.indexOf(close, 1);
|
|
45
|
+
if (end < 0) return null;
|
|
46
|
+
term = s.slice(1, end);
|
|
47
|
+
rest = s.slice(end + 1);
|
|
48
|
+
rest = rest.replace(new RegExp(`^(?:\\*\\*|__|\\*|_|[${QUOTES}])+`), "");
|
|
49
|
+
} else {
|
|
50
|
+
const match = /^(.+?)(\s+(?:shall\s+(?:have\s+the\s+meaning|mean|include)s?|ha(?:s|ve)\s+the\s+meanings?|means?\s+and\s+includes?|means|includes)\b|\s*[:—–]|\s+-\s)/i.exec(
|
|
51
|
+
s
|
|
52
|
+
);
|
|
53
|
+
if (!match) return null;
|
|
54
|
+
term = match[1];
|
|
55
|
+
rest = s.slice(match[1].length);
|
|
56
|
+
}
|
|
57
|
+
term = term.replace(/(\*\*|__|\*|_)/g, "").trim();
|
|
58
|
+
const joint = connectorAt(rest);
|
|
59
|
+
if (!joint || !validTerm(term)) return null;
|
|
60
|
+
const meaning = rest.slice(joint).trim();
|
|
61
|
+
if (!meaning) return null;
|
|
62
|
+
return { term, rest: rest.replace(/^\s+/, " ").replace(/^ (?=[:—–])/, ""), meaning };
|
|
63
|
+
}
|
|
64
|
+
var compareTerms = (a, b) => a.localeCompare(b, void 0, { sensitivity: "base", numeric: true });
|
|
65
|
+
var DEFINITION_BOOKMARK_PREFIX = "_dt_";
|
|
66
|
+
var MAX_BOOKMARK_LENGTH = 40;
|
|
67
|
+
function definitionBookmark(factId) {
|
|
68
|
+
return (DEFINITION_BOOKMARK_PREFIX + factId.replace(/[^A-Za-z0-9_]/g, "_")).slice(
|
|
69
|
+
0,
|
|
70
|
+
MAX_BOOKMARK_LENGTH
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
function definitionIdOfBookmark(name) {
|
|
74
|
+
const match = /^_dt_([a-z]{3})_([A-Za-z0-9]+)$/.exec(name);
|
|
75
|
+
return match ? `${match[1]}-${match[2]}` : null;
|
|
76
|
+
}
|
|
77
|
+
var W_NAMESPACES = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"';
|
|
78
|
+
var INVALID_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g;
|
|
79
|
+
var esc = (text) => text.replace(INVALID_XML, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
80
|
+
var documentXml = (body) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
81
|
+
<w:document ${W_NAMESPACES}><w:body>${body}</w:body></w:document>`;
|
|
82
|
+
var stylesXml = (defaults, styles) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
83
|
+
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${defaults}${styles}</w:styles>`;
|
|
84
|
+
var CONTENT_TYPES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
85
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`;
|
|
86
|
+
var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
87
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`;
|
|
88
|
+
var documentRels = (more = "") => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
89
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${more}</Relationships>`;
|
|
90
|
+
var hyperlinkRelationship = (id, url) => `<Relationship Id="${esc(id)}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${esc(url)}" TargetMode="External"/>`;
|
|
91
|
+
var coreXml = (title, at) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
92
|
+
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${esc(
|
|
93
|
+
title
|
|
94
|
+
)}</dc:title><dcterms:created xsi:type="dcterms:W3CDTF">${at}</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">${at}</dcterms:modified></cp:coreProperties>`;
|
|
95
|
+
function docxPackage(parts) {
|
|
96
|
+
const at = new Date(parts.at ?? Date.now()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
97
|
+
return fflate.zipSync({
|
|
98
|
+
"[Content_Types].xml": fflate.strToU8(CONTENT_TYPES),
|
|
99
|
+
"_rels/.rels": fflate.strToU8(ROOT_RELS),
|
|
100
|
+
"docProps/core.xml": fflate.strToU8(coreXml(parts.title, at)),
|
|
101
|
+
"word/document.xml": fflate.strToU8(parts.document),
|
|
102
|
+
"word/styles.xml": fflate.strToU8(parts.styles),
|
|
103
|
+
"word/_rels/document.xml.rels": fflate.strToU8(documentRels(parts.relationships))
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
exports.DEFINITION_BOOKMARK_PREFIX = DEFINITION_BOOKMARK_PREFIX;
|
|
108
|
+
exports.W_NAMESPACES = W_NAMESPACES;
|
|
109
|
+
exports.compareTerms = compareTerms;
|
|
110
|
+
exports.definitionBookmark = definitionBookmark;
|
|
111
|
+
exports.definitionIdOfBookmark = definitionIdOfBookmark;
|
|
112
|
+
exports.documentXml = documentXml;
|
|
113
|
+
exports.docxPackage = docxPackage;
|
|
114
|
+
exports.esc = esc;
|
|
115
|
+
exports.hyperlinkRelationship = hyperlinkRelationship;
|
|
116
|
+
exports.parseDefinition = parseDefinition;
|
|
117
|
+
exports.stylesXml = stylesXml;
|
|
118
|
+
exports.termKey = termKey;
|
|
119
|
+
//# sourceMappingURL=ooxml.cjs.map
|
|
120
|
+
//# sourceMappingURL=ooxml.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/terms.ts","../src/commands/ooxml.ts"],"names":["zipSync","strToU8"],"mappings":"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","file":"ooxml.cjs","sourcesContent":["/**\n * Defined terms in text: a definition's term read from the start of its text (`\"Shares\" means …`,\n * `**Business Day** shall mean …`, `Completion: the date …`), the order of a glossary, and the hidden\n * Word bookmark a term links to. Shared by the editor's Defined Terms and ARBI's headless tools.\n */\n\n/** A definition's text split into its term and the rest. */\nexport interface ParsedDefinition {\n /** The term as written, without quotes or emphasis (`Business Day`). */\n term: string\n /** What follows the term, its connecting word included (`means a day …`, `: the date …`). */\n rest: string\n /** Just the meaning, the connecting word dropped (`a day …`). */\n meaning: string\n}\n\n/** A definition as the commands insert it: the id of its fact, its term and the rest. */\nexport interface DefinitionText extends Pick<ParsedDefinition, 'term' | 'rest'> {\n id: string\n}\n\nconst QUOTES = `\"“”„‟'‘’‚‛«»`\nconst OPEN_CLOSE: Record<string, string> = {\n '\"': '\"',\n '“': '”',\n '„': '“',\n '‟': '”',\n \"'\": \"'\",\n '‘': '’',\n '‚': '‘',\n '‛': '’',\n '«': '»',\n}\nconst EMPHASIS = /^(\\*\\*|__|\\*|_)/\n/** The words (or punctuation) joining a term to its meaning, longest first. */\nconst CONNECTORS =\n /^(?:shall\\s+(?:have\\s+the\\s+meaning|mean|include)s?|ha(?:s|ve)\\s+the\\s+meanings?|means?\\s+and\\s+includes?|means|mean|includes|include|[:—–]|\\s-\\s|-\\s)/i\nconst MAX_TERM_LENGTH = 80\nconst MAX_TERM_WORDS = 10\n\n/** Markdown emphasis and escapes dropped, whitespace collapsed. */\nfunction plain(text: string): string {\n return text\n .replace(/\\\\([\\\\`*_{}[\\]()#+\\-.!\"'])/g, '$1')\n .replace(/(\\*\\*|__)(.+?)\\1/g, '$2')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/** The matching key of a term: case and whitespace folded, quotes and emphasis dropped. */\nexport function termKey(term: string): string {\n return plain(term)\n .replace(new RegExp(`[${QUOTES}*_]`, 'g'), '')\n .replace(/\\s+/g, ' ')\n .trim()\n .toLowerCase()\n}\n\nconst validTerm = (term: string) =>\n term.length > 0 &&\n term.length <= MAX_TERM_LENGTH &&\n term.split(/\\s+/).length <= MAX_TERM_WORDS &&\n /\\p{L}/u.test(term)\n\n/** A connector at the start of `rest` (after optional whitespace): its length, or 0. */\nfunction connectorAt(rest: string): number {\n const lead = rest.length - rest.trimStart().length\n // A spaced hyphen needs its leading space, so try the raw text before the trimmed one.\n const spaced = /^\\s-\\s/.exec(rest)\n if (spaced) return spaced[0].length\n const match = CONNECTORS.exec(rest.slice(lead))\n return match ? lead + match[0].length : 0\n}\n\n/**\n * Read a definition's term from the start of its text, or `null` when it does not start with one.\n *\n * Optional straight or curly quotes and bold markers around the term are dropped; the term is what\n * comes before \"means\", \"shall mean\", \"includes\", \"has the meaning\", a colon, or a dash. A quoted\n * term is taken whole, so `\"Long-Stop Date\" means …` keeps its hyphen.\n */\nexport function parseDefinition(text: string): ParsedDefinition | null {\n let s = plain(text)\n // Leading emphasis markers (`**\"Shares\"** means`), any order with the opening quote.\n for (let i = 0; i < 3; i++) s = s.replace(EMPHASIS, '').trimStart()\n let term: string\n let rest: string\n const open = s[0]\n const close = open ? OPEN_CLOSE[open] : undefined\n if (close) {\n const end = s.indexOf(close, 1)\n if (end < 0) return null\n term = s.slice(1, end)\n rest = s.slice(end + 1)\n // Emphasis or a closing quote between the term and its connector (`\"Shares\"**`, `**\"X**\"`).\n rest = rest.replace(new RegExp(`^(?:\\\\*\\\\*|__|\\\\*|_|[${QUOTES}])+`), '')\n } else {\n // Unquoted: the term runs up to the first connector.\n const match =\n /^(.+?)(\\s+(?:shall\\s+(?:have\\s+the\\s+meaning|mean|include)s?|ha(?:s|ve)\\s+the\\s+meanings?|means?\\s+and\\s+includes?|means|includes)\\b|\\s*[:—–]|\\s+-\\s)/i.exec(\n s\n )\n if (!match) return null\n term = match[1]\n rest = s.slice(match[1].length)\n }\n term = term.replace(/(\\*\\*|__|\\*|_)/g, '').trim()\n const joint = connectorAt(rest)\n if (!joint || !validTerm(term)) return null\n const meaning = rest.slice(joint).trim()\n if (!meaning) return null\n return { term, rest: rest.replace(/^\\s+/, ' ').replace(/^ (?=[:—–])/, ''), meaning }\n}\n\n/** Alphabetical order of terms, as a reader sorts a glossary (case and accents folded). */\nexport const compareTerms = (a: string, b: string) =>\n a.localeCompare(b, undefined, { sensitivity: 'base', numeric: true })\n\n/** Prefix of every bookmark a defined term links to: hidden in Word (leading underscore). */\nexport const DEFINITION_BOOKMARK_PREFIX = '_dt_'\nconst MAX_BOOKMARK_LENGTH = 40\n\n/**\n * The Word bookmark of a definition: `_dt_` and the fact id in valid bookmark characters\n * (`fct-AbC12345` → `_dt_fct_AbC12345`), at most 40 characters as Word requires.\n */\nexport function definitionBookmark(factId: string): string {\n return (DEFINITION_BOOKMARK_PREFIX + factId.replace(/[^A-Za-z0-9_]/g, '_')).slice(\n 0,\n MAX_BOOKMARK_LENGTH\n )\n}\n\n/** The fact id a definition bookmark names, or `null` for any other bookmark. */\nexport function definitionIdOfBookmark(name: string): string | null {\n const match = /^_dt_([a-z]{3})_([A-Za-z0-9]+)$/.exec(name)\n return match ? `${match[1]}-${match[2]}` : null\n}\n","/**\n * The minimal WordprocessingML a command writes: escaped text, a `word/document.xml` and\n * `word/styles.xml` around it, and the `.docx` package that carries them (a paste fragment).\n */\n\nimport { strToU8, zipSync } from 'fflate'\n\nexport const W_NAMESPACES =\n 'xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"'\n\n// Text that is not allowed in XML 1.0 at all (control characters) is dropped, not escaped.\n// eslint-disable-next-line no-control-regex\nconst INVALID_XML = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F]/g\n\n/** Text as XML character data or an attribute value. */\nexport const esc = (text: string) =>\n text\n .replace(INVALID_XML, '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n\n/** A `word/document.xml` around body content (paragraphs, tables, then the section). */\nexport const documentXml = (body: string) =>\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<w:document ${W_NAMESPACES}><w:body>${body}</w:body></w:document>`\n\n/** `word/styles.xml` holding the given style definitions. */\nexport const stylesXml = (defaults: string, styles: string) =>\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<w:styles xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">${defaults}${styles}</w:styles>`\n\nconst CONTENT_TYPES = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Default Extension=\"xml\" ContentType=\"application/xml\"/><Override PartName=\"/word/document.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\"/><Override PartName=\"/word/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml\"/><Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/></Types>`\n\nconst ROOT_RELS = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"word/document.xml\"/><Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\" Target=\"docProps/core.xml\"/></Relationships>`\n\n/** `word/_rels/document.xml.rels`: the styles part, then any further relationships given. */\nconst documentRels = (more = '') => `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>${more}</Relationships>`\n\n/** A relationship to an external hyperlink target, for `w:hyperlink r:id`. */\nexport const hyperlinkRelationship = (id: string, url: string) =>\n `<Relationship Id=\"${esc(id)}\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\"${esc(url)}\" TargetMode=\"External\"/>`\n\nconst coreXml = (\n title: string,\n at: string\n) => `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<cp:coreProperties xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><dc:title>${esc(\n title\n)}</dc:title><dcterms:created xsi:type=\"dcterms:W3CDTF\">${at}</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">${at}</dcterms:modified></cp:coreProperties>`\n\n/** The minimal `.docx` package: a document, its styles, and its title and time. */\nexport function docxPackage(parts: {\n document: string\n styles: string\n title: string\n /** ISO time; default now. */\n at?: string\n /** Further `word/document.xml` relationships (e.g. {@link hyperlinkRelationship}). */\n relationships?: string\n}): Uint8Array {\n const at = new Date(parts.at ?? Date.now()).toISOString().replace(/\\.\\d{3}Z$/, 'Z')\n return zipSync({\n '[Content_Types].xml': strToU8(CONTENT_TYPES),\n '_rels/.rels': strToU8(ROOT_RELS),\n 'docProps/core.xml': strToU8(coreXml(parts.title, at)),\n 'word/document.xml': strToU8(parts.document),\n 'word/styles.xml': strToU8(parts.styles),\n 'word/_rels/document.xml.rels': strToU8(documentRels(parts.relationships)),\n })\n}\n"]}
|
package/dist/ooxml.d.cts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The minimal WordprocessingML a command writes: escaped text, a `word/document.xml` and
|
|
3
|
+
* `word/styles.xml` around it, and the `.docx` package that carries them (a paste fragment).
|
|
4
|
+
*/
|
|
5
|
+
declare const W_NAMESPACES = "xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"";
|
|
6
|
+
/** Text as XML character data or an attribute value. */
|
|
7
|
+
declare const esc: (text: string) => string;
|
|
8
|
+
/** A `word/document.xml` around body content (paragraphs, tables, then the section). */
|
|
9
|
+
declare const documentXml: (body: string) => string;
|
|
10
|
+
/** `word/styles.xml` holding the given style definitions. */
|
|
11
|
+
declare const stylesXml: (defaults: string, styles: string) => string;
|
|
12
|
+
/** A relationship to an external hyperlink target, for `w:hyperlink r:id`. */
|
|
13
|
+
declare const hyperlinkRelationship: (id: string, url: string) => string;
|
|
14
|
+
/** The minimal `.docx` package: a document, its styles, and its title and time. */
|
|
15
|
+
declare function docxPackage(parts: {
|
|
16
|
+
document: string;
|
|
17
|
+
styles: string;
|
|
18
|
+
title: string;
|
|
19
|
+
/** ISO time; default now. */
|
|
20
|
+
at?: string;
|
|
21
|
+
/** Further `word/document.xml` relationships (e.g. {@link hyperlinkRelationship}). */
|
|
22
|
+
relationships?: string;
|
|
23
|
+
}): Uint8Array;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Defined terms in text: a definition's term read from the start of its text (`"Shares" means …`,
|
|
27
|
+
* `**Business Day** shall mean …`, `Completion: the date …`), the order of a glossary, and the hidden
|
|
28
|
+
* Word bookmark a term links to. Shared by the editor's Defined Terms and ARBI's headless tools.
|
|
29
|
+
*/
|
|
30
|
+
/** A definition's text split into its term and the rest. */
|
|
31
|
+
interface ParsedDefinition {
|
|
32
|
+
/** The term as written, without quotes or emphasis (`Business Day`). */
|
|
33
|
+
term: string;
|
|
34
|
+
/** What follows the term, its connecting word included (`means a day …`, `: the date …`). */
|
|
35
|
+
rest: string;
|
|
36
|
+
/** Just the meaning, the connecting word dropped (`a day …`). */
|
|
37
|
+
meaning: string;
|
|
38
|
+
}
|
|
39
|
+
/** A definition as the commands insert it: the id of its fact, its term and the rest. */
|
|
40
|
+
interface DefinitionText extends Pick<ParsedDefinition, 'term' | 'rest'> {
|
|
41
|
+
id: string;
|
|
42
|
+
}
|
|
43
|
+
/** The matching key of a term: case and whitespace folded, quotes and emphasis dropped. */
|
|
44
|
+
declare function termKey(term: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Read a definition's term from the start of its text, or `null` when it does not start with one.
|
|
47
|
+
*
|
|
48
|
+
* Optional straight or curly quotes and bold markers around the term are dropped; the term is what
|
|
49
|
+
* comes before "means", "shall mean", "includes", "has the meaning", a colon, or a dash. A quoted
|
|
50
|
+
* term is taken whole, so `"Long-Stop Date" means …` keeps its hyphen.
|
|
51
|
+
*/
|
|
52
|
+
declare function parseDefinition(text: string): ParsedDefinition | null;
|
|
53
|
+
/** Alphabetical order of terms, as a reader sorts a glossary (case and accents folded). */
|
|
54
|
+
declare const compareTerms: (a: string, b: string) => number;
|
|
55
|
+
/** Prefix of every bookmark a defined term links to: hidden in Word (leading underscore). */
|
|
56
|
+
declare const DEFINITION_BOOKMARK_PREFIX = "_dt_";
|
|
57
|
+
/**
|
|
58
|
+
* The Word bookmark of a definition: `_dt_` and the fact id in valid bookmark characters
|
|
59
|
+
* (`fct-AbC12345` → `_dt_fct_AbC12345`), at most 40 characters as Word requires.
|
|
60
|
+
*/
|
|
61
|
+
declare function definitionBookmark(factId: string): string;
|
|
62
|
+
/** The fact id a definition bookmark names, or `null` for any other bookmark. */
|
|
63
|
+
declare function definitionIdOfBookmark(name: string): string | null;
|
|
64
|
+
|
|
65
|
+
export { DEFINITION_BOOKMARK_PREFIX, type DefinitionText, type ParsedDefinition, W_NAMESPACES, compareTerms, definitionBookmark, definitionIdOfBookmark, documentXml, docxPackage, esc, hyperlinkRelationship, parseDefinition, stylesXml, termKey };
|
package/dist/ooxml.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The minimal WordprocessingML a command writes: escaped text, a `word/document.xml` and
|
|
3
|
+
* `word/styles.xml` around it, and the `.docx` package that carries them (a paste fragment).
|
|
4
|
+
*/
|
|
5
|
+
declare const W_NAMESPACES = "xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"";
|
|
6
|
+
/** Text as XML character data or an attribute value. */
|
|
7
|
+
declare const esc: (text: string) => string;
|
|
8
|
+
/** A `word/document.xml` around body content (paragraphs, tables, then the section). */
|
|
9
|
+
declare const documentXml: (body: string) => string;
|
|
10
|
+
/** `word/styles.xml` holding the given style definitions. */
|
|
11
|
+
declare const stylesXml: (defaults: string, styles: string) => string;
|
|
12
|
+
/** A relationship to an external hyperlink target, for `w:hyperlink r:id`. */
|
|
13
|
+
declare const hyperlinkRelationship: (id: string, url: string) => string;
|
|
14
|
+
/** The minimal `.docx` package: a document, its styles, and its title and time. */
|
|
15
|
+
declare function docxPackage(parts: {
|
|
16
|
+
document: string;
|
|
17
|
+
styles: string;
|
|
18
|
+
title: string;
|
|
19
|
+
/** ISO time; default now. */
|
|
20
|
+
at?: string;
|
|
21
|
+
/** Further `word/document.xml` relationships (e.g. {@link hyperlinkRelationship}). */
|
|
22
|
+
relationships?: string;
|
|
23
|
+
}): Uint8Array;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Defined terms in text: a definition's term read from the start of its text (`"Shares" means …`,
|
|
27
|
+
* `**Business Day** shall mean …`, `Completion: the date …`), the order of a glossary, and the hidden
|
|
28
|
+
* Word bookmark a term links to. Shared by the editor's Defined Terms and ARBI's headless tools.
|
|
29
|
+
*/
|
|
30
|
+
/** A definition's text split into its term and the rest. */
|
|
31
|
+
interface ParsedDefinition {
|
|
32
|
+
/** The term as written, without quotes or emphasis (`Business Day`). */
|
|
33
|
+
term: string;
|
|
34
|
+
/** What follows the term, its connecting word included (`means a day …`, `: the date …`). */
|
|
35
|
+
rest: string;
|
|
36
|
+
/** Just the meaning, the connecting word dropped (`a day …`). */
|
|
37
|
+
meaning: string;
|
|
38
|
+
}
|
|
39
|
+
/** A definition as the commands insert it: the id of its fact, its term and the rest. */
|
|
40
|
+
interface DefinitionText extends Pick<ParsedDefinition, 'term' | 'rest'> {
|
|
41
|
+
id: string;
|
|
42
|
+
}
|
|
43
|
+
/** The matching key of a term: case and whitespace folded, quotes and emphasis dropped. */
|
|
44
|
+
declare function termKey(term: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Read a definition's term from the start of its text, or `null` when it does not start with one.
|
|
47
|
+
*
|
|
48
|
+
* Optional straight or curly quotes and bold markers around the term are dropped; the term is what
|
|
49
|
+
* comes before "means", "shall mean", "includes", "has the meaning", a colon, or a dash. A quoted
|
|
50
|
+
* term is taken whole, so `"Long-Stop Date" means …` keeps its hyphen.
|
|
51
|
+
*/
|
|
52
|
+
declare function parseDefinition(text: string): ParsedDefinition | null;
|
|
53
|
+
/** Alphabetical order of terms, as a reader sorts a glossary (case and accents folded). */
|
|
54
|
+
declare const compareTerms: (a: string, b: string) => number;
|
|
55
|
+
/** Prefix of every bookmark a defined term links to: hidden in Word (leading underscore). */
|
|
56
|
+
declare const DEFINITION_BOOKMARK_PREFIX = "_dt_";
|
|
57
|
+
/**
|
|
58
|
+
* The Word bookmark of a definition: `_dt_` and the fact id in valid bookmark characters
|
|
59
|
+
* (`fct-AbC12345` → `_dt_fct_AbC12345`), at most 40 characters as Word requires.
|
|
60
|
+
*/
|
|
61
|
+
declare function definitionBookmark(factId: string): string;
|
|
62
|
+
/** The fact id a definition bookmark names, or `null` for any other bookmark. */
|
|
63
|
+
declare function definitionIdOfBookmark(name: string): string | null;
|
|
64
|
+
|
|
65
|
+
export { DEFINITION_BOOKMARK_PREFIX, type DefinitionText, type ParsedDefinition, W_NAMESPACES, compareTerms, definitionBookmark, definitionIdOfBookmark, documentXml, docxPackage, esc, hyperlinkRelationship, parseDefinition, stylesXml, termKey };
|
package/dist/ooxml.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { zipSync, strToU8 } from 'fflate';
|
|
2
|
+
|
|
3
|
+
// src/commands/terms.ts
|
|
4
|
+
var QUOTES = `"\u201C\u201D\u201E\u201F'\u2018\u2019\u201A\u201B\xAB\xBB`;
|
|
5
|
+
var OPEN_CLOSE = {
|
|
6
|
+
'"': '"',
|
|
7
|
+
"\u201C": "\u201D",
|
|
8
|
+
"\u201E": "\u201C",
|
|
9
|
+
"\u201F": "\u201D",
|
|
10
|
+
"'": "'",
|
|
11
|
+
"\u2018": "\u2019",
|
|
12
|
+
"\u201A": "\u2018",
|
|
13
|
+
"\u201B": "\u2019",
|
|
14
|
+
"\xAB": "\xBB"
|
|
15
|
+
};
|
|
16
|
+
var EMPHASIS = /^(\*\*|__|\*|_)/;
|
|
17
|
+
var CONNECTORS = /^(?:shall\s+(?:have\s+the\s+meaning|mean|include)s?|ha(?:s|ve)\s+the\s+meanings?|means?\s+and\s+includes?|means|mean|includes|include|[:—–]|\s-\s|-\s)/i;
|
|
18
|
+
var MAX_TERM_LENGTH = 80;
|
|
19
|
+
var MAX_TERM_WORDS = 10;
|
|
20
|
+
function plain(text) {
|
|
21
|
+
return text.replace(/\\([\\`*_{}[\]()#+\-.!"'])/g, "$1").replace(/(\*\*|__)(.+?)\1/g, "$2").replace(/\s+/g, " ").trim();
|
|
22
|
+
}
|
|
23
|
+
function termKey(term) {
|
|
24
|
+
return plain(term).replace(new RegExp(`[${QUOTES}*_]`, "g"), "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
var validTerm = (term) => term.length > 0 && term.length <= MAX_TERM_LENGTH && term.split(/\s+/).length <= MAX_TERM_WORDS && /\p{L}/u.test(term);
|
|
27
|
+
function connectorAt(rest) {
|
|
28
|
+
const lead = rest.length - rest.trimStart().length;
|
|
29
|
+
const spaced = /^\s-\s/.exec(rest);
|
|
30
|
+
if (spaced) return spaced[0].length;
|
|
31
|
+
const match = CONNECTORS.exec(rest.slice(lead));
|
|
32
|
+
return match ? lead + match[0].length : 0;
|
|
33
|
+
}
|
|
34
|
+
function parseDefinition(text) {
|
|
35
|
+
let s = plain(text);
|
|
36
|
+
for (let i = 0; i < 3; i++) s = s.replace(EMPHASIS, "").trimStart();
|
|
37
|
+
let term;
|
|
38
|
+
let rest;
|
|
39
|
+
const open = s[0];
|
|
40
|
+
const close = open ? OPEN_CLOSE[open] : void 0;
|
|
41
|
+
if (close) {
|
|
42
|
+
const end = s.indexOf(close, 1);
|
|
43
|
+
if (end < 0) return null;
|
|
44
|
+
term = s.slice(1, end);
|
|
45
|
+
rest = s.slice(end + 1);
|
|
46
|
+
rest = rest.replace(new RegExp(`^(?:\\*\\*|__|\\*|_|[${QUOTES}])+`), "");
|
|
47
|
+
} else {
|
|
48
|
+
const match = /^(.+?)(\s+(?:shall\s+(?:have\s+the\s+meaning|mean|include)s?|ha(?:s|ve)\s+the\s+meanings?|means?\s+and\s+includes?|means|includes)\b|\s*[:—–]|\s+-\s)/i.exec(
|
|
49
|
+
s
|
|
50
|
+
);
|
|
51
|
+
if (!match) return null;
|
|
52
|
+
term = match[1];
|
|
53
|
+
rest = s.slice(match[1].length);
|
|
54
|
+
}
|
|
55
|
+
term = term.replace(/(\*\*|__|\*|_)/g, "").trim();
|
|
56
|
+
const joint = connectorAt(rest);
|
|
57
|
+
if (!joint || !validTerm(term)) return null;
|
|
58
|
+
const meaning = rest.slice(joint).trim();
|
|
59
|
+
if (!meaning) return null;
|
|
60
|
+
return { term, rest: rest.replace(/^\s+/, " ").replace(/^ (?=[:—–])/, ""), meaning };
|
|
61
|
+
}
|
|
62
|
+
var compareTerms = (a, b) => a.localeCompare(b, void 0, { sensitivity: "base", numeric: true });
|
|
63
|
+
var DEFINITION_BOOKMARK_PREFIX = "_dt_";
|
|
64
|
+
var MAX_BOOKMARK_LENGTH = 40;
|
|
65
|
+
function definitionBookmark(factId) {
|
|
66
|
+
return (DEFINITION_BOOKMARK_PREFIX + factId.replace(/[^A-Za-z0-9_]/g, "_")).slice(
|
|
67
|
+
0,
|
|
68
|
+
MAX_BOOKMARK_LENGTH
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
function definitionIdOfBookmark(name) {
|
|
72
|
+
const match = /^_dt_([a-z]{3})_([A-Za-z0-9]+)$/.exec(name);
|
|
73
|
+
return match ? `${match[1]}-${match[2]}` : null;
|
|
74
|
+
}
|
|
75
|
+
var W_NAMESPACES = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"';
|
|
76
|
+
var INVALID_XML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g;
|
|
77
|
+
var esc = (text) => text.replace(INVALID_XML, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
78
|
+
var documentXml = (body) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
79
|
+
<w:document ${W_NAMESPACES}><w:body>${body}</w:body></w:document>`;
|
|
80
|
+
var stylesXml = (defaults, styles) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
81
|
+
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${defaults}${styles}</w:styles>`;
|
|
82
|
+
var CONTENT_TYPES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
83
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`;
|
|
84
|
+
var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
85
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`;
|
|
86
|
+
var documentRels = (more = "") => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
87
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>${more}</Relationships>`;
|
|
88
|
+
var hyperlinkRelationship = (id, url) => `<Relationship Id="${esc(id)}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${esc(url)}" TargetMode="External"/>`;
|
|
89
|
+
var coreXml = (title, at) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
90
|
+
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${esc(
|
|
91
|
+
title
|
|
92
|
+
)}</dc:title><dcterms:created xsi:type="dcterms:W3CDTF">${at}</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">${at}</dcterms:modified></cp:coreProperties>`;
|
|
93
|
+
function docxPackage(parts) {
|
|
94
|
+
const at = new Date(parts.at ?? Date.now()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
95
|
+
return zipSync({
|
|
96
|
+
"[Content_Types].xml": strToU8(CONTENT_TYPES),
|
|
97
|
+
"_rels/.rels": strToU8(ROOT_RELS),
|
|
98
|
+
"docProps/core.xml": strToU8(coreXml(parts.title, at)),
|
|
99
|
+
"word/document.xml": strToU8(parts.document),
|
|
100
|
+
"word/styles.xml": strToU8(parts.styles),
|
|
101
|
+
"word/_rels/document.xml.rels": strToU8(documentRels(parts.relationships))
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { DEFINITION_BOOKMARK_PREFIX, W_NAMESPACES, compareTerms, definitionBookmark, definitionIdOfBookmark, documentXml, docxPackage, esc, hyperlinkRelationship, parseDefinition, stylesXml, termKey };
|
|
106
|
+
//# sourceMappingURL=ooxml.js.map
|
|
107
|
+
//# sourceMappingURL=ooxml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/terms.ts","../src/commands/ooxml.ts"],"names":[],"mappings":";;;AAqBA,IAAM,MAAA,GAAS,CAAA,0DAAA,CAAA;AACf,IAAM,UAAA,GAAqC;AAAA,EACzC,GAAA,EAAK,GAAA;AAAA,EACL,QAAA,EAAK,QAAA;AAAA,EACL,QAAA,EAAK,QAAA;AAAA,EACL,QAAA,EAAK,QAAA;AAAA,EACL,GAAA,EAAK,GAAA;AAAA,EACL,QAAA,EAAK,QAAA;AAAA,EACL,QAAA,EAAK,QAAA;AAAA,EACL,QAAA,EAAK,QAAA;AAAA,EACL,MAAA,EAAK;AACP,CAAA;AACA,IAAM,QAAA,GAAW,iBAAA;AAEjB,IAAM,UAAA,GACJ,yJAAA;AACF,IAAM,eAAA,GAAkB,EAAA;AACxB,IAAM,cAAA,GAAiB,EAAA;AAGvB,SAAS,MAAM,IAAA,EAAsB;AACnC,EAAA,OAAO,IAAA,CACJ,OAAA,CAAQ,6BAAA,EAA+B,IAAI,CAAA,CAC3C,OAAA,CAAQ,mBAAA,EAAqB,IAAI,CAAA,CACjC,OAAA,CAAQ,MAAA,EAAQ,GAAG,EACnB,IAAA,EAAK;AACV;AAGO,SAAS,QAAQ,IAAA,EAAsB;AAC5C,EAAA,OAAO,MAAM,IAAI,CAAA,CACd,QAAQ,IAAI,MAAA,CAAO,IAAI,MAAM,CAAA,GAAA,CAAA,EAAO,GAAG,CAAA,EAAG,EAAE,EAC5C,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA,CACnB,IAAA,GACA,WAAA,EAAY;AACjB;AAEA,IAAM,YAAY,CAAC,IAAA,KACjB,IAAA,CAAK,MAAA,GAAS,KACd,IAAA,CAAK,MAAA,IAAU,eAAA,IACf,IAAA,CAAK,MAAM,KAAK,CAAA,CAAE,UAAU,cAAA,IAC5B,QAAA,CAAS,KAAK,IAAI,CAAA;AAGpB,SAAS,YAAY,IAAA,EAAsB;AACzC,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,WAAU,CAAE,MAAA;AAE5C,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,IAAA,CAAK,IAAI,CAAA;AACjC,EAAA,IAAI,MAAA,EAAQ,OAAO,MAAA,CAAO,CAAC,CAAA,CAAE,MAAA;AAC7B,EAAA,MAAM,QAAQ,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,IAAI,CAAC,CAAA;AAC9C,EAAA,OAAO,KAAA,GAAQ,IAAA,GAAO,KAAA,CAAM,CAAC,EAAE,MAAA,GAAS,CAAA;AAC1C;AASO,SAAS,gBAAgB,IAAA,EAAuC;AACrE,EAAA,IAAI,CAAA,GAAI,MAAM,IAAI,CAAA;AAElB,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK,CAAA,GAAI,CAAA,CAAE,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA,CAAE,SAAA,EAAU;AAClE,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,IAAA;AACJ,EAAA,MAAM,IAAA,GAAO,EAAE,CAAC,CAAA;AAChB,EAAA,MAAM,KAAA,GAAQ,IAAA,GAAO,UAAA,CAAW,IAAI,CAAA,GAAI,MAAA;AACxC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,GAAA,GAAM,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAC9B,IAAA,IAAI,GAAA,GAAM,GAAG,OAAO,IAAA;AACpB,IAAA,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AACrB,IAAA,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA;AAEtB,IAAA,IAAA,GAAO,IAAA,CAAK,QAAQ,IAAI,MAAA,CAAO,wBAAwB,MAAM,CAAA,GAAA,CAAK,GAAG,EAAE,CAAA;AAAA,EACzE,CAAA,MAAO;AAEL,IAAA,MAAM,QACJ,wJAAA,CAAyJ,IAAA;AAAA,MACvJ;AAAA,KACF;AACF,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,IAAA,IAAA,GAAO,MAAM,CAAC,CAAA;AACd,IAAA,IAAA,GAAO,CAAA,CAAE,KAAA,CAAM,KAAA,CAAM,CAAC,EAAE,MAAM,CAAA;AAAA,EAChC;AACA,EAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,iBAAA,EAAmB,EAAE,EAAE,IAAA,EAAK;AAChD,EAAA,MAAM,KAAA,GAAQ,YAAY,IAAI,CAAA;AAC9B,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,SAAA,CAAU,IAAI,GAAG,OAAO,IAAA;AACvC,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,KAAK,EAAE,IAAA,EAAK;AACvC,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,EAAA,OAAO,EAAE,IAAA,EAAM,IAAA,EAAM,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAA,CAAE,OAAA,CAAQ,aAAA,EAAe,EAAE,CAAA,EAAG,OAAA,EAAQ;AACrF;AAGO,IAAM,YAAA,GAAe,CAAC,CAAA,EAAW,CAAA,KACtC,CAAA,CAAE,aAAA,CAAc,CAAA,EAAG,MAAA,EAAW,EAAE,WAAA,EAAa,MAAA,EAAQ,OAAA,EAAS,MAAM;AAG/D,IAAM,0BAAA,GAA6B;AAC1C,IAAM,mBAAA,GAAsB,EAAA;AAMrB,SAAS,mBAAmB,MAAA,EAAwB;AACzD,EAAA,OAAA,CAAQ,0BAAA,GAA6B,MAAA,CAAO,OAAA,CAAQ,gBAAA,EAAkB,GAAG,CAAA,EAAG,KAAA;AAAA,IAC1E,CAAA;AAAA,IACA;AAAA,GACF;AACF;AAGO,SAAS,uBAAuB,IAAA,EAA6B;AAClE,EAAA,MAAM,KAAA,GAAQ,iCAAA,CAAkC,IAAA,CAAK,IAAI,CAAA;AACzD,EAAA,OAAO,KAAA,GAAQ,GAAG,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA,EAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA,GAAK,IAAA;AAC7C;AClIO,IAAM,YAAA,GACX;AAIF,IAAM,WAAA,GAAc,6CAAA;AAGb,IAAM,GAAA,GAAM,CAAC,IAAA,KAClB,IAAA,CACG,QAAQ,WAAA,EAAa,EAAE,CAAA,CACvB,OAAA,CAAQ,IAAA,EAAM,OAAO,EACrB,OAAA,CAAQ,IAAA,EAAM,MAAM,CAAA,CACpB,OAAA,CAAQ,MAAM,MAAM,CAAA,CACpB,OAAA,CAAQ,IAAA,EAAM,QAAQ;AAGpB,IAAM,WAAA,GAAc,CAAC,IAAA,KAC1B,CAAA;AAAA,YAAA,EACY,YAAY,YAAY,IAAI,CAAA,sBAAA;AAGnC,IAAM,SAAA,GAAY,CAAC,QAAA,EAAkB,MAAA,KAC1C,CAAA;AAAA,iFAAA,EACiF,QAAQ,GAAG,MAAM,CAAA,WAAA;AAEpG,IAAM,aAAA,GAAgB,CAAA;AAAA,umBAAA,CAAA;AAGtB,IAAM,SAAA,GAAY,CAAA;AAAA,mYAAA,CAAA;AAIlB,IAAM,YAAA,GAAe,CAAC,IAAA,GAAO,EAAA,KAAO,CAAA;AAAA,mNAAA,EACiL,IAAI,CAAA,gBAAA,CAAA;AAGlN,IAAM,qBAAA,GAAwB,CAAC,EAAA,EAAY,GAAA,KAChD,CAAA,kBAAA,EAAqB,GAAA,CAAI,EAAE,CAAC,CAAA,+FAAA,EAAkG,GAAA,CAAI,GAAG,CAAC,CAAA,yBAAA;AAExI,IAAM,OAAA,GAAU,CACd,KAAA,EACA,EAAA,KACG,CAAA;AAAA,4PAAA,EACyP,GAAA;AAAA,EAC5P;AACF,CAAC,CAAA,sDAAA,EAAyD,EAAE,CAAA,8DAAA,EAAiE,EAAE,CAAA,uCAAA,CAAA;AAGxH,SAAS,YAAY,KAAA,EAQb;AACb,EAAA,MAAM,EAAA,GAAK,IAAI,IAAA,CAAK,KAAA,CAAM,EAAA,IAAM,IAAA,CAAK,GAAA,EAAK,CAAA,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,aAAa,GAAG,CAAA;AAClF,EAAA,OAAO,OAAA,CAAQ;AAAA,IACb,qBAAA,EAAuB,QAAQ,aAAa,CAAA;AAAA,IAC5C,aAAA,EAAe,QAAQ,SAAS,CAAA;AAAA,IAChC,qBAAqB,OAAA,CAAQ,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,EAAE,CAAC,CAAA;AAAA,IACrD,mBAAA,EAAqB,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA;AAAA,IAC3C,iBAAA,EAAmB,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA;AAAA,IACvC,8BAAA,EAAgC,OAAA,CAAQ,YAAA,CAAa,KAAA,CAAM,aAAa,CAAC;AAAA,GAC1E,CAAA;AACH","file":"ooxml.js","sourcesContent":["/**\n * Defined terms in text: a definition's term read from the start of its text (`\"Shares\" means …`,\n * `**Business Day** shall mean …`, `Completion: the date …`), the order of a glossary, and the hidden\n * Word bookmark a term links to. Shared by the editor's Defined Terms and ARBI's headless tools.\n */\n\n/** A definition's text split into its term and the rest. */\nexport interface ParsedDefinition {\n /** The term as written, without quotes or emphasis (`Business Day`). */\n term: string\n /** What follows the term, its connecting word included (`means a day …`, `: the date …`). */\n rest: string\n /** Just the meaning, the connecting word dropped (`a day …`). */\n meaning: string\n}\n\n/** A definition as the commands insert it: the id of its fact, its term and the rest. */\nexport interface DefinitionText extends Pick<ParsedDefinition, 'term' | 'rest'> {\n id: string\n}\n\nconst QUOTES = `\"“”„‟'‘’‚‛«»`\nconst OPEN_CLOSE: Record<string, string> = {\n '\"': '\"',\n '“': '”',\n '„': '“',\n '‟': '”',\n \"'\": \"'\",\n '‘': '’',\n '‚': '‘',\n '‛': '’',\n '«': '»',\n}\nconst EMPHASIS = /^(\\*\\*|__|\\*|_)/\n/** The words (or punctuation) joining a term to its meaning, longest first. */\nconst CONNECTORS =\n /^(?:shall\\s+(?:have\\s+the\\s+meaning|mean|include)s?|ha(?:s|ve)\\s+the\\s+meanings?|means?\\s+and\\s+includes?|means|mean|includes|include|[:—–]|\\s-\\s|-\\s)/i\nconst MAX_TERM_LENGTH = 80\nconst MAX_TERM_WORDS = 10\n\n/** Markdown emphasis and escapes dropped, whitespace collapsed. */\nfunction plain(text: string): string {\n return text\n .replace(/\\\\([\\\\`*_{}[\\]()#+\\-.!\"'])/g, '$1')\n .replace(/(\\*\\*|__)(.+?)\\1/g, '$2')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\n/** The matching key of a term: case and whitespace folded, quotes and emphasis dropped. */\nexport function termKey(term: string): string {\n return plain(term)\n .replace(new RegExp(`[${QUOTES}*_]`, 'g'), '')\n .replace(/\\s+/g, ' ')\n .trim()\n .toLowerCase()\n}\n\nconst validTerm = (term: string) =>\n term.length > 0 &&\n term.length <= MAX_TERM_LENGTH &&\n term.split(/\\s+/).length <= MAX_TERM_WORDS &&\n /\\p{L}/u.test(term)\n\n/** A connector at the start of `rest` (after optional whitespace): its length, or 0. */\nfunction connectorAt(rest: string): number {\n const lead = rest.length - rest.trimStart().length\n // A spaced hyphen needs its leading space, so try the raw text before the trimmed one.\n const spaced = /^\\s-\\s/.exec(rest)\n if (spaced) return spaced[0].length\n const match = CONNECTORS.exec(rest.slice(lead))\n return match ? lead + match[0].length : 0\n}\n\n/**\n * Read a definition's term from the start of its text, or `null` when it does not start with one.\n *\n * Optional straight or curly quotes and bold markers around the term are dropped; the term is what\n * comes before \"means\", \"shall mean\", \"includes\", \"has the meaning\", a colon, or a dash. A quoted\n * term is taken whole, so `\"Long-Stop Date\" means …` keeps its hyphen.\n */\nexport function parseDefinition(text: string): ParsedDefinition | null {\n let s = plain(text)\n // Leading emphasis markers (`**\"Shares\"** means`), any order with the opening quote.\n for (let i = 0; i < 3; i++) s = s.replace(EMPHASIS, '').trimStart()\n let term: string\n let rest: string\n const open = s[0]\n const close = open ? OPEN_CLOSE[open] : undefined\n if (close) {\n const end = s.indexOf(close, 1)\n if (end < 0) return null\n term = s.slice(1, end)\n rest = s.slice(end + 1)\n // Emphasis or a closing quote between the term and its connector (`\"Shares\"**`, `**\"X**\"`).\n rest = rest.replace(new RegExp(`^(?:\\\\*\\\\*|__|\\\\*|_|[${QUOTES}])+`), '')\n } else {\n // Unquoted: the term runs up to the first connector.\n const match =\n /^(.+?)(\\s+(?:shall\\s+(?:have\\s+the\\s+meaning|mean|include)s?|ha(?:s|ve)\\s+the\\s+meanings?|means?\\s+and\\s+includes?|means|includes)\\b|\\s*[:—–]|\\s+-\\s)/i.exec(\n s\n )\n if (!match) return null\n term = match[1]\n rest = s.slice(match[1].length)\n }\n term = term.replace(/(\\*\\*|__|\\*|_)/g, '').trim()\n const joint = connectorAt(rest)\n if (!joint || !validTerm(term)) return null\n const meaning = rest.slice(joint).trim()\n if (!meaning) return null\n return { term, rest: rest.replace(/^\\s+/, ' ').replace(/^ (?=[:—–])/, ''), meaning }\n}\n\n/** Alphabetical order of terms, as a reader sorts a glossary (case and accents folded). */\nexport const compareTerms = (a: string, b: string) =>\n a.localeCompare(b, undefined, { sensitivity: 'base', numeric: true })\n\n/** Prefix of every bookmark a defined term links to: hidden in Word (leading underscore). */\nexport const DEFINITION_BOOKMARK_PREFIX = '_dt_'\nconst MAX_BOOKMARK_LENGTH = 40\n\n/**\n * The Word bookmark of a definition: `_dt_` and the fact id in valid bookmark characters\n * (`fct-AbC12345` → `_dt_fct_AbC12345`), at most 40 characters as Word requires.\n */\nexport function definitionBookmark(factId: string): string {\n return (DEFINITION_BOOKMARK_PREFIX + factId.replace(/[^A-Za-z0-9_]/g, '_')).slice(\n 0,\n MAX_BOOKMARK_LENGTH\n )\n}\n\n/** The fact id a definition bookmark names, or `null` for any other bookmark. */\nexport function definitionIdOfBookmark(name: string): string | null {\n const match = /^_dt_([a-z]{3})_([A-Za-z0-9]+)$/.exec(name)\n return match ? `${match[1]}-${match[2]}` : null\n}\n","/**\n * The minimal WordprocessingML a command writes: escaped text, a `word/document.xml` and\n * `word/styles.xml` around it, and the `.docx` package that carries them (a paste fragment).\n */\n\nimport { strToU8, zipSync } from 'fflate'\n\nexport const W_NAMESPACES =\n 'xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"'\n\n// Text that is not allowed in XML 1.0 at all (control characters) is dropped, not escaped.\n// eslint-disable-next-line no-control-regex\nconst INVALID_XML = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F]/g\n\n/** Text as XML character data or an attribute value. */\nexport const esc = (text: string) =>\n text\n .replace(INVALID_XML, '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n\n/** A `word/document.xml` around body content (paragraphs, tables, then the section). */\nexport const documentXml = (body: string) =>\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<w:document ${W_NAMESPACES}><w:body>${body}</w:body></w:document>`\n\n/** `word/styles.xml` holding the given style definitions. */\nexport const stylesXml = (defaults: string, styles: string) =>\n `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<w:styles xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\">${defaults}${styles}</w:styles>`\n\nconst CONTENT_TYPES = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Default Extension=\"xml\" ContentType=\"application/xml\"/><Override PartName=\"/word/document.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\"/><Override PartName=\"/word/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml\"/><Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/></Types>`\n\nconst ROOT_RELS = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"word/document.xml\"/><Relationship Id=\"rId2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\" Target=\"docProps/core.xml\"/></Relationships>`\n\n/** `word/_rels/document.xml.rels`: the styles part, then any further relationships given. */\nconst documentRels = (more = '') => `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\" Target=\"styles.xml\"/>${more}</Relationships>`\n\n/** A relationship to an external hyperlink target, for `w:hyperlink r:id`. */\nexport const hyperlinkRelationship = (id: string, url: string) =>\n `<Relationship Id=\"${esc(id)}\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\"${esc(url)}\" TargetMode=\"External\"/>`\n\nconst coreXml = (\n title: string,\n at: string\n) => `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<cp:coreProperties xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><dc:title>${esc(\n title\n)}</dc:title><dcterms:created xsi:type=\"dcterms:W3CDTF\">${at}</dcterms:created><dcterms:modified xsi:type=\"dcterms:W3CDTF\">${at}</dcterms:modified></cp:coreProperties>`\n\n/** The minimal `.docx` package: a document, its styles, and its title and time. */\nexport function docxPackage(parts: {\n document: string\n styles: string\n title: string\n /** ISO time; default now. */\n at?: string\n /** Further `word/document.xml` relationships (e.g. {@link hyperlinkRelationship}). */\n relationships?: string\n}): Uint8Array {\n const at = new Date(parts.at ?? Date.now()).toISOString().replace(/\\.\\d{3}Z$/, 'Z')\n return zipSync({\n '[Content_Types].xml': strToU8(CONTENT_TYPES),\n '_rels/.rels': strToU8(ROOT_RELS),\n 'docProps/core.xml': strToU8(coreXml(parts.title, at)),\n 'word/document.xml': strToU8(parts.document),\n 'word/styles.xml': strToU8(parts.styles),\n 'word/_rels/document.xml.rels': strToU8(documentRels(parts.relationships)),\n })\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arbidocs/coedit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.111",
|
|
4
4
|
"description": "Real-time collaboration for the ARBI native DOCX editor: a Yjs mirror of the canonical OOXML package, session, and server runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -37,6 +37,11 @@
|
|
|
37
37
|
"types": "./dist/commands.d.ts",
|
|
38
38
|
"import": "./dist/commands.js",
|
|
39
39
|
"require": "./dist/commands.cjs"
|
|
40
|
+
},
|
|
41
|
+
"./ooxml": {
|
|
42
|
+
"types": "./dist/ooxml.d.ts",
|
|
43
|
+
"import": "./dist/ooxml.js",
|
|
44
|
+
"require": "./dist/ooxml.cjs"
|
|
40
45
|
}
|
|
41
46
|
},
|
|
42
47
|
"files": [
|