"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseImage = exports.default = void 0; var _plugin = _interopRequireDefault(require("../../plugin")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** @module */ const escape = target => target.replace(/[\\;:()]/g, matched => `\\${matched[0].codePointAt(0).toString(16)} `); const optionMatchers = new Map(); // The scale percentage for resize background optionMatchers.set(/^(\d*\.)?\d+%$/, matches => ({ size: matches[0] })); // width and height const normalizeLength = v => `${v}${/^(\d*\.)?\d+$/.test(v) ? 'px' : ''}`; optionMatchers.set(/^w(?:idth)?:((?:\d*\.)?\d+(?:%|ch|cm|em|ex|in|mm|pc|pt|px)?|auto)$/, matches => ({ width: normalizeLength(matches[1]) })); optionMatchers.set(/^h(?:eight)?:((?:\d*\.)?\d+(?:%|ch|cm|em|ex|in|mm|pc|pt|px)?|auto)$/, matches => ({ height: normalizeLength(matches[1]) })); // CSS filters optionMatchers.set(/^blur(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['blur', escape(matches[1] || '10px')]] })); optionMatchers.set(/^brightness(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['brightness', escape(matches[1] || '1.5')]] })); optionMatchers.set(/^contrast(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['contrast', escape(matches[1] || '2')]] })); optionMatchers.set(/^drop-shadow(?::(.+?),(.+?)(?:,(.+?))?(?:,(.+?))?)?$/, (matches, meta) => { const args = []; for (const arg of matches.slice(1)) { if (arg) { const colorFunc = arg.match(/^(rgba?|hsla?|hwb|(?:ok)?(?:lab|lch)|color)\((.*)\)$/); args.push(colorFunc ? `${colorFunc[1]}(${escape(colorFunc[2])})` : escape(arg)); } } return { filters: [...meta.filters, ['drop-shadow', args.join(' ') || '0 5px 10px rgba(0,0,0,.4)']] }; }); optionMatchers.set(/^grayscale(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['grayscale', escape(matches[1] || '1')]] })); optionMatchers.set(/^hue-rotate(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['hue-rotate', escape(matches[1] || '180deg')]] })); optionMatchers.set(/^invert(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['invert', escape(matches[1] || '1')]] })); optionMatchers.set(/^opacity(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['opacity', escape(matches[1] || '.5')]] })); optionMatchers.set(/^saturate(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['saturate', escape(matches[1] || '2')]] })); optionMatchers.set(/^sepia(?::(.+))?$/, (matches, meta) => ({ filters: [...meta.filters, ['sepia', escape(matches[1] || '1')]] })); /** * Marpit image parse plugin. * * Parse image tokens and store the result into `marpitImage` meta. It has an * image url and options. The alternative text is regarded as space-separated * options. * * @function parseImage * @param {MarkdownIt} md markdown-it instance. */ function _parseImage(md) { const { process } = md.core; let refCount = 0; const finalizeTokenAttr = (token, state) => { // Apply finalization recursively to inline tokens if (token.type === 'inline') { for (const t of token.children) finalizeTokenAttr(t, state); } // Re-generate the alt text of image token to remove Marpit specific options if (token.type === 'image' && token.meta && token.meta.marpitImage) { let updatedAlt = ''; let hasConsumed = false; for (const opt of token.meta.marpitImage.options) { if (opt.consumed) { hasConsumed = true; } else { updatedAlt += opt.leading + opt.content; } } if (hasConsumed) { let newTokens = []; md.inline.parse(updatedAlt.trimStart(), state.md, state.env, newTokens); token.children = newTokens; } } }; md.core.process = state => { try { refCount += 1; return process.call(md.core, state); } finally { refCount -= 1; if (refCount === 0) { // Apply finalization for every tokens for (const token of state.tokens) finalizeTokenAttr(token, state); } } }; md.inline.ruler2.push('marpit_parse_image', ({ tokens }) => { for (const token of tokens) { if (token.type === 'image') { // Parse alt text as options const optsBase = token.content.split(/(\s+)/); let currentIdx = 0; let leading = ''; const options = optsBase.reduce((acc, opt, i) => { if (i % 2 === 0 && opt.length > 0) { currentIdx += leading.length; acc.push({ content: opt, index: currentIdx, leading, consumed: false }); leading = ''; currentIdx += opt.length; } else { leading += opt; } return acc; }, []); const url = token.attrGet('src'); token.meta = token.meta || {}; token.meta.marpitImage = { ...(token.meta.marpitImage || {}), url: url.toString(), options }; // Parse keyword through matchers for (const opt of options) { for (const [regexp, mergeFunc] of optionMatchers) { if (opt.consumed) continue; const matched = opt.content.match(regexp); if (matched) { opt.consumed = true; token.meta.marpitImage = { ...token.meta.marpitImage, ...mergeFunc(matched, { filters: [], ...token.meta.marpitImage }) }; } } } } } }); } const parseImage = exports.parseImage = (0, _plugin.default)(_parseImage); var _default = exports.default = parseImage;