Skip to content

Commit 49bcceb

Browse files
authored
Merge branch 'dev' into rss1102/fix/build/rpllup
2 parents 8de2644 + 3110c04 commit 49bcceb

File tree

9 files changed

+141
-73
lines changed

9 files changed

+141
-73
lines changed

.changeset/odd-rivers-swim.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'cherry-markdown': patch
3+
---
4+
5+
feat: onPaste粘贴的回调函数支持异步回调 #1595

examples/assets/scripts/index-demo.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,16 @@ const basicConfig = {
480480
console.log(`url-processor`, url, srcType);
481481
return url;
482482
},
483+
// onPaste: (clipboardData, cherry, callback) => {
484+
// setTimeout(() => {
485+
// callback({
486+
// html: '<span>hello <strong>world</strong> !</span>',
487+
// htmlText: 'hello world !',
488+
// mdText: '',
489+
// });
490+
// }, 1000)
491+
// return '<<正在处理中...>>';
492+
// },
483493
},
484494
editor: {
485495
id: 'cherry-text',

packages/cherry-markdown/index.html

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,27 @@
8383

8484
switch (currentPath) {
8585
case '/':
86-
CONFIG = Object.assign({}, basicConfig, { value: indexMd, ...devCompatibleConfig });
86+
basicConfig.callback.urlProcessor = devCompatibleConfig.callback.urlProcessor;
87+
CONFIG = Object.assign({}, basicConfig, { value: indexMd });
8788
break;
8889
case '/h5.html':
89-
CONFIG = Object.assign({}, h5Config, { value: indexMd, ...devCompatibleConfig });
90+
h5Config.callback.urlProcessor = devCompatibleConfig.callback.urlProcessor;
91+
CONFIG = Object.assign({}, h5Config, { value: indexMd });
9092
break;
9193
case '/multiple.html':
92-
CONFIG = Object.assign({}, multipleCherryConfig1, { value: indexMd, ...devCompatibleConfig });
94+
multipleCherryConfig1.callback.urlProcessor = devCompatibleConfig.callback.urlProcessor;
95+
CONFIG = Object.assign({}, multipleCherryConfig1, { value: indexMd });
9396

9497
const CONFIG2 = Object.assign({}, multipleCherryConfig2, { value: indexMd, ...devCompatibleConfig });
9598
window.cherry = new Cherry(CONFIG2);
9699
break;
97100
case '/notoolbar.html':
98-
CONFIG = Object.assign({}, noToolbarConfig, { value: indexMd, ...devCompatibleConfig });
101+
noToolbarConfig.callback.urlProcessor = devCompatibleConfig.callback.urlProcessor;
102+
CONFIG = Object.assign({}, noToolbarConfig, { value: indexMd });
99103
break;
100104
case '/preview_only.html':
101-
CONFIG = Object.assign({}, previewConfig, { value: indexMd, ...devCompatibleConfig });
105+
previewConfig.callback.urlProcessor = devCompatibleConfig.callback.urlProcessor;
106+
CONFIG = Object.assign({}, previewConfig, { value: indexMd });
102107
break;
103108
case '/xss.html':
104109
CONFIG = {
@@ -112,7 +117,8 @@
112117
};
113118
break;
114119
case '/img.html':
115-
CONFIG = Object.assign({}, imgConfig, { value: imgMd, ...devCompatibleConfig });
120+
imgConfig.callback.urlProcessor = devCompatibleConfig.callback.urlProcessor;
121+
CONFIG = Object.assign({}, imgConfig, { value: imgMd });
116122
break;
117123
case '/table.html':
118124
CONFIG = {

packages/cherry-markdown/src/Editor.js

Lines changed: 98 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { addEvent } from './utils/event';
3939
import Logger from '@/Logger';
4040
import { handleFileUploadCallback } from '@/utils/file';
4141
import { createElement } from './utils/dom';
42-
import { longTextReg, base64Reg, imgDrawioXmlReg, createUrlReg } from './utils/regexp';
42+
import { longTextReg, base64Reg, imgDrawioXmlReg, createUrlReg, pasteWrapperReg } from './utils/regexp';
4343
import { handleNewlineIndentList } from './utils/autoindent';
4444

4545
/**
@@ -153,6 +153,17 @@ export default class Editor {
153153
if (this.$cherry.status.editor === 'hide') {
154154
return;
155155
}
156+
this.formatBigData2Mark(pasteWrapperReg, 'cm-url paste-wrapper', (target, oneSearch) => {
157+
const whole = oneSearch[0] ?? '';
158+
const id = oneSearch[1] ?? '';
159+
const bigString = oneSearch[2] ?? '';
160+
const targetChFrom = target.ch;
161+
const targetChTo = targetChFrom + whole.length;
162+
const targetLine = target.line;
163+
const begin = { line: targetLine, ch: targetChFrom };
164+
const end = { line: targetLine, ch: targetChTo };
165+
return { bigString, begin, end, id };
166+
});
156167
this.formatBigData2Mark(base64Reg, 'cm-url base64');
157168
this.formatBigData2Mark(imgDrawioXmlReg, 'cm-url drawio');
158169
this.formatBigData2Mark(longTextReg, 'cm-url long-text');
@@ -168,8 +179,22 @@ export default class Editor {
168179
* 把大字符串变成省略号
169180
* @param {*} reg 正则
170181
* @param {*} className 利用codemirror的MarkText生成的新元素的class
182+
* @param {function} getBeginEnd 获取begin和end的函数
171183
*/
172-
formatBigData2Mark = (reg, className) => {
184+
formatBigData2Mark = (
185+
reg,
186+
className,
187+
getBeginEnd = (target, oneSearch) => {
188+
const bigString = oneSearch[2] ?? '';
189+
const targetChFrom = target.ch + oneSearch[1]?.length;
190+
const targetChTo = targetChFrom + bigString.length;
191+
const targetLine = target.line;
192+
const begin = { line: targetLine, ch: targetChFrom };
193+
const end = { line: targetLine, ch: targetChTo };
194+
const id = '';
195+
return { bigString, begin, end, id };
196+
},
197+
) => {
173198
const codemirror = this.editor;
174199
const searcher = codemirror.getSearchCursor(reg);
175200

@@ -179,17 +204,12 @@ export default class Editor {
179204
if (!target) {
180205
continue;
181206
}
182-
const bigString = oneSearch[2] ?? '';
183-
const targetChFrom = target.ch + oneSearch[1]?.length;
184-
const targetChTo = targetChFrom + bigString.length;
185-
const targetLine = target.line;
186-
const begin = { line: targetLine, ch: targetChFrom };
187-
const end = { line: targetLine, ch: targetChTo };
207+
const { bigString, begin, end, id } = getBeginEnd(target, oneSearch);
188208
// 如果所在区域已经有mark了,则不再增加mark
189209
if (codemirror.findMarks(begin, end).length > 0) {
190210
continue;
191211
}
192-
const newSpan = createElement('span', `cm-string ${className}`, { title: bigString });
212+
const newSpan = createElement('span', `cm-string ${className}`, { title: bigString, 'data-id': id });
193213
newSpan.textContent = bigString;
194214
codemirror.markText(begin, end, { replacedWith: newSpan, atomic: true });
195215
}
@@ -285,32 +305,72 @@ export default class Editor {
285305
/**
286306
*
287307
* @param {ClipboardEvent} e
288-
* @param {CodeMirror.Editor} codemirror
289308
*/
290-
onPaste(e, codemirror) {
309+
onPaste(e) {
291310
let { clipboardData } = e;
292-
if (clipboardData) {
293-
this.handlePaste(e, clipboardData, codemirror);
294-
} else {
311+
if (!clipboardData) {
295312
({ clipboardData } = window);
296-
this.handlePaste(e, clipboardData, codemirror);
313+
}
314+
const needHandlePaste = this.handleThirdPaste(e, clipboardData);
315+
if (needHandlePaste) {
316+
this.handlePaste(e, clipboardData);
317+
}
318+
}
319+
320+
onPasteCallback({ html, htmlText, mdText }) {
321+
// @ts-ignore
322+
const { randomId, _this } = this;
323+
const allMarks = _this.editor.getAllMarks();
324+
for (let i = 0; i < allMarks.length; i++) {
325+
const mark = allMarks[i];
326+
const span = mark.widgetNode.querySelector(`.paste-wrapper[data-id="${randomId}"]`);
327+
if (span) {
328+
const { from, to } = mark.find();
329+
mark.clear();
330+
_this.editor.setSelection(from, to);
331+
if (mdText) {
332+
_this.editor.replaceSelection(mdText, 'end');
333+
} else {
334+
_this.formatHtml2MdWhenPaste(null, html, htmlText);
335+
}
336+
break;
337+
}
297338
}
298339
}
299340

341+
/**
342+
* 调用第三方的粘贴回调
343+
* @returns {boolean} true: 需要继续处理粘贴内容,false: 不需要继续处理粘贴内容
344+
*/
345+
handleThirdPaste(event, clipboardData) {
346+
// 生成一个随机id,用于有可能的异步回调
347+
const randomId = `cherry-paste-${Math.random().toString(36).slice(2)}${new Date().getTime()}`;
348+
const onPasteRet = this.$cherry.options.callback.onPaste(
349+
clipboardData,
350+
this.$cherry,
351+
this.onPasteCallback.bind({ randomId, _this: this }),
352+
);
353+
if (onPasteRet !== false && typeof onPasteRet === 'string') {
354+
event.preventDefault();
355+
// 是否命中语法糖
356+
if (/^<<[\s\S]+>>$/.test(onPasteRet)) {
357+
const newText = `{{${randomId}|${onPasteRet.replace(/^<<([\s\S]+)>>$/, (whole, $1) => `<<${$1.replace(/[<>]/g, '')}>>`)}}}`;
358+
this.editor.replaceSelection(newText);
359+
} else {
360+
this.editor.replaceSelection(onPasteRet, 'around');
361+
}
362+
return false;
363+
}
364+
return true;
365+
}
366+
300367
/**
301368
*
302369
* @param {ClipboardEvent} event
303370
* @param {ClipboardEvent['clipboardData']} clipboardData
304-
* @param {CodeMirror.Editor} codemirror
305371
* @returns {boolean | void}
306372
*/
307-
handlePaste(event, clipboardData, codemirror) {
308-
const onPasteRet = this.$cherry.options.callback.onPaste(clipboardData, this.$cherry);
309-
if (onPasteRet !== false && typeof onPasteRet === 'string') {
310-
event.preventDefault();
311-
codemirror.replaceSelection(onPasteRet);
312-
return;
313-
}
373+
handlePaste(event, clipboardData) {
314374
let html = clipboardData.getData('Text/Html');
315375
const { items } = clipboardData;
316376

@@ -331,7 +391,6 @@ export default class Editor {
331391
) {
332392
html = '';
333393
}
334-
const codemirrorDoc = codemirror.getDoc();
335394
this.fileUploadCount = 0;
336395
// 只要有html内容,就不处理剪切板里的其他内容,这么做的后果是粘贴excel内容时,只会粘贴html内容,不会把excel对应的截图粘进来
337396
for (let i = 0; !html && i < items.length; i++) {
@@ -346,14 +405,14 @@ export default class Editor {
346405
return;
347406
}
348407
const mdStr = `${this.fileUploadCount > 1 ? '\n' : ''}${handleFileUploadCallback(url, params, file)}`;
349-
codemirrorDoc.replaceSelection(mdStr);
408+
this.editor.replaceSelection(mdStr, 'end');
350409
// if (this.pasterHtml) {
351410
// // 如果同时粘贴了html内容和文件内容,则在文件上传完成后强制让光标处于非选中状态,以防止自动选中的html内容被文件内容替换掉
352411
// const { line, ch } = codemirror.getCursor();
353412
// codemirror.setSelection({ line, ch }, { line, ch });
354-
// codemirrorDoc.replaceSelection(mdStr, 'end');
413+
// this.editor.replaceSelection(mdStr, 'end');
355414
// } else {
356-
// codemirrorDoc.replaceSelection(mdStr);
415+
// this.editor.replaceSelection(mdStr);
357416
// }
358417
});
359418
event.preventDefault();
@@ -365,23 +424,22 @@ export default class Editor {
365424
if (!html || !this.options.convertWhenPaste) {
366425
return true;
367426
}
427+
this.formatHtml2MdWhenPaste(event, html, htmlText);
428+
}
368429

430+
formatHtml2MdWhenPaste(event, html, htmlText) {
369431
let divObj = document.createElement('DIV');
370432
divObj.innerHTML = html;
371-
html = divObj.innerHTML;
372-
const mdText = htmlParser.run(html);
433+
const mdText = htmlParser.run(divObj.innerHTML);
373434
if (typeof mdText === 'string' && mdText.trim().length > 0) {
374-
const range = codemirror.listSelections();
375-
if (codemirror.getSelections().length <= 1 && range[0] && range[0].anchor) {
376-
const currentCursor = {};
377-
currentCursor.line = range[0].anchor.line;
378-
currentCursor.ch = range[0].anchor.ch;
379-
codemirrorDoc.replaceSelection(mdText);
380-
pasteHelper.showSwitchBtnAfterPasteHtml(this.$cherry, currentCursor, codemirror, htmlText, mdText);
435+
const range = this.editor.listSelections();
436+
if (this.editor.getSelections().length <= 1 && range[0] && range[0].anchor) {
437+
this.editor.replaceSelection(mdText, 'around');
438+
pasteHelper.showSwitchBtnAfterPasteHtml(this.$cherry.locale, this.editor, htmlText, mdText);
381439
} else {
382-
codemirrorDoc.replaceSelection(mdText);
440+
this.editor.replaceSelection(mdText, 'around');
383441
}
384-
event.preventDefault();
442+
event && event.preventDefault();
385443
}
386444
divObj = null;
387445
}
@@ -505,7 +563,7 @@ export default class Editor {
505563
});
506564

507565
editor.on('paste', (codemirror, evt) => {
508-
this.options.onPaste.call(this, evt, codemirror);
566+
this.options.onPaste.call(this, evt);
509567
});
510568

511569
if (this.options.autoScrollByCursor) {
@@ -537,7 +595,7 @@ export default class Editor {
537595
const mdStr = handleFileUploadCallback(url, params, file);
538596
// 当批量上传文件时,每个被插入的文件中间需要加个换行,但单个上传文件的时候不需要加换行
539597
const insertValue = i > 0 ? `\n${mdStr} ` : `${mdStr} `;
540-
codemirror.replaceSelection(insertValue);
598+
codemirror.replaceSelection(insertValue, 'end');
541599
this.dealSpecialWords();
542600
});
543601
}

packages/cherry-markdown/src/Engine.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import NestedError, { $expectTarget, $expectInherit, $expectInstance } from './u
1919
import CryptoJS from 'crypto-js';
2020
import SyntaxBase from './core/SyntaxBase';
2121
import ParagraphBase from './core/ParagraphBase';
22-
import { PUNCTUATION, longTextReg, imgBase64Reg, imgDrawioXmlReg, base64Reg } from './utils/regexp';
22+
import { PUNCTUATION, longTextReg, imgBase64Reg, imgDrawioXmlReg, base64Reg, pasteWrapperReg } from './utils/regexp';
2323
import { escapeHTMLSpecialChar } from './utils/sanitize';
2424
import Logger from './Logger';
2525
import { configureMathJax } from './utils/mathjax';
@@ -371,6 +371,7 @@ export default class Engine {
371371
this.cachedBigData[cacheKey] = m2;
372372
return `${m1}${cacheKey}}`;
373373
});
374+
$md = $md.replace(pasteWrapperReg, '');
374375
return $md;
375376
}
376377

packages/cherry-markdown/src/utils/pasteHelper.js

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,16 @@ const pasteHelper = {
2727
/**
2828
* 核心方法,粘贴后展示切换按钮
2929
* 只有粘贴html时才会出现切换按钮
30-
* @param {Object} currentCursor 当前的光标位置
3130
* @param {Object} editor 编辑器对象
3231
* @param {string} html html里的纯文本内容
3332
* @param {string} md html对应的markdown源码
3433
* @returns
3534
*/
36-
showSwitchBtnAfterPasteHtml($cherry, currentCursor, editor, html, md) {
35+
showSwitchBtnAfterPasteHtml(locale, editor, html, md) {
3736
if (html.trim() === md.trim()) {
3837
return;
3938
}
40-
this.init($cherry, currentCursor, editor, html, md);
41-
this.setSelection();
39+
this.init(locale, editor, html, md);
4240
this.bindListener();
4341
this.initBubble();
4442
this.showBubble();
@@ -48,13 +46,11 @@ const pasteHelper = {
4846
}
4947
},
5048

51-
init($cherry, currentCursor, editor, html, md) {
52-
this.$cherry = $cherry;
49+
init(locale, editor, html, md) {
5350
this.html = html;
5451
this.md = md;
5552
this.codemirror = editor;
56-
this.currentCursor = currentCursor;
57-
this.locale = $cherry.locale;
53+
this.locale = locale;
5854
},
5955

6056
/**
@@ -77,14 +73,6 @@ const pasteHelper = {
7773
localStorage.setItem('cherry-paste-type', type);
7874
},
7975

80-
/**
81-
* 在编辑器中自动选中刚刚粘贴的内容
82-
*/
83-
setSelection() {
84-
const { /* sticky, xRel, */ ...end } = this.codemirror.getCursor();
85-
const begin = this.currentCursor;
86-
this.codemirror.setSelection(begin, end);
87-
},
8876
/**
8977
* 绑定事件
9078
* 当编辑器选中区域改变、内容改变时,隐藏切换按钮
@@ -215,8 +203,7 @@ const pasteHelper = {
215203
}
216204
this.noHide = true;
217205
this.bubbleDom.setAttribute('data-type', 'md');
218-
this.codemirror.doc.replaceSelection(this.md);
219-
this.setSelection();
206+
this.codemirror.doc.replaceSelection(this.md, 'around');
220207
this.showBubble();
221208
this.switchMd.classList.add('active');
222209
this.switchText.classList.remove('active');
@@ -230,8 +217,7 @@ const pasteHelper = {
230217
// }
231218
this.noHide = true;
232219
this.bubbleDom.setAttribute('data-type', 'text');
233-
this.codemirror.doc.replaceSelection(this.html);
234-
this.setSelection();
220+
this.codemirror.doc.replaceSelection(this.html, 'around');
235221
this.showBubble();
236222
this.switchText.classList.add('active');
237223
this.switchMd.classList.remove('active');

packages/cherry-markdown/src/utils/regexp.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ export function getDetailRule() {
259259
return ret;
260260
}
261261

262+
export const pasteWrapperReg = /{{(cherry-paste-[^|\n]+?)\|<<([^>]+?)>>}}/g;
263+
262264
// 匹配图片URL里的base64,[name](data:image/png;base64,xxx) 和 ![alt](data:image/png;base64,xxx) 这两种形式的都处理
263265
export const imgBase64Reg = /(\[[^\n]*?\]\(data:image\/[a-z]{1,10};base64,)([^)]+)\)/g;
264266

0 commit comments

Comments
 (0)