diff --git a/demo/54-track-revisions.ts b/demo/54-track-revisions.ts
new file mode 100644
index 0000000000..01d96082b8
--- /dev/null
+++ b/demo/54-track-revisions.ts
@@ -0,0 +1,132 @@
+// Track Revisions aka. "Track Changes"
+// Import from 'docx' rather than '../build' if you install from npm
+import * as fs from "fs";
+import { Document, Packer, Paragraph, TextRun, ShadingType, DeletedTextRun, InsertedTextRun, Footer, PageNumber, AlignmentType, FootnoteReferenceRun } 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",
+ })
+ ]
+ }),
+ ],
+});
+
+doc.Settings.addTrackRevisions()
+
+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({
+ 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",
+ }).break(),
+ 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/docs/_sidebar.md b/docs/_sidebar.md
index a63970dff9..5d2ba31551 100644
--- a/docs/_sidebar.md
+++ b/docs/_sidebar.md
@@ -20,6 +20,7 @@
* [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)
* Styling
* [Styling with JS](usage/styling-with-js.md)
* [Styling with XML](usage/styling-with-xml.md)
@@ -28,4 +29,3 @@
* [Packers](usage/packers.md)
* [Contribution Guidelines](contribution-guidelines.md)
-
diff --git a/docs/usage/change-tracking.md b/docs/usage/change-tracking.md
new file mode 100644
index 0000000000..6f81e4d0d7
--- /dev/null
+++ b/docs/usage/change-tracking.md
@@ -0,0 +1,58 @@
+# 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({});
+doc.Settings.addTrackRevisions()
+```
\ No newline at end of file
diff --git a/src/file/track-revision/index.ts b/src/file/track-revision/index.ts
index 20bb87a229..eb2465d8fe 100644
--- a/src/file/track-revision/index.ts
+++ b/src/file/track-revision/index.ts
@@ -1 +1,2 @@
-export * from "./track-revision";
+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..7931e50406
--- /dev/null
+++ b/src/file/track-revision/track-revision-components/deleted-text-run.spec.ts
@@ -0,0 +1,371 @@
+import { expect } from "chai";
+import { Formatter } from "export/formatter";
+import { DeletedTextRun } from "./deleted-text-run";
+import { FootnoteReferenceRun, PageNumber } from "../../index";
+
+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({
+ children: ["some text"],
+ id: 0,
+ date: "123",
+ author: "Author",
+ }).break();
+ 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..2342f70104
--- /dev/null
+++ b/src/file/track-revision/track-revision-components/deleted-text-run.ts
@@ -0,0 +1,83 @@
+import { IChangedAttributesProperties, ChangeAttributes } from "../track-revision";
+import { XmlComponent } from "file/xml-components";
+import { IRunOptions, RunProperties, IRunPropertiesOptions, FootnoteReferenceRun } from "../../index";
+
+import { Break } from "../../paragraph/run/break";
+import { Begin, Separate, End } from "../../paragraph/run/field";
+import { PageNumber } from "../../paragraph/run/run";
+
+import { DeletedPage, DeletedNumberOfPages, DeletedNumberOfPagesSection } from "./deleted-page-number";
+import { DeletedText } from "./deleted-text";
+
+interface IDeletedRunOptions extends IRunPropertiesOptions, IChangedAttributesProperties {
+ readonly children?: (Begin | Separate | End | PageNumber | FootnoteReferenceRun | string)[];
+ readonly text?: string;
+}
+
+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 as IRunOptions);
+ this.addChildElement(this.deletedTextRunWrapper);
+ }
+
+ public break(): DeletedTextRun {
+ this.deletedTextRunWrapper.break();
+ return this;
+ }
+}
+
+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));
+ }
+ }
+
+ public break(): void {
+ 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..49bd7f53ae
--- /dev/null
+++ b/src/file/track-revision/track-revision-components/inserted-text-run.ts
@@ -0,0 +1,19 @@
+import { IChangedAttributesProperties, ChangeAttributes } from "../track-revision";
+import { XmlComponent } from "file/xml-components";
+import { TextRun, IRunOptions } from "../../index";
+
+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.spec.ts b/src/file/track-revision/track-revision.spec.ts
deleted file mode 100644
index 30a684fe9c..0000000000
--- a/src/file/track-revision/track-revision.spec.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { expect } from "chai";
-
-import { Formatter } from "export/formatter";
-
-import { InsertedTextRun, DeletedTextRun } from "./track-revision";
-import { TextRun } from "../paragraph";
-
-describe("InsertedTestRun", () => {
- describe("#constructor", () => {
- it("should create a inserted text run", () => {
- const textRun = new TextRun({
- text: "some text",
- });
- const insertedTextRun = new InsertedTextRun({ child: textRun, 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",
- ],
- },
- ],
- },
- ],
- });
- });
- });
-});
-describe("DeletedTestRun", () => {
- describe("#constructor", () => {
- it("should create a deleted text run", () => {
- const insertedParagraph = new DeletedTextRun({ text: "some text", id: 0, date: "123", author: "Author" });
- const tree = new Formatter().format(insertedParagraph);
- 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",
- ],
- },
- ],
- },
- ],
- });
- });
- });
-});
diff --git a/src/file/track-revision/track-revision.ts b/src/file/track-revision/track-revision.ts
index 052c7e29ea..4318e9a468 100644
--- a/src/file/track-revision/track-revision.ts
+++ b/src/file/track-revision/track-revision.ts
@@ -1,72 +1,15 @@
-import { SpaceType } from "file/space-type";
-import { XmlComponent, XmlAttributeComponent } from "file/xml-components";
-import { TextRun } from "../index";
+import { XmlAttributeComponent } from "file/xml-components";
-export interface ITrackRevisionAttributesProperties {
+export interface IChangedAttributesProperties {
readonly id: number;
readonly author: string;
readonly date: string;
}
-export class TrackRevisionAttributes extends XmlAttributeComponent {
+export class ChangeAttributes extends XmlAttributeComponent {
protected readonly xmlKeys = {
id: "w:id",
author: "w:author",
date: "w:date",
};
}
-
-export interface IInsertedTextRunOptions extends ITrackRevisionAttributesProperties {
- readonly child: TextRun;
-}
-
-export interface IDeletedTextRunOptions extends ITrackRevisionAttributesProperties {
- readonly text: string;
-}
-
-export class InsertedTextRun extends XmlComponent {
- constructor(options: IInsertedTextRunOptions) {
- super("w:ins");
- this.root.push(
- new TrackRevisionAttributes({
- id: options.id,
- author: options.author,
- date: options.date,
- }),
- );
- this.addChildElement(options.child);
- }
-}
-
-export class DeletedTextRunWrapper extends XmlComponent {
- constructor(text: string) {
- super("w:r");
- this.root.push(new DeletedText(text));
- }
-}
-
-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);
- }
-}
-
-export class DeletedTextRun extends XmlComponent {
- constructor(options: IDeletedTextRunOptions) {
- super("w:del");
- this.root.push(
- new TrackRevisionAttributes({
- id: options.id,
- author: options.author,
- date: options.date,
- }),
- );
- this.addChildElement(new DeletedTextRunWrapper(options.text));
- }
-}