diff --git a/.editorconfig b/.editorconfig index 9b7352176a..46c1eafce3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,7 @@ indent_style = space indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true +end_of_line = lf [*.md] max_line_length = off diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml new file mode 100644 index 0000000000..a063d1ee1d --- /dev/null +++ b/.github/workflows/default.yml @@ -0,0 +1,104 @@ +name: Default +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@master + - name: Install Dependencies + run: npm ci + - name: Build + run: npm run build + - name: Archive Production Artifact + uses: actions/upload-artifact@master + with: + name: build + path: build + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@master + - name: Install Dependencies + run: npm ci + - name: Test + run: npm run test.coverage + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@master + - name: Install Dependencies + run: npm ci + - name: Lint + run: npm run lint + prettier: + name: Prettier + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@master + - name: Install Dependencies + run: npm ci + - name: Prettier + run: npm run style + demos: + name: Run Demos + needs: [build] + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@master + - name: Install Dependencies + run: npm ci + - name: Download Artifact + uses: actions/download-artifact@master + with: + name: build + path: build + - name: Run demos + run: | + npm run ts-node -- ./demo/1-basic.ts + npm run ts-node -- ./demo/2-declaritive-styles.ts + npm run ts-node -- ./demo/3-numbering-and-bullet-points.ts + npm run ts-node -- ./demo/4-basic-table.ts + npm run ts-node -- ./demo/5-images.ts + npm run ts-node -- ./demo/6-page-borders.ts + npm run ts-node -- ./demo/7-landscape.ts + npm run ts-node -- ./demo/8-header-footer.ts + npm run ts-node -- ./demo/9-images-in-header-and-footer.ts + npm run ts-node -- ./demo/10-my-cv.ts + npm run ts-node -- ./demo/11-declaritive-styles-2.ts + npm run ts-node -- ./demo/12-scaling-images.ts + npm run ts-node -- ./demo/13-xml-styles.ts + npm run ts-node -- ./demo/14-page-numbers.ts + npm run ts-node -- ./demo/15-page-break-before.ts + npm run ts-node -- ./demo/16-multiple-sections.ts + npm run ts-node -- ./demo/17-footnotes.ts + npm run ts-node -- ./demo/18-image-from-buffer.ts + npm run ts-node -- ./demo/19-export-to-base64.ts + npm run ts-node -- ./demo/20-table-cell-borders.ts + npm run ts-node -- ./demo/21-bookmarks.ts + npm run ts-node -- ./demo/22-right-to-left-text.ts + npm run ts-node -- ./demo/23-base64-images.ts + npm run ts-node -- ./demo/24-images-to-table-cell.ts + npm run ts-node -- ./demo/26-paragraph-borders.ts + npm run ts-node -- ./demo/27-declaritive-styles-3.ts + npm run ts-node -- ./demo/28-table-of-contents.ts + npm run ts-node -- ./demo/29-numbered-lists.ts + npm run ts-node -- ./demo/30-template-document.ts + npm run ts-node -- ./demo/31-tables.ts + npm run ts-node -- ./demo/32-merge-and-shade-table-cells.ts + npm run ts-node -- ./demo/33-sequential-captions.ts + npm run ts-node -- ./demo/34-floating-tables.ts diff --git a/README.md b/README.md index 9dd52e1a94..2460a5d498 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ [![NPM version][npm-image]][npm-url] [![Downloads per month][downloads-image]][downloads-url] [![Build Status][travis-image]][travis-url] +[![GitHub Action Workflow Status][github-actions-workflow-image]][github-actions-workflow-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Known Vulnerabilities][snky-image]][snky-url] [![Chat on Gitter][gitter-image]][gitter-url] @@ -67,7 +68,7 @@ Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/ # Examples -Check the `examples` section in the [documentation](https://docx.js.org/#/examples) and the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples. +Check the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples. # Contributing @@ -85,6 +86,7 @@ Read the contribution guidelines [here](https://docx.js.org/#/contribution-guide [drawing](https://www.beekast.com/) [drawing](https://herraizsoto.com/) [drawing](http://www.ativer.com.br/) +[drawing](https://www.arity.co/) ...and many more! @@ -102,6 +104,8 @@ Made with 💖 [downloads-url]: https://npmjs.org/package/docx [travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master [travis-url]: https://travis-ci.org/dolanmiu/docx +[github-actions-workflow-image]: https://github.com/dolanmiu/docx/workflows/Default/badge.svg +[github-actions-workflow-url]: https://github.com/dolanmiu/docx/actions [daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io [daviddm-url]: https://david-dm.org/dolanmiu/docx [snky-image]: https://snyk.io/test/github/dolanmiu/docx/badge.svg diff --git a/demo/10-my-cv.ts b/demo/10-my-cv.ts index b91ca318bb..484a9c249c 100644 --- a/demo/10-my-cv.ts +++ b/demo/10-my-cv.ts @@ -204,7 +204,10 @@ class DocumentCreator { alignment: AlignmentType.CENTER, children: [ new TextRun(`Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`), - new TextRun("Address: 58 Elm Avenue, Kent ME4 6ER, UK").break(), + new TextRun({ + text: "Address: 58 Elm Avenue, Kent ME4 6ER, UK", + break: 1, + }), ], }); } diff --git a/demo/11-declaritive-styles-2.ts b/demo/11-declaritive-styles-2.ts index 3a0154a0fb..2f4ed78098 100644 --- a/demo/11-declaritive-styles-2.ts +++ b/demo/11-declaritive-styles-2.ts @@ -3,6 +3,7 @@ import * as fs from "fs"; import { AlignmentType, + convertInchesToTwip, Document, Footer, HeadingLevel, @@ -18,13 +19,8 @@ import { const doc = new Document({ styles: { - paragraphStyles: [ - { - id: "Heading1", - name: "Heading 1", - basedOn: "Normal", - next: "Normal", - quickFormat: true, + default: { + heading1: { run: { font: "Calibri", size: 52, @@ -40,12 +36,7 @@ const doc = new Document({ spacing: { line: 340 }, }, }, - { - id: "Heading2", - name: "Heading 2", - basedOn: "Normal", - next: "Normal", - quickFormat: true, + heading2: { run: { font: "Calibri", size: 26, @@ -55,12 +46,7 @@ const doc = new Document({ spacing: { line: 340 }, }, }, - { - id: "Heading3", - name: "Heading 3", - basedOn: "Normal", - next: "Normal", - quickFormat: true, + heading3: { run: { font: "Calibri", size: 26, @@ -70,12 +56,7 @@ const doc = new Document({ spacing: { line: 276 }, }, }, - { - id: "Heading4", - name: "Heading 4", - basedOn: "Normal", - next: "Normal", - quickFormat: true, + heading4: { run: { font: "Calibri", size: 26, @@ -85,6 +66,8 @@ const doc = new Document({ alignment: AlignmentType.JUSTIFIED, }, }, + }, + paragraphStyles: [ { id: "normalPara", name: "Normal Para", @@ -128,7 +111,7 @@ const doc = new Document({ }, paragraph: { spacing: { line: 276 }, - indent: { left: 720 }, + indent: { left: convertInchesToTwip(0.5) }, }, }, { @@ -139,12 +122,6 @@ const doc = new Document({ spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 }, }, }, - { - id: "ListParagraph", - name: "List Paragraph", - basedOn: "Normal", - quickFormat: true, - }, ], }, }); diff --git a/demo/2-declaritive-styles.ts b/demo/2-declaritive-styles.ts index c34eae9628..deada8198a 100644 --- a/demo/2-declaritive-styles.ts +++ b/demo/2-declaritive-styles.ts @@ -1,20 +1,25 @@ // Example on how to customise the look at feel using Styles // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType } from "../build"; +import { + AlignmentType, + convertInchesToTwip, + Document, + HeadingLevel, + LevelFormat, + Packer, + Paragraph, + TextRun, + UnderlineType, +} from "../build"; const doc = new Document({ creator: "Clippy", title: "Sample Document", description: "A brief example of using docx", styles: { - paragraphStyles: [ - { - id: "Heading1", - name: "Heading 1", - basedOn: "Normal", - next: "Normal", - quickFormat: true, + default: { + heading1: { run: { size: 28, bold: true, @@ -27,12 +32,7 @@ const doc = new Document({ }, }, }, - { - id: "Heading2", - name: "Heading 2", - basedOn: "Normal", - next: "Normal", - quickFormat: true, + heading2: { run: { size: 26, bold: true, @@ -48,6 +48,13 @@ const doc = new Document({ }, }, }, + listParagraph: { + run: { + color: "#FF0000", + }, + }, + }, + paragraphStyles: [ { id: "aside", name: "Aside", @@ -59,7 +66,7 @@ const doc = new Document({ }, paragraph: { indent: { - left: 720, + left: convertInchesToTwip(0.5), }, spacing: { line: 276, @@ -75,12 +82,6 @@ const doc = new Document({ spacing: { line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 }, }, }, - { - id: "ListParagraph", - name: "List Paragraph", - basedOn: "Normal", - quickFormat: true, - }, ], }, numbering: { @@ -90,7 +91,7 @@ const doc = new Document({ levels: [ { level: 0, - format: "lowerLetter", + format: LevelFormat.LOWER_LETTER, text: "%1)", alignment: AlignmentType.LEFT, }, @@ -170,6 +171,17 @@ doc.addSection({ }), ], }), + new Paragraph({ + style: "Strong", + children: [ + new TextRun({ + text: "Strong Style", + }), + new TextRun({ + text: " - Very strong.", + }), + ], + }), ], }); diff --git a/demo/21-bookmarks.ts b/demo/21-bookmarks.ts index 1ad50eb9b1..632fcb212f 100644 --- a/demo/21-bookmarks.ts +++ b/demo/21-bookmarks.ts @@ -1,7 +1,7 @@ // This demo shows how to create bookmarks then link to them with internal hyperlinks // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Bookmark, Document, HeadingLevel, HyperlinkRef, HyperlinkType, Packer, PageBreak, Paragraph } from "../build"; +import { Bookmark, Document, Footer, HeadingLevel, InternalHyperlink, Packer, PageBreak, Paragraph, TextRun } from "../build"; const LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mi velit, convallis convallis scelerisque nec, faucibus nec leo. Phasellus at posuere mauris, tempus dignissim velit. Integer et tortor dolor. Duis auctor efficitur mattis. Vivamus ut metus accumsan tellus auctor sollicitudin venenatis et nibh. Cras quis massa ac metus fringilla venenatis. Proin rutrum mauris purus, ut suscipit magna consectetur id. Integer consectetur sollicitudin ante, vitae faucibus neque efficitur in. Praesent ultricies nibh lectus. Mauris pharetra id odio eget iaculis. Duis dictum, risus id pellentesque rutrum, lorem quam malesuada massa, quis ullamcorper turpis urna a diam. Cras vulputate metus vel massa porta ullamcorper. Etiam porta condimentum nulla nec tristique. Sed nulla urna, pharetra non tortor sed, sollicitudin molestie diam. Maecenas enim leo, feugiat eget vehicula id, sollicitudin vitae ante."; @@ -10,15 +10,26 @@ const doc = new Document({ creator: "Clippy", title: "Sample Document", description: "A brief example of using docx with bookmarks and internal hyperlinks", - hyperlinks: { - myAnchorId: { - text: "Hyperlink", - type: HyperlinkType.INTERNAL, - }, - }, }); doc.addSection({ + footers: { + default: new Footer({ + children: [ + new Paragraph({ + children: [ + new InternalHyperlink({ + child: new TextRun({ + text: "Click here!", + style: "Hyperlink", + }), + anchor: "myAnchorId", + }), + ], + }), + ], + }), + }, children: [ new Paragraph({ heading: HeadingLevel.HEADING_1, @@ -30,7 +41,15 @@ doc.addSection({ children: [new PageBreak()], }), new Paragraph({ - children: [new HyperlinkRef("myAnchorId")], + children: [ + new InternalHyperlink({ + child: new TextRun({ + text: "Anchor Text", + style: "Hyperlink", + }), + anchor: "myAnchorId", + }), + ], }), ], }); diff --git a/demo/27-declaritive-styles-3.ts b/demo/27-declaritive-styles-3.ts index 8419467799..8737c69101 100644 --- a/demo/27-declaritive-styles-3.ts +++ b/demo/27-declaritive-styles-3.ts @@ -1,7 +1,7 @@ // Custom styles using JavaScript configuration // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, HeadingLevel, Packer, Paragraph, UnderlineType } from "../build"; +import { Document, convertInchesToTwip, HeadingLevel, Packer, Paragraph, UnderlineType } from "../build"; const doc = new Document({ styles: { @@ -17,7 +17,7 @@ const doc = new Document({ }, paragraph: { indent: { - left: 720, + left: convertInchesToTwip(0.5), }, spacing: { line: 276, diff --git a/demo/29-numbered-lists.ts b/demo/29-numbered-lists.ts index 5cd3966015..77f50381d1 100644 --- a/demo/29-numbered-lists.ts +++ b/demo/29-numbered-lists.ts @@ -1,7 +1,7 @@ // Numbered lists // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { AlignmentType, Document, Packer, Paragraph } from "../build"; +import { AlignmentType, convertInchesToTwip, Document, LevelFormat, Packer, Paragraph } from "../build"; const doc = new Document({ numbering: { @@ -10,12 +10,12 @@ const doc = new Document({ levels: [ { level: 0, - format: "upperRoman", + format: LevelFormat.UPPER_ROMAN, text: "%1", alignment: AlignmentType.START, style: { paragraph: { - indent: { left: 720, hanging: 260 }, + indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) }, }, }, }, @@ -26,18 +26,34 @@ const doc = new Document({ levels: [ { level: 0, - format: "decimal", + format: LevelFormat.DECIMAL, text: "%1", alignment: AlignmentType.START, style: { paragraph: { - indent: { left: 720, hanging: 260 }, + indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) }, }, }, }, ], reference: "my-number-numbering-reference", }, + { + levels: [ + { + level: 0, + format: LevelFormat.DECIMAL_ZERO, + text: "[%1]", + alignment: AlignmentType.START, + style: { + paragraph: { + indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) }, + }, + }, + }, + ], + reference: "padded-numbering-reference", + }, ], }, }); @@ -109,6 +125,139 @@ doc.addSection({ level: 0, }, }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "test", + numbering: { + reference: "padded-numbering-reference", + level: 0, + }, + }), ], }); diff --git a/demo/3-numbering-and-bullet-points.ts b/demo/3-numbering-and-bullet-points.ts index 58c0a4f5a6..375c1439a7 100644 --- a/demo/3-numbering-and-bullet-points.ts +++ b/demo/3-numbering-and-bullet-points.ts @@ -1,7 +1,7 @@ // Numbering and bullet points example // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { AlignmentType, Document, Packer, Paragraph } from "../build"; +import { AlignmentType, convertInchesToTwip, Document, LevelFormat, Packer, Paragraph } from "../build"; const doc = new Document({ numbering: { @@ -11,40 +11,40 @@ const doc = new Document({ levels: [ { level: 0, - format: "upperRoman", + format: LevelFormat.UPPER_ROMAN, text: "%1", alignment: AlignmentType.START, style: { paragraph: { - indent: { left: 720, hanging: 260 }, + indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.18) }, }, }, }, { level: 1, - format: "decimal", + format: LevelFormat.DECIMAL, text: "%2.", alignment: AlignmentType.START, style: { paragraph: { - indent: { left: 1440, hanging: 980 }, + indent: { left: convertInchesToTwip(1), hanging: convertInchesToTwip(0.68) }, }, }, }, { level: 2, - format: "lowerLetter", + format: LevelFormat.LOWER_LETTER, text: "%3)", alignment: AlignmentType.START, style: { paragraph: { - indent: { left: 2160, hanging: 1700 }, + indent: { left: convertInchesToTwip(1.5), hanging: convertInchesToTwip(1.18) }, }, }, }, { level: 3, - format: "upperLetter", + format: LevelFormat.UPPER_LETTER, text: "%4)", alignment: AlignmentType.START, style: { diff --git a/demo/32-merge-and-shade-table-cells.ts b/demo/32-merge-and-shade-table-cells.ts index f12350988e..284b456e05 100644 --- a/demo/32-merge-and-shade-table-cells.ts +++ b/demo/32-merge-and-shade-table-cells.ts @@ -2,7 +2,20 @@ // Also includes an example on how to center tables // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { AlignmentType, BorderStyle, Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; +import { + AlignmentType, + BorderStyle, + convertInchesToTwip, + Document, + HeadingLevel, + Packer, + Paragraph, + ShadingType, + Table, + TableCell, + TableRow, + WidthType, +} from "../build"; const doc = new Document(); @@ -37,10 +50,10 @@ const table2 = new Table({ new TableCell({ children: [new Paragraph("World")], margins: { - top: 1000, - bottom: 1000, - left: 1000, - right: 1000, + top: convertInchesToTwip(0.69), + bottom: convertInchesToTwip(0.69), + left: convertInchesToTwip(0.69), + right: convertInchesToTwip(0.69), }, columnSpan: 3, }), @@ -64,7 +77,7 @@ const table2 = new Table({ size: 100, type: WidthType.AUTO, }, - columnWidths: [1000, 1000, 1000], + columnWidths: [convertInchesToTwip(0.69), convertInchesToTwip(0.69), convertInchesToTwip(0.69)], }); const table3 = new Table({ @@ -119,14 +132,14 @@ const table3 = new Table({ }), ], width: { - size: 7000, + size: convertInchesToTwip(4.86), type: WidthType.DXA, }, margins: { - top: 400, - bottom: 400, - right: 400, - left: 400, + top: convertInchesToTwip(0.27), + bottom: convertInchesToTwip(0.27), + right: convertInchesToTwip(0.27), + left: convertInchesToTwip(0.27), }, }); @@ -355,9 +368,7 @@ const table8 = new Table({ ], }), new TableRow({ - children: [ - new TableCell({ children: [new Paragraph("4,1")] }), - ], + children: [new TableCell({ children: [new Paragraph("4,1")] })], }), ], width: { diff --git a/demo/35-hyperlinks.ts b/demo/35-hyperlinks.ts index 65ed9390d4..c9ef863aa4 100644 --- a/demo/35-hyperlinks.ts +++ b/demo/35-hyperlinks.ts @@ -1,32 +1,75 @@ // Example on how to add hyperlinks to websites // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, HyperlinkRef, HyperlinkType, Packer, Paragraph, Media } from "../build"; +import { Document, ExternalHyperlink, Footer, Media, Packer, Paragraph, TextRun } from "../build"; -const doc = new Document({ - hyperlinks: { - myCoolLink: { - link: "http://www.example.com", - text: "Hyperlink", - type: HyperlinkType.EXTERNAL, - }, - myOtherLink: { - link: "http://www.google.com", - text: "Google Link", - type: HyperlinkType.EXTERNAL, - }, - }, -}); +const doc = new Document({}); const image1 = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg")); doc.addSection({ + footers: { + default: new Footer({ + children: [ + new Paragraph({ + children: [ + new TextRun("Click here for the "), + new ExternalHyperlink({ + child: new TextRun({ + text: "Footer external hyperlink", + style: "Hyperlink", + }), + link: "http://www.example.com", + }), + ], + }), + ], + }), + }, + headers: { + default: new Footer({ + children: [ + new Paragraph({ + children: [ + new TextRun("Click here for the "), + new ExternalHyperlink({ + child: new TextRun({ + text: "Header external hyperlink", + style: "Hyperlink", + }), + link: "http://www.google.com", + }), + ], + }), + ], + }), + }, children: [ new Paragraph({ - children: [new HyperlinkRef("myCoolLink")], + children: [ + new ExternalHyperlink({ + child: new TextRun({ + text: "Anchor Text", + style: "Hyperlink", + }), + link: "http://www.example.com", + }), + ], }), new Paragraph({ - children: [image1, new HyperlinkRef("myOtherLink")], + children: [ + new ExternalHyperlink({ + child: image1, + link: "http://www.google.com", + }), + new ExternalHyperlink({ + child: new TextRun({ + text: "BBC News Link", + style: "Hyperlink", + }), + link: "https://www.bbc.co.uk/news", + }), + ], }), ], }); diff --git a/demo/46-shading-text.ts b/demo/46-shading-text.ts index 61f3f3d984..e662e6dffe 100644 --- a/demo/46-shading-text.ts +++ b/demo/46-shading-text.ts @@ -28,6 +28,18 @@ doc.addSection({ }), ], }), + new Paragraph({ + shading: { + type: ShadingType.DIAGONAL_CROSS, + color: "00FFFF", + fill: "FF0000", + }, + children: [ + new TextRun({ + text: "Hello World for entire paragraph", + }), + ], + }), ], }), }, diff --git a/demo/49-table-borders.ts b/demo/49-table-borders.ts index 2aebf1feb1..75dd89c49f 100644 --- a/demo/49-table-borders.ts +++ b/demo/49-table-borders.ts @@ -1,7 +1,19 @@ -// Add custom borders to the table itself +// Add custom borders and no-borders to the table itself // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { BorderStyle, Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; +import { + BorderStyle, + Document, + HeadingLevel, + Packer, + Paragraph, + Table, + TableBorders, + TableCell, + TableRow, + TextDirection, + VerticalAlign, +} from "../build"; const doc = new Document(); @@ -10,6 +22,28 @@ const table = new Table({ new TableRow({ children: [ new TableCell({ + borders: { + top: { + style: BorderStyle.DASH_SMALL_GAP, + size: 1, + color: "red", + }, + bottom: { + style: BorderStyle.DASH_SMALL_GAP, + size: 1, + color: "red", + }, + left: { + style: BorderStyle.DASH_SMALL_GAP, + size: 1, + color: "red", + }, + right: { + style: BorderStyle.DASH_SMALL_GAP, + size: 1, + color: "red", + }, + }, children: [new Paragraph("Hello")], }), new TableCell({ @@ -30,7 +64,103 @@ const table = new Table({ ], }); -doc.addSection({ children: [table] }); +// Using the no-border convenience object. It is the same as writing this manually: +// const borders = { +// top: { +// style: BorderStyle.NONE, +// size: 0, +// color: "auto", +// }, +// bottom: { +// style: BorderStyle.NONE, +// size: 0, +// color: "auto", +// }, +// left: { +// style: BorderStyle.NONE, +// size: 0, +// color: "auto", +// }, +// right: { +// style: BorderStyle.NONE, +// size: 0, +// color: "auto", +// }, +// insideHorizontal: { +// style: BorderStyle.NONE, +// size: 0, +// color: "auto", +// }, +// insideVertical: { +// style: BorderStyle.NONE, +// size: 0, +// color: "auto", +// }, +// }; +const noBorderTable = new Table({ + borders: TableBorders.NONE, + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph({}), new Paragraph({})], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [new Paragraph({}), new Paragraph({})], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [new Paragraph({ text: "bottom to top" }), new Paragraph({})], + textDirection: TextDirection.BOTTOM_TO_TOP_LEFT_TO_RIGHT, + }), + new TableCell({ + children: [new Paragraph({ text: "top to bottom" }), new Paragraph({})], + textDirection: TextDirection.TOP_TO_BOTTOM_RIGHT_TO_LEFT, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [ + new Paragraph({ + text: + "Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah", + heading: HeadingLevel.HEADING_1, + }), + ], + }), + new TableCell({ + children: [ + new Paragraph({ + text: "This text should be in the middle of the cell", + }), + ], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [ + new Paragraph({ + text: "Text above should be vertical from bottom to top", + }), + ], + verticalAlign: VerticalAlign.CENTER, + }), + new TableCell({ + children: [ + new Paragraph({ + text: "Text above should be vertical from top to bottom", + }), + ], + verticalAlign: VerticalAlign.CENTER, + }), + ], + }), + ], +}); + +doc.addSection({ children: [table, new Paragraph("Hello"), noBorderTable] }); Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); diff --git a/demo/5-images.ts b/demo/5-images.ts index 260fd25f15..3c752d9a58 100644 --- a/demo/5-images.ts +++ b/demo/5-images.ts @@ -22,6 +22,7 @@ const image4 = Media.addImage(doc, fs.readFileSync("./demo/images/parrots.bmp")) const image5 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif")); const image6 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"), 200, 200, { floating: { + zIndex: 10, horizontalPosition: { offset: 1014400, }, @@ -33,6 +34,7 @@ const image6 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"), 2 const image7 = Media.addImage(doc, fs.readFileSync("./demo/images/cat.jpg"), 200, 200, { floating: { + zIndex: 5, horizontalPosition: { relative: HorizontalPositionRelativeFrom.PAGE, align: HorizontalPositionAlign.RIGHT, diff --git a/demo/51-character-styles.ts b/demo/51-character-styles.ts index 3230a7c86f..a6064a4ddc 100644 --- a/demo/51-character-styles.ts +++ b/demo/51-character-styles.ts @@ -15,6 +15,14 @@ const doc = new Document({ italics: true, }, }, + { + id: "strong", + name: "Strong", + basedOn: "Normal", + run: { + bold: true, + }, + }, ], }, }); @@ -29,6 +37,18 @@ doc.addSection({ }), ], }), + new Paragraph({ + children: [ + new TextRun({ + text: "First Word", + style: "strong", + }), + new TextRun({ + text: + " - Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", + }), + ], + }), ], }); diff --git a/demo/54-track-revisions.ts b/demo/54-track-revisions.ts new file mode 100644 index 0000000000..e035fbb18a --- /dev/null +++ b/demo/54-track-revisions.ts @@ -0,0 +1,146 @@ +// Track Revisions aka. "Track Changes" +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { + AlignmentType, + DeletedTextRun, + Document, + Footer, + FootnoteReferenceRun, + InsertedTextRun, + Packer, + PageNumber, + Paragraph, + ShadingType, + TextRun, +} from "../build"; + +/* + For reference, see + - https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.insertedrun + - https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.deletedrun + + The method `addTrackRevisions()` adds an element `` to the `settings.xml` file. This specifies that the application shall track *new* revisions made to the existing document. + See also https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.wordprocessing.trackrevisions + + Note that this setting enables to track *new changes* after teh file is generated, so this example will still show inserted and deleted text runs when you remove it. +*/ + +const doc = new Document({ + footnotes: [ + new Paragraph({ + children: [ + new TextRun("This is a footnote"), + new DeletedTextRun({ + text: " with some extra text which was deleted", + id: 0, + author: "Firstname Lastname", + date: "2020-10-06T09:05:00Z", + }), + new InsertedTextRun({ + text: " and new content", + id: 1, + author: "Firstname Lastname", + date: "2020-10-06T09:05:00Z", + }), + ], + }), + ], + features: { + trackRevisions: true, + }, +}); + +const paragraph = new Paragraph({ + children: [ + new TextRun("This is a simple demo "), + new TextRun({ + text: "on how to ", + }), + new InsertedTextRun({ + text: "mark a text as an insertion ", + id: 0, + author: "Firstname Lastname", + date: "2020-10-06T09:00:00Z", + }), + new DeletedTextRun({ + text: "or a deletion.", + id: 1, + author: "Firstname Lastname", + date: "2020-10-06T09:00:00Z", + }), + ], +}); + +doc.addSection({ + properties: {}, + children: [ + paragraph, + new Paragraph({ + children: [ + new TextRun("This is a demo "), + new DeletedTextRun({ + break: 1, + text: "in order", + color: "red", + bold: true, + size: 24, + font: { + name: "Garamond", + }, + shading: { + type: ShadingType.REVERSE_DIAGONAL_STRIPE, + color: "00FFFF", + fill: "FF0000", + }, + id: 2, + author: "Firstname Lastname", + date: "2020-10-06T09:00:00Z", + }), + new InsertedTextRun({ + text: "to show how to ", + bold: false, + id: 3, + author: "Firstname Lastname", + date: "2020-10-06T09:05:00Z", + }), + new TextRun({ + bold: true, + children: ["\tuse Inserted and Deleted TextRuns.", new FootnoteReferenceRun(1)], + }), + ], + }), + ], + footers: { + default: new Footer({ + children: [ + new Paragraph({ + alignment: AlignmentType.CENTER, + children: [ + new TextRun("Awesome LLC"), + new TextRun({ + children: ["Page Number: ", PageNumber.CURRENT], + }), + new DeletedTextRun({ + children: [" to ", PageNumber.TOTAL_PAGES], + id: 4, + author: "Firstname Lastname", + date: "2020-10-06T09:05:00Z", + }), + new InsertedTextRun({ + children: [" from ", PageNumber.TOTAL_PAGES], + bold: true, + id: 5, + author: "Firstname Lastname", + date: "2020-10-06T09:05:00Z", + }), + ], + }), + ], + }), + }, +}); + +Packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/55-math.ts b/demo/55-math.ts new file mode 100644 index 0000000000..2a240986d0 --- /dev/null +++ b/demo/55-math.ts @@ -0,0 +1,294 @@ +// Simple example to add text to a document +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { + Document, + Math, + MathAngledBrackets, + MathCurlyBrackets, + MathFraction, + MathFunction, + MathPreSubSuperScript, + MathRadical, + MathRoundBrackets, + MathRun, + MathSquareBrackets, + MathSubScript, + MathSubSuperScript, + MathSum, + MathSuperScript, + Packer, + Paragraph, + TextRun, +} from "../build"; + +const doc = new Document(); + +doc.addSection({ + properties: {}, + children: [ + new Paragraph({ + children: [ + new Math({ + children: [ + new MathRun("2+2"), + new MathFraction({ + numerator: [new MathRun("hi")], + denominator: [new MathRun("2")], + }), + ], + }), + new TextRun({ + text: "Foo Bar", + bold: true, + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathFraction({ + numerator: [ + new MathRun("1"), + new MathRadical({ + children: [new MathRun("2")], + }), + ], + denominator: [new MathRun("2")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathSum({ + children: [new MathRun("test")], + }), + new MathSum({ + children: [ + new MathSuperScript({ + children: [new MathRun("e")], + superScript: [new MathRun("2")], + }), + ], + subScript: [new MathRun("i")], + }), + new MathSum({ + children: [ + new MathRadical({ + children: [new MathRun("i")], + }), + ], + subScript: [new MathRun("i")], + superScript: [new MathRun("10")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathSuperScript({ + children: [new MathRun("test")], + superScript: [new MathRun("hello")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathSubScript({ + children: [new MathRun("test")], + subScript: [new MathRun("hello")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathSubScript({ + children: [new MathRun("x")], + subScript: [ + new MathSuperScript({ + children: [new MathRun("y")], + superScript: [new MathRun("2")], + }), + ], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathSubSuperScript({ + children: [new MathRun("test")], + superScript: [new MathRun("hello")], + subScript: [new MathRun("world")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathPreSubSuperScript({ + children: [new MathRun("test")], + superScript: [new MathRun("hello")], + subScript: [new MathRun("world")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathSubScript({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], + subScript: [new MathRun("4")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathSubScript({ + children: [ + new MathRadical({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], + degree: [new MathRun("4")], + }), + ], + subScript: [new MathRun("x")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathRadical({ + children: [new MathRun("4")], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathFunction({ + name: [ + new MathSuperScript({ + children: [new MathRun("cos")], + superScript: [new MathRun("-1")], + }), + ], + children: [new MathRun("100")], + }), + new MathRun("×"), + new MathFunction({ + name: [new MathRun("sin")], + children: [new MathRun("360")], + }), + new MathRun("= x"), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathRoundBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], + }), + new MathSquareBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], + }), + new MathCurlyBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], + }), + new MathAngledBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], + }), + ], + }), + ], + }), + new Paragraph({ + children: [ + new Math({ + children: [ + new MathFraction({ + numerator: [ + new MathRadical({ + children: [new MathRun("4")], + }), + ], + denominator: [new MathRun("2a")], + }), + ], + }), + ], + }), + ], +}); + +Packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/56-background-color.ts b/demo/56-background-color.ts new file mode 100644 index 0000000000..6e8658fd00 --- /dev/null +++ b/demo/56-background-color.ts @@ -0,0 +1,33 @@ +// Change background colour of whole document +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun } from "../build"; + +const doc = new Document({ + background: { + color: "C45911", + }, +}); + +doc.addSection({ + properties: {}, + children: [ + new Paragraph({ + children: [ + new TextRun("Hello World"), + new TextRun({ + text: "Foo Bar", + bold: true, + }), + new TextRun({ + text: "\tGithub is the best", + bold: true, + }), + ], + }), + ], +}); + +Packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/57-add-parent-numbered-lists.ts b/demo/57-add-parent-numbered-lists.ts new file mode 100644 index 0000000000..e35cd06a27 --- /dev/null +++ b/demo/57-add-parent-numbered-lists.ts @@ -0,0 +1,88 @@ +// Numbered lists - Add parent number in sub number +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { AlignmentType, convertInchesToTwip, Document, HeadingLevel, LevelFormat, Packer, Paragraph } from "../build"; + +const doc = new Document({ + numbering: { + config: [ + { + levels: [ + { + level: 0, + format: LevelFormat.DECIMAL, + text: "%1", + alignment: AlignmentType.START, + style: { + paragraph: { + indent: { left: convertInchesToTwip(0.5), hanging: 260 }, + }, + }, + }, + { + level: 1, + format: LevelFormat.DECIMAL, + text: "%1.%2", + alignment: AlignmentType.START, + style: { + paragraph: { + indent: { left: 1.25 * convertInchesToTwip(0.5), hanging: 1.25 * 260 }, + }, + run: { + bold: true, + size: 18, + font: "Times New Roman", + }, + }, + }, + ], + reference: "my-number-numbering-reference", + }, + ], + }, +}); + +doc.addSection({ + children: [ + new Paragraph({ + text: "How to make cake", + heading: HeadingLevel.HEADING_1, + }), + new Paragraph({ + text: "Step 1 - Add sugar", + numbering: { + reference: "my-number-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "Step 2 - Add wheat", + numbering: { + reference: "my-number-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "Step 2a - Stir the wheat in a circle", + numbering: { + reference: "my-number-numbering-reference", + level: 1, + }, + }), + new Paragraph({ + text: "Step 3 - Put in oven", + numbering: { + reference: "my-number-numbering-reference", + level: 0, + }, + }), + new Paragraph({ + text: "How to make cake", + heading: HeadingLevel.HEADING_1, + }), + ], +}); + +Packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/demo/58-section-types.ts b/demo/58-section-types.ts new file mode 100644 index 0000000000..6c16b44600 --- /dev/null +++ b/demo/58-section-types.ts @@ -0,0 +1,93 @@ +// Usage of different Section Types +// Import from 'docx' rather than '../build' if you install from npm +import * as fs from "fs"; +import { Document, Packer, Paragraph, TextRun, SectionType } from "../build"; + +const doc = new Document(); + +doc.addSection({ + properties: {}, + children: [ + new Paragraph({ + children: [ + new TextRun("Hello World"), + new TextRun({ + text: "Foo Bar", + bold: true, + }), + ], + }), + ], +}); + +doc.addSection({ + properties: { + type: SectionType.CONTINUOUS, + }, + children: [ + new Paragraph({ + children: [ + new TextRun("Hello World"), + new TextRun({ + text: "Foo Bar", + bold: true, + }), + ], + }), + ], +}); + +doc.addSection({ + properties: { + type: SectionType.ODD_PAGE, + }, + children: [ + new Paragraph({ + children: [ + new TextRun("Hello World"), + new TextRun({ + text: "Foo Bar", + bold: true, + }), + ], + }), + ], +}); + +doc.addSection({ + properties: { + type: SectionType.EVEN_PAGE, + }, + children: [ + new Paragraph({ + children: [ + new TextRun("Hello World"), + new TextRun({ + text: "Foo Bar", + bold: true, + }), + ], + }), + ], +}); + +doc.addSection({ + properties: { + type: SectionType.NEXT_PAGE, + }, + children: [ + new Paragraph({ + children: [ + new TextRun("Hello World"), + new TextRun({ + text: "Foo Bar", + bold: true, + }), + ], + }), + ], +}); + +Packer.toBuffer(doc).then((buffer) => { + fs.writeFileSync("My Document.docx", buffer); +}); diff --git a/docs/README.md b/docs/README.md index 28781012fe..9d2150a441 100644 --- a/docs/README.md +++ b/docs/README.md @@ -53,11 +53,11 @@ Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); -// Done! A file called 'My First Document.docx' will be in your file system. +// Done! A file called 'My Document.docx' will be in your file system. ```

- clippy the assistant + clippy the assistant

--- diff --git a/docs/_sidebar.md b/docs/_sidebar.md index a63970dff9..2538e975c7 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -20,12 +20,16 @@ * [Tab Stops](usage/tab-stops.md) * [Table of Contents](usage/table-of-contents.md) * [Page Numbers](usage/page-numbers.md) + * [Change Tracking](usage/change-tracking.md) + * [Math](usage/math.md) * Styling * [Styling with JS](usage/styling-with-js.md) * [Styling with XML](usage/styling-with-xml.md) * Exporting * [Packers](usage/packers.md) +* Utility + + * [Convenience functions](usage/convenience-functions.md) * [Contribution Guidelines](contribution-guidelines.md) - diff --git a/docs/clippy.png b/docs/clippy.png new file mode 100644 index 0000000000..97bd4af2df Binary files /dev/null and b/docs/clippy.png differ diff --git a/docs/clippy.psd b/docs/clippy.psd new file mode 100644 index 0000000000..0d32ab12d0 Binary files /dev/null and b/docs/clippy.psd differ diff --git a/docs/images/math-example.png b/docs/images/math-example.png new file mode 100644 index 0000000000..c92f8806f9 Binary files /dev/null and b/docs/images/math-example.png differ diff --git a/docs/usage/bullet-points.md b/docs/usage/bullet-points.md index 045ec61d1a..159241b3b0 100644 --- a/docs/usage/bullet-points.md +++ b/docs/usage/bullet-points.md @@ -5,12 +5,21 @@ To make a bullet point, simply make a paragraph into a bullet point: ```ts -const text = new TextRun("Bullet points"); -const paragraph = new Paragraph({ - text: "Bullet points", - bullet: { - level: 0, // How deep you want the bullet to me - }, +doc.addSection({ + children: [ + new Paragraph({ + text: "Bullet points", + bullet: { + level: 0 //How deep you want the bullet to be + } + }), + new Paragraph({ + text: "Are awesome", + bullet: { + level: 0 + } + }) + ], }); ``` diff --git a/docs/usage/change-tracking.md b/docs/usage/change-tracking.md new file mode 100644 index 0000000000..a5ca66ebb3 --- /dev/null +++ b/docs/usage/change-tracking.md @@ -0,0 +1,61 @@ +# Change Tracking + +> Instead of adding a `TextRun` into a `Paragraph`, you can also add an `InsertedTextRun` or `DeletedTextRun` where you need to supply an `id`, `author` and `date` for the change. + +```ts +import { Paragraph, TextRun, InsertedTextRun, DeletedTextRun } from "docx"; + +const paragraph = new Paragraph({ + children: [ + new TextRun("This is a simple demo "), + new TextRun({ + text: "on how to " + }), + new InsertedTextRun({ + text: "mark a text as an insertion ", + id: 0, + author: "Firstname Lastname", + date: "2020-10-06T09:00:00Z", + }), + new DeletedTextRun({ + text: "or a deletion.", + id: 1, + author: "Firstname Lastname", + date: "2020-10-06T09:00:00Z", + }) + ], +}); +``` + +Note that for a `InsertedTextRun` and `DeletedTextRun`, it is not possible to simply call it with only a text as in `new TextRun("some text")`, since the additonal fields for change tracking need to be provided. Similar to a normal `TextRun` you can add additional text properties. + +```ts +import { Paragraph, TextRun, InsertedTextRun, DeletedTextRun } from "docx"; + +const paragraph = new Paragraph({ + children: [ + new TextRun("This is a simple demo"), + new DeletedTextRun({ + text: "with a deletion.", + color: "red", + bold: true, + size: 24, + id: 0, + author: "Firstname Lastname", + date: "2020-10-06T09:00:00Z", + }) + ], +}); +``` + +In addtion to marking text as inserted or deleted, change tracking can also be added via the document settings. This will enable new changes to be tracked as well. + +```ts +import { Document } from "docx"; + +const doc = new Document({ + features: { + trackRevisions: true, + }, +}); +``` diff --git a/docs/usage/convenience-functions.md b/docs/usage/convenience-functions.md new file mode 100644 index 0000000000..76236ca6c4 --- /dev/null +++ b/docs/usage/convenience-functions.md @@ -0,0 +1,22 @@ +# Convenience functions + +OOXML and this library mainly uses a unit called twentieths of a point or `twip` for short. a twip is a typographical measurement, defined as 1/20 of a typographical point. One twip is 1/1440 inch, or 17.64 μm. This unit is not intuitive for many users, so some functions were created to help + +More info here: https://en.wikipedia.org/wiki/Twip + +## Convert Inches to Twip + +```ts +import { convertInchesToTwip } from "docx"; + +const twip = convertInchesToTwip(1); // returns 1440 +const twip = convertInchesToTwip(0.5); // returns 720 +``` + +## Convert Millimeters to Twip + +```ts +import { convertMillimetersToTwip } from "docx"; + +const twip = convertMillimetersToTwip(50); // returns 2834 +``` diff --git a/docs/usage/document.md b/docs/usage/document.md index bf06d30613..0f1a12486a 100644 --- a/docs/usage/document.md +++ b/docs/usage/document.md @@ -30,6 +30,24 @@ const doc = new docx.Document({ * keywords * lastModifiedBy * revision +* externalStyles +* styles +* numbering +* footnotes +* hyperlinks +* background + +### Change background color of Document + +Set the hex value in the document like so: + +```ts +const doc = new docx.Document({ + background: { + color: "C45911", + }, +}); +``` You can mix and match whatever properties you want, or provide no properties. diff --git a/docs/usage/images.md b/docs/usage/images.md index 4893348cc0..94e75e75a7 100644 --- a/docs/usage/images.md +++ b/docs/usage/images.md @@ -125,6 +125,7 @@ Full options you can pass into `floating` are: | lockAnchor | `boolean` | Optional | | behindDocument | `boolean` | Optional | | layoutInCell | `boolean` | Optional | +| zIndex | `number` | Optional | `HorizontalPositionOptions` are: diff --git a/docs/usage/math.md b/docs/usage/math.md new file mode 100644 index 0000000000..1e8ab4f894 --- /dev/null +++ b/docs/usage/math.md @@ -0,0 +1,265 @@ +# Math + +!> Math requires an understanding of [Sections](usage/sections.md) and [Paragraphs](usage/paragraph.md). + +## Intro + +1. To add math, create a `Math` object +2. Add `MathComponents` inside `Math` +3. `MathComponents` can have nested `MathComponents` inside. e.g. A fraction where the numerator is a square root, and the demoninator as another fraction. More on `MathComponents` below +4. Make sure to add the `Math` object inside a `Paragraph` + +## Example + +```ts +new Math({ + children: [ + new MathRun("2+2"), + new MathFraction({ + numerator: [new MathRun("hi")], + denominator: [new MathRun("2")], + }), + ], +}), +``` + +This will produce: + +

+ clippy the assistant +

+ +## Math Components + +`MathComponents` are the unit sized building blocks of an equation in `docx`. A `MathComponent` takes in more nested `MathComponents` until you reach `MathRun`, which has no children. `MathRun` is similar to a [TextRun](usage/text.md). + +### Math Run + +`MathRun` is the most basic `MathComponent`. + +#### Example + +```ts +new MathRun("2+2"); +``` + +```ts +new MathRun("hello"); +``` + +An example of it being used inside `Math`: + +```ts +new Math({ + children: [ + new MathRun("2"), + new MathRun("+"), + new MathRun("2"), + ], +}), +``` + +### Math Fraction + +`MathFractions` require a `numerator` and a `demoninator`, which are both a list of `MathComponents` + +#### Example + +```ts +new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], +}), +``` + +```ts +new MathFraction({ + numerator: [ + new MathRun("1"), + new MathRadical({ + children: [new MathRun("2")], + }), + ], + denominator: [new MathRun("2")], +}), +``` + +An example of it being used inside `Math`: + +```ts +new Math({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + new MathText("+"), + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + new MathText("= 1"), + ], +}), +``` + +### Sum + +A `MathComponent` for `Σ`. It can take a `superScript` and/or `subScript` as arguments to add `MathComponents` (usually limits) on the top and bottom + +```ts +new MathSum({ + children: [new MathRun("i")], +}), +``` + +```ts +new MathSum({ + children: [ + new MathSuperScript({ + children: [new MathRun("e")], + superScript: [new MathRun("2")], + }) + ], + subScript: [new MathRun("i")], + superScript: [new MathRun("10")], +}), +``` + +### Radicals + +A `MathComponent` for the `√` symbol. Examples include, square root, cube root etc. There is an optional `degree` parameter to specify the number of times the radicand is multiplied by itself. For example, `3` for cube root. + +```ts +new MathRadical({ + children: [new MathRun("2")], +}), +``` + +Cube root example: + +```ts +new MathRadical({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + new MathRun('+ 1'), + ], + degree: [new MathRun("3")], +}), +``` + +### Super Script + +`MathSuperScripts` are the little numbers written to the top right of numbers or variables. It means the exponent or power if written by itself with the number or variable. + +```ts +new MathSuperScript({ + children: [new MathRun("x")], + superScript: [new MathRun("2")], +}), +``` + +An example with cosine: + +```ts +new MathSuperScript({ + children: [new MathRun("cos")], + superScript: [new MathRun("-1")], +}), +``` + +### Sub Script + +`MathSubScripts` are similar to `MathSuperScripts`, except the little number is written below. + +```ts +new MathSubScript({ + children: [new MathRun("F")], + subScript: [new MathRun("n-1")], +}), +``` + +### Sub-Super Script + +`MathSubSuperScripts` are a combination of both `MathSuperScript` and `MathSubScript`. + +```ts +new MathSubSuperScript({ + children: [new MathRun("test")], + superScript: [new MathRun("hello")], + subScript: [new MathRun("world")], +}), +``` + +### Function + +`MathFunctions` are a way of describing what happens to an input variable, in order to get the output result. It takes a `name` parameter to specify the name of the function. + +```ts +new MathFunction({ + name: [ + new MathSuperScript({ + children: [new MathRun("cos")], + superScript: [new MathRun("-1")], + }), + ], + children: [new MathRun("100")], +}), +``` + +### Brackets + +#### Square brackets + +```ts +new MathSquareBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], +}), +``` + +#### Round brackets + +```ts +new MathRoundBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], +}), +``` + +#### Curly brackets + +```ts +new MathCurlyBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], +}), +``` + +#### Angled brackets + +```ts +new MathAngledBrackets({ + children: [ + new MathFraction({ + numerator: [new MathRun("1")], + denominator: [new MathRun("2")], + }), + ], +}), +``` diff --git a/docs/usage/paragraph.md b/docs/usage/paragraph.md index f6da3ec963..ceb4e0aa34 100644 --- a/docs/usage/paragraph.md +++ b/docs/usage/paragraph.md @@ -142,6 +142,21 @@ const paragraph = new Paragraph({ }); ``` +## Shading + +Add color to an entire paragraph block + +```ts +const paragraph = new Paragraph({ + text: "shading", + shading: { + type: ShadingType.REVERSE_DIAGONAL_STRIPE, + color: "00FFFF", + fill: "FF0000", + }, +}); +``` + ## Spacing Adding spacing between paragraphs diff --git a/docs/usage/sections.md b/docs/usage/sections.md index 5b6d4e9c74..daabb02294 100644 --- a/docs/usage/sections.md +++ b/docs/usage/sections.md @@ -19,3 +19,30 @@ doc.addSection({ ], }); ``` + +## Properties + +You can specify additional properties to the section, by providing a `properties` attribute. + +### Section Type + +Setting the section type determines how the contents of the section will be placed relative to the previous section. E.g., There are five different types: + +- `CONTINUOUS` +- `EVEN_PAGE` +- `NEXT_COLUMN` +- `NEXT_PAGE` +- `ODD_PAGE` + +```ts +doc.addSection({ + properties: { + type: SectionType.CONTINUOUS, + } + children: [ + new Paragraph({ + children: [new TextRun("Hello World")], + }), + ], +}); +``` diff --git a/docs/usage/tables.md b/docs/usage/tables.md index a8e41de58a..8d10b55ca3 100644 --- a/docs/usage/tables.md +++ b/docs/usage/tables.md @@ -51,19 +51,6 @@ const table = new Table({ }); ``` -### Pagination - -#### Prevent row pagination - -To prevent breaking contents of a row across multiple pages, call `cantSplit`: - -```ts -const table = new Table({ - rows: [], - cantSplit: true, -}); -``` - ## Table Row A table consists of multiple `table rows`. Table rows have a list of `children` which accepts a list of `table cells` explained below. You can create a simple `table row` like so: @@ -103,7 +90,7 @@ Here is a list of options you can add to the `table row`: | children | `Array` | Required | | cantSplit | `boolean` | Optional | | tableHeader | `boolean` | Optional | -| height | `{ value: number, rule: HeightRule }` | Optional | +| height | `{ height: number, rule: HeightRule }` | Optional | ### Repeat row @@ -116,6 +103,19 @@ const row = new TableRow({ }); ``` +### Pagination + +#### Prevent row pagination + +To prevent breaking contents of a row across multiple pages, call `cantSplit`: + +```ts +const row = new Row({ + ..., + cantSplit: true, +}); +``` + ## Table Cells Cells need to be added in the `table row`, you can create a table cell like: diff --git a/docs/usage/text.md b/docs/usage/text.md index 4816959eb6..0b7b88686b 100644 --- a/docs/usage/text.md +++ b/docs/usage/text.md @@ -77,40 +77,78 @@ const text = new TextRun({ }); ``` +### Shading and Highlighting + +```ts +const text = new TextRun({ + text: "shading", + shading: { + type: ShadingType.REVERSE_DIAGONAL_STRIPE, + color: "00FFFF", + fill: "FF0000", + }, +}); +``` + +```ts +const text = new TextRun({ + text: "highlighting", + highlight: "yellow", +}); +``` + ### Strike through ```ts -text.strike(); +const text = new TextRun({ + text: "strike", + strike: true, +}); ``` ### Double strike through ```ts -text.doubleStrike(); +const text = new TextRun({ + text: "doubleStrike", + doubleStrike: true, +}); ``` ### Superscript ```ts -text.superScript(); +const text = new TextRun({ + text: "superScript", + superScript: true, +}); ``` ### Subscript ```ts -text.subScript(); +const text = new TextRun({ + text: "subScript", + subScript: true, +}); ``` ### All Capitals ```ts -text.allCaps(); +const text = new TextRun({ + text: "allCaps", + allCaps: true, +}); ``` ### Small Capitals ```ts -text.smallCaps(); +const text = new TextRun({ + text: "smallCaps", + smallCaps: true, +}); ``` ## Break @@ -118,13 +156,17 @@ text.smallCaps(); Sometimes you would want to put text underneath another line of text but inside the same paragraph. ```ts -text.break(); +const text = new TextRun({ + text: "break", + break: 1, +}); ``` -## Chaining - -What if you want to create a paragraph which is **_bold_** and **_italic_**? +Adding two breaks: ```ts -paragraph.bold().italics(); +const text = new TextRun({ + text: "break", + break: 2, +}); ``` diff --git a/package-lock.json b/package-lock.json index 5174555098..bad5fe5e17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "docx", - "version": "5.3.0", + "version": "5.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -462,9 +462,9 @@ "dev": true }, "@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", + "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -487,20 +487,10 @@ "@sinonjs/commons": "^1.7.0" } }, - "@sinonjs/formatio": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", - "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^5.0.2" - } - }, "@sinonjs/samsam": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.0.3.tgz", - "integrity": "sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -538,9 +528,9 @@ "dev": true }, "@types/bluebird": { - "version": "3.5.32", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.32.tgz", - "integrity": "sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g==", + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.33.tgz", + "integrity": "sha512-ndEo1xvnYeHxm7I/5sF6tBvnsA4Tdi3zj1keRKRs12SP+2ye2A27NDJ1B6PqkfMbGAcT+mqQVqbZRIrhfOp5PQ==", "dev": true }, "@types/caseless": { @@ -569,22 +559,16 @@ "jszip": "*" } }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, "@types/mocha": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.0.tgz", - "integrity": "sha512-jWeYcTo3sCH/rMgsdYXDTO85GNRyTCII5dayMIu/ZO4zbEot1E3iNGaOwpLReLUHjeNQFkgeNNVYlY4dX6azQQ==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-NysN+bNqj6E0Hv4CTGWSlPzMW6vTKjDpOteycDkV4IWBsO+PU48JonrPzV9ODjiI2XrjmA05KInLgF5ivZ/YGQ==", "dev": true }, "@types/node": { - "version": "14.0.27", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", - "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==" + "version": "14.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.2.tgz", + "integrity": "sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A==" }, "@types/request": { "version": "2.48.5", @@ -612,9 +596,9 @@ } }, "@types/request-promise": { - "version": "4.1.46", - "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.46.tgz", - "integrity": "sha512-3Thpj2Va5m0ji3spaCk8YKrjkZyZc6RqUVOphA0n/Xet66AW/AiOAs5vfXhQIL5NmkaO7Jnun7Nl9NEjJ2zBaw==", + "version": "4.1.47", + "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.47.tgz", + "integrity": "sha512-eRSZhAS8SMsrWOM8vbhxFGVZhTbWSJvaRKyufJTdIf4gscUouQvOBlfotPSPHbMR3S7kfkyKbhb1SWPmQdy3KQ==", "dev": true, "requires": { "@types/bluebird": "*", @@ -661,18 +645,18 @@ "dev": true }, "@types/uglify-js": { - "version": "3.9.3", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.9.3.tgz", - "integrity": "sha512-KswB5C7Kwduwjj04Ykz+AjvPcfgv/37Za24O2EDzYNbwyzOo8+ydtvzUfZ5UMguiVu29Gx44l1A6VsPPcmYu9w==", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.11.1.tgz", + "integrity": "sha512-7npvPKV+jINLu1SpSYVWG8KvyJBhBa8tmzMMdDoVc2pWUYHN8KIXlPJhjJ4LT97c4dXJA2SHL/q6ADbDriZN+Q==", "dev": true, "requires": { "source-map": "^0.6.1" } }, "@types/webpack": { - "version": "4.41.21", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.21.tgz", - "integrity": "sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA==", + "version": "4.41.26", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.26.tgz", + "integrity": "sha512-7ZyTfxjCRwexh+EJFwRUM+CDB2XvgHl4vfuqf1ZKrgGvcS5BrNvPQqJh3tsZ0P6h6Aa1qClVHaJZszLPzpqHeA==", "dev": true, "requires": { "@types/anymatch": "*", @@ -684,9 +668,9 @@ } }, "@types/webpack-sources": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-1.4.2.tgz", - "integrity": "sha512-77T++JyKow4BQB/m9O96n9d/UUHWLQHlcqXb9Vsf4F1+wKNrrlWNFPDLKNT92RJnCSL6CieTc+NDXtCVZswdTw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.0.tgz", + "integrity": "sha512-LXn/oYIpBeucgP1EIJbKQ2/4ZmpvRl+dlrFdX7+94SKRUV3Evy3FsfMZY318vGhkWUS5MPhtOM3w1/hCOAOXcg==", "dev": true, "requires": { "@types/node": "*", @@ -812,6 +796,12 @@ } } }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -1029,7 +1019,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1058,7 +1048,7 @@ }, "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -1080,6 +1070,12 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -1088,7 +1084,7 @@ }, "awesome-typescript-loader": { "version": "3.5.0", - "resolved": "http://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.5.0.tgz", + "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.5.0.tgz", "integrity": "sha512-qzgm9SEvodVkSi9QY7Me1/rujg+YBNMjayNSAyzNghwTEez++gXoPCwMvpbHRG7wrOkDCiF6dquvv9ESmUBAuw==", "dev": true, "requires": { @@ -1132,7 +1128,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -1250,15 +1246,6 @@ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, - "backbone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.0.tgz", - "integrity": "sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==", - "dev": true, - "requires": { - "underscore": ">=1.8.3" - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1360,51 +1347,112 @@ "dev": true }, "boxen": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz", - "integrity": "sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", "dev": true, "requires": { "ansi-align": "^3.0.0", "camelcase": "^5.3.1", - "chalk": "^2.4.2", + "chalk": "^3.0.0", "cli-boxes": "^2.2.0", - "string-width": "^3.0.0", - "term-size": "^1.2.0", - "type-fest": "^0.3.0", - "widest-line": "^2.0.0" + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } } } @@ -1599,9 +1647,9 @@ }, "dependencies": { "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -1665,7 +1713,7 @@ }, "chai": { "version": "3.5.0", - "resolved": "http://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", "dev": true, "requires": { @@ -1768,9 +1816,9 @@ "dev": true }, "cli-boxes": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", - "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", "dev": true }, "clipboard": { @@ -1865,9 +1913,9 @@ "dev": true }, "colors": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, "combined-stream": { @@ -1881,7 +1929,7 @@ }, "commander": { "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, @@ -1916,34 +1964,17 @@ } }, "configstore": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz", - "integrity": "sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "dev": true, "requires": { - "dot-prop": "^4.1.0", + "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" } }, "connect": { @@ -2054,6 +2085,12 @@ "sha.js": "^2.4.8" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -2085,9 +2122,9 @@ } }, "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, "cycle": { @@ -2153,7 +2190,7 @@ }, "deep-eql": { "version": "0.1.3", - "resolved": "http://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", "dev": true, "requires": { @@ -2306,25 +2343,48 @@ } }, "docsify": { - "version": "4.11.4", - "resolved": "https://registry.npmjs.org/docsify/-/docsify-4.11.4.tgz", - "integrity": "sha512-Qwt98y6ddM2Wb46gRH/zQpEAvw70AlIpzVlB9Wi2u2T2REg9O+bXMpJ27F5TaRTn2bD6SF1VyZYNUfimpihZwQ==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/docsify/-/docsify-4.12.0.tgz", + "integrity": "sha512-oLr48dLeJ8sTVQfL8HLFqd2sPPG8DNAOvYAXXJQr/+/K9uC2KDhoeu+GGj5U2uFGR5czF3oLvqNBxhEElg1wGw==", "dev": true, "requires": { - "dompurify": "^2.0.8", - "marked": "^0.7.0", - "medium-zoom": "^1.0.5", + "dompurify": "^2.2.6", + "marked": "^1.2.9", + "medium-zoom": "^1.0.6", "opencollective-postinstall": "^2.0.2", - "prismjs": "^1.19.0", + "prismjs": "^1.23.0", "strip-indent": "^3.0.0", - "tinydate": "^1.0.0", + "tinydate": "^1.3.0", "tweezer.js": "^1.4.0" + }, + "dependencies": { + "dompurify": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.6.tgz", + "integrity": "sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ==", + "dev": true + }, + "marked": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.9.tgz", + "integrity": "sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==", + "dev": true + }, + "prismjs": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.23.0.tgz", + "integrity": "sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==", + "dev": true, + "requires": { + "clipboard": "^2.0.0" + } + } } }, "docsify-cli": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/docsify-cli/-/docsify-cli-4.4.1.tgz", - "integrity": "sha512-M5VU/8UCILz87D519KnURH3LA+A3RAAqj7ifDv4xOUf4c32QDkWSETS3yLmXlTNdxhATVi5P1fVklnvVW4uOyw==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/docsify-cli/-/docsify-cli-4.4.2.tgz", + "integrity": "sha512-iCTRyKjjNiSroo5cgVkb/C86PsUEEsVV30PXp5GkzbcMG+mMxzBPmJ/8xukTLoeaQddEsSeSWW376eC2t4KQJw==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -2333,13 +2393,14 @@ "cp-file": "^7.0.0", "docsify": "^4.10.2", "docsify-server-renderer": ">=4", + "enquirer": "^2.3.6", "fs-extra": "^8.1.0", "get-port": "^5.0.0", "livereload": "^0.9.1", "lru-cache": "^5.1.1", "open": "^6.4.0", "serve-static": "^1.12.1", - "update-notifier": "^3.0.1", + "update-notifier": "^4.1.0", "yargonaut": "^1.1.2", "yargs": "^14.2.0" }, @@ -2376,17 +2437,6 @@ "locate-path": "^3.0.0" } }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2489,25 +2539,25 @@ } }, "docsify-server-renderer": { - "version": "4.11.4", - "resolved": "https://registry.npmjs.org/docsify-server-renderer/-/docsify-server-renderer-4.11.4.tgz", - "integrity": "sha512-TxOxqX/fwBFLHz788PtB0OeVis1EDWCVK8CEE/fvxTzQqsgZNTw2UO1wEg1qt/uuldJps3G+DPv/FN9xIeQgcg==", + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/docsify-server-renderer/-/docsify-server-renderer-4.11.6.tgz", + "integrity": "sha512-IAEM+kKsDfo1qnrEdaBH5pCQFjAWA7B7jWWmfCKzDA/BPcHO+zCR4++Mw/NPR/huJKU58AzuGtEJ/NhF/l0Y6Q==", "dev": true, "requires": { "debug": "^4.1.1", - "docsify": "^4.11.2", + "docsify": "^4.11.4", "dompurify": "^2.0.8", "node-fetch": "^2.6.0", "resolve-pathname": "^3.0.0" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -2525,18 +2575,18 @@ "dev": true }, "dompurify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.12.tgz", - "integrity": "sha512-Fl8KseK1imyhErHypFPA8qpq9gPzlsJ/EukA6yk9o0gX23p1TzC+rh9LqNg1qvErRTc0UNMYlKxEGSfSh43NDg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.2.tgz", + "integrity": "sha512-BsGR4nDLaC5CNBnyT5I+d5pOeaoWvgVeg6Gq/aqmKYWMPR07131u60I80BvExLAJ0FQEIBQ1BTicw+C5+jOyrg==", "dev": true }, "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "^2.0.0" } }, "duplexer3": { @@ -2615,6 +2665,15 @@ "tapable": "^0.2.5" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -2710,6 +2769,12 @@ "es6-symbol": "^3.1.1" } }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3015,7 +3080,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -3440,13 +3505,6 @@ "dev": true, "optional": true }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true, - "optional": true - }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -3953,12 +4011,12 @@ } }, "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "^1.3.5" } }, "globals": { @@ -4022,9 +4080,9 @@ "dev": true }, "handlebars": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", - "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", "dev": true, "requires": { "minimist": "^1.2.5", @@ -4039,12 +4097,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true } } }, @@ -4194,9 +4246,9 @@ "dev": true }, "highlight.js": { - "version": "9.18.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", - "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.6.0.tgz", + "integrity": "sha512-8mlRcn5vk/r4+QcqerapwBYTe+iPL5ih6xrNylxrnBdHQiijDETfXX7VIxC3UiCRiINBJfANBAsPzAvRQj8RpQ==", "dev": true }, "hmac-drbg": { @@ -4309,9 +4361,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "interpret": { @@ -4373,7 +4425,7 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", "dev": true }, "is-ci": { @@ -4484,19 +4536,19 @@ } }, "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" } }, "is-npm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-3.0.0.tgz", - "integrity": "sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", "dev": true }, "is-number": { @@ -4520,19 +4572,16 @@ } }, "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true }, "is-plain-object": { "version": "2.0.4", @@ -4813,12 +4862,6 @@ "istanbul-lib-report": "^3.0.0" } }, - "jquery": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", - "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==", - "dev": true - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -4843,7 +4886,7 @@ }, "jsesc": { "version": "1.3.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, @@ -4908,9 +4951,9 @@ } }, "jszip": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", - "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz", + "integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==", "requires": { "lie": "~3.3.0", "pako": "~1.0.2", @@ -4919,9 +4962,9 @@ } }, "just-extend": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.0.tgz", - "integrity": "sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", + "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==", "dev": true }, "keyv": { @@ -5009,9 +5052,9 @@ } }, "chokidar": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.1.tgz", - "integrity": "sha512-TQTJyr2stihpC4Sya9hs2Xh+O2wf+igjL36Y75xx2WdHuiICcn/XJza46Jwt0eT5hVpQOzo3FpY3cj3RVYLX0g==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -5021,7 +5064,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "fill-range": { @@ -5071,9 +5114,9 @@ "dev": true }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -5091,14 +5134,14 @@ } }, "livereload-js": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.2.4.tgz", - "integrity": "sha512-kgTCrrYAfwnb5469+7bB2hxIct8Giq0RemDnxOESIzyRwRqYGGMZh5VIveYqJiDvpglH0dpd074BU1zJj4NaNw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.1.tgz", + "integrity": "sha512-CBu1gTEfzVhlOK1WASKAAJ9Qx1fHECTq0SUB67sfxwQssopTyvzqTlgl+c0h9pZ6V+Fzd2rc510ppuNusg9teQ==", "dev": true }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -5200,9 +5243,9 @@ } }, "lunr": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz", - "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==", + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, "make-dir": { @@ -5253,9 +5296,9 @@ } }, "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.4.tgz", + "integrity": "sha512-6x5TFGCTKSQBLTZtOburGxCxFEBJEGYVLwCMTBCxzvyuisGcC20UNzDSJhCr/cJ/Kmh6ulfJm10g6WWEAJ3kvg==", "dev": true }, "math-random": { @@ -5395,7 +5438,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -5430,7 +5473,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -5547,9 +5590,9 @@ "dev": true }, "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, "nan": { @@ -5560,9 +5603,9 @@ "optional": true }, "nanoid": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.6.tgz", - "integrity": "sha512-2NDzpiuEy3+H0AVtdt8LoFi7PnqkOnIzYmJQp7xsEU6VexLluHQwKREuiz57XaQC5006seIadPrIZJhyS2n7aw==" + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" }, "nanomatch": { "version": "1.2.13", @@ -5585,7 +5628,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -5603,7 +5646,7 @@ }, "next-tick": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, @@ -5627,9 +5670,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "node-libs-browser": { @@ -6090,9 +6133,9 @@ "dev": true }, "opts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.0.tgz", - "integrity": "sha512-rPleeyX48sBEc4aj7rAok5dCbvRdYpdbIdSRR4gnIK98a7Rvd4l3wlv4YHQr2mwPQTpKQiw8uipi/WoyItDINg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", "dev": true }, "os-browserify": { @@ -6103,7 +6146,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -6315,16 +6358,10 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -6392,7 +6429,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -6456,12 +6493,6 @@ } } }, - "pkginfo": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", - "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", - "dev": true - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -6492,20 +6523,11 @@ "dev": true }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", "dev": true }, - "prismjs": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.20.0.tgz", - "integrity": "sha512-AEDjSrVNkynnw6A+B1DsFkd6AVdTnp+/WoUixFRULlCLZVRZlVQMVWio/16jv7G1FscUxQxOQhWwApgbnxr6kQ==", - "dev": true, - "requires": { - "clipboard": "^2.0.0" - } - }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -6533,17 +6555,16 @@ "dev": true }, "prompt": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz", - "integrity": "sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.1.0.tgz", + "integrity": "sha512-ec1vUPXCplDBDUVD8uPa3XGA+OzLrO40Vxv3F1uxoiZGkZhdctlK2JotcHq5X6ExjocDOGwGdCSXloGNyU5L1Q==", "dev": true, "requires": { "colors": "^1.1.2", - "pkginfo": "0.x.x", "read": "1.0.x", "revalidator": "0.1.x", "utile": "0.3.x", - "winston": "2.1.x" + "winston": "2.x" } }, "prr": { @@ -6594,6 +6615,15 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -6752,9 +6782,9 @@ } }, "registry-auth-token": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", - "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", "dev": true, "requires": { "rc": "^1.2.8" @@ -7116,7 +7146,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -7157,12 +7187,20 @@ "dev": true }, "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "send": { @@ -7289,9 +7327,9 @@ } }, "shortid": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.15.tgz", - "integrity": "sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", + "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", "requires": { "nanoid": "^2.1.0" } @@ -7303,17 +7341,16 @@ "dev": true }, "sinon": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.0.2.tgz", - "integrity": "sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.7.2", + "@sinonjs/commons": "^1.8.1", "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/formatio": "^5.0.1", - "@sinonjs/samsam": "^5.0.3", + "@sinonjs/samsam": "^5.3.1", "diff": "^4.0.2", - "nise": "^4.0.1", + "nise": "^4.0.4", "supports-color": "^7.1.0" }, "dependencies": { @@ -7330,9 +7367,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -7701,7 +7738,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -7719,7 +7756,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -7754,36 +7791,10 @@ "dev": true }, "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "^0.7.0" - }, - "dependencies": { - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - } - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true }, "test-exclude": { "version": "6.0.0", @@ -7915,12 +7926,13 @@ "dev": true }, "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", "dev": true, "requires": { "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "source-map-support": "^0.5.17", @@ -7996,10 +8008,13 @@ } }, "tslint-immutable": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/tslint-immutable/-/tslint-immutable-4.9.1.tgz", - "integrity": "sha512-iIFCq08H4YyNIX0bV5N6fGQtAmjc4OQZKQCgBP5WHgQaITyGAHPVmAw+Yf7qe0zbRCvCDZdrdEC/191fLGFiww==", - "dev": true + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tslint-immutable/-/tslint-immutable-6.0.1.tgz", + "integrity": "sha512-3GQ6HffN64gLmT/N1YzyVMqyf6uBjMvhNaevK8B0K01/QC0OU5AQZrH4TjMHo1IdG3JpqsZvuRy9IW1LA3zjwA==", + "dev": true, + "requires": { + "tsutils": "^2.28.0 || ^3.0.0" + } }, "tsutils": { "version": "2.29.0", @@ -8050,9 +8065,9 @@ "dev": true }, "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, "typedarray": { @@ -8071,86 +8086,77 @@ } }, "typedoc": { - "version": "0.16.11", - "resolved": "http://registry.npmjs.org/typedoc/-/typedoc-0.16.11.tgz", - "integrity": "sha512-YEa5i0/n0yYmLJISJ5+po6seYfJQJ5lQYcHCPF9ffTF92DB/TAZO/QrazX5skPHNPtmlIht5FdTXCM2kC7jQFQ==", + "version": "0.19.0", + "resolved": "http://registry.npmjs.org/typedoc/-/typedoc-0.19.0.tgz", + "integrity": "sha512-Rn68JwgDDYyIWl3HXeSsLZcsvxd2anISjhKu64PvID7RETeS2Iwnc4cH60yqc8/N50Xo1d3MHPGYinCPhMMliQ==", "dev": true, "requires": { - "@types/minimatch": "3.0.3", - "fs-extra": "^8.1.0", - "handlebars": "^4.7.2", - "highlight.js": "^9.17.1", - "lodash": "^4.17.15", - "marked": "^0.8.0", + "fs-extra": "^9.0.1", + "handlebars": "^4.7.6", + "highlight.js": "^10.0.0", + "lodash": "^4.17.20", + "lunr": "^2.3.9", + "marked": "^1.1.1", "minimatch": "^3.0.0", "progress": "^2.0.3", - "shelljs": "^0.8.3", - "typedoc-default-themes": "^0.7.2", - "typescript": "3.7.x" + "shelljs": "^0.8.4", + "typedoc-default-themes": "^0.11.1" }, "dependencies": { - "marked": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", - "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", - "dev": true - }, - "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } }, - "typescript": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", - "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", "dev": true } } }, "typedoc-default-themes": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.7.2.tgz", - "integrity": "sha512-fiFKlFO6VTqjcno8w6WpTsbCgXmfPHVjnLfYkmByZE7moaz+E2DSpAT+oHtDHv7E0BM5kAhPrHJELP2J2Y2T9A==", - "dev": true, - "requires": { - "backbone": "^1.4.0", - "jquery": "^3.4.1", - "lunr": "^2.3.8", - "underscore": "^1.9.1" - } + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.4.tgz", + "integrity": "sha512-Y4Lf+qIb9NTydrexlazAM46SSLrmrQRqWiD52593g53SsmUFioAsMWt8m834J6qsp+7wHRjxCXSZeiiW5cMUdw==", + "dev": true }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.2.tgz", + "integrity": "sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==", "dev": true }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.12.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.8.tgz", + "integrity": "sha512-fvBeuXOsvqjecUtF/l1dwsrrf5y2BCUk9AOJGzGcm6tE7vegku5u/YvqjyDaAGr422PLoLnrxg3EnRvTqsdC1w==", "dev": true, - "optional": true, - "requires": { - "commander": "~2.20.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true, - "optional": true - } - } + "optional": true }, "uglify-to-browserify": { "version": "1.0.2", @@ -8218,7 +8224,7 @@ }, "yargs": { "version": "3.10.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { @@ -8230,12 +8236,6 @@ } } }, - "underscore": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", - "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", - "dev": true - }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -8249,12 +8249,12 @@ } }, "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "^2.0.0" } }, "universalify": { @@ -8316,23 +8316,75 @@ "dev": true }, "update-notifier": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-3.0.1.tgz", - "integrity": "sha512-grrmrB6Zb8DUiyDIaeRTBCkgISYUgETNe7NglEbVsrLWXeESnlCSP50WfRSj/GmzMPl6Uchj24S/p80nP/ZQrQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", "dev": true, "requires": { - "boxen": "^3.0.0", - "chalk": "^2.0.1", - "configstore": "^4.0.0", + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", "has-yarn": "^2.1.0", "import-lazy": "^2.1.0", "is-ci": "^2.0.0", - "is-installed-globally": "^0.1.0", - "is-npm": "^3.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", "is-yarn-global": "^0.3.0", "latest-version": "^5.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "uri-js": { @@ -8654,7 +8706,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -8839,12 +8891,52 @@ "dev": true }, "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, "requires": { - "string-width": "^2.1.1" + "string-width": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } } }, "window-size": { @@ -8854,9 +8946,9 @@ "dev": true }, "winston": { - "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", - "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz", + "integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==", "dev": true, "requires": { "async": "~1.0.0", @@ -8864,33 +8956,32 @@ "cycle": "1.0.x", "eyes": "0.1.x", "isstream": "0.1.x", - "pkginfo": "0.3.x", "stack-trace": "0.0.x" }, "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true - }, - "pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", - "dev": true } } }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -8927,14 +9018,15 @@ "dev": true }, "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "ws": { @@ -8947,9 +9039,9 @@ } }, "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, "xml": { @@ -9023,7 +9115,7 @@ }, "yargs": { "version": "4.8.1", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", "dev": true, "requires": { @@ -9067,7 +9159,7 @@ }, "yargs-parser": { "version": "2.4.1", - "resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", "dev": true, "requires": { diff --git a/package.json b/package.json index 9edda454f0..9db7e34f55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docx", - "version": "5.3.0", + "version": "5.5.0", "description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.", "main": "build/index.js", "scripts": { @@ -79,7 +79,7 @@ "mocha-webpack": "^1.0.1", "nyc": "^15.1.0", "pre-commit": "^1.2.2", - "prettier": "^2.0.5", + "prettier": "^2.1.2", "prompt": "^1.0.0", "replace-in-file": "^3.1.0", "request": "^2.88.0", @@ -87,11 +87,11 @@ "rimraf": "^3.0.2", "shelljs": "^0.8.4", "sinon": "^9.0.2", - "ts-node": "^8.10.2", + "ts-node": "^9.0.0", "tslint": "^6.1.3", - "tslint-immutable": "^4.9.0", - "typedoc": "^0.16.11", - "typescript": "2.9.2", + "tslint-immutable": "^6.0.1", + "typedoc": "^0.19.0", + "typescript": "4.2.2", "webpack": "^3.10.0" }, "engines": { diff --git a/src/convenience-functions.spec.ts b/src/convenience-functions.spec.ts new file mode 100644 index 0000000000..3333fdd2d0 --- /dev/null +++ b/src/convenience-functions.spec.ts @@ -0,0 +1,18 @@ +import { expect } from "chai"; +import { convertInchesToTwip, convertMillimetersToTwip } from "./convenience-functions"; + +describe("Utility", () => { + describe("#convertMillimetersToTwip", () => { + it("should call the underlying header's addChildElement for Paragraph", () => { + expect(convertMillimetersToTwip(1000)).to.equal(56692); + }); + }); + + describe("#convertInchesToTwip", () => { + it("should call the underlying header's addChildElement", () => { + expect(convertInchesToTwip(1)).to.equal(1440); + expect(convertInchesToTwip(0.5)).to.equal(720); + expect(convertInchesToTwip(0.25)).to.equal(360); + }); + }); +}); diff --git a/src/convenience-functions.ts b/src/convenience-functions.ts new file mode 100644 index 0000000000..6fe110b72b --- /dev/null +++ b/src/convenience-functions.ts @@ -0,0 +1,8 @@ +// Twip - twentieths of a point +export const convertMillimetersToTwip = (millimeters: number): number => { + return Math.floor((millimeters / 25.4) * 72 * 20); +}; + +export const convertInchesToTwip = (inches: number): number => { + return Math.floor(inches * 72 * 20); +}; diff --git a/src/export/formatter.ts b/src/export/formatter.ts index b863ea4b97..a91702212e 100644 --- a/src/export/formatter.ts +++ b/src/export/formatter.ts @@ -1,8 +1,8 @@ +import { IViewWrapper } from "file/document-wrapper"; import { BaseXmlComponent, IXmlableObject } from "file/xml-components"; -import { File } from "../file"; export class Formatter { - public format(input: BaseXmlComponent, file?: File): IXmlableObject { + public format(input: BaseXmlComponent, file?: IViewWrapper): IXmlableObject { const output = input.prepForXml(file); if (output) { diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts index 637646deb5..5f6b8183b0 100644 --- a/src/export/packer/next-compiler.ts +++ b/src/export/packer/next-compiler.ts @@ -70,23 +70,23 @@ export class Compiler { private xmlifyFile(file: File, prettify?: boolean): IXmlifyedFileMapping { file.verifyUpdateFields(); - const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1; + const documentRelationshipCount = file.Document.Relationships.RelationshipCount + 1; - const documentXmlData = xml(this.formatter.format(file.Document, file), prettify); + const documentXmlData = xml(this.formatter.format(file.Document.View, file.Document), prettify); const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media); return { Relationships: { data: (() => { documentMediaDatas.forEach((mediaData, i) => { - file.DocumentRelationships.createRelationship( + file.Document.Relationships.createRelationship( documentRelationshipCount + i, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", `media/${mediaData.fileName}`, ); }); - return xml(this.formatter.format(file.DocumentRelationships, file), prettify); + return xml(this.formatter.format(file.Document.Relationships, file.Document), prettify); })(), path: "word/_rels/document.xml.rels", }, @@ -100,11 +100,11 @@ export class Compiler { path: "word/document.xml", }, Styles: { - data: xml(this.formatter.format(file.Styles, file), prettify), + data: xml(this.formatter.format(file.Styles, file.Document), prettify), path: "word/styles.xml", }, Properties: { - data: xml(this.formatter.format(file.CoreProperties, file), { + data: xml(this.formatter.format(file.CoreProperties, file.Document), { declaration: { standalone: "yes", encoding: "UTF-8", @@ -113,15 +113,15 @@ export class Compiler { path: "docProps/core.xml", }, Numbering: { - data: xml(this.formatter.format(file.Numbering, file), prettify), + data: xml(this.formatter.format(file.Numbering, file.Document), prettify), path: "word/numbering.xml", }, FileRelationships: { - data: xml(this.formatter.format(file.FileRelationships, file), prettify), + data: xml(this.formatter.format(file.FileRelationships, file.Document), prettify), path: "_rels/.rels", }, HeaderRelationships: file.Headers.map((headerWrapper, index) => { - const xmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify); + const xmlData = xml(this.formatter.format(headerWrapper.View, headerWrapper), prettify); const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media); mediaDatas.forEach((mediaData, i) => { @@ -133,12 +133,12 @@ export class Compiler { }); return { - data: xml(this.formatter.format(headerWrapper.Relationships, file), prettify), + data: xml(this.formatter.format(headerWrapper.Relationships, headerWrapper), prettify), path: `word/_rels/header${index + 1}.xml.rels`, }; }), FooterRelationships: file.Footers.map((footerWrapper, index) => { - const xmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify); + const xmlData = xml(this.formatter.format(footerWrapper.View, footerWrapper), prettify); const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media); mediaDatas.forEach((mediaData, i) => { @@ -150,12 +150,12 @@ export class Compiler { }); return { - data: xml(this.formatter.format(footerWrapper.Relationships, file), prettify), + data: xml(this.formatter.format(footerWrapper.Relationships, footerWrapper), prettify), path: `word/_rels/footer${index + 1}.xml.rels`, }; }), Headers: file.Headers.map((headerWrapper, index) => { - const tempXmlData = xml(this.formatter.format(headerWrapper.Header, file), prettify); + const tempXmlData = xml(this.formatter.format(headerWrapper.View, headerWrapper), prettify); const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media); // TODO: 0 needs to be changed when headers get relationships of their own const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0); @@ -166,7 +166,7 @@ export class Compiler { }; }), Footers: file.Footers.map((footerWrapper, index) => { - const tempXmlData = xml(this.formatter.format(footerWrapper.Footer, file), prettify); + const tempXmlData = xml(this.formatter.format(footerWrapper.View, footerWrapper), prettify); const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media); // TODO: 0 needs to be changed when headers get relationships of their own const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0); @@ -177,7 +177,7 @@ export class Compiler { }; }), ContentTypes: { - data: xml(this.formatter.format(file.ContentTypes, file), prettify), + data: xml(this.formatter.format(file.ContentTypes, file.Document), prettify), path: "[Content_Types].xml", }, CustomProperties: { @@ -185,15 +185,15 @@ export class Compiler { path: "docProps/custom.xml", }, AppProperties: { - data: xml(this.formatter.format(file.AppProperties, file), prettify), + data: xml(this.formatter.format(file.AppProperties, file.Document), prettify), path: "docProps/app.xml", }, FootNotes: { - data: xml(this.formatter.format(file.FootNotes, file), prettify), + data: xml(this.formatter.format(file.FootNotes, file.Document), prettify), path: "word/footnotes.xml", }, Settings: { - data: xml(this.formatter.format(file.Settings, file), prettify), + data: xml(this.formatter.format(file.Settings, file.Document), prettify), path: "word/settings.xml", }, }; diff --git a/src/file/core-properties/properties.ts b/src/file/core-properties/properties.ts index ceddff39e0..8e3921df0c 100644 --- a/src/file/core-properties/properties.ts +++ b/src/file/core-properties/properties.ts @@ -1,22 +1,12 @@ import { XmlComponent } from "file/xml-components"; +import { IDocumentBackgroundOptions } from "../document"; import { DocumentAttributes } from "../document/document-attributes"; import { INumberingOptions } from "../numbering"; -import { HyperlinkType, Paragraph } from "../paragraph"; +import { Paragraph } from "../paragraph"; import { IStylesOptions } from "../styles"; import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components"; -export interface IInternalHyperlinkDefinition { - readonly text: string; - readonly type: HyperlinkType.INTERNAL; -} - -export interface IExternalHyperlinkDefinition { - readonly link: string; - readonly text: string; - readonly type: HyperlinkType.EXTERNAL; -} - export interface IPropertiesOptions { readonly title?: string; readonly subject?: string; @@ -29,9 +19,11 @@ export interface IPropertiesOptions { readonly styles?: IStylesOptions; readonly numbering?: INumberingOptions; readonly footnotes?: Paragraph[]; - readonly hyperlinks?: { - readonly [key: string]: IInternalHyperlinkDefinition | IExternalHyperlinkDefinition; + readonly background?: IDocumentBackgroundOptions; + readonly features?: { + readonly trackRevisions?: boolean; }; + readonly compatabilityModeVersion?: number; } export class CoreProperties extends XmlComponent { diff --git a/src/file/document-wrapper.spec.ts b/src/file/document-wrapper.spec.ts new file mode 100644 index 0000000000..2a32ad7968 --- /dev/null +++ b/src/file/document-wrapper.spec.ts @@ -0,0 +1,50 @@ +import { expect } from "chai"; +import * as sinon from "sinon"; + +import { FooterWrapper } from "./footer-wrapper"; +import { Media } from "./media"; +import { Paragraph } from "./paragraph"; +import { Table, TableCell, TableRow } from "./table"; + +describe("FooterWrapper", () => { + describe("#add", () => { + it("should call the underlying footer's addParagraph", () => { + const file = new FooterWrapper(new Media(), 1); + const spy = sinon.spy(file.View, "add"); + file.add(new Paragraph({})); + + expect(spy.called).to.equal(true); + }); + + it("should call the underlying footer's addParagraph", () => { + const file = new FooterWrapper(new Media(), 1); + const spy = sinon.spy(file.View, "add"); + file.add( + new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("hello")], + }), + ], + }), + ], + }), + ); + + expect(spy.called).to.equal(true); + }); + }); + + describe("#addChildElement", () => { + it("should call the underlying footer's addChildElement", () => { + const file = new FooterWrapper(new Media(), 1); + const spy = sinon.spy(file.View, "addChildElement"); + // tslint:disable-next-line:no-any + file.addChildElement({} as any); + + expect(spy.called).to.equal(true); + }); + }); +}); diff --git a/src/file/document-wrapper.ts b/src/file/document-wrapper.ts new file mode 100644 index 0000000000..3054f2c30e --- /dev/null +++ b/src/file/document-wrapper.ts @@ -0,0 +1,27 @@ +import { Document, IDocumentOptions } from "./document"; +import { Footer } from "./footer"; +import { Header } from "./header/header"; +import { Relationships } from "./relationships"; + +export interface IViewWrapper { + readonly View: Document | Footer | Header; + readonly Relationships: Relationships; +} + +export class DocumentWrapper implements IViewWrapper { + private readonly document: Document; + private readonly relationships: Relationships; + + constructor(options: IDocumentOptions) { + this.document = new Document(options); + this.relationships = new Relationships(); + } + + public get View(): Document { + return this.document; + } + + public get Relationships(): Relationships { + return this.relationships; + } +} diff --git a/src/file/document/body/body.ts b/src/file/document/body/body.ts index ea0d24a448..4b0e588809 100644 --- a/src/file/document/body/body.ts +++ b/src/file/document/body/body.ts @@ -1,6 +1,7 @@ +import { IViewWrapper } from "file/document-wrapper"; import { IXmlableObject, XmlComponent } from "file/xml-components"; + import { Paragraph, ParagraphProperties, TableOfContents } from "../.."; -import { File } from "../../../file"; import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties"; export class Body extends XmlComponent { @@ -25,7 +26,7 @@ export class Body extends XmlComponent { this.sections.push(new SectionProperties(options)); } - public prepForXml(file?: File): IXmlableObject | undefined { + public prepForXml(file?: IViewWrapper): IXmlableObject | undefined { if (this.sections.length === 1) { this.root.splice(0, 1); this.root.push(this.sections.pop() as SectionProperties); diff --git a/src/file/document/body/section-properties/index.ts b/src/file/document/body/section-properties/index.ts index 47e56ec172..ee0820c640 100644 --- a/src/file/document/body/section-properties/index.ts +++ b/src/file/document/body/section-properties/index.ts @@ -6,3 +6,4 @@ export * from "./page-number"; export * from "./page-border"; export * from "./line-number"; export * from "./vertical-align"; +export * from "./type"; diff --git a/src/file/document/body/section-properties/section-properties.spec.ts b/src/file/document/body/section-properties/section-properties.spec.ts index 70fc567b83..7fca1ae265 100644 --- a/src/file/document/body/section-properties/section-properties.spec.ts +++ b/src/file/document/body/section-properties/section-properties.spec.ts @@ -1,5 +1,6 @@ import { expect } from "chai"; +import { convertInchesToTwip } from "convenience-functions"; import { Formatter } from "export/formatter"; import { FooterWrapper } from "file/footer-wrapper"; import { HeaderWrapper } from "file/header-wrapper"; @@ -8,6 +9,7 @@ import { Media } from "file/media"; import { PageBorderOffsetFrom } from "./page-border"; import { PageNumberFormat } from "./page-number"; import { SectionProperties } from "./section-properties"; +import { SectionType } from "./type/section-type-attributes"; import { SectionVerticalAlignValue } from "./vertical-align"; describe("SectionProperties", () => { @@ -18,10 +20,10 @@ describe("SectionProperties", () => { const properties = new SectionProperties({ width: 11906, height: 16838, - top: 1440, - right: 1440, - bottom: 1440, - left: 1440, + top: convertInchesToTwip(1), + right: convertInchesToTwip(1), + bottom: convertInchesToTwip(1), + left: convertInchesToTwip(1), header: 708, footer: 708, gutter: 0, @@ -30,7 +32,7 @@ describe("SectionProperties", () => { space: 708, count: 1, }, - linePitch: 360, + linePitch: convertInchesToTwip(0.25), headers: { default: new HeaderWrapper(media, 100), }, @@ -198,5 +200,17 @@ describe("SectionProperties", () => { const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined); expect(pgNumType).to.equal(undefined); }); + + it("should create section properties with section type", () => { + const properties = new SectionProperties({ + type: SectionType.CONTINUOUS, + }); + const tree = new Formatter().format(properties); + expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]); + const type = tree["w:sectPr"].find((item) => item["w:type"] !== undefined); + expect(type).to.deep.equal({ + "w:type": { _attr: { "w:val": "continuous" } }, + }); + }); }); }); diff --git a/src/file/document/body/section-properties/section-properties.ts b/src/file/document/body/section-properties/section-properties.ts index 37e09e4498..e3fb1be2a4 100644 --- a/src/file/document/body/section-properties/section-properties.ts +++ b/src/file/document/body/section-properties/section-properties.ts @@ -1,4 +1,5 @@ // http://officeopenxml.com/WPsection.php +import { convertInchesToTwip } from "convenience-functions"; import { FooterWrapper } from "file/footer-wrapper"; import { HeaderWrapper } from "file/header-wrapper"; import { XmlComponent } from "file/xml-components"; @@ -18,6 +19,8 @@ import { IPageNumberTypeAttributes, PageNumberType } from "./page-number"; import { PageSize } from "./page-size/page-size"; import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes"; import { TitlePage } from "./title-page/title-page"; +import { Type } from "./type/section-type"; +import { SectionType } from "./type/section-type-attributes"; import { ISectionVerticalAlignAttributes, SectionVerticalAlign } from "./vertical-align"; export interface IHeaderFooterGroup { @@ -52,6 +55,7 @@ export type SectionPropertiesOptions = IPageSizeAttributes & readonly space?: number; readonly count?: number; }; + readonly type?: SectionType; }; // Need to decouple this from the attributes @@ -64,10 +68,10 @@ export class SectionProperties extends XmlComponent { const { width = 11906, height = 16838, - top = 1440, - right = 1440, - bottom = 1440, - left = 1440, + top = convertInchesToTwip(1), + right = convertInchesToTwip(1), + bottom = convertInchesToTwip(1), + left = convertInchesToTwip(1), header = 708, footer = 708, gutter = 0, @@ -90,6 +94,7 @@ export class SectionProperties extends XmlComponent { pageBorderLeft, titlePage = false, verticalAlign, + type, } = options; this.options = options; @@ -128,6 +133,10 @@ export class SectionProperties extends XmlComponent { if (verticalAlign) { this.root.push(new SectionVerticalAlign(verticalAlign)); } + + if (type) { + this.root.push(new Type(type)); + } } private addHeaders(headers?: IHeaderFooterGroup): void { @@ -136,7 +145,7 @@ export class SectionProperties extends XmlComponent { this.root.push( new HeaderReference({ headerType: HeaderReferenceType.DEFAULT, - headerId: headers.default.Header.ReferenceId, + headerId: headers.default.View.ReferenceId, }), ); } @@ -145,7 +154,7 @@ export class SectionProperties extends XmlComponent { this.root.push( new HeaderReference({ headerType: HeaderReferenceType.FIRST, - headerId: headers.first.Header.ReferenceId, + headerId: headers.first.View.ReferenceId, }), ); } @@ -154,7 +163,7 @@ export class SectionProperties extends XmlComponent { this.root.push( new HeaderReference({ headerType: HeaderReferenceType.EVEN, - headerId: headers.even.Header.ReferenceId, + headerId: headers.even.View.ReferenceId, }), ); } @@ -167,7 +176,7 @@ export class SectionProperties extends XmlComponent { this.root.push( new FooterReference({ footerType: FooterReferenceType.DEFAULT, - footerId: footers.default.Footer.ReferenceId, + footerId: footers.default.View.ReferenceId, }), ); } @@ -176,7 +185,7 @@ export class SectionProperties extends XmlComponent { this.root.push( new FooterReference({ footerType: FooterReferenceType.FIRST, - footerId: footers.first.Footer.ReferenceId, + footerId: footers.first.View.ReferenceId, }), ); } @@ -185,7 +194,7 @@ export class SectionProperties extends XmlComponent { this.root.push( new FooterReference({ footerType: FooterReferenceType.EVEN, - footerId: footers.even.Footer.ReferenceId, + footerId: footers.even.View.ReferenceId, }), ); } diff --git a/src/file/document/body/section-properties/type/index.ts b/src/file/document/body/section-properties/type/index.ts new file mode 100644 index 0000000000..fd7a8abd9c --- /dev/null +++ b/src/file/document/body/section-properties/type/index.ts @@ -0,0 +1,2 @@ +export * from "./section-type"; +export * from "./section-type-attributes"; diff --git a/src/file/document/body/section-properties/type/section-type-attributes.ts b/src/file/document/body/section-properties/type/section-type-attributes.ts new file mode 100644 index 0000000000..4ac8dd60b4 --- /dev/null +++ b/src/file/document/body/section-properties/type/section-type-attributes.ts @@ -0,0 +1,17 @@ +import { XmlAttributeComponent } from "file/xml-components"; + +export enum SectionType { + CONTINUOUS = "continuous", + EVEN_PAGE = "evenPage", + NEXT_COLUMN = "nextColumn", + NEXT_PAGE = "nextPage", + ODD_PAGE = "oddPage", +} + +export class SectionTypeAttributes extends XmlAttributeComponent<{ + readonly val: SectionType; +}> { + protected readonly xmlKeys = { + val: "w:val", + }; +} diff --git a/src/file/document/body/section-properties/type/section-type.spec.ts b/src/file/document/body/section-properties/type/section-type.spec.ts new file mode 100644 index 0000000000..7276825fab --- /dev/null +++ b/src/file/document/body/section-properties/type/section-type.spec.ts @@ -0,0 +1,35 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; +import { Type } from "./section-type"; +import { SectionType } from "./section-type-attributes"; + +describe("Type", () => { + it("should create with even page section type", () => { + const sectionType = new Type(SectionType.EVEN_PAGE); + + const tree = new Formatter().format(sectionType); + + expect(tree).to.deep.equal({ + "w:type": { + _attr: { + "w:val": "evenPage", + }, + }, + }); + }); + + it("should create with continuous section type", () => { + const sectionType = new Type(SectionType.CONTINUOUS); + + const tree = new Formatter().format(sectionType); + + expect(tree).to.deep.equal({ + "w:type": { + _attr: { + "w:val": "continuous", + }, + }, + }); + }); +}); diff --git a/src/file/document/body/section-properties/type/section-type.ts b/src/file/document/body/section-properties/type/section-type.ts new file mode 100644 index 0000000000..3a11f2e041 --- /dev/null +++ b/src/file/document/body/section-properties/type/section-type.ts @@ -0,0 +1,10 @@ +// http://officeopenxml.com/WPsection.php +import { XmlComponent } from "file/xml-components"; +import { SectionType, SectionTypeAttributes } from "./section-type-attributes"; + +export class Type extends XmlComponent { + constructor(value: SectionType) { + super("w:type"); + this.root.push(new SectionTypeAttributes({ val: value })); + } +} diff --git a/src/file/document/document-background/document-background.spec.ts b/src/file/document/document-background/document-background.spec.ts new file mode 100644 index 0000000000..83d1fb36b1 --- /dev/null +++ b/src/file/document/document-background/document-background.spec.ts @@ -0,0 +1,53 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { DocumentBackground } from "./document-background"; + +describe("DocumentBackground", () => { + describe("#constructor()", () => { + it("should create a DocumentBackground with no options and set color to auto", () => { + const documentBackground = new DocumentBackground({}); + const tree = new Formatter().format(documentBackground); + expect(tree).to.deep.equal({ + "w:background": { + _attr: { + "w:color": "FFFFFF", + }, + }, + }); + }); + + it("should create a DocumentBackground with no options and set color to value", () => { + const documentBackground = new DocumentBackground({ color: "ffff00" }); + const tree = new Formatter().format(documentBackground); + expect(tree).to.deep.equal({ + "w:background": { + _attr: { + "w:color": "ffff00", + }, + }, + }); + }); + + it("should create a DocumentBackground with no options and set other values", () => { + const documentBackground = new DocumentBackground({ + color: "ffff00", + themeColor: "test", + themeShade: "test", + themeTint: "test", + }); + const tree = new Formatter().format(documentBackground); + expect(tree).to.deep.equal({ + "w:background": { + _attr: { + "w:color": "ffff00", + "w:themeColor": "test", + "w:themeShade": "test", + "w:themeTint": "test", + }, + }, + }); + }); + }); +}); diff --git a/src/file/document/document-background/document-background.ts b/src/file/document/document-background/document-background.ts new file mode 100644 index 0000000000..44b04aabe6 --- /dev/null +++ b/src/file/document/document-background/document-background.ts @@ -0,0 +1,39 @@ +// http://officeopenxml.com/WPdocument.php +// http://www.datypic.com/sc/ooxml/e-w_background-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +export class DocumentBackgroundAttributes extends XmlAttributeComponent<{ + readonly color: string; + readonly themeColor?: string; + readonly themeShade?: string; + readonly themeTint?: string; +}> { + protected readonly xmlKeys = { + color: "w:color", + themeColor: "w:themeColor", + themeShade: "w:themeShade", + themeTint: "w:themeTint", + }; +} + +export interface IDocumentBackgroundOptions { + readonly color?: string; + readonly themeColor?: string; + readonly themeShade?: string; + readonly themeTint?: string; +} + +export class DocumentBackground extends XmlComponent { + constructor(options: IDocumentBackgroundOptions) { + super("w:background"); + + this.root.push( + new DocumentBackgroundAttributes({ + color: options.color ? options.color : "FFFFFF", + themeColor: options.themeColor, + themeShade: options.themeShade, + themeTint: options.themeTint, + }), + ); + } +} diff --git a/src/file/document/document-background/index.ts b/src/file/document/document-background/index.ts new file mode 100644 index 0000000000..604f3da1f2 --- /dev/null +++ b/src/file/document/document-background/index.ts @@ -0,0 +1 @@ +export * from "./document-background"; diff --git a/src/file/document/document.spec.ts b/src/file/document/document.spec.ts index dba489601e..d903ff78aa 100644 --- a/src/file/document/document.spec.ts +++ b/src/file/document/document.spec.ts @@ -8,7 +8,7 @@ describe("Document", () => { let document: Document; beforeEach(() => { - document = new Document(); + document = new Document({ background: {} }); }); describe("#constructor()", () => { @@ -38,6 +38,13 @@ describe("Document", () => { "mc:Ignorable": "w14 w15 wp14", }, }, + { + "w:background": { + _attr: { + "w:color": "FFFFFF", + }, + }, + }, { "w:body": {} }, ], }); diff --git a/src/file/document/document.ts b/src/file/document/document.ts index 80b04a379c..cc33709076 100644 --- a/src/file/document/document.ts +++ b/src/file/document/document.ts @@ -1,15 +1,20 @@ // http://officeopenxml.com/WPdocument.php import { XmlComponent } from "file/xml-components"; -import { Hyperlink, Paragraph } from "../paragraph"; +import { ConcreteHyperlink, Paragraph } from "../paragraph"; import { Table } from "../table"; import { TableOfContents } from "../table-of-contents"; import { Body } from "./body"; import { DocumentAttributes } from "./document-attributes"; +import { DocumentBackground, IDocumentBackgroundOptions } from "./document-background"; + +export interface IDocumentOptions { + readonly background: IDocumentBackgroundOptions; +} export class Document extends XmlComponent { private readonly body: Body; - constructor() { + constructor(options: IDocumentOptions) { super("w:document"); this.root.push( new DocumentAttributes({ @@ -33,10 +38,11 @@ export class Document extends XmlComponent { }), ); this.body = new Body(); + this.root.push(new DocumentBackground(options.background)); this.root.push(this.body); } - public add(item: Paragraph | Table | TableOfContents | Hyperlink): Document { + public add(item: Paragraph | Table | TableOfContents | ConcreteHyperlink): Document { this.body.push(item); return this; } diff --git a/src/file/document/index.ts b/src/file/document/index.ts index 3430666623..a93dd86f99 100644 --- a/src/file/document/index.ts +++ b/src/file/document/index.ts @@ -1,3 +1,4 @@ export * from "./document"; export * from "./document-attributes"; export * from "./body"; +export * from "./document-background"; diff --git a/src/file/drawing/anchor/anchor.spec.ts b/src/file/drawing/anchor/anchor.spec.ts index 6b86a31346..10d4b08d13 100644 --- a/src/file/drawing/anchor/anchor.spec.ts +++ b/src/file/drawing/anchor/anchor.spec.ts @@ -218,5 +218,150 @@ describe("Anchor", () => { const textWrap = newJson.root[6]; assert.equal(textWrap.rootKey, "wp:wrapTopAndBottom"); }); + + it("should create a Drawing with a margin", () => { + anchor = createAnchor({ + floating: { + verticalPosition: { + offset: 0, + }, + horizontalPosition: { + offset: 0, + }, + margins: { + top: 10, + left: 10, + bottom: 10, + right: 10, + }, + }, + }); + const newJson = Utility.jsonify(anchor); + const anchorAttributes = newJson.root[0].root; + assert.include(anchorAttributes, { + distT: 10, + distB: 10, + distL: 10, + distR: 10, + }); + }); + + it("should create a Drawing with a default margin", () => { + anchor = createAnchor({ + floating: { + verticalPosition: { + offset: 0, + }, + horizontalPosition: { + offset: 0, + }, + margins: {}, + }, + }); + const newJson = Utility.jsonify(anchor); + const anchorAttributes = newJson.root[0].root; + assert.include(anchorAttributes, { + distT: 0, + distB: 0, + distL: 0, + distR: 0, + }); + }); + + it("should create a Drawing with allowOverlap being false", () => { + anchor = createAnchor({ + floating: { + verticalPosition: { + offset: 0, + }, + horizontalPosition: { + offset: 0, + }, + allowOverlap: false, + }, + }); + const newJson = Utility.jsonify(anchor); + const anchorAttributes = newJson.root[0].root; + assert.include(anchorAttributes, { + allowOverlap: "0", + }); + }); + + it("should create a Drawing with behindDocument being true", () => { + anchor = createAnchor({ + floating: { + verticalPosition: { + offset: 0, + }, + horizontalPosition: { + offset: 0, + }, + behindDocument: true, + }, + }); + const newJson = Utility.jsonify(anchor); + const anchorAttributes = newJson.root[0].root; + assert.include(anchorAttributes, { + behindDoc: "1", + }); + }); + + it("should create a Drawing with locked being true", () => { + anchor = createAnchor({ + floating: { + verticalPosition: { + offset: 0, + }, + horizontalPosition: { + offset: 0, + }, + lockAnchor: true, + }, + }); + const newJson = Utility.jsonify(anchor); + const anchorAttributes = newJson.root[0].root; + assert.include(anchorAttributes, { + locked: "1", + }); + }); + + it("should create a Drawing with locked being false", () => { + anchor = createAnchor({ + floating: { + verticalPosition: { + offset: 0, + }, + horizontalPosition: { + offset: 0, + }, + layoutInCell: false, + }, + }); + const newJson = Utility.jsonify(anchor); + const anchorAttributes = newJson.root[0].root; + assert.include(anchorAttributes, { + layoutInCell: "0", + }); + }); + + it("should create a Drawing with a certain z-index", () => { + anchor = createAnchor({ + floating: { + verticalPosition: { + offset: 0, + }, + horizontalPosition: { + offset: 0, + }, + zIndex: 120, + }, + }); + const newJson = Utility.jsonify(anchor); + const anchorAttributes = newJson.root[0].root; + + assert.include(anchorAttributes, { + relativeHeight: 120, + }); + }); }); }); diff --git a/src/file/drawing/anchor/anchor.ts b/src/file/drawing/anchor/anchor.ts index d52c349ebb..7eb4487a89 100644 --- a/src/file/drawing/anchor/anchor.ts +++ b/src/file/drawing/anchor/anchor.ts @@ -11,42 +11,32 @@ import { Extent } from "./../extent/extent"; import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties"; import { AnchorAttributes } from "./anchor-attributes"; -const defaultOptions: IFloating = { - allowOverlap: true, - behindDocument: false, - lockAnchor: false, - layoutInCell: true, - verticalPosition: {}, - horizontalPosition: {}, -}; - export class Anchor extends XmlComponent { constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) { super("wp:anchor"); - const floating = { - margins: { - top: 0, - bottom: 0, - left: 0, - right: 0, - }, - ...defaultOptions, + const floating: IFloating = { + allowOverlap: true, + behindDocument: false, + lockAnchor: false, + layoutInCell: true, + verticalPosition: {}, + horizontalPosition: {}, ...drawingOptions.floating, }; this.root.push( new AnchorAttributes({ - distT: floating.margins.top || 0, - distB: floating.margins.bottom || 0, - distL: floating.margins.left || 0, - distR: floating.margins.right || 0, + distT: floating.margins ? floating.margins.top || 0 : 0, + distB: floating.margins ? floating.margins.bottom || 0 : 0, + distL: floating.margins ? floating.margins.left || 0 : 0, + distR: floating.margins ? floating.margins.right || 0 : 0, simplePos: "0", // note: word doesn't fully support - so we use 0 allowOverlap: floating.allowOverlap === true ? "1" : "0", behindDoc: floating.behindDocument === true ? "1" : "0", locked: floating.lockAnchor === true ? "1" : "0", layoutInCell: floating.layoutInCell === true ? "1" : "0", - relativeHeight: dimensions.emus.y, + relativeHeight: floating.zIndex ? floating.zIndex : dimensions.emus.y, }), ); diff --git a/src/file/drawing/floating/floating-position.ts b/src/file/drawing/floating/floating-position.ts index 9191ef1e1b..6c5ad7f332 100644 --- a/src/file/drawing/floating/floating-position.ts +++ b/src/file/drawing/floating/floating-position.ts @@ -1,4 +1,5 @@ // http://officeopenxml.com/drwPicFloating-position.php +// http://officeopenxml.com/drwPicFloating.php import { ITextWrapping } from "../text-wrap"; export enum HorizontalPositionRelativeFrom { @@ -67,4 +68,5 @@ export interface IFloating { readonly layoutInCell?: boolean; readonly margins?: IMargins; readonly wrap?: ITextWrapping; + readonly zIndex?: number; } diff --git a/src/file/drawing/inline/inline.ts b/src/file/drawing/inline/inline.ts index 7c40e7c3b3..bf83e36764 100644 --- a/src/file/drawing/inline/inline.ts +++ b/src/file/drawing/inline/inline.ts @@ -12,7 +12,7 @@ export class Inline extends XmlComponent { private readonly extent: Extent; private readonly graphic: Graphic; - constructor(readonly mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) { + constructor(mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) { super("wp:inline"); this.root.push( diff --git a/src/file/file.spec.ts b/src/file/file.spec.ts index 6ec30e1be9..952fd6a35c 100644 --- a/src/file/file.spec.ts +++ b/src/file/file.spec.ts @@ -5,7 +5,7 @@ import { Formatter } from "export/formatter"; import { File } from "./file"; import { Footer, Header } from "./header"; -import { HyperlinkRef, Paragraph } from "./paragraph"; +import { Paragraph } from "./paragraph"; import { Table, TableCell, TableRow } from "./table"; import { TableOfContents } from "./table-of-contents"; @@ -18,7 +18,7 @@ describe("File", () => { children: [], }); - const tree = new Formatter().format(doc.Document.Body); + const tree = new Formatter().format(doc.Document.View.Body); expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default"); @@ -37,7 +37,7 @@ describe("File", () => { children: [], }); - const tree = new Formatter().format(doc.Document.Body); + const tree = new Formatter().format(doc.Document.View.Body); expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default"); @@ -56,7 +56,7 @@ describe("File", () => { children: [], }); - const tree = new Formatter().format(doc.Document.Body); + const tree = new Formatter().format(doc.Document.View.Body); expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first"); expect(tree["w:body"][0]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("first"); @@ -79,7 +79,7 @@ describe("File", () => { children: [], }); - const tree = new Formatter().format(doc.Document.Body); + const tree = new Formatter().format(doc.Document.View.Body); expect(tree["w:body"][0]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default"); expect(tree["w:body"][0]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first"); @@ -97,7 +97,7 @@ describe("File", () => { }, ]); - const tree = new Formatter().format(doc.Document.Body); + const tree = new Formatter().format(doc.Document.View.Body); expect(tree).to.deep.equal({ "w:body": [ @@ -164,22 +164,12 @@ describe("File", () => { ], }); }); - - it("should add hyperlink child", () => { - const doc = new File(undefined, undefined, [ - { - children: [new HyperlinkRef("test")], - }, - ]); - - expect(doc.HyperlinkCache).to.deep.equal({}); - }); }); describe("#addSection", () => { it("should call the underlying document's add a Paragraph", () => { const file = new File(); - const spy = sinon.spy(file.Document, "add"); + const spy = sinon.spy(file.Document.View, "add"); file.addSection({ children: [new Paragraph({})], }); @@ -187,19 +177,9 @@ describe("File", () => { expect(spy.called).to.equal(true); }); - it("should add hyperlink child", () => { - const doc = new File(); - - doc.addSection({ - children: [new HyperlinkRef("test")], - }); - - expect(doc.HyperlinkCache).to.deep.equal({}); - }); - it("should call the underlying document's add when adding a Table", () => { const file = new File(); - const spy = sinon.spy(file.Document, "add"); + const spy = sinon.spy(file.Document.View, "add"); file.addSection({ children: [ new Table({ @@ -221,7 +201,7 @@ describe("File", () => { it("should call the underlying document's add when adding an Image (paragraph)", () => { const file = new File(); - const spy = sinon.spy(file.Document, "add"); + const spy = sinon.spy(file.Document.View, "add"); // tslint:disable-next-line:no-any file.addSection({ children: [new Paragraph("")], @@ -234,7 +214,7 @@ describe("File", () => { describe("#addSection", () => { it("should call the underlying document's add", () => { const file = new File(); - const spy = sinon.spy(file.Document, "add"); + const spy = sinon.spy(file.Document.View, "add"); file.addSection({ children: [new TableOfContents()], }); @@ -243,11 +223,16 @@ describe("File", () => { }); }); - describe("#HyperlinkCache", () => { - it("should initially have empty hyperlink cache", () => { - const file = new File(); + describe("#addTrackRevisionsFeature", () => { + it("should call the underlying document's add", () => { + const file = new File({ + features: { + trackRevisions: true, + }, + }); - expect(file.HyperlinkCache).to.deep.equal({}); + // tslint:disable-next-line: no-unused-expression no-string-literal + expect(file.Settings["trackRevisions"]).to.exist; }); }); diff --git a/src/file/file.ts b/src/file/file.ts index 4ecedaab47..e00d57e532 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -1,9 +1,8 @@ -import * as shortid from "shortid"; import { AppProperties } from "./app-properties/app-properties"; import { ContentTypes } from "./content-types/content-types"; import { CoreProperties, IPropertiesOptions } from "./core-properties"; import { CustomProperties, ICustomPropertyOptions } from "./custom-properties"; -import { Document } from "./document"; +import { DocumentWrapper } from "./document-wrapper"; import { FooterReferenceType, HeaderReferenceType, @@ -18,9 +17,8 @@ import { Footer, Header } from "./header"; import { HeaderWrapper, IDocumentHeader } from "./header-wrapper"; import { Media } from "./media"; import { Numbering } from "./numbering"; -import { Hyperlink, HyperlinkRef, HyperlinkType, Paragraph } from "./paragraph"; +import { Paragraph } from "./paragraph"; import { Relationships } from "./relationships"; -import { TargetModeType } from "./relationships/relationship/relationship"; import { Settings } from "./settings"; import { Styles } from "./styles"; import { ExternalStylesFactory } from "./styles/external-styles-factory"; @@ -42,17 +40,16 @@ export interface ISectionOptions { readonly size?: IPageSizeAttributes; readonly margins?: IPageMarginAttributes; readonly properties?: SectionPropertiesOptions; - readonly children: (Paragraph | Table | TableOfContents | HyperlinkRef)[]; + readonly children: (Paragraph | Table | TableOfContents)[]; } export class File { // tslint:disable-next-line:readonly-keyword private currentRelationshipId: number = 1; - private readonly document: Document; + private readonly documentWrapper: DocumentWrapper; private readonly headers: IDocumentHeader[] = []; private readonly footers: IDocumentFooter[] = []; - private readonly docRelationships: Relationships; private readonly coreProperties: CoreProperties; private readonly numbering: Numbering; private readonly media: Media; @@ -63,7 +60,6 @@ export class File { private readonly customProperties: CustomProperties; private readonly appProperties: AppProperties; private readonly styles: Styles; - private readonly hyperlinkCache: { readonly [key: string]: Hyperlink } = {}; constructor( options: IPropertiesOptions = { @@ -83,14 +79,16 @@ export class File { config: [], }, ); - this.docRelationships = new Relationships(); + // this.documentWrapper.Relationships = new Relationships(); this.fileRelationships = new Relationships(); this.customProperties = new CustomProperties(customProperties); this.appProperties = new AppProperties(); this.footNotes = new FootNotes(); this.contentTypes = new ContentTypes(); - this.document = new Document(); - this.settings = new Settings(); + this.documentWrapper = new DocumentWrapper({ background: options.background || {} }); + this.settings = new Settings({ + compatabilityModeVersion: options.compatabilityModeVersion, + }); this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media(); @@ -110,7 +108,7 @@ export class File { this.styles = stylesFactory.newInstance(options.externalStyles); } else if (options.styles) { const stylesFactory = new DefaultStylesFactory(); - const defaultStyles = stylesFactory.newInstance(); + const defaultStyles = stylesFactory.newInstance(options.styles.default); this.styles = new Styles({ ...defaultStyles, ...options.styles, @@ -135,16 +133,10 @@ export class File { } for (const section of sections) { - this.document.Body.addSection(section.properties ? section.properties : {}); + this.documentWrapper.View.Body.addSection(section.properties ? section.properties : {}); for (const child of section.children) { - if (child instanceof HyperlinkRef) { - const hyperlink = this.hyperlinkCache[child.id]; - this.document.add(hyperlink); - continue; - } - - this.document.add(child); + this.documentWrapper.View.add(child); } } @@ -154,25 +146,10 @@ export class File { } } - if (options.hyperlinks) { - const cache = {}; - - for (const key in options.hyperlinks) { - if (!options.hyperlinks[key]) { - continue; - } - - const hyperlinkRef = options.hyperlinks[key]; - - const hyperlink = - hyperlinkRef.type === HyperlinkType.EXTERNAL - ? this.createHyperlink(hyperlinkRef.link, hyperlinkRef.text) - : this.createInternalHyperLink(key, hyperlinkRef.text); - - cache[key] = hyperlink; + if (options.features) { + if (options.features.trackRevisions) { + this.settings.addTrackRevisions(); } - - this.hyperlinkCache = cache; } } @@ -184,7 +161,7 @@ export class File { properties, children, }: ISectionOptions): void { - this.document.Body.addSection({ + this.documentWrapper.View.Body.addSection({ ...properties, headers: { default: headers.default ? this.createHeader(headers.default) : this.createHeader(new Header()), @@ -201,40 +178,16 @@ export class File { }); for (const child of children) { - if (child instanceof HyperlinkRef) { - const hyperlink = this.hyperlinkCache[child.id]; - this.document.add(hyperlink); - continue; - } - - this.document.add(child); + this.documentWrapper.View.add(child); } } public verifyUpdateFields(): void { - if (this.document.getTablesOfContents().length) { + if (this.documentWrapper.View.getTablesOfContents().length) { this.settings.addUpdateFields(); } } - private createHyperlink(link: string, text: string = link): Hyperlink { - const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase()); - this.docRelationships.createRelationship( - hyperlink.linkId, - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", - link, - TargetModeType.EXTERNAL, - ); - return hyperlink; - } - - private createInternalHyperLink(anchor: string, text: string = anchor): Hyperlink { - const hyperlink = new Hyperlink(text, shortid.generate().toLowerCase(), anchor); - // NOTE: unlike File#createHyperlink(), since the link is to an internal bookmark - // we don't need to create a new relationship. - return hyperlink; - } - private createHeader(header: Header): HeaderWrapper { const wrapper = new HeaderWrapper(this.media, this.currentRelationshipId++); @@ -259,8 +212,8 @@ export class File { private addHeaderToDocument(header: HeaderWrapper, type: HeaderReferenceType = HeaderReferenceType.DEFAULT): void { this.headers.push({ header, type }); - this.docRelationships.createRelationship( - header.Header.ReferenceId, + this.documentWrapper.Relationships.createRelationship( + header.View.ReferenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header", `header${this.headers.length}.xml`, ); @@ -269,8 +222,8 @@ export class File { private addFooterToDocument(footer: FooterWrapper, type: FooterReferenceType = FooterReferenceType.DEFAULT): void { this.footers.push({ footer, type }); - this.docRelationships.createRelationship( - footer.Footer.ReferenceId, + this.documentWrapper.Relationships.createRelationship( + footer.View.ReferenceId, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer", `footer${this.footers.length}.xml`, ); @@ -299,30 +252,30 @@ export class File { "docProps/custom.xml", ); - this.docRelationships.createRelationship( + this.documentWrapper.Relationships.createRelationship( this.currentRelationshipId++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles", "styles.xml", ); - this.docRelationships.createRelationship( + this.documentWrapper.Relationships.createRelationship( this.currentRelationshipId++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering", "numbering.xml", ); - this.docRelationships.createRelationship( + this.documentWrapper.Relationships.createRelationship( this.currentRelationshipId++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes", "footnotes.xml", ); - this.docRelationships.createRelationship( + this.documentWrapper.Relationships.createRelationship( this.currentRelationshipId++, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings", "settings.xml", ); } - public get Document(): Document { - return this.document; + public get Document(): DocumentWrapper { + return this.documentWrapper; } public get Styles(): Styles { @@ -341,10 +294,6 @@ export class File { return this.media; } - public get DocumentRelationships(): Relationships { - return this.docRelationships; - } - public get FileRelationships(): Relationships { return this.fileRelationships; } @@ -376,8 +325,4 @@ export class File { public get Settings(): Settings { return this.settings; } - - public get HyperlinkCache(): { readonly [key: string]: Hyperlink } { - return this.hyperlinkCache; - } } diff --git a/src/file/footer-wrapper.spec.ts b/src/file/footer-wrapper.spec.ts index 4f3e95acdb..2a32ad7968 100644 --- a/src/file/footer-wrapper.spec.ts +++ b/src/file/footer-wrapper.spec.ts @@ -10,7 +10,7 @@ describe("FooterWrapper", () => { describe("#add", () => { it("should call the underlying footer's addParagraph", () => { const file = new FooterWrapper(new Media(), 1); - const spy = sinon.spy(file.Footer, "add"); + const spy = sinon.spy(file.View, "add"); file.add(new Paragraph({})); expect(spy.called).to.equal(true); @@ -18,7 +18,7 @@ describe("FooterWrapper", () => { it("should call the underlying footer's addParagraph", () => { const file = new FooterWrapper(new Media(), 1); - const spy = sinon.spy(file.Footer, "add"); + const spy = sinon.spy(file.View, "add"); file.add( new Table({ rows: [ @@ -40,7 +40,7 @@ describe("FooterWrapper", () => { describe("#addChildElement", () => { it("should call the underlying footer's addChildElement", () => { const file = new FooterWrapper(new Media(), 1); - const spy = sinon.spy(file.Footer, "addChildElement"); + const spy = sinon.spy(file.View, "addChildElement"); // tslint:disable-next-line:no-any file.addChildElement({} as any); diff --git a/src/file/footer-wrapper.ts b/src/file/footer-wrapper.ts index 8390887ba4..e16a72e974 100644 --- a/src/file/footer-wrapper.ts +++ b/src/file/footer-wrapper.ts @@ -1,6 +1,7 @@ import { XmlComponent } from "file/xml-components"; import { FooterReferenceType } from "./document"; +import { IViewWrapper } from "./document-wrapper"; import { Footer } from "./footer/footer"; import { Media } from "./media"; import { Paragraph } from "./paragraph"; @@ -12,7 +13,7 @@ export interface IDocumentFooter { readonly type: FooterReferenceType; } -export class FooterWrapper { +export class FooterWrapper implements IViewWrapper { private readonly footer: Footer; private readonly relationships: Relationships; @@ -29,7 +30,7 @@ export class FooterWrapper { this.footer.addChildElement(childElement); } - public get Footer(): Footer { + public get View(): Footer { return this.footer; } diff --git a/src/file/header-wrapper.spec.ts b/src/file/header-wrapper.spec.ts index 00ee776a95..c6a7272d16 100644 --- a/src/file/header-wrapper.spec.ts +++ b/src/file/header-wrapper.spec.ts @@ -10,7 +10,7 @@ describe("HeaderWrapper", () => { describe("#add", () => { it("should call the underlying header's addChildElement for Paragraph", () => { const wrapper = new HeaderWrapper(new Media(), 1); - const spy = sinon.spy(wrapper.Header, "add"); + const spy = sinon.spy(wrapper.View, "add"); wrapper.add(new Paragraph({})); expect(spy.called).to.equal(true); @@ -18,7 +18,7 @@ describe("HeaderWrapper", () => { it("should call the underlying header's addChildElement for Table", () => { const wrapper = new HeaderWrapper(new Media(), 1); - const spy = sinon.spy(wrapper.Header, "add"); + const spy = sinon.spy(wrapper.View, "add"); wrapper.add( new Table({ rows: [ @@ -40,7 +40,7 @@ describe("HeaderWrapper", () => { describe("#addChildElement", () => { it("should call the underlying header's addChildElement", () => { const file = new HeaderWrapper(new Media(), 1); - const spy = sinon.spy(file.Header, "addChildElement"); + const spy = sinon.spy(file.View, "addChildElement"); // tslint:disable-next-line:no-any file.addChildElement({} as any); diff --git a/src/file/header-wrapper.ts b/src/file/header-wrapper.ts index 407399a8fe..945a9d674a 100644 --- a/src/file/header-wrapper.ts +++ b/src/file/header-wrapper.ts @@ -1,6 +1,7 @@ import { XmlComponent } from "file/xml-components"; import { HeaderReferenceType } from "./document"; +import { IViewWrapper } from "./document-wrapper"; import { Header } from "./header/header"; import { Media } from "./media"; import { Paragraph } from "./paragraph"; @@ -12,7 +13,7 @@ export interface IDocumentHeader { readonly type: HeaderReferenceType; } -export class HeaderWrapper { +export class HeaderWrapper implements IViewWrapper { private readonly header: Header; private readonly relationships: Relationships; @@ -31,7 +32,7 @@ export class HeaderWrapper { this.header.addChildElement(childElement); } - public get Header(): Header { + public get View(): Header { return this.header; } diff --git a/src/file/index.ts b/src/file/index.ts index c4ce99e345..18f0a595e4 100644 --- a/src/file/index.ts +++ b/src/file/index.ts @@ -13,3 +13,4 @@ export * from "./header-wrapper"; export * from "./footer-wrapper"; export * from "./header"; export * from "./footnotes"; +export * from "./track-revision"; diff --git a/src/file/media/media.ts b/src/file/media/media.ts index 71e7c6f60e..65a7f1bf58 100644 --- a/src/file/media/media.ts +++ b/src/file/media/media.ts @@ -1,14 +1,12 @@ import { IDrawingOptions } from "../drawing"; import { File } from "../file"; -import { FooterWrapper } from "../footer-wrapper"; -import { HeaderWrapper } from "../header-wrapper"; import { PictureRun } from "../paragraph"; import { IMediaData } from "./data"; // import { Image } from "./image"; export class Media { public static addImage( - file: File | HeaderWrapper | FooterWrapper, + file: File, buffer: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number, @@ -82,7 +80,7 @@ export class Media { return imageData; } - public get Array(): IMediaData[] { + public get Array(): readonly IMediaData[] { const array = new Array(); this.map.forEach((data) => { diff --git a/src/file/numbering/abstract-numbering.spec.ts b/src/file/numbering/abstract-numbering.spec.ts index ef1df0de4c..df5416f453 100644 --- a/src/file/numbering/abstract-numbering.spec.ts +++ b/src/file/numbering/abstract-numbering.spec.ts @@ -7,7 +7,7 @@ import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph"; import { UnderlineType } from "../paragraph/run/underline"; import { ShadingType } from "../table"; import { AbstractNumbering } from "./abstract-numbering"; -import { LevelSuffix } from "./level"; +import { LevelFormat, LevelSuffix } from "./level"; describe("AbstractNumbering", () => { it("stores its ID at its .id property", () => { @@ -20,7 +20,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 3, - format: "lowerLetter", + format: LevelFormat.LOWER_LETTER, text: "%1)", alignment: AlignmentType.END, }, @@ -29,7 +29,7 @@ describe("AbstractNumbering", () => { expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "end" } } }); - expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } }); + expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": LevelFormat.LOWER_LETTER } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } }); }); @@ -37,7 +37,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 3, - format: "lowerLetter", + format: LevelFormat.LOWER_LETTER, text: "%1)", }, ]); @@ -45,7 +45,7 @@ describe("AbstractNumbering", () => { expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "start" } } }); - expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } }); + expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": LevelFormat.LOWER_LETTER } } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } }); }); @@ -53,7 +53,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 3, - format: "lowerLetter", + format: LevelFormat.LOWER_LETTER, text: "%1)", alignment: AlignmentType.END, suffix: LevelSuffix.SPACE, @@ -68,7 +68,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -87,7 +87,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -106,7 +106,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -125,7 +125,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -144,7 +144,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -163,7 +163,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -182,7 +182,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -216,7 +216,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -239,7 +239,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -262,7 +262,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -281,7 +281,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { paragraph: { @@ -324,7 +324,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { size, sizeComplexScript }, @@ -340,7 +340,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -359,7 +359,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -378,7 +378,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -398,7 +398,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -417,7 +417,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -436,7 +436,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -455,7 +455,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -485,7 +485,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -533,7 +533,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { bold, boldComplexScript }, @@ -566,7 +566,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { italics, italicsComplexScript }, @@ -604,7 +604,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { highlight, highlightComplexScript }, @@ -682,7 +682,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { shadow, shading, shadingComplexScript }, @@ -699,7 +699,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -718,7 +718,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -739,7 +739,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -763,7 +763,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -782,7 +782,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { @@ -804,7 +804,7 @@ describe("AbstractNumbering", () => { const abstractNumbering = new AbstractNumbering(1, [ { level: 0, - format: "lowerRoman", + format: LevelFormat.LOWER_ROMAN, text: "%0.", style: { run: { diff --git a/src/file/numbering/level.ts b/src/file/numbering/level.ts index 6a68f0712f..22ff07acf3 100644 --- a/src/file/numbering/level.ts +++ b/src/file/numbering/level.ts @@ -1,8 +1,26 @@ +// http://officeopenxml.com/WPnumbering-numFmt.php import { Attributes, XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { AlignmentType } from "../paragraph/formatting"; import { IParagraphStylePropertiesOptions, ParagraphProperties } from "../paragraph/properties"; import { IRunStylePropertiesOptions, RunProperties } from "../paragraph/run/properties"; +export enum LevelFormat { + BULLET = "bullet", + CARDINAL_TEXT = "cardinalText", + CHICAGO = "chicago", + DECIMAL = "decimal", + DECIMAL_ENCLOSED_CIRCLE = "decimalEnclosedCircle", + DECIMAL_ENCLOSED_FULLSTOP = "decimalEnclosedFullstop", + DECIMAL_ENCLOSED_PARENTHESES = "decimalEnclosedParen", + DECIMAL_ZERO = "decimalZero", + LOWER_LETTER = "lowerLetter", + LOWER_ROMAN = "lowerRoman", + NONE = "none", + ORDINAL_TEXT = "ordinalText", + UPPER_LETTER = "upperLetter", + UPPER_ROMAN = "upperRoman", +} + interface ILevelAttributesProperties { readonly ilvl?: number; readonly tentative?: number; @@ -67,7 +85,7 @@ export enum LevelSuffix { export interface ILevelsOptions { readonly level: number; - readonly format?: string; + readonly format?: LevelFormat; readonly text?: string; readonly alignment?: AlignmentType; readonly start?: number; diff --git a/src/file/numbering/numbering.ts b/src/file/numbering/numbering.ts index a02caf6bef..901f13f4bb 100644 --- a/src/file/numbering/numbering.ts +++ b/src/file/numbering/numbering.ts @@ -1,10 +1,11 @@ // http://officeopenxml.com/WPnumbering.php +import { convertInchesToTwip } from "convenience-functions"; import { AlignmentType } from "file/paragraph"; import { IXmlableObject, XmlComponent } from "file/xml-components"; import { DocumentAttributes } from "../document/document-attributes"; import { AbstractNumbering } from "./abstract-numbering"; -import { ILevelsOptions } from "./level"; +import { ILevelsOptions, LevelFormat } from "./level"; import { ConcreteNumbering } from "./num"; export interface INumberingOptions { @@ -50,100 +51,100 @@ export class Numbering extends XmlComponent { const abstractNumbering = this.createAbstractNumbering([ { level: 0, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25CF", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 720, hanging: 360 }, + indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 1, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25CB", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 1440, hanging: 360 }, + indent: { left: convertInchesToTwip(1), hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 2, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25A0", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 2160, hanging: 360 }, + indent: { left: 2160, hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 3, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25CF", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 2880, hanging: 360 }, + indent: { left: 2880, hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 4, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25CB", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 3600, hanging: 360 }, + indent: { left: 3600, hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 5, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25A0", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 4320, hanging: 360 }, + indent: { left: 4320, hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 6, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25CF", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 5040, hanging: 360 }, + indent: { left: 5040, hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 7, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25CF", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 5760, hanging: 360 }, + indent: { left: 5760, hanging: convertInchesToTwip(0.25) }, }, }, }, { level: 8, - format: "bullet", + format: LevelFormat.BULLET, text: "\u25CF", alignment: AlignmentType.LEFT, style: { paragraph: { - indent: { left: 6480, hanging: 360 }, + indent: { left: 6480, hanging: convertInchesToTwip(0.25) }, }, }, }, diff --git a/src/file/paragraph/index.ts b/src/file/paragraph/index.ts index 68a1b0b800..d68c1d7f03 100644 --- a/src/file/paragraph/index.ts +++ b/src/file/paragraph/index.ts @@ -3,3 +3,4 @@ export * from "./paragraph"; export * from "./properties"; export * from "./run"; export * from "./links"; +export * from "./math"; diff --git a/src/file/paragraph/links/hyperlink.spec.ts b/src/file/paragraph/links/hyperlink.spec.ts index 4b06a933f5..50d6aa76aa 100644 --- a/src/file/paragraph/links/hyperlink.spec.ts +++ b/src/file/paragraph/links/hyperlink.spec.ts @@ -2,14 +2,20 @@ import { expect } from "chai"; import { Formatter } from "export/formatter"; -import { Hyperlink } from "./"; -import { HyperlinkRef } from "./hyperlink"; +import { TextRun } from "../run"; +import { ConcreteHyperlink, ExternalHyperlink, InternalHyperlink } from "./hyperlink"; -describe("Hyperlink", () => { - let hyperlink: Hyperlink; +describe("ConcreteHyperlink", () => { + let hyperlink: ConcreteHyperlink; beforeEach(() => { - hyperlink = new Hyperlink("https://example.com", "superid"); + hyperlink = new ConcreteHyperlink( + new TextRun({ + text: "https://example.com", + style: "Hyperlink", + }), + "superid", + ); }); describe("#constructor()", () => { @@ -35,7 +41,14 @@ describe("Hyperlink", () => { describe("with optional anchor parameter", () => { beforeEach(() => { - hyperlink = new Hyperlink("Anchor Text", "superid2", "anchor"); + hyperlink = new ConcreteHyperlink( + new TextRun({ + text: "Anchor Text", + style: "Hyperlink", + }), + "superid2", + "anchor", + ); }); it("should create an internal link with anchor tag", () => { @@ -61,10 +74,53 @@ describe("Hyperlink", () => { }); }); -describe("HyperlinkRef", () => { +describe("ExternalHyperlink", () => { describe("#constructor()", () => { - const hyperlinkRef = new HyperlinkRef("test-id"); + it("should create", () => { + const externalHyperlink = new ExternalHyperlink({ + child: new TextRun("test"), + link: "http://www.google.com", + }); - expect(hyperlinkRef.id).to.equal("test-id"); + expect(externalHyperlink.options.link).to.equal("http://www.google.com"); + }); + }); +}); + +describe("InternalHyperlink", () => { + describe("#constructor()", () => { + it("should create", () => { + const internalHyperlink = new InternalHyperlink({ + child: new TextRun("test"), + anchor: "test-id", + }); + + const tree = new Formatter().format(internalHyperlink); + + expect(tree).to.deep.equal({ + "w:hyperlink": [ + { + _attr: { + "w:anchor": "test-id", + "w:history": 1, + }, + }, + { + "w:r": [ + { + "w:t": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "test", + ], + }, + ], + }, + ], + }); + }); }); }); diff --git a/src/file/paragraph/links/hyperlink.ts b/src/file/paragraph/links/hyperlink.ts index 302acfd603..a7512e7beb 100644 --- a/src/file/paragraph/links/hyperlink.ts +++ b/src/file/paragraph/links/hyperlink.ts @@ -1,6 +1,9 @@ // http://officeopenxml.com/WPhyperlink.php +import * as shortid from "shortid"; + import { XmlComponent } from "file/xml-components"; -import { TextRun } from "../run"; + +import { ParagraphChild } from "../paragraph"; import { HyperlinkAttributes, IHyperlinkAttributesProperties } from "./hyperlink-attributes"; export enum HyperlinkType { @@ -8,15 +11,10 @@ export enum HyperlinkType { EXTERNAL = "EXTERNAL", } -export class HyperlinkRef { - constructor(public readonly id: string) {} -} - -export class Hyperlink extends XmlComponent { +export class ConcreteHyperlink extends XmlComponent { public readonly linkId: string; - private readonly textRun: TextRun; - constructor(text: string, relationshipId: string, anchor?: string) { + constructor(child: ParagraphChild, relationshipId: string, anchor?: string) { super("w:hyperlink"); this.linkId = relationshipId; @@ -29,14 +27,16 @@ export class Hyperlink extends XmlComponent { const attributes = new HyperlinkAttributes(props); this.root.push(attributes); - this.textRun = new TextRun({ - text: text, - style: "Hyperlink", - }); - this.root.push(this.textRun); - } - - public get TextRun(): TextRun { - return this.textRun; + this.root.push(child); } } + +export class InternalHyperlink extends ConcreteHyperlink { + constructor(options: { readonly child: ParagraphChild; readonly anchor: string }) { + super(options.child, shortid.generate().toLowerCase(), options.anchor); + } +} + +export class ExternalHyperlink { + constructor(public readonly options: { readonly child: ParagraphChild; readonly link: string }) {} +} diff --git a/src/file/paragraph/math/brackets/index.ts b/src/file/paragraph/math/brackets/index.ts new file mode 100644 index 0000000000..e6559cde29 --- /dev/null +++ b/src/file/paragraph/math/brackets/index.ts @@ -0,0 +1,4 @@ +export * from "./math-round-brackets"; +export * from "./math-square-brackets"; +export * from "./math-curly-brackets"; +export * from "./math-angled-brackets"; diff --git a/src/file/paragraph/math/brackets/math-angled-brackets.spec.ts b/src/file/paragraph/math/brackets/math-angled-brackets.spec.ts new file mode 100644 index 0000000000..ccd73b192b --- /dev/null +++ b/src/file/paragraph/math/brackets/math-angled-brackets.spec.ts @@ -0,0 +1,51 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathAngledBrackets } from "./math-angled-brackets"; + +describe("MathAngledBrackets", () => { + describe("#constructor()", () => { + it("should create a MathAngledBrackets with correct root key", () => { + const mathAngledBrackets = new MathAngledBrackets({ + children: [new MathRun("60")], + }); + + const tree = new Formatter().format(mathAngledBrackets); + expect(tree).to.deep.equal({ + "m:d": [ + { + "m:dPr": [ + { + "m:begChr": { + _attr: { + "m:val": "〈", + }, + }, + }, + { + "m:endChr": { + _attr: { + "m:val": "〉", + }, + }, + }, + ], + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["60"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/brackets/math-angled-brackets.ts b/src/file/paragraph/math/brackets/math-angled-brackets.ts new file mode 100644 index 0000000000..48fe0d415d --- /dev/null +++ b/src/file/paragraph/math/brackets/math-angled-brackets.ts @@ -0,0 +1,20 @@ +// http://www.datypic.com/sc/ooxml/e-m_d-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathBase } from "../n-ary"; +import { MathBracketProperties } from "./math-bracket-properties"; + +export class MathAngledBrackets extends XmlComponent { + constructor(options: { readonly children: MathComponent[] }) { + super("m:d"); + + this.root.push( + new MathBracketProperties({ + beginningCharacter: "〈", + endingCharacter: "〉", + }), + ); + this.root.push(new MathBase(options.children)); + } +} diff --git a/src/file/paragraph/math/brackets/math-beginning-character.spec.ts b/src/file/paragraph/math/brackets/math-beginning-character.spec.ts new file mode 100644 index 0000000000..bf9196c0a9 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-beginning-character.spec.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathBeginningCharacter } from "./math-beginning-character"; + +describe("MathBeginningCharacter", () => { + describe("#constructor()", () => { + it("should create a MathBeginningCharacter with correct root key", () => { + const mathBeginningCharacter = new MathBeginningCharacter("["); + + const tree = new Formatter().format(mathBeginningCharacter); + expect(tree).to.deep.equal({ + "m:begChr": { + _attr: { + "m:val": "[", + }, + }, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/brackets/math-beginning-character.ts b/src/file/paragraph/math/brackets/math-beginning-character.ts new file mode 100644 index 0000000000..aa9e06d87a --- /dev/null +++ b/src/file/paragraph/math/brackets/math-beginning-character.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_begChr-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class MathBeginningCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> { + protected readonly xmlKeys = { character: "m:val" }; +} + +export class MathBeginningCharacter extends XmlComponent { + constructor(character: string) { + super("m:begChr"); + + this.root.push(new MathBeginningCharacterAttributes({ character })); + } +} diff --git a/src/file/paragraph/math/brackets/math-bracket-properties.spec.ts b/src/file/paragraph/math/brackets/math-bracket-properties.spec.ts new file mode 100644 index 0000000000..d80976d8f6 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-bracket-properties.spec.ts @@ -0,0 +1,45 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathBracketProperties } from "./math-bracket-properties"; + +describe("MathBracketProperties", () => { + describe("#constructor()", () => { + it("should create a MathBracketProperties with correct root key", () => { + const mathBracketProperties = new MathBracketProperties(); + + const tree = new Formatter().format(mathBracketProperties); + expect(tree).to.deep.equal({ + "m:dPr": {}, + }); + }); + + it("should create a MathBracketProperties with correct root key and add brackets", () => { + const mathBracketProperties = new MathBracketProperties({ + beginningCharacter: "[", + endingCharacter: "]", + }); + + const tree = new Formatter().format(mathBracketProperties); + expect(tree).to.deep.equal({ + "m:dPr": [ + { + "m:begChr": { + _attr: { + "m:val": "[", + }, + }, + }, + { + "m:endChr": { + _attr: { + "m:val": "]", + }, + }, + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/brackets/math-bracket-properties.ts b/src/file/paragraph/math/brackets/math-bracket-properties.ts new file mode 100644 index 0000000000..5bba7bf935 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-bracket-properties.ts @@ -0,0 +1,16 @@ +// http://www.datypic.com/sc/ooxml/e-m_dPr-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathBeginningCharacter } from "./math-beginning-character"; +import { MathEndingCharacter } from "./math-ending-char"; + +export class MathBracketProperties extends XmlComponent { + constructor(options?: { readonly beginningCharacter: string; readonly endingCharacter: string }) { + super("m:dPr"); + + if (!!options) { + this.root.push(new MathBeginningCharacter(options.beginningCharacter)); + this.root.push(new MathEndingCharacter(options.endingCharacter)); + } + } +} diff --git a/src/file/paragraph/math/brackets/math-curly-brackets.spec.ts b/src/file/paragraph/math/brackets/math-curly-brackets.spec.ts new file mode 100644 index 0000000000..d6defd57ef --- /dev/null +++ b/src/file/paragraph/math/brackets/math-curly-brackets.spec.ts @@ -0,0 +1,51 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathCurlyBrackets } from "./math-curly-brackets"; + +describe("MathCurlyBrackets", () => { + describe("#constructor()", () => { + it("should create a MathCurlyBrackets with correct root key", () => { + const mathCurlyBrackets = new MathCurlyBrackets({ + children: [new MathRun("60")], + }); + + const tree = new Formatter().format(mathCurlyBrackets); + expect(tree).to.deep.equal({ + "m:d": [ + { + "m:dPr": [ + { + "m:begChr": { + _attr: { + "m:val": "{", + }, + }, + }, + { + "m:endChr": { + _attr: { + "m:val": "}", + }, + }, + }, + ], + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["60"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/brackets/math-curly-brackets.ts b/src/file/paragraph/math/brackets/math-curly-brackets.ts new file mode 100644 index 0000000000..ccce71e6a7 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-curly-brackets.ts @@ -0,0 +1,20 @@ +// http://www.datypic.com/sc/ooxml/e-m_d-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathBase } from "../n-ary"; +import { MathBracketProperties } from "./math-bracket-properties"; + +export class MathCurlyBrackets extends XmlComponent { + constructor(options: { readonly children: MathComponent[] }) { + super("m:d"); + + this.root.push( + new MathBracketProperties({ + beginningCharacter: "{", + endingCharacter: "}", + }), + ); + this.root.push(new MathBase(options.children)); + } +} diff --git a/src/file/paragraph/math/brackets/math-ending-char.ts b/src/file/paragraph/math/brackets/math-ending-char.ts new file mode 100644 index 0000000000..86d0a0a58c --- /dev/null +++ b/src/file/paragraph/math/brackets/math-ending-char.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_endChr-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class MathEndingCharacterAttributes extends XmlAttributeComponent<{ readonly character: string }> { + protected readonly xmlKeys = { character: "m:val" }; +} + +export class MathEndingCharacter extends XmlComponent { + constructor(character: string) { + super("m:endChr"); + + this.root.push(new MathEndingCharacterAttributes({ character })); + } +} diff --git a/src/file/paragraph/math/brackets/math-ending-character.spec.ts b/src/file/paragraph/math/brackets/math-ending-character.spec.ts new file mode 100644 index 0000000000..ba28ac7840 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-ending-character.spec.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathEndingCharacter } from "./math-ending-char"; + +describe("MathEndingCharacter", () => { + describe("#constructor()", () => { + it("should create a MathEndingCharacter with correct root key", () => { + const mathEndingCharacter = new MathEndingCharacter("]"); + + const tree = new Formatter().format(mathEndingCharacter); + expect(tree).to.deep.equal({ + "m:endChr": { + _attr: { + "m:val": "]", + }, + }, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/brackets/math-round-brackets.spec.ts b/src/file/paragraph/math/brackets/math-round-brackets.spec.ts new file mode 100644 index 0000000000..5138e9d085 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-round-brackets.spec.ts @@ -0,0 +1,36 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathRoundBrackets } from "./math-round-brackets"; + +describe("MathRoundBrackets", () => { + describe("#constructor()", () => { + it("should create a MathRoundBrackets with correct root key", () => { + const mathRoundBrackets = new MathRoundBrackets({ + children: [new MathRun("60")], + }); + + const tree = new Formatter().format(mathRoundBrackets); + expect(tree).to.deep.equal({ + "m:d": [ + { + "m:dPr": {}, + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["60"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/brackets/math-round-brackets.ts b/src/file/paragraph/math/brackets/math-round-brackets.ts new file mode 100644 index 0000000000..6fe60318a4 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-round-brackets.ts @@ -0,0 +1,15 @@ +// http://www.datypic.com/sc/ooxml/e-m_d-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathBase } from "../n-ary"; +import { MathBracketProperties } from "./math-bracket-properties"; + +export class MathRoundBrackets extends XmlComponent { + constructor(options: { readonly children: MathComponent[] }) { + super("m:d"); + + this.root.push(new MathBracketProperties()); + this.root.push(new MathBase(options.children)); + } +} diff --git a/src/file/paragraph/math/brackets/math-square-brackets.spec.ts b/src/file/paragraph/math/brackets/math-square-brackets.spec.ts new file mode 100644 index 0000000000..b0e2ec9e26 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-square-brackets.spec.ts @@ -0,0 +1,51 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathSquareBrackets } from "./math-square-brackets"; + +describe("MathSquareBrackets", () => { + describe("#constructor()", () => { + it("should create a MathSquareBrackets with correct root key", () => { + const mathSquareBrackets = new MathSquareBrackets({ + children: [new MathRun("60")], + }); + + const tree = new Formatter().format(mathSquareBrackets); + expect(tree).to.deep.equal({ + "m:d": [ + { + "m:dPr": [ + { + "m:begChr": { + _attr: { + "m:val": "[", + }, + }, + }, + { + "m:endChr": { + _attr: { + "m:val": "]", + }, + }, + }, + ], + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["60"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/brackets/math-square-brackets.ts b/src/file/paragraph/math/brackets/math-square-brackets.ts new file mode 100644 index 0000000000..fdfe88a004 --- /dev/null +++ b/src/file/paragraph/math/brackets/math-square-brackets.ts @@ -0,0 +1,20 @@ +// http://www.datypic.com/sc/ooxml/e-m_d-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathBase } from "../n-ary"; +import { MathBracketProperties } from "./math-bracket-properties"; + +export class MathSquareBrackets extends XmlComponent { + constructor(options: { readonly children: MathComponent[] }) { + super("m:d"); + + this.root.push( + new MathBracketProperties({ + beginningCharacter: "[", + endingCharacter: "]", + }), + ); + this.root.push(new MathBase(options.children)); + } +} diff --git a/src/file/paragraph/math/fraction/index.ts b/src/file/paragraph/math/fraction/index.ts new file mode 100644 index 0000000000..c8af438329 --- /dev/null +++ b/src/file/paragraph/math/fraction/index.ts @@ -0,0 +1,3 @@ +export * from "./math-fraction"; +export * from "./math-denominator"; +export * from "./math-numerator"; diff --git a/src/file/paragraph/math/fraction/math-denominator.spec.ts b/src/file/paragraph/math/fraction/math-denominator.spec.ts new file mode 100644 index 0000000000..f2e7459e1e --- /dev/null +++ b/src/file/paragraph/math/fraction/math-denominator.spec.ts @@ -0,0 +1,26 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathDenominator } from "./math-denominator"; + +describe("MathDenominator", () => { + describe("#constructor()", () => { + it("should create a MathDenominator with correct root key", () => { + const mathDenominator = new MathDenominator([new MathRun("2+2")]); + const tree = new Formatter().format(mathDenominator); + expect(tree).to.deep.equal({ + "m:den": [ + { + "m:r": [ + { + "m:t": ["2+2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/fraction/math-denominator.ts b/src/file/paragraph/math/fraction/math-denominator.ts new file mode 100644 index 0000000000..8df68bf4b0 --- /dev/null +++ b/src/file/paragraph/math/fraction/math-denominator.ts @@ -0,0 +1,13 @@ +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; + +export class MathDenominator extends XmlComponent { + constructor(children: MathComponent[]) { + super("m:den"); + + for (const child of children) { + this.root.push(child); + } + } +} diff --git a/src/file/paragraph/math/fraction/math-fraction.spec.ts b/src/file/paragraph/math/fraction/math-fraction.spec.ts new file mode 100644 index 0000000000..51cac646bf --- /dev/null +++ b/src/file/paragraph/math/fraction/math-fraction.spec.ts @@ -0,0 +1,44 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathFraction } from "./math-fraction"; + +describe("MathFraction", () => { + describe("#constructor()", () => { + it("should create a MathFraction with correct root key", () => { + const mathFraction = new MathFraction({ + numerator: [new MathRun("2")], + denominator: [new MathRun("2")], + }); + const tree = new Formatter().format(mathFraction); + expect(tree).to.deep.equal({ + "m:f": [ + { + "m:num": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + { + "m:den": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/fraction/math-fraction.ts b/src/file/paragraph/math/fraction/math-fraction.ts new file mode 100644 index 0000000000..84a803a872 --- /dev/null +++ b/src/file/paragraph/math/fraction/math-fraction.ts @@ -0,0 +1,19 @@ +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathDenominator } from "./math-denominator"; +import { MathNumerator } from "./math-numerator"; + +export interface IMathFractionOptions { + readonly numerator: MathComponent[]; + readonly denominator: MathComponent[]; +} + +export class MathFraction extends XmlComponent { + constructor(options: IMathFractionOptions) { + super("m:f"); + + this.root.push(new MathNumerator(options.numerator)); + this.root.push(new MathDenominator(options.denominator)); + } +} diff --git a/src/file/paragraph/math/fraction/math-numerator.spec.ts b/src/file/paragraph/math/fraction/math-numerator.spec.ts new file mode 100644 index 0000000000..e3aaf35c0e --- /dev/null +++ b/src/file/paragraph/math/fraction/math-numerator.spec.ts @@ -0,0 +1,26 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathNumerator } from "./math-numerator"; + +describe("MathNumerator", () => { + describe("#constructor()", () => { + it("should create a MathNumerator with correct root key", () => { + const mathNumerator = new MathNumerator([new MathRun("2+2")]); + const tree = new Formatter().format(mathNumerator); + expect(tree).to.deep.equal({ + "m:num": [ + { + "m:r": [ + { + "m:t": ["2+2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/fraction/math-numerator.ts b/src/file/paragraph/math/fraction/math-numerator.ts new file mode 100644 index 0000000000..b7a49d9a75 --- /dev/null +++ b/src/file/paragraph/math/fraction/math-numerator.ts @@ -0,0 +1,13 @@ +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; + +export class MathNumerator extends XmlComponent { + constructor(children: MathComponent[]) { + super("m:num"); + + for (const child of children) { + this.root.push(child); + } + } +} diff --git a/src/file/paragraph/math/function/index.ts b/src/file/paragraph/math/function/index.ts new file mode 100644 index 0000000000..58243c2710 --- /dev/null +++ b/src/file/paragraph/math/function/index.ts @@ -0,0 +1,3 @@ +export * from "./math-function"; +export * from "./math-function-name"; +export * from "./math-function-properties"; diff --git a/src/file/paragraph/math/function/math-function-name.spec.ts b/src/file/paragraph/math/function/math-function-name.spec.ts new file mode 100644 index 0000000000..8a37ee998e --- /dev/null +++ b/src/file/paragraph/math/function/math-function-name.spec.ts @@ -0,0 +1,27 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathFunctionName } from "./math-function-name"; + +describe("MathFunctionName", () => { + describe("#constructor()", () => { + it("should create a MathFunctionName with correct root key", () => { + const mathFunctionName = new MathFunctionName([new MathRun("2")]); + + const tree = new Formatter().format(mathFunctionName); + expect(tree).to.deep.equal({ + "m:fName": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/function/math-function-name.ts b/src/file/paragraph/math/function/math-function-name.ts new file mode 100644 index 0000000000..e58488dd28 --- /dev/null +++ b/src/file/paragraph/math/function/math-function-name.ts @@ -0,0 +1,13 @@ +// http://www.datypic.com/sc/ooxml/e-m_fName-1.html +import { XmlComponent } from "file/xml-components"; +import { MathComponent } from "../math-component"; + +export class MathFunctionName extends XmlComponent { + constructor(children: MathComponent[]) { + super("m:fName"); + + for (const child of children) { + this.root.push(child); + } + } +} diff --git a/src/file/paragraph/math/function/math-function-properties.spec.ts b/src/file/paragraph/math/function/math-function-properties.spec.ts new file mode 100644 index 0000000000..63ec2e4f60 --- /dev/null +++ b/src/file/paragraph/math/function/math-function-properties.spec.ts @@ -0,0 +1,18 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathFunctionProperties } from "./math-function-properties"; + +describe("MathFunctionProperties", () => { + describe("#constructor()", () => { + it("should create a MathFunctionProperties with correct root key", () => { + const mathFunctionProperties = new MathFunctionProperties(); + + const tree = new Formatter().format(mathFunctionProperties); + expect(tree).to.deep.equal({ + "m:funcPr": {}, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/function/math-function-properties.ts b/src/file/paragraph/math/function/math-function-properties.ts new file mode 100644 index 0000000000..f6e8d608ac --- /dev/null +++ b/src/file/paragraph/math/function/math-function-properties.ts @@ -0,0 +1,8 @@ +// http://www.datypic.com/sc/ooxml/e-m_radPr-1.html +import { XmlComponent } from "file/xml-components"; + +export class MathFunctionProperties extends XmlComponent { + constructor() { + super("m:funcPr"); + } +} diff --git a/src/file/paragraph/math/function/math-function.spec.ts b/src/file/paragraph/math/function/math-function.spec.ts new file mode 100644 index 0000000000..6ea02b6c9d --- /dev/null +++ b/src/file/paragraph/math/function/math-function.spec.ts @@ -0,0 +1,48 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathFunction } from "./math-function"; + +describe("MathFunction", () => { + describe("#constructor()", () => { + it("should create a MathFunction with correct root key", () => { + const mathFunction = new MathFunction({ + name: [new MathRun("sin")], + children: [new MathRun("60")], + }); + + const tree = new Formatter().format(mathFunction); + expect(tree).to.deep.equal({ + "m:func": [ + { + "m:funcPr": {}, + }, + { + "m:fName": [ + { + "m:r": [ + { + "m:t": ["sin"], + }, + ], + }, + ], + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["60"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/function/math-function.ts b/src/file/paragraph/math/function/math-function.ts new file mode 100644 index 0000000000..86b66392cc --- /dev/null +++ b/src/file/paragraph/math/function/math-function.ts @@ -0,0 +1,22 @@ +// http://www.datypic.com/sc/ooxml/e-m_func-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathBase } from "../n-ary"; +import { MathFunctionName } from "./math-function-name"; +import { MathFunctionProperties } from "./math-function-properties"; + +export interface IMathFunctionOptions { + readonly children: MathComponent[]; + readonly name: MathComponent[]; +} + +export class MathFunction extends XmlComponent { + constructor(options: IMathFunctionOptions) { + super("m:func"); + + this.root.push(new MathFunctionProperties()); + this.root.push(new MathFunctionName(options.name)); + this.root.push(new MathBase(options.children)); + } +} diff --git a/src/file/paragraph/math/index.ts b/src/file/paragraph/math/index.ts new file mode 100644 index 0000000000..b42ec908f6 --- /dev/null +++ b/src/file/paragraph/math/index.ts @@ -0,0 +1,9 @@ +export * from "./math"; +export * from "./math-run"; +export * from "./fraction"; +export * from "./n-ary"; +export * from "./script"; +export * from "./math-component"; +export * from "./radical"; +export * from "./function"; +export * from "./brackets"; diff --git a/src/file/paragraph/math/math-component.ts b/src/file/paragraph/math/math-component.ts new file mode 100644 index 0000000000..8251235455 --- /dev/null +++ b/src/file/paragraph/math/math-component.ts @@ -0,0 +1,27 @@ +import { MathAngledBrackets, MathCurlyBrackets, MathRoundBrackets, MathSquareBrackets } from "./brackets"; +import { MathFraction } from "./fraction"; +import { MathFunction } from "./function"; +import { MathRun } from "./math-run"; +import { MathSum } from "./n-ary"; +import { MathRadical } from "./radical"; +import { MathSubScript, MathSubSuperScript, MathSuperScript } from "./script"; + +export type MathComponent = + | MathRun + | MathFraction + | MathSum + | MathSuperScript + | MathSubScript + | MathSubSuperScript + | MathRadical + | MathFunction + | MathRoundBrackets + | MathCurlyBrackets + | MathAngledBrackets + | MathSquareBrackets; + +// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432 +/** + * @ignore + */ +export const WORKAROUND4 = ""; diff --git a/src/file/paragraph/math/math-run.spec.ts b/src/file/paragraph/math/math-run.spec.ts new file mode 100644 index 0000000000..2773a0fb9c --- /dev/null +++ b/src/file/paragraph/math/math-run.spec.ts @@ -0,0 +1,21 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "./math-run"; + +describe("MathRun", () => { + describe("#constructor()", () => { + it("should create a MathRun with correct root key", () => { + const mathRun = new MathRun("2+2"); + const tree = new Formatter().format(mathRun); + expect(tree).to.deep.equal({ + "m:r": [ + { + "m:t": ["2+2"], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/math-run.ts b/src/file/paragraph/math/math-run.ts new file mode 100644 index 0000000000..0d2c48910f --- /dev/null +++ b/src/file/paragraph/math/math-run.ts @@ -0,0 +1,12 @@ +// http://www.datypic.com/sc/ooxml/e-m_r-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathText } from "./math-text"; + +export class MathRun extends XmlComponent { + constructor(text: string) { + super("m:r"); + + this.root.push(new MathText(text)); + } +} diff --git a/src/file/paragraph/math/math-text.spec.ts b/src/file/paragraph/math/math-text.spec.ts new file mode 100644 index 0000000000..0001816ed1 --- /dev/null +++ b/src/file/paragraph/math/math-text.spec.ts @@ -0,0 +1,17 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathText } from "./math-text"; + +describe("MathText", () => { + describe("#constructor()", () => { + it("should create a MathText with correct root key", () => { + const mathText = new MathText("2+2"); + const tree = new Formatter().format(mathText); + expect(tree).to.deep.equal({ + "m:t": ["2+2"], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/math-text.ts b/src/file/paragraph/math/math-text.ts new file mode 100644 index 0000000000..6087c7e611 --- /dev/null +++ b/src/file/paragraph/math/math-text.ts @@ -0,0 +1,9 @@ +import { XmlComponent } from "file/xml-components"; + +export class MathText extends XmlComponent { + constructor(text: string) { + super("m:t"); + + this.root.push(text); + } +} diff --git a/src/file/paragraph/math/math.spec.ts b/src/file/paragraph/math/math.spec.ts new file mode 100644 index 0000000000..d5c4f6f494 --- /dev/null +++ b/src/file/paragraph/math/math.spec.ts @@ -0,0 +1,38 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { Math } from "./math"; +import { MathRun } from "./math-run"; + +describe("Math", () => { + describe("#constructor()", () => { + it("should create a Math with correct root key", () => { + const math = new Math({ + children: [], + }); + const tree = new Formatter().format(math); + expect(tree).to.deep.equal({ + "m:oMath": {}, + }); + }); + + it("should be able to add children", () => { + const math = new Math({ + children: [new MathRun("2+2")], + }); + const tree = new Formatter().format(math); + expect(tree).to.deep.equal({ + "m:oMath": [ + { + "m:r": [ + { + "m:t": ["2+2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/math.ts b/src/file/paragraph/math/math.ts new file mode 100644 index 0000000000..69ebae4dff --- /dev/null +++ b/src/file/paragraph/math/math.ts @@ -0,0 +1,18 @@ +// http://www.datypic.com/sc/ooxml/e-m_oMath-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "./math-component"; + +export interface IMathOptions { + readonly children: MathComponent[]; +} + +export class Math extends XmlComponent { + constructor(options: IMathOptions) { + super("m:oMath"); + + for (const child of options.children) { + this.root.push(child); + } + } +} diff --git a/src/file/paragraph/math/n-ary/index.ts b/src/file/paragraph/math/n-ary/index.ts new file mode 100644 index 0000000000..6929544152 --- /dev/null +++ b/src/file/paragraph/math/n-ary/index.ts @@ -0,0 +1,7 @@ +export * from "./math-accent-character"; +export * from "./math-base"; +export * from "./math-limit-location"; +export * from "./math-naray-properties"; +export * from "./math-sub-script"; +export * from "./math-sum"; +export * from "./math-super-script"; diff --git a/src/file/paragraph/math/n-ary/math-accent-character.spec.ts b/src/file/paragraph/math/n-ary/math-accent-character.spec.ts new file mode 100644 index 0000000000..b414f4c744 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-accent-character.spec.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathAccentCharacter } from "./math-accent-character"; + +describe("MathAccentCharacter", () => { + describe("#constructor()", () => { + it("should create a MathAccentCharacter with correct root key", () => { + const mathAccentCharacter = new MathAccentCharacter("∑"); + + const tree = new Formatter().format(mathAccentCharacter); + expect(tree).to.deep.equal({ + "m:chr": { + _attr: { + "m:val": "∑", + }, + }, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-accent-character.ts b/src/file/paragraph/math/n-ary/math-accent-character.ts new file mode 100644 index 0000000000..00182bd1f2 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-accent-character.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_chr-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class MathAccentCharacterAttributes extends XmlAttributeComponent<{ readonly accent: string }> { + protected readonly xmlKeys = { accent: "m:val" }; +} + +export class MathAccentCharacter extends XmlComponent { + constructor(accent: string) { + super("m:chr"); + + this.root.push(new MathAccentCharacterAttributes({ accent })); + } +} diff --git a/src/file/paragraph/math/n-ary/math-base.spec.ts b/src/file/paragraph/math/n-ary/math-base.spec.ts new file mode 100644 index 0000000000..c282537970 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-base.spec.ts @@ -0,0 +1,27 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathBase } from "./math-base"; + +describe("MathBase", () => { + describe("#constructor()", () => { + it("should create a MathBase with correct root key", () => { + const mathBase = new MathBase([new MathRun("2+2")]); + + const tree = new Formatter().format(mathBase); + expect(tree).to.deep.equal({ + "m:e": [ + { + "m:r": [ + { + "m:t": ["2+2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-base.ts b/src/file/paragraph/math/n-ary/math-base.ts new file mode 100644 index 0000000000..6c2320439f --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-base.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_e-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; + +export class MathBase extends XmlComponent { + constructor(children: MathComponent[]) { + super("m:e"); + + for (const child of children) { + this.root.push(child); + } + } +} diff --git a/src/file/paragraph/math/n-ary/math-limit-location.spec.ts b/src/file/paragraph/math/n-ary/math-limit-location.spec.ts new file mode 100644 index 0000000000..426f3d0198 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-limit-location.spec.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathLimitLocation } from "./math-limit-location"; + +describe("MathLimitLocation", () => { + describe("#constructor()", () => { + it("should create a MathLimitLocation with correct root key", () => { + const mathLimitLocation = new MathLimitLocation(); + + const tree = new Formatter().format(mathLimitLocation); + expect(tree).to.deep.equal({ + "m:limLoc": { + _attr: { + "m:val": "undOvr", + }, + }, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-limit-location.ts b/src/file/paragraph/math/n-ary/math-limit-location.ts new file mode 100644 index 0000000000..5504e1890d --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-limit-location.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_limLoc-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class MathLimitLocationAttributes extends XmlAttributeComponent<{ readonly value: string }> { + protected readonly xmlKeys = { value: "m:val" }; +} + +export class MathLimitLocation extends XmlComponent { + constructor() { + super("m:limLoc"); + + this.root.push(new MathLimitLocationAttributes({ value: "undOvr" })); + } +} diff --git a/src/file/paragraph/math/n-ary/math-naray-properties.spec.ts b/src/file/paragraph/math/n-ary/math-naray-properties.spec.ts new file mode 100644 index 0000000000..70aa74a1c4 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-naray-properties.spec.ts @@ -0,0 +1,133 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathNArayProperties } from "./math-naray-properties"; + +describe("MathNArayProperties", () => { + describe("#constructor()", () => { + it("should create a MathNArayProperties with correct root key", () => { + const mathNArayProperties = new MathNArayProperties("∑", true, true); + + const tree = new Formatter().format(mathNArayProperties); + expect(tree).to.deep.equal({ + "m:naryPr": [ + { + "m:chr": { + _attr: { + "m:val": "∑", + }, + }, + }, + { + "m:limLoc": { + _attr: { + "m:val": "undOvr", + }, + }, + }, + ], + }); + }); + + it("should add super-script hide attributes", () => { + const mathNArayProperties = new MathNArayProperties("∑", false, true); + + const tree = new Formatter().format(mathNArayProperties); + expect(tree).to.deep.equal({ + "m:naryPr": [ + { + "m:chr": { + _attr: { + "m:val": "∑", + }, + }, + }, + { + "m:limLoc": { + _attr: { + "m:val": "undOvr", + }, + }, + }, + { + "m:supHide": { + _attr: { + "m:val": 1, + }, + }, + }, + ], + }); + }); + + it("should add sub-script hide attributes", () => { + const mathNArayProperties = new MathNArayProperties("∑", true, false); + + const tree = new Formatter().format(mathNArayProperties); + expect(tree).to.deep.equal({ + "m:naryPr": [ + { + "m:chr": { + _attr: { + "m:val": "∑", + }, + }, + }, + { + "m:limLoc": { + _attr: { + "m:val": "undOvr", + }, + }, + }, + { + "m:subHide": { + _attr: { + "m:val": 1, + }, + }, + }, + ], + }); + }); + + it("should add both super-script and sub-script hide attributes", () => { + const mathNArayProperties = new MathNArayProperties("∑", false, false); + + const tree = new Formatter().format(mathNArayProperties); + expect(tree).to.deep.equal({ + "m:naryPr": [ + { + "m:chr": { + _attr: { + "m:val": "∑", + }, + }, + }, + { + "m:limLoc": { + _attr: { + "m:val": "undOvr", + }, + }, + }, + { + "m:supHide": { + _attr: { + "m:val": 1, + }, + }, + }, + { + "m:subHide": { + _attr: { + "m:val": 1, + }, + }, + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-naray-properties.ts b/src/file/paragraph/math/n-ary/math-naray-properties.ts new file mode 100644 index 0000000000..f8e91e746a --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-naray-properties.ts @@ -0,0 +1,24 @@ +// http://www.datypic.com/sc/ooxml/e-m_naryPr-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathAccentCharacter } from "./math-accent-character"; +import { MathLimitLocation } from "./math-limit-location"; +import { MathSubScriptHide } from "./math-sub-script-hide"; +import { MathSuperScriptHide } from "./math-super-script-hide"; + +export class MathNArayProperties extends XmlComponent { + constructor(accent: string, hasSuperScript: boolean, hasSubScript: boolean) { + super("m:naryPr"); + + this.root.push(new MathAccentCharacter(accent)); + this.root.push(new MathLimitLocation()); + + if (!hasSuperScript) { + this.root.push(new MathSuperScriptHide()); + } + + if (!hasSubScript) { + this.root.push(new MathSubScriptHide()); + } + } +} diff --git a/src/file/paragraph/math/n-ary/math-sub-script-hide.spec.ts b/src/file/paragraph/math/n-ary/math-sub-script-hide.spec.ts new file mode 100644 index 0000000000..2e9845172f --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-sub-script-hide.spec.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathSubScriptHide } from "./math-sub-script-hide"; + +describe("MathSubScriptHide", () => { + describe("#constructor()", () => { + it("should create a MathSubScriptHide with correct root key", () => { + const mathSubScriptHide = new MathSubScriptHide(); + + const tree = new Formatter().format(mathSubScriptHide); + expect(tree).to.deep.equal({ + "m:subHide": { + _attr: { + "m:val": 1, + }, + }, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-sub-script-hide.ts b/src/file/paragraph/math/n-ary/math-sub-script-hide.ts new file mode 100644 index 0000000000..ef735744e0 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-sub-script-hide.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_subHide-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class MathSubScriptHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> { + protected readonly xmlKeys = { hide: "m:val" }; +} + +export class MathSubScriptHide extends XmlComponent { + constructor() { + super("m:subHide"); + + this.root.push(new MathSubScriptHideAttributes({ hide: 1 })); + } +} diff --git a/src/file/paragraph/math/n-ary/math-sub-script.spec.ts b/src/file/paragraph/math/n-ary/math-sub-script.spec.ts new file mode 100644 index 0000000000..d342946b74 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-sub-script.spec.ts @@ -0,0 +1,27 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathSubScriptElement } from "./math-sub-script"; + +describe("MathSubScriptElement", () => { + describe("#constructor()", () => { + it("should create a MathSubScriptElement with correct root key", () => { + const mathSubScriptElement = new MathSubScriptElement([new MathRun("2+2")]); + + const tree = new Formatter().format(mathSubScriptElement); + expect(tree).to.deep.equal({ + "m:sub": [ + { + "m:r": [ + { + "m:t": ["2+2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-sub-script.ts b/src/file/paragraph/math/n-ary/math-sub-script.ts new file mode 100644 index 0000000000..48268312cf --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-sub-script.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_sub-3.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; + +export class MathSubScriptElement extends XmlComponent { + constructor(children: MathComponent[]) { + super("m:sub"); + + for (const child of children) { + this.root.push(child); + } + } +} diff --git a/src/file/paragraph/math/n-ary/math-sum.spec.ts b/src/file/paragraph/math/n-ary/math-sum.spec.ts new file mode 100644 index 0000000000..1e76815147 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-sum.spec.ts @@ -0,0 +1,75 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathSum } from "./math-sum"; + +describe("MathSum", () => { + describe("#constructor()", () => { + it("should create a MathSum with correct root key", () => { + const mathSum = new MathSum({ + children: [new MathRun("1")], + subScript: [new MathRun("2")], + superScript: [new MathRun("3")], + }); + + const tree = new Formatter().format(mathSum); + expect(tree).to.deep.equal({ + "m:nary": [ + { + "m:naryPr": [ + { + "m:chr": { + _attr: { + "m:val": "∑", + }, + }, + }, + { + "m:limLoc": { + _attr: { + "m:val": "undOvr", + }, + }, + }, + ], + }, + { + "m:sub": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + { + "m:sup": [ + { + "m:r": [ + { + "m:t": ["3"], + }, + ], + }, + ], + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["1"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-sum.ts b/src/file/paragraph/math/n-ary/math-sum.ts new file mode 100644 index 0000000000..af14730bbf --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-sum.ts @@ -0,0 +1,32 @@ +// http://www.datypic.com/sc/ooxml/e-m_nary-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathBase } from "./math-base"; +import { MathNArayProperties } from "./math-naray-properties"; +import { MathSubScriptElement } from "./math-sub-script"; +import { MathSuperScriptElement } from "./math-super-script"; + +export interface IMathSumOptions { + readonly children: MathComponent[]; + readonly subScript?: MathComponent[]; + readonly superScript?: MathComponent[]; +} + +export class MathSum extends XmlComponent { + constructor(options: IMathSumOptions) { + super("m:nary"); + + this.root.push(new MathNArayProperties("∑", !!options.superScript, !!options.subScript)); + + if (!!options.subScript) { + this.root.push(new MathSubScriptElement(options.subScript)); + } + + if (!!options.superScript) { + this.root.push(new MathSuperScriptElement(options.superScript)); + } + + this.root.push(new MathBase(options.children)); + } +} diff --git a/src/file/paragraph/math/n-ary/math-super-script-hide.spec.ts b/src/file/paragraph/math/n-ary/math-super-script-hide.spec.ts new file mode 100644 index 0000000000..89a28564ac --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-super-script-hide.spec.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathSuperScriptHide } from "./math-super-script-hide"; + +describe("MathSuperScriptHide", () => { + describe("#constructor()", () => { + it("should create a MathSuperScriptHide with correct root key", () => { + const mathSuperScriptHide = new MathSuperScriptHide(); + + const tree = new Formatter().format(mathSuperScriptHide); + expect(tree).to.deep.equal({ + "m:supHide": { + _attr: { + "m:val": 1, + }, + }, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-super-script-hide.ts b/src/file/paragraph/math/n-ary/math-super-script-hide.ts new file mode 100644 index 0000000000..a55c15a7ad --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-super-script-hide.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_subHide-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class MathSuperScriptHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> { + protected readonly xmlKeys = { hide: "m:val" }; +} + +export class MathSuperScriptHide extends XmlComponent { + constructor() { + super("m:supHide"); + + this.root.push(new MathSuperScriptHideAttributes({ hide: 1 })); + } +} diff --git a/src/file/paragraph/math/n-ary/math-super-script.spec.ts b/src/file/paragraph/math/n-ary/math-super-script.spec.ts new file mode 100644 index 0000000000..10e037eba3 --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-super-script.spec.ts @@ -0,0 +1,27 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathSuperScriptElement } from "./math-super-script"; + +describe("MathSuperScriptElement", () => { + describe("#constructor()", () => { + it("should create a MathSuperScriptElement with correct root key", () => { + const mathSuperScriptElement = new MathSuperScriptElement([new MathRun("2+2")]); + + const tree = new Formatter().format(mathSuperScriptElement); + expect(tree).to.deep.equal({ + "m:sup": [ + { + "m:r": [ + { + "m:t": ["2+2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/n-ary/math-super-script.ts b/src/file/paragraph/math/n-ary/math-super-script.ts new file mode 100644 index 0000000000..8d8386addc --- /dev/null +++ b/src/file/paragraph/math/n-ary/math-super-script.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_sup-3.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; + +export class MathSuperScriptElement extends XmlComponent { + constructor(children: MathComponent[]) { + super("m:sup"); + + for (const child of children) { + this.root.push(child); + } + } +} diff --git a/src/file/paragraph/math/radical/index.ts b/src/file/paragraph/math/radical/index.ts new file mode 100644 index 0000000000..78240ccd13 --- /dev/null +++ b/src/file/paragraph/math/radical/index.ts @@ -0,0 +1,3 @@ +export * from "./math-degree"; +export * from "./math-radical"; +export * from "./math-radical-properties"; diff --git a/src/file/paragraph/math/radical/math-degree-hide.spec.ts b/src/file/paragraph/math/radical/math-degree-hide.spec.ts new file mode 100644 index 0000000000..0c3f335a13 --- /dev/null +++ b/src/file/paragraph/math/radical/math-degree-hide.spec.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathDegreeHide } from "./math-degree-hide"; + +describe("MathDegreeHide", () => { + describe("#constructor()", () => { + it("should create a MathDegreeHide with correct root key", () => { + const mathDegreeHide = new MathDegreeHide(); + + const tree = new Formatter().format(mathDegreeHide); + expect(tree).to.deep.equal({ + "m:degHide": { + _attr: { + "m:val": 1, + }, + }, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/radical/math-degree-hide.ts b/src/file/paragraph/math/radical/math-degree-hide.ts new file mode 100644 index 0000000000..c1b9dd2ae5 --- /dev/null +++ b/src/file/paragraph/math/radical/math-degree-hide.ts @@ -0,0 +1,14 @@ +// http://www.datypic.com/sc/ooxml/e-m_degHide-1.html +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class MathDegreeHideAttributes extends XmlAttributeComponent<{ readonly hide: number }> { + protected readonly xmlKeys = { hide: "m:val" }; +} + +export class MathDegreeHide extends XmlComponent { + constructor() { + super("m:degHide"); + + this.root.push(new MathDegreeHideAttributes({ hide: 1 })); + } +} diff --git a/src/file/paragraph/math/radical/math-degree.spec.ts b/src/file/paragraph/math/radical/math-degree.spec.ts new file mode 100644 index 0000000000..3d1f17dfa8 --- /dev/null +++ b/src/file/paragraph/math/radical/math-degree.spec.ts @@ -0,0 +1,36 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathDegree } from "./math-degree"; + +describe("MathDegree", () => { + describe("#constructor()", () => { + it("should create a MathDegree with correct root key", () => { + const mathDegree = new MathDegree(); + + const tree = new Formatter().format(mathDegree); + expect(tree).to.deep.equal({ + "m:deg": {}, + }); + }); + + it("should create a MathDegree with correct root key with child", () => { + const mathDegree = new MathDegree([new MathRun("2")]); + + const tree = new Formatter().format(mathDegree); + expect(tree).to.deep.equal({ + "m:deg": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/radical/math-degree.ts b/src/file/paragraph/math/radical/math-degree.ts new file mode 100644 index 0000000000..79923bbde8 --- /dev/null +++ b/src/file/paragraph/math/radical/math-degree.ts @@ -0,0 +1,15 @@ +// http://www.datypic.com/sc/ooxml/e-m_deg-1.html +import { XmlComponent } from "file/xml-components"; +import { MathComponent } from "../math-component"; + +export class MathDegree extends XmlComponent { + constructor(children?: MathComponent[]) { + super("m:deg"); + + if (!!children) { + for (const child of children) { + this.root.push(child); + } + } + } +} diff --git a/src/file/paragraph/math/radical/math-radical-properties.spec.ts b/src/file/paragraph/math/radical/math-radical-properties.spec.ts new file mode 100644 index 0000000000..12d29a2962 --- /dev/null +++ b/src/file/paragraph/math/radical/math-radical-properties.spec.ts @@ -0,0 +1,35 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRadicalProperties } from "./math-radical-properties"; + +describe("MathRadicalProperties", () => { + describe("#constructor()", () => { + it("should create a MathRadicalProperties with correct root key", () => { + const mathRadicalProperties = new MathRadicalProperties(true); + + const tree = new Formatter().format(mathRadicalProperties); + expect(tree).to.deep.equal({ + "m:radPr": {}, + }); + }); + + it("should create a MathRadicalProperties with correct root key with degree hide", () => { + const mathRadicalProperties = new MathRadicalProperties(false); + + const tree = new Formatter().format(mathRadicalProperties); + expect(tree).to.deep.equal({ + "m:radPr": [ + { + "m:degHide": { + _attr: { + "m:val": 1, + }, + }, + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/radical/math-radical-properties.ts b/src/file/paragraph/math/radical/math-radical-properties.ts new file mode 100644 index 0000000000..b4e90ed4df --- /dev/null +++ b/src/file/paragraph/math/radical/math-radical-properties.ts @@ -0,0 +1,13 @@ +// http://www.datypic.com/sc/ooxml/e-m_radPr-1.html +import { XmlComponent } from "file/xml-components"; +import { MathDegreeHide } from "./math-degree-hide"; + +export class MathRadicalProperties extends XmlComponent { + constructor(hasDegree: boolean) { + super("m:radPr"); + + if (!hasDegree) { + this.root.push(new MathDegreeHide()); + } + } +} diff --git a/src/file/paragraph/math/radical/math-radical.spec.ts b/src/file/paragraph/math/radical/math-radical.spec.ts new file mode 100644 index 0000000000..e388edece4 --- /dev/null +++ b/src/file/paragraph/math/radical/math-radical.spec.ts @@ -0,0 +1,48 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../math-run"; +import { MathRadical } from "./math-radical"; + +describe("MathRadical", () => { + describe("#constructor()", () => { + it("should create a MathRadical with correct root key", () => { + const mathRadical = new MathRadical({ + children: [new MathRun("e")], + degree: [new MathRun("2")], + }); + + const tree = new Formatter().format(mathRadical); + expect(tree).to.deep.equal({ + "m:rad": [ + { + "m:radPr": {}, + }, + { + "m:deg": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["e"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/radical/math-radical.ts b/src/file/paragraph/math/radical/math-radical.ts new file mode 100644 index 0000000000..1469867a5f --- /dev/null +++ b/src/file/paragraph/math/radical/math-radical.ts @@ -0,0 +1,22 @@ +// http://www.datypic.com/sc/ooxml/e-m_rad-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../math-component"; +import { MathBase } from "../n-ary"; +import { MathDegree } from "./math-degree"; +import { MathRadicalProperties } from "./math-radical-properties"; + +export interface IMathRadicalOptions { + readonly children: MathComponent[]; + readonly degree?: MathComponent[]; +} + +export class MathRadical extends XmlComponent { + constructor(options: IMathRadicalOptions) { + super("m:rad"); + + this.root.push(new MathRadicalProperties(!!options.degree)); + this.root.push(new MathDegree(options.degree)); + this.root.push(new MathBase(options.children)); + } +} diff --git a/src/file/paragraph/math/script/index.ts b/src/file/paragraph/math/script/index.ts new file mode 100644 index 0000000000..a83e54975e --- /dev/null +++ b/src/file/paragraph/math/script/index.ts @@ -0,0 +1,4 @@ +export * from "./super-script"; +export * from "./sub-script"; +export * from "./sub-super-script"; +export * from "./pre-sub-super-script"; diff --git a/src/file/paragraph/math/script/pre-sub-super-script/index.ts b/src/file/paragraph/math/script/pre-sub-super-script/index.ts new file mode 100644 index 0000000000..8660a75017 --- /dev/null +++ b/src/file/paragraph/math/script/pre-sub-super-script/index.ts @@ -0,0 +1,2 @@ +export * from "./math-pre-sub-super-script-function"; +export * from "./math-pre-sub-super-script-function-properties"; diff --git a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.spec.ts b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.spec.ts new file mode 100644 index 0000000000..a6f1d5383e --- /dev/null +++ b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.spec.ts @@ -0,0 +1,17 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; +import { MathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties"; + +describe("MathPreSubSuperScriptProperties", () => { + describe("#constructor()", () => { + it("should create a MathPreSubSuperScriptProperties with correct root key", () => { + const mathPreSubSuperScriptProperties = new MathPreSubSuperScriptProperties(); + + const tree = new Formatter().format(mathPreSubSuperScriptProperties); + expect(tree).to.deep.equal({ + "m:sPrePr": {}, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.ts b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.ts new file mode 100644 index 0000000000..fed0cb3564 --- /dev/null +++ b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function-properties.ts @@ -0,0 +1,8 @@ +// http://www.datypic.com/sc/ooxml/e-m_sPrePr-1.html +import { XmlComponent } from "file/xml-components"; + +export class MathPreSubSuperScriptProperties extends XmlComponent { + constructor() { + super("m:sPrePr"); + } +} diff --git a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.spec.ts b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.spec.ts new file mode 100644 index 0000000000..20d00a639d --- /dev/null +++ b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.spec.ts @@ -0,0 +1,60 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../../math-run"; +import { MathPreSubSuperScript } from "./math-pre-sub-super-script-function"; + +describe("MathPreSubScript", () => { + describe("#constructor()", () => { + it("should create a MathPreSubScript with correct root key", () => { + const mathPreSubScript = new MathPreSubSuperScript({ + children: [new MathRun("e")], + subScript: [new MathRun("2")], + superScript: [new MathRun("5")], + }); + + const tree = new Formatter().format(mathPreSubScript); + expect(tree).to.deep.equal({ + "m:sPre": [ + { + "m:sPrePr": {}, + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["e"], + }, + ], + }, + ], + }, + { + "m:sub": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + { + "m:sup": [ + { + "m:r": [ + { + "m:t": ["5"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.ts b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.ts new file mode 100644 index 0000000000..3a58675b0f --- /dev/null +++ b/src/file/paragraph/math/script/pre-sub-super-script/math-pre-sub-super-script-function.ts @@ -0,0 +1,23 @@ +// http://www.datypic.com/sc/ooxml/e-m_sPre-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../../math-component"; +import { MathBase, MathSubScriptElement, MathSuperScriptElement } from "../../n-ary"; +import { MathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties"; + +export interface IMathPreSubSuperScriptOptions { + readonly children: MathComponent[]; + readonly subScript: MathComponent[]; + readonly superScript: MathComponent[]; +} + +export class MathPreSubSuperScript extends XmlComponent { + constructor(options: IMathPreSubSuperScriptOptions) { + super("m:sPre"); + + this.root.push(new MathPreSubSuperScriptProperties()); + this.root.push(new MathBase(options.children)); + this.root.push(new MathSubScriptElement(options.subScript)); + this.root.push(new MathSuperScriptElement(options.superScript)); + } +} diff --git a/src/file/paragraph/math/script/sub-script/index.ts b/src/file/paragraph/math/script/sub-script/index.ts new file mode 100644 index 0000000000..0f1c27fe51 --- /dev/null +++ b/src/file/paragraph/math/script/sub-script/index.ts @@ -0,0 +1,2 @@ +export * from "./math-sub-script-function"; +export * from "./math-sub-script-function-properties"; diff --git a/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.spec.ts b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.spec.ts new file mode 100644 index 0000000000..be229e066b --- /dev/null +++ b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.spec.ts @@ -0,0 +1,17 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; +import { MathSubScriptProperties } from "./math-sub-script-function-properties"; + +describe("MathSubScriptProperties", () => { + describe("#constructor()", () => { + it("should create a MathSubScriptProperties with correct root key", () => { + const mathSubScriptProperties = new MathSubScriptProperties(); + + const tree = new Formatter().format(mathSubScriptProperties); + expect(tree).to.deep.equal({ + "m:sSubPr": {}, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.ts b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.ts new file mode 100644 index 0000000000..bf37b1d885 --- /dev/null +++ b/src/file/paragraph/math/script/sub-script/math-sub-script-function-properties.ts @@ -0,0 +1,8 @@ +// http://www.datypic.com/sc/ooxml/e-m_sSubPr-1.html +import { XmlComponent } from "file/xml-components"; + +export class MathSubScriptProperties extends XmlComponent { + constructor() { + super("m:sSubPr"); + } +} diff --git a/src/file/paragraph/math/script/sub-script/math-sub-script-function.spec.ts b/src/file/paragraph/math/script/sub-script/math-sub-script-function.spec.ts new file mode 100644 index 0000000000..a3ea8d680c --- /dev/null +++ b/src/file/paragraph/math/script/sub-script/math-sub-script-function.spec.ts @@ -0,0 +1,48 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../../math-run"; +import { MathSubScript } from "./math-sub-script-function"; + +describe("MathSubScript", () => { + describe("#constructor()", () => { + it("should create a MathSubScript with correct root key", () => { + const mathSubScript = new MathSubScript({ + children: [new MathRun("e")], + subScript: [new MathRun("2")], + }); + + const tree = new Formatter().format(mathSubScript); + expect(tree).to.deep.equal({ + "m:sSub": [ + { + "m:sSubPr": {}, + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["e"], + }, + ], + }, + ], + }, + { + "m:sub": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/sub-script/math-sub-script-function.ts b/src/file/paragraph/math/script/sub-script/math-sub-script-function.ts new file mode 100644 index 0000000000..319d4d1f1a --- /dev/null +++ b/src/file/paragraph/math/script/sub-script/math-sub-script-function.ts @@ -0,0 +1,21 @@ +// http://www.datypic.com/sc/ooxml/e-m_sSub-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../../math-component"; +import { MathBase, MathSubScriptElement } from "../../n-ary"; +import { MathSubScriptProperties } from "./math-sub-script-function-properties"; + +export interface IMathSubScriptOptions { + readonly children: MathComponent[]; + readonly subScript: MathComponent[]; +} + +export class MathSubScript extends XmlComponent { + constructor(options: IMathSubScriptOptions) { + super("m:sSub"); + + this.root.push(new MathSubScriptProperties()); + this.root.push(new MathBase(options.children)); + this.root.push(new MathSubScriptElement(options.subScript)); + } +} diff --git a/src/file/paragraph/math/script/sub-super-script/index.ts b/src/file/paragraph/math/script/sub-super-script/index.ts new file mode 100644 index 0000000000..88ddeb69fa --- /dev/null +++ b/src/file/paragraph/math/script/sub-super-script/index.ts @@ -0,0 +1,2 @@ +export * from "./math-sub-super-script-function"; +export * from "./math-sub-super-script-function-properties"; diff --git a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.spec.ts b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.spec.ts new file mode 100644 index 0000000000..b0f934823a --- /dev/null +++ b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.spec.ts @@ -0,0 +1,17 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; +import { MathSubSuperScriptProperties } from "./math-sub-super-script-function-properties"; + +describe("MathSubSuperScriptProperties", () => { + describe("#constructor()", () => { + it("should create a MathSubSuperScriptProperties with correct root key", () => { + const mathSubSuperScriptProperties = new MathSubSuperScriptProperties(); + + const tree = new Formatter().format(mathSubSuperScriptProperties); + expect(tree).to.deep.equal({ + "m:sSubSupPr": {}, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.ts b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.ts new file mode 100644 index 0000000000..b3218a7d3a --- /dev/null +++ b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function-properties.ts @@ -0,0 +1,8 @@ +// http://www.datypic.com/sc/ooxml/e-m_sSubSupPr-1.html +import { XmlComponent } from "file/xml-components"; + +export class MathSubSuperScriptProperties extends XmlComponent { + constructor() { + super("m:sSubSupPr"); + } +} diff --git a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.spec.ts b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.spec.ts new file mode 100644 index 0000000000..68a86fb26b --- /dev/null +++ b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.spec.ts @@ -0,0 +1,60 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../../math-run"; +import { MathSubSuperScript } from "./math-sub-super-script-function"; + +describe("MathSubScript", () => { + describe("#constructor()", () => { + it("should create a MathSubScript with correct root key", () => { + const mathSubScript = new MathSubSuperScript({ + children: [new MathRun("e")], + subScript: [new MathRun("2")], + superScript: [new MathRun("5")], + }); + + const tree = new Formatter().format(mathSubScript); + expect(tree).to.deep.equal({ + "m:sSubSup": [ + { + "m:sSubSupPr": {}, + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["e"], + }, + ], + }, + ], + }, + { + "m:sub": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + { + "m:sup": [ + { + "m:r": [ + { + "m:t": ["5"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.ts b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.ts new file mode 100644 index 0000000000..c382c9ff4c --- /dev/null +++ b/src/file/paragraph/math/script/sub-super-script/math-sub-super-script-function.ts @@ -0,0 +1,23 @@ +// http://www.datypic.com/sc/ooxml/e-m_sSubSup-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../../math-component"; +import { MathBase, MathSubScriptElement, MathSuperScriptElement } from "../../n-ary"; +import { MathSubSuperScriptProperties } from "./math-sub-super-script-function-properties"; + +export interface IMathSubSuperScriptOptions { + readonly children: MathComponent[]; + readonly subScript: MathComponent[]; + readonly superScript: MathComponent[]; +} + +export class MathSubSuperScript extends XmlComponent { + constructor(options: IMathSubSuperScriptOptions) { + super("m:sSubSup"); + + this.root.push(new MathSubSuperScriptProperties()); + this.root.push(new MathBase(options.children)); + this.root.push(new MathSubScriptElement(options.subScript)); + this.root.push(new MathSuperScriptElement(options.superScript)); + } +} diff --git a/src/file/paragraph/math/script/super-script/index.ts b/src/file/paragraph/math/script/super-script/index.ts new file mode 100644 index 0000000000..2864fe9ac5 --- /dev/null +++ b/src/file/paragraph/math/script/super-script/index.ts @@ -0,0 +1,2 @@ +export * from "./math-super-script-function"; +export * from "./math-super-script-function-properties"; diff --git a/src/file/paragraph/math/script/super-script/math-super-script-function-properties.spec.ts b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.spec.ts new file mode 100644 index 0000000000..f95920f152 --- /dev/null +++ b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.spec.ts @@ -0,0 +1,17 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; +import { MathSuperScriptProperties } from "./math-super-script-function-properties"; + +describe("MathSuperScriptProperties", () => { + describe("#constructor()", () => { + it("should create a MathSuperScriptProperties with correct root key", () => { + const mathSuperScriptProperties = new MathSuperScriptProperties(); + + const tree = new Formatter().format(mathSuperScriptProperties); + expect(tree).to.deep.equal({ + "m:sSupPr": {}, + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/super-script/math-super-script-function-properties.ts b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.ts new file mode 100644 index 0000000000..ea5dbac20b --- /dev/null +++ b/src/file/paragraph/math/script/super-script/math-super-script-function-properties.ts @@ -0,0 +1,8 @@ +// http://www.datypic.com/sc/ooxml/e-m_sSupPr-1.html +import { XmlComponent } from "file/xml-components"; + +export class MathSuperScriptProperties extends XmlComponent { + constructor() { + super("m:sSupPr"); + } +} diff --git a/src/file/paragraph/math/script/super-script/math-super-script-function.spec.ts b/src/file/paragraph/math/script/super-script/math-super-script-function.spec.ts new file mode 100644 index 0000000000..ae3740360b --- /dev/null +++ b/src/file/paragraph/math/script/super-script/math-super-script-function.spec.ts @@ -0,0 +1,48 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { MathRun } from "../../math-run"; +import { MathSuperScript } from "./math-super-script-function"; + +describe("MathSuperScript", () => { + describe("#constructor()", () => { + it("should create a MathSuperScript with correct root key", () => { + const mathSuperScript = new MathSuperScript({ + children: [new MathRun("e")], + superScript: [new MathRun("2")], + }); + + const tree = new Formatter().format(mathSuperScript); + expect(tree).to.deep.equal({ + "m:sSup": [ + { + "m:sSupPr": {}, + }, + { + "m:e": [ + { + "m:r": [ + { + "m:t": ["e"], + }, + ], + }, + ], + }, + { + "m:sup": [ + { + "m:r": [ + { + "m:t": ["2"], + }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/math/script/super-script/math-super-script-function.ts b/src/file/paragraph/math/script/super-script/math-super-script-function.ts new file mode 100644 index 0000000000..eeffdf124a --- /dev/null +++ b/src/file/paragraph/math/script/super-script/math-super-script-function.ts @@ -0,0 +1,21 @@ +// http://www.datypic.com/sc/ooxml/e-m_sSup-1.html +import { XmlComponent } from "file/xml-components"; + +import { MathComponent } from "../../math-component"; +import { MathBase, MathSuperScriptElement } from "../../n-ary"; +import { MathSuperScriptProperties } from "./math-super-script-function-properties"; + +export interface IMathSuperScriptOptions { + readonly children: MathComponent[]; + readonly superScript: MathComponent[]; +} + +export class MathSuperScript extends XmlComponent { + constructor(options: IMathSuperScriptOptions) { + super("m:sSup"); + + this.root.push(new MathSuperScriptProperties()); + this.root.push(new MathBase(options.children)); + this.root.push(new MathSuperScriptElement(options.superScript)); + } +} diff --git a/src/file/paragraph/paragraph.spec.ts b/src/file/paragraph/paragraph.spec.ts index 04cc8af8e4..4302801a01 100644 --- a/src/file/paragraph/paragraph.spec.ts +++ b/src/file/paragraph/paragraph.spec.ts @@ -5,9 +5,12 @@ import { stub } from "sinon"; import { Formatter } from "export/formatter"; import { EMPTY_OBJECT } from "file/xml-components"; +import { IViewWrapper } from "../document-wrapper"; +import { ShadingType } from "../table/shading"; import { AlignmentType, HeadingLevel, LeaderType, PageBreak, TabStopPosition, TabStopType } from "./formatting"; -import { Bookmark } from "./links"; +import { Bookmark, ExternalHyperlink } from "./links"; import { Paragraph } from "./paragraph"; +import { TextRun } from "./run"; describe("Paragraph", () => { describe("#constructor()", () => { @@ -759,4 +762,82 @@ describe("Paragraph", () => { }); }); }); + + describe("#shading", () => { + it("should set shading to the given value", () => { + const paragraph = new Paragraph({ + shading: { + type: ShadingType.REVERSE_DIAGONAL_STRIPE, + color: "00FFFF", + fill: "FF0000", + }, + }); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:pPr": [ + { + "w:shd": { + _attr: { + "w:color": "00FFFF", + "w:fill": "FF0000", + "w:val": "reverseDiagStripe", + }, + }, + }, + ], + }, + ], + }); + }); + }); + + describe("#prepForXml", () => { + it("should set Internal Hyperlink", () => { + const paragraph = new Paragraph({ + children: [ + new ExternalHyperlink({ + child: new TextRun("test"), + link: "http://www.google.com", + }), + ], + }); + const fileMock = ({ + Relationships: { + createRelationship: () => ({}), + }, + } as unknown) as IViewWrapper; + paragraph.prepForXml(fileMock); + const tree = new Formatter().format(paragraph); + expect(tree).to.deep.equal({ + "w:p": [ + { + "w:hyperlink": [ + { + _attr: { + "r:id": "rIdtest-unique-id", + "w:history": 1, + }, + }, + { + "w:r": [ + { + "w:t": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "test", + ], + }, + ], + }, + ], + }, + ], + }); + }); + }); }); diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index e1db0ec910..7c05ee711c 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -1,25 +1,35 @@ // http://officeopenxml.com/WPparagraph.php +import * as shortid from "shortid"; + import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run"; import { IXmlableObject, XmlComponent } from "file/xml-components"; -import { File } from "../file"; +import { IViewWrapper } from "../document-wrapper"; +import { TargetModeType } from "../relationships/relationship/relationship"; +import { DeletedTextRun, InsertedTextRun } from "../track-revision"; import { PageBreak } from "./formatting/page-break"; -import { Bookmark, HyperlinkRef } from "./links"; +import { Bookmark, ConcreteHyperlink, ExternalHyperlink, InternalHyperlink } from "./links"; +import { Math } from "./math"; import { IParagraphPropertiesOptions, ParagraphProperties } from "./properties"; import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run"; +export type ParagraphChild = + | TextRun + | PictureRun + | SymbolRun + | Bookmark + | PageBreak + | SequentialIdentifier + | FootnoteReferenceRun + | InternalHyperlink + | ExternalHyperlink + | InsertedTextRun + | DeletedTextRun + | Math; + export interface IParagraphOptions extends IParagraphPropertiesOptions { readonly text?: string; - readonly children?: ( - | TextRun - | PictureRun - | SymbolRun - | Bookmark - | PageBreak - | SequentialIdentifier - | FootnoteReferenceRun - | HyperlinkRef - )[]; + readonly children?: ParagraphChild[]; } export class Paragraph extends XmlComponent { @@ -64,11 +74,18 @@ export class Paragraph extends XmlComponent { } } - public prepForXml(file: File): IXmlableObject | undefined { + public prepForXml(file: IViewWrapper): IXmlableObject | undefined { for (const element of this.root) { - if (element instanceof HyperlinkRef) { + if (element instanceof ExternalHyperlink) { const index = this.root.indexOf(element); - this.root[index] = file.HyperlinkCache[element.id]; + const concreteHyperlink = new ConcreteHyperlink(element.options.child, shortid.generate().toLowerCase()); + file.Relationships.createRelationship( + concreteHyperlink.linkId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", + element.options.link, + TargetModeType.EXTERNAL, + ); + this.root[index] = concreteHyperlink; } } diff --git a/src/file/paragraph/properties.ts b/src/file/paragraph/properties.ts index af97e2c684..b12bfedd8e 100644 --- a/src/file/paragraph/properties.ts +++ b/src/file/paragraph/properties.ts @@ -1,5 +1,6 @@ // http://officeopenxml.com/WPparagraphProperties.php import { IgnoreIfEmptyXmlComponent, XmlComponent } from "file/xml-components"; +import { ShadingType } from "../table/shading"; import { Alignment, AlignmentType } from "./formatting/alignment"; import { Bidirectional } from "./formatting/bidirectional"; import { Border, IBorderOptions, ThematicBreak } from "./formatting/border"; @@ -11,6 +12,7 @@ import { HeadingLevel, Style } from "./formatting/style"; import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop"; import { NumberProperties } from "./formatting/unordered-list"; import { OutlineLevel } from "./links"; +import { Shading } from "./run/formatting"; export interface IParagraphStylePropertiesOptions { readonly alignment?: AlignmentType; @@ -44,6 +46,11 @@ export interface IParagraphPropertiesOptions extends IParagraphStylePropertiesOp readonly level: number; readonly custom?: boolean; }; + readonly shading?: { + readonly type: ShadingType; + readonly fill: string; + readonly color: string; + }; } export class ParagraphProperties extends IgnoreIfEmptyXmlComponent { @@ -131,6 +138,10 @@ export class ParagraphProperties extends IgnoreIfEmptyXmlComponent { if (options.leftTabStop) { this.push(new TabStop(TabStopType.LEFT, options.leftTabStop)); } + + if (options.shading) { + this.push(new Shading(options.shading.type, options.shading.fill, options.shading.color)); + } } public push(item: XmlComponent): void { diff --git a/src/file/paragraph/run/run.spec.ts b/src/file/paragraph/run/run.spec.ts index 40cc7055cd..017c616938 100644 --- a/src/file/paragraph/run/run.spec.ts +++ b/src/file/paragraph/run/run.spec.ts @@ -239,13 +239,29 @@ describe("Run", () => { describe("#break()", () => { it("it should add break to the run", () => { - const run = new Run({}); - run.break(); + const run = new Run({ + break: 1, + }); const tree = new Formatter().format(run); expect(tree).to.deep.equal({ "w:r": [{ "w:br": {} }], }); }); + + it("it should add two breaks to the run", () => { + const run = new Run({ + break: 2, + }); + const tree = new Formatter().format(run); + expect(tree).to.deep.equal({ + "w:r": [ + { "w:br": {} }, + { + "w:br": {}, + }, + ], + }); + }); }); describe("#font()", () => { diff --git a/src/file/paragraph/run/run.ts b/src/file/paragraph/run/run.ts index bf7903e35e..461fd9da29 100644 --- a/src/file/paragraph/run/run.ts +++ b/src/file/paragraph/run/run.ts @@ -11,6 +11,7 @@ import { Text } from "./run-components/text"; export interface IRunOptions extends IRunPropertiesOptions { readonly children?: (Begin | FieldInstruction | Separate | End | PageNumber | FootnoteReferenceRun | string)[]; + readonly break?: number; readonly text?: string; } @@ -62,10 +63,11 @@ export class Run extends XmlComponent { } else if (options.text) { this.root.push(new Text(options.text)); } - } - public break(): Run { - this.root.splice(1, 0, new Break()); - return this; + if (options.break) { + for (let i = 0; i < options.break; i++) { + this.root.splice(1, 0, new Break()); + } + } } } diff --git a/src/file/settings/compatibility-setting/compatibility-setting.spec.ts b/src/file/settings/compatibility-setting/compatibility-setting.spec.ts new file mode 100644 index 0000000000..0f65697cda --- /dev/null +++ b/src/file/settings/compatibility-setting/compatibility-setting.spec.ts @@ -0,0 +1,23 @@ +import { expect } from "chai"; +import { Formatter } from "export/formatter"; + +import { CompatibilitySetting } from "./compatibility-setting"; + +describe("CompatibilitySetting", () => { + describe("#constructor", () => { + it("creates an initially empty property object", () => { + const compatibilitySetting = new CompatibilitySetting(15); + + const tree = new Formatter().format(compatibilitySetting); + expect(tree).to.deep.equal({ + "w:compatSetting": { + _attr: { + "w:name": "compatibilityMode", + "w:uri": "http://schemas.microsoft.com/office/word", + "w:val": 15, + }, + }, + }); + }); + }); +}); diff --git a/src/file/settings/compatibility-setting/compatibility-setting.ts b/src/file/settings/compatibility-setting/compatibility-setting.ts new file mode 100644 index 0000000000..9f2a0b4982 --- /dev/null +++ b/src/file/settings/compatibility-setting/compatibility-setting.ts @@ -0,0 +1,27 @@ +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +export class CompatibilitySettingAttributes extends XmlAttributeComponent<{ + readonly version: number; + readonly name: string; + readonly uri: string; +}> { + protected readonly xmlKeys = { + version: "w:val", + name: "w:name", + uri: "w:uri", + }; +} + +export class CompatibilitySetting extends XmlComponent { + constructor(version: number) { + super("w:compatSetting"); + + this.root.push( + new CompatibilitySettingAttributes({ + version, + uri: "http://schemas.microsoft.com/office/word", + name: "compatibilityMode", + }), + ); + } +} diff --git a/src/file/settings/compatibility.spec.ts b/src/file/settings/compatibility.spec.ts index 6399dcfeea..fa9809672d 100644 --- a/src/file/settings/compatibility.spec.ts +++ b/src/file/settings/compatibility.spec.ts @@ -1,13 +1,14 @@ import { expect } from "chai"; import { Formatter } from "export/formatter"; -import { Compatibility } from "file/settings/compatibility"; import { EMPTY_OBJECT } from "file/xml-components"; +import { Compatibility } from "./compatibility"; + describe("Compatibility", () => { describe("#constructor", () => { it("creates an initially empty property object", () => { - const compatibility = new Compatibility(); + const compatibility = new Compatibility({}); const tree = new Formatter().format(compatibility); expect(tree).to.deep.equal({ "w:compat": EMPTY_OBJECT }); @@ -16,8 +17,9 @@ describe("Compatibility", () => { describe("#doNotExpandShiftReturn", () => { it("should create a setting for not justifying lines ending in soft line break", () => { - const compatibility = new Compatibility(); - compatibility.doNotExpandShiftReturn(); + const compatibility = new Compatibility({ + doNotExpandShiftReturn: true, + }); const tree = new Formatter().format(compatibility); expect(tree).to.deep.equal({ "w:compat": [{ "w:doNotExpandShiftReturn": EMPTY_OBJECT }] }); diff --git a/src/file/settings/compatibility.ts b/src/file/settings/compatibility.ts index afd2dd0f37..fe63da92c5 100644 --- a/src/file/settings/compatibility.ts +++ b/src/file/settings/compatibility.ts @@ -1,4 +1,5 @@ import { XmlComponent } from "file/xml-components"; +import { CompatibilitySetting } from "./compatibility-setting/compatibility-setting"; class DoNotExpandShiftReturn extends XmlComponent { constructor() { @@ -6,14 +7,21 @@ class DoNotExpandShiftReturn extends XmlComponent { } } +export interface ICompatibilityOptions { + readonly doNotExpandShiftReturn?: boolean; + readonly version?: number; +} + export class Compatibility extends XmlComponent { - constructor() { + constructor(options: ICompatibilityOptions) { super("w:compat"); - } - public doNotExpandShiftReturn(): Compatibility { - this.root.push(new DoNotExpandShiftReturn()); + if (options.doNotExpandShiftReturn) { + this.root.push(new DoNotExpandShiftReturn()); + } - return this; + if (options.version) { + this.root.push(new CompatibilitySetting(options.version)); + } } } diff --git a/src/file/settings/display-background-shape.spec.ts b/src/file/settings/display-background-shape.spec.ts new file mode 100644 index 0000000000..34c23b65db --- /dev/null +++ b/src/file/settings/display-background-shape.spec.ts @@ -0,0 +1,17 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { DisplayBackgroundShape } from "./display-background-shape"; + +describe("DisplayBackgroundShape", () => { + describe("#constructor()", () => { + it("should create", () => { + const displayBackgroundShape = new DisplayBackgroundShape(); + const tree = new Formatter().format(displayBackgroundShape); + expect(tree).to.deep.equal({ + "w:displayBackgroundShape": {}, + }); + }); + }); +}); diff --git a/src/file/settings/display-background-shape.ts b/src/file/settings/display-background-shape.ts new file mode 100644 index 0000000000..bd0cd15675 --- /dev/null +++ b/src/file/settings/display-background-shape.ts @@ -0,0 +1,9 @@ +// http://officeopenxml.com/WPdocument.php +// http://www.datypic.com/sc/ooxml/e-w_background-1.html +import { XmlComponent } from "file/xml-components"; + +export class DisplayBackgroundShape extends XmlComponent { + constructor() { + super("w:displayBackgroundShape"); + } +} diff --git a/src/file/settings/settings.spec.ts b/src/file/settings/settings.spec.ts index 1e34c781c2..381b24d372 100644 --- a/src/file/settings/settings.spec.ts +++ b/src/file/settings/settings.spec.ts @@ -7,7 +7,7 @@ import { Settings } from "./settings"; describe("Settings", () => { describe("#constructor", () => { it("should create a empty Settings with correct rootKey", () => { - const settings = new Settings(); + const settings = new Settings({}); const tree = new Formatter().format(settings); let keys = Object.keys(tree); expect(keys).is.an.instanceof(Array); @@ -15,8 +15,7 @@ describe("Settings", () => { expect(keys[0]).to.be.equal("w:settings"); keys = Object.keys(tree["w:settings"]); expect(keys).is.an.instanceof(Array); - expect(keys).has.length(1); - expect(keys[0]).to.be.equal("_attr"); + expect(keys).has.length(3); }); }); describe("#addUpdateFields", () => { @@ -28,16 +27,16 @@ describe("Settings", () => { expect(keys[0]).to.be.equal("w:settings"); const rootArray = tree["w:settings"]; expect(rootArray).is.an.instanceof(Array); - expect(rootArray).has.length(2); + expect(rootArray).has.length(4); keys = Object.keys(rootArray[0]); expect(keys).is.an.instanceof(Array); expect(keys).has.length(1); expect(keys[0]).to.be.equal("_attr"); - keys = Object.keys(rootArray[1]); + keys = Object.keys(rootArray[3]); expect(keys).is.an.instanceof(Array); expect(keys).has.length(1); expect(keys[0]).to.be.equal("w:updateFields"); - const updateFields = rootArray[1]["w:updateFields"]; + const updateFields = rootArray[3]["w:updateFields"]; keys = Object.keys(updateFields); expect(keys).is.an.instanceof(Array); expect(keys).has.length(1); @@ -46,12 +45,12 @@ describe("Settings", () => { expect(updateFieldsAttr["w:val"]).to.be.equal(true); }; it("should add a UpdateFields with value true", () => { - const settings = new Settings(); + const settings = new Settings({}); settings.addUpdateFields(); assertSettingsWithUpdateFields(settings); }); it("should add a UpdateFields with value true only once", () => { - const settings = new Settings(); + const settings = new Settings({}); settings.addUpdateFields(); assertSettingsWithUpdateFields(settings); settings.addUpdateFields(); @@ -59,16 +58,15 @@ describe("Settings", () => { }); }); describe("#addCompatibility", () => { - it("should add an empty Compatibility", () => { - const settings = new Settings(); - settings.addCompatibility(); + it("should add an empty Compatibility by default", () => { + const settings = new Settings({}); const tree = new Formatter().format(settings); let keys: string[] = Object.keys(tree); expect(keys[0]).to.be.equal("w:settings"); const rootArray = tree["w:settings"]; expect(rootArray).is.an.instanceof(Array); - expect(rootArray).has.length(2); + expect(rootArray).has.length(3); keys = Object.keys(rootArray[0]); expect(keys).is.an.instanceof(Array); expect(keys).has.length(1); @@ -77,6 +75,58 @@ describe("Settings", () => { expect(keys).is.an.instanceof(Array); expect(keys).has.length(1); expect(keys[0]).to.be.equal("w:compat"); + expect(rootArray[1]["w:compat"][0]).to.deep.equal({ + "w:compatSetting": { + _attr: { + "w:val": 15, + "w:uri": "http://schemas.microsoft.com/office/word", + "w:name": "compatibilityMode", + }, + }, + }); + }); + }); + describe("#addTrackRevisions", () => { + it("should add an empty Track Revisions", () => { + const settings = new Settings({}); + settings.addTrackRevisions(); + + const tree = new Formatter().format(settings); + let keys: string[] = Object.keys(tree); + expect(keys[0]).to.be.equal("w:settings"); + const rootArray = tree["w:settings"]; + expect(rootArray).is.an.instanceof(Array); + expect(rootArray).has.length(4); + keys = Object.keys(rootArray[0]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("_attr"); + keys = Object.keys(rootArray[3]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("w:trackRevisions"); + }); + }); + describe("#addTrackRevisionsTwice", () => { + it("should add an empty Track Revisions if called twice", () => { + const settings = new Settings({}); + settings.addTrackRevisions(); + settings.addTrackRevisions(); + + const tree = new Formatter().format(settings); + let keys: string[] = Object.keys(tree); + expect(keys[0]).to.be.equal("w:settings"); + const rootArray = tree["w:settings"]; + expect(rootArray).is.an.instanceof(Array); + expect(rootArray).has.length(4); + keys = Object.keys(rootArray[0]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("_attr"); + keys = Object.keys(rootArray[3]); + expect(keys).is.an.instanceof(Array); + expect(keys).has.length(1); + expect(keys[0]).to.be.equal("w:trackRevisions"); }); }); }); diff --git a/src/file/settings/settings.ts b/src/file/settings/settings.ts index abae0e61d4..e63d539a39 100644 --- a/src/file/settings/settings.ts +++ b/src/file/settings/settings.ts @@ -1,5 +1,7 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { Compatibility } from "./compatibility"; +import { DisplayBackgroundShape } from "./display-background-shape"; +import { TrackRevisions } from "./track-revisions"; import { UpdateFields } from "./update-fields"; export interface ISettingsAttributesProperties { @@ -44,10 +46,15 @@ export class SettingsAttributes extends XmlAttributeComponent child instanceof Compatibility)) { - this.addChildElement(this.compatibility); + public addTrackRevisions(): TrackRevisions { + if (!this.root.find((child) => child instanceof TrackRevisions)) { + this.addChildElement(this.trackRevisions); } - return this.compatibility; + return this.trackRevisions; } } diff --git a/src/file/settings/track-revisions.spec.ts b/src/file/settings/track-revisions.spec.ts new file mode 100644 index 0000000000..3875d51f0a --- /dev/null +++ b/src/file/settings/track-revisions.spec.ts @@ -0,0 +1,16 @@ +import { expect } from "chai"; +import { Formatter } from "export/formatter"; +import { TrackRevisions } from "file/settings/track-revisions"; + +import { EMPTY_OBJECT } from "file/xml-components"; + +describe("TrackRevisions", () => { + describe("#constructor", () => { + it("creates an initially empty property object", () => { + const trackRevisions = new TrackRevisions(); + + const tree = new Formatter().format(trackRevisions); + expect(tree).to.deep.equal({ "w:trackRevisions": EMPTY_OBJECT }); + }); + }); +}); diff --git a/src/file/settings/track-revisions.ts b/src/file/settings/track-revisions.ts new file mode 100644 index 0000000000..2da692827e --- /dev/null +++ b/src/file/settings/track-revisions.ts @@ -0,0 +1,7 @@ +import { XmlComponent } from "file/xml-components"; + +export class TrackRevisions extends XmlComponent { + constructor() { + super("w:trackRevisions"); + } +} diff --git a/src/file/styles/factory.ts b/src/file/styles/factory.ts index f5bca8248d..097c812433 100644 --- a/src/file/styles/factory.ts +++ b/src/file/styles/factory.ts @@ -1,7 +1,7 @@ import { DocumentAttributes } from "../document/document-attributes"; import { IStylesOptions } from "./styles"; -import { DocumentDefaults } from "./defaults"; +import { DocumentDefaults, IDocumentDefaultsOptions } from "./defaults"; import { FootnoteReferenceStyle, FootnoteText, @@ -13,12 +13,32 @@ import { Heading5Style, Heading6Style, HyperlinkStyle, + IBaseCharacterStyleOptions, + IBaseParagraphStyleOptions, ListParagraph, + StrongStyle, TitleStyle, } from "./style"; +export interface IDefaultStylesOptions { + readonly document?: IDocumentDefaultsOptions; + readonly title?: IBaseParagraphStyleOptions; + readonly heading1?: IBaseParagraphStyleOptions; + readonly heading2?: IBaseParagraphStyleOptions; + readonly heading3?: IBaseParagraphStyleOptions; + readonly heading4?: IBaseParagraphStyleOptions; + readonly heading5?: IBaseParagraphStyleOptions; + readonly heading6?: IBaseParagraphStyleOptions; + readonly strong?: IBaseParagraphStyleOptions; + readonly listParagraph?: IBaseParagraphStyleOptions; + readonly hyperlink?: IBaseCharacterStyleOptions; + readonly footnoteReference?: IBaseCharacterStyleOptions; + readonly footnoteText?: IBaseParagraphStyleOptions; + readonly footnoteTextChar?: IBaseCharacterStyleOptions; +} + export class DefaultStylesFactory { - public newInstance(): IStylesOptions { + public newInstance(options: IDefaultStylesOptions = {}): IStylesOptions { const documentAttributes = new DocumentAttributes({ mc: "http://schemas.openxmlformats.org/markup-compatibility/2006", r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships", @@ -30,51 +50,64 @@ export class DefaultStylesFactory { return { initialStyles: documentAttributes, importedStyles: [ - new DocumentDefaults(), + new DocumentDefaults(options.document), new TitleStyle({ run: { size: 56, }, + ...options.title, }), new Heading1Style({ run: { color: "2E74B5", size: 32, }, + ...options.heading1, }), new Heading2Style({ run: { color: "2E74B5", size: 26, }, + ...options.heading2, }), new Heading3Style({ run: { color: "1F4D78", size: 24, }, + ...options.heading3, }), new Heading4Style({ run: { color: "2E74B5", italics: true, }, + ...options.heading4, }), new Heading5Style({ run: { color: "2E74B5", }, + ...options.heading5, }), new Heading6Style({ run: { color: "1F4D78", }, + ...options.heading6, }), - new ListParagraph({}), - new HyperlinkStyle({}), - new FootnoteReferenceStyle({}), - new FootnoteText({}), - new FootnoteTextChar({}), + new StrongStyle({ + run: { + bold: true, + }, + ...options.strong, + }), + new ListParagraph(options.listParagraph || {}), + new HyperlinkStyle(options.hyperlink || {}), + new FootnoteReferenceStyle(options.footnoteReference || {}), + new FootnoteText(options.footnoteText || {}), + new FootnoteTextChar(options.footnoteTextChar || {}), ], }; } diff --git a/src/file/styles/style/character-style.spec.ts b/src/file/styles/style/character-style.spec.ts index 2fddce1c1b..c7adf4ff97 100644 --- a/src/file/styles/style/character-style.spec.ts +++ b/src/file/styles/style/character-style.spec.ts @@ -6,12 +6,12 @@ import { UnderlineType } from "file/paragraph/run/underline"; import { ShadingType } from "file/table"; import { EMPTY_OBJECT } from "file/xml-components"; -import { CharacterStyle } from "./character-style"; +import { StyleForCharacter } from "./character-style"; describe("CharacterStyle", () => { describe("#constructor", () => { it("should set the style type to character and use the given style id", () => { - const style = new CharacterStyle({ id: "myStyleId" }); + const style = new StyleForCharacter({ id: "myStyleId" }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -31,7 +31,7 @@ describe("CharacterStyle", () => { }); it("should set the name of the style, if given", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", name: "Style Name", }); @@ -55,7 +55,7 @@ describe("CharacterStyle", () => { }); it("should add smallCaps", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { smallCaps: true, @@ -83,7 +83,7 @@ describe("CharacterStyle", () => { }); it("should add allCaps", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { allCaps: true, @@ -111,7 +111,7 @@ describe("CharacterStyle", () => { }); it("should add strike", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { strike: true, @@ -139,7 +139,7 @@ describe("CharacterStyle", () => { }); it("should add double strike", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { doubleStrike: true, @@ -167,7 +167,7 @@ describe("CharacterStyle", () => { }); it("should add sub script", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { subScript: true, @@ -203,7 +203,7 @@ describe("CharacterStyle", () => { }); it("should add font by name", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { font: "test font", @@ -242,7 +242,7 @@ describe("CharacterStyle", () => { }); it("should add font for ascii and eastAsia", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { font: { @@ -282,7 +282,7 @@ describe("CharacterStyle", () => { }); it("should add character spacing", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { characterSpacing: 100, @@ -312,7 +312,7 @@ describe("CharacterStyle", () => { describe("formatting methods: style attributes", () => { it("#basedOn", () => { - const style = new CharacterStyle({ id: "myStyleId", basedOn: "otherId" }); + const style = new StyleForCharacter({ id: "myStyleId", basedOn: "otherId" }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -357,7 +357,7 @@ describe("CharacterStyle", () => { ]; sizeTests.forEach(({ size, sizeComplexScript, expected }) => { it(`#size ${size} cs ${sizeComplexScript}`, () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { size, sizeComplexScript }, }); @@ -385,7 +385,7 @@ describe("CharacterStyle", () => { describe("#underline", () => { it("should set underline to 'single' if no arguments are given", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { underline: {}, @@ -413,7 +413,7 @@ describe("CharacterStyle", () => { }); it("should set the style if given", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { underline: { @@ -443,7 +443,7 @@ describe("CharacterStyle", () => { }); it("should set the style and color if given", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { underline: { @@ -476,7 +476,7 @@ describe("CharacterStyle", () => { describe("#emphasisMark", () => { it("should set emphasisMark to 'dot' if no arguments are given", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { emphasisMark: {}, @@ -504,7 +504,7 @@ describe("CharacterStyle", () => { }); it("should set the style if given", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { emphasisMark: { @@ -535,7 +535,7 @@ describe("CharacterStyle", () => { }); it("#superScript", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { superScript: true, @@ -571,7 +571,7 @@ describe("CharacterStyle", () => { }); it("#color", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { color: "123456", @@ -616,7 +616,7 @@ describe("CharacterStyle", () => { ]; boldTests.forEach(({ bold, boldComplexScript, expected }) => { it(`#bold ${bold} cs ${boldComplexScript}`, () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { bold, boldComplexScript }, }); @@ -660,7 +660,7 @@ describe("CharacterStyle", () => { ]; italicsTests.forEach(({ italics, italicsComplexScript, expected }) => { it(`#italics ${italics} cs ${italicsComplexScript}`, () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { italics, italicsComplexScript }, }); @@ -687,7 +687,7 @@ describe("CharacterStyle", () => { }); it("#link", () => { - const style = new CharacterStyle({ id: "myStyleId", link: "MyLink" }); + const style = new StyleForCharacter({ id: "myStyleId", link: "MyLink" }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -708,7 +708,7 @@ describe("CharacterStyle", () => { }); it("#semiHidden", () => { - const style = new CharacterStyle({ id: "myStyleId", semiHidden: true }); + const style = new StyleForCharacter({ id: "myStyleId", semiHidden: true }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -749,7 +749,7 @@ describe("CharacterStyle", () => { ]; highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => { it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { highlight, highlightComplexScript }, }); @@ -838,7 +838,7 @@ describe("CharacterStyle", () => { ]; shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => { it("#shadow correctly", () => { - const style = new CharacterStyle({ + const style = new StyleForCharacter({ id: "myStyleId", run: { shadow, shading, shadingComplexScript }, }); diff --git a/src/file/styles/style/character-style.ts b/src/file/styles/style/character-style.ts index 0191a0460e..63252bddd5 100644 --- a/src/file/styles/style/character-style.ts +++ b/src/file/styles/style/character-style.ts @@ -15,7 +15,7 @@ export interface ICharacterStyleOptions extends IBaseCharacterStyleOptions { readonly name?: string; } -export class CharacterStyle extends Style { +export class StyleForCharacter extends Style { private readonly runProperties: RunProperties; constructor(options: ICharacterStyleOptions) { diff --git a/src/file/styles/style/default-styles.spec.ts b/src/file/styles/style/default-styles.spec.ts index 1cf26ba482..962f47d9c0 100644 --- a/src/file/styles/style/default-styles.spec.ts +++ b/src/file/styles/style/default-styles.spec.ts @@ -120,6 +120,20 @@ describe("Default Styles", () => { }); }); + it("StrongStyle#constructor", () => { + const style = new defaultStyles.StrongStyle({}); + const tree = new Formatter().format(style); + expect(tree).to.deep.equal({ + "w:style": [ + { _attr: { "w:type": "paragraph", "w:styleId": "Strong" } }, + { "w:name": { _attr: { "w:val": "Strong" } } }, + { "w:basedOn": { _attr: { "w:val": "Normal" } } }, + { "w:next": { _attr: { "w:val": "Normal" } } }, + { "w:qFormat": EMPTY_OBJECT }, + ], + }); + }); + it("ListParagraph#constructor", () => { const style = new defaultStyles.ListParagraph({}); const tree = new Formatter().format(style); diff --git a/src/file/styles/style/default-styles.ts b/src/file/styles/style/default-styles.ts index 8974435a95..f8be7572ee 100644 --- a/src/file/styles/style/default-styles.ts +++ b/src/file/styles/style/default-styles.ts @@ -1,9 +1,9 @@ import { UnderlineType } from "file/paragraph/run/underline"; -import { CharacterStyle, IBaseCharacterStyleOptions } from "./character-style"; -import { IBaseParagraphStyleOptions, IParagraphStyleOptions, ParagraphStyle } from "./paragraph-style"; +import { IBaseCharacterStyleOptions, StyleForCharacter } from "./character-style"; +import { IBaseParagraphStyleOptions, IParagraphStyleOptions, StyleForParagraph } from "./paragraph-style"; -export class HeadingStyle extends ParagraphStyle { +export class HeadingStyle extends StyleForParagraph { constructor(options: IParagraphStyleOptions) { super({ ...options, @@ -84,7 +84,17 @@ export class Heading6Style extends HeadingStyle { } } -export class ListParagraph extends ParagraphStyle { +export class StrongStyle extends HeadingStyle { + constructor(options: IBaseParagraphStyleOptions) { + super({ + ...options, + id: "Strong", + name: "Strong", + }); + } +} + +export class ListParagraph extends StyleForParagraph { constructor(options: IBaseParagraphStyleOptions) { super({ ...options, @@ -96,7 +106,7 @@ export class ListParagraph extends ParagraphStyle { } } -export class FootnoteText extends ParagraphStyle { +export class FootnoteText extends StyleForParagraph { constructor(options: IBaseParagraphStyleOptions) { super({ ...options, @@ -121,7 +131,7 @@ export class FootnoteText extends ParagraphStyle { } } -export class FootnoteReferenceStyle extends CharacterStyle { +export class FootnoteReferenceStyle extends StyleForCharacter { constructor(options: IBaseCharacterStyleOptions) { super({ ...options, @@ -136,7 +146,7 @@ export class FootnoteReferenceStyle extends CharacterStyle { } } -export class FootnoteTextChar extends CharacterStyle { +export class FootnoteTextChar extends StyleForCharacter { constructor(options: IBaseCharacterStyleOptions) { super({ ...options, @@ -152,7 +162,7 @@ export class FootnoteTextChar extends CharacterStyle { } } -export class HyperlinkStyle extends CharacterStyle { +export class HyperlinkStyle extends StyleForCharacter { constructor(options: IBaseCharacterStyleOptions) { super({ ...options, diff --git a/src/file/styles/style/paragraph-style.spec.ts b/src/file/styles/style/paragraph-style.spec.ts index 92b798f862..8683234793 100644 --- a/src/file/styles/style/paragraph-style.spec.ts +++ b/src/file/styles/style/paragraph-style.spec.ts @@ -6,12 +6,12 @@ import { UnderlineType } from "file/paragraph/run/underline"; import { ShadingType } from "file/table"; import { EMPTY_OBJECT } from "file/xml-components"; -import { ParagraphStyle } from "./paragraph-style"; +import { StyleForParagraph } from "./paragraph-style"; describe("ParagraphStyle", () => { describe("#constructor", () => { it("should set the style type to paragraph and use the given style id", () => { - const style = new ParagraphStyle({ id: "myStyleId" }); + const style = new StyleForParagraph({ id: "myStyleId" }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": { _attr: { "w:type": "paragraph", "w:styleId": "myStyleId" } }, @@ -19,7 +19,7 @@ describe("ParagraphStyle", () => { }); it("should set the name of the style, if given", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", name: "Style Name", }); @@ -35,7 +35,7 @@ describe("ParagraphStyle", () => { describe("formatting methods: style attributes", () => { it("#basedOn", () => { - const style = new ParagraphStyle({ id: "myStyleId", basedOn: "otherId" }); + const style = new StyleForParagraph({ id: "myStyleId", basedOn: "otherId" }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -46,7 +46,7 @@ describe("ParagraphStyle", () => { }); it("#quickFormat", () => { - const style = new ParagraphStyle({ id: "myStyleId", quickFormat: true }); + const style = new StyleForParagraph({ id: "myStyleId", quickFormat: true }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -62,7 +62,7 @@ describe("ParagraphStyle", () => { }); it("#next", () => { - const style = new ParagraphStyle({ id: "myStyleId", next: "otherId" }); + const style = new StyleForParagraph({ id: "myStyleId", next: "otherId" }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -75,7 +75,7 @@ describe("ParagraphStyle", () => { describe("formatting methods: paragraph properties", () => { it("#indent", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { indent: { left: 720 }, @@ -93,7 +93,7 @@ describe("ParagraphStyle", () => { }); it("#spacing", () => { - const style = new ParagraphStyle({ id: "myStyleId", paragraph: { spacing: { before: 50, after: 150 } } }); + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { spacing: { before: 50, after: 150 } } }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -106,7 +106,7 @@ describe("ParagraphStyle", () => { }); it("#center", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { alignment: AlignmentType.CENTER, @@ -124,7 +124,7 @@ describe("ParagraphStyle", () => { }); it("#character spacing", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { characterSpacing: 24, @@ -142,7 +142,7 @@ describe("ParagraphStyle", () => { }); it("#left", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { alignment: AlignmentType.LEFT, @@ -160,7 +160,7 @@ describe("ParagraphStyle", () => { }); it("#right", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { alignment: AlignmentType.RIGHT, @@ -178,7 +178,7 @@ describe("ParagraphStyle", () => { }); it("#justified", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { alignment: AlignmentType.JUSTIFIED, @@ -196,7 +196,7 @@ describe("ParagraphStyle", () => { }); it("#thematicBreak", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { thematicBreak: true, @@ -229,7 +229,7 @@ describe("ParagraphStyle", () => { }); it("#contextualSpacing", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { contextualSpacing: true, @@ -255,7 +255,7 @@ describe("ParagraphStyle", () => { }); it("#leftTabStop", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { leftTabStop: 1200, @@ -277,7 +277,7 @@ describe("ParagraphStyle", () => { }); it("#maxRightTabStop", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { rightTabStop: TabStopPosition.MAX, @@ -299,7 +299,7 @@ describe("ParagraphStyle", () => { }); it("#keepLines", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { keepLines: true, @@ -320,7 +320,7 @@ describe("ParagraphStyle", () => { }); it("#keepNext", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { keepNext: true, @@ -341,7 +341,7 @@ describe("ParagraphStyle", () => { }); it("#outlineLevel", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", paragraph: { outlineLevel: 1, @@ -381,7 +381,7 @@ describe("ParagraphStyle", () => { ]; sizeTests.forEach(({ size, sizeComplexScript, expected }) => { it(`#size ${size} cs ${sizeComplexScript}`, () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { size, sizeComplexScript }, }); @@ -393,7 +393,7 @@ describe("ParagraphStyle", () => { }); it("#smallCaps", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { smallCaps: true, @@ -411,7 +411,7 @@ describe("ParagraphStyle", () => { }); it("#allCaps", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { allCaps: true, @@ -429,7 +429,7 @@ describe("ParagraphStyle", () => { }); it("#strike", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { strike: true, @@ -447,7 +447,7 @@ describe("ParagraphStyle", () => { }); it("#doubleStrike", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { doubleStrike: true, @@ -465,7 +465,7 @@ describe("ParagraphStyle", () => { }); it("#subScript", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { subScript: true, @@ -483,7 +483,7 @@ describe("ParagraphStyle", () => { }); it("#superScript", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { superScript: true, @@ -501,7 +501,7 @@ describe("ParagraphStyle", () => { }); it("#font by name", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { font: "Times", @@ -530,7 +530,7 @@ describe("ParagraphStyle", () => { }); it("#font for ascii and eastAsia", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { font: { @@ -577,7 +577,7 @@ describe("ParagraphStyle", () => { ]; boldTests.forEach(({ bold, boldComplexScript, expected }) => { it(`#bold ${bold} cs ${boldComplexScript}`, () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { bold, boldComplexScript }, }); @@ -606,7 +606,7 @@ describe("ParagraphStyle", () => { ]; italicsTests.forEach(({ italics, italicsComplexScript, expected }) => { it(`#italics ${italics} cs ${italicsComplexScript}`, () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { italics, italicsComplexScript }, }); @@ -640,7 +640,7 @@ describe("ParagraphStyle", () => { ]; highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => { it(`#highlight ${highlight} cs ${highlightComplexScript}`, () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { highlight, highlightComplexScript }, }); @@ -714,7 +714,7 @@ describe("ParagraphStyle", () => { ]; shadingTests.forEach(({ shadow, shading, shadingComplexScript, expected }) => { it("#shadow correctly", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { shadow, shading, shadingComplexScript }, }); @@ -727,7 +727,7 @@ describe("ParagraphStyle", () => { describe("#underline", () => { it("should set underline to 'single' if no arguments are given", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { underline: {}, @@ -745,7 +745,7 @@ describe("ParagraphStyle", () => { }); it("should set the style if given", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { underline: { @@ -765,7 +765,7 @@ describe("ParagraphStyle", () => { }); it("should set the style and color if given", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { underline: { @@ -788,7 +788,7 @@ describe("ParagraphStyle", () => { describe("#emphasisMark", () => { it("should set emphasisMark to 'dot' if no arguments are given", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { emphasisMark: {}, @@ -806,7 +806,7 @@ describe("ParagraphStyle", () => { }); it("should set the style if given", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { emphasisMark: { @@ -827,7 +827,7 @@ describe("ParagraphStyle", () => { }); it("#color", () => { - const style = new ParagraphStyle({ + const style = new StyleForParagraph({ id: "myStyleId", run: { color: "123456", @@ -845,7 +845,7 @@ describe("ParagraphStyle", () => { }); it("#link", () => { - const style = new ParagraphStyle({ id: "myStyleId", link: "MyLink" }); + const style = new StyleForParagraph({ id: "myStyleId", link: "MyLink" }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -861,7 +861,7 @@ describe("ParagraphStyle", () => { }); it("#semiHidden", () => { - const style = new ParagraphStyle({ id: "myStyleId", semiHidden: true }); + const style = new StyleForParagraph({ id: "myStyleId", semiHidden: true }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -877,7 +877,7 @@ describe("ParagraphStyle", () => { }); it("#uiPriority", () => { - const style = new ParagraphStyle({ id: "myStyleId", uiPriority: 99 }); + const style = new StyleForParagraph({ id: "myStyleId", uiPriority: 99 }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ @@ -894,7 +894,7 @@ describe("ParagraphStyle", () => { }); it("#unhideWhenUsed", () => { - const style = new ParagraphStyle({ id: "myStyleId", unhideWhenUsed: true }); + const style = new StyleForParagraph({ id: "myStyleId", unhideWhenUsed: true }); const tree = new Formatter().format(style); expect(tree).to.deep.equal({ "w:style": [ diff --git a/src/file/styles/style/paragraph-style.ts b/src/file/styles/style/paragraph-style.ts index 635e7b7e2f..25c035e514 100644 --- a/src/file/styles/style/paragraph-style.ts +++ b/src/file/styles/style/paragraph-style.ts @@ -21,7 +21,7 @@ export interface IParagraphStyleOptions extends IBaseParagraphStyleOptions { readonly name?: string; } -export class ParagraphStyle extends Style { +export class StyleForParagraph extends Style { private readonly paragraphProperties: ParagraphProperties; private readonly runProperties: RunProperties; diff --git a/src/file/styles/styles.ts b/src/file/styles/styles.ts index 45efe9f784..1906b45ba0 100644 --- a/src/file/styles/styles.ts +++ b/src/file/styles/styles.ts @@ -1,15 +1,16 @@ +import { IDefaultStylesOptions } from "file/styles/factory"; import { BaseXmlComponent, ImportedXmlComponent, XmlComponent } from "file/xml-components"; - -import { CharacterStyle, ParagraphStyle } from "./style"; +import { StyleForCharacter, StyleForParagraph } from "./style"; import { ICharacterStyleOptions } from "./style/character-style"; import { IParagraphStyleOptions } from "./style/paragraph-style"; export * from "./border"; export interface IStylesOptions { + readonly default?: IDefaultStylesOptions; readonly initialStyles?: BaseXmlComponent; readonly paragraphStyles?: IParagraphStyleOptions[]; readonly characterStyles?: ICharacterStyleOptions[]; - readonly importedStyles?: (XmlComponent | ParagraphStyle | CharacterStyle | ImportedXmlComponent)[]; + readonly importedStyles?: (XmlComponent | StyleForParagraph | StyleForCharacter | ImportedXmlComponent)[]; } export class Styles extends XmlComponent { @@ -28,13 +29,13 @@ export class Styles extends XmlComponent { if (options.paragraphStyles) { for (const style of options.paragraphStyles) { - this.root.push(new ParagraphStyle(style)); + this.root.push(new StyleForParagraph(style)); } } if (options.characterStyles) { for (const style of options.characterStyles) { - this.root.push(new CharacterStyle(style)); + this.root.push(new StyleForCharacter(style)); } } } diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index 6fbd748cf5..e598888f83 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -1,9 +1,9 @@ // http://officeopenxml.com/WPtableGrid.php +import { IViewWrapper } from "file/document-wrapper"; import { Paragraph } from "file/paragraph"; import { BorderStyle } from "file/styles"; import { IXmlableObject, XmlComponent } from "file/xml-components"; -import { File } from "../../file"; import { ITableShadingAttributesProperties } from "../shading"; import { Table } from "../table"; import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins"; @@ -115,7 +115,7 @@ export class TableCell extends XmlComponent { } } - public prepForXml(file?: File): IXmlableObject | undefined { + public prepForXml(file?: IViewWrapper): IXmlableObject | undefined { // Cells must end with a paragraph if (!(this.root[this.root.length - 1] instanceof Paragraph)) { this.root.push(new Paragraph({})); diff --git a/src/file/table/table-properties/table-borders.spec.ts b/src/file/table/table-properties/table-borders.spec.ts index 679192f120..4a3212feb1 100644 --- a/src/file/table/table-properties/table-borders.spec.ts +++ b/src/file/table/table-properties/table-borders.spec.ts @@ -546,5 +546,77 @@ describe("TableBorders", () => { }); }); }); + + describe("TableBorders.NONE convenience object", () => { + it("should add no borders", () => { + const tableBorders = new TableBorders(TableBorders.NONE); + const tree = new Formatter().format(tableBorders); + + expect(tree).to.deep.equal({ + "w:tblBorders": [ + { + "w:top": { + _attr: { + "w:color": "auto", + "w:space": 0, + "w:sz": 0, + "w:val": "none", + }, + }, + }, + { + "w:left": { + _attr: { + "w:color": "auto", + "w:space": 0, + "w:sz": 0, + "w:val": "none", + }, + }, + }, + { + "w:bottom": { + _attr: { + "w:color": "auto", + "w:space": 0, + "w:sz": 0, + "w:val": "none", + }, + }, + }, + { + "w:right": { + _attr: { + "w:color": "auto", + "w:space": 0, + "w:sz": 0, + "w:val": "none", + }, + }, + }, + { + "w:insideH": { + _attr: { + "w:color": "auto", + "w:space": 0, + "w:sz": 0, + "w:val": "none", + }, + }, + }, + { + "w:insideV": { + _attr: { + "w:color": "auto", + "w:space": 0, + "w:sz": 0, + "w:val": "none", + }, + }, + }, + ], + }); + }); + }); }); }); diff --git a/src/file/table/table-properties/table-borders.ts b/src/file/table/table-properties/table-borders.ts index 7c7ac96ca9..8de354fc36 100644 --- a/src/file/table/table-properties/table-borders.ts +++ b/src/file/table/table-properties/table-borders.ts @@ -36,6 +36,39 @@ export interface ITableBordersOptions { } export class TableBorders extends XmlComponent { + public static readonly NONE = { + top: { + style: BorderStyle.NONE, + size: 0, + color: "auto", + }, + bottom: { + style: BorderStyle.NONE, + size: 0, + color: "auto", + }, + left: { + style: BorderStyle.NONE, + size: 0, + color: "auto", + }, + right: { + style: BorderStyle.NONE, + size: 0, + color: "auto", + }, + insideHorizontal: { + style: BorderStyle.NONE, + size: 0, + color: "auto", + }, + insideVertical: { + style: BorderStyle.NONE, + size: 0, + color: "auto", + }, + }; + constructor(options: ITableBordersOptions) { super("w:tblBorders"); diff --git a/src/file/table/table-row/table-row-properties.ts b/src/file/table/table-row/table-row-properties.ts index c9b60c13f4..8e4d518e6f 100644 --- a/src/file/table/table-row/table-row-properties.ts +++ b/src/file/table/table-row/table-row-properties.ts @@ -1,6 +1,8 @@ -import { HeightRule, TableRowHeight } from "file/table/table-row/table-row-height"; +// http://officeopenxml.com/WPtableRowProperties.php import { IgnoreIfEmptyXmlComponent, XmlAttributeComponent, XmlComponent } from "file/xml-components"; +import { HeightRule, TableRowHeight } from "./table-row-height"; + export class TableRowProperties extends IgnoreIfEmptyXmlComponent { constructor() { super("w:trPr"); diff --git a/src/file/track-revision/index.ts b/src/file/track-revision/index.ts new file mode 100644 index 0000000000..eb2465d8fe --- /dev/null +++ b/src/file/track-revision/index.ts @@ -0,0 +1,2 @@ +export * from "./track-revision-components/inserted-text-run"; +export * from "./track-revision-components/deleted-text-run"; diff --git a/src/file/track-revision/track-revision-components/deleted-page-number.spec.ts b/src/file/track-revision/track-revision-components/deleted-page-number.spec.ts new file mode 100644 index 0000000000..5e0238da96 --- /dev/null +++ b/src/file/track-revision/track-revision-components/deleted-page-number.spec.ts @@ -0,0 +1,30 @@ +import { expect } from "chai"; +import { Formatter } from "export/formatter"; +import { DeletedNumberOfPages, DeletedNumberOfPagesSection, DeletedPage } from "./deleted-page-number"; + +describe("Deleted Page", () => { + describe("#constructor()", () => { + it("uses the font name for both ascii and hAnsi", () => { + const tree = new Formatter().format(new DeletedPage()); + expect(tree).to.deep.equal({ "w:delInstrText": [{ _attr: { "xml:space": "preserve" } }, "PAGE"] }); + }); + }); +}); + +describe("Delted NumberOfPages", () => { + describe("#constructor()", () => { + it("uses the font name for both ascii and hAnsi", () => { + const tree = new Formatter().format(new DeletedNumberOfPages()); + expect(tree).to.deep.equal({ "w:delInstrText": [{ _attr: { "xml:space": "preserve" } }, "NUMPAGES"] }); + }); + }); +}); + +describe("Deleted NumberOfPagesSection", () => { + describe("#constructor()", () => { + it("uses the font name for both ascii and hAnsi", () => { + const tree = new Formatter().format(new DeletedNumberOfPagesSection()); + expect(tree).to.deep.equal({ "w:delInstrText": [{ _attr: { "xml:space": "preserve" } }, "SECTIONPAGES"] }); + }); + }); +}); diff --git a/src/file/track-revision/track-revision-components/deleted-page-number.ts b/src/file/track-revision/track-revision-components/deleted-page-number.ts new file mode 100644 index 0000000000..6ce6266f13 --- /dev/null +++ b/src/file/track-revision/track-revision-components/deleted-page-number.ts @@ -0,0 +1,30 @@ +import { SpaceType } from "file/space-type"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class TextAttributes extends XmlAttributeComponent<{ readonly space: SpaceType }> { + protected readonly xmlKeys = { space: "xml:space" }; +} + +export class DeletedPage extends XmlComponent { + constructor() { + super("w:delInstrText"); + this.root.push(new TextAttributes({ space: SpaceType.PRESERVE })); + this.root.push("PAGE"); + } +} + +export class DeletedNumberOfPages extends XmlComponent { + constructor() { + super("w:delInstrText"); + this.root.push(new TextAttributes({ space: SpaceType.PRESERVE })); + this.root.push("NUMPAGES"); + } +} + +export class DeletedNumberOfPagesSection extends XmlComponent { + constructor() { + super("w:delInstrText"); + this.root.push(new TextAttributes({ space: SpaceType.PRESERVE })); + this.root.push("SECTIONPAGES"); + } +} diff --git a/src/file/track-revision/track-revision-components/deleted-text-run.spec.ts b/src/file/track-revision/track-revision-components/deleted-text-run.spec.ts new file mode 100644 index 0000000000..6bd11c24d7 --- /dev/null +++ b/src/file/track-revision/track-revision-components/deleted-text-run.spec.ts @@ -0,0 +1,372 @@ +import { expect } from "chai"; +import { Formatter } from "export/formatter"; +import { FootnoteReferenceRun, PageNumber } from "../../index"; +import { DeletedTextRun } from "./deleted-text-run"; + +describe("DeletedTextRun", () => { + describe("#constructor", () => { + it("should create a deleted text run", () => { + const deletedTextRun = new DeletedTextRun({ text: "some text", id: 0, date: "123", author: "Author" }); + const tree = new Formatter().format(deletedTextRun); + expect(tree).to.deep.equal({ + "w:del": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:delText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "some text", + ], + }, + ], + }, + ], + }); + }); + }); + + describe("#constructor with formatting", () => { + it("should create a deleted text run", () => { + const deletedTextRun = new DeletedTextRun({ text: "some text", bold: true, id: 0, date: "123", author: "Author" }); + const tree = new Formatter().format(deletedTextRun); + expect(tree).to.deep.equal({ + "w:del": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:rPr": [ + { + "w:b": { + _attr: { + "w:val": true, + }, + }, + }, + { + "w:bCs": { + _attr: { + "w:val": true, + }, + }, + }, + ], + }, + { + "w:delText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "some text", + ], + }, + ], + }, + ], + }); + }); + }); + + describe("#break()", () => { + it("should add a break", () => { + const deletedTextRun = new DeletedTextRun({ + break: 1, + children: ["some text"], + id: 0, + date: "123", + author: "Author", + }); + const tree = new Formatter().format(deletedTextRun); + expect(tree).to.deep.equal({ + "w:del": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:br": {}, + }, + { + "w:delText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "some text", + ], + }, + ], + }, + ], + }); + }); + }); + + describe("page numbering", () => { + it("should be able to delete the total pages", () => { + const deletedTextRun = new DeletedTextRun({ + children: [" to ", PageNumber.TOTAL_PAGES], + id: 0, + date: "123", + author: "Author", + }); + const tree = new Formatter().format(deletedTextRun); + expect(tree).to.deep.equal({ + "w:del": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:delText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + " to ", + ], + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "begin", + }, + }, + }, + { + "w:delInstrText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "NUMPAGES", + ], + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "separate", + }, + }, + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "end", + }, + }, + }, + ], + }, + ], + }); + }); + + it("should be able to delete the total pages in section", () => { + const deletedTextRun = new DeletedTextRun({ + children: [" to ", PageNumber.TOTAL_PAGES_IN_SECTION], + id: 0, + date: "123", + author: "Author", + }); + const tree = new Formatter().format(deletedTextRun); + expect(tree).to.deep.equal({ + "w:del": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:delText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + " to ", + ], + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "begin", + }, + }, + }, + { + "w:delInstrText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "SECTIONPAGES", + ], + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "separate", + }, + }, + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "end", + }, + }, + }, + ], + }, + ], + }); + }); + + it("should be able to delete the current page", () => { + const deletedTextRun = new DeletedTextRun({ + children: [" to ", PageNumber.CURRENT], + id: 0, + date: "123", + author: "Author", + }); + const tree = new Formatter().format(deletedTextRun); + expect(tree).to.deep.equal({ + "w:del": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:delText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + " to ", + ], + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "begin", + }, + }, + }, + { + "w:delInstrText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "PAGE", + ], + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "separate", + }, + }, + }, + { + "w:fldChar": { + _attr: { + "w:fldCharType": "end", + }, + }, + }, + ], + }, + ], + }); + }); + }); + + describe("footnote references", () => { + it("should add a valid footnote reference", () => { + const deletedTextRun = new DeletedTextRun({ + children: ["some text", new FootnoteReferenceRun(1)], + id: 0, + date: "123", + author: "Author", + }); + const tree = new Formatter().format(deletedTextRun); + expect(tree).to.deep.equal({ + "w:del": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:delText": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "some text", + ], + }, + { + "w:r": [ + { "w:rPr": [{ "w:rStyle": { _attr: { "w:val": "FootnoteReference" } } }] }, + { "w:footnoteReference": { _attr: { "w:id": 1 } } }, + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/track-revision/track-revision-components/deleted-text-run.ts b/src/file/track-revision/track-revision-components/deleted-text-run.ts new file mode 100644 index 0000000000..7830d3d63c --- /dev/null +++ b/src/file/track-revision/track-revision-components/deleted-text-run.ts @@ -0,0 +1,76 @@ +import { XmlComponent } from "file/xml-components"; + +import { IRunOptions, RunProperties } from "../../index"; +import { Break } from "../../paragraph/run/break"; +import { Begin, End, Separate } from "../../paragraph/run/field"; +import { PageNumber } from "../../paragraph/run/run"; +import { ChangeAttributes, IChangedAttributesProperties } from "../track-revision"; +import { DeletedNumberOfPages, DeletedNumberOfPagesSection, DeletedPage } from "./deleted-page-number"; +import { DeletedText } from "./deleted-text"; + +interface IDeletedRunOptions extends IRunOptions, IChangedAttributesProperties {} + +export class DeletedTextRun extends XmlComponent { + protected readonly deletedTextRunWrapper: DeletedTextRunWrapper; + + constructor(options: IDeletedRunOptions) { + super("w:del"); + this.root.push( + new ChangeAttributes({ + id: options.id, + author: options.author, + date: options.date, + }), + ); + this.deletedTextRunWrapper = new DeletedTextRunWrapper(options); + this.addChildElement(this.deletedTextRunWrapper); + } +} + +class DeletedTextRunWrapper extends XmlComponent { + constructor(options: IRunOptions) { + super("w:r"); + this.root.push(new RunProperties(options)); + + if (options.children) { + for (const child of options.children) { + if (typeof child === "string") { + switch (child) { + case PageNumber.CURRENT: + this.root.push(new Begin()); + this.root.push(new DeletedPage()); + this.root.push(new Separate()); + this.root.push(new End()); + break; + case PageNumber.TOTAL_PAGES: + this.root.push(new Begin()); + this.root.push(new DeletedNumberOfPages()); + this.root.push(new Separate()); + this.root.push(new End()); + break; + case PageNumber.TOTAL_PAGES_IN_SECTION: + this.root.push(new Begin()); + this.root.push(new DeletedNumberOfPagesSection()); + this.root.push(new Separate()); + this.root.push(new End()); + break; + default: + this.root.push(new DeletedText(child)); + break; + } + continue; + } + + this.root.push(child); + } + } else if (options.text) { + this.root.push(new DeletedText(options.text)); + } + + if (options.break) { + for (let i = 0; i < options.break; i++) { + this.root.splice(1, 0, new Break()); + } + } + } +} diff --git a/src/file/track-revision/track-revision-components/deleted-text.spec.ts b/src/file/track-revision/track-revision-components/deleted-text.spec.ts new file mode 100644 index 0000000000..d9c8de46bf --- /dev/null +++ b/src/file/track-revision/track-revision-components/deleted-text.spec.ts @@ -0,0 +1,15 @@ +import { expect } from "chai"; +import { Formatter } from "export/formatter"; +import { DeletedText } from "./deleted-text"; + +describe("Deleted Text", () => { + describe("#constructor", () => { + it("adds the passed in text to the component", () => { + const t = new DeletedText(" this is\n text"); + const f = new Formatter().format(t); + expect(f).to.deep.equal({ + "w:delText": [{ _attr: { "xml:space": "preserve" } }, " this is\n text"], + }); + }); + }); +}); diff --git a/src/file/track-revision/track-revision-components/deleted-text.ts b/src/file/track-revision/track-revision-components/deleted-text.ts new file mode 100644 index 0000000000..408b47304d --- /dev/null +++ b/src/file/track-revision/track-revision-components/deleted-text.ts @@ -0,0 +1,15 @@ +import { SpaceType } from "file/space-type"; +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +class TextAttributes extends XmlAttributeComponent<{ readonly space: SpaceType }> { + protected readonly xmlKeys = { space: "xml:space" }; +} + +export class DeletedText extends XmlComponent { + constructor(text: string) { + super("w:delText"); + this.root.push(new TextAttributes({ space: SpaceType.PRESERVE })); + + this.root.push(text); + } +} diff --git a/src/file/track-revision/track-revision-components/inserted-text-run.spec.ts b/src/file/track-revision/track-revision-components/inserted-text-run.spec.ts new file mode 100644 index 0000000000..c23069fbba --- /dev/null +++ b/src/file/track-revision/track-revision-components/inserted-text-run.spec.ts @@ -0,0 +1,37 @@ +import { expect } from "chai"; +import { Formatter } from "export/formatter"; +import { InsertedTextRun } from "./inserted-text-run"; + +describe("InsertedTextRun", () => { + describe("#constructor", () => { + it("should create a inserted text run", () => { + const insertedTextRun = new InsertedTextRun({ text: "some text", id: 0, date: "123", author: "Author" }); + const tree = new Formatter().format(insertedTextRun); + expect(tree).to.deep.equal({ + "w:ins": [ + { + _attr: { + "w:author": "Author", + "w:date": "123", + "w:id": 0, + }, + }, + { + "w:r": [ + { + "w:t": [ + { + _attr: { + "xml:space": "preserve", + }, + }, + "some text", + ], + }, + ], + }, + ], + }); + }); + }); +}); diff --git a/src/file/track-revision/track-revision-components/inserted-text-run.ts b/src/file/track-revision/track-revision-components/inserted-text-run.ts new file mode 100644 index 0000000000..3d39e92c1c --- /dev/null +++ b/src/file/track-revision/track-revision-components/inserted-text-run.ts @@ -0,0 +1,20 @@ +import { XmlComponent } from "file/xml-components"; + +import { IRunOptions, TextRun } from "../../index"; +import { ChangeAttributes, IChangedAttributesProperties } from "../track-revision"; + +interface IInsertedRunOptions extends IChangedAttributesProperties, IRunOptions {} + +export class InsertedTextRun extends XmlComponent { + constructor(options: IInsertedRunOptions) { + super("w:ins"); + this.root.push( + new ChangeAttributes({ + id: options.id, + author: options.author, + date: options.date, + }), + ); + this.addChildElement(new TextRun(options as IRunOptions)); + } +} diff --git a/src/file/track-revision/track-revision.ts b/src/file/track-revision/track-revision.ts new file mode 100644 index 0000000000..4318e9a468 --- /dev/null +++ b/src/file/track-revision/track-revision.ts @@ -0,0 +1,15 @@ +import { XmlAttributeComponent } from "file/xml-components"; + +export interface IChangedAttributesProperties { + readonly id: number; + readonly author: string; + readonly date: string; +} + +export class ChangeAttributes extends XmlAttributeComponent { + protected readonly xmlKeys = { + id: "w:id", + author: "w:author", + date: "w:date", + }; +} diff --git a/src/file/xml-components/base.ts b/src/file/xml-components/base.ts index 782dfdba12..4bb37c8ad9 100644 --- a/src/file/xml-components/base.ts +++ b/src/file/xml-components/base.ts @@ -1,4 +1,4 @@ -import { File } from "../file"; +import { IViewWrapper } from "../document-wrapper"; import { IXmlableObject } from "./xmlable-object"; export abstract class BaseXmlComponent { @@ -10,7 +10,7 @@ export abstract class BaseXmlComponent { this.rootKey = rootKey; } - public abstract prepForXml(file?: File): IXmlableObject | undefined; + public abstract prepForXml(file?: IViewWrapper): IXmlableObject | undefined; public get IsDeleted(): boolean { return this.deleted; diff --git a/src/file/xml-components/xml-component.ts b/src/file/xml-components/xml-component.ts index b06f73b930..84fd5f98e4 100644 --- a/src/file/xml-components/xml-component.ts +++ b/src/file/xml-components/xml-component.ts @@ -1,4 +1,4 @@ -import { File } from "../file"; +import { IViewWrapper } from "../document-wrapper"; import { BaseXmlComponent } from "./base"; import { IXmlableObject } from "./xmlable-object"; @@ -13,7 +13,7 @@ export abstract class XmlComponent extends BaseXmlComponent { this.root = new Array(); } - public prepForXml(file?: File): IXmlableObject | undefined { + public prepForXml(file?: IViewWrapper): IXmlableObject | undefined { const children = this.root .filter((c) => { if (c instanceof BaseXmlComponent) { diff --git a/src/import-dotx/import-dotx.ts b/src/import-dotx/import-dotx.ts index d8d5d1190c..4c77ea75be 100644 --- a/src/import-dotx/import-dotx.ts +++ b/src/import-dotx/import-dotx.ts @@ -46,7 +46,9 @@ export interface IDocumentTemplate { } export class ImportDotx { - public async extract(data: Buffer): Promise { + public async extract( + data: Buffer | string | number[] | Uint8Array | ArrayBuffer | Blob | NodeJS.ReadableStream, + ): Promise { const zipContent = await JSZip.loadAsync(data); const documentContent = await zipContent.files["word/document.xml"].async("text"); diff --git a/src/index.ts b/src/index.ts index 1e35c87d30..a319e4dcfe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,3 +4,4 @@ export { File as Document } from "./file"; export * from "./file"; export * from "./export"; export * from "./import-dotx"; +export * from "./convenience-functions"; diff --git a/tslint.json b/tslint.json index d6eb606eba..cb979f920a 100644 --- a/tslint.json +++ b/tslint.json @@ -21,6 +21,8 @@ "no-duplicate-imports": true, "unnecessary-constructor": true, "file-name-casing": [true, "kebab-case"], + "interface-name": [true, "always-prefix"], + "ordered-imports": true, // Functional Programming Rules "no-parameter-reassignment": true, "readonly-keyword": true,