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,251 @@
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 SyntaxBase from '@/core/SyntaxBase';
17
+ import { escapeHTMLSpecialCharOnce as $e, encodeURIOnce } from '@/utils/sanitize';
18
+ import { compileRegExp, EMAIL, EMAIL_INLINE, URL_INLINE_NO_SLASH, URL, URL_NO_SLASH, URL_INLINE } from '@/utils/regexp';
19
+
20
+ export default class AutoLink extends SyntaxBase {
21
+ static HOOK_NAME = 'autoLink';
22
+
23
+ static escapePreservedSymbol = (text) => {
24
+ // _ prevent conflict with emphasis
25
+ // _ => 0x5f
26
+ // * => 0x2a
27
+ return text.replace(/_/g, '_').replace(/\*/g, '*');
28
+ };
29
+
30
+ constructor({ config, globalConfig }) {
31
+ super({ config });
32
+ this.urlProcessor = globalConfig.urlProcessor;
33
+ this.enableShortLink = !!config.enableShortLink;
34
+ this.shortLinkLength = config.shortLinkLength;
35
+ // eslint-disable-next-line no-nested-ternary
36
+ this.target = config.target ? `target="${config.target}"` : !!config.openNewPage ? 'target="_blank"' : '';
37
+ this.rel = config.rel ? `rel="${config.rel}"` : '';
38
+ }
39
+
40
+ isLinkInHtmlAttribute(str, index, linkLength) {
41
+ const xmlTagRegex = new RegExp(
42
+ [
43
+ '<', // tag start
44
+ '([a-zA-Z][a-zA-Z0-9-]*)', // tagName
45
+ '(', // attrs start
46
+ [
47
+ '\\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*', // attr name
48
+ '(', // attr value start
49
+ [
50
+ '\\s*=\\s*',
51
+ '(',
52
+ [
53
+ '([^\\s"\'=<>`]+)', // unquoted value
54
+ "('[^']*')", // single-quoted value
55
+ '("[^"]*")', // double-quoted value
56
+ ].join('|'), // either is ok
57
+ ')',
58
+ ].join(''),
59
+ ')?', // attr value end
60
+ ].join(''),
61
+ ')*', // attrs end
62
+ '\\s*[/]?>', // tag end
63
+ ].join(''),
64
+ 'g',
65
+ );
66
+ let match;
67
+ while ((match = xmlTagRegex.exec(str)) !== null) {
68
+ // 搜索范围超过了字符串匹配到的位置
69
+ if (match.index > index + linkLength) {
70
+ break;
71
+ }
72
+ // 正好在范围内,说明是HTML的属性,取等号是因为AutoLink的正则可能会匹配到标签的结束符号,如<img src="http://www.google.com">
73
+ if (match.index < index && match.index + match[0].length >= index + linkLength) {
74
+ return true;
75
+ }
76
+ }
77
+ return false;
78
+ }
79
+
80
+ /**
81
+ * 判断链接是否被包裹在a标签内部,如果被包裹,则不识别为自动链接
82
+ * @param {string} str
83
+ * @param {number} index
84
+ * @param {number} linkLength
85
+ */
86
+ isLinkInATag(str, index, linkLength) {
87
+ const aTagRegex = /<a.*>[^<]*<\/a>/g;
88
+ let match;
89
+ while ((match = aTagRegex.exec(str)) !== null) {
90
+ // 搜索范围超过了字符串匹配到的位置
91
+ if (match.index > index + linkLength) {
92
+ break;
93
+ }
94
+ // 正好在范围内,说明是HTML的属性,取等号是因为AutoLink的正则可能会匹配到标签的结束符号
95
+ // 如<a href="http://www.google.com">http://www.google.com</a>
96
+ if (match.index < index && match.index + match[0].length >= index + linkLength) {
97
+ return true;
98
+ }
99
+ }
100
+ return false;
101
+ }
102
+
103
+ makeHtml(str, sentenceMakeFunc) {
104
+ if (!(this.test(str) && (EMAIL_INLINE.test(str) || URL_INLINE_NO_SLASH.test(str)))) {
105
+ return str;
106
+ }
107
+ return str.replace(this.RULE.reg, (match, left, protocol, address, right, index, str) => {
108
+ // 数字实体字符系临时处理方法,详情参见HTMLBlock注释
109
+ // maybe a html attr, skip it
110
+ if (
111
+ // ((left !== '<' || left !== '&#60;') && (right !== '>' || right !== '&#62;')) ||
112
+ this.isLinkInHtmlAttribute(str, index, protocol.length + address.length) ||
113
+ this.isLinkInATag(str, index, protocol.length + address.length)
114
+ ) {
115
+ return match;
116
+ }
117
+ const $protocol = protocol.toLowerCase();
118
+ let prefix = '';
119
+ let suffix = '';
120
+ let isWrappedByBracket = true;
121
+ // not a pair
122
+ if (!((left === '<' || left === '&#60;') && (right === '>' || right === '&#62;'))) {
123
+ prefix = left;
124
+ suffix = right;
125
+ isWrappedByBracket = false;
126
+ }
127
+ // not a valid address
128
+ // 不被尖括号包裹,不带协议头,且不以www.开头的不识别
129
+ if (address.trim() === '' || (!isWrappedByBracket && $protocol === '' && !/www\./.test(address))) {
130
+ return match;
131
+ }
132
+ switch ($protocol) {
133
+ case 'javascript:':
134
+ return match;
135
+ case 'mailto:': // email
136
+ if (EMAIL.test(address)) {
137
+ return `${prefix}<a href="${encodeURIOnce(`${$protocol}${address}`)}" ${this.target} ${this.rel}>${$e(
138
+ address,
139
+ )}</a>${suffix}`;
140
+ }
141
+ return match;
142
+ case '': // 协议为空
143
+ // 不被<>包裹或单边无效包裹,prefix === suffix 时都为空串
144
+ if (prefix === suffix || !isWrappedByBracket) {
145
+ // mailto
146
+ if (EMAIL.test(address)) {
147
+ return `${prefix}<a href="mailto:${encodeURIOnce(address)}" ${this.target} ${this.rel}>${$e(
148
+ address,
149
+ )}</a>${suffix}`;
150
+ }
151
+ // 不识别无协议头的URL,且开头不应该含有斜杠
152
+ if (URL_NO_SLASH.test(address)) {
153
+ return `${prefix}${this.renderLink(`//${address}`, address)}${suffix}`;
154
+ }
155
+ // 其他的属于非法情况
156
+ return match;
157
+ }
158
+ // 被<>包裹
159
+ if (isWrappedByBracket) {
160
+ // mailto
161
+ if (EMAIL.test(address)) {
162
+ return `<a href="mailto:${encodeURIOnce(address)}" ${this.target} ${this.rel}>${$e(address)}</a>`;
163
+ }
164
+ // 可识别任意协议的URL,或不以斜杠开头的URL
165
+ if (URL.test(address) || URL_NO_SLASH.test(address)) {
166
+ return this.renderLink(address);
167
+ }
168
+ // 其他非法
169
+ return match;
170
+ }
171
+ default:
172
+ // 协议头不为空时的非法URL
173
+ if (!URL.test(address)) {
174
+ return match;
175
+ }
176
+ // TODO: Url Validator
177
+ return `${prefix}${this.renderLink(`${$protocol}${address}`)}${suffix}`;
178
+ }
179
+ // this should never happen
180
+ return match;
181
+ });
182
+ }
183
+
184
+ rule() {
185
+ // (?<protocol>\\w+:)\\/\\/
186
+ const ret = {
187
+ // ?<left>
188
+ begin: '(<?)',
189
+ content: [
190
+ // ?<protocol>
191
+ '((?:[a-z][a-z0-9+.-]{1,31}:)?)', // protocol is any seq of 2-32 chars beginning with letter
192
+ // '(?<slash>(?:\\/{2})?)',
193
+ // ?<address>
194
+ // '([^\\s\\x00-\\x1f"<>]+)',
195
+ `((?:${URL_INLINE.source})|(?:${EMAIL_INLINE.source}))`,
196
+ // [
197
+ // `(?<url>${ URL_INLINE.source })`,
198
+ // `(?<email>${ EMAIL_INLINE.source })`, // email
199
+ // ].join('|'),
200
+ // ')'
201
+ ].join(''),
202
+ // ?<right>
203
+ end: '(>?)', // TODO: extend attrs e.g. {target=_blank}
204
+ };
205
+ ret.reg = compileRegExp(ret, 'ig');
206
+ return ret;
207
+ }
208
+
209
+ /**
210
+ * 渲染链接为a标签,返回html
211
+ * @param {string} url src链接
212
+ * @param {string} [text] 展示的链接文本,不传默认使用url
213
+ * @returns 渲染的a标签
214
+ */
215
+ renderLink(url, text) {
216
+ let linkText = text;
217
+ if (typeof linkText !== 'string') {
218
+ if (this.enableShortLink) {
219
+ const Url = url.replace(/^https?:\/\//i, '');
220
+ linkText = `${Url.substring(0, this.shortLinkLength)}${Url.length > this.shortLinkLength ? '...' : ''}`;
221
+ } else {
222
+ linkText = url;
223
+ }
224
+ }
225
+ const processedURL = this.urlProcessor(url, 'autolink');
226
+ const safeUri = encodeURIOnce(processedURL);
227
+ const displayUri = $e(linkText);
228
+ let additionalAttrs = ' ';
229
+ const isExternalLink = safeUri.startsWith('http') && !safeUri.startsWith(window.location.origin);
230
+ // 处理target属性
231
+ if (this.target) {
232
+ // 如果已配置target,优先使用配置的target
233
+ additionalAttrs += ` ${this.target}`;
234
+ } else if (isExternalLink) {
235
+ // 外部链接且未配置target时,默认使用_blank
236
+ additionalAttrs += ' target="_blank"';
237
+ }
238
+
239
+ // 处理rel属性
240
+ if (this.rel) {
241
+ additionalAttrs += ` ${this.rel}`;
242
+ } else if (isExternalLink) {
243
+ // 外部链接时,添加noopener noreferrer安全属性
244
+ additionalAttrs += ' rel="noopener noreferrer"';
245
+ }
246
+
247
+ return `<a href="${AutoLink.escapePreservedSymbol(safeUri)}" title="${AutoLink.escapePreservedSymbol(
248
+ displayUri,
249
+ )}" ${additionalAttrs}>${AutoLink.escapePreservedSymbol(displayUri)}</a>`;
250
+ }
251
+ }
@@ -0,0 +1,46 @@
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 SyntaxBase from '@/core/SyntaxBase';
17
+ import { isLookbehindSupported } from '@/utils/regexp';
18
+ import { replaceLookbehind } from '@/utils/lookbehind-replace';
19
+
20
+ export default class BackgroundColor extends SyntaxBase {
21
+ static HOOK_NAME = 'bgColor';
22
+ // constructor() {
23
+ // super();
24
+ // }
25
+
26
+ toHtml(whole, leadingChar, m1, m2) {
27
+ return `${leadingChar}<span style="background-color:${m1}">${m2}</span>`;
28
+ }
29
+
30
+ makeHtml(str) {
31
+ if (isLookbehindSupported()) {
32
+ return str.replace(this.RULE.reg, this.toHtml);
33
+ }
34
+ return replaceLookbehind(str, this.RULE.reg, this.toHtml, true, 1);
35
+ }
36
+
37
+ rule() {
38
+ const ret = {
39
+ begin: isLookbehindSupported() ? '((?<!\\\\))!!!' : '(^|[^\\\\])!!!',
40
+ end: '!!!',
41
+ content: '(#[0-9a-zA-Z]{3,6}|[a-z]{3,10})[\\s]([\\w\\W]+?)',
42
+ };
43
+ ret.reg = new RegExp(ret.begin + ret.content + ret.end, 'g');
44
+ return ret;
45
+ }
46
+ }
@@ -0,0 +1,100 @@
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 SyntaxBase from '../SyntaxBase';
18
+ export default class Badge extends SyntaxBase {
19
+ static HOOK_NAME = 'badge';
20
+
21
+ // 预定义颜色类
22
+ static predefinedColors = new Set(['important', 'info', 'note', 'tip', 'warning', 'danger']);
23
+
24
+ toHtml(whole, m1, m2, m3) {
25
+ // 处理颜色
26
+ let clazz = 'cherry-badge ';
27
+ let style = '';
28
+ let m4 = m3;
29
+ // 检查是否是预定义颜色
30
+ if (m2 && Badge.predefinedColors.has(m2)) {
31
+ clazz += `cherry-badge-${m2} `;
32
+ } else if (m2 && /^#\w{6}$/.test(m2)) {
33
+ // 检查是否是自定义颜色
34
+ style += `background-color: ${m2}`;
35
+ } else if (m2) {
36
+ // 处理位置作为第二个参数的情况
37
+ clazz += 'cherry-badge-info ';
38
+ m4 = m2;
39
+ } else {
40
+ clazz += 'cherry-badge-info ';
41
+ }
42
+
43
+ // 处理位置
44
+ const position = ['top', 'bottom', 'center'].includes(m4) ? m4 : 'center';
45
+ const positionClass = `cherry-badge-${position}`;
46
+ clazz += positionClass;
47
+
48
+ return `<span style="${style}" class="${clazz}">${m1}</span>`;
49
+ }
50
+
51
+ makeHtml(str) {
52
+ if (!this.test(str)) {
53
+ return str;
54
+ }
55
+
56
+ return str.replace(this.RULE.reg, this.toHtml.bind(this));
57
+ }
58
+
59
+ rule() {
60
+ // 正则表达式匹配 [[文本]], [[文本:颜色或位置]], [[文本:颜色,位置]]
61
+ return {
62
+ begin: '',
63
+ content: '',
64
+ end: '',
65
+ reg: new RegExp('\\[\\[([^:\\]]+):?([^,\\]]*)?,?([^\\]]*)\\]\\]', 'g'),
66
+ };
67
+ }
68
+ overlayMode() {
69
+ return {
70
+ name: 'badge',
71
+ inBadge: false,
72
+ token(stream, state) {
73
+ // 检查行的开头是否有 ':::'
74
+ if (stream.match(/\[\[(.*)]]/)) {
75
+ this.inBadge = true;
76
+ stream.backUp(stream.current().length); // 回退以单独处理
77
+ }
78
+ if (this.inBadge) {
79
+ if (stream.match('[[')) {
80
+ return 'badge-container';
81
+ }
82
+ if (stream.match(':') || stream.match(',')) {
83
+ return 'badge-color';
84
+ }
85
+
86
+ if (stream.match(/important|info|note|tip|warning|danger|top|bottom|center/) || stream.match(/#[\w+]{6}/)) {
87
+ return 'badge-text';
88
+ }
89
+ if (stream.match(']]')) {
90
+ this.inBadge = false;
91
+ return 'badge-container';
92
+ }
93
+ }
94
+
95
+ stream.next(); // 前进到下一个字符
96
+ return null; // 默认返回 null
97
+ },
98
+ };
99
+ }
100
+ }
@@ -0,0 +1,113 @@
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 ParagraphBase from '@/core/ParagraphBase';
17
+ import { compileRegExp } from '@/utils/regexp';
18
+
19
+ function computeLeadingSpaces(leadingChars) {
20
+ const indentRegex = /^(\t|[ ]{1,4})/;
21
+ let leadingCharsTemp = leadingChars;
22
+ let indent = 0;
23
+ while (indentRegex.test(leadingCharsTemp)) {
24
+ leadingCharsTemp = leadingCharsTemp.replace(/^(\t|[ ]{1,4})/g, '');
25
+ indent += 1;
26
+ }
27
+ return indent;
28
+ }
29
+
30
+ export default class Blockquote extends ParagraphBase {
31
+ static HOOK_NAME = 'blockquote';
32
+
33
+ constructor() {
34
+ super({ needCache: true });
35
+ // TODO: String.prototype.repeat polyfill
36
+ }
37
+
38
+ handleMatch(str, sentenceMakeFunc) {
39
+ return str.replace(this.RULE.reg, (match, lines, content) => {
40
+ const { sign: contentSign, html: parsedHtml } = sentenceMakeFunc(content);
41
+ const sign = this.signWithCache(parsedHtml) || contentSign;
42
+ const lineCount = this.getLineCount(match, lines); // 段落所占行数
43
+ const listRegex =
44
+ /^(([ \t]{0,3}([*+-]|\d+[.]|[a-z]\.|[I一二三四五六七八九十]+\.)[ \t]+)([^\r]+?)($|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.]|[a-z]\.|[I一二三四五六七八九十]+\.)[ \t]+)))/;
45
+ let lastIndent = computeLeadingSpaces(lines);
46
+ // 逐行处理
47
+ const contentLines = parsedHtml.split('\n');
48
+ const replaceReg = /^[>\s]+/;
49
+ const countReg = />/g;
50
+ let lastLevel = 1;
51
+ let level = 0;
52
+ let handledHtml = `<blockquote data-sign="${sign}_${lineCount}" data-lines="${lineCount}">`;
53
+ for (let i = 0; contentLines[i]; i++) {
54
+ if (i !== 0) {
55
+ const leadIndent = computeLeadingSpaces(contentLines[i]);
56
+ if (leadIndent <= lastIndent && listRegex.test(contentLines[i])) {
57
+ break;
58
+ }
59
+ lastIndent = leadIndent;
60
+ }
61
+ /* eslint-disable no-loop-func */
62
+ const $line = contentLines[i].replace(replaceReg, (leadSymbol) => {
63
+ const leadSymbols = leadSymbol.match(countReg);
64
+ // 本行引用嵌套层级比上层要多
65
+ if (leadSymbols && leadSymbols.length > lastLevel) {
66
+ level = leadSymbols.length;
67
+ } else {
68
+ // 否则保持当前缩进层级
69
+ level = lastLevel;
70
+ }
71
+ return '';
72
+ });
73
+ // 同层级,且不为首行时补充一个换行
74
+ if (lastLevel === level && i !== 0) {
75
+ handledHtml += '<br>';
76
+ }
77
+ // 补充缩进
78
+ if (lastLevel < level) {
79
+ handledHtml += '<blockquote>'.repeat(level - lastLevel);
80
+ lastLevel = level;
81
+ }
82
+ // 插入当前行内容
83
+ handledHtml += $line;
84
+ }
85
+ // 标签闭合
86
+ handledHtml += '</blockquote>'.repeat(lastLevel);
87
+ return this.getCacheWithSpace(this.pushCache(handledHtml, sign, lineCount), match);
88
+ });
89
+ }
90
+
91
+ makeHtml(str, sentenceMakeFunc) {
92
+ if (!this.test(str)) {
93
+ return str;
94
+ }
95
+ return this.handleMatch(str, sentenceMakeFunc);
96
+ }
97
+
98
+ rule() {
99
+ const ret = {
100
+ begin: '(?:^|\\n)(\\s*)',
101
+ content: [
102
+ '(',
103
+ '>(?:.+?\\n(?![*+-]|\\d+[.]|[a-z]\\.))(?:>*.+?\\n(?![*+-]|\\d+[.]|[a-z]\\.))*(?:>*.+?)', // multiline
104
+ '|', // or
105
+ '>(?:.+?)', // single line
106
+ ')',
107
+ ].join(''),
108
+ end: '(?=(\\n)|$)',
109
+ };
110
+ ret.reg = compileRegExp(ret, 'g');
111
+ return ret;
112
+ }
113
+ }
@@ -0,0 +1,85 @@
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 ParagraphBase from '@/core/ParagraphBase';
17
+ import { isBrowser } from '@/utils/env';
18
+ import { compileRegExp } from '@/utils/regexp';
19
+ import { getIsClassicBrFromLocal, testKeyInLocal } from '@/utils/config';
20
+
21
+ export default class Br extends ParagraphBase {
22
+ static HOOK_NAME = 'br';
23
+
24
+ constructor(options) {
25
+ super({ needCache: true });
26
+ this.classicBr = testKeyInLocal('classicBr') ? getIsClassicBrFromLocal() : options.globalConfig.classicBr;
27
+ }
28
+
29
+ beforeMakeHtml(str) {
30
+ if (!this.test(str)) {
31
+ return str;
32
+ }
33
+ return str.replace(this.RULE.reg, (match, lines, index) => {
34
+ // 不处理全文开头的连续空行
35
+ if (index === 0) {
36
+ return match;
37
+ }
38
+ const lineCount = lines.match(/\n/g)?.length ?? 0;
39
+ const sign = `br${lineCount}`;
40
+ let html = '';
41
+ if (isBrowser()) {
42
+ // 为了同步滚动
43
+ if (this.classicBr) {
44
+ html = `<span data-sign="${sign}" data-type="br" data-lines="${lineCount}"></span>`;
45
+ } else {
46
+ html = `<p data-sign="${sign}" data-type="br" data-lines="${lineCount}">&nbsp;</p>`;
47
+ }
48
+ } else {
49
+ // node环境下直接输出br
50
+ html = this.classicBr ? '' : '<br/>';
51
+ }
52
+ const placeHolder = this.pushCache(html, sign, lineCount);
53
+ // 结尾只补充一个\n是因为Br将下一个段落中间的所有换行都替换掉了,而两个换行符会导致下一个区块行数计算错误
54
+ return `\n\n${placeHolder}\n`;
55
+ });
56
+ }
57
+
58
+ makeHtml(str, sentenceMakeFunc) {
59
+ return str;
60
+ }
61
+
62
+ // afterMakeHtml(str) {
63
+ // return str.replace(/~~B/g, (match) => {
64
+ // let lines = that.brCache.shift() - 1;
65
+ // return '<p data-sign="br' + lines + '" data-type="br" data-lines="' + lines + '">&nbsp;</p>';
66
+ // });
67
+ // }
68
+ // default: this.restoreCache();
69
+
70
+ rule() {
71
+ /**
72
+ * 样例:
73
+ * block1\n
74
+ * \n
75
+ * \n
76
+ * block2
77
+ *
78
+ * 匹配逻辑:
79
+ * 开头必为一个换行符,所以后续只需要匹配至少两个空行即可生成一个换行,行数即content匹配到的换行符个数
80
+ */
81
+ const ret = { begin: '(?:\\n)', end: '', content: '((?:\\h*\\n){2,})' };
82
+ ret.reg = compileRegExp(ret, 'g', true);
83
+ return ret;
84
+ }
85
+ }