From 05c6cab6383e3d56ccaedcfc1966528a088ece41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sunsonliu=28=E5=88=98=E9=98=B3=29?= Date: Fri, 3 Jan 2025 16:56:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20#947=20urlProcessor=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=AC=AC=E4=B8=89=E4=B8=AA=E5=8F=82=E6=95=B0=EF=BC=8C=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E4=B8=AA=E5=8F=82=E6=95=B0=E4=B8=BA=E5=9B=9E=E8=B0=83?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Engine.js | 46 +++++++++++++++++++++++++++++++++++++ src/UrlCache.js | 6 ++--- src/core/hooks/AutoLink.js | 2 +- src/core/hooks/HtmlBlock.js | 8 +++---- src/core/hooks/Image.js | 4 ++-- src/core/hooks/Link.js | 2 +- types/cherry.d.ts | 4 ++-- 7 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/Engine.js b/src/Engine.js index 9969de26..78d0573a 100644 --- a/src/Engine.js +++ b/src/Engine.js @@ -53,6 +53,52 @@ export default class Engine { this.currentStrMd5 = []; this.globalConfig = markdownParams.engine.global; this.htmlWhiteListAppend = this.globalConfig.htmlWhiteList; + this.urlProcessorMap = {}; + } + + /** + * 重新生成html + * 这是为urlProcessor支持异步回调函数而实现的重新生成html的方法 + * 该方法会清空所有缓存,所以降低了该方法的执行频率,1s内最多执行一次 + */ + reMakeHtml() { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + this.timer = setTimeout(() => { + this.$cherry.lastMarkdownText = ''; + this.hashCache = {}; + const markdownText = this.$cherry.editor.editor.getValue(); + const html = this.makeHtml(markdownText); + this.$cherry.previewer.refresh(html); + this.$cherry.$event.emit('afterChange', { + markdownText, + html, + }); + }, 1000); + } + + urlProcessor(url, srcType) { + const key = `${srcType}_${url}`; + if (this.urlProcessorMap[key]) { + return this.urlProcessorMap[key]; + } + const ret = this.$cherry.options.callback.urlProcessor(url, srcType, (/** @type {string} */ newUrl) => { + if (newUrl) { + if (!this.urlProcessorMap[key]) { + this.urlProcessorMap[key] = newUrl; + this.reMakeHtml(); + } + } else { + delete this.urlProcessorMap[key]; + } + return; + }); + if (ret) { + return ret; + } + return url; } initMath(opts) { diff --git a/src/UrlCache.js b/src/UrlCache.js index fd977cb8..8286a972 100644 --- a/src/UrlCache.js +++ b/src/UrlCache.js @@ -19,12 +19,12 @@ let urlCache = {}; const cherryInnerLinkRegex = /^cherry-inner:\/\/([0-9a-f]+)$/i; export function urlProcessorProxy(urlProcessor) { - return function (url, srcType) { + return function (url, srcType, callback) { if (UrlCache.isInnerLink(url)) { - const newUrl = urlProcessor(UrlCache.get(url), srcType); + const newUrl = urlProcessor(UrlCache.get(url), srcType, callback); return UrlCache.replace(url, newUrl); } - return urlProcessor(url, srcType); + return urlProcessor(url, srcType, callback); }; } diff --git a/src/core/hooks/AutoLink.js b/src/core/hooks/AutoLink.js index 0ea65657..25b49012 100644 --- a/src/core/hooks/AutoLink.js +++ b/src/core/hooks/AutoLink.js @@ -221,7 +221,7 @@ export default class AutoLink extends SyntaxBase { linkText = url; } } - const processedURL = this.$engine.$cherry.options.callback.urlProcessor(url, 'autolink'); + const processedURL = this.$engine.urlProcessor(url, 'autolink'); const safeUri = encodeURIOnce(processedURL); const displayUri = $e(linkText); const additionalAttrs = [this.target, this.rel].filter(Boolean).join(' '); diff --git a/src/core/hooks/HtmlBlock.js b/src/core/hooks/HtmlBlock.js index 4d83559c..594f88ce 100644 --- a/src/core/hooks/HtmlBlock.js +++ b/src/core/hooks/HtmlBlock.js @@ -92,19 +92,19 @@ export default class HtmlBlock extends ParagraphBase { let wholeStr = whole; // 识别标签的href和src属性,并触发urlProcessor回调 m1.replace(/^a .*? href="([^"]+)"/, (all, href) => { - const processedURL = this.$engine.$cherry.options.callback.urlProcessor(href, 'link'); + const processedURL = this.$engine.urlProcessor(href, 'link'); wholeStr = wholeStr.replace(/ href="[^"]+"/, ` href="${processedURL}"`); }); m1.replace(/^a href="([^"]+)"/, (all, href) => { - const processedURL = this.$engine.$cherry.options.callback.urlProcessor(href, 'link'); + const processedURL = this.$engine.urlProcessor(href, 'link'); wholeStr = wholeStr.replace(/ href="[^"]+"/, ` href="${processedURL}"`); }); m1.replace(/^img .*? src="([^"]+)"/, (all, src) => { - const processedURL = this.$engine.$cherry.options.callback.urlProcessor(src, 'image'); + const processedURL = this.$engine.urlProcessor(src, 'image'); wholeStr = wholeStr.replace(/ src="[^"]+"/, ` src="${processedURL}"`); }); m1.replace(/^img src="([^"]+)"/, (all, src) => { - const processedURL = this.$engine.$cherry.options.callback.urlProcessor(src, 'image'); + const processedURL = this.$engine.urlProcessor(src, 'image'); wholeStr = wholeStr.replace(/ src="[^"]+"/, ` src="${processedURL}"`); }); diff --git a/src/core/hooks/Image.js b/src/core/hooks/Image.js index eebf6097..6c817141 100644 --- a/src/core/hooks/Image.js +++ b/src/core/hooks/Image.js @@ -53,7 +53,7 @@ export default class Image extends SyntaxBase { attrs += ` poster="${encodeURIOnce(poster)}"`; } - const processedURL = this.$engine.$cherry.options.callback.urlProcessor(link, type); + const processedURL = this.$engine.urlProcessor(link, type); const defaultWrapper = `<${type} src="${UrlCache.set( encodeURIOnce(processedURL), )}"${attrs} ${extent} ${style} ${classes} controls="controls">${$e(alt || '')}`; @@ -98,7 +98,7 @@ export default class Image extends SyntaxBase { .replace(/&/g, '&') // 对&多做一次转义,cherry现有的机制会自动把&转成&,只有多做一次转义才能抵消cherry的机制 : ''; return `${leadingChar}${$e(alt || '')}`; } // should never happen diff --git a/src/core/hooks/Link.js b/src/core/hooks/Link.js index 79ba4d07..b873ee9b 100644 --- a/src/core/hooks/Link.js +++ b/src/core/hooks/Link.js @@ -91,7 +91,7 @@ export default class Link extends SyntaxBase { const processedText = coreText.replace(/~1D/g, '~D'); // 还原替换的$符号 // text可能是html标签,依赖htmlBlock进行处理 if (isValidScheme(processedURL)) { - processedURL = this.$engine.$cherry.options.callback.urlProcessor(processedURL, 'link'); + processedURL = this.$engine.urlProcessor(processedURL, 'link'); processedURL = encodeURIOnce(processedURL); return `${leadingChar + extraLeadingChar} string; + urlProcessor?: (url: string, srcType: 'image' | 'audio' | 'video' | 'autolink' | 'link', callback?: any) => string; /** 文件上传回调 */ fileUpload?: CherryFileUploadHandler; /** 多文件上传回调 */ @@ -219,7 +219,7 @@ export interface CherryEngineOptions { * @param url 来源url * @param srcType 来源类型 */ - urlProcessor?: (url: string, srcType: 'image' | 'audio' | 'video' | 'autolink' | 'link') => string; + urlProcessor?: (url: string, srcType: 'image' | 'audio' | 'video' | 'autolink' | 'link', callback?: any) => string; /** * 额外允许渲染的html标签 * 标签以英文竖线分隔,如:htmlWhiteList: 'iframe|script|style'