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.
Files changed (226) hide show
  1. package/LICENSE +162 -0
  2. package/README.md +139 -0
  3. package/dist/addons/cherry-code-block-card-plugin.js +1 -0
  4. package/dist/addons/cherry-code-block-echarts-plugin.js +1 -0
  5. package/dist/addons/cherry-code-block-mermaid-plugin.js +1 -0
  6. package/dist/cherry-markdown.core.common.js +1 -0
  7. package/dist/cherry-markdown.core.js +1 -0
  8. package/dist/cherry-markdown.css +4089 -0
  9. package/dist/cherry-markdown.engine.core.common.js +1 -0
  10. package/dist/cherry-markdown.engine.core.esm.js +1 -0
  11. package/dist/cherry-markdown.engine.core.js +1 -0
  12. package/dist/cherry-markdown.esm.js +1 -0
  13. package/dist/cherry-markdown.js +70837 -0
  14. package/dist/cherry-markdown.js.map +1 -0
  15. package/dist/cherry-markdown.min.css +1 -0
  16. package/dist/cherry-markdown.min.js +1 -0
  17. package/dist/stats.html +4838 -0
  18. package/examples/scripts/pinyin/README.md +53 -0
  19. package/package.json +167 -0
  20. package/src/Cherry.config.js +411 -0
  21. package/src/Cherry.js +788 -0
  22. package/src/CherryStatic.js +70 -0
  23. package/src/Editor.js +746 -0
  24. package/src/Engine.js +334 -0
  25. package/src/Event.js +74 -0
  26. package/src/Factory.js +180 -0
  27. package/src/Logger.js +31 -0
  28. package/src/Previewer.js +1147 -0
  29. package/src/Sanitizer.js +4 -0
  30. package/src/Sanitizer.node.js +7 -0
  31. package/src/Stats.js +101 -0
  32. package/src/Theme.js +46 -0
  33. package/src/UrlCache.js +98 -0
  34. package/src/addons/cherry-code-block-card-plugin.js +213 -0
  35. package/src/addons/cherry-code-block-echarts-plugin.js +161 -0
  36. package/src/addons/cherry-code-block-mermaid-plugin.js +118 -0
  37. package/src/core/HookCenter.js +303 -0
  38. package/src/core/HooksConfig.js +106 -0
  39. package/src/core/ParagraphBase.js +314 -0
  40. package/src/core/SentenceBase.js +65 -0
  41. package/src/core/SyntaxBase.js +197 -0
  42. package/src/core/hooks/AutoLink.js +251 -0
  43. package/src/core/hooks/BackgroundColor.js +46 -0
  44. package/src/core/hooks/Badge.js +100 -0
  45. package/src/core/hooks/Blockquote.js +113 -0
  46. package/src/core/hooks/Br.js +85 -0
  47. package/src/core/hooks/CodeBlock.js +876 -0
  48. package/src/core/hooks/Color.js +78 -0
  49. package/src/core/hooks/CommentReference.js +96 -0
  50. package/src/core/hooks/Detail.js +138 -0
  51. package/src/core/hooks/Emoji.config.js +9388 -0
  52. package/src/core/hooks/Emoji.js +223 -0
  53. package/src/core/hooks/Emphasis.js +113 -0
  54. package/src/core/hooks/Footnote.js +125 -0
  55. package/src/core/hooks/FrontMatter.js +52 -0
  56. package/src/core/hooks/FrontMatterVars.js +82 -0
  57. package/src/core/hooks/Header.js +229 -0
  58. package/src/core/hooks/HighLight.js +37 -0
  59. package/src/core/hooks/Hr.js +52 -0
  60. package/src/core/hooks/HtmlBlock.js +159 -0
  61. package/src/core/hooks/Iframe.js +80 -0
  62. package/src/core/hooks/Image.js +276 -0
  63. package/src/core/hooks/InlineCode.js +45 -0
  64. package/src/core/hooks/InlineMath.js +142 -0
  65. package/src/core/hooks/Link.js +169 -0
  66. package/src/core/hooks/List.js +260 -0
  67. package/src/core/hooks/Mark.js +55 -0
  68. package/src/core/hooks/MathBlock.js +97 -0
  69. package/src/core/hooks/Panel.js +170 -0
  70. package/src/core/hooks/Paragraph.js +84 -0
  71. package/src/core/hooks/Ruby.js +34 -0
  72. package/src/core/hooks/Size.js +84 -0
  73. package/src/core/hooks/Strikethrough.js +54 -0
  74. package/src/core/hooks/Sub.js +47 -0
  75. package/src/core/hooks/SuggestList.js +317 -0
  76. package/src/core/hooks/Suggester.js +759 -0
  77. package/src/core/hooks/Sup.js +47 -0
  78. package/src/core/hooks/Table.js +315 -0
  79. package/src/core/hooks/Toc.js +290 -0
  80. package/src/core/hooks/Transfer.js +47 -0
  81. package/src/core/hooks/Underline.js +37 -0
  82. package/src/index.core.js +29 -0
  83. package/src/index.engine.core.js +62 -0
  84. package/src/index.engine.js +30 -0
  85. package/src/index.js +28 -0
  86. package/src/locales/index.js +21 -0
  87. package/src/locales/zh_CN.js +170 -0
  88. package/src/sass/cherry.scss +122 -0
  89. package/src/sass/components/bubble.scss +122 -0
  90. package/src/sass/components/codemirror.scss +628 -0
  91. package/src/sass/components/dropdown.scss +37 -0
  92. package/src/sass/components/editor.scss +78 -0
  93. package/src/sass/components/preview.scss +71 -0
  94. package/src/sass/components/prism.scss +142 -0
  95. package/src/sass/components/stats.scss +32 -0
  96. package/src/sass/components/toc.scss +184 -0
  97. package/src/sass/components/toolbar.scss +117 -0
  98. package/src/sass/core/AutoLink.scss +20 -0
  99. package/src/sass/core/BackgroundColor.scss +0 -0
  100. package/src/sass/core/Badge.scss +116 -0
  101. package/src/sass/core/Blockquote.scss +12 -0
  102. package/src/sass/core/Br.scss +0 -0
  103. package/src/sass/core/Card.scss +219 -0
  104. package/src/sass/core/CodeBlock.scss +205 -0
  105. package/src/sass/core/Color.scss +37 -0
  106. package/src/sass/core/CommentReference.scss +0 -0
  107. package/src/sass/core/Detail.scss +107 -0
  108. package/src/sass/core/Emoji.scss +127 -0
  109. package/src/sass/core/Emphasis.scss +9 -0
  110. package/src/sass/core/Footnote.scss +21 -0
  111. package/src/sass/core/FrontMatterVars.scss +19 -0
  112. package/src/sass/core/Header.scss +103 -0
  113. package/src/sass/core/HighLight.scss +0 -0
  114. package/src/sass/core/Hr.scss +10 -0
  115. package/src/sass/core/HtmlBlock.scss +0 -0
  116. package/src/sass/core/Iframe.scss +36 -0
  117. package/src/sass/core/Image.scss +59 -0
  118. package/src/sass/core/InlineCode.scss +10 -0
  119. package/src/sass/core/InlineMath.scss +11 -0
  120. package/src/sass/core/Link.scss +16 -0
  121. package/src/sass/core/List.scss +61 -0
  122. package/src/sass/core/Mark.scss +15 -0
  123. package/src/sass/core/MathBlock.scss +0 -0
  124. package/src/sass/core/Panel.scss +150 -0
  125. package/src/sass/core/Paragraph.scss +6 -0
  126. package/src/sass/core/Ruby.scss +0 -0
  127. package/src/sass/core/Size.scss +8 -0
  128. package/src/sass/core/Strikethrough.scss +0 -0
  129. package/src/sass/core/Sub.scss +5 -0
  130. package/src/sass/core/Suggester.scss +62 -0
  131. package/src/sass/core/Sup.scss +5 -0
  132. package/src/sass/core/Table.scss +127 -0
  133. package/src/sass/core/Toc.scss +28 -0
  134. package/src/sass/core/Transfer.scss +0 -0
  135. package/src/sass/core/Underline.scss +0 -0
  136. package/src/sass/google-fonts.scss +34 -0
  137. package/src/sass/index.scss +3 -0
  138. package/src/sass/prism/dark.scss +131 -0
  139. package/src/sass/prism/light.scss +143 -0
  140. package/src/sass/variables/colors.scss +96 -0
  141. package/src/toolbars/Bubble.js +232 -0
  142. package/src/toolbars/BubbleTable.js +147 -0
  143. package/src/toolbars/HookCenter.js +185 -0
  144. package/src/toolbars/MenuBase.js +357 -0
  145. package/src/toolbars/PreviewerBubble.js +558 -0
  146. package/src/toolbars/Toc.js +246 -0
  147. package/src/toolbars/Toolbar.js +401 -0
  148. package/src/toolbars/hooks/Audio.js +53 -0
  149. package/src/toolbars/hooks/Badge.js +80 -0
  150. package/src/toolbars/hooks/BarTable.js +41 -0
  151. package/src/toolbars/hooks/Bold.js +70 -0
  152. package/src/toolbars/hooks/Br.js +34 -0
  153. package/src/toolbars/hooks/Card.js +64 -0
  154. package/src/toolbars/hooks/CheckList.js +41 -0
  155. package/src/toolbars/hooks/Code.js +46 -0
  156. package/src/toolbars/hooks/Color.js +285 -0
  157. package/src/toolbars/hooks/Copy.js +139 -0
  158. package/src/toolbars/hooks/Detail.js +70 -0
  159. package/src/toolbars/hooks/ECharts.js +303 -0
  160. package/src/toolbars/hooks/Emoji.js +303 -0
  161. package/src/toolbars/hooks/Export.js +47 -0
  162. package/src/toolbars/hooks/File.js +54 -0
  163. package/src/toolbars/hooks/Formula.js +36 -0
  164. package/src/toolbars/hooks/FullScreen.js +55 -0
  165. package/src/toolbars/hooks/Graph.js +281 -0
  166. package/src/toolbars/hooks/H1.js +71 -0
  167. package/src/toolbars/hooks/H2.js +71 -0
  168. package/src/toolbars/hooks/H3.js +71 -0
  169. package/src/toolbars/hooks/Header.js +100 -0
  170. package/src/toolbars/hooks/Hr.js +35 -0
  171. package/src/toolbars/hooks/Iframe.js +35 -0
  172. package/src/toolbars/hooks/Image.js +60 -0
  173. package/src/toolbars/hooks/Insert.js +36 -0
  174. package/src/toolbars/hooks/Italic.js +70 -0
  175. package/src/toolbars/hooks/LineTable.js +41 -0
  176. package/src/toolbars/hooks/Link.js +46 -0
  177. package/src/toolbars/hooks/List.js +55 -0
  178. package/src/toolbars/hooks/Ol.js +41 -0
  179. package/src/toolbars/hooks/Panel.js +155 -0
  180. package/src/toolbars/hooks/Quote.js +45 -0
  181. package/src/toolbars/hooks/Redo.js +33 -0
  182. package/src/toolbars/hooks/Ruby.js +59 -0
  183. package/src/toolbars/hooks/Size.js +100 -0
  184. package/src/toolbars/hooks/Split.js +37 -0
  185. package/src/toolbars/hooks/Strikethrough.js +65 -0
  186. package/src/toolbars/hooks/Sub.js +58 -0
  187. package/src/toolbars/hooks/Sup.js +58 -0
  188. package/src/toolbars/hooks/SwitchModel.js +78 -0
  189. package/src/toolbars/hooks/Table.js +56 -0
  190. package/src/toolbars/hooks/Toc.js +35 -0
  191. package/src/toolbars/hooks/TogglePreview.js +79 -0
  192. package/src/toolbars/hooks/Ul.js +41 -0
  193. package/src/toolbars/hooks/Underline.js +65 -0
  194. package/src/toolbars/hooks/Undo.js +30 -0
  195. package/src/toolbars/hooks/Video.js +53 -0
  196. package/src/utils/LazyLoadImg.js +341 -0
  197. package/src/utils/autoindent.js +58 -0
  198. package/src/utils/codeBlockContentHandler.js +351 -0
  199. package/src/utils/config.js +98 -0
  200. package/src/utils/copy.js +55 -0
  201. package/src/utils/dialog.js +196 -0
  202. package/src/utils/dom.js +162 -0
  203. package/src/utils/downloadUtil.js +23 -0
  204. package/src/utils/env.js +22 -0
  205. package/src/utils/error.js +61 -0
  206. package/src/utils/event.js +38 -0
  207. package/src/utils/export.js +115 -0
  208. package/src/utils/file.js +121 -0
  209. package/src/utils/formulaUtilsHandler.js +230 -0
  210. package/src/utils/htmlparser.js +977 -0
  211. package/src/utils/image.js +99 -0
  212. package/src/utils/imgSizeHandler.js +279 -0
  213. package/src/utils/jsonUtils.js +17 -0
  214. package/src/utils/lineFeed.js +49 -0
  215. package/src/utils/listContentHandler.js +227 -0
  216. package/src/utils/lookbehind-replace.js +81 -0
  217. package/src/utils/mathjax.js +89 -0
  218. package/src/utils/myersDiff.js +211 -0
  219. package/src/utils/pasteHelper.js +253 -0
  220. package/src/utils/recount-pos.js +59 -0
  221. package/src/utils/regexp.js +295 -0
  222. package/src/utils/sanitize.js +477 -0
  223. package/src/utils/selection.js +50 -0
  224. package/src/utils/svgUtils.js +96 -0
  225. package/src/utils/tableContentHandler.js +592 -0
  226. package/tools/README.md +3 -0
