UNPKG

quill-delta-to-html

Version:
234 lines (188 loc) 7.5 kB
import { InsertOpsConverter } from './InsertOpsConverter'; import { OpToHtmlConverter, IOpToHtmlConverterOptions } from './OpToHtmlConverter'; import { DeltaInsertOp } from './DeltaInsertOp'; import { Grouper } from './grouper/Grouper'; import { VideoItem, InlineGroup, BlockGroup, ListGroup, ListItem, TDataGroup } from './grouper/group-types'; import { ListNester } from './grouper/ListNester'; import { makeStartTag, makeEndTag, encodeHtml } from './funcs-html'; import './extensions/Object'; import { GroupType } from './value-types'; interface IQuillDeltaToHtmlConverterOptions { // no more allowing these to be customized; unnecessary orderedListTag?: string, bulletListTag?: string, listItemTag?: string, paragraphTag?: string, classPrefix?: string, encodeHtml?: boolean, multiLineBlockquote?: boolean, multiLineHeader?: boolean, multiLineCodeblock?: boolean, linkRel?: string, linkTarget?: string, allowBackgroundClasses?: boolean } const BrTag = '<br/>'; class QuillDeltaToHtmlConverter { private options: IQuillDeltaToHtmlConverterOptions; private rawDeltaOps: any[] = []; private converterOptions: IOpToHtmlConverterOptions; // render callbacks private callbacks: any = {}; constructor( deltaOps: any[], options?: IQuillDeltaToHtmlConverterOptions) { this.options = Object._assign({ paragraphTag: 'p', encodeHtml: true, classPrefix: 'ql', multiLineBlockquote: true, multiLineHeader: true, multiLineCodeblock: true, allowBackgroundClasses: false, linkTarget: '_blank' }, options, { orderedListTag: 'ol', bulletListTag: 'ul', listItemTag: 'li' }); this.converterOptions = { encodeHtml: this.options.encodeHtml, classPrefix: this.options.classPrefix, listItemTag: this.options.listItemTag, paragraphTag: this.options.paragraphTag, linkRel: this.options.linkRel, linkTarget: this.options.linkTarget, allowBackgroundClasses: this.options.allowBackgroundClasses }; this.rawDeltaOps = deltaOps; } _getListTag(op: DeltaInsertOp): string { return op.isOrderedList() ? this.options.orderedListTag + '' : op.isBulletList() ? this.options.bulletListTag + '' : ''; } getGroupedOps (): TDataGroup[] { var deltaOps = InsertOpsConverter.convert(this.rawDeltaOps); var pairedOps = Grouper.pairOpsWithTheirBlock(deltaOps); var groupedSameStyleBlocks = Grouper.groupConsecutiveSameStyleBlocks(pairedOps, { blockquotes: !!this.options.multiLineBlockquote, header: !!this.options.multiLineHeader, codeBlocks: !!this.options.multiLineCodeblock }); var groupedOps = Grouper.reduceConsecutiveSameStyleBlocksToOne(groupedSameStyleBlocks); var listNester = new ListNester(); return listNester.nest(groupedOps); } convert() { return this.getGroupedOps() .map(group => { if (group instanceof ListGroup) { return this._renderWithCallbacks( GroupType.List, group, () => this._renderList(<ListGroup>group)); } else if (group instanceof BlockGroup) { var g = <BlockGroup>group; return this._renderWithCallbacks( GroupType.Block, group, () => this._renderBlock(g.op, g.ops)); } else if (group instanceof VideoItem) { return this._renderWithCallbacks(GroupType.Video, group, () => { var g = <VideoItem>group; var converter = new OpToHtmlConverter(g.op, this.converterOptions); return converter.getHtml(); }); } else { // InlineGroup return this._renderWithCallbacks(GroupType.InlineGroup, group, () => { return this._renderInlines((<InlineGroup>group).ops); }); } }) .join(""); } _renderWithCallbacks(groupType: GroupType, group: TDataGroup, myRenderFn: () => string) { var html = ''; var beforeCb = this.callbacks['beforeRender_cb']; html = typeof beforeCb === 'function' ? beforeCb.apply(null, [groupType, group]) : ''; if (!html) { html = myRenderFn(); } var afterCb = this.callbacks['afterRender_cb']; html = typeof afterCb === 'function' ? afterCb.apply(null, [groupType, html]) : html; return html; } _renderList(list: ListGroup): string { var firstItem = list.items[0]; return makeStartTag(this._getListTag(firstItem.item.op)) + list.items.map((li: ListItem) => this._renderListItem(li)).join('') + makeEndTag(this._getListTag(firstItem.item.op)); } _renderListItem(li: ListItem ): string { //if (!isOuterMost) { li.item.op.attributes.indent = 0; //} var converter = new OpToHtmlConverter(li.item.op, this.converterOptions); var parts = converter.getHtmlParts(); var liElementsHtml = this._renderInlines(li.item.ops, false); return parts.openingTag + (liElementsHtml) + (li.innerList ? this._renderList(li.innerList) : '') + parts.closingTag; } _renderBlock(bop: DeltaInsertOp, ops: DeltaInsertOp[]) { var converter = new OpToHtmlConverter(bop, this.converterOptions); var htmlParts = converter.getHtmlParts(); if (bop.isCodeBlock()) { return htmlParts.openingTag + encodeHtml( ops.map((iop) => iop.isCustom() ? this._renderCustom(iop, bop) : iop.insert.value ).join("") ) + htmlParts.closingTag; } var inlines = ops.map(op => this._renderInline(op, bop)).join(''); return htmlParts.openingTag + (inlines || BrTag) + htmlParts.closingTag; } _renderInlines(ops: DeltaInsertOp[], wrapInParagraphTag = true) { var opsLen = ops.length - 1; var html = ops.map((op: DeltaInsertOp, i: number) => { if (i > 0 && i === opsLen && op.isJustNewline()) { return ''; } return this._renderInline(op, null); }).join(''); if (!wrapInParagraphTag) { return html; } return makeStartTag(this.options.paragraphTag) + html + makeEndTag(this.options.paragraphTag); } _renderInline(op: DeltaInsertOp, contextOp: DeltaInsertOp | null) { if (op.isCustom()) { return this._renderCustom(op, contextOp); } var converter = new OpToHtmlConverter(op, this.converterOptions); return converter.getHtml().replace(/\n/g, BrTag); } _renderCustom(op: DeltaInsertOp, contextOp: DeltaInsertOp | null) { var renderCb = this.callbacks['renderCustomOp_cb']; if (typeof renderCb === 'function') { return renderCb.apply(null, [op, contextOp]); } return ""; } beforeRender(cb: (group: GroupType, data: TDataGroup) => string) { if (typeof cb === 'function') { this.callbacks['beforeRender_cb'] = cb; } } afterRender(cb: (group: GroupType, html: string) => string) { if (typeof cb === 'function') { this.callbacks['afterRender_cb'] = cb; } } renderCustomWith(cb: (op: DeltaInsertOp, contextOp: DeltaInsertOp) => string) { this.callbacks['renderCustomOp_cb'] = cb; } } export { QuillDeltaToHtmlConverter };