├── gulpfile.js ├── .gitignore ├── gulp ├── tasks │ ├── index.js │ ├── clean.js │ └── scripts.js ├── config.js └── index.js ├── src ├── index.ts ├── miniDOM │ ├── bold.ts │ ├── italic.ts │ ├── subscript.ts │ ├── underline.ts │ ├── superscript.ts │ ├── strikethrough.ts │ ├── link.ts │ ├── color.ts │ ├── bgcolor.ts │ ├── span.ts │ ├── header.ts │ ├── index.ts │ ├── listItem.ts │ ├── block.ts │ ├── root.ts │ ├── image.ts │ ├── orderedList.ts │ ├── unorderedList.ts │ ├── paragraph.ts │ ├── text.ts │ └── treeNode.ts ├── tokenize.ts └── formatter.ts ├── dist ├── index.d.ts ├── miniDOM │ ├── span.d.ts │ ├── root.d.ts │ ├── bold.d.ts │ ├── color.d.ts │ ├── italic.d.ts │ ├── link.d.ts │ ├── bgcolor.d.ts │ ├── block.d.ts │ ├── subscript.d.ts │ ├── superscript.d.ts │ ├── underline.d.ts │ ├── strikethrough.d.ts │ ├── header.d.ts │ ├── listItem.d.ts │ ├── image.d.ts │ ├── orderedList.d.ts │ ├── unorderedList.d.ts │ ├── paragraph.d.ts │ ├── text.d.ts │ ├── index.d.ts │ ├── treeNode.d.ts │ ├── span.js │ ├── bold.js │ ├── italic.js │ ├── link.js │ ├── color.js │ ├── subscript.js │ ├── underline.js │ ├── superscript.js │ ├── strikethrough.js │ ├── bgcolor.js │ ├── header.js │ ├── root.js │ ├── image.js │ ├── orderedList.js │ ├── unorderedList.js │ ├── block.js │ ├── listItem.js │ ├── text.js │ ├── paragraph.js │ ├── treeNode.js │ └── index.js ├── tokenize.d.ts ├── index.js ├── formatter.d.ts ├── tokenize.js └── formatter.js ├── tsconfig.json ├── CHANGELOG.md ├── LICENSE ├── package.json ├── test ├── extension.spec.ts ├── tokenizer.spec.ts ├── integration.spec.ts └── formats.spec.ts ├── README.md └── tslint.json /gulpfile.js: -------------------------------------------------------------------------------- 1 | require('./gulp'); 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/npm-debug.log 3 | scratch.txt 4 | -------------------------------------------------------------------------------- /gulp/tasks/index.js: -------------------------------------------------------------------------------- 1 | require('./scripts'); 2 | require('./clean'); 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './miniDOM'; 2 | export * from './formatter'; 3 | export * from './tokenize'; 4 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './miniDOM'; 2 | export * from './formatter'; 3 | export * from './tokenize'; 4 | -------------------------------------------------------------------------------- /gulp/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dest: 'dist', 3 | src: 'src', 4 | tests: 'tests', 5 | scripts: ['**/*.ts'], 6 | }; 7 | -------------------------------------------------------------------------------- /gulp/index.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | require('./tasks'); 3 | 4 | gulp.task('default', gulp.series('clean', 'build')); 5 | -------------------------------------------------------------------------------- /dist/miniDOM/span.d.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | export declare class SpanNode extends TreeNode { 3 | constructor(opts: any); 4 | absorb(child: any): any; 5 | } 6 | -------------------------------------------------------------------------------- /gulp/tasks/clean.js: -------------------------------------------------------------------------------- 1 | const config = require('../config'); 2 | const gulp = require('gulp'); 3 | const del = require('del'); 4 | 5 | gulp.task('clean', () => { 6 | return del([config.dest]); 7 | }); 8 | -------------------------------------------------------------------------------- /dist/miniDOM/root.d.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | export declare class RootNode extends TreeNode { 3 | constructor(opts?: {}); 4 | absorb(child: any): any; 5 | toHTML(): string; 6 | toHTMLAsync(): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/bold.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class BoldNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/color.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class ColorNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/italic.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class ItalicNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/link.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class LinkNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/bgcolor.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class BackgroundColorNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/block.d.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | export declare class BlockNode extends TreeNode { 3 | constructor(opts: any); 4 | plainTextAsync(): Promise; 5 | plainText(): string; 6 | appendChild(child: any): void; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/subscript.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class SubscriptNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/superscript.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class SuperscriptNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/underline.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class UnderlineNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/strikethrough.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class StrikethroughNode extends SpanNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | static matches(token: Token): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/miniDOM/header.d.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { Token } from '../tokenize'; 3 | export declare class HeaderNode extends BlockNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | absorb(child: any): any; 7 | static matches(token: Token): any; 8 | } 9 | -------------------------------------------------------------------------------- /dist/miniDOM/listItem.d.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | export declare class ListItemNode extends TreeNode { 3 | openTag(): string; 4 | closeTag(): string; 5 | absorb(child: any): any; 6 | plainTextAsync(): any; 7 | plainText(): string; 8 | static matches(token?: {}): boolean; 9 | } 10 | -------------------------------------------------------------------------------- /dist/miniDOM/image.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class ImageNode extends SpanNode { 4 | imageUrl: string; 5 | constructor(opts?: any); 6 | plainText(): string; 7 | isLeaf(): boolean; 8 | openTag(): string; 9 | closeTag(): string; 10 | static matches(token: Token): boolean; 11 | } 12 | -------------------------------------------------------------------------------- /dist/miniDOM/orderedList.d.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { Token } from '../tokenize'; 3 | export declare class OrderedListNode extends BlockNode { 4 | constructor(opts: any); 5 | appendChild(node: any): void; 6 | openTag(): string; 7 | closeTag(): string; 8 | absorb(child: any): any; 9 | static matches(token: Token): boolean; 10 | } 11 | -------------------------------------------------------------------------------- /dist/miniDOM/unorderedList.d.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { Token } from '../tokenize'; 3 | export declare class UnorderedListNode extends BlockNode { 4 | constructor(opts: any); 5 | appendChild(node: any): void; 6 | openTag(): string; 7 | closeTag(): string; 8 | absorb(child: any): any; 9 | static matches(token: Token): boolean; 10 | } 11 | -------------------------------------------------------------------------------- /src/miniDOM/bold.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class BoldNode extends SpanNode { 5 | openTag() { 6 | return ''; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.bold; 13 | } 14 | } 15 | 16 | BoldNode.priority = 1; 17 | -------------------------------------------------------------------------------- /src/miniDOM/italic.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class ItalicNode extends SpanNode { 5 | openTag() { 6 | return ''; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.italic; 13 | } 14 | } 15 | 16 | ItalicNode.priority = 2; 17 | -------------------------------------------------------------------------------- /dist/miniDOM/paragraph.d.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { Token } from '../tokenize'; 3 | export declare class ParagraphNode extends BlockNode { 4 | openTag(): string; 5 | closeTag(): string; 6 | absorb(child: any): any; 7 | toHTMLAsync(indentLevel?: number): Promise; 8 | toHTML(indentLevel?: number): any; 9 | static matches(token: Token): boolean; 10 | } 11 | -------------------------------------------------------------------------------- /src/miniDOM/subscript.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class SubscriptNode extends SpanNode { 5 | openTag() { 6 | return ''; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.sub; 13 | } 14 | } 15 | 16 | SubscriptNode.priority = 9; 17 | -------------------------------------------------------------------------------- /src/miniDOM/underline.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class UnderlineNode extends SpanNode { 5 | openTag() { 6 | return ''; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.underline; 13 | } 14 | } 15 | 16 | UnderlineNode.priority = 3; 17 | -------------------------------------------------------------------------------- /src/miniDOM/superscript.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class SuperscriptNode extends SpanNode { 5 | openTag() { 6 | return ''; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.super; 13 | } 14 | } 15 | 16 | SuperscriptNode.priority = 10; 17 | -------------------------------------------------------------------------------- /src/miniDOM/strikethrough.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class StrikethroughNode extends SpanNode { 5 | openTag() { 6 | return ''; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.strike; 13 | } 14 | } 15 | 16 | StrikethroughNode.priority = 8; 17 | -------------------------------------------------------------------------------- /src/miniDOM/link.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class LinkNode extends SpanNode { 5 | openTag() { 6 | return ``; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.link; 13 | } 14 | } 15 | 16 | LinkNode.priority = 11; 17 | -------------------------------------------------------------------------------- /src/miniDOM/color.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class ColorNode extends SpanNode { 5 | openTag() { 6 | return ``; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.color; 13 | } 14 | } 15 | 16 | ColorNode.priority = 5; 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "lib": ["dom", "es6"], 5 | "module": "es2015", 6 | "declaration": true, 7 | "moduleResolution": "node", 8 | "target": "es2017", 9 | "baseUrl": "." 10 | }, 11 | "include": ["src/**/*.ts"], 12 | "exclude": ["node_modules"], 13 | "compileOnSave": false, 14 | "atom": { 15 | "rewriteTsconfig": false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/miniDOM/bgcolor.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class BackgroundColorNode extends SpanNode { 5 | openTag() { 6 | return ``; 7 | } 8 | closeTag() { 9 | return ''; 10 | } 11 | static matches(token: Token) { 12 | return token.attributes && token.attributes.bg; 13 | } 14 | } 15 | 16 | BackgroundColorNode.priority = 6; 17 | -------------------------------------------------------------------------------- /src/miniDOM/span.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class SpanNode extends TreeNode { 5 | constructor(opts) { 6 | super(opts); 7 | this.level = 'span'; 8 | } 9 | absorb(child) { 10 | if (child.type === this.type) { 11 | this.children = this.children.concat(child.children); 12 | return null; 13 | } else { 14 | return child; 15 | } 16 | } 17 | } 18 | SpanNode.priority = 100; 19 | -------------------------------------------------------------------------------- /dist/miniDOM/text.d.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | export declare class TextNode extends SpanNode { 4 | unescapedContents: string; 5 | constructor(opts?: any); 6 | readonly contents: any; 7 | plainText(): string; 8 | openTag(): string; 9 | closeTag(): string; 10 | appendChild(): void; 11 | isLeaf(): boolean; 12 | absorb(child: any): any; 13 | escape(contents: any): any; 14 | static matches(token: Token): boolean; 15 | } 16 | -------------------------------------------------------------------------------- /src/miniDOM/header.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class HeaderNode extends BlockNode { 5 | openTag() { 6 | return ``; 7 | } 8 | closeTag() { 9 | return ``; 10 | } 11 | absorb(child) { 12 | return child; 13 | } 14 | static matches(token: Token) { 15 | return token.type === 'linebreak' && token.attributes.header; 16 | } 17 | } 18 | 19 | HeaderNode.priority = 21; 20 | -------------------------------------------------------------------------------- /dist/tokenize.d.ts: -------------------------------------------------------------------------------- 1 | export interface BasicToken { 2 | type: string; 3 | attributes: { 4 | [key: string]: any; 5 | }; 6 | } 7 | export interface TextToken extends BasicToken { 8 | type: 'text'; 9 | contents: string; 10 | } 11 | export interface LinebreakToken extends BasicToken { 12 | type: 'linebreak'; 13 | } 14 | export interface ImageToken extends BasicToken { 15 | type: 'image'; 16 | contents: { 17 | image: string; 18 | }; 19 | } 20 | export declare type Token = TextToken | LinebreakToken | ImageToken; 21 | export declare function tokenize(ops: any): Token[]; 22 | -------------------------------------------------------------------------------- /src/miniDOM/index.ts: -------------------------------------------------------------------------------- 1 | export * from './bgcolor'; 2 | export * from './block'; 3 | export * from './bold'; 4 | export * from './color'; 5 | export * from './header'; 6 | export * from './image'; 7 | export * from './italic'; 8 | export * from './link'; 9 | export * from './listItem'; 10 | export * from './orderedList'; 11 | export * from './paragraph'; 12 | export * from './root'; 13 | export * from './span'; 14 | export * from './strikethrough'; 15 | export * from './subscript'; 16 | export * from './superscript'; 17 | export * from './text'; 18 | export * from './treeNode'; 19 | export * from './underline'; 20 | export * from './unorderedList'; 21 | -------------------------------------------------------------------------------- /dist/miniDOM/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './bgcolor'; 2 | export * from './block'; 3 | export * from './bold'; 4 | export * from './color'; 5 | export * from './header'; 6 | export * from './image'; 7 | export * from './italic'; 8 | export * from './link'; 9 | export * from './listItem'; 10 | export * from './orderedList'; 11 | export * from './paragraph'; 12 | export * from './root'; 13 | export * from './span'; 14 | export * from './strikethrough'; 15 | export * from './subscript'; 16 | export * from './superscript'; 17 | export * from './text'; 18 | export * from './treeNode'; 19 | export * from './underline'; 20 | export * from './unorderedList'; 21 | -------------------------------------------------------------------------------- /src/miniDOM/listItem.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class ListItemNode extends TreeNode { 5 | openTag() { 6 | return '
  • '; 7 | } 8 | closeTag() { 9 | return '
  • '; 10 | } 11 | absorb(child) { 12 | return child; 13 | } 14 | 15 | plainTextAsync() { 16 | return super.plainTextAsync().then(t => `* ${t}\n`); 17 | } 18 | 19 | plainText() { 20 | return `* ${super.plainText()}\n`; 21 | } 22 | 23 | static matches(token = {}) { 24 | return false; 25 | // return (token.attributes && token.attributes.list); 26 | } 27 | } 28 | 29 | ListItemNode.priority = 20; 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.1 (2016-06-14) 2 | Export tokenize and blockize functions 3 | 4 | ## 1.1.0 (2016-06-08) 5 | Add unit tests, fix a number of previously-unnoticed bugs 6 | 7 | ## 1.0.1 (2016-06-03) 8 | 9 | API Extension: 10 | - Allow TextNode to be subclassed and the escape() function extended. 11 | 12 | ```javascript 13 | import { Registry } from 'delta-transform-html'; 14 | export class Text extends Registry.get('text') { 15 | escape(contents) { 16 | return super.escape(contents) 17 | .replace(/{{/g, '{{"{{"}}'); 18 | } 19 | } 20 | Registry.add('text', Text); 21 | ``` 22 | 23 | As an example - the above will escape double-curly braces in angular-parsed contexts, in order to prevent 24 | injection attacks. 25 | 26 | ## 1.0.0 (2016-06-02) 27 | 28 | Initial Release 29 | -------------------------------------------------------------------------------- /src/miniDOM/block.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class BlockNode extends TreeNode { 5 | constructor(opts) { 6 | super(opts); 7 | this.level = 'block'; 8 | } 9 | 10 | plainTextAsync() { 11 | return Promise.all(this.children.map(child => child.plainTextAsync())).then( 12 | c => `${c.join('')}\n`, 13 | ); 14 | } 15 | 16 | plainText() { 17 | return `${super.plainText()}\n`; 18 | } 19 | appendChild(child) { 20 | if (this.children.length === 0) { 21 | this.children.push(child); 22 | } else { 23 | const remains = this.children[this.children.length - 1].absorb(child); 24 | if (remains !== null) { 25 | this.children.push(remains); 26 | } 27 | } 28 | } 29 | } 30 | 31 | BlockNode.priority = 101; 32 | -------------------------------------------------------------------------------- /src/miniDOM/root.ts: -------------------------------------------------------------------------------- 1 | import { TreeNode } from './treeNode'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class RootNode extends TreeNode { 5 | constructor(opts = {}) { 6 | super(opts); 7 | } 8 | absorb(child) { 9 | let remains = child; 10 | if (this.children.length > 0) { 11 | remains = this.children[this.children.length - 1].absorb(child); 12 | } 13 | if (remains !== null) { 14 | this.children.push(remains); 15 | } 16 | return null; 17 | } 18 | 19 | toHTML() { 20 | return this.children.map(c => c.toHTML(0)).join(''); // eslint-disable-line max-len 21 | } 22 | 23 | toHTMLAsync() { 24 | return Promise.all( 25 | this.children.map(c => c.toHTMLAsync(0)), 26 | ).then(childHTML => { 27 | return childHTML.join(''); // eslint-disable-line max-len 28 | }); 29 | } 30 | } 31 | 32 | RootNode.priority = -1; 33 | -------------------------------------------------------------------------------- /src/miniDOM/image.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class ImageNode extends SpanNode { 5 | imageUrl: string; 6 | constructor(opts: any = {}) { 7 | super(opts); 8 | if (opts.contents && opts.contents.image) { 9 | this.imageUrl = opts.contents.image; 10 | } else { 11 | this.imageUrl = opts.attributes.image; 12 | } 13 | this.contents = ``; 14 | } 15 | plainText() { 16 | return `IMAGE: ${this.imageUrl}`; 17 | } 18 | isLeaf() { 19 | return true; 20 | } 21 | openTag() { 22 | return ''; 23 | } 24 | closeTag() { 25 | return ''; 26 | } 27 | static matches(token: Token) { 28 | return token.type === 'image'; 29 | // (token.contents && token.contents.image) || 30 | // (token.attributes && token.attributes.image) 31 | // ); 32 | } 33 | } 34 | 35 | ImageNode.priority = 99; 36 | -------------------------------------------------------------------------------- /gulp/tasks/scripts.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const config = require('../config'); 3 | const ts = require('gulp-typescript'); 4 | const mergeStream = require('merge-stream'); 5 | const babel = require('gulp-babel'); 6 | 7 | function build() { 8 | const tsProject = ts.createProject('tsconfig.json'); 9 | const tsResult = gulp 10 | .src(config.scripts, { cwd: config.src }) 11 | .pipe(tsProject()); 12 | 13 | return mergeStream( 14 | tsResult.js.pipe( 15 | babel({ 16 | presets: [ 17 | [ 18 | 'env', 19 | { 20 | targets: { 21 | browsers: ['last 2 versions', 'safari >= 7'], 22 | node: '8.0.0', 23 | }, 24 | }, 25 | ], 26 | ], 27 | }), 28 | ), 29 | tsResult.dts, 30 | ).pipe(gulp.dest(config.dest)); 31 | } 32 | 33 | gulp.task('build', build); 34 | 35 | module.exports = build; 36 | -------------------------------------------------------------------------------- /src/miniDOM/orderedList.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { ListItemNode } from './listItem'; 3 | import { Token } from '../tokenize'; 4 | 5 | export class OrderedListNode extends BlockNode { 6 | constructor(opts) { 7 | super(opts); 8 | this.children = [new ListItemNode(opts)]; 9 | } 10 | 11 | appendChild(node) { 12 | this.children[0].appendChild(node); 13 | } 14 | 15 | openTag() { 16 | return '
      '; 17 | } 18 | closeTag() { 19 | return '
    '; 20 | } 21 | 22 | absorb(child) { 23 | if (child.type === this.type) { 24 | this.children = this.children.concat(child.children); 25 | return null; 26 | } else { 27 | return child; 28 | } 29 | } 30 | static matches(token: Token) { 31 | return ( 32 | token.type === 'linebreak' && 33 | token.attributes && 34 | (token.attributes.list === 'ordered' || token.attributes.ordered === true) 35 | ); 36 | } 37 | } 38 | 39 | OrderedListNode.priority = 30; 40 | -------------------------------------------------------------------------------- /src/miniDOM/unorderedList.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { ListItemNode } from './listItem'; 3 | import { Token } from '../tokenize'; 4 | 5 | export class UnorderedListNode extends BlockNode { 6 | constructor(opts) { 7 | super(opts); 8 | this.children = [new ListItemNode(opts)]; 9 | } 10 | 11 | appendChild(node) { 12 | this.children[0].appendChild(node); 13 | } 14 | 15 | openTag() { 16 | return '
      '; 17 | } 18 | 19 | closeTag() { 20 | return '
    '; 21 | } 22 | 23 | absorb(child) { 24 | if (child.type === this.type) { 25 | this.children = this.children.concat(child.children); 26 | return null; 27 | } else { 28 | return child; 29 | } 30 | } 31 | 32 | static matches(token: Token) { 33 | return ( 34 | token.type === 'linebreak' && 35 | token.attributes && 36 | (token.attributes.list === 'bullet' || token.attributes.bullet === true) 37 | ); 38 | } 39 | } 40 | 41 | UnorderedListNode.priority = 35; 42 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _miniDOM = require('./miniDOM'); 8 | 9 | Object.keys(_miniDOM).forEach(function (key) { 10 | if (key === "default" || key === "__esModule") return; 11 | Object.defineProperty(exports, key, { 12 | enumerable: true, 13 | get: function get() { 14 | return _miniDOM[key]; 15 | } 16 | }); 17 | }); 18 | 19 | var _formatter = require('./formatter'); 20 | 21 | Object.keys(_formatter).forEach(function (key) { 22 | if (key === "default" || key === "__esModule") return; 23 | Object.defineProperty(exports, key, { 24 | enumerable: true, 25 | get: function get() { 26 | return _formatter[key]; 27 | } 28 | }); 29 | }); 30 | 31 | var _tokenize = require('./tokenize'); 32 | 33 | Object.keys(_tokenize).forEach(function (key) { 34 | if (key === "default" || key === "__esModule") return; 35 | Object.defineProperty(exports, key, { 36 | enumerable: true, 37 | get: function get() { 38 | return _tokenize[key]; 39 | } 40 | }); 41 | }); -------------------------------------------------------------------------------- /dist/miniDOM/treeNode.d.ts: -------------------------------------------------------------------------------- 1 | import { Token } from '../tokenize'; 2 | export interface TreeAttributes { 3 | } 4 | export declare class TreeNode { 5 | static priority: number; 6 | children: TreeNode[]; 7 | attributes: any; 8 | type: string; 9 | contents: string; 10 | level: 'span' | 'block'; 11 | promisePlainContents?: () => Promise; 12 | constructor(opts?: { 13 | attributes?: any; 14 | }); 15 | dfsTraverse(): TreeNode[]; 16 | openTag(): string; 17 | closeTag(): string; 18 | promiseContents(): Promise; 19 | toHTMLAsync(indentLevel?: number): any; 20 | plainTextAsync(): any; 21 | plainText(): any; 22 | toHTML(indentLevel?: number): any; 23 | isLeaf(): boolean; 24 | appendChild(child: any): void; 25 | absorb(child: any): any; 26 | toJSON(): { 27 | type: string; 28 | level: "span" | "block"; 29 | children: TreeNode[]; 30 | attributes: any; 31 | contents: string; 32 | }; 33 | readonly priority: any; 34 | static matches(token: Token): boolean; 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Eric Eslinger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "delta-transform-html", 3 | "version": "2.0.0", 4 | "description": "A package for converting ottypes/rich-text Deltas to an HTML string", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "test": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' mocha --require ts-node/register test/*.spec.ts" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ericeslinger/delta-transform-html.git" 13 | }, 14 | "keywords": [ 15 | "rich-text", 16 | "quilljs" 17 | ], 18 | "author": "Eric Eslinger", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/ericeslinger/delta-transform-html/issues" 22 | }, 23 | "homepage": "https://github.com/ericeslinger/delta-transform-html#readme", 24 | "devDependencies": { 25 | "@types/chai": "^4.0.2", 26 | "@types/core-js": "^0.9.42", 27 | "@types/knex": "0.0.64", 28 | "@types/mocha": "^2.2.41", 29 | "@types/node": "^8.0.47", 30 | "@types/quill": "^1.3.3", 31 | "babel-core": "^6.26.0", 32 | "babel-preset-env": "^1.6.1", 33 | "chai": "^4.1.0", 34 | "del": "^3.0.0", 35 | "gulp": "gulpjs/gulp.git#4.0", 36 | "gulp-babel": "^7.0.0", 37 | "gulp-sourcemaps": "^2.6.0", 38 | "gulp-typescript": "^3.2.3", 39 | "merge-stream": "^1.0.1", 40 | "mocha": "^3.5.0", 41 | "ts-node": "^3.3.0", 42 | "tslint": "^5.8.0", 43 | "tslint-eslint-rules": "^4.1.1", 44 | "typescript": "2.5.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/miniDOM/paragraph.ts: -------------------------------------------------------------------------------- 1 | import { BlockNode } from './block'; 2 | import { TextNode } from './text'; 3 | import { Token } from '../tokenize'; 4 | 5 | export class ParagraphNode extends BlockNode { 6 | openTag() { 7 | return '

    '; 8 | } 9 | closeTag() { 10 | return '

    '; 11 | } 12 | absorb(child) { 13 | return child; 14 | } 15 | 16 | toHTMLAsync(indentLevel = 0) { 17 | if (this.children.length === 0) { 18 | this.children.push( 19 | new TextNode({ type: 'text', attributes: {}, contents: '' }), 20 | ); 21 | } 22 | return Promise.all( 23 | this.children.map(c => c.toHTMLAsync(0)), 24 | ).then(childHTML => { 25 | return `${this.openTag()}${childHTML.join('')}${this.closeTag()}`; // eslint-disable-line max-len 26 | }); 27 | } 28 | 29 | toHTML(indentLevel = 0) { 30 | if (this.children.length === 0) { 31 | this.children.push( 32 | new TextNode({ type: 'text', attributes: {}, contents: '' }), 33 | ); 34 | } 35 | // if (this.children.length === 0) { 36 | // return `${new Array(0).join(' ')}${this.openTag()} ${this.closeTag()}`; 37 | // } else { 38 | return super.toHTML(indentLevel); 39 | // } 40 | } 41 | 42 | static matches(token: Token) { 43 | return ( 44 | token.type === 'linebreak' && 45 | (!!token.attributes || 46 | (token.attributes.list === undefined && 47 | token.attributes.header === undefined)) 48 | ); 49 | } 50 | } 51 | 52 | ParagraphNode.priority = 19; 53 | -------------------------------------------------------------------------------- /dist/formatter.d.ts: -------------------------------------------------------------------------------- 1 | import * as MiniDOM from './miniDOM'; 2 | import { RootNode, TreeNode } from './miniDOM'; 3 | export interface TypeRegistry { 4 | bold?: typeof MiniDOM.BoldNode; 5 | italic?: typeof MiniDOM.ItalicNode; 6 | link?: typeof MiniDOM.LinkNode; 7 | listItem?: typeof MiniDOM.ListItemNode; 8 | ordered?: typeof MiniDOM.OrderedListNode; 9 | paragraph?: typeof MiniDOM.ParagraphNode; 10 | text?: typeof MiniDOM.TextNode; 11 | TreeNode?: typeof MiniDOM.TreeNode; 12 | RootNode?: typeof MiniDOM.RootNode; 13 | bullet?: typeof MiniDOM.UnorderedListNode; 14 | header?: typeof MiniDOM.HeaderNode; 15 | underline?: typeof MiniDOM.UnderlineNode; 16 | strikethrough?: typeof MiniDOM.StrikethroughNode; 17 | color?: typeof MiniDOM.ColorNode; 18 | bgcolor?: typeof MiniDOM.BackgroundColorNode; 19 | subscript?: typeof MiniDOM.SuperscriptNode; 20 | superscript?: typeof MiniDOM.SubscriptNode; 21 | SpanNode?: typeof MiniDOM.SpanNode; 22 | BlockNode?: typeof MiniDOM.BlockNode; 23 | image?: typeof MiniDOM.ImageNode; 24 | } 25 | export interface FormatOptions { 26 | rootNode?: string; 27 | rootClass?: string; 28 | } 29 | export declare class Formatter { 30 | formats: TypeRegistry; 31 | formatList: (typeof MiniDOM.TreeNode)[]; 32 | constructor(); 33 | sortRegistry(): void; 34 | checkPriorities(): void; 35 | transform(delta: any, opts?: FormatOptions): string; 36 | build(token: any): TreeNode; 37 | blockize(tokens: any): RootNode; 38 | transformAsync(delta: any, opts?: any): Promise; 39 | plainText(delta: any): any; 40 | plainTextAsync(delta: any): any; 41 | } 42 | -------------------------------------------------------------------------------- /src/miniDOM/text.ts: -------------------------------------------------------------------------------- 1 | import { SpanNode } from './span'; 2 | import { Token } from '../tokenize'; 3 | 4 | export class TextNode extends SpanNode { 5 | unescapedContents: string; 6 | 7 | constructor(opts: any = {}) { 8 | super(opts); 9 | this.unescapedContents = opts.contents || ''; 10 | } 11 | 12 | get contents() { 13 | if (this.unescapedContents.trim() === '') { 14 | return ' '; 15 | } else { 16 | return this.escape(this.unescapedContents); 17 | } 18 | } 19 | 20 | plainText() { 21 | return this.unescapedContents; 22 | } 23 | 24 | openTag() { 25 | return ''; 26 | } 27 | 28 | closeTag() { 29 | return ''; 30 | } 31 | 32 | appendChild() { 33 | throw new Error('TextNode cannot have chldren'); 34 | } 35 | 36 | isLeaf() { 37 | return true; 38 | } 39 | 40 | absorb(child) { 41 | if (child.type === this.type) { 42 | this.unescapedContents = this.unescapedContents.concat( 43 | child.unescapedContents, 44 | ); 45 | return null; 46 | } else { 47 | return child; 48 | } 49 | } 50 | 51 | escape(contents) { 52 | return contents 53 | .replace(/&/g, '&') 54 | .replace(//g, '>') 56 | .replace(/"/g, '"') 57 | .replace(/'/g, '''); 58 | } 59 | 60 | static matches(token: Token) { 61 | return ( 62 | token.type === 'text' && 63 | (token.contents === '' || token.contents) && 64 | typeof token.contents === 'string' && 65 | (token.attributes === undefined || token.attributes.image === undefined) 66 | ); 67 | } 68 | } 69 | 70 | TextNode.priority = 0; 71 | -------------------------------------------------------------------------------- /test/extension.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { Formatter, TypeRegistry, Token, SpanNode } from '../src/index'; 3 | import 'mocha'; 4 | 5 | class BetterItalicNode extends SpanNode { 6 | openTag() { 7 | return ''; 8 | } 9 | closeTag() { 10 | return ''; 11 | } 12 | static matches(token: Token) { 13 | return token.attributes && token.attributes.italic; 14 | } 15 | } 16 | BetterItalicNode.priority = 2; 17 | 18 | class PotatoNode extends SpanNode { 19 | openTag() { 20 | return ''; 21 | } 22 | closeTag() { 23 | return ''; 24 | } 25 | static matches(token: Token) { 26 | return token.attributes && token.attributes.potato; 27 | } 28 | } 29 | PotatoNode.priority = 102; 30 | 31 | class ExtendedFormatter extends Formatter { 32 | formats: TypeRegistry & { potato: typeof PotatoNode }; 33 | constructor() { 34 | super(); 35 | this.formats.italic = BetterItalicNode; 36 | this.formats.potato = PotatoNode; 37 | this.sortRegistry(); 38 | this.checkPriorities(); 39 | } 40 | } 41 | 42 | describe('extensions', () => { 43 | it('lets you extend the formatter by replacing formats', () => { 44 | const transform = new ExtendedFormatter(); 45 | const delta = { 46 | ops: [ 47 | { 48 | insert: 'word\n', 49 | attributes: { 50 | italic: true, 51 | }, 52 | }, 53 | ], 54 | }; 55 | const result = 56 | '

    word

    '; 57 | expect(transform.transform(delta)).to.equal(result); 58 | }); 59 | it('lets you extend the formatter by adding formats', () => { 60 | const transform = new ExtendedFormatter(); 61 | const delta = { 62 | ops: [ 63 | { 64 | insert: 'word\n', 65 | attributes: { 66 | potato: true, 67 | }, 68 | }, 69 | ], 70 | }; 71 | const result = '

    word

    '; 72 | expect(transform.transform(delta)).to.equal(result); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # delta-transform-html 2 | A transformer that pulls in ottypes/rich-text Deltas and emits HTML. 3 | 4 | ## supported formats: 5 | - [x] plain text 6 | - [x] unordered list 7 | - [x] ordered list 8 | - [ ] multilevel lists 9 | - [x] bold 10 | - [x] italic 11 | - [x] underscore 12 | - [x] superscript 13 | - [x] subscript 14 | - [ ] preformatted 15 | - [x] strikethrough 16 | - [x] color 17 | - [x] background-color 18 | - [x] link 19 | 20 | ## interface: 21 | 22 | import { Formatter } from 'delta-transform-html'; 23 | 24 | const testVal = { 25 | ops: [ 26 | {insert: 'multiline \n value'}, 27 | {insert: '\n'}, 28 | ], 29 | }; 30 | const htmlString = transformer.transform(testVal); 31 | 32 | You can request that the output html be wrapped in a root node by passing options to transform (and transformAsync) 33 | thusly: 34 | 35 | var htmlString = transformer.transform(testVal, { rootNode: 'my-element', rootClass: 'class1 class2'}); 36 | htmlString === ' ... stuff ... ' 37 | 38 | One can register custom formats or override formats by extending the Formatter class. If you replace `this.formats.italic` in 39 | the subclass constructor with a new italic format, you'll use that one instead. Similarly, you can create new formats and add them 40 | in the subclass constructor as well. 41 | Be sure to call `this.sortRegistry()` and `this.checkPriorities()` at the end of the constructor. 42 | 43 | FormatterClass should look a bit like: 44 | 45 | import { TreeNode } from 'delta-transform-html'; 46 | class FormatterClass extends TreeNode { 47 | openTag() { 48 | return ''; 49 | } 50 | closeTag() { 51 | return ''; 52 | } 53 | } 54 | FormatterClass.priority = 44 55 | 56 | Note that there's a special node type (text) that handles the actual text content in the token. 57 | The priority value on the constructor is used to figure out a stable inside - outside order. Given 58 | a text node like `{insert: 'foo', attributes: {bold: true, italic: true}}`, the `` tag will 59 | always be outside the `` tag, because the priority on ItalicNode is 2 and BoldNode is 1. 60 | -------------------------------------------------------------------------------- /dist/miniDOM/span.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.SpanNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _treeNode = require('./treeNode'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var SpanNode = exports.SpanNode = function (_TreeNode) { 19 | _inherits(SpanNode, _TreeNode); 20 | 21 | function SpanNode(opts) { 22 | _classCallCheck(this, SpanNode); 23 | 24 | var _this = _possibleConstructorReturn(this, (SpanNode.__proto__ || Object.getPrototypeOf(SpanNode)).call(this, opts)); 25 | 26 | _this.level = 'span'; 27 | return _this; 28 | } 29 | 30 | _createClass(SpanNode, [{ 31 | key: 'absorb', 32 | value: function absorb(child) { 33 | if (child.type === this.type) { 34 | this.children = this.children.concat(child.children); 35 | return null; 36 | } else { 37 | return child; 38 | } 39 | } 40 | }]); 41 | 42 | return SpanNode; 43 | }(_treeNode.TreeNode); 44 | 45 | SpanNode.priority = 100; -------------------------------------------------------------------------------- /dist/miniDOM/bold.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.BoldNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var BoldNode = exports.BoldNode = function (_SpanNode) { 19 | _inherits(BoldNode, _SpanNode); 20 | 21 | function BoldNode() { 22 | _classCallCheck(this, BoldNode); 23 | 24 | return _possibleConstructorReturn(this, (BoldNode.__proto__ || Object.getPrototypeOf(BoldNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(BoldNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.bold; 41 | } 42 | }]); 43 | 44 | return BoldNode; 45 | }(_span.SpanNode); 46 | 47 | BoldNode.priority = 1; -------------------------------------------------------------------------------- /dist/miniDOM/italic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.ItalicNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var ItalicNode = exports.ItalicNode = function (_SpanNode) { 19 | _inherits(ItalicNode, _SpanNode); 20 | 21 | function ItalicNode() { 22 | _classCallCheck(this, ItalicNode); 23 | 24 | return _possibleConstructorReturn(this, (ItalicNode.__proto__ || Object.getPrototypeOf(ItalicNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(ItalicNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.italic; 41 | } 42 | }]); 43 | 44 | return ItalicNode; 45 | }(_span.SpanNode); 46 | 47 | ItalicNode.priority = 2; -------------------------------------------------------------------------------- /dist/miniDOM/link.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.LinkNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var LinkNode = exports.LinkNode = function (_SpanNode) { 19 | _inherits(LinkNode, _SpanNode); 20 | 21 | function LinkNode() { 22 | _classCallCheck(this, LinkNode); 23 | 24 | return _possibleConstructorReturn(this, (LinkNode.__proto__ || Object.getPrototypeOf(LinkNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(LinkNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.link; 41 | } 42 | }]); 43 | 44 | return LinkNode; 45 | }(_span.SpanNode); 46 | 47 | LinkNode.priority = 11; -------------------------------------------------------------------------------- /dist/miniDOM/color.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.ColorNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var ColorNode = exports.ColorNode = function (_SpanNode) { 19 | _inherits(ColorNode, _SpanNode); 20 | 21 | function ColorNode() { 22 | _classCallCheck(this, ColorNode); 23 | 24 | return _possibleConstructorReturn(this, (ColorNode.__proto__ || Object.getPrototypeOf(ColorNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(ColorNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.color; 41 | } 42 | }]); 43 | 44 | return ColorNode; 45 | }(_span.SpanNode); 46 | 47 | ColorNode.priority = 5; -------------------------------------------------------------------------------- /dist/miniDOM/subscript.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.SubscriptNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var SubscriptNode = exports.SubscriptNode = function (_SpanNode) { 19 | _inherits(SubscriptNode, _SpanNode); 20 | 21 | function SubscriptNode() { 22 | _classCallCheck(this, SubscriptNode); 23 | 24 | return _possibleConstructorReturn(this, (SubscriptNode.__proto__ || Object.getPrototypeOf(SubscriptNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(SubscriptNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.sub; 41 | } 42 | }]); 43 | 44 | return SubscriptNode; 45 | }(_span.SpanNode); 46 | 47 | SubscriptNode.priority = 9; -------------------------------------------------------------------------------- /dist/miniDOM/underline.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.UnderlineNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var UnderlineNode = exports.UnderlineNode = function (_SpanNode) { 19 | _inherits(UnderlineNode, _SpanNode); 20 | 21 | function UnderlineNode() { 22 | _classCallCheck(this, UnderlineNode); 23 | 24 | return _possibleConstructorReturn(this, (UnderlineNode.__proto__ || Object.getPrototypeOf(UnderlineNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(UnderlineNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.underline; 41 | } 42 | }]); 43 | 44 | return UnderlineNode; 45 | }(_span.SpanNode); 46 | 47 | UnderlineNode.priority = 3; -------------------------------------------------------------------------------- /dist/miniDOM/superscript.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.SuperscriptNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var SuperscriptNode = exports.SuperscriptNode = function (_SpanNode) { 19 | _inherits(SuperscriptNode, _SpanNode); 20 | 21 | function SuperscriptNode() { 22 | _classCallCheck(this, SuperscriptNode); 23 | 24 | return _possibleConstructorReturn(this, (SuperscriptNode.__proto__ || Object.getPrototypeOf(SuperscriptNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(SuperscriptNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.super; 41 | } 42 | }]); 43 | 44 | return SuperscriptNode; 45 | }(_span.SpanNode); 46 | 47 | SuperscriptNode.priority = 10; -------------------------------------------------------------------------------- /dist/miniDOM/strikethrough.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.StrikethroughNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var StrikethroughNode = exports.StrikethroughNode = function (_SpanNode) { 19 | _inherits(StrikethroughNode, _SpanNode); 20 | 21 | function StrikethroughNode() { 22 | _classCallCheck(this, StrikethroughNode); 23 | 24 | return _possibleConstructorReturn(this, (StrikethroughNode.__proto__ || Object.getPrototypeOf(StrikethroughNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(StrikethroughNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.strike; 41 | } 42 | }]); 43 | 44 | return StrikethroughNode; 45 | }(_span.SpanNode); 46 | 47 | StrikethroughNode.priority = 8; -------------------------------------------------------------------------------- /test/tokenizer.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { tokenize } from '../src/index'; 3 | import 'mocha'; 4 | 5 | describe('tokenizer', () => { 6 | it('should convert single lines into a line and a break', () => { 7 | const ops = [ 8 | { 9 | insert: 'word\n', 10 | }, 11 | ]; 12 | const result = [ 13 | { type: 'text', contents: 'word', attributes: {} }, 14 | { type: 'linebreak', attributes: {} }, 15 | ]; 16 | expect(tokenize(ops)).to.deep.equal(result); 17 | }); 18 | 19 | it('should convert newlines into styled linebreaks', () => { 20 | const ops = [ 21 | { 22 | insert: 'listitem', 23 | }, 24 | { 25 | insert: '\n', 26 | attributes: { 27 | bullet: true, 28 | }, 29 | }, 30 | ]; 31 | const result = [ 32 | { type: 'text', contents: 'listitem', attributes: {} }, 33 | { type: 'linebreak', attributes: { bullet: true } }, 34 | ]; 35 | expect(tokenize(ops)).to.deep.equal(result); 36 | }); 37 | 38 | it('should convert inline newlines into multiple lines', () => { 39 | const ops = [ 40 | { 41 | insert: 'word\nword\n\nword\n', 42 | }, 43 | ]; 44 | const result = [ 45 | { type: 'text', contents: 'word', attributes: {} }, 46 | { type: 'linebreak', attributes: {} }, 47 | { type: 'text', contents: 'word', attributes: {} }, 48 | { type: 'linebreak', attributes: {} }, 49 | { type: 'linebreak', attributes: {} }, 50 | { type: 'text', contents: 'word', attributes: {} }, 51 | { type: 'linebreak', attributes: {} }, 52 | ]; 53 | expect(tokenize(ops)).to.deep.equal(result); 54 | }); 55 | 56 | it('add a linebreak if there is not one at the end of input', () => { 57 | const ops = [ 58 | { 59 | insert: 'word', 60 | }, 61 | ]; 62 | const result = [ 63 | { type: 'text', contents: 'word', attributes: {} }, 64 | { type: 'linebreak', attributes: {} }, 65 | ]; 66 | expect(tokenize(ops)).to.deep.equal(result); 67 | }); 68 | 69 | it('should handle inline newlines at the start of the line', () => { 70 | const ops = [ 71 | { 72 | insert: '\nword\n\nword\n', 73 | }, 74 | ]; 75 | const result = [ 76 | { type: 'linebreak', attributes: {} }, 77 | { type: 'text', contents: 'word', attributes: {} }, 78 | { type: 'linebreak', attributes: {} }, 79 | { type: 'linebreak', attributes: {} }, 80 | { type: 'text', contents: 'word', attributes: {} }, 81 | { type: 'linebreak', attributes: {} }, 82 | ]; 83 | expect(tokenize(ops)).to.deep.equal(result); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /dist/tokenize.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.tokenize = tokenize; 7 | function tokenize(ops) { 8 | var retVal = []; 9 | ops.forEach(function (op) { 10 | if (typeof op.insert !== 'string') { 11 | if (op.insert && op.insert.image) { 12 | retVal.push({ 13 | type: 'image', 14 | contents: op.insert, 15 | attributes: {} 16 | }); 17 | } else { 18 | retVal.push({ 19 | type: 'text', 20 | contents: op.insert, 21 | attributes: op.attributes || {} 22 | }); 23 | } 24 | } else if (op.insert === '\n') { 25 | retVal.push({ 26 | type: 'linebreak', 27 | attributes: op.attributes || {} 28 | }); 29 | } else if (op.insert.indexOf('\n') < 0) { 30 | retVal.push({ 31 | type: 'text', 32 | contents: op.insert, 33 | attributes: op.attributes || {} 34 | }); 35 | } else { 36 | var contents = op.insert; 37 | while (contents.length) { 38 | var nextNewline = contents.indexOf('\n'); 39 | if (nextNewline === -1) { 40 | retVal.push({ 41 | type: 'text', 42 | contents: contents, 43 | attributes: op.attributes || {} 44 | }); 45 | contents = ''; 46 | } else if (nextNewline === 0) { 47 | retVal.push({ 48 | type: 'linebreak', 49 | attributes: {} 50 | }); 51 | contents = contents.slice(nextNewline + 1); 52 | } else { 53 | retVal.push({ 54 | type: 'text', 55 | contents: contents.slice(0, nextNewline), 56 | attributes: op.attributes || {} 57 | }); 58 | retVal.push({ 59 | type: 'linebreak', 60 | attributes: {} 61 | }); 62 | contents = contents.slice(nextNewline + 1); 63 | } 64 | } 65 | } 66 | }); 67 | if (retVal.length > 0 && retVal.slice(-1)[0].type !== 'linebreak') { 68 | retVal.push({ 69 | type: 'linebreak', 70 | attributes: {} 71 | }); 72 | } 73 | return retVal; 74 | } -------------------------------------------------------------------------------- /dist/miniDOM/bgcolor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.BackgroundColorNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var BackgroundColorNode = exports.BackgroundColorNode = function (_SpanNode) { 19 | _inherits(BackgroundColorNode, _SpanNode); 20 | 21 | function BackgroundColorNode() { 22 | _classCallCheck(this, BackgroundColorNode); 23 | 24 | return _possibleConstructorReturn(this, (BackgroundColorNode.__proto__ || Object.getPrototypeOf(BackgroundColorNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(BackgroundColorNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }], [{ 38 | key: 'matches', 39 | value: function matches(token) { 40 | return token.attributes && token.attributes.bg; 41 | } 42 | }]); 43 | 44 | return BackgroundColorNode; 45 | }(_span.SpanNode); 46 | 47 | BackgroundColorNode.priority = 6; -------------------------------------------------------------------------------- /src/tokenize.ts: -------------------------------------------------------------------------------- 1 | export interface BasicToken { 2 | type: string; 3 | attributes: { 4 | [key: string]: any; 5 | }; 6 | } 7 | 8 | export interface TextToken extends BasicToken { 9 | type: 'text'; 10 | contents: string; 11 | } 12 | 13 | export interface LinebreakToken extends BasicToken { 14 | type: 'linebreak'; 15 | } 16 | 17 | export interface ImageToken extends BasicToken { 18 | type: 'image'; 19 | contents: { image: string }; 20 | } 21 | 22 | export type Token = TextToken | LinebreakToken | ImageToken; 23 | 24 | export function tokenize(ops) { 25 | const retVal: Token[] = []; 26 | ops.forEach(op => { 27 | if (typeof op.insert !== 'string') { 28 | if (op.insert && op.insert.image) { 29 | retVal.push({ 30 | type: 'image', 31 | contents: op.insert, 32 | attributes: {}, 33 | }); 34 | } else { 35 | retVal.push({ 36 | type: 'text', 37 | contents: op.insert, 38 | attributes: op.attributes || {}, 39 | }); 40 | } 41 | } else if (op.insert === '\n') { 42 | retVal.push({ 43 | type: 'linebreak', 44 | attributes: op.attributes || {}, 45 | }); 46 | } else if (op.insert.indexOf('\n') < 0) { 47 | retVal.push({ 48 | type: 'text', 49 | contents: op.insert, 50 | attributes: op.attributes || {}, 51 | }); 52 | } else { 53 | let contents = op.insert; 54 | while (contents.length) { 55 | const nextNewline = contents.indexOf('\n'); 56 | if (nextNewline === -1) { 57 | retVal.push({ 58 | type: 'text', 59 | contents: contents, 60 | attributes: op.attributes || {}, 61 | }); 62 | contents = ''; 63 | } else if (nextNewline === 0) { 64 | retVal.push({ 65 | type: 'linebreak', 66 | attributes: {}, // mid-insert linebreaks have no line-level styling 67 | }); 68 | contents = contents.slice(nextNewline + 1); 69 | } else { 70 | retVal.push({ 71 | type: 'text', 72 | contents: contents.slice(0, nextNewline), 73 | attributes: op.attributes || {}, 74 | }); 75 | retVal.push({ 76 | type: 'linebreak', 77 | attributes: {}, // mid-insert linebreaks have no line-level styling 78 | }); 79 | contents = contents.slice(nextNewline + 1); 80 | } 81 | } 82 | } 83 | }); 84 | if (retVal.length > 0 && retVal.slice(-1)[0].type !== 'linebreak') { 85 | retVal.push({ 86 | type: 'linebreak', 87 | attributes: {}, // mid-insert linebreaks have no line-level styling 88 | }); 89 | } 90 | return retVal; 91 | } 92 | -------------------------------------------------------------------------------- /dist/miniDOM/header.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.HeaderNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _block = require('./block'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var HeaderNode = exports.HeaderNode = function (_BlockNode) { 19 | _inherits(HeaderNode, _BlockNode); 20 | 21 | function HeaderNode() { 22 | _classCallCheck(this, HeaderNode); 23 | 24 | return _possibleConstructorReturn(this, (HeaderNode.__proto__ || Object.getPrototypeOf(HeaderNode)).apply(this, arguments)); 25 | } 26 | 27 | _createClass(HeaderNode, [{ 28 | key: 'openTag', 29 | value: function openTag() { 30 | return ''; 31 | } 32 | }, { 33 | key: 'closeTag', 34 | value: function closeTag() { 35 | return ''; 36 | } 37 | }, { 38 | key: 'absorb', 39 | value: function absorb(child) { 40 | return child; 41 | } 42 | }], [{ 43 | key: 'matches', 44 | value: function matches(token) { 45 | return token.type === 'linebreak' && token.attributes.header; 46 | } 47 | }]); 48 | 49 | return HeaderNode; 50 | }(_block.BlockNode); 51 | 52 | HeaderNode.priority = 21; -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "align": [true, "parameters", "statements"], 4 | "ban": false, 5 | "class-name": true, 6 | "comment-format": [true, "check-space"], 7 | "curly": true, 8 | "object-curly-spacing": true, 9 | "eofline": true, 10 | "forin": false, 11 | "indent": [true, "spaces"], 12 | "linebreak-style": [true, "LF"], 13 | "interface-name": false, 14 | "jsdoc-format": true, 15 | "label-position": true, 16 | "label-undefined": true, 17 | "max-line-length": false, 18 | "member-ordering": [ 19 | true, 20 | "public-before-private", 21 | "instance-before-static", 22 | "variables-before-functions" 23 | ], 24 | "no-any": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 28 | "no-construct": true, 29 | "no-constructor-vars": false, 30 | "no-debugger": true, 31 | "no-duplicate-key": true, 32 | "no-shadowed-variable": true, 33 | "no-duplicate-variable": true, 34 | "no-empty": true, 35 | "no-eval": true, 36 | "no-internal-module": true, 37 | "no-require-imports": true, 38 | "no-string-literal": false, 39 | "no-switch-case-fall-through": false, 40 | "trailing-comma": { 41 | "singleline": "never", 42 | "multiline": "always" 43 | }, 44 | "no-trailing-whitespace": true, 45 | "no-unreachable": true, 46 | "no-unused-expression": true, 47 | "no-unused-variable": [true, "react"], 48 | "no-use-before-declare": true, 49 | "no-var-keyword": true, 50 | "no-var-requires": false, 51 | "one-line": [ 52 | true, 53 | "check-open-brace", 54 | "check-catch", 55 | "check-else", 56 | "check-whitespace" 57 | ], 58 | "quotemark": [true, "single", "jsx-double"], 59 | "radix": true, 60 | "semicolon": [true, "always"], 61 | "switch-default": false, 62 | "triple-equals": [true, "allow-null-check"], 63 | "typedef": [ 64 | false, 65 | "call-signature", 66 | "parameter", 67 | "property-declaration", 68 | "member-variable-declaration" 69 | ], 70 | "typedef-whitespace": [ 71 | true, 72 | { 73 | "call-signature": "nospace", 74 | "index-signature": "nospace", 75 | "parameter": "nospace", 76 | "property-declaration": "nospace", 77 | "variable-declaration": "nospace" 78 | } 79 | ], 80 | "use-strict": [false, "check-module", "check-function"], 81 | "variable-name": [true, "ban-keywords"], 82 | "whitespace": [ 83 | true, 84 | "check-branch", 85 | "check-decl", 86 | "check-operator", 87 | "check-separator", 88 | "check-type" 89 | ] 90 | }, 91 | "rulesDirectory": ["node_modules/tslint-eslint-rules/dist/rules"] 92 | } 93 | -------------------------------------------------------------------------------- /src/miniDOM/treeNode.ts: -------------------------------------------------------------------------------- 1 | import { Token } from '../tokenize'; 2 | 3 | export interface TreeAttributes {} 4 | 5 | export class TreeNode { 6 | static priority: number; 7 | children: TreeNode[]; 8 | attributes: any; 9 | type: string; 10 | contents: string; 11 | // level: string; 12 | level: 'span' | 'block'; 13 | promisePlainContents?: () => Promise; 14 | 15 | constructor(opts: { attributes?: any } = {}) { 16 | this.children = []; 17 | this.attributes = Object.assign({}, opts.attributes); 18 | this.type = this.constructor.name; 19 | } 20 | 21 | dfsTraverse() { 22 | return this.children.reduce((prev: TreeNode[], curr) => prev.concat(curr), [ 23 | this, 24 | ]); 25 | } 26 | 27 | openTag() { 28 | return ''; 29 | } 30 | 31 | closeTag() { 32 | return ''; 33 | } 34 | 35 | promiseContents() { 36 | return Promise.resolve(this.contents || ''); 37 | } 38 | 39 | toHTMLAsync(indentLevel = 0) { 40 | if (this.isLeaf()) { 41 | return this.promiseContents().then(contents => { 42 | return `${this.openTag()}${contents}${this.closeTag()}`; // eslint-disable-line max-len 43 | }); 44 | } else { 45 | return Promise.all( 46 | this.children.map(c => c.toHTMLAsync(0)), 47 | ).then(childHTML => { 48 | return `${this.openTag()}${childHTML.join('')}${this.closeTag()}`; // eslint-disable-line max-len 49 | }); 50 | } 51 | } 52 | 53 | plainTextAsync() { 54 | if (this.isLeaf()) { 55 | if (this.promisePlainContents) { 56 | return this.promisePlainContents(); 57 | } else { 58 | return Promise.resolve(this.plainText()); 59 | } 60 | } else { 61 | return Promise.all(this.children.map(c => c.plainTextAsync())).then(c => 62 | c.join(''), 63 | ); 64 | } 65 | } 66 | 67 | plainText() { 68 | return this.children.map(c => c.plainText()).join(''); 69 | } 70 | 71 | toHTML(indentLevel = 0) { 72 | if (this.isLeaf()) { 73 | return `${this.openTag()}${this.contents}${this.closeTag()}`; // eslint-disable-line max-len 74 | } else { 75 | return `${this.openTag()}${this.children 76 | .map(c => c.toHTML(0)) 77 | .join('')}${this.closeTag()}`; // eslint-disable-line max-len 78 | } 79 | } 80 | 81 | isLeaf() { 82 | return false; 83 | } 84 | 85 | appendChild(child) { 86 | this.children.push(child); 87 | } 88 | 89 | absorb(child) { 90 | this.children.push(child); 91 | return null; 92 | } 93 | 94 | toJSON() { 95 | return { 96 | type: this.type, 97 | level: this.level, 98 | children: this.children, 99 | attributes: this.attributes, 100 | contents: this.contents, 101 | }; 102 | } 103 | 104 | get priority() { 105 | return this.constructor['priority']; 106 | } 107 | 108 | static matches(token: Token) { 109 | return false; 110 | } 111 | } 112 | 113 | TreeNode.priority = -2; 114 | -------------------------------------------------------------------------------- /dist/miniDOM/root.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.RootNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _treeNode = require('./treeNode'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var RootNode = exports.RootNode = function (_TreeNode) { 19 | _inherits(RootNode, _TreeNode); 20 | 21 | function RootNode() { 22 | var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 23 | 24 | _classCallCheck(this, RootNode); 25 | 26 | return _possibleConstructorReturn(this, (RootNode.__proto__ || Object.getPrototypeOf(RootNode)).call(this, opts)); 27 | } 28 | 29 | _createClass(RootNode, [{ 30 | key: 'absorb', 31 | value: function absorb(child) { 32 | var remains = child; 33 | if (this.children.length > 0) { 34 | remains = this.children[this.children.length - 1].absorb(child); 35 | } 36 | if (remains !== null) { 37 | this.children.push(remains); 38 | } 39 | return null; 40 | } 41 | }, { 42 | key: 'toHTML', 43 | value: function toHTML() { 44 | return this.children.map(function (c) { 45 | return c.toHTML(0); 46 | }).join(''); // eslint-disable-line max-len 47 | } 48 | }, { 49 | key: 'toHTMLAsync', 50 | value: function toHTMLAsync() { 51 | return Promise.all(this.children.map(function (c) { 52 | return c.toHTMLAsync(0); 53 | })).then(function (childHTML) { 54 | return childHTML.join(''); // eslint-disable-line max-len 55 | }); 56 | } 57 | }]); 58 | 59 | return RootNode; 60 | }(_treeNode.TreeNode); 61 | 62 | RootNode.priority = -1; -------------------------------------------------------------------------------- /dist/miniDOM/image.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.ImageNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var ImageNode = exports.ImageNode = function (_SpanNode) { 19 | _inherits(ImageNode, _SpanNode); 20 | 21 | function ImageNode() { 22 | var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 23 | 24 | _classCallCheck(this, ImageNode); 25 | 26 | var _this = _possibleConstructorReturn(this, (ImageNode.__proto__ || Object.getPrototypeOf(ImageNode)).call(this, opts)); 27 | 28 | if (opts.contents && opts.contents.image) { 29 | _this.imageUrl = opts.contents.image; 30 | } else { 31 | _this.imageUrl = opts.attributes.image; 32 | } 33 | _this.contents = ''; 34 | return _this; 35 | } 36 | 37 | _createClass(ImageNode, [{ 38 | key: 'plainText', 39 | value: function plainText() { 40 | return 'IMAGE: ' + this.imageUrl; 41 | } 42 | }, { 43 | key: 'isLeaf', 44 | value: function isLeaf() { 45 | return true; 46 | } 47 | }, { 48 | key: 'openTag', 49 | value: function openTag() { 50 | return ''; 51 | } 52 | }, { 53 | key: 'closeTag', 54 | value: function closeTag() { 55 | return ''; 56 | } 57 | }], [{ 58 | key: 'matches', 59 | value: function matches(token) { 60 | return token.type === 'image'; 61 | // (token.contents && token.contents.image) || 62 | // (token.attributes && token.attributes.image) 63 | // ); 64 | } 65 | }]); 66 | 67 | return ImageNode; 68 | }(_span.SpanNode); 69 | 70 | ImageNode.priority = 99; -------------------------------------------------------------------------------- /dist/miniDOM/orderedList.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.OrderedListNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _block = require('./block'); 11 | 12 | var _listItem = require('./listItem'); 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 17 | 18 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 19 | 20 | var OrderedListNode = exports.OrderedListNode = function (_BlockNode) { 21 | _inherits(OrderedListNode, _BlockNode); 22 | 23 | function OrderedListNode(opts) { 24 | _classCallCheck(this, OrderedListNode); 25 | 26 | var _this = _possibleConstructorReturn(this, (OrderedListNode.__proto__ || Object.getPrototypeOf(OrderedListNode)).call(this, opts)); 27 | 28 | _this.children = [new _listItem.ListItemNode(opts)]; 29 | return _this; 30 | } 31 | 32 | _createClass(OrderedListNode, [{ 33 | key: 'appendChild', 34 | value: function appendChild(node) { 35 | this.children[0].appendChild(node); 36 | } 37 | }, { 38 | key: 'openTag', 39 | value: function openTag() { 40 | return '
      '; 41 | } 42 | }, { 43 | key: 'closeTag', 44 | value: function closeTag() { 45 | return '
    '; 46 | } 47 | }, { 48 | key: 'absorb', 49 | value: function absorb(child) { 50 | if (child.type === this.type) { 51 | this.children = this.children.concat(child.children); 52 | return null; 53 | } else { 54 | return child; 55 | } 56 | } 57 | }], [{ 58 | key: 'matches', 59 | value: function matches(token) { 60 | return token.type === 'linebreak' && token.attributes && (token.attributes.list === 'ordered' || token.attributes.ordered === true); 61 | } 62 | }]); 63 | 64 | return OrderedListNode; 65 | }(_block.BlockNode); 66 | 67 | OrderedListNode.priority = 30; -------------------------------------------------------------------------------- /dist/miniDOM/unorderedList.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.UnorderedListNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _block = require('./block'); 11 | 12 | var _listItem = require('./listItem'); 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 17 | 18 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 19 | 20 | var UnorderedListNode = exports.UnorderedListNode = function (_BlockNode) { 21 | _inherits(UnorderedListNode, _BlockNode); 22 | 23 | function UnorderedListNode(opts) { 24 | _classCallCheck(this, UnorderedListNode); 25 | 26 | var _this = _possibleConstructorReturn(this, (UnorderedListNode.__proto__ || Object.getPrototypeOf(UnorderedListNode)).call(this, opts)); 27 | 28 | _this.children = [new _listItem.ListItemNode(opts)]; 29 | return _this; 30 | } 31 | 32 | _createClass(UnorderedListNode, [{ 33 | key: 'appendChild', 34 | value: function appendChild(node) { 35 | this.children[0].appendChild(node); 36 | } 37 | }, { 38 | key: 'openTag', 39 | value: function openTag() { 40 | return '
      '; 41 | } 42 | }, { 43 | key: 'closeTag', 44 | value: function closeTag() { 45 | return '
    '; 46 | } 47 | }, { 48 | key: 'absorb', 49 | value: function absorb(child) { 50 | if (child.type === this.type) { 51 | this.children = this.children.concat(child.children); 52 | return null; 53 | } else { 54 | return child; 55 | } 56 | } 57 | }], [{ 58 | key: 'matches', 59 | value: function matches(token) { 60 | return token.type === 'linebreak' && token.attributes && (token.attributes.list === 'bullet' || token.attributes.bullet === true); 61 | } 62 | }]); 63 | 64 | return UnorderedListNode; 65 | }(_block.BlockNode); 66 | 67 | UnorderedListNode.priority = 35; -------------------------------------------------------------------------------- /dist/miniDOM/block.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.BlockNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 11 | 12 | var _treeNode = require('./treeNode'); 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 17 | 18 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 19 | 20 | var BlockNode = exports.BlockNode = function (_TreeNode) { 21 | _inherits(BlockNode, _TreeNode); 22 | 23 | function BlockNode(opts) { 24 | _classCallCheck(this, BlockNode); 25 | 26 | var _this = _possibleConstructorReturn(this, (BlockNode.__proto__ || Object.getPrototypeOf(BlockNode)).call(this, opts)); 27 | 28 | _this.level = 'block'; 29 | return _this; 30 | } 31 | 32 | _createClass(BlockNode, [{ 33 | key: 'plainTextAsync', 34 | value: function plainTextAsync() { 35 | return Promise.all(this.children.map(function (child) { 36 | return child.plainTextAsync(); 37 | })).then(function (c) { 38 | return c.join('') + '\n'; 39 | }); 40 | } 41 | }, { 42 | key: 'plainText', 43 | value: function plainText() { 44 | return _get(BlockNode.prototype.__proto__ || Object.getPrototypeOf(BlockNode.prototype), 'plainText', this).call(this) + '\n'; 45 | } 46 | }, { 47 | key: 'appendChild', 48 | value: function appendChild(child) { 49 | if (this.children.length === 0) { 50 | this.children.push(child); 51 | } else { 52 | var remains = this.children[this.children.length - 1].absorb(child); 53 | if (remains !== null) { 54 | this.children.push(remains); 55 | } 56 | } 57 | } 58 | }]); 59 | 60 | return BlockNode; 61 | }(_treeNode.TreeNode); 62 | 63 | BlockNode.priority = 101; -------------------------------------------------------------------------------- /dist/miniDOM/listItem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.ListItemNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 11 | 12 | var _treeNode = require('./treeNode'); 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 17 | 18 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 19 | 20 | var ListItemNode = exports.ListItemNode = function (_TreeNode) { 21 | _inherits(ListItemNode, _TreeNode); 22 | 23 | function ListItemNode() { 24 | _classCallCheck(this, ListItemNode); 25 | 26 | return _possibleConstructorReturn(this, (ListItemNode.__proto__ || Object.getPrototypeOf(ListItemNode)).apply(this, arguments)); 27 | } 28 | 29 | _createClass(ListItemNode, [{ 30 | key: 'openTag', 31 | value: function openTag() { 32 | return '
  • '; 33 | } 34 | }, { 35 | key: 'closeTag', 36 | value: function closeTag() { 37 | return '
  • '; 38 | } 39 | }, { 40 | key: 'absorb', 41 | value: function absorb(child) { 42 | return child; 43 | } 44 | }, { 45 | key: 'plainTextAsync', 46 | value: function plainTextAsync() { 47 | return _get(ListItemNode.prototype.__proto__ || Object.getPrototypeOf(ListItemNode.prototype), 'plainTextAsync', this).call(this).then(function (t) { 48 | return '* ' + t + '\n'; 49 | }); 50 | } 51 | }, { 52 | key: 'plainText', 53 | value: function plainText() { 54 | return '* ' + _get(ListItemNode.prototype.__proto__ || Object.getPrototypeOf(ListItemNode.prototype), 'plainText', this).call(this) + '\n'; 55 | } 56 | }], [{ 57 | key: 'matches', 58 | value: function matches() { 59 | var token = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 60 | 61 | return false; 62 | // return (token.attributes && token.attributes.list); 63 | } 64 | }]); 65 | 66 | return ListItemNode; 67 | }(_treeNode.TreeNode); 68 | 69 | ListItemNode.priority = 20; -------------------------------------------------------------------------------- /dist/miniDOM/text.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.TextNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _span = require('./span'); 11 | 12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 13 | 14 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 15 | 16 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 17 | 18 | var TextNode = exports.TextNode = function (_SpanNode) { 19 | _inherits(TextNode, _SpanNode); 20 | 21 | function TextNode() { 22 | var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 23 | 24 | _classCallCheck(this, TextNode); 25 | 26 | var _this = _possibleConstructorReturn(this, (TextNode.__proto__ || Object.getPrototypeOf(TextNode)).call(this, opts)); 27 | 28 | _this.unescapedContents = opts.contents || ''; 29 | return _this; 30 | } 31 | 32 | _createClass(TextNode, [{ 33 | key: 'plainText', 34 | value: function plainText() { 35 | return this.unescapedContents; 36 | } 37 | }, { 38 | key: 'openTag', 39 | value: function openTag() { 40 | return ''; 41 | } 42 | }, { 43 | key: 'closeTag', 44 | value: function closeTag() { 45 | return ''; 46 | } 47 | }, { 48 | key: 'appendChild', 49 | value: function appendChild() { 50 | throw new Error('TextNode cannot have chldren'); 51 | } 52 | }, { 53 | key: 'isLeaf', 54 | value: function isLeaf() { 55 | return true; 56 | } 57 | }, { 58 | key: 'absorb', 59 | value: function absorb(child) { 60 | if (child.type === this.type) { 61 | this.unescapedContents = this.unescapedContents.concat(child.unescapedContents); 62 | return null; 63 | } else { 64 | return child; 65 | } 66 | } 67 | }, { 68 | key: 'escape', 69 | value: function escape(contents) { 70 | return contents.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); 71 | } 72 | }, { 73 | key: 'contents', 74 | get: function get() { 75 | if (this.unescapedContents.trim() === '') { 76 | return ' '; 77 | } else { 78 | return this.escape(this.unescapedContents); 79 | } 80 | } 81 | }], [{ 82 | key: 'matches', 83 | value: function matches(token) { 84 | return token.type === 'text' && (token.contents === '' || token.contents) && typeof token.contents === 'string' && (token.attributes === undefined || token.attributes.image === undefined); 85 | } 86 | }]); 87 | 88 | return TextNode; 89 | }(_span.SpanNode); 90 | 91 | TextNode.priority = 0; -------------------------------------------------------------------------------- /dist/miniDOM/paragraph.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.ParagraphNode = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 11 | 12 | var _block = require('./block'); 13 | 14 | var _text = require('./text'); 15 | 16 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 17 | 18 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 19 | 20 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 21 | 22 | var ParagraphNode = exports.ParagraphNode = function (_BlockNode) { 23 | _inherits(ParagraphNode, _BlockNode); 24 | 25 | function ParagraphNode() { 26 | _classCallCheck(this, ParagraphNode); 27 | 28 | return _possibleConstructorReturn(this, (ParagraphNode.__proto__ || Object.getPrototypeOf(ParagraphNode)).apply(this, arguments)); 29 | } 30 | 31 | _createClass(ParagraphNode, [{ 32 | key: 'openTag', 33 | value: function openTag() { 34 | return '

    '; 35 | } 36 | }, { 37 | key: 'closeTag', 38 | value: function closeTag() { 39 | return '

    '; 40 | } 41 | }, { 42 | key: 'absorb', 43 | value: function absorb(child) { 44 | return child; 45 | } 46 | }, { 47 | key: 'toHTMLAsync', 48 | value: function toHTMLAsync() { 49 | var _this2 = this; 50 | 51 | var indentLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 52 | 53 | if (this.children.length === 0) { 54 | this.children.push(new _text.TextNode({ type: 'text', attributes: {}, contents: '' })); 55 | } 56 | return Promise.all(this.children.map(function (c) { 57 | return c.toHTMLAsync(0); 58 | })).then(function (childHTML) { 59 | return '' + _this2.openTag() + childHTML.join('') + _this2.closeTag(); // eslint-disable-line max-len 60 | }); 61 | } 62 | }, { 63 | key: 'toHTML', 64 | value: function toHTML() { 65 | var indentLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 66 | 67 | if (this.children.length === 0) { 68 | this.children.push(new _text.TextNode({ type: 'text', attributes: {}, contents: '' })); 69 | } 70 | // if (this.children.length === 0) { 71 | // return `${new Array(0).join(' ')}${this.openTag()} ${this.closeTag()}`; 72 | // } else { 73 | return _get(ParagraphNode.prototype.__proto__ || Object.getPrototypeOf(ParagraphNode.prototype), 'toHTML', this).call(this, indentLevel); 74 | // } 75 | } 76 | }], [{ 77 | key: 'matches', 78 | value: function matches(token) { 79 | return token.type === 'linebreak' && (!!token.attributes || token.attributes.list === undefined && token.attributes.header === undefined); 80 | } 81 | }]); 82 | 83 | return ParagraphNode; 84 | }(_block.BlockNode); 85 | 86 | ParagraphNode.priority = 19; -------------------------------------------------------------------------------- /src/formatter.ts: -------------------------------------------------------------------------------- 1 | import * as MiniDOM from './miniDOM'; 2 | import { RootNode, TreeNode } from './miniDOM'; 3 | import { tokenize } from './tokenize'; 4 | export interface TypeRegistry { 5 | bold?: typeof MiniDOM.BoldNode; 6 | italic?: typeof MiniDOM.ItalicNode; 7 | link?: typeof MiniDOM.LinkNode; 8 | listItem?: typeof MiniDOM.ListItemNode; 9 | ordered?: typeof MiniDOM.OrderedListNode; 10 | paragraph?: typeof MiniDOM.ParagraphNode; 11 | text?: typeof MiniDOM.TextNode; 12 | TreeNode?: typeof MiniDOM.TreeNode; 13 | RootNode?: typeof MiniDOM.RootNode; 14 | bullet?: typeof MiniDOM.UnorderedListNode; 15 | header?: typeof MiniDOM.HeaderNode; 16 | underline?: typeof MiniDOM.UnderlineNode; 17 | strikethrough?: typeof MiniDOM.StrikethroughNode; 18 | color?: typeof MiniDOM.ColorNode; 19 | bgcolor?: typeof MiniDOM.BackgroundColorNode; 20 | subscript?: typeof MiniDOM.SuperscriptNode; 21 | superscript?: typeof MiniDOM.SubscriptNode; 22 | SpanNode?: typeof MiniDOM.SpanNode; 23 | BlockNode?: typeof MiniDOM.BlockNode; 24 | image?: typeof MiniDOM.ImageNode; 25 | } 26 | 27 | export interface FormatOptions { 28 | rootNode?: string; 29 | rootClass?: string; 30 | } 31 | 32 | export class Formatter { 33 | formats: TypeRegistry = { 34 | bold: MiniDOM.BoldNode, 35 | italic: MiniDOM.ItalicNode, 36 | link: MiniDOM.LinkNode, 37 | listItem: MiniDOM.ListItemNode, 38 | ordered: MiniDOM.OrderedListNode, 39 | paragraph: MiniDOM.ParagraphNode, 40 | text: MiniDOM.TextNode, 41 | TreeNode: MiniDOM.TreeNode, 42 | RootNode: MiniDOM.RootNode, 43 | bullet: MiniDOM.UnorderedListNode, 44 | header: MiniDOM.HeaderNode, 45 | underline: MiniDOM.UnderlineNode, 46 | strikethrough: MiniDOM.StrikethroughNode, 47 | color: MiniDOM.ColorNode, 48 | bgcolor: MiniDOM.BackgroundColorNode, 49 | subscript: MiniDOM.SuperscriptNode, 50 | superscript: MiniDOM.SubscriptNode, 51 | SpanNode: MiniDOM.SpanNode, 52 | BlockNode: MiniDOM.BlockNode, 53 | image: MiniDOM.ImageNode, 54 | }; 55 | formatList: (typeof MiniDOM.TreeNode)[] = []; 56 | constructor() { 57 | this.sortRegistry(); 58 | this.checkPriorities(); 59 | } 60 | sortRegistry() { 61 | this.formatList = Object.keys(this.formats) 62 | .sort((a, b) => this.formats[b].priority - this.formats[a].priority) 63 | .map(n => this.formats[n]); 64 | } 65 | checkPriorities() { 66 | const seen = {}; 67 | Object.keys(this.formats).forEach(key => { 68 | if (seen[this.formats[key].priority]) { 69 | console.log( 70 | `ERROR: conflict between ${key} and ${seen[ 71 | this.formats[key].priority 72 | ]}`, 73 | ); 74 | } 75 | seen[this.formats[key].priority] = key; 76 | }); 77 | } 78 | 79 | transform(delta, opts: FormatOptions = {}) { 80 | let openTag = ''; 81 | let closeTag = ''; 82 | if (opts.rootNode) { 83 | if (opts.rootClass) { 84 | openTag = `<${opts.rootNode} class="${opts.rootClass}">`; 85 | } else { 86 | openTag = `<${opts.rootNode}>`; 87 | } 88 | closeTag = ``; 89 | } 90 | return `${openTag}${this.blockize( 91 | tokenize(delta.ops), 92 | ).toHTML()}${closeTag}`; 93 | } 94 | 95 | build(token) { 96 | const matchingFormats = this.formatList 97 | .filter(format => format.matches(token)) 98 | .map(N => new N(token)); 99 | if (matchingFormats.length === 0) { 100 | // console.log(`token ${JSON.stringify(token)} has no matching formats`); 101 | return new this.formats.TreeNode(); 102 | } 103 | const retVal = matchingFormats.shift(); 104 | matchingFormats.reduce((prev, curr) => { 105 | prev.children = [curr]; // eslint-disable-line no-param-reassign 106 | return curr; 107 | }, retVal); 108 | return retVal; 109 | } 110 | 111 | blockize(tokens) { 112 | const RN: typeof MiniDOM.RootNode = this.formats.RootNode; 113 | const retVal = new RN(); 114 | let childList = []; 115 | tokens.forEach(token => { 116 | if (token.type === 'linebreak') { 117 | const blockArray = this.formatList.filter(f => f.matches(token)); 118 | const currentBlock = new blockArray[0](token); 119 | childList.forEach(child => currentBlock.appendChild(this.build(child))); 120 | retVal.absorb(currentBlock); 121 | childList = []; 122 | } else { 123 | childList.push(token); 124 | } 125 | }); 126 | return retVal; 127 | } 128 | 129 | transformAsync(delta, opts: any = {}) { 130 | return this.blockize(tokenize(delta.ops)) 131 | .toHTMLAsync() 132 | .then(v => { 133 | let openTag = ''; 134 | let closeTag = ''; 135 | if (opts.rootNode) { 136 | if (opts.rootClass) { 137 | openTag = `<${opts.rootNode} class="${opts.rootClass}">`; 138 | } else { 139 | openTag = `<${opts.rootNode}>`; 140 | } 141 | closeTag = ``; 142 | } 143 | return `${openTag}${v}${closeTag}`; 144 | }); 145 | } 146 | plainText(delta) { 147 | return this.blockize(tokenize(delta.ops)).plainText(); 148 | } 149 | plainTextAsync(delta) { 150 | return this.blockize(tokenize(delta.ops)).plainTextAsync(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /dist/miniDOM/treeNode.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 8 | 9 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 10 | 11 | var TreeNode = exports.TreeNode = function () { 12 | function TreeNode() { 13 | var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 14 | 15 | _classCallCheck(this, TreeNode); 16 | 17 | this.children = []; 18 | this.attributes = Object.assign({}, opts.attributes); 19 | this.type = this.constructor.name; 20 | } 21 | 22 | _createClass(TreeNode, [{ 23 | key: 'dfsTraverse', 24 | value: function dfsTraverse() { 25 | return this.children.reduce(function (prev, curr) { 26 | return prev.concat(curr); 27 | }, [this]); 28 | } 29 | }, { 30 | key: 'openTag', 31 | value: function openTag() { 32 | return ''; 33 | } 34 | }, { 35 | key: 'closeTag', 36 | value: function closeTag() { 37 | return ''; 38 | } 39 | }, { 40 | key: 'promiseContents', 41 | value: function promiseContents() { 42 | return Promise.resolve(this.contents || ''); 43 | } 44 | }, { 45 | key: 'toHTMLAsync', 46 | value: function toHTMLAsync() { 47 | var _this = this; 48 | 49 | var indentLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 50 | 51 | if (this.isLeaf()) { 52 | return this.promiseContents().then(function (contents) { 53 | return '' + _this.openTag() + contents + _this.closeTag(); // eslint-disable-line max-len 54 | }); 55 | } else { 56 | return Promise.all(this.children.map(function (c) { 57 | return c.toHTMLAsync(0); 58 | })).then(function (childHTML) { 59 | return '' + _this.openTag() + childHTML.join('') + _this.closeTag(); // eslint-disable-line max-len 60 | }); 61 | } 62 | } 63 | }, { 64 | key: 'plainTextAsync', 65 | value: function plainTextAsync() { 66 | if (this.isLeaf()) { 67 | if (this.promisePlainContents) { 68 | return this.promisePlainContents(); 69 | } else { 70 | return Promise.resolve(this.plainText()); 71 | } 72 | } else { 73 | return Promise.all(this.children.map(function (c) { 74 | return c.plainTextAsync(); 75 | })).then(function (c) { 76 | return c.join(''); 77 | }); 78 | } 79 | } 80 | }, { 81 | key: 'plainText', 82 | value: function plainText() { 83 | return this.children.map(function (c) { 84 | return c.plainText(); 85 | }).join(''); 86 | } 87 | }, { 88 | key: 'toHTML', 89 | value: function toHTML() { 90 | var indentLevel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 91 | 92 | if (this.isLeaf()) { 93 | return '' + this.openTag() + this.contents + this.closeTag(); // eslint-disable-line max-len 94 | } else { 95 | return '' + this.openTag() + this.children.map(function (c) { 96 | return c.toHTML(0); 97 | }).join('') + this.closeTag(); // eslint-disable-line max-len 98 | } 99 | } 100 | }, { 101 | key: 'isLeaf', 102 | value: function isLeaf() { 103 | return false; 104 | } 105 | }, { 106 | key: 'appendChild', 107 | value: function appendChild(child) { 108 | this.children.push(child); 109 | } 110 | }, { 111 | key: 'absorb', 112 | value: function absorb(child) { 113 | this.children.push(child); 114 | return null; 115 | } 116 | }, { 117 | key: 'toJSON', 118 | value: function toJSON() { 119 | return { 120 | type: this.type, 121 | level: this.level, 122 | children: this.children, 123 | attributes: this.attributes, 124 | contents: this.contents 125 | }; 126 | } 127 | }, { 128 | key: 'priority', 129 | get: function get() { 130 | return this.constructor['priority']; 131 | } 132 | }], [{ 133 | key: 'matches', 134 | value: function matches(token) { 135 | return false; 136 | } 137 | }]); 138 | 139 | return TreeNode; 140 | }(); 141 | 142 | TreeNode.priority = -2; -------------------------------------------------------------------------------- /test/integration.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha*/ 2 | 3 | import { expect } from 'chai'; 4 | import { Formatter } from '../src/index'; 5 | import 'mocha'; 6 | 7 | const testVal = { 8 | ops: [ 9 | { insert: 'multiline\nvalue' }, 10 | { insert: '\n' }, 11 | { insert: 'simple text' }, 12 | { insert: ' with a\n' }, 13 | { insert: '\nfollowing text\n\n' }, 14 | { insert: 'bulleted list one', attributes: { link: 'linkTarget' } }, 15 | { insert: '\n', attributes: { list: 'bullet' } }, 16 | { insert: 'bulleted list two' }, 17 | { insert: '\n', attributes: { list: 'bullet' } }, 18 | { insert: 'bulleted list three' }, 19 | { insert: '\n', attributes: { list: 'bullet' } }, 20 | { insert: 'numbered list one' }, 21 | { insert: '\n', attributes: { list: 'ordered' } }, 22 | { insert: 'numbered list two' }, 23 | { insert: '\n', attributes: { ordered: true } }, 24 | { insert: 'numbered list three' }, 25 | { insert: '\n', attributes: { list: 'ordered' } }, 26 | { insert: 'header two' }, 27 | { insert: '\n', attributes: { header: 2 } }, 28 | { insert: 'underlined header one', attributes: { underline: true } }, 29 | { insert: '\n', attributes: { header: 1 } }, 30 | { insert: 'red', attributes: { color: 'red' } }, 31 | { insert: 'bgred', attributes: { bg: 'red' } }, 32 | { insert: 'strikethru', attributes: { strike: true } }, 33 | { insert: '\n' }, 34 | { insert: { image: 'IMAGEURL' } }, 35 | { insert: 'escaped HTML & < > " \' &' }, 36 | { insert: '\n' }, 37 | { insert: 'empty newline should have nbsp (four after this)\n\n\n' }, 38 | { insert: '\n' }, 39 | { insert: '\n' }, 40 | { 41 | insert: 'going NUTS', 42 | attributes: { 43 | italic: true, 44 | bold: true, 45 | sub: true, 46 | super: true, 47 | bg: '#000000', 48 | color: '#ffffff', 49 | strike: true, 50 | underline: true, 51 | }, 52 | }, 53 | { insert: '\n' }, 54 | { insert: 'bold multiline\nvalue', attributes: { bold: true } }, 55 | { insert: 'italic value', attributes: { italic: true } }, 56 | { insert: 'bold-italic value', attributes: { bold: true, italic: true } }, 57 | { insert: '\n' }, 58 | ], 59 | }; 60 | 61 | const integrationResultPlain = `multiline 62 | value 63 | simple text with a 64 | 65 | following text 66 | 67 | * bulleted list one 68 | * bulleted list two 69 | * bulleted list three 70 | 71 | * numbered list one 72 | * numbered list two 73 | * numbered list three 74 | 75 | header two 76 | underlined header one 77 | redbgredstrikethru 78 | IMAGE: IMAGEURLescaped HTML & < > " ' & 79 | empty newline should have nbsp (four after this) 80 | 81 | 82 | 83 | 84 | going NUTS 85 | bold multiline 86 | valueitalic valuebold-italic value 87 | `; 88 | 89 | const integrationResultHTML = 90 | '

    ' + 91 | 'multiline' + 92 | '

    ' + 93 | '

    ' + 94 | 'value' + 95 | '

    ' + 96 | '

    ' + 97 | 'simple text with a' + 98 | '

    ' + 99 | '

    ' + 100 | ' ' + 101 | '

    ' + 102 | '

    ' + 103 | 'following text' + 104 | '

    ' + 105 | '

    ' + 106 | ' ' + 107 | '

    ' + 108 | '' + 121 | '
      ' + 122 | '
    1. ' + 123 | 'numbered list one' + 124 | '
    2. ' + 125 | '
    3. ' + 126 | 'numbered list two' + 127 | '
    4. ' + 128 | '
    5. ' + 129 | 'numbered list three' + 130 | '
    6. ' + 131 | '
    ' + 132 | '

    ' + 133 | 'header two' + 134 | '

    ' + 135 | '

    ' + 136 | '' + 137 | 'underlined header one' + 138 | '' + 139 | '

    ' + 140 | '

    ' + 141 | '' + 142 | 'red' + 143 | '' + 144 | '' + 145 | 'bgred' + 146 | '' + 147 | '' + 148 | 'strikethru' + 149 | '' + 150 | '

    ' + 151 | '

    ' + 152 | '' + 153 | 'escaped HTML & < > " ' &' + 154 | '

    ' + 155 | '

    ' + 156 | 'empty newline should have nbsp (four after this)' + 157 | '

    ' + 158 | '

    ' + 159 | ' ' + 160 | '

    ' + 161 | '

    ' + 162 | ' ' + 163 | '

    ' + 164 | '

    ' + 165 | ' ' + 166 | '

    ' + 167 | '

    ' + 168 | ' ' + 169 | '

    ' + 170 | '

    ' + 171 | '' + 172 | '' + 173 | '' + 174 | '' + 175 | '' + 176 | '' + 177 | '' + 178 | '' + 179 | 'going NUTS' + 180 | '' + 181 | '' + 182 | '' + 183 | '' + 184 | '' + 185 | '' + 186 | '' + 187 | '' + 188 | '

    ' + 189 | '

    ' + 190 | '' + 191 | 'bold multiline' + 192 | '' + 193 | '

    ' + 194 | '

    ' + 195 | '' + 196 | 'value' + 197 | '' + 198 | '' + 199 | 'italic value' + 200 | '' + 201 | 'bold-italic value' + 202 | '' + 203 | '' + 204 | '

    '; 205 | 206 | describe('integration', () => { 207 | const transform = new Formatter(); 208 | it('synchronously output html that meets all specifications', () => { 209 | return expect(transform.transform(testVal)).to.equal(integrationResultHTML); 210 | }); 211 | it('synchronously output plain text that meets all specifications', () => { 212 | return expect(transform.plainText(testVal)).to.equal( 213 | integrationResultPlain, 214 | ); 215 | }); 216 | it('asynchronously output html that meets all specifications', () => { 217 | return transform 218 | .transformAsync(testVal) 219 | .then(v => expect(v).to.equal(integrationResultHTML)); 220 | }); 221 | it('asynchronously output plain text that meets all specifications', () => { 222 | return transform 223 | .plainTextAsync(testVal) 224 | .then(v => expect(v).to.equal(integrationResultPlain)); 225 | }); 226 | }); 227 | -------------------------------------------------------------------------------- /dist/miniDOM/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _bgcolor = require('./bgcolor'); 8 | 9 | Object.keys(_bgcolor).forEach(function (key) { 10 | if (key === "default" || key === "__esModule") return; 11 | Object.defineProperty(exports, key, { 12 | enumerable: true, 13 | get: function get() { 14 | return _bgcolor[key]; 15 | } 16 | }); 17 | }); 18 | 19 | var _block = require('./block'); 20 | 21 | Object.keys(_block).forEach(function (key) { 22 | if (key === "default" || key === "__esModule") return; 23 | Object.defineProperty(exports, key, { 24 | enumerable: true, 25 | get: function get() { 26 | return _block[key]; 27 | } 28 | }); 29 | }); 30 | 31 | var _bold = require('./bold'); 32 | 33 | Object.keys(_bold).forEach(function (key) { 34 | if (key === "default" || key === "__esModule") return; 35 | Object.defineProperty(exports, key, { 36 | enumerable: true, 37 | get: function get() { 38 | return _bold[key]; 39 | } 40 | }); 41 | }); 42 | 43 | var _color = require('./color'); 44 | 45 | Object.keys(_color).forEach(function (key) { 46 | if (key === "default" || key === "__esModule") return; 47 | Object.defineProperty(exports, key, { 48 | enumerable: true, 49 | get: function get() { 50 | return _color[key]; 51 | } 52 | }); 53 | }); 54 | 55 | var _header = require('./header'); 56 | 57 | Object.keys(_header).forEach(function (key) { 58 | if (key === "default" || key === "__esModule") return; 59 | Object.defineProperty(exports, key, { 60 | enumerable: true, 61 | get: function get() { 62 | return _header[key]; 63 | } 64 | }); 65 | }); 66 | 67 | var _image = require('./image'); 68 | 69 | Object.keys(_image).forEach(function (key) { 70 | if (key === "default" || key === "__esModule") return; 71 | Object.defineProperty(exports, key, { 72 | enumerable: true, 73 | get: function get() { 74 | return _image[key]; 75 | } 76 | }); 77 | }); 78 | 79 | var _italic = require('./italic'); 80 | 81 | Object.keys(_italic).forEach(function (key) { 82 | if (key === "default" || key === "__esModule") return; 83 | Object.defineProperty(exports, key, { 84 | enumerable: true, 85 | get: function get() { 86 | return _italic[key]; 87 | } 88 | }); 89 | }); 90 | 91 | var _link = require('./link'); 92 | 93 | Object.keys(_link).forEach(function (key) { 94 | if (key === "default" || key === "__esModule") return; 95 | Object.defineProperty(exports, key, { 96 | enumerable: true, 97 | get: function get() { 98 | return _link[key]; 99 | } 100 | }); 101 | }); 102 | 103 | var _listItem = require('./listItem'); 104 | 105 | Object.keys(_listItem).forEach(function (key) { 106 | if (key === "default" || key === "__esModule") return; 107 | Object.defineProperty(exports, key, { 108 | enumerable: true, 109 | get: function get() { 110 | return _listItem[key]; 111 | } 112 | }); 113 | }); 114 | 115 | var _orderedList = require('./orderedList'); 116 | 117 | Object.keys(_orderedList).forEach(function (key) { 118 | if (key === "default" || key === "__esModule") return; 119 | Object.defineProperty(exports, key, { 120 | enumerable: true, 121 | get: function get() { 122 | return _orderedList[key]; 123 | } 124 | }); 125 | }); 126 | 127 | var _paragraph = require('./paragraph'); 128 | 129 | Object.keys(_paragraph).forEach(function (key) { 130 | if (key === "default" || key === "__esModule") return; 131 | Object.defineProperty(exports, key, { 132 | enumerable: true, 133 | get: function get() { 134 | return _paragraph[key]; 135 | } 136 | }); 137 | }); 138 | 139 | var _root = require('./root'); 140 | 141 | Object.keys(_root).forEach(function (key) { 142 | if (key === "default" || key === "__esModule") return; 143 | Object.defineProperty(exports, key, { 144 | enumerable: true, 145 | get: function get() { 146 | return _root[key]; 147 | } 148 | }); 149 | }); 150 | 151 | var _span = require('./span'); 152 | 153 | Object.keys(_span).forEach(function (key) { 154 | if (key === "default" || key === "__esModule") return; 155 | Object.defineProperty(exports, key, { 156 | enumerable: true, 157 | get: function get() { 158 | return _span[key]; 159 | } 160 | }); 161 | }); 162 | 163 | var _strikethrough = require('./strikethrough'); 164 | 165 | Object.keys(_strikethrough).forEach(function (key) { 166 | if (key === "default" || key === "__esModule") return; 167 | Object.defineProperty(exports, key, { 168 | enumerable: true, 169 | get: function get() { 170 | return _strikethrough[key]; 171 | } 172 | }); 173 | }); 174 | 175 | var _subscript = require('./subscript'); 176 | 177 | Object.keys(_subscript).forEach(function (key) { 178 | if (key === "default" || key === "__esModule") return; 179 | Object.defineProperty(exports, key, { 180 | enumerable: true, 181 | get: function get() { 182 | return _subscript[key]; 183 | } 184 | }); 185 | }); 186 | 187 | var _superscript = require('./superscript'); 188 | 189 | Object.keys(_superscript).forEach(function (key) { 190 | if (key === "default" || key === "__esModule") return; 191 | Object.defineProperty(exports, key, { 192 | enumerable: true, 193 | get: function get() { 194 | return _superscript[key]; 195 | } 196 | }); 197 | }); 198 | 199 | var _text = require('./text'); 200 | 201 | Object.keys(_text).forEach(function (key) { 202 | if (key === "default" || key === "__esModule") return; 203 | Object.defineProperty(exports, key, { 204 | enumerable: true, 205 | get: function get() { 206 | return _text[key]; 207 | } 208 | }); 209 | }); 210 | 211 | var _treeNode = require('./treeNode'); 212 | 213 | Object.keys(_treeNode).forEach(function (key) { 214 | if (key === "default" || key === "__esModule") return; 215 | Object.defineProperty(exports, key, { 216 | enumerable: true, 217 | get: function get() { 218 | return _treeNode[key]; 219 | } 220 | }); 221 | }); 222 | 223 | var _underline = require('./underline'); 224 | 225 | Object.keys(_underline).forEach(function (key) { 226 | if (key === "default" || key === "__esModule") return; 227 | Object.defineProperty(exports, key, { 228 | enumerable: true, 229 | get: function get() { 230 | return _underline[key]; 231 | } 232 | }); 233 | }); 234 | 235 | var _unorderedList = require('./unorderedList'); 236 | 237 | Object.keys(_unorderedList).forEach(function (key) { 238 | if (key === "default" || key === "__esModule") return; 239 | Object.defineProperty(exports, key, { 240 | enumerable: true, 241 | get: function get() { 242 | return _unorderedList[key]; 243 | } 244 | }); 245 | }); -------------------------------------------------------------------------------- /dist/formatter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.Formatter = undefined; 7 | 8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9 | 10 | var _miniDOM = require('./miniDOM'); 11 | 12 | var MiniDOM = _interopRequireWildcard(_miniDOM); 13 | 14 | var _tokenize = require('./tokenize'); 15 | 16 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 17 | 18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19 | 20 | var Formatter = exports.Formatter = function () { 21 | function Formatter() { 22 | _classCallCheck(this, Formatter); 23 | 24 | this.formats = { 25 | bold: MiniDOM.BoldNode, 26 | italic: MiniDOM.ItalicNode, 27 | link: MiniDOM.LinkNode, 28 | listItem: MiniDOM.ListItemNode, 29 | ordered: MiniDOM.OrderedListNode, 30 | paragraph: MiniDOM.ParagraphNode, 31 | text: MiniDOM.TextNode, 32 | TreeNode: MiniDOM.TreeNode, 33 | RootNode: MiniDOM.RootNode, 34 | bullet: MiniDOM.UnorderedListNode, 35 | header: MiniDOM.HeaderNode, 36 | underline: MiniDOM.UnderlineNode, 37 | strikethrough: MiniDOM.StrikethroughNode, 38 | color: MiniDOM.ColorNode, 39 | bgcolor: MiniDOM.BackgroundColorNode, 40 | subscript: MiniDOM.SuperscriptNode, 41 | superscript: MiniDOM.SubscriptNode, 42 | SpanNode: MiniDOM.SpanNode, 43 | BlockNode: MiniDOM.BlockNode, 44 | image: MiniDOM.ImageNode 45 | }; 46 | this.formatList = []; 47 | this.sortRegistry(); 48 | this.checkPriorities(); 49 | } 50 | 51 | _createClass(Formatter, [{ 52 | key: 'sortRegistry', 53 | value: function sortRegistry() { 54 | var _this = this; 55 | 56 | this.formatList = Object.keys(this.formats).sort(function (a, b) { 57 | return _this.formats[b].priority - _this.formats[a].priority; 58 | }).map(function (n) { 59 | return _this.formats[n]; 60 | }); 61 | } 62 | }, { 63 | key: 'checkPriorities', 64 | value: function checkPriorities() { 65 | var _this2 = this; 66 | 67 | var seen = {}; 68 | Object.keys(this.formats).forEach(function (key) { 69 | if (seen[_this2.formats[key].priority]) { 70 | console.log('ERROR: conflict between ' + key + ' and ' + seen[_this2.formats[key].priority]); 71 | } 72 | seen[_this2.formats[key].priority] = key; 73 | }); 74 | } 75 | }, { 76 | key: 'transform', 77 | value: function transform(delta) { 78 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 79 | 80 | var openTag = ''; 81 | var closeTag = ''; 82 | if (opts.rootNode) { 83 | if (opts.rootClass) { 84 | openTag = '<' + opts.rootNode + ' class="' + opts.rootClass + '">'; 85 | } else { 86 | openTag = '<' + opts.rootNode + '>'; 87 | } 88 | closeTag = ''; 89 | } 90 | return '' + openTag + this.blockize((0, _tokenize.tokenize)(delta.ops)).toHTML() + closeTag; 91 | } 92 | }, { 93 | key: 'build', 94 | value: function build(token) { 95 | var matchingFormats = this.formatList.filter(function (format) { 96 | return format.matches(token); 97 | }).map(function (N) { 98 | return new N(token); 99 | }); 100 | if (matchingFormats.length === 0) { 101 | // console.log(`token ${JSON.stringify(token)} has no matching formats`); 102 | return new this.formats.TreeNode(); 103 | } 104 | var retVal = matchingFormats.shift(); 105 | matchingFormats.reduce(function (prev, curr) { 106 | prev.children = [curr]; // eslint-disable-line no-param-reassign 107 | return curr; 108 | }, retVal); 109 | return retVal; 110 | } 111 | }, { 112 | key: 'blockize', 113 | value: function blockize(tokens) { 114 | var _this3 = this; 115 | 116 | var RN = this.formats.RootNode; 117 | var retVal = new RN(); 118 | var childList = []; 119 | tokens.forEach(function (token) { 120 | if (token.type === 'linebreak') { 121 | var blockArray = _this3.formatList.filter(function (f) { 122 | return f.matches(token); 123 | }); 124 | var currentBlock = new blockArray[0](token); 125 | childList.forEach(function (child) { 126 | return currentBlock.appendChild(_this3.build(child)); 127 | }); 128 | retVal.absorb(currentBlock); 129 | childList = []; 130 | } else { 131 | childList.push(token); 132 | } 133 | }); 134 | return retVal; 135 | } 136 | }, { 137 | key: 'transformAsync', 138 | value: function transformAsync(delta) { 139 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 140 | 141 | return this.blockize((0, _tokenize.tokenize)(delta.ops)).toHTMLAsync().then(function (v) { 142 | var openTag = ''; 143 | var closeTag = ''; 144 | if (opts.rootNode) { 145 | if (opts.rootClass) { 146 | openTag = '<' + opts.rootNode + ' class="' + opts.rootClass + '">'; 147 | } else { 148 | openTag = '<' + opts.rootNode + '>'; 149 | } 150 | closeTag = ''; 151 | } 152 | return '' + openTag + v + closeTag; 153 | }); 154 | } 155 | }, { 156 | key: 'plainText', 157 | value: function plainText(delta) { 158 | return this.blockize((0, _tokenize.tokenize)(delta.ops)).plainText(); 159 | } 160 | }, { 161 | key: 'plainTextAsync', 162 | value: function plainTextAsync(delta) { 163 | return this.blockize((0, _tokenize.tokenize)(delta.ops)).plainTextAsync(); 164 | } 165 | }]); 166 | 167 | return Formatter; 168 | }(); -------------------------------------------------------------------------------- /test/formats.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha*/ 2 | 3 | import { expect } from 'chai'; 4 | import { Formatter } from '../src/index'; 5 | import 'mocha'; 6 | 7 | describe('formats', () => { 8 | it('should wrap in a root node on request', () => { 9 | const transform = new Formatter(); 10 | const delta = { 11 | ops: [ 12 | { 13 | insert: 'word\n', 14 | attributes: { 15 | bg: 'red', 16 | }, 17 | }, 18 | ], 19 | }; 20 | const result = 21 | '

    word

    '; // eslint-disable-line max-len 22 | expect(transform.transform(delta, { rootNode: 'special-thing' })).to.equal( 23 | result, 24 | ); 25 | }); 26 | it('should wrap in a classy root node on request', () => { 27 | const transform = new Formatter(); 28 | const delta = { 29 | ops: [ 30 | { 31 | insert: 'word\n', 32 | attributes: { 33 | bg: 'red', 34 | }, 35 | }, 36 | ], 37 | }; 38 | const result = 39 | '

    word

    '; 40 | expect( 41 | transform.transform(delta, { 42 | rootNode: 'special-thing', 43 | rootClass: 'potato', 44 | }), 45 | ).to.equal(result); 46 | }); 47 | it('should format background color', () => { 48 | const transform = new Formatter(); 49 | 50 | const delta = { 51 | ops: [ 52 | { 53 | insert: 'word\n', 54 | attributes: { 55 | bg: 'red', 56 | }, 57 | }, 58 | ], 59 | }; 60 | const result = 61 | '

    word

    '; // eslint-disable-line max-len 62 | expect(transform.transform(delta)).to.equal(result); 63 | }); 64 | it('should format bold', () => { 65 | const transform = new Formatter(); 66 | const delta = { 67 | ops: [ 68 | { 69 | insert: 'word\n', 70 | attributes: { 71 | bold: true, 72 | }, 73 | }, 74 | ], 75 | }; 76 | const result = '

    word

    '; // eslint-disable-line max-len 77 | expect(transform.transform(delta)).to.equal(result); 78 | }); 79 | it('should format foreground color', () => { 80 | const transform = new Formatter(); 81 | const delta = { 82 | ops: [ 83 | { 84 | insert: 'word\n', 85 | attributes: { 86 | color: 'red', 87 | }, 88 | }, 89 | ], 90 | }; 91 | const result = '

    word

    '; // eslint-disable-line max-len 92 | expect(transform.transform(delta)).to.equal(result); 93 | }); 94 | it('should format headers', () => { 95 | const transform = new Formatter(); 96 | const delta = { 97 | ops: [ 98 | { 99 | insert: 'word', 100 | }, 101 | { 102 | insert: '\n', 103 | attributes: { 104 | header: 1, 105 | }, 106 | }, 107 | ], 108 | }; 109 | const result = '

    word

    '; // eslint-disable-line max-len 110 | expect(transform.transform(delta)).to.equal(result); 111 | }); 112 | it('should format images', () => { 113 | const transform = new Formatter(); 114 | const delta = { 115 | ops: [ 116 | { 117 | insert: { 118 | image: 'URL', 119 | }, 120 | }, 121 | { 122 | insert: '\n', 123 | attributes: {}, 124 | }, 125 | ], 126 | }; 127 | const result = '

    '; // eslint-disable-line max-len 128 | expect(transform.transform(delta)).to.equal(result); 129 | }); 130 | it('should format italic', () => { 131 | const transform = new Formatter(); 132 | const delta = { 133 | ops: [ 134 | { 135 | insert: 'word\n', 136 | attributes: { 137 | italic: true, 138 | }, 139 | }, 140 | ], 141 | }; 142 | const result = '

    word

    '; // eslint-disable-line max-len 143 | expect(transform.transform(delta)).to.equal(result); 144 | }); 145 | it('should format links', () => { 146 | const transform = new Formatter(); 147 | const delta = { 148 | ops: [ 149 | { 150 | insert: 'word', 151 | attributes: { 152 | link: 'URL', 153 | }, 154 | }, 155 | { 156 | insert: '\n', 157 | attributes: {}, 158 | }, 159 | ], 160 | }; 161 | const result = '

    word

    '; // eslint-disable-line max-len 162 | expect(transform.transform(delta)).to.equal(result); 163 | }); 164 | it('should format ordered lists', () => { 165 | const transform = new Formatter(); 166 | const delta = { 167 | ops: [ 168 | { 169 | insert: 'word', 170 | }, 171 | { 172 | insert: '\n', 173 | attributes: { 174 | ordered: true, 175 | }, 176 | }, 177 | ], 178 | }; 179 | const result = '
    1. word
    '; // eslint-disable-line max-len 180 | expect(transform.transform(delta)).to.equal(result); 181 | }); 182 | it('should format unordered lists', () => { 183 | const transform = new Formatter(); 184 | const delta = { 185 | ops: [ 186 | { 187 | insert: 'word', 188 | }, 189 | { 190 | insert: '\n', 191 | attributes: { 192 | bullet: true, 193 | }, 194 | }, 195 | ], 196 | }; 197 | const result = '
    • word
    '; // eslint-disable-line max-len 198 | expect(transform.transform(delta)).to.equal(result); 199 | }); 200 | it('should format paragraphs', () => { 201 | const transform = new Formatter(); 202 | const delta = { 203 | ops: [ 204 | { 205 | insert: 'word\n', 206 | }, 207 | ], 208 | }; 209 | const result = '

    word

    '; // eslint-disable-line max-len 210 | expect(transform.transform(delta)).to.equal(result); 211 | }); 212 | it('should format strikethrough', () => { 213 | const transform = new Formatter(); 214 | const delta = { 215 | ops: [ 216 | { 217 | insert: 'word\n', 218 | attributes: { 219 | strike: true, 220 | }, 221 | }, 222 | ], 223 | }; 224 | const result = '

    word

    '; // eslint-disable-line max-len 225 | expect(transform.transform(delta)).to.equal(result); 226 | }); 227 | it('should format superscript', () => { 228 | const transform = new Formatter(); 229 | const delta = { 230 | ops: [ 231 | { 232 | insert: 'word\n', 233 | attributes: { 234 | super: true, 235 | }, 236 | }, 237 | ], 238 | }; 239 | const result = '

    word

    '; // eslint-disable-line max-len 240 | expect(transform.transform(delta)).to.equal(result); 241 | }); 242 | it('should format subscript', () => { 243 | const transform = new Formatter(); 244 | const delta = { 245 | ops: [ 246 | { 247 | insert: 'word\n', 248 | attributes: { 249 | sub: true, 250 | }, 251 | }, 252 | ], 253 | }; 254 | const result = '

    word

    '; // eslint-disable-line max-len 255 | expect(transform.transform(delta)).to.equal(result); 256 | }); 257 | it('should ignore unknowns', () => { 258 | const transform = new Formatter(); 259 | const delta = { 260 | ops: [ 261 | { 262 | attributes: { 263 | tuber: 'POTATO', 264 | }, 265 | }, 266 | { 267 | insert: '\n', 268 | }, 269 | ], 270 | }; 271 | const result = '

    '; // eslint-disable-line max-len 272 | expect(transform.transform(delta)).to.equal(result); 273 | }); 274 | it('should format underlines', () => { 275 | const transform = new Formatter(); 276 | const delta = { 277 | ops: [ 278 | { 279 | insert: 'word\n', 280 | attributes: { 281 | underline: true, 282 | }, 283 | }, 284 | ], 285 | }; 286 | const result = '

    word

    '; // eslint-disable-line max-len 287 | expect(transform.transform(delta)).to.equal(result); 288 | }); 289 | }); 290 | --------------------------------------------------------------------------------