@@ -0,0 +1,977 @@
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
+ // @ts-nocheck
17
+ /**
18
+ * 将html内容转换成md内容的工具
19
+ * 调用方式为:htmlParser.run(htmlStr)
20
+ * 主要流程为:
21
+ * 1、接收html字符串
22
+ * 2、根据html字符串生成html语法树
23
+ * 3、递归遍历语法树,将标签替换为对应的markdown语法
24
+ **/
25
+ const htmlParser = {
26
+ /**
27
+ * 入口函数,负责将传入的html字符串转成对应的markdown源码
28
+ * @param {string} htmlStr
29
+ * @returns {string} 对应的markdown源码
30
+ */
31
+ run(htmlStr) {
32
+ let $htmlStr = `<div>${htmlStr}</div>`;
33
+ // 挂载对应的格式化引擎,这里挂载的是markdown逆向引擎,后续可以扩展支持其他标记语言
34
+ this.tagParser.formatEngine = this.mdFormatEngine;
35
+ // 去掉注释
36
+ $htmlStr = $htmlStr.replace(/<!--[\s\S]*?-->/g, '');
37
+ // 将html字符串解析成html语法树
38
+ let htmlparsedArrays = this.htmlParser.parseHtml($htmlStr);
39
+ // 预处理,去掉一些不需要的样式、属性
40
+ htmlparsedArrays = this.paragraphStyleClear(htmlparsedArrays);
41
+ // 核心逻辑,遍历html语法树,生成对应的markdown源码
42
+ return this.$dealHtml(htmlparsedArrays)
43
+ .replace(/\n{3,}/g, '\n\n\n')
44
+ .replace(/&gt;/g, '>')
45
+ .replace(/&lt;/g, '<')
46
+ .replace(/&amp;/g, '&')
47
+ .trim('\n');
48
+ },
49
+ /**
50
+ * 解析html语法树
51
+ * @param {Array} arr
52
+ * @returns {string} 对应的markdown源码
53
+ */
54
+ $dealHtml(arr) {
55
+ let ret = '';
56
+ for (let i = 0; i < arr.length; i++) {
57
+ const temObj = arr[i];
58
+ if (temObj.type === 'tag') ret = this.$handleTagObject(temObj, ret);
59
+ else if (temObj.type === 'text' && temObj.content.length > 0) {
60
+ ret += temObj.content
61
+ .replace(/&nbsp;/g, ' ')
62
+ .replace(/[\n]+/g, '\n')
63
+ .replace(/^[ \t\n]+\n\s*$/, '\n');
64
+ }
65
+ }
66
+ return ret;
67
+ },
68
+ /**
69
+ * 处理html标签内容
70
+ * @param {object} temObj
71
+ * @param {string} returnString
72
+ */
73
+ $handleTagObject(temObj, returnString) {
74
+ let ret = returnString;
75
+
76
+ if (temObj.attrs.class && /cherry-checkbox/.test(temObj.attrs.class)) {
77
+ // 针对checklist
78
+ if (temObj.attrs.checked) {
79
+ ret += '[x]';
80
+ } else {
81
+ ret += '[ ]';
82
+ }
83
+ } else if (temObj.attrs.class && /cherry-code-preview-lang-select/.test(temObj.attrs.class)) {
84
+ // 如果是代码块的选择语言标签,则不做任何处理
85
+ ret += '';
86
+ } else {
87
+ // 如果是标签
88
+ ret += this.$dealTag(temObj);
89
+ }
90
+ return ret;
91
+ },
92
+ /**
93
+ * 解析具体的html标签
94
+ * @param {HTMLElement} obj
95
+ * @returns {string} 对应的markdown源码
96
+ */
97
+ $dealTag(obj) {
98
+ const self = this;
99
+ let tmpText = '';
100
+ if (obj.children) {
101
+ // 递归每一个子元素
102
+ tmpText = self.$dealHtml(obj.children);
103
+ }
104
+ if (/(style|meta|link|script)/.test(obj.name)) {
105
+ return '';
106
+ }
107
+ if (obj.name === 'code' || obj.name === 'pre') {
108
+ // 解析代码块 或 行内代码
109
+ // pre时,强制转成代码块
110
+ return self.tagParser.codeParser(obj, self.$dealCodeTag(obj), obj.name === 'pre');
111
+ }
112
+ if (typeof self.tagParser[`${obj.name}Parser`] === 'function') {
113
+ // 解析对应的具体标签
114
+ return self.tagParser[`${obj.name}Parser`](obj, tmpText);
115
+ }
116
+ return tmpText;
117
+ },
118
+ /**
119
+ * 解析代码块
120
+ * 本函数认为代码块是由text标签和li标签组成的
121
+ * @param {HTMLElement} obj
122
+ * @returns {string} 对应的markdown源码
123
+ */
124
+ $dealCodeTag(obj) {
125
+ const self = this;
126
+ if (obj.children.length < 0) {
127
+ return '';
128
+ }
129
+ let ret = '';
130
+ for (let i = 0; i < obj.children.length; i++) {
131
+ const temObj = obj.children[i];
132
+ if (temObj.type !== 'text') {
133
+ // 如果是非text标签,则需要处理换行逻辑
134
+ if (temObj.name === 'li') {
135
+ ret += '\n';
136
+ }
137
+ if (temObj.name === 'br') {
138
+ ret += '\n';
139
+ }
140
+ // 递归找到对应的代码文本
141
+ ret += self.$dealCodeTag(temObj);
142
+ } else {
143
+ ret += temObj.content;
144
+ }
145
+ }
146
+ return ret;
147
+ },
148
+
149
+ /** **
150
+ * html解析器
151
+ * 将html解析成对象数组
152
+ * https://github.com/HenrikJoreteg/html-parse-stringify
153
+ **/
154
+ htmlParser: {
155
+ attrRE: /([\w-]+)|['"]{1}([^'"]*)['"]{1}/g,
156
+ lookup: {
157
+ area: true,
158
+ base: true,
159
+ br: true,
160
+ col: true,
161
+ embed: true,
162
+ hr: true,
163
+ img: true,
164
+ video: true,
165
+ input: true,
166
+ keygen: true,
167
+ link: true,
168
+ menuitem: true,
169
+ meta: true,
170
+ param: true,
171
+ source: true,
172
+ track: true,
173
+ wbr: true,
174
+ },
175
+ tagRE: /<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g,
176
+ empty: Object.create ? Object.create(null) : {},
177
+ parseTags(tag) {
178
+ const self = this;
179
+ let i = 0;
180
+ let key;
181
+ const res = {
182
+ type: 'tag',
183
+ name: '',
184
+ voidElement: false,
185
+ attrs: {},
186
+ children: [],
187
+ };
188
+
189
+ tag.replace(this.attrRE, (match) => {
190
+ if (i % 2) {
191
+ key = match;
192
+ } else {
193
+ if (i === 0) {
194
+ if (self.lookup[match] || tag.charAt(tag.length - 2) === '/') {
195
+ res.voidElement = true;
196
+ }
197
+ res.name = match;
198
+ } else {
199
+ res.attrs[key] = match.replace(/['"]/g, '');
200
+ }
201
+ }
202
+ i += 1;
203
+ });
204
+
205
+ return res;
206
+ },
207
+ parseHtml(html, options) {
208
+ const self = this;
209
+ const $options = options || {};
210
+ $options.components || ($options.components = this.empty);
211
+ const result = [];
212
+ let current;
213
+ let level = -1;
214
+ const arr = [];
215
+ const byTag = {};
216
+ let inComponent = false;
217
+
218
+ html.replace(this.tagRE, (tag, index) => {
219
+ if (inComponent) {
220
+ if (tag !== `</${current.name}>`) {
221
+ return;
222
+ }
223
+ inComponent = false;
224
+ }
225
+ const isOpen = tag.charAt(1) !== '/';
226
+ const start = index + tag.length;
227
+ const nextChar = html.charAt(start);
228
+ let parent;
229
+
230
+ if (isOpen) {
231
+ level += 1;
232
+
233
+ current = self.parseTags(tag);
234
+ if (current.type === 'tag' && $options.components[current.name]) {
235
+ current.type = 'component';
236
+ inComponent = true;
237
+ }
238
+
239
+ if (!current.voidElement && !inComponent && nextChar && nextChar !== '<') {
240
+ current.children.push({
241
+ type: 'text',
242
+ content: html.slice(start, html.indexOf('<', start)),
243
+ });
244
+ }
245
+
246
+ byTag[current.tagName] = current;
247
+
248
+ // if we're at root, push new base node
249
+ if (level === 0) {
250
+ result.push(current);
251
+ }
252
+
253
+ parent = arr[level - 1];
254
+
255
+ if (parent) {
256
+ parent.children.push(current);
257
+ }
258
+
259
+ arr[level] = current;
260
+ }
261
+
262
+ if (!isOpen || current.voidElement) {
263
+ level -= 1;
264
+ if (!inComponent && nextChar !== '<' && nextChar) {
265
+ // trailing text node
266
+ if (arr[level]) {
267
+ arr[level].children.push({
268
+ type: 'text',
269
+ content: html.slice(start, html.indexOf('<', start)),
270
+ });
271
+ }
272
+ }
273
+ }
274
+ });
275
+ return result;
276
+ },
277
+ },
278
+
279
+ /** **
280
+ * 标签解析器
281
+ * 解析对应的标签,并调用格式化引擎生成对应格式内容
282
+ **/
283
+ tagParser: {
284
+ // 挂载的解析引擎,一次只能挂在一个解析引擎,目前只实现和挂载了markdown解析引擎
285
+ formatEngine: {},
286
+ /**
287
+ * 解析p标签
288
+ * @param {HTMLElement} obj
289
+ * @param {string} str 需要回填的字符串
290
+ * @returns {string} str
291
+ */
292
+ pParser(obj, str) {
293
+ const $str = str;
294
+ if (/\n$/.test($str)) {
295
+ return $str;
296
+ }
297
+ return `${$str}\n`;
298
+ },
299
+ /**
300
+ * 解析div标签
301
+ * @param {HTMLElement} obj
302
+ * @param {string} str 需要回填的字符串
303
+ * @returns {string} str
304
+ */
305
+ divParser(obj, str) {
306
+ const $str = str;
307
+ if (/\n$/.test($str)) {
308
+ return $str;
309
+ }
310
+ return `${$str}\n`;
311
+ },
312
+ /**
313
+ * 解析span标签
314
+ * @param {HTMLElement} obj
315
+ * @param {string} str 需要回填的字符串
316
+ * @returns {string} str
317
+ */
318
+ spanParser(obj, str) {
319
+ const $str = str.replace(/\t/g, '').replace(/\n/g, ' '); // span标签里不应该有\n的,有的话就转化成空格
320
+ if (obj.attrs && obj.attrs.style) {
321
+ // 先屏蔽字体颜色、字体大小、字体背景色的转义逻辑
322
+ // let color = this.styleParser.colorAttrParser(obj.attrs.style);
323
+ // let size = this.styleParser.sizeAttrParser(obj.attrs.style);
324
+ // bgcolor = this.styleParser.bgColorAttrParser(obj.attrs.style);
325
+ // str = this.formatEngine.convertColor(str, color);
326
+ // str = this.formatEngine.convertSize(str, size);
327
+ // str = this.formatEngine.convertBgColor(str, bgcolor);
328
+ // return str;
329
+ }
330
+ return $str;
331
+ },
332
+ /**
333
+ * 解析code标签
334
+ * @param {HTMLElement} obj
335
+ * @param {string} str 需要回填的字符串
336
+ * @param {boolean} isBlock 是否强制为代码块
337
+ * @returns {string} str
338
+ */
339
+ codeParser(obj, str, isBlock = false) {
340
+ return this.formatEngine.convertCode(str, isBlock);
341
+ },
342
+ /**
343
+ * 解析br标签
344
+ * @param {HTMLElement} obj
345
+ * @param {string} str 需要回填的字符串
346
+ * @returns {string} str
347
+ */
348
+ brParser(obj, str) {
349
+ return this.formatEngine.convertBr(str, '\n');
350
+ },
351
+ /**
352
+ * 解析img标签
353
+ * @param {HTMLElement} obj
354
+ * @param {string} str 需要回填的字符串
355
+ * @returns {string} str
356
+ */
357
+ imgParser(obj, str) {
358
+ if (obj.attrs && obj.attrs['data-control'] === 'tapd-graph') {
359
+ return this.formatEngine.convertGraph(obj.attrs.title, obj.attrs.src, obj.attrs['data-origin-xml'], obj);
360
+ }
361
+ if (obj.attrs && obj.attrs.src) {
362
+ return this.formatEngine.convertImg(obj.attrs.alt, obj.attrs.src);
363
+ }
364
+ },
365
+ /**
366
+ * 解析video标签
367
+ * @param {HTMLElement} obj
368
+ * @param {string} str 需要回填的字符串
369
+ * @returns {string} str
370
+ */
371
+ videoParser(obj, str) {
372
+ if (obj.attrs && obj.attrs.src) {
373
+ return this.formatEngine.convertVideo(str, obj.attrs.src, obj.attrs.poster, obj.attrs.title);
374
+ }
375
+ },
376
+ /**
377
+ * 解析b标签
378
+ * @param {HTMLElement} obj
379
+ * @param {string} str 需要回填的字符串
380
+ * @returns {string} str
381
+ */
382
+ bParser(obj, str) {
383
+ const strArr = str.split('\n');
384
+ const ret = [];
385
+ for (let i = 0; i < strArr.length; i++) {
386
+ ret.push(this.formatEngine.convertB(strArr[i]));
387
+ }
388
+ return ret.join('\n');
389
+ },
390
+ /**
391
+ * 解析i标签
392
+ * @param {HTMLElement} obj
393
+ * @param {string} str 需要回填的字符串
394
+ * @returns {string} str
395
+ */
396
+ iParser(obj, str) {
397
+ const strArr = str.split('\n');
398
+ const ret = [];
399
+ for (let i = 0; i < strArr.length; i++) {
400
+ ret.push(this.formatEngine.convertI(strArr[i]));
401
+ }
402
+ return ret.join('\n');
403
+ },
404
+ /**
405
+ * 解析strike标签
406
+ * @param {HTMLElement} obj
407
+ * @param {string} str 需要回填的字符串
408
+ * @returns {string} str
409
+ */
410
+ strikeParser(obj, str) {
411
+ const strArr = str.split('\n');
412
+ const ret = [];
413
+ for (let i = 0; i < strArr.length; i++) {
414
+ ret.push(this.formatEngine.convertStrike(strArr[i]));
415
+ }
416
+ return ret.join('\n');
417
+ },
418
+ /**
419
+ * 解析del标签
420
+ * @param {HTMLElement} obj
421
+ * @param {string} str 需要回填的字符串
422
+ * @returns {string} str
423
+ */
424
+ delParser(obj, str) {
425
+ const strArr = str.split('\n');
426
+ const ret = [];
427
+ for (let i = 0; i < strArr.length; i++) {
428
+ ret.push(this.formatEngine.convertDel(strArr[i]));
429
+ }
430
+ return ret.join('\n');
431
+ },
432
+ /**
433
+ * 解析u标签
434
+ * @param {HTMLElement} obj
435
+ * @param {string} str 需要回填的字符串
436
+ * @returns {string} str
437
+ */
438
+ uParser(obj, str) {
439
+ const strArr = str.split('\n');
440
+ const ret = [];
441
+ for (let i = 0; i < strArr.length; i++) {
442
+ ret.push(this.formatEngine.convertU(strArr[i]));
443
+ }
444
+ return ret.join('\n');
445
+ },
446
+ /**
447
+ * 解析a标签
448
+ * @param {HTMLElement} obj
449
+ * @param {string} str 需要回填的字符串
450
+ * @returns {string} str
451
+ */
452
+ aParser(obj, str) {
453
+ if (obj.attrs && obj.attrs.href) {
454
+ return this.formatEngine.convertA(str, obj.attrs.href);
455
+ }
456
+ return '';
457
+ },
458
+ /**
459
+ * 解析sup标签
460
+ * @param {HTMLElement} obj
461
+ * @param {string} str 需要回填的字符串
462
+ * @returns {string} str
463
+ */
464
+ supParser(obj, str) {
465
+ return this.formatEngine.convertSup(str);
466
+ },
467
+ /**
468
+ * 解析sub标签
469
+ * @param {HTMLElement} obj
470
+ * @param {string} str 需要回填的字符串
471
+ * @returns {string} str
472
+ */
473
+ subParser(obj, str) {
474
+ return this.formatEngine.convertSub(str);
475
+ },
476
+ /**
477
+ * 解析td标签
478
+ * @param {HTMLElement} obj
479
+ * @param {string} str 需要回填的字符串
480
+ * @returns {string} str
481
+ */
482
+ tdParser(obj, str) {
483
+ return this.formatEngine.convertTd(str);
484
+ },
485
+ /**
486
+ * 解析tr标签
487
+ * @param {HTMLElement} obj
488
+ * @param {string} str 需要回填的字符串
489
+ * @returns {string} str
490
+ */
491
+ trParser(obj, str) {
492
+ return this.formatEngine.convertTr(str);
493
+ },
494
+ /**
495
+ * 解析th标签
496
+ * @param {HTMLElement} obj
497
+ * @param {string} str 需要回填的字符串
498
+ * @returns {string} str
499
+ */
500
+ thParser(obj, str) {
501
+ return this.formatEngine.convertTh(str);
502
+ },
503
+ /**
504
+ * 解析thead标签
505
+ * @param {HTMLElement} obj
506
+ * @param {string} str 需要回填的字符串
507
+ * @returns {string} str
508
+ */
509
+ theadParser(obj, str) {
510
+ return this.formatEngine.convertThead(str);
511
+ },
512
+ /**
513
+ * 解析table标签
514
+ * @param {HTMLElement} obj
515
+ * @param {string} str 需要回填的字符串
516
+ * @returns {string} str
517
+ */
518
+ tableParser(obj, str) {
519
+ return this.formatEngine.convertTable(str);
520
+ },
521
+ /**
522
+ * 解析li标签
523
+ * @param {HTMLElement} obj
524
+ * @param {string} str 需要回填的字符串
525
+ * @returns {string} str
526
+ */
527
+ liParser(obj, str) {
528
+ return this.formatEngine.convertLi(str);
529
+ },
530
+ /**
531
+ * 解析ul标签
532
+ * @param {HTMLElement} obj
533
+ * @param {string} str 需要回填的字符串
534
+ * @returns {string} str
535
+ */
536
+ ulParser(obj, str) {
537
+ return this.formatEngine.convertUl(str);
538
+ },
539
+ /**
540
+ * 解析ol标签
541
+ * @param {HTMLElement} obj
542
+ * @param {string} str 需要回填的字符串
543
+ * @returns {string} str
544
+ */
545
+ olParser(obj, str) {
546
+ return this.formatEngine.convertOl(str);
547
+ },
548
+ /**
549
+ * 解析strong标签
550
+ * @param {HTMLElement} obj
551
+ * @param {string} str 需要回填的字符串
552
+ * @returns {string} str
553
+ */
554
+ strongParser(obj, str) {
555
+ return this.formatEngine.convertStrong(str);
556
+ },
557
+ /**
558
+ * 解析hr标签
559
+ * @param {HTMLElement} obj
560
+ * @param {string} str 需要回填的字符串
561
+ * @returns {string} str
562
+ */
563
+ hrParser(obj, str) {
564
+ return this.formatEngine.convertHr(str);
565
+ },
566
+ /**
567
+ * 解析h1标签
568
+ * @param {HTMLElement} obj
569
+ * @param {string} str 需要回填的字符串
570
+ * @returns {string} str
571
+ */
572
+ h1Parser(obj, str) {
573
+ return this.formatEngine.convertH1(str);
574
+ },
575
+ /**
576
+ * 解析h2标签
577
+ * @param {HTMLElement} obj
578
+ * @param {string} str 需要回填的字符串
579
+ * @returns {string} str
580
+ */
581
+ h2Parser(obj, str) {
582
+ return this.formatEngine.convertH2(str);
583
+ },
584
+ /**
585
+ * 解析h3标签
586
+ * @param {HTMLElement} obj
587
+ * @param {string} str 需要回填的字符串
588
+ * @returns {string} str
589
+ */
590
+ h3Parser(obj, str) {
591
+ return this.formatEngine.convertH3(str);
592
+ },
593
+ /**
594
+ * 解析h4标签
595
+ * @param {HTMLElement} obj
596
+ * @param {string} str 需要回填的字符串
597
+ * @returns {string} str
598
+ */
599
+ h4Parser(obj, str) {
600
+ return this.formatEngine.convertH4(str);
601
+ },
602
+ /**
603
+ * 解析h5标签
604
+ * @param {HTMLElement} obj
605
+ * @param {string} str 需要回填的字符串
606
+ * @returns {string} str
607
+ */
608
+ h5Parser(obj, str) {
609
+ return this.formatEngine.convertH5(str);
610
+ },
611
+ /**
612
+ * 解析h6标签
613
+ * @param {HTMLElement} obj
614
+ * @param {string} str 需要回填的字符串
615
+ * @returns {string} str
616
+ */
617
+ h6Parser(obj, str) {
618
+ return this.formatEngine.convertH6(str);
619
+ },
620
+ /**
621
+ * 解析blockquote标签
622
+ * @param {HTMLElement} obj
623
+ * @param {string} str 需要回填的字符串
624
+ * @returns {string} str
625
+ */
626
+ blockquoteParser(obj, str) {
627
+ return this.formatEngine.convertBlockquote(str.replace(/\n+/g, '\n'));
628
+ },
629
+ /**
630
+ * 解析address标签
631
+ * @param {HTMLElement} obj
632
+ * @param {string} str 需要回填的字符串
633
+ * @returns {string} str
634
+ */
635
+ addressParser(obj, str) {
636
+ return this.formatEngine.convertAddress(str.replace(/\n+/g, '\n'));
637
+ },
638
+ // 样式解析器
639
+ styleParser: {
640
+ // 识别字体颜色 color
641
+ colorAttrParser(style) {
642
+ const color = style.match(/color:\s*(#[a-zA-Z0-9]{3,6});/);
643
+ if (color && color[1]) {
644
+ return color[1];
645
+ }
646
+ return '';
647
+ },
648
+ // 识别字体大小 font-size
649
+ sizeAttrParser(style) {
650
+ const fontSize = style.match(/font-size:\s*([a-zA-Z0-9-]+?);/);
651
+ if (fontSize && fontSize[1]) {
652
+ let size = 0;
653
+ if (/[0-9]+px/.test(fontSize[1])) {
654
+ size = fontSize[1].replace(/px/, '').trim();
655
+ } else {
656
+ switch (fontSize[1]) {
657
+ case 'x-small':
658
+ size = 10;
659
+ break;
660
+ case 'small':
661
+ size = 12;
662
+ break;
663
+ case 'medium':
664
+ size = 16;
665
+ break;
666
+ case 'large':
667
+ size = 18;
668
+ break;
669
+ case 'x-large':
670
+ size = 24;
671
+ break;
672
+ case 'xx-large':
673
+ size = 32;
674
+ break;
675
+ default:
676
+ size = '';
677
+ }
678
+ }
679
+ return size > 0 ? size : '';
680
+ }
681
+ return '';
682
+ },
683
+ // 识别字体背景颜色 background-color
684
+ bgColorAttrParser(style) {
685
+ const color = style.match(/background-color:\s*([^;]+?);/);
686
+ if (color && color[1]) {
687
+ let bgColor = '';
688
+ if (/rgb\([ 0-9]+,[ 0-9]+,[ 0-9]+\)/.test(color[1])) {
689
+ const values = color[1].match(/rgb\(([ 0-9]+),([ 0-9]+),([ 0-9]+)\)/);
690
+ if (values[1] && values[2] && values[3]) {
691
+ values[1] = parseInt(values[1].trim(), 10);
692
+ values[2] = parseInt(values[2].trim(), 10);
693
+ values[3] = parseInt(values[3].trim(), 10);
694
+ bgColor = `#${values[1].toString(16)}${values[2].toString(16)}${values[3].toString(16)}`;
695
+ }
696
+ } else {
697
+ [, bgColor] = color;
698
+ }
699
+ return bgColor;
700
+ }
701
+ return '';
702
+ },
703
+ },
704
+ },
705
+
706
+ /**
707
+ * 一个格式化引擎
708
+ * 将字符串格式化成markdown语法的引擎
709
+ **/
710
+ mdFormatEngine: {
711
+ convertColor(str, attr) {
712
+ const $str = str.trim();
713
+ if (!$str || /\n/.test($str)) {
714
+ return $str;
715
+ }
716
+ return attr ? `!!${attr} ${$str}!!` : $str;
717
+ },
718
+ convertSize(str, attr) {
719
+ const $str = str.trim();
720
+ if (!$str || /\n/.test($str)) {
721
+ return $str;
722
+ }
723
+ return attr ? `!${attr} ${$str}!` : $str;
724
+ },
725
+ convertBgColor(str, attr) {
726
+ const $str = str.trim();
727
+ if (!$str || /\n/.test($str)) {
728
+ return $str;
729
+ }
730
+ return attr ? `!!!${attr} ${$str}!!!` : $str;
731
+ },
732
+ convertBr(str, attr) {
733
+ return str + attr;
734
+ },
735
+ convertCode(str, isBlock = false) {
736
+ if (/\n/.test(str) || isBlock) {
737
+ return `\`\`\`\n${str.replace(/\n+$/, '')}\n\`\`\``;
738
+ }
739
+ return `\`${str.replace(/`/g, '\\`')}\``;
740
+ },
741
+ convertB(str) {
742
+ return /^\s*$/.test(str) ? '' : `**${str}**`;
743
+ },
744
+ convertI(str) {
745
+ return /^\s*$/.test(str) ? '' : `*${str}*`;
746
+ },
747
+ convertU(str) {
748
+ return /^\s*$/.test(str) ? '' : ` /${str}/ `;
749
+ },
750
+ convertImg(alt, src) {
751
+ const $alt = alt && alt.length > 0 ? alt : 'image';
752
+ return `![${$alt}](${src})`;
753
+ },
754
+ convertGraph(str, attr, data, obj) {
755
+ const $str = str && str.length > 0 ? str : 'graph';
756
+ let moreAttrs = '';
757
+ if (obj) {
758
+ try {
759
+ const { attrs } = obj;
760
+ Object.keys(attrs).forEach((prop) => {
761
+ if (Object.prototype.hasOwnProperty.call(attrs, prop)) {
762
+ if (prop.indexOf('data-graph-') >= 0 && attrs[prop]) {
763
+ moreAttrs += ` ${prop}=${attrs[prop]}`;
764
+ }
765
+ }
766
+ });
767
+ } catch (error) {
768
+ // console.log('error', error)
769
+ }
770
+ }
771
+ return `![${$str}](${attr}){data-control=tapd-graph data-origin-xml=${data}${moreAttrs}}`;
772
+ },
773
+ convertVideo(str, src, poster, title) {
774
+ const $title = title && title.length > 0 ? title : 'video';
775
+ return `!video[${$title}](${src}){poster=${poster}}`;
776
+ },
777
+ convertA(str, attr) {
778
+ if (str === attr) {
779
+ return `${str} `;
780
+ }
781
+ const $str = str.trim();
782
+ if (!$str) {
783
+ return $str;
784
+ }
785
+ return `[${$str}](${attr})`;
786
+ },
787
+ convertSup(str) {
788
+ return `^${str.trim().replace(/\^/g, '\\^')}^`;
789
+ },
790
+ convertSub(str) {
791
+ return `^^${str.trim().replace(/\^\^/g, '\\^\\^')}^^`;
792
+ },
793
+ convertTd(str) {
794
+ return `~|${str
795
+ .trim()
796
+ .replace(/\n{1,}/g, '<br>')
797
+ .replace(/ /g, '~s~')} ~|`;
798
+ },
799
+ convertTh(str) {
800
+ if (/^\s*$/.test(str)) {
801
+ return '';
802
+ }
803
+ return `~|${str.trim().replace(/\n{1,}/g, '<br>')} ~|`;
804
+ },
805
+ convertTr(str) {
806
+ if (/^\s*$/.test(str)) {
807
+ return '';
808
+ }
809
+ return `${str.trim().replace(/\n/g, '')}\n`;
810
+ },
811
+ convertThead(str) {
812
+ const $str = `${str
813
+ .replace(/[ \t]+/g, '')
814
+ .replace(/~\|~\|/g, '~|')
815
+ .replace(/~\|/g, '|')}\n`;
816
+ const headsCount = $str.match(/\|/g).length - 1;
817
+ return `${$str}|${':-:|'.repeat(headsCount)}\n`;
818
+ },
819
+ convertTable(str) {
820
+ let ret = `\n${str
821
+ .replace(/[ \t]+/g, '')
822
+ .replace(/~\|~\|/g, '~|')
823
+ .replace(/~\|/g, '|')}\n`
824
+ .replace(/\n{2,}/g, '\n')
825
+ .replace(/\n[ \t]+\n/g, '\n')
826
+ .replace(/~s~/g, ' ');
827
+ if (!/\|:-:\|/.test(ret)) {
828
+ const headsCount = ret.match(/^\n[^\n]+\n/)[0].match(/\|/g).length - 1;
829
+ ret = `\n|${' |'.repeat(headsCount)}\n|${':-:|'.repeat(headsCount)}${ret}`;
830
+ }
831
+ return ret;
832
+ },
833
+ convertLi(str) {
834
+ return `- ${str.replace(/^\n/, '').replace(/\n+$/, '').replace(/\n+/g, '\n\t')}\n`;
835
+ },
836
+ convertUl(str) {
837
+ return `${str}\n`;
838
+ },
839
+ convertOl(str) {
840
+ const arr = str.split('\n');
841
+ let index = 1;
842
+ for (let i = 0; i < arr.length; i++) {
843
+ if (/^- /.test(arr[i])) {
844
+ arr[i] = arr[i].replace(/^- /, `${index}. `);
845
+ index += 1;
846
+ }
847
+ }
848
+ const $str = arr.join('\n');
849
+ return `${$str}\n`;
850
+ },
851
+ convertStrong(str) {
852
+ return /^\s*$/.test(str) ? '' : `**${str}**`;
853
+ },
854
+ convertStrike(str) {
855
+ return /^\s*$/.test(str) ? '' : `~~${str}~~`;
856
+ },
857
+ convertDel(str) {
858
+ return /^\s*$/.test(str) ? '' : `~~${str}~~`;
859
+ },
860
+ convertHr(str) {
861
+ return /^\s*$/.test(str) ? '\n\n----\n' : `\n\n----\n${str}`;
862
+ },
863
+ convertH1(str) {
864
+ return `# ${str.trim().replace(/\n+$/, '')}\n\n`;
865
+ },
866
+ convertH2(str) {
867
+ return `## ${str.trim().replace(/\n+$/, '')}\n\n`;
868
+ },
869
+ convertH3(str) {
870
+ return `### ${str.trim().replace(/\n+$/, '')}\n\n`;
871
+ },
872
+ convertH4(str) {
873
+ return `#### ${str.trim().replace(/\n+$/, '')}\n\n`;
874
+ },
875
+ convertH5(str) {
876
+ return `##### ${str.trim().replace(/\n+$/, '')}\n\n`;
877
+ },
878
+ convertH6(str) {
879
+ return `###### ${str.trim().replace(/\n+$/, '')}\n\n`;
880
+ },
881
+ convertBlockquote(str) {
882
+ return `>${str.trim()}\n\n`;
883
+ },
884
+ convertAddress(str) {
885
+ return `>${str.trim()}\n\n`;
886
+ },
887
+ },
888
+ /**
889
+ * 清除整段的样式、方便编辑
890
+ * 暂时先屏蔽字体色和背景色
891
+ * @param {Array} htmlparsedArrays 由HTMLElement组成的数组
892
+ */
893
+ paragraphStyleClear(htmlparsedArrays) {
894
+ for (let index = 0; index < htmlparsedArrays[0].children.length; index++) {
895
+ const htmlItem = htmlparsedArrays[0].children[index];
896
+ const stack = [htmlItem];
897
+ let paragraphs = [];
898
+ while (stack.length) {
899
+ const temp = stack.shift();
900
+ const childCount = this.notEmptyTagCount(temp);
901
+ if (childCount === 1) {
902
+ paragraphs.push(temp);
903
+ } else if (childCount > 1) {
904
+ for (let k = 0; k < temp.children.length; k++) {
905
+ stack.push(temp.children[k]);
906
+ }
907
+ } else {
908
+ if (paragraphs.length === 1) {
909
+ this.clearChildColorAttrs(paragraphs.pop());
910
+ }
911
+ paragraphs = [];
912
+ }
913
+ }
914
+ if (paragraphs.length === 1) {
915
+ this.clearChildColorAttrs(paragraphs.pop());
916
+ }
917
+ }
918
+
919
+ return htmlparsedArrays;
920
+ },
921
+ /**
922
+ * 非空子元素数量
923
+ */
924
+ notEmptyTagCount(htmlItem) {
925
+ if (
926
+ !htmlItem ||
927
+ htmlItem.voidElement ||
928
+ (htmlItem.type === 'tag' && !htmlItem.children.length) ||
929
+ (htmlItem.type === 'text' && !htmlItem.content.replace(/(\r|\n|\s)+/g, ''))
930
+ ) {
931
+ return 0;
932
+ }
933
+
934
+ if (htmlItem.children && htmlItem.children.length) {
935
+ let res = 0;
936
+ for (let index = 0; index < htmlItem.children.length; index++) {
937
+ res += this.notEmptyTagCount(htmlItem.children[index]);
938
+ }
939
+ return res;
940
+ }
941
+ return 1;
942
+ },
943
+ clearChildColorAttrs(htmlItems) {
944
+ const self = this;
945
+ this.forEachHtmlParsedItems(htmlItems, (htmlItem) => {
946
+ self.clearSelfNodeColorAttrs(htmlItem);
947
+ });
948
+ },
949
+ clearSelfNodeColorAttrs(htmlItem) {
950
+ if (htmlItem.attrs && htmlItem.attrs.style) {
951
+ const styles = htmlItem.attrs.style.split(';');
952
+ const newStyles = [];
953
+ for (let index = 0; index < styles.length; index++) {
954
+ if (styles[index] && styles[index].indexOf('color') === -1) {
955
+ newStyles.push(styles[index]);
956
+ }
957
+ }
958
+ if (newStyles.length) {
959
+ htmlItem.attrs.style = `${newStyles.join(';')};`;
960
+ } else {
961
+ delete htmlItem.attrs.style;
962
+ }
963
+ }
964
+ },
965
+ forEachHtmlParsedItems(htmlItems, cb) {
966
+ if (htmlItems) {
967
+ cb(htmlItems);
968
+ if (htmlItems.children && htmlItems.children.length) {
969
+ for (let index = 0; index < htmlItems.children.length; index++) {
970
+ this.forEachHtmlParsedItems(htmlItems.children[index], cb);
971
+ }
972
+ }
973
+ }
974
+ },
975
+ };
976
+
977
+ export default htmlParser;