import { ProsemirrorNode, DOMOutputSpec } from 'prosemirror-model'; import NodeSchema from '@/spec/node'; import { escapeXml } from '@/utils/common'; import { sanitizeHTML } from '@/sanitizer/htmlSanitizer'; import { EditorCommand } from '@t/spec'; import { getCustomAttrs, getDefaultCustomAttrs } from '../helper/node'; export class Image extends NodeSchema { get name() { return 'image'; } get schema() { return { inline: true, attrs: { imageUrl: { default: '' }, altText: { default: null }, rawHTML: { default: null }, ...getDefaultCustomAttrs(), }, group: 'inline', selectable: false, parseDOM: [ { tag: 'img[src]', getAttrs(dom: Node | string) { const sanitizedDOM = sanitizeHTML(dom, { RETURN_DOM_FRAGMENT: true }) .firstChild as HTMLElement; const imageUrl = sanitizedDOM.getAttribute('src') || ''; const rawHTML = sanitizedDOM.getAttribute('data-raw-html'); const altText = sanitizedDOM.getAttribute('alt'); return { imageUrl, altText, ...(rawHTML && { rawHTML }), }; }, }, ], toDOM({ attrs }: ProsemirrorNode): DOMOutputSpec { return [ attrs.rawHTML || 'img', { src: escapeXml(attrs.imageUrl), ...(attrs.altText && { alt: attrs.altText }), ...getCustomAttrs(attrs), }, ]; }, }; } private addImage(): EditorCommand { return (payload) => ({ schema, tr }, dispatch) => { const { imageUrl, altText } = payload!; if (!imageUrl) { return false; } const node = schema.nodes.image.createAndFill({ imageUrl, ...(altText && { altText }), }); dispatch!(tr.replaceSelectionWith(node!).scrollIntoView()); return true; }; } commands() { return { addImage: this.addImage(), }; } }