From 32972dcbc5fa3617212a4d53062b072a4c88e5c3 Mon Sep 17 00:00:00 2001 From: Tim Evans Date: Thu, 2 Aug 2018 11:42:53 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20annotations=20to=20GDocs=20pa?= =?UTF-8?q?ste=20buffers=20(#65)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/@atjson/document/src/annotation.ts | 7 +- .../document/src/annotations/unknown.ts | 4 +- packages/@atjson/document/src/collection.ts | 8 ++ packages/@atjson/document/src/index.ts | 7 +- packages/@atjson/document/test/query-test.ts | 57 ++++++-- packages/@atjson/hir/src/hir-node.ts | 4 +- packages/@atjson/renderer-hir/src/index.ts | 8 +- .../renderer-hir/test/renderer-hir-test.ts | 4 +- .../src/annotations/bold.ts | 6 + .../src/annotations/heading.ts | 9 ++ .../src/annotations/horizontal-rule.ts | 6 + .../src/annotations/index.ts | 10 ++ .../src/annotations/italic.ts | 6 + .../src/annotations/link.ts | 10 ++ .../src/annotations/list-item.ts | 10 ++ .../src/annotations/list.ts | 12 ++ .../src/annotations/strikethrough.ts | 6 + .../src/annotations/underline.ts | 6 + .../src/annotations/vertical-adjust.ts | 9 ++ .../source-gdocs-paste/src/gdocs-parser.ts | 14 +- .../source-gdocs-paste/src/horizontal-rule.ts | 7 +- .../@atjson/source-gdocs-paste/src/index.ts | 41 +++--- .../source-gdocs-paste/src/link-styles.ts | 11 +- .../source-gdocs-paste/src/list-styles.ts | 15 ++- .../src/paragraph-styles.ts | 7 +- .../source-gdocs-paste/src/text-styles.ts | 10 +- ...paste-test.ts.snap => source-test.ts.snap} | 0 ...rce-gdocs-paste-test.ts => source-test.ts} | 123 +++++++++--------- ...-translator-test.ts => translator-test.ts} | 8 +- 29 files changed, 274 insertions(+), 151 deletions(-) create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/bold.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/heading.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/horizontal-rule.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/index.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/italic.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/link.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/list-item.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/list.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/strikethrough.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/underline.ts create mode 100644 packages/@atjson/source-gdocs-paste/src/annotations/vertical-adjust.ts rename packages/@atjson/source-gdocs-paste/test/__snapshots__/{atjson-source-gdocs-paste-test.ts.snap => source-test.ts.snap} (100%) rename packages/@atjson/source-gdocs-paste/test/{atjson-source-gdocs-paste-test.ts => source-test.ts} (65%) rename packages/@atjson/source-gdocs-paste/test/{atjson-source-gdocs-translator-test.ts => translator-test.ts} (88%) diff --git a/packages/@atjson/document/src/annotation.ts b/packages/@atjson/document/src/annotation.ts index 8c3143885..41f8a0c9a 100644 --- a/packages/@atjson/document/src/annotation.ts +++ b/packages/@atjson/document/src/annotation.ts @@ -4,13 +4,12 @@ import Document from './index'; import JSON from './json'; export type ConcreteAnnotation = T; -export type AnyAnnotation = ConcreteAnnotation; export interface AnnotationConstructor { vendorPrefix: string; type: string; subdocuments: { [key: string]: typeof Document }; - new(attributes: { id: string, start: number, end: number, attributes: Attributes }): AnyAnnotation; - hydrate(attrs: { id: string, start: number, end: number, attributes: JSON }): AnyAnnotation; + new(attributes: { id: string, start: number, end: number, attributes: Attributes }): Annotation; + hydrate(attrs: { id: string, start: number, end: number, attributes: JSON }): Annotation; } export default abstract class Annotation { @@ -170,7 +169,7 @@ export default abstract class Annotation { type: `-${vendorPrefix}-${this.type}`, start: this.start, end: this.end, - attributes: toJSON(vendorPrefix, this.attributes) as any + attributes: toJSON(vendorPrefix, this.attributes) }; } } diff --git a/packages/@atjson/document/src/annotations/unknown.ts b/packages/@atjson/document/src/annotations/unknown.ts index 042cb3e87..fef1877c1 100644 --- a/packages/@atjson/document/src/annotations/unknown.ts +++ b/packages/@atjson/document/src/annotations/unknown.ts @@ -1,12 +1,12 @@ import Annotation from '../annotation'; -import { Attributes } from '../attributes'; +import JSON from '../json'; export default class UnknownAnnotation extends Annotation { static vendorPrefix = 'atjson'; static type = 'unknown'; attributes!: { type: string; - attributes: Attributes; + attributes: JSON; }; get rank() { diff --git a/packages/@atjson/document/src/collection.ts b/packages/@atjson/document/src/collection.ts index 136636f9b..7216393b5 100644 --- a/packages/@atjson/document/src/collection.ts +++ b/packages/@atjson/document/src/collection.ts @@ -26,6 +26,14 @@ class Collection { } } + get length() { + return this.annotations.length; + } + + map(mapper: (annotation: Annotation) => T) { + return this.annotations.map(mapper); + } + where(filter: { [key: string]: any; } | ((annotation: Annotation) => boolean)) { if (filter instanceof Function) { return new AnnotationCollection(this.document, this.annotations.filter(filter)); diff --git a/packages/@atjson/document/src/index.ts b/packages/@atjson/document/src/index.ts index 21fdacbb9..99bfd76f2 100644 --- a/packages/@atjson/document/src/index.ts +++ b/packages/@atjson/document/src/index.ts @@ -1,4 +1,4 @@ -import Annotation, { AnnotationConstructor, AnyAnnotation } from './annotation'; +import Annotation, { AnnotationConstructor } from './annotation'; import { Block, Inline, Object, Parse, Unknown } from './annotations'; import { Attribute, Attributes } from './attributes'; import Change, { AdjacentBoundaryBehaviour, Deletion, Insertion } from './change'; @@ -11,7 +11,7 @@ export interface AnnotationJSON { type: string; start: number; end: number; - attributes: Attributes; + attributes: JSON; } export { @@ -19,7 +19,6 @@ export { Annotation, AnnotationCollection, AnnotationConstructor, - AnyAnnotation, Attribute, Attributes, Block as BlockAnnotation, @@ -36,7 +35,7 @@ export { export default class Document { static contentType: string; - static schema: AnyAnnotation[] = []; + static schema: AnnotationConstructor[] = []; content: string; readonly contentType: string; diff --git a/packages/@atjson/document/test/query-test.ts b/packages/@atjson/document/test/query-test.ts index 1ddf55b42..f3401f020 100644 --- a/packages/@atjson/document/test/query-test.ts +++ b/packages/@atjson/document/test/query-test.ts @@ -1,7 +1,38 @@ import { AnnotationJSON, Attributes } from '../src'; -import TestSource, { Anchor } from './test-source'; +import TestSource, { Anchor, Code, Preformatted, Locale } from './test-source'; describe('Document.where', () => { + it('length on collections', () => { + let doc = new TestSource({ + content: 'Hello', + annotations: [{ + id: '1', + type: '-test-bold', + start: 0, + end: 5, + attributes: {} + }] + }); + + expect(doc.where({ type: '-test-bold' }).length).toEqual(1); + expect(doc.where({ type: '-test-italic' }).length).toEqual(0); + }); + + it('map over collections', () => { + let doc = new TestSource({ + content: 'Hello', + annotations: [{ + id: '1', + type: '-test-bold', + start: 0, + end: 5, + attributes: {} + }] + }); + + expect(doc.where({ type: '-test-bold' }).map(a => a.type)).toEqual(['bold']); + }); + it('set', () => { let doc = new TestSource({ content: 'Hello', @@ -179,24 +210,24 @@ describe('Document.where', () => { }] }); - doc.where({ type: '-test-code' }).update(annotation => { - let annotations = doc.replaceAnnotation(annotation, { - id: annotation.id + '-1', + doc.where({ type: '-test-code' }).update((code: Code) => { + let annotations = doc.replaceAnnotation(code, { + id: code.id + '-1', type: '-test-pre', - start: annotation.start, - end: annotation.end, - attributes: annotation.toJSON().attributes as Attributes + start: code.start, + end: code.end, + attributes: code.toJSON().attributes }, { - id: annotation.id + '-2', + id: code.id + '-2', type: '-test-code', - start: annotation.start, - end: annotation.end, + start: code.start, + end: code.end, attributes: {} }); return { add: annotations, - remove: [annotation] + remove: [code] }; }).unset('attributes.-test-class'); @@ -290,7 +321,7 @@ describe('Document.where', () => { }] }]); - preAndCode.update(({ code, pre }) => { + preAndCode.update(({ code, pre }: { code: Code, pre: Preformatted[] }) => { doc.removeAnnotation(pre[0]); code.attributes.textStyle = 'pre'; doc.deleteText(2, 4); @@ -412,7 +443,7 @@ describe('Document.where', () => { }] }]); - threeWayJoin.update(({ code, preElements, locale }) => { + threeWayJoin.update(({ code, preElements, locale }: { code: Code, preElements: Preformatted[], locale: Locale[] }) => { doc.insertText(0, 'Hello!\n'); let newCode = code.clone(); diff --git a/packages/@atjson/hir/src/hir-node.ts b/packages/@atjson/hir/src/hir-node.ts index d46dc5207..551f05862 100644 --- a/packages/@atjson/hir/src/hir-node.ts +++ b/packages/@atjson/hir/src/hir-node.ts @@ -1,4 +1,4 @@ -import Document, { Annotation, AnyAnnotation, Attribute, JSON, ParseAnnotation } from '@atjson/document'; +import Document, { Annotation, Attribute, JSON, ParseAnnotation } from '@atjson/document'; import { Root, Text } from './annotations'; import HIR from './hir'; @@ -66,7 +66,7 @@ function toJSON(attribute: HIRAttribute): JSON { export default class HIRNode { - annotation: AnyAnnotation; + annotation: Annotation; id: string; attributes: HIRAttributes; start: number; diff --git a/packages/@atjson/renderer-hir/src/index.ts b/packages/@atjson/renderer-hir/src/index.ts index acd89941f..29527d1a9 100644 --- a/packages/@atjson/renderer-hir/src/index.ts +++ b/packages/@atjson/renderer-hir/src/index.ts @@ -1,4 +1,4 @@ -import Document, { AnyAnnotation } from '@atjson/document'; +import Document, { Annotation } from '@atjson/document'; import { HIR, HIRNode, TextAnnotation } from '@atjson/hir'; interface Mapping { @@ -34,8 +34,8 @@ function flatten(array: any[]): any[] { return flattenedArray; } -function compile(renderer: Renderer, node: HIRNode, parent?: AnyAnnotation, index?: number): any { - let annotation: AnyAnnotation = node.annotation.clone(); +function compile(renderer: Renderer, node: HIRNode, parent?: Annotation, index?: number): any { + let annotation: Annotation = node.annotation.clone(); let children = node.children(); // Add metadata to annotations for formats that require context @@ -85,7 +85,7 @@ function compile(renderer: Renderer, node: HIRNode, parent?: AnyAnnotation, inde export default class Renderer { - *renderAnnotation(annotation: AnyAnnotation): IterableIterator { + *renderAnnotation(annotation: Annotation): IterableIterator { let generator = (this as any)[annotation.type]; if (generator) { return yield* generator.call(this, annotation); diff --git a/packages/@atjson/renderer-hir/test/renderer-hir-test.ts b/packages/@atjson/renderer-hir/test/renderer-hir-test.ts index 6a50c33dd..a522c94bd 100644 --- a/packages/@atjson/renderer-hir/test/renderer-hir-test.ts +++ b/packages/@atjson/renderer-hir/test/renderer-hir-test.ts @@ -1,4 +1,4 @@ -import Document, { AnyAnnotation, InlineAnnotation } from '@atjson/document'; +import Document, { Annotation, InlineAnnotation } from '@atjson/document'; import { HIR, HIRNode } from '@atjson/hir'; import HIRRenderer, { escapeHTML } from '../src/index'; @@ -66,7 +66,7 @@ describe('@atjson/renderer-hir', () => { ]; class ConcreteRenderer extends HIRRenderer { - *renderAnnotation(annotation: AnyAnnotation): IterableIterator { + *renderAnnotation(annotation: Annotation): IterableIterator { let expected = callStack.shift(); expect(annotation.toJSON()).toEqual(expected.annotation.toJSON()); diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/bold.ts b/packages/@atjson/source-gdocs-paste/src/annotations/bold.ts new file mode 100644 index 000000000..45fcc1c83 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/bold.ts @@ -0,0 +1,6 @@ +import { InlineAnnotation } from '@atjson/document'; + +export default class Bold extends InlineAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'ts_bd'; // Text style: bold +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/heading.ts b/packages/@atjson/source-gdocs-paste/src/annotations/heading.ts new file mode 100644 index 000000000..e03ed510a --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/heading.ts @@ -0,0 +1,9 @@ +import { BlockAnnotation } from '@atjson/document'; + +export default class Heading extends BlockAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'ps_hd'; + attributes!: { + level: 1 | 2 | 3 | 4 | 5 | 6 | 100 | 101; + }; +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/horizontal-rule.ts b/packages/@atjson/source-gdocs-paste/src/annotations/horizontal-rule.ts new file mode 100644 index 000000000..081a0cdf6 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/horizontal-rule.ts @@ -0,0 +1,6 @@ +import { ObjectAnnotation } from '@atjson/document'; + +export default class HorizontalRule extends ObjectAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'horizontal_rule'; +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/index.ts b/packages/@atjson/source-gdocs-paste/src/annotations/index.ts new file mode 100644 index 000000000..9b1dea467 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/index.ts @@ -0,0 +1,10 @@ +export { default as Bold } from './bold'; +export { default as Heading } from './heading'; +export { default as HorizontalRule } from './horizontal-rule'; +export { default as Italic } from './italic'; +export { default as Link } from './link'; +export { default as ListItem } from './list-item'; +export { default as List } from './list'; +export { default as Strikethrough } from './strikethrough'; +export { default as Underline } from './underline'; +export { default as VerticalAdjust } from './vertical-adjust'; diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/italic.ts b/packages/@atjson/source-gdocs-paste/src/annotations/italic.ts new file mode 100644 index 000000000..65be41411 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/italic.ts @@ -0,0 +1,6 @@ +import { InlineAnnotation } from '@atjson/document'; + +export default class Italic extends InlineAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'ts_it'; // Text style: italic +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/link.ts b/packages/@atjson/source-gdocs-paste/src/annotations/link.ts new file mode 100644 index 000000000..e328a286b --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/link.ts @@ -0,0 +1,10 @@ +import { InlineAnnotation } from '@atjson/document'; + +export default class Link extends InlineAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'lnks_link'; + attributes!: { + ulnk_url: string; + lnk_type: number; + }; +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/list-item.ts b/packages/@atjson/source-gdocs-paste/src/annotations/list-item.ts new file mode 100644 index 000000000..b254d56b6 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/list-item.ts @@ -0,0 +1,10 @@ +import { BlockAnnotation } from '@atjson/document'; + +export default class ListItem extends BlockAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'list_item'; + attributes!: { + ls_id: string; + ls_nest: number; + }; +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/list.ts b/packages/@atjson/source-gdocs-paste/src/annotations/list.ts new file mode 100644 index 000000000..465d7a2f7 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/list.ts @@ -0,0 +1,12 @@ +import { BlockAnnotation } from '@atjson/document'; + +export default class List extends BlockAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'list'; + attributes!: { + ls_id: string; + ls_b_gs: string; + ls_b_gt: number; + ls_b_a: number; + }; +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/strikethrough.ts b/packages/@atjson/source-gdocs-paste/src/annotations/strikethrough.ts new file mode 100644 index 000000000..32bfd4290 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/strikethrough.ts @@ -0,0 +1,6 @@ +import { InlineAnnotation } from '@atjson/document'; + +export default class Strikethrough extends InlineAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'ts_st'; // Text style: strikethrough +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/underline.ts b/packages/@atjson/source-gdocs-paste/src/annotations/underline.ts new file mode 100644 index 000000000..aa524f2a5 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/underline.ts @@ -0,0 +1,6 @@ +import { InlineAnnotation } from '@atjson/document'; + +export default class Underline extends InlineAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'ts_un'; // Text style: underline +} diff --git a/packages/@atjson/source-gdocs-paste/src/annotations/vertical-adjust.ts b/packages/@atjson/source-gdocs-paste/src/annotations/vertical-adjust.ts new file mode 100644 index 000000000..43cf5bc64 --- /dev/null +++ b/packages/@atjson/source-gdocs-paste/src/annotations/vertical-adjust.ts @@ -0,0 +1,9 @@ +import { InlineAnnotation } from '@atjson/document'; + +export default class VerticalAdjust extends InlineAnnotation { + static vendorPrefix = 'gdocs'; + static type = 'ts_va'; // Text style: underline + attributes!: { + va: 'sub' | 'sup'; // Vertical adjust: subscript / superscript + }; +} diff --git a/packages/@atjson/source-gdocs-paste/src/gdocs-parser.ts b/packages/@atjson/source-gdocs-paste/src/gdocs-parser.ts index f59489954..5a5d0f7d4 100644 --- a/packages/@atjson/source-gdocs-paste/src/gdocs-parser.ts +++ b/packages/@atjson/source-gdocs-paste/src/gdocs-parser.ts @@ -1,4 +1,4 @@ -import { Annotation } from '@atjson/document'; +import { AnnotationJSON } from '@atjson/document'; import { GDocsEntityMap, GDocsStyleSlice } from './types'; @@ -8,12 +8,12 @@ import extractListStyles from './list-styles'; import extractParagraphStyles from './paragraph-styles'; import extractTextStyles from './text-styles'; -export interface GDocsSource { +export interface GDocsPasteBuffer { [key: string]: any; } export interface Transforms { - [key: string]: (styles: GDocsStyleSlice[], entityMap: GDocsEntityMap) => Annotation[]; + [key: string]: (styles: GDocsStyleSlice[], entityMap: GDocsEntityMap) => AnnotationJSON[]; } export default class GDocsParser { @@ -26,9 +26,9 @@ export default class GDocsParser { link: extractLinkStyles }; - gdocsSource: GDocsSource; + gdocsSource: GDocsPasteBuffer; - constructor(source: GDocsSource) { + constructor(source: GDocsPasteBuffer) { this.gdocsSource = source; } @@ -36,7 +36,7 @@ export default class GDocsParser { return this.gdocsSource.resolved.dsl_spacers; } - getAnnotations(): Annotation[] { + getAnnotations(): AnnotationJSON[] { const styleSlices = this.gdocsSource.resolved.dsl_styleslices; const transforms = GDocsParser.transforms; const entityMap: GDocsEntityMap = this.gdocsSource.resolved.dsl_entitymap; @@ -51,6 +51,6 @@ export default class GDocsParser { return null; }); - return [].concat.apply([], annotations).filter((a: Annotation | null) => a != null); + return [].concat.apply([], annotations).filter((a: AnnotationJSON | null) => a != null); } } diff --git a/packages/@atjson/source-gdocs-paste/src/horizontal-rule.ts b/packages/@atjson/source-gdocs-paste/src/horizontal-rule.ts index 6c0e702b2..445fb482d 100644 --- a/packages/@atjson/source-gdocs-paste/src/horizontal-rule.ts +++ b/packages/@atjson/source-gdocs-paste/src/horizontal-rule.ts @@ -1,9 +1,8 @@ -import { Annotation } from '@atjson/document'; -import { HorizontalRule } from './schema'; +import { AnnotationJSON } from '@atjson/document'; import { GDocsStyleSlice } from './types'; -export default function extractHorizontalRule(styles: GDocsStyleSlice[]): Annotation[] { - let annotations: HorizontalRule[] = []; +export default function extractHorizontalRule(styles: GDocsStyleSlice[]): AnnotationJSON[] { + let annotations: AnnotationJSON[] = []; for (let i = 0; i < styles.length; i++) { let style = styles[i]; diff --git a/packages/@atjson/source-gdocs-paste/src/index.ts b/packages/@atjson/source-gdocs-paste/src/index.ts index 1b8216397..5990ca945 100644 --- a/packages/@atjson/source-gdocs-paste/src/index.ts +++ b/packages/@atjson/source-gdocs-paste/src/index.ts @@ -1,28 +1,33 @@ -import Document, { Schema } from '@atjson/document'; -import schema from '@atjson/schema'; -import gdocsSchema from './schema'; - -import GDocsParser, { GDocsSource } from './gdocs-parser'; +import Document from '@atjson/document'; +import { + Bold, + Heading, + HorizontalRule, + Italic, + Link, + List, + ListItem, + Strikethrough, + Underline, + VerticalAdjust +} from './annotations'; + +import GDocsParser, { GDocsPasteBuffer } from './gdocs-parser'; export default class extends Document { - constructor(gdocsSource: GDocsSource) { - let gdocsParser = new GDocsParser(gdocsSource); + static contentType = 'application/vnd.atjson+gdocs'; + static schema = [Bold, Heading, HorizontalRule, Italic, Link, List, ListItem, Strikethrough, Underline, VerticalAdjust]; + static fromSource(pasteBuffer: GDocsPasteBuffer) { + let gdocsParser = new GDocsParser(pasteBuffer); - super({ + return new this({ content: gdocsParser.getContent(), - contentType: 'text/google-docs', - annotations: gdocsParser.getAnnotations(), - schema: gdocsSchema as Schema + annotations: gdocsParser.getAnnotations() }); } toCommonSchema(): Document { - let doc = new Document({ - content: this.content, - contentType: 'text/atjson', - annotations: [...this.annotations], - schema: schema as Schema - }); + let doc = this.clone(); doc.where({ type: '-gdocs-ts_bd' }).set({ type: 'bold' }); doc.where({ type: '-gdocs-ts_it' }).set({ type: 'italic' }); @@ -40,7 +45,7 @@ export default class extends Document { // b_gt: 9 indicates an unordered list, but ordered lists have a variety of b_gt values doc.where({ type: '-gdocs-list', attributes: { '-gdocs-ls_b_gt': 9 } }).set({ type: 'list', attributes: { type: 'bulleted' } }); doc.where({ type: '-gdocs-list' }).set({ type: 'list', attributes: { type: 'numbered' } }); - doc.where({ type: '-gdocs-list-item' }).set({ type: 'list-item' }); + doc.where({ type: '-gdocs-list_item' }).set({ type: 'list-item' }); doc.where({ type: '-gdocs-lnks_link' }) .set({ type: 'link' }) diff --git a/packages/@atjson/source-gdocs-paste/src/link-styles.ts b/packages/@atjson/source-gdocs-paste/src/link-styles.ts index 8fb325ee3..a5518fc7c 100644 --- a/packages/@atjson/source-gdocs-paste/src/link-styles.ts +++ b/packages/@atjson/source-gdocs-paste/src/link-styles.ts @@ -1,10 +1,9 @@ -import { Annotation } from '@atjson/document'; -import { Link } from './schema'; +import { AnnotationJSON } from '@atjson/document'; import { GDocsStyleSlice } from './types'; -export default function extractLinkStyles(linkStyles: GDocsStyleSlice[]): Annotation[] { - let currentLink: Partial | null = null; - let links: Link[] = []; +export default function extractLinkStyles(linkStyles: GDocsStyleSlice[]): AnnotationJSON[] { + let currentLink: Partial | null = null; + let links: AnnotationJSON[] = []; for (let i = 0; i < linkStyles.length; i++) { let link = linkStyles[i]; @@ -16,7 +15,7 @@ export default function extractLinkStyles(linkStyles: GDocsStyleSlice[]): Annota // push it into the list of found links. if (currentLink !== null) { currentLink.end = i; - links.push(currentLink as Link); + links.push(currentLink as AnnotationJSON); currentLink = null; } diff --git a/packages/@atjson/source-gdocs-paste/src/list-styles.ts b/packages/@atjson/source-gdocs-paste/src/list-styles.ts index 7737cb35f..e22446b82 100644 --- a/packages/@atjson/source-gdocs-paste/src/list-styles.ts +++ b/packages/@atjson/source-gdocs-paste/src/list-styles.ts @@ -1,11 +1,10 @@ -import { Annotation } from '@atjson/document'; -import { List, ListItem } from './schema'; +import { AnnotationJSON } from '@atjson/document'; import { GDocsEntityMap, GDocsStyleSlice } from './types'; -export default function extractListStyles(lists: GDocsStyleSlice[], entityMap: GDocsEntityMap): Annotation[] { +export default function extractListStyles(lists: GDocsStyleSlice[], entityMap: GDocsEntityMap): AnnotationJSON[] { let lastParagraphStart = 0; - let listAnnotations: { [key: string]: List } = {}; - let listItems: ListItem[] = []; + let listAnnotations: { [key: string]: AnnotationJSON } = {}; + let listItems: AnnotationJSON[] = []; for (let i = 0; i < lists.length; i++) { let list = lists[i]; @@ -18,6 +17,7 @@ export default function extractListStyles(lists: GDocsStyleSlice[], entityMap: G if (!listAnnotations[list.ls_id]) { listAnnotations[list.ls_id] = { + id: list.ls_id, type: '-gdocs-list', start: lastParagraphStart, end: i, @@ -33,7 +33,8 @@ export default function extractListStyles(lists: GDocsStyleSlice[], entityMap: G } listItems.push({ - type: '-gdocs-list-item', + id: `${list.ls_id}-${listItems.length}`, + type: '-gdocs-list_item', start: lastParagraphStart, end: i, attributes: { @@ -45,7 +46,7 @@ export default function extractListStyles(lists: GDocsStyleSlice[], entityMap: G lastParagraphStart = i + 1; } - let annotations: Annotation[] = listItems; + let annotations: AnnotationJSON[] = listItems; for (let listAnnotation in listAnnotations) { annotations.push(listAnnotations[listAnnotation]); } diff --git a/packages/@atjson/source-gdocs-paste/src/paragraph-styles.ts b/packages/@atjson/source-gdocs-paste/src/paragraph-styles.ts index 8392a3aab..100201341 100644 --- a/packages/@atjson/source-gdocs-paste/src/paragraph-styles.ts +++ b/packages/@atjson/source-gdocs-paste/src/paragraph-styles.ts @@ -1,5 +1,4 @@ -import { Annotation } from '@atjson/document'; -import { Heading } from './schema'; +import { AnnotationJSON } from '@atjson/document'; import { GDocsStyleSlice } from './types'; /* @@ -33,9 +32,9 @@ import { GDocsStyleSlice } from './types'; * ps_sm: unknown * */ -export default function extractParagraphStyles(styles: GDocsStyleSlice[]): Annotation[] { +export default function extractParagraphStyles(styles: GDocsStyleSlice[]): AnnotationJSON[] { let lastParagraphStart = 0; - let annotations: Heading[] = []; + let annotations: AnnotationJSON[] = []; for (let i = 0; i < styles.length; i++) { let style = styles[i]; diff --git a/packages/@atjson/source-gdocs-paste/src/text-styles.ts b/packages/@atjson/source-gdocs-paste/src/text-styles.ts index 86463fc3d..b47acfe49 100644 --- a/packages/@atjson/source-gdocs-paste/src/text-styles.ts +++ b/packages/@atjson/source-gdocs-paste/src/text-styles.ts @@ -1,13 +1,13 @@ -import { Annotation } from '@atjson/document'; +import { AnnotationJSON } from '@atjson/document'; import { GDocsStyleSlice } from './types'; interface ParseState { - [key: string]: Annotation; + [key: string]: AnnotationJSON; } -export default function extractTextStyles(styles: GDocsStyleSlice[]): Annotation[] { +export default function extractTextStyles(styles: GDocsStyleSlice[]): AnnotationJSON[] { let state: ParseState = {}; - let annotations: Annotation[] = []; + let annotations: AnnotationJSON[] = []; for (let i = 0; i < styles.length; i++) { let style = styles[i]; @@ -32,7 +32,7 @@ export default function extractTextStyles(styles: GDocsStyleSlice[]): Annotation for (let styleType of ['ts_bd', 'ts_it', 'ts_un', 'ts_st']) { if (style[styleType] === true && !state[styleType]) { - state[styleType] = { type: '-gdocs-' + styleType, start: i, end: -1 }; + state[styleType] = { type: '-gdocs-' + styleType, start: i, end: -1, attributes: {} }; } else if (style[styleType] === false && style[styleType + '_i'] === false && state[styleType]) { state[styleType].end = i; annotations.push(state[styleType]); diff --git a/packages/@atjson/source-gdocs-paste/test/__snapshots__/atjson-source-gdocs-paste-test.ts.snap b/packages/@atjson/source-gdocs-paste/test/__snapshots__/source-test.ts.snap similarity index 100% rename from packages/@atjson/source-gdocs-paste/test/__snapshots__/atjson-source-gdocs-paste-test.ts.snap rename to packages/@atjson/source-gdocs-paste/test/__snapshots__/source-test.ts.snap diff --git a/packages/@atjson/source-gdocs-paste/test/atjson-source-gdocs-paste-test.ts b/packages/@atjson/source-gdocs-paste/test/source-test.ts similarity index 65% rename from packages/@atjson/source-gdocs-paste/test/atjson-source-gdocs-paste-test.ts rename to packages/@atjson/source-gdocs-paste/test/source-test.ts index d43e84c51..b8e2e0a36 100644 --- a/packages/@atjson/source-gdocs-paste/test/atjson-source-gdocs-paste-test.ts +++ b/packages/@atjson/source-gdocs-paste/test/source-test.ts @@ -1,35 +1,35 @@ -import Document from '@atjson/document'; import * as fs from 'fs'; import * as path from 'path'; -import GDocsSource from '../src'; +import { VerticalAdjust } from '../src/annotations'; +import GDocsSource from '../src/index'; describe('@atjson/source-gdocs-paste', () => { describe('relatively complex document', () => { - let atjson: Document; + let pasteBuffer; beforeAll(() => { // https://docs.google.com/document/d/18pp4dAGx5II596HHGOLUXXcc6VKLAVRBUMLm9Ge8eOE/edit?usp=sharing let fixturePath = path.join(__dirname, 'fixtures', 'complex.json'); - atjson = JSON.parse(fs.readFileSync(fixturePath).toString()); + pasteBuffer = JSON.parse(fs.readFileSync(fixturePath).toString()); }); it('has some json', () => { - expect(atjson).toHaveProperty('resolved'); + expect(pasteBuffer).toHaveProperty('resolved'); }); it('does not throw an error when instantiating with GDocsSource', () => { - expect(new GDocsSource(atjson)).toBeDefined(); + expect(GDocsSource.fromSource(pasteBuffer)).toBeDefined(); }); it('correctly sets the content', () => { - let gdocs = new GDocsSource(atjson); + let gdocs = GDocsSource.fromSource(pasteBuffer); expect(gdocs.content.length).toEqual(438); expect(gdocs.content).toMatchSnapshot(); }); it('extracts bold', () => { - let gdocs = new GDocsSource(atjson); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_bd'); + let gdocs = GDocsSource.fromSource(pasteBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'ts_bd'); expect(annotations.length).toEqual(2); let [a0, a1] = annotations; @@ -38,8 +38,8 @@ describe('@atjson/source-gdocs-paste', () => { }); it('extracts italic', () => { - let gdocs = new GDocsSource(atjson); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_it'); + let gdocs = GDocsSource.fromSource(pasteBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'ts_it'); expect(annotations.length).toEqual(2); let [a0, a1] = annotations; @@ -48,63 +48,63 @@ describe('@atjson/source-gdocs-paste', () => { }); it('extracts headings', () => { - let gdocs = new GDocsSource(atjson); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ps_hd').sort((a, b) => a.start - b.start); + let gdocs = GDocsSource.fromSource(pasteBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'ps_hd').sort((a, b) => a.start - b.start); expect(annotations.length).toEqual(4); let [a0, a1, a2, a3] = annotations; expect(gdocs.content.substring(a0.start, a0.end)).toEqual('Heading 1'); - expect(a0.attributes!['-gdocs-level']).toEqual(1); + expect(a0.attributes.level).toEqual(1); expect(gdocs.content.substring(a1.start, a1.end)).toEqual('Heading 2'); - expect(a1.attributes!['-gdocs-level']).toEqual(2); + expect(a1.attributes.level).toEqual(2); expect(gdocs.content.substring(a2.start, a2.end)).toEqual('Title'); - expect(a2.attributes!['-gdocs-level']).toEqual(100); + expect(a2.attributes.level).toEqual(100); expect(gdocs.content.substring(a3.start, a3.end)).toEqual('Subtitle'); - expect(a3.attributes!['-gdocs-level']).toEqual(101); + expect(a3.attributes.level).toEqual(101); }); it('extracts lists', () => { - let gdocs = new GDocsSource(atjson); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-list'); + let gdocs = GDocsSource.fromSource(pasteBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'list'); expect(annotations.length).toEqual(2); let a0 = annotations[0]; expect(gdocs.content.substring(a0.start, a0.end)).toEqual('Here’s a numbered list\nAnd another item'); - expect(a0.attributes!['-gdocs-ls_id']).toEqual('kix.trdi2u6o1bvt'); + expect(a0.attributes.ls_id).toEqual('kix.trdi2u6o1bvt'); }); it('extracts list items', () => { - let gdocs = new GDocsSource(atjson); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-list-item'); + let gdocs = GDocsSource.fromSource(pasteBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'list_item'); expect(annotations.length).toEqual(4); let [a0, a1] = annotations; expect(gdocs.content.substring(a0.start, a0.end)).toEqual('Here’s a numbered list'); - expect(a0.attributes!['-gdocs-ls_id']).toEqual('kix.trdi2u6o1bvt'); - expect(a0.attributes!['-gdocs-ls_nest']).toEqual(0); + expect(a0.attributes.ls_id).toEqual('kix.trdi2u6o1bvt'); + expect(a0.attributes.ls_nest).toEqual(0); expect(gdocs.content.substring(a1.start, a1.end)).toEqual('And another item'); - expect(a1.attributes!['-gdocs-ls_id']).toEqual('kix.trdi2u6o1bvt'); - expect(a1.attributes!['-gdocs-ls_nest']).toEqual(0); + expect(a1.attributes.ls_id).toEqual('kix.trdi2u6o1bvt'); + expect(a1.attributes.ls_nest).toEqual(0); }); it('extracts links', () => { - let gdocs = new GDocsSource(atjson); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-lnks_link'); + let gdocs = GDocsSource.fromSource(pasteBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'lnks_link'); expect(annotations.length).toEqual(1); let link = annotations[0]; expect(gdocs.content.substring(link.start, link.end)).toEqual(' is '); - expect(link.attributes!['-gdocs-ulnk_url']).toEqual('https://www.google.com/'); - expect(link.attributes!['-gdocs-lnk_type']).toEqual(0); + expect(link.attributes.ulnk_url).toEqual('https://www.google.com/'); + expect(link.attributes.lnk_type).toEqual(0); }); }); @@ -122,18 +122,18 @@ describe('@atjson/source-gdocs-paste', () => { }); it('does not throw an error when instantiating with GDocsSource', () => { - expect(new GDocsSource(gdocsBuffer)).toBeDefined(); + expect(GDocsSource.fromSource(gdocsBuffer)).toBeDefined(); }); it('correctly sets the content', () => { - let gdocs = new GDocsSource(gdocsBuffer); + let gdocs = GDocsSource.fromSource(gdocsBuffer); expect(gdocs.content.length).toEqual(219); expect(gdocs.content).toMatchSnapshot(); }); it('extracts bold', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_bd'); + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'ts_bd'); expect(annotations.length).toEqual(1); let [bold] = annotations; @@ -141,8 +141,8 @@ describe('@atjson/source-gdocs-paste', () => { }); it('extracts italic', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_it'); + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'ts_it'); expect(annotations.length).toEqual(1); let [italic] = annotations; @@ -150,8 +150,8 @@ describe('@atjson/source-gdocs-paste', () => { }); it('extracts underline', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_un'); + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'ts_un'); expect(annotations.length).toEqual(1); let [underline] = annotations; @@ -159,8 +159,8 @@ describe('@atjson/source-gdocs-paste', () => { }); it('extracts horizontal rules', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-horizontal_rule'); + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'horizontal_rule'); expect(annotations.length).toEqual(1); let [hr] = annotations; @@ -168,29 +168,22 @@ describe('@atjson/source-gdocs-paste', () => { }); it('extracts strikethrough', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_st'); + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let annotations = gdocs.annotations.filter(a => a.type === 'ts_st'); expect(annotations.length).toEqual(1); let [strikethrough] = annotations; expect(gdocs.content.substring(strikethrough.start, strikethrough.end)).toEqual('strikethrough'); }); - it('extracts superscript', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_va' && a.attributes!['-gdocs-va'] === 'sup'); - expect(annotations.length).toEqual(1); + it('extracts vertical adjust', () => { + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let annotations: VerticalAdjust[] = gdocs.annotations.filter(a => a instanceof VerticalAdjust) as VerticalAdjust[]; + expect(annotations.length).toEqual(2); - let [superscript] = annotations; + let [superscript] = annotations.filter(annotation => annotation.attributes.va === 'sup'); + let [subscript] = annotations.filter(annotation => annotation.attributes.va === 'sub'); expect(gdocs.content.substring(superscript.start, superscript.end)).toEqual('TM'); - }); - - it('extracts subscript', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let annotations = gdocs.annotations.filter(a => a.type === '-gdocs-ts_va' && a.attributes!['-gdocs-va'] === 'sub'); - expect(annotations.length).toEqual(1); - - let [subscript] = annotations; expect(gdocs.content.substring(subscript.start, subscript.end)).toEqual('2'); }); }); @@ -204,19 +197,19 @@ describe('@atjson/source-gdocs-paste', () => { }); it('creates the right number of list annotations', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let lists = gdocs.annotations.filter(a => a.type === '-gdocs-list'); + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let lists = gdocs.annotations.filter(a => a.type === 'list'); expect(lists.length).toEqual(2); }); it('captures list-specific attributes', () => { - let gdocs = new GDocsSource(gdocsBuffer); - let lists = gdocs.annotations.filter(a => a.type === '-gdocs-list'); + let gdocs = GDocsSource.fromSource(gdocsBuffer); + let lists = gdocs.annotations.filter(a => a.type === 'list'); let expectedShape = expect.objectContaining({ - '-gdocs-ls_b_gs': expect.anything(), - '-gdocs-ls_b_gt': expect.anything(), - '-gdocs-ls_b_a' : expect.anything() + ls_b_gs: expect.anything(), + ls_b_gt: expect.anything(), + ls_b_a: expect.anything() }); lists.forEach(list => { @@ -225,10 +218,10 @@ describe('@atjson/source-gdocs-paste', () => { }); it('distinguishes numbered from bulleted lists', () => { - let gdocs = new GDocsSource(gdocsBuffer); + let gdocs = GDocsSource.fromSource(gdocsBuffer); let lists = gdocs.annotations - .filter(a => a.type === '-gdocs-list') - .filter(a => a.attributes!['-gdocs-ls_b_gt'] === 9); + .filter(a => a.type === 'list') + .filter(a => a.attributes.ls_b_gt === 9); expect(lists.length).toEqual(1); }); diff --git a/packages/@atjson/source-gdocs-paste/test/atjson-source-gdocs-translator-test.ts b/packages/@atjson/source-gdocs-paste/test/translator-test.ts similarity index 88% rename from packages/@atjson/source-gdocs-paste/test/atjson-source-gdocs-translator-test.ts rename to packages/@atjson/source-gdocs-paste/test/translator-test.ts index d070cb5e3..327962710 100644 --- a/packages/@atjson/source-gdocs-paste/test/atjson-source-gdocs-translator-test.ts +++ b/packages/@atjson/source-gdocs-paste/test/translator-test.ts @@ -10,22 +10,22 @@ describe('@atjson/source-gdocs-paste', () => { // https://docs.google.com/document/d/18pp4dAGx5II596HHGOLUXXcc6VKLAVRBUMLm9Ge8eOE/edit?usp=sharing let fixturePath = path.join(__dirname, 'fixtures', 'complex.json'); let rawJSON = JSON.parse(fs.readFileSync(fixturePath).toString()); - let gdocs = new GDocsSource(rawJSON); + let gdocs = GDocsSource.fromSource(rawJSON); atjson = gdocs.toCommonSchema(); }); it('correctly converts -gdocs-ts_bd to bold', () => { - let bolds = atjson.annotations.filter(a => a.type === 'bold'); + let bolds = atjson.where(a => a.type === 'bold'); expect(bolds.length).toEqual(2); }); it('correctly converts italic', () => { - let italics = atjson.annotations.filter(a => a.type === 'italic'); + let italics = atjson.where(a => a.type === 'italic'); expect(italics.length).toEqual(2); }); it('correctly converts headings', () => { - let headings = atjson.annotations.filter(a => a.type === 'heading'); + let headings = atjson.where(a => a.type === 'heading'); expect(headings.length).toEqual(4); expect(headings.map(h => h.attributes!.level)).toEqual([1, 2, 100, 101]); });