cherry-muse 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +162 -0
- package/README.md +139 -0
- package/dist/addons/cherry-code-block-card-plugin.js +1 -0
- package/dist/addons/cherry-code-block-echarts-plugin.js +1 -0
- package/dist/addons/cherry-code-block-mermaid-plugin.js +1 -0
- package/dist/cherry-markdown.core.common.js +1 -0
- package/dist/cherry-markdown.core.js +1 -0
- package/dist/cherry-markdown.css +4089 -0
- package/dist/cherry-markdown.engine.core.common.js +1 -0
- package/dist/cherry-markdown.engine.core.esm.js +1 -0
- package/dist/cherry-markdown.engine.core.js +1 -0
- package/dist/cherry-markdown.esm.js +1 -0
- package/dist/cherry-markdown.js +70837 -0
- package/dist/cherry-markdown.js.map +1 -0
- package/dist/cherry-markdown.min.css +1 -0
- package/dist/cherry-markdown.min.js +1 -0
- package/dist/stats.html +4838 -0
- package/examples/scripts/pinyin/README.md +53 -0
- package/package.json +167 -0
- package/src/Cherry.config.js +411 -0
- package/src/Cherry.js +788 -0
- package/src/CherryStatic.js +70 -0
- package/src/Editor.js +746 -0
- package/src/Engine.js +334 -0
- package/src/Event.js +74 -0
- package/src/Factory.js +180 -0
- package/src/Logger.js +31 -0
- package/src/Previewer.js +1147 -0
- package/src/Sanitizer.js +4 -0
- package/src/Sanitizer.node.js +7 -0
- package/src/Stats.js +101 -0
- package/src/Theme.js +46 -0
- package/src/UrlCache.js +98 -0
- package/src/addons/cherry-code-block-card-plugin.js +213 -0
- package/src/addons/cherry-code-block-echarts-plugin.js +161 -0
- package/src/addons/cherry-code-block-mermaid-plugin.js +118 -0
- package/src/core/HookCenter.js +303 -0
- package/src/core/HooksConfig.js +106 -0
- package/src/core/ParagraphBase.js +314 -0
- package/src/core/SentenceBase.js +65 -0
- package/src/core/SyntaxBase.js +197 -0
- package/src/core/hooks/AutoLink.js +251 -0
- package/src/core/hooks/BackgroundColor.js +46 -0
- package/src/core/hooks/Badge.js +100 -0
- package/src/core/hooks/Blockquote.js +113 -0
- package/src/core/hooks/Br.js +85 -0
- package/src/core/hooks/CodeBlock.js +876 -0
- package/src/core/hooks/Color.js +78 -0
- package/src/core/hooks/CommentReference.js +96 -0
- package/src/core/hooks/Detail.js +138 -0
- package/src/core/hooks/Emoji.config.js +9388 -0
- package/src/core/hooks/Emoji.js +223 -0
- package/src/core/hooks/Emphasis.js +113 -0
- package/src/core/hooks/Footnote.js +125 -0
- package/src/core/hooks/FrontMatter.js +52 -0
- package/src/core/hooks/FrontMatterVars.js +82 -0
- package/src/core/hooks/Header.js +229 -0
- package/src/core/hooks/HighLight.js +37 -0
- package/src/core/hooks/Hr.js +52 -0
- package/src/core/hooks/HtmlBlock.js +159 -0
- package/src/core/hooks/Iframe.js +80 -0
- package/src/core/hooks/Image.js +276 -0
- package/src/core/hooks/InlineCode.js +45 -0
- package/src/core/hooks/InlineMath.js +142 -0
- package/src/core/hooks/Link.js +169 -0
- package/src/core/hooks/List.js +260 -0
- package/src/core/hooks/Mark.js +55 -0
- package/src/core/hooks/MathBlock.js +97 -0
- package/src/core/hooks/Panel.js +170 -0
- package/src/core/hooks/Paragraph.js +84 -0
- package/src/core/hooks/Ruby.js +34 -0
- package/src/core/hooks/Size.js +84 -0
- package/src/core/hooks/Strikethrough.js +54 -0
- package/src/core/hooks/Sub.js +47 -0
- package/src/core/hooks/SuggestList.js +317 -0
- package/src/core/hooks/Suggester.js +759 -0
- package/src/core/hooks/Sup.js +47 -0
- package/src/core/hooks/Table.js +315 -0
- package/src/core/hooks/Toc.js +290 -0
- package/src/core/hooks/Transfer.js +47 -0
- package/src/core/hooks/Underline.js +37 -0
- package/src/index.core.js +29 -0
- package/src/index.engine.core.js +62 -0
- package/src/index.engine.js +30 -0
- package/src/index.js +28 -0
- package/src/locales/index.js +21 -0
- package/src/locales/zh_CN.js +170 -0
- package/src/sass/cherry.scss +122 -0
- package/src/sass/components/bubble.scss +122 -0
- package/src/sass/components/codemirror.scss +628 -0
- package/src/sass/components/dropdown.scss +37 -0
- package/src/sass/components/editor.scss +78 -0
- package/src/sass/components/preview.scss +71 -0
- package/src/sass/components/prism.scss +142 -0
- package/src/sass/components/stats.scss +32 -0
- package/src/sass/components/toc.scss +184 -0
- package/src/sass/components/toolbar.scss +117 -0
- package/src/sass/core/AutoLink.scss +20 -0
- package/src/sass/core/BackgroundColor.scss +0 -0
- package/src/sass/core/Badge.scss +116 -0
- package/src/sass/core/Blockquote.scss +12 -0
- package/src/sass/core/Br.scss +0 -0
- package/src/sass/core/Card.scss +219 -0
- package/src/sass/core/CodeBlock.scss +205 -0
- package/src/sass/core/Color.scss +37 -0
- package/src/sass/core/CommentReference.scss +0 -0
- package/src/sass/core/Detail.scss +107 -0
- package/src/sass/core/Emoji.scss +127 -0
- package/src/sass/core/Emphasis.scss +9 -0
- package/src/sass/core/Footnote.scss +21 -0
- package/src/sass/core/FrontMatterVars.scss +19 -0
- package/src/sass/core/Header.scss +103 -0
- package/src/sass/core/HighLight.scss +0 -0
- package/src/sass/core/Hr.scss +10 -0
- package/src/sass/core/HtmlBlock.scss +0 -0
- package/src/sass/core/Iframe.scss +36 -0
- package/src/sass/core/Image.scss +59 -0
- package/src/sass/core/InlineCode.scss +10 -0
- package/src/sass/core/InlineMath.scss +11 -0
- package/src/sass/core/Link.scss +16 -0
- package/src/sass/core/List.scss +61 -0
- package/src/sass/core/Mark.scss +15 -0
- package/src/sass/core/MathBlock.scss +0 -0
- package/src/sass/core/Panel.scss +150 -0
- package/src/sass/core/Paragraph.scss +6 -0
- package/src/sass/core/Ruby.scss +0 -0
- package/src/sass/core/Size.scss +8 -0
- package/src/sass/core/Strikethrough.scss +0 -0
- package/src/sass/core/Sub.scss +5 -0
- package/src/sass/core/Suggester.scss +62 -0
- package/src/sass/core/Sup.scss +5 -0
- package/src/sass/core/Table.scss +127 -0
- package/src/sass/core/Toc.scss +28 -0
- package/src/sass/core/Transfer.scss +0 -0
- package/src/sass/core/Underline.scss +0 -0
- package/src/sass/google-fonts.scss +34 -0
- package/src/sass/index.scss +3 -0
- package/src/sass/prism/dark.scss +131 -0
- package/src/sass/prism/light.scss +143 -0
- package/src/sass/variables/colors.scss +96 -0
- package/src/toolbars/Bubble.js +232 -0
- package/src/toolbars/BubbleTable.js +147 -0
- package/src/toolbars/HookCenter.js +185 -0
- package/src/toolbars/MenuBase.js +357 -0
- package/src/toolbars/PreviewerBubble.js +558 -0
- package/src/toolbars/Toc.js +246 -0
- package/src/toolbars/Toolbar.js +401 -0
- package/src/toolbars/hooks/Audio.js +53 -0
- package/src/toolbars/hooks/Badge.js +80 -0
- package/src/toolbars/hooks/BarTable.js +41 -0
- package/src/toolbars/hooks/Bold.js +70 -0
- package/src/toolbars/hooks/Br.js +34 -0
- package/src/toolbars/hooks/Card.js +64 -0
- package/src/toolbars/hooks/CheckList.js +41 -0
- package/src/toolbars/hooks/Code.js +46 -0
- package/src/toolbars/hooks/Color.js +285 -0
- package/src/toolbars/hooks/Copy.js +139 -0
- package/src/toolbars/hooks/Detail.js +70 -0
- package/src/toolbars/hooks/ECharts.js +303 -0
- package/src/toolbars/hooks/Emoji.js +303 -0
- package/src/toolbars/hooks/Export.js +47 -0
- package/src/toolbars/hooks/File.js +54 -0
- package/src/toolbars/hooks/Formula.js +36 -0
- package/src/toolbars/hooks/FullScreen.js +55 -0
- package/src/toolbars/hooks/Graph.js +281 -0
- package/src/toolbars/hooks/H1.js +71 -0
- package/src/toolbars/hooks/H2.js +71 -0
- package/src/toolbars/hooks/H3.js +71 -0
- package/src/toolbars/hooks/Header.js +100 -0
- package/src/toolbars/hooks/Hr.js +35 -0
- package/src/toolbars/hooks/Iframe.js +35 -0
- package/src/toolbars/hooks/Image.js +60 -0
- package/src/toolbars/hooks/Insert.js +36 -0
- package/src/toolbars/hooks/Italic.js +70 -0
- package/src/toolbars/hooks/LineTable.js +41 -0
- package/src/toolbars/hooks/Link.js +46 -0
- package/src/toolbars/hooks/List.js +55 -0
- package/src/toolbars/hooks/Ol.js +41 -0
- package/src/toolbars/hooks/Panel.js +155 -0
- package/src/toolbars/hooks/Quote.js +45 -0
- package/src/toolbars/hooks/Redo.js +33 -0
- package/src/toolbars/hooks/Ruby.js +59 -0
- package/src/toolbars/hooks/Size.js +100 -0
- package/src/toolbars/hooks/Split.js +37 -0
- package/src/toolbars/hooks/Strikethrough.js +65 -0
- package/src/toolbars/hooks/Sub.js +58 -0
- package/src/toolbars/hooks/Sup.js +58 -0
- package/src/toolbars/hooks/SwitchModel.js +78 -0
- package/src/toolbars/hooks/Table.js +56 -0
- package/src/toolbars/hooks/Toc.js +35 -0
- package/src/toolbars/hooks/TogglePreview.js +79 -0
- package/src/toolbars/hooks/Ul.js +41 -0
- package/src/toolbars/hooks/Underline.js +65 -0
- package/src/toolbars/hooks/Undo.js +30 -0
- package/src/toolbars/hooks/Video.js +53 -0
- package/src/utils/LazyLoadImg.js +341 -0
- package/src/utils/autoindent.js +58 -0
- package/src/utils/codeBlockContentHandler.js +351 -0
- package/src/utils/config.js +98 -0
- package/src/utils/copy.js +55 -0
- package/src/utils/dialog.js +196 -0
- package/src/utils/dom.js +162 -0
- package/src/utils/downloadUtil.js +23 -0
- package/src/utils/env.js +22 -0
- package/src/utils/error.js +61 -0
- package/src/utils/event.js +38 -0
- package/src/utils/export.js +115 -0
- package/src/utils/file.js +121 -0
- package/src/utils/formulaUtilsHandler.js +230 -0
- package/src/utils/htmlparser.js +977 -0
- package/src/utils/image.js +99 -0
- package/src/utils/imgSizeHandler.js +279 -0
- package/src/utils/jsonUtils.js +17 -0
- package/src/utils/lineFeed.js +49 -0
- package/src/utils/listContentHandler.js +227 -0
- package/src/utils/lookbehind-replace.js +81 -0
- package/src/utils/mathjax.js +89 -0
- package/src/utils/myersDiff.js +211 -0
- package/src/utils/pasteHelper.js +253 -0
- package/src/utils/recount-pos.js +59 -0
- package/src/utils/regexp.js +295 -0
- package/src/utils/sanitize.js +477 -0
- package/src/utils/selection.js +50 -0
- package/src/utils/svgUtils.js +96 -0
- package/src/utils/tableContentHandler.js +592 -0
- package/tools/README.md +3 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {string} str
|
|
19
|
+
* @param {{replacedText:string;begin:number;length:number;}[]} buffer
|
|
20
|
+
*/
|
|
21
|
+
function replaceStringByBuffer(str, buffer) {
|
|
22
|
+
if (!buffer.length) {
|
|
23
|
+
return str;
|
|
24
|
+
}
|
|
25
|
+
const slicedString = [];
|
|
26
|
+
let offset = 0;
|
|
27
|
+
buffer.forEach((buf, index) => {
|
|
28
|
+
slicedString.push(str.slice(offset, buf.begin));
|
|
29
|
+
slicedString.push(buf.replacedText);
|
|
30
|
+
offset = buf.begin + buf.length;
|
|
31
|
+
if (index === buffer.length - 1) {
|
|
32
|
+
slicedString.push(str.slice(offset));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
// console.log(slicedString, slicedString.join(''));
|
|
36
|
+
return slicedString.join('');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} str 原始字符串
|
|
41
|
+
* @param {RegExp} regex 正则
|
|
42
|
+
* @param {(...args: any[])=>string} replacer 字符串替换函数
|
|
43
|
+
* @param {boolean} [continuousMatch=false] 是否连续匹配,主要用于需要后向断言的连续语法匹配
|
|
44
|
+
* @param {number} [rollbackLength=1] 连续匹配时,每次指针回退的长度,默认为 1
|
|
45
|
+
*/
|
|
46
|
+
export function replaceLookbehind(str, regex, replacer, continuousMatch = false, rollbackLength = 1) {
|
|
47
|
+
if (!regex) {
|
|
48
|
+
return str;
|
|
49
|
+
}
|
|
50
|
+
// 从头开始匹配
|
|
51
|
+
regex.lastIndex = 0;
|
|
52
|
+
let args;
|
|
53
|
+
let lastIndex = 0;
|
|
54
|
+
const replaceBuffer = [];
|
|
55
|
+
while ((args = regex.exec(str)) !== null) {
|
|
56
|
+
const replaceInfo = {
|
|
57
|
+
begin: args.index,
|
|
58
|
+
length: args[0].length,
|
|
59
|
+
};
|
|
60
|
+
if (continuousMatch && args.index === lastIndex - rollbackLength) {
|
|
61
|
+
const [match, , ...restArgs] = args;
|
|
62
|
+
// 丢弃 leadingChar,需要调整begin和length
|
|
63
|
+
replaceBuffer.push({
|
|
64
|
+
begin: replaceInfo.begin + rollbackLength,
|
|
65
|
+
length: replaceInfo.length - rollbackLength,
|
|
66
|
+
replacedText: replacer(match.slice(rollbackLength), '', ...restArgs),
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
replaceBuffer.push({
|
|
70
|
+
...replaceInfo,
|
|
71
|
+
replacedText: replacer(...args),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
// console.log(args);
|
|
75
|
+
lastIndex = regex.lastIndex;
|
|
76
|
+
regex.lastIndex -= rollbackLength;
|
|
77
|
+
}
|
|
78
|
+
// 正则复位,避免影响其他逻辑
|
|
79
|
+
regex.lastIndex = 0;
|
|
80
|
+
return replaceStringByBuffer(str, replaceBuffer);
|
|
81
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { isBrowser } from './env';
|
|
17
|
+
import { PUNCTUATION } from './regexp';
|
|
18
|
+
import { escapeHTMLSpecialChar } from './sanitize';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 装饰器,挂载对应的模块到实例上
|
|
22
|
+
*/
|
|
23
|
+
export function LoadMathModule() {
|
|
24
|
+
if (!isBrowser()) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// @ts-ignore
|
|
28
|
+
this.katex = this.externals?.katex ?? window.katex;
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
this.MathJax = this.externals?.MathJax ?? window.MathJax;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const configureMathJax = (usePlugins) => {
|
|
34
|
+
if (!isBrowser()) {
|
|
35
|
+
console.log('mathjax disabled');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const plugins = usePlugins
|
|
39
|
+
? ['input/asciimath', '[tex]/noerrors', '[tex]/cancel', '[tex]/color', '[tex]/boldsymbol', 'ui/safe']
|
|
40
|
+
: ['ui/safe'];
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
window.MathJax = {
|
|
43
|
+
startup: {
|
|
44
|
+
elements: ['.Cherry-Math', '.Cherry-InlineMath'],
|
|
45
|
+
typeset: true,
|
|
46
|
+
},
|
|
47
|
+
tex: {
|
|
48
|
+
inlineMath: [
|
|
49
|
+
['$', '$'],
|
|
50
|
+
['\\(', '\\)'],
|
|
51
|
+
],
|
|
52
|
+
displayMath: [
|
|
53
|
+
['$$', '$$'],
|
|
54
|
+
['\\[', '\\]'],
|
|
55
|
+
],
|
|
56
|
+
tags: 'ams',
|
|
57
|
+
packages: { '[+]': ['noerrors', 'cancel', 'color'] },
|
|
58
|
+
macros: {
|
|
59
|
+
bm: ['{\\boldsymbol{#1}}', 1],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
options: {
|
|
63
|
+
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code', 'a'],
|
|
64
|
+
ignoreHtmlClass: 'tex2jax_ignore',
|
|
65
|
+
processHtmlClass: 'tex2jax_process',
|
|
66
|
+
// 关闭 mathjax 菜单
|
|
67
|
+
enableMenu: false,
|
|
68
|
+
},
|
|
69
|
+
loader: {
|
|
70
|
+
load: plugins,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const noEscape = ['&', '<', '>', '"', "'"]; // 需要转换为HTML实体字符的符号
|
|
76
|
+
|
|
77
|
+
// 用于预处理会在Markdown中被反转义的字符,如:\\ 会被反转义为 \
|
|
78
|
+
export const escapeFormulaPunctuations = (formula) => {
|
|
79
|
+
const $formula = formula.replace(new RegExp(PUNCTUATION, 'g'), (match) => {
|
|
80
|
+
if (noEscape.indexOf(match) !== -1) {
|
|
81
|
+
// HTML特殊字符需要转换为实体字符,防XSS注入
|
|
82
|
+
return escapeHTMLSpecialChar(match);
|
|
83
|
+
}
|
|
84
|
+
return `\\${match}`; // 先转义特殊字符,防止在afterMakeHtml中被反转义
|
|
85
|
+
});
|
|
86
|
+
return $formula;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export default {};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Myers' Diff 算法
|
|
18
|
+
* 用来对两个数列/字符串做diff,得到增/删元素的最简形式
|
|
19
|
+
* 参考文献: http://www.xmailserver.org/diff2.pdf
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import Logger from '@/Logger';
|
|
23
|
+
|
|
24
|
+
export default class MyersDiff {
|
|
25
|
+
constructor(newObj, oldObj, getElement) {
|
|
26
|
+
this.options = {
|
|
27
|
+
newObj, // 用于diff的新列表/字符串
|
|
28
|
+
oldObj, // 用于diff的旧列表/字符串
|
|
29
|
+
getElement, // 获取用于比较的元素的函数
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 执行diff操作
|
|
35
|
+
*/
|
|
36
|
+
doDiff() {
|
|
37
|
+
const snakes = this.findSnakes(this.options.newObj, this.options.oldObj);
|
|
38
|
+
const result = this.assembleResult(snakes, this.options.newObj, this.options.oldObj);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 用于判断列表/字符串元素是否相等的判据函数
|
|
44
|
+
*/
|
|
45
|
+
getElement(obj, index) {
|
|
46
|
+
if (typeof this.options.getElement === 'function') {
|
|
47
|
+
// 支持传入自定义的比较函数
|
|
48
|
+
return this.options.getElement(obj, index);
|
|
49
|
+
}
|
|
50
|
+
return obj[index];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 寻找从起点到终点的折线
|
|
55
|
+
*/
|
|
56
|
+
findSnakes(newObj, oldObj) {
|
|
57
|
+
const newLen = newObj.length || 0; // 新diff对象的长度
|
|
58
|
+
const oldLen = oldObj.length || 0; // 旧diff对象的长度
|
|
59
|
+
const lengthSum = newLen + oldLen; // 长度之和
|
|
60
|
+
const v = { 1: 0 }; // "每个节点的深度值"的缓存对象
|
|
61
|
+
const allSnakes = { 0: { 1: 0 } }; // "每个节点对应的折线"的缓存对象
|
|
62
|
+
|
|
63
|
+
// d是起点到对应节点的编辑距离,简而言之,若把新增一个节点或删除一个节点都视作一次"操作",那么通过d次操作可以从起点到达对应节点
|
|
64
|
+
for (let d = 0; d <= lengthSum; d++) {
|
|
65
|
+
const tmp = {};
|
|
66
|
+
for (let k = -d; k <= d; k += 2) {
|
|
67
|
+
// 转换坐标系,k可以视为对应节点(x,y)的x坐标值减y坐标值
|
|
68
|
+
const down = k === -d || (k !== d && v[k - 1] < v[k + 1]);
|
|
69
|
+
const kPrev = down ? k + 1 : k - 1;
|
|
70
|
+
|
|
71
|
+
const xStart = v[kPrev];
|
|
72
|
+
// let yStart = xStart - kPrev;
|
|
73
|
+
const xMid = down ? xStart : xStart + 1;
|
|
74
|
+
const yMid = xMid - k;
|
|
75
|
+
let xEnd = xMid;
|
|
76
|
+
let yEnd = yMid;
|
|
77
|
+
|
|
78
|
+
while (xEnd < oldLen && yEnd < newLen && this.getElement(oldObj, xEnd) === this.getElement(newObj, yEnd)) {
|
|
79
|
+
xEnd += 1;
|
|
80
|
+
yEnd += 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
v[k] = xEnd;
|
|
84
|
+
tmp[k] = xEnd;
|
|
85
|
+
|
|
86
|
+
if (xEnd >= oldLen && yEnd >= newLen) {
|
|
87
|
+
// 成功抵达终点
|
|
88
|
+
allSnakes[d] = tmp;
|
|
89
|
+
return this.$backtraceSnakes(allSnakes, newLen, oldLen, d);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
allSnakes[d] = tmp;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 回溯,找出关键路径对应的折线
|
|
100
|
+
*/
|
|
101
|
+
$backtraceSnakes(allSnakes, newLen, oldLen, d) {
|
|
102
|
+
const keySnakes = [];
|
|
103
|
+
const p = { x: oldLen, y: newLen }; // 模拟节点,从终点开始
|
|
104
|
+
|
|
105
|
+
// 执行回溯,倒回起点,找到并记录关键路径
|
|
106
|
+
for (let i = d; i > 0; i--) {
|
|
107
|
+
const v = allSnakes[i];
|
|
108
|
+
const vPrev = allSnakes[i - 1];
|
|
109
|
+
const k = p.x - p.y;
|
|
110
|
+
|
|
111
|
+
const xEnd = v[k];
|
|
112
|
+
// let yEnd = xEnd - k;
|
|
113
|
+
|
|
114
|
+
const down = k === -i || (k !== i && vPrev[k + 1] > vPrev[k - 1]);
|
|
115
|
+
const kPrev = down ? k + 1 : k - 1;
|
|
116
|
+
|
|
117
|
+
const xStart = vPrev[kPrev];
|
|
118
|
+
const yStart = xStart - kPrev;
|
|
119
|
+
|
|
120
|
+
const xMid = down ? xStart : xStart + 1;
|
|
121
|
+
// let yMid = xMid - k;
|
|
122
|
+
|
|
123
|
+
keySnakes.unshift({ xStart, xMid, xEnd });
|
|
124
|
+
|
|
125
|
+
p.x = xStart;
|
|
126
|
+
p.y = yStart;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return keySnakes;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 组装出返回值
|
|
134
|
+
*/
|
|
135
|
+
assembleResult(snakes, newObj, oldObj) {
|
|
136
|
+
const grayColor = 'color: gray';
|
|
137
|
+
const redColor = 'color: red';
|
|
138
|
+
const greenColor = 'color: green';
|
|
139
|
+
const blueColor = 'color: blue';
|
|
140
|
+
let consoleStr = '';
|
|
141
|
+
const args = [];
|
|
142
|
+
let yOffset = 0;
|
|
143
|
+
const result = []; // 返回的操作集
|
|
144
|
+
let change = {}; // 本次操作
|
|
145
|
+
let lastChange = {}; // 缓存上一次操作
|
|
146
|
+
let firstDeleteChange = {}; // 连续删除时用来缓存最初的删除
|
|
147
|
+
snakes.forEach((snake, index) => {
|
|
148
|
+
let currentPos = snake.xStart;
|
|
149
|
+
|
|
150
|
+
if (index === 0 && snake.xStart !== 0) {
|
|
151
|
+
for (let j = 0; j < snake.xStart; j++) {
|
|
152
|
+
consoleStr += `%c${this.getElement(oldObj, j)}, `;
|
|
153
|
+
args.push(grayColor);
|
|
154
|
+
yOffset += 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (snake.xMid - snake.xStart === 1) {
|
|
159
|
+
// 删除
|
|
160
|
+
change = {
|
|
161
|
+
type: 'delete',
|
|
162
|
+
oldIndex: snake.xStart,
|
|
163
|
+
newIndex: 0,
|
|
164
|
+
};
|
|
165
|
+
if (lastChange.type === 'delete' && lastChange.oldIndex === change.oldIndex - 1) {
|
|
166
|
+
// 检测到连续删除,缓存最初的删除
|
|
167
|
+
firstDeleteChange = firstDeleteChange ? lastChange : firstDeleteChange;
|
|
168
|
+
}
|
|
169
|
+
result.push(change);
|
|
170
|
+
lastChange = change;
|
|
171
|
+
consoleStr += `%c${this.getElement(oldObj, snake.xStart)}, `;
|
|
172
|
+
args.push(redColor);
|
|
173
|
+
currentPos = snake.xMid;
|
|
174
|
+
} else {
|
|
175
|
+
// 添加
|
|
176
|
+
change = {
|
|
177
|
+
type: 'insert',
|
|
178
|
+
oldIndex: snake.xStart,
|
|
179
|
+
newIndex: yOffset,
|
|
180
|
+
};
|
|
181
|
+
if (lastChange.type === 'delete' && lastChange.oldIndex === change.oldIndex - 1) {
|
|
182
|
+
// 和上一条删除合并为"更新"
|
|
183
|
+
result.pop();
|
|
184
|
+
firstDeleteChange = firstDeleteChange ? lastChange : firstDeleteChange;
|
|
185
|
+
change = {
|
|
186
|
+
type: 'update',
|
|
187
|
+
oldIndex: firstDeleteChange.oldIndex, // 合并时,更新目标定位连续删除块中的首个元素
|
|
188
|
+
newIndex: yOffset,
|
|
189
|
+
};
|
|
190
|
+
args.push(blueColor);
|
|
191
|
+
} else {
|
|
192
|
+
args.push(greenColor);
|
|
193
|
+
}
|
|
194
|
+
firstDeleteChange = {};
|
|
195
|
+
result.push(change);
|
|
196
|
+
lastChange = change;
|
|
197
|
+
consoleStr += `%c${this.getElement(newObj, yOffset)}, `;
|
|
198
|
+
yOffset += 1;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 不变
|
|
202
|
+
for (let i = 0; i < snake.xEnd - currentPos; i++) {
|
|
203
|
+
consoleStr += `%c${this.getElement(oldObj, currentPos + i)}, `;
|
|
204
|
+
args.push(grayColor);
|
|
205
|
+
yOffset += 1;
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
Logger.log(consoleStr, ...args);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { createElement } from '@/utils/dom';
|
|
18
|
+
|
|
19
|
+
const SAFE_AREA_MARGIN = 15;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Cherry实现了将粘贴的html内容转成对应的markdown源码的功能
|
|
23
|
+
* 本工具主要实现将粘贴html转成的markdown源码在编辑器中选中,并给出切换按钮
|
|
24
|
+
* 可以切换为纯文本内容,或者markdown内容
|
|
25
|
+
*/
|
|
26
|
+
const pasteHelper = {
|
|
27
|
+
/**
|
|
28
|
+
* 核心方法,粘贴后展示切换按钮
|
|
29
|
+
* 只有粘贴html时才会出现切换按钮
|
|
30
|
+
* @param {Object} currentCursor 当前的光标位置
|
|
31
|
+
* @param {Object} editor 编辑器对象
|
|
32
|
+
* @param {string} html html里的纯文本内容
|
|
33
|
+
* @param {string} md html对应的markdown源码
|
|
34
|
+
* @returns
|
|
35
|
+
*/
|
|
36
|
+
showSwitchBtnAfterPasteHtml($cherry, currentCursor, editor, html, md) {
|
|
37
|
+
if (html.trim() === md.trim()) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.init($cherry, currentCursor, editor, html, md);
|
|
41
|
+
this.setSelection();
|
|
42
|
+
this.bindListener();
|
|
43
|
+
this.initBubble();
|
|
44
|
+
this.showBubble();
|
|
45
|
+
// 默认粘贴成markdown格式,如果用户上次选择粘贴为纯文本,则需要切换为text
|
|
46
|
+
if (this.getTypeFromLocalStorage() === 'text') {
|
|
47
|
+
this.switchTextClick();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
init($cherry, currentCursor, editor, html, md) {
|
|
52
|
+
this.$cherry = $cherry;
|
|
53
|
+
this.html = html;
|
|
54
|
+
this.md = md;
|
|
55
|
+
this.codemirror = editor;
|
|
56
|
+
this.currentCursor = currentCursor;
|
|
57
|
+
this.locale = $cherry.locale;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 获取缓存中的复制粘贴类型
|
|
62
|
+
*/
|
|
63
|
+
getTypeFromLocalStorage() {
|
|
64
|
+
if (typeof localStorage === 'undefined') {
|
|
65
|
+
return 'md';
|
|
66
|
+
}
|
|
67
|
+
return localStorage.getItem('cherry-paste-type') || 'md';
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 记忆最近一次用户选择的粘贴类型
|
|
72
|
+
*/
|
|
73
|
+
setTypeToLocalStorage(type) {
|
|
74
|
+
if (typeof localStorage === 'undefined') {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
localStorage.setItem('cherry-paste-type', type);
|
|
78
|
+
},
|
|
79
|
+
|
|
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
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* 绑定事件
|
|
90
|
+
* 当编辑器选中区域改变、内容改变时,隐藏切换按钮
|
|
91
|
+
* 当编辑器滚动时,实时更新切换按钮的位置
|
|
92
|
+
* @returns null
|
|
93
|
+
*/
|
|
94
|
+
bindListener() {
|
|
95
|
+
if (!this.hasBindListener) {
|
|
96
|
+
this.hasBindListener = true;
|
|
97
|
+
} else {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
this.codemirror.on('beforeSelectionChange', (codemirror, info) => {
|
|
101
|
+
this.hideBubble();
|
|
102
|
+
});
|
|
103
|
+
this.codemirror.on('beforeChange', (codemirror, info) => {
|
|
104
|
+
this.hideBubble();
|
|
105
|
+
});
|
|
106
|
+
this.codemirror.on('scroll', (codemirror) => {
|
|
107
|
+
this.updatePositionWhenScroll();
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
isHidden() {
|
|
112
|
+
return this.bubbleDom.style.display === 'none';
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
toggleBubbleDisplay() {
|
|
116
|
+
if (this.isHidden()) {
|
|
117
|
+
this.bubbleDom.style.display = '';
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.bubbleDom.style.display = 'none';
|
|
121
|
+
return;
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
hideBubble() {
|
|
125
|
+
if (this.noHide) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
if (this.isHidden()) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.toggleBubbleDisplay();
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
updatePositionWhenScroll() {
|
|
135
|
+
if (this.isHidden()) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// FIXME: update position when stick to the bottom
|
|
139
|
+
// const isStickToBottom = !this.bubbleDom.style.top;
|
|
140
|
+
const offset = this.bubbleDom.dataset.scrollTop - this.getScrollTop();
|
|
141
|
+
this.bubbleDom.style.marginTop = `${offset}px`;
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
getScrollTop() {
|
|
145
|
+
return this.codemirror.getScrollInfo().top;
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
showBubble() {
|
|
149
|
+
const { top } = this.getLastSelectedPosition();
|
|
150
|
+
if (this.isHidden()) {
|
|
151
|
+
this.toggleBubbleDisplay();
|
|
152
|
+
this.bubbleDom.style.marginTop = '0';
|
|
153
|
+
this.bubbleDom.dataset.scrollTop = this.getScrollTop();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* @type {HTMLDivElement}
|
|
157
|
+
*/
|
|
158
|
+
const codemirrorWrapper = this.codemirror.getWrapperElement();
|
|
159
|
+
const maxTop = codemirrorWrapper.clientHeight - this.bubbleDom.getBoundingClientRect().height - SAFE_AREA_MARGIN;
|
|
160
|
+
|
|
161
|
+
if (top > maxTop) {
|
|
162
|
+
this.bubbleDom.style.top = '';
|
|
163
|
+
this.bubbleDom.style.bottom = `${SAFE_AREA_MARGIN}px`;
|
|
164
|
+
} else {
|
|
165
|
+
this.bubbleDom.style.top = `${top}px`;
|
|
166
|
+
this.bubbleDom.style.bottom = '';
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
initBubble() {
|
|
171
|
+
if (this.bubbleDom) {
|
|
172
|
+
this.bubbleDom.setAttribute('data-type', 'md');
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
const dom = createElement('div', 'cherry-bubble cherry-bubble--centered cherry-switch-paste');
|
|
176
|
+
dom.style.display = 'none';
|
|
177
|
+
|
|
178
|
+
const switchText = createElement('span', 'cherry-toolbar-button cherry-text-btn', {
|
|
179
|
+
title: this.locale.pastePlain,
|
|
180
|
+
});
|
|
181
|
+
switchText.innerText = 'TEXT';
|
|
182
|
+
|
|
183
|
+
const switchMd = createElement('span', 'cherry-toolbar-button cherry-md-btn', {
|
|
184
|
+
title: this.locale.pasteMarkdown,
|
|
185
|
+
});
|
|
186
|
+
switchMd.innerText = 'Markdown';
|
|
187
|
+
|
|
188
|
+
const switchBG = createElement('span', 'switch-btn--bg');
|
|
189
|
+
|
|
190
|
+
this.bubbleDom = dom;
|
|
191
|
+
this.switchText = switchText;
|
|
192
|
+
this.switchMd = switchMd;
|
|
193
|
+
this.switchBG = switchBG;
|
|
194
|
+
this.bubbleDom.appendChild(switchText);
|
|
195
|
+
this.bubbleDom.appendChild(switchMd);
|
|
196
|
+
this.bubbleDom.appendChild(switchBG);
|
|
197
|
+
this.bubbleDom.setAttribute('data-type', 'md');
|
|
198
|
+
this.codemirror.getWrapperElement().appendChild(this.bubbleDom);
|
|
199
|
+
this.switchMd.addEventListener('click', this.switchMDClick.bind(this));
|
|
200
|
+
this.switchText.addEventListener('click', this.switchTextClick.bind(this));
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
switchMDClick(event) {
|
|
204
|
+
this.setTypeToLocalStorage('md');
|
|
205
|
+
if (this.bubbleDom.getAttribute('data-type') === 'md') {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
this.noHide = true;
|
|
209
|
+
this.bubbleDom.setAttribute('data-type', 'md');
|
|
210
|
+
this.codemirror.doc.replaceSelection(this.md);
|
|
211
|
+
this.setSelection();
|
|
212
|
+
this.showBubble();
|
|
213
|
+
this.noHide = false;
|
|
214
|
+
},
|
|
215
|
+
switchTextClick(event) {
|
|
216
|
+
this.setTypeToLocalStorage('text');
|
|
217
|
+
if (this.bubbleDom.getAttribute('data-type') === 'text') {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
this.noHide = true;
|
|
221
|
+
this.bubbleDom.setAttribute('data-type', 'text');
|
|
222
|
+
this.codemirror.doc.replaceSelection(this.html);
|
|
223
|
+
this.setSelection();
|
|
224
|
+
this.showBubble();
|
|
225
|
+
this.noHide = false;
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
getLastSelectedPosition() {
|
|
229
|
+
const selectedObjs = Array.from(this.codemirror.getWrapperElement().getElementsByClassName('CodeMirror-selected'));
|
|
230
|
+
let width = 0;
|
|
231
|
+
let top = 0;
|
|
232
|
+
if (selectedObjs.length <= 0) {
|
|
233
|
+
this.hideBubble();
|
|
234
|
+
return {};
|
|
235
|
+
}
|
|
236
|
+
// FIXME: remove redundant width calculation
|
|
237
|
+
for (let key = 0; key < selectedObjs.length; key++) {
|
|
238
|
+
const item = selectedObjs[key];
|
|
239
|
+
const position = item.getBoundingClientRect();
|
|
240
|
+
const tmpWidth = position.left + position.width / 2;
|
|
241
|
+
const tmpTop = position.top + position.height;
|
|
242
|
+
if (tmpTop > top && tmpWidth >= width) {
|
|
243
|
+
top = tmpTop;
|
|
244
|
+
}
|
|
245
|
+
if (tmpWidth > width) {
|
|
246
|
+
width = tmpWidth;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return { top };
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export default pasteHelper;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import diff from 'fast-diff';
|
|
18
|
+
/**
|
|
19
|
+
* 更新内容时保持光标不变
|
|
20
|
+
* @param {Number} pos 光标相对文档开头的偏移量
|
|
21
|
+
* @param {String} oldContent 变更前的内容
|
|
22
|
+
* @param {String} newContent 变更后的内容
|
|
23
|
+
* @returns {Number} newPos 新的光标偏移量
|
|
24
|
+
*/
|
|
25
|
+
export default function getPosBydiffs(pos, oldContent, newContent) {
|
|
26
|
+
const diffs = diff(oldContent, newContent);
|
|
27
|
+
let newPos = pos;
|
|
28
|
+
let tmpPos = pos;
|
|
29
|
+
for (let i = 0; i < diffs.length; i++) {
|
|
30
|
+
const val = diffs[i];
|
|
31
|
+
if (tmpPos <= 0) {
|
|
32
|
+
return newPos;
|
|
33
|
+
}
|
|
34
|
+
const opType = val[0];
|
|
35
|
+
const opLength = val[1].length;
|
|
36
|
+
switch (opType) {
|
|
37
|
+
// 没有改变的内容
|
|
38
|
+
case diff.EQUAL:
|
|
39
|
+
if (tmpPos <= opLength) {
|
|
40
|
+
return newPos;
|
|
41
|
+
}
|
|
42
|
+
tmpPos -= opLength;
|
|
43
|
+
break;
|
|
44
|
+
// 删除的内容
|
|
45
|
+
case diff.DELETE:
|
|
46
|
+
if (tmpPos <= opLength) {
|
|
47
|
+
return newPos - opLength + tmpPos;
|
|
48
|
+
}
|
|
49
|
+
tmpPos -= opLength;
|
|
50
|
+
newPos -= opLength;
|
|
51
|
+
break;
|
|
52
|
+
// 新增的内容
|
|
53
|
+
case diff.INSERT:
|
|
54
|
+
newPos += opLength;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return newPos;
|
|
59
|
+
}
|