import katex from 'katex' import prism, { loadedCache } from '../../../prism/' import { CLASS_OR_ID, DEVICE_MEMORY, isInElectron, PREVIEW_DOMPURIFY_CONFIG } from '../../../config' import { tokenizer } from '../../parse' import { snakeToCamel, sanitize, escapeHtml, getLongUniqueId } from '../../../utils' import { h, htmlToVNode } from '../snabbdom' import alignLeftIcon from '../../../assets/icons/align_left.svg' import alignRightIcon from '../../../assets/icons/align_right.svg' import alignCenterIcon from '../../../assets/icons/align_center.svg' import tableIcon from '../../../assets/icons/table.svg' import deleteIcon from '../../../assets/icons/delete.svg' // todo@jocs any better solutions? const MARKER_HASK = { '<': `%${getLongUniqueId()}%`, '>': `%${getLongUniqueId()}%`, '"': `%${getLongUniqueId()}%`, "'": `%${getLongUniqueId()}%` } const ICON_MAP = { 'icon-alignright': alignRightIcon, 'icon-alignleft': alignLeftIcon, 'icon-del': deleteIcon, 'icon-table': tableIcon, 'icon-aligncenter': alignCenterIcon } const getHighlightHtml = (text, highlights, escape = false) => { let code = '' let pos = 0 for (const highlight of highlights) { const { start, end, active } = highlight code += text.substring(pos, start) const className = active ? 'ag-highlight' : 'ag-selection' code += escape ? `${MARKER_HASK['<']}span class=${MARKER_HASK['"']}${className}${MARKER_HASK['"']}${MARKER_HASK['>']}${text.substring(start, end)}${MARKER_HASK['<']}/span${MARKER_HASK['>']}` : `${text.substring(start, end)}` pos = end } if (pos !== text.length) { code += text.substring(pos) } return code } export default function renderLeafBlock (block, cursor, activeBlocks, matches, useCache = false) { const { loadMathMap } = this let selector = this.getSelector(block, cursor, activeBlocks) // highlight search key in block const highlights = matches.filter(m => m.key === block.key) const { text, type, headingStyle, align, icon, checked, key, lang, functionType, editable } = block const data = { props: {}, attrs: {}, dataset: {}, style: {} } let children = '' if (text) { let tokens = null if (highlights.length === 0 && this.tokenCache.has(text)) { tokens = this.tokenCache.get(text) } else { tokens = tokenizer(text, highlights) if (highlights.length === 0 && useCache && DEVICE_MEMORY >= 4) this.tokenCache.set(text, tokens) } children = tokens.reduce((acc, token) => [...acc, ...this[snakeToCamel(token.type)](h, cursor, block, token)], []) } if (editable === false) { Object.assign(data.attrs, { contenteditable: 'false' }) } if (/th|td/.test(type) && align) { Object.assign(data.attrs, { style: `text-align:${align}` }) } else if (type === 'div') { if (functionType === 'preview') { selector += `.${CLASS_OR_ID['AG_HTML_PREVIEW']}` const htmlContent = sanitize(this.muya.contentState.codeBlocks.get(block.preSibling), PREVIEW_DOMPURIFY_CONFIG) // handle empty html bock if (/<([a-z][a-z\d]*).*>\s*<\/\1>/.test(htmlContent)) { children = htmlToVNode('