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,99 @@
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
+ const imgAltHelper = {
18
+ /**
19
+ * 提取alt部分的扩展属性
20
+ * @param {string} alt 图片引用中的alt部分
21
+ * @returns
22
+ */
23
+ processExtendAttributesInAlt(alt) {
24
+ const attrRegex = /#([0-9]+(px|em|pt|pc|in|mm|cm|ex|%)|auto)/g;
25
+ const info = alt.match(attrRegex);
26
+ if (!info) {
27
+ return '';
28
+ }
29
+ let extendAttrs = '';
30
+ const [width, height] = info;
31
+ if (width) {
32
+ extendAttrs = ` width="${width.replace(/[ #]*/g, '')}"`;
33
+ }
34
+ if (height) {
35
+ extendAttrs += ` height="${height.replace(/[ #]*/g, '')}"`;
36
+ }
37
+ return extendAttrs;
38
+ },
39
+
40
+ /**
41
+ * 提取alt部分的扩展样式
42
+ * @param {string} alt 图片引用中的alt部分
43
+ * @returns {{extendStyles:string, extendClasses:string}}
44
+ */
45
+ processExtendStyleInAlt(alt) {
46
+ let extendStyles = this.$getAlignment(alt);
47
+ let extendClasses = '';
48
+ const info = alt.match(/#(border|shadow|radius|B|S|R)/g);
49
+ if (info) {
50
+ for (let i = 0; i < info.length; i++) {
51
+ switch (info[i]) {
52
+ case '#border':
53
+ case '#B':
54
+ extendStyles += 'border:1px solid #888888;padding: 2px;box-sizing: border-box;';
55
+ extendClasses += ' cherry-img-border';
56
+ break;
57
+ case '#shadow':
58
+ case '#S':
59
+ extendStyles += 'box-shadow:0 2px 15px -5px rgb(0 0 0 / 50%);';
60
+ extendClasses += ' cherry-img-shadow';
61
+ break;
62
+ case '#radius':
63
+ case '#R':
64
+ extendStyles += 'border-radius: 15px;';
65
+ extendClasses += ' cherry-img-radius';
66
+ break;
67
+ }
68
+ }
69
+ }
70
+ return { extendStyles, extendClasses };
71
+ },
72
+
73
+ /**
74
+ * 从alt中提取对齐方式信息
75
+ * @param {string} alt
76
+ * @returns {string}
77
+ */
78
+ $getAlignment(alt) {
79
+ const styleRegex = /#(center|right|left|float-right|float-left)/i;
80
+ const info = alt.match(styleRegex);
81
+ if (!info) {
82
+ return '';
83
+ }
84
+ const [, alignment] = info;
85
+ switch (alignment) {
86
+ case 'center':
87
+ return 'transform:translateX(-50%);margin-left:50%;display:block;';
88
+ case 'right':
89
+ return 'transform:translateX(-100%);margin-left:100%;margin-right:-100%;display:block;';
90
+ case 'left':
91
+ return 'transform:translateX(0);margin-left:0;display:block;';
92
+ case 'float-right':
93
+ return 'float:right;transform:translateX(0);margin-left:0;display:block;';
94
+ case 'float-left':
95
+ return 'float:left;transform:translateX(0);margin-left:0;display:block;';
96
+ }
97
+ },
98
+ };
99
+ export default imgAltHelper;
@@ -0,0 +1,279 @@
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
+ */
19
+ const imgSizeHandler = {
20
+ mouseResize: {},
21
+ getImgPosition() {
22
+ const position = this.img.getBoundingClientRect();
23
+ const editorPosition = this.previewerDom.parentNode.getBoundingClientRect();
24
+ const padding = parseFloat(this.img.style.padding) || 0;
25
+ return {
26
+ bottom: position.bottom - editorPosition.bottom,
27
+ top: position.top - editorPosition.top + padding * 1.5,
28
+ height: position.height,
29
+ width: position.width,
30
+ right: position.right - editorPosition.right,
31
+ left: position.left - editorPosition.left + padding * 1.5,
32
+ x: position.x - editorPosition.x,
33
+ y: position.y - editorPosition.y,
34
+ };
35
+ },
36
+ initBubbleButtons() {
37
+ const position = this.getImgPosition();
38
+ return {
39
+ points: {
40
+ arr: [
41
+ 'leftTop',
42
+ 'leftBottom',
43
+ 'rightTop',
44
+ 'rightBottom',
45
+ 'leftMiddle',
46
+ 'middleBottom',
47
+ 'middleTop',
48
+ 'rightMiddle',
49
+ ],
50
+ arrInfo: {
51
+ leftTop: { name: '20', left: 0, top: 0 },
52
+ leftBottom: { name: '00', left: 0, top: 0 },
53
+ rightTop: { name: '22', left: 0, top: 0 },
54
+ rightBottom: { name: '02', left: 0, top: 0 },
55
+ leftMiddle: { name: '10', left: 0, top: 0 },
56
+ middleBottom: { name: '01', left: 0, top: 0 },
57
+ middleTop: { name: '21', left: 0, top: 0 },
58
+ rightMiddle: { name: '12', left: 0, top: 0 },
59
+ },
60
+ },
61
+ imgSrc: this.img.src,
62
+ style: {
63
+ width: this.img.width,
64
+ height: this.img.height,
65
+ left: position.left - 1,
66
+ top: position.top - 1,
67
+ marginTop: 0,
68
+ marginLeft: 0,
69
+ },
70
+ scrollTop: this.previewerDom.scrollTop,
71
+ position,
72
+ };
73
+ },
74
+ showBubble(img, container, previewerDom) {
75
+ if (this.$isResizing()) {
76
+ return;
77
+ }
78
+ this.img = img;
79
+ this.previewerDom = previewerDom;
80
+ this.container = container;
81
+ this.buts = this.initBubbleButtons();
82
+ this.drawBubbleButs();
83
+ },
84
+ emit(type, event = {}) {
85
+ switch (type) {
86
+ case 'mousedown':
87
+ return this.resizeBegin(event);
88
+ case 'mouseup':
89
+ return this.resizeStop(event);
90
+ case 'mousemove':
91
+ return this.resizeWorking(event);
92
+ case 'scroll':
93
+ return this.dealScroll(event);
94
+ case 'remove':
95
+ return this.remove();
96
+ case 'previewUpdate':
97
+ return this.previewUpdate(event);
98
+ }
99
+ },
100
+ previewUpdate(callback) {
101
+ if (this.$isResizing()) {
102
+ return;
103
+ }
104
+ this.remove();
105
+ callback();
106
+ },
107
+ drawBubbleButs() {
108
+ if (this.butsLayout) {
109
+ return this.updateBubbleButs();
110
+ }
111
+ this.butsLayout = this.container;
112
+ this.butsImg = document.createElement('div');
113
+ this.butsImg.className = 'cherry-previewer-img-size-handler__background';
114
+ this.butsImg.style.backgroundImage = `url(${this.buts.imgSrc})`;
115
+ this.butsLayout.appendChild(this.butsImg);
116
+
117
+ this.butsPoints = {};
118
+ Object.keys(this.buts.points.arr).forEach((index) => {
119
+ const name = this.buts.points.arr[index];
120
+ const tmp = document.createElement('div');
121
+ tmp.className = [
122
+ 'cherry-previewer-img-size-handler__points',
123
+ `cherry-previewer-img-size-handler__points-${name}`,
124
+ ].join(' ');
125
+ tmp.dataset.name = name;
126
+ this.butsLayout.appendChild(tmp);
127
+ this.butsPoints[`pints-${name}`] = tmp;
128
+ });
129
+ return this.updateBubbleButs();
130
+ },
131
+ remove() {
132
+ this.butsLayout = false;
133
+ },
134
+ updateBubbleButs() {
135
+ this.$updatePointsInfo();
136
+ Object.keys(this.buts.style).forEach((name) => {
137
+ this.butsLayout.style[name] = `${this.buts.style[name]}px`;
138
+ });
139
+ Object.keys(this.buts.points.arr).forEach((index) => {
140
+ const name = this.buts.points.arr[index];
141
+ this.butsPoints[`pints-${name}`].style.top = `${this.buts.points.arrInfo[name].top}px`;
142
+ this.butsPoints[`pints-${name}`].style.left = `${this.buts.points.arrInfo[name].left}px`;
143
+ });
144
+ },
145
+ $updatePointsInfo() {
146
+ const pointLeft = this.buts.style.width;
147
+ const pointTop = this.buts.style.height;
148
+ const newPointsInfo = this.$getPointsInfo(pointLeft, pointTop);
149
+ Object.keys(this.buts.points.arr).forEach((index) => {
150
+ const name = this.buts.points.arr[index];
151
+ if (this.buts.points.arrInfo[name].left !== newPointsInfo[name].left) {
152
+ this.buts.points.arrInfo[name].left = newPointsInfo[name].left;
153
+ }
154
+ if (this.buts.points.arrInfo[name].top !== newPointsInfo[name].top) {
155
+ this.buts.points.arrInfo[name].top = newPointsInfo[name].top;
156
+ }
157
+ });
158
+ },
159
+ $getPointsInfo(left, top) {
160
+ return {
161
+ leftTop: { left: 0, top: 0 },
162
+ leftBottom: { left: 0, top },
163
+ rightTop: { left, top: 0 },
164
+ rightBottom: { left, top },
165
+ leftMiddle: { left: 0, top: top / 2 },
166
+ middleBottom: { left: left / 2, top },
167
+ middleTop: { left: left / 2, top: 0 },
168
+ rightMiddle: { left, top: top / 2 },
169
+ };
170
+ },
171
+ $isResizing() {
172
+ return this.mouseResize.resize;
173
+ },
174
+ dealScroll(event) {
175
+ const position = this.getImgPosition();
176
+ if (this.butsLayout.style.marginTop !== position.top - this.buts.position.top) {
177
+ this.butsLayout.style.marginTop = `${position.top - this.buts.position.top}px`;
178
+ this.buts.style.marginTop = `${position.top - this.buts.position.top}px`;
179
+ }
180
+ if (this.butsLayout.style.marginLeft !== position.left - this.buts.position.left) {
181
+ this.butsLayout.style.marginLeft = `${position.left - this.buts.position.left}px`;
182
+ this.buts.style.marginLeft = `${position.left - this.buts.position.left}px`;
183
+ }
184
+ },
185
+ initMouse() {
186
+ return { left: 0, top: 0, resize: false, name: '' };
187
+ },
188
+ resizeBegin(event) {
189
+ const point = event.target;
190
+ if (!point.classList.contains('cherry-previewer-img-size-handler__points')) {
191
+ return false;
192
+ }
193
+ this.mouseResize.left = event.clientX;
194
+ this.mouseResize.top = event.clientY;
195
+ this.mouseResize.resize = true;
196
+ this.mouseResize.name = point.getAttribute('data-name');
197
+ this.previewerDom.classList.add('doing-resize-img');
198
+ },
199
+ resizeStop(event, buts, editor, menu) {
200
+ if (!this.$isResizing()) {
201
+ return false;
202
+ }
203
+ this.img.style.width = `${this.buts.style.width}px`;
204
+ this.img.style.height = `${this.buts.style.height}px`;
205
+ this.buts.style.marginTop = 0;
206
+ this.buts.style.marginLeft = 0;
207
+ this.updateBubbleButs();
208
+ this.mouseResize.resize = false;
209
+ this.previewerDom.classList.remove('doing-resize-img');
210
+ this.change();
211
+ },
212
+ resizeWorking(event, buts) {
213
+ if (!this.$isResizing()) {
214
+ return;
215
+ }
216
+ const changeX = event.clientX - this.mouseResize.left;
217
+ const changeY = event.clientY - this.mouseResize.top;
218
+ let change = {};
219
+ switch (this.mouseResize.name) {
220
+ case 'leftTop':
221
+ case 'leftBottom':
222
+ case 'leftMiddle':
223
+ change = this.$getChange(changeX, changeY, 'x');
224
+ this.buts.style.width = this.buts.position.width - change.changeX;
225
+ if (this.mouseResize.name !== 'leftMiddle') {
226
+ this.buts.style.height = this.buts.position.height - change.changeY;
227
+ }
228
+ break;
229
+ case 'rightTop':
230
+ case 'rightBottom':
231
+ case 'rightMiddle':
232
+ change = this.$getChange(changeX, changeY, 'x');
233
+ this.buts.style.width = this.buts.position.width + change.changeX;
234
+ if (this.mouseResize.name !== 'rightMiddle') {
235
+ this.buts.style.height = this.buts.position.height + change.changeY;
236
+ }
237
+ break;
238
+ case 'middleTop':
239
+ change = this.$getChange(changeX, changeY, 'y');
240
+ this.buts.style.height = this.buts.position.height - change.changeY;
241
+ break;
242
+ case 'middleBottom':
243
+ change = this.$getChange(changeX, changeY, 'y');
244
+ this.buts.style.height = this.buts.position.height + change.changeY;
245
+ break;
246
+ }
247
+ this.updateBubbleButs();
248
+ this.change();
249
+ },
250
+ change() {
251
+ this.emitChange(this.img, { width: this.buts.style.width, height: this.buts.style.height });
252
+ },
253
+ bindChange(func) {
254
+ this.emitChange = func;
255
+ },
256
+ /**
257
+ * 根据宽(x)或高(y)来进行等比例缩放
258
+ * @param {number} x 宽度
259
+ * @param {number} y 高度
260
+ * @param {string} type 类型,以宽/高为基准做等比例缩放
261
+ * @returns
262
+ */
263
+ $getChange(x, y, type) {
264
+ const ret = { changeX: 0, changeY: 0 };
265
+ switch (type) {
266
+ case 'y':
267
+ ret.changeY = y;
268
+ ret.changeX = (y * this.buts.position.width) / this.buts.position.height;
269
+ break;
270
+ default:
271
+ ret.changeX = x;
272
+ ret.changeY = (x * this.buts.position.height) / this.buts.position.width;
273
+ break;
274
+ }
275
+ return ret;
276
+ },
277
+ };
278
+
279
+ export default imgSizeHandler;
@@ -0,0 +1,17 @@
1
+ export default function extraJSON(src) {
2
+ if (typeof src === 'object') {
3
+ return src;
4
+ }
5
+ try {
6
+ return JSON.parse(src);
7
+ } catch (e) {
8
+ try {
9
+ // eslint-disable-next-line no-new-func
10
+ const fn = new Function(`return ${src}`);
11
+ return fn();
12
+ } catch (e) {
13
+ console.error('解析错误:', e);
14
+ }
15
+ return {};
16
+ }
17
+ }
@@ -0,0 +1,49 @@
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} match 匹配全文
19
+ * @param {string} processedContent 加入的内容
20
+ */
21
+ export function prependLineFeedForParagraph(match, processedContent, canNestedInList = false) {
22
+ if (!/^\n/.test(match)) {
23
+ return processedContent;
24
+ }
25
+ if (canNestedInList) {
26
+ const leadingLinesCount = match.match(/^\n+/g)?.[0]?.length ?? 0;
27
+ // 前置换行符数量大于2时,补充两个换行符,否则只补充一个
28
+ if (leadingLinesCount > 1) {
29
+ return `\n\n${processedContent}`;
30
+ }
31
+ return `\n${processedContent}`;
32
+ }
33
+ return `\n\n${processedContent}`;
34
+ }
35
+
36
+ /**
37
+ * 计算段落所占行数,必须传入通过 prependLineFeedForParagraph 方法处理后的内容,才能计算准确
38
+ * @param {string} preLinesMatch 前置匹配行
39
+ * @param {number} contentLines 实际内容行数
40
+ */
41
+ export function calculateLinesOfParagraph(preLinesMatch, contentLines) {
42
+ let preLineCount = (preLinesMatch.match(/\n/g) || []).length;
43
+ // 前置行匹配文本为空,说明是全文开头
44
+ // 非全文开头前面必有两个从 prependLineFeed 方法新增加的换行符
45
+ if (preLinesMatch !== '') {
46
+ preLineCount -= 2;
47
+ }
48
+ return preLineCount + contentLines;
49
+ }
@@ -0,0 +1,227 @@
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 { getValueWithoutCode, LIST_CONTENT } from '@/utils/regexp';
18
+
19
+ export default class ListHandler {
20
+ /** @type{HTMLElement} */
21
+ bubbleContainer = null;
22
+
23
+ regList = LIST_CONTENT;
24
+
25
+ /** @type{Array.<import('codemirror').Position>} */
26
+ range = [];
27
+
28
+ /** @type{import('codemirror').Position} */
29
+ position = { line: 0, ch: 0 };
30
+
31
+ input = false;
32
+
33
+ isCheckbox = false;
34
+
35
+ /**
36
+ * @param {string} trigger 触发方式
37
+ * @param {HTMLParagraphElement} target 目标dom
38
+ * @param {HTMLDivElement} container bubble容器
39
+ * @param {HTMLDivElement} previewerDom 预览器dom
40
+ * @param {import('../Editor').default} editor 编辑器实例
41
+ */
42
+ constructor(trigger, target, container, previewerDom, editor, options = {}) {
43
+ this.trigger = trigger;
44
+ this.target = target;
45
+ this.container = container;
46
+ this.previewerDom = previewerDom;
47
+ this.editor = editor;
48
+ this.insertLineBreak = false; // 是否为插入换行符
49
+ this.handleEditablesInputBinded = this.handleEditablesInput.bind(this); // 保证this指向正确以及能够正确移除事件
50
+ this.handleEditablesUnfocusBinded = this.handleEditablesUnfocus.bind(this);
51
+ this.target.addEventListener('input', this.handleEditablesInputBinded, false);
52
+ this.target.addEventListener('focusout', this.handleEditablesUnfocusBinded, false);
53
+ this.setSelection();
54
+ }
55
+
56
+ /**
57
+ * 触发事件
58
+ * @param {string} type 事件类型
59
+ * @param {Event} event 事件对象
60
+ */
61
+ emit(type, event) {
62
+ switch (type) {
63
+ case 'remove':
64
+ return this.remove();
65
+ }
66
+ }
67
+
68
+ remove() {
69
+ if (this.bubbleContainer) {
70
+ this.bubbleContainer.style.display = 'none';
71
+ if (this.bubbleContainer.children[0] instanceof HTMLTextAreaElement) {
72
+ this.bubbleContainer.children[0].value = ''; // 清空内容
73
+ }
74
+ }
75
+ this.target.removeAttribute('contenteditable');
76
+ this.target.removeEventListener('input', this.handleEditablesInputBinded, false);
77
+ this.target.removeEventListener('focusout', this.handleEditablesUnfocusBinded, false);
78
+ const cursor = this.editor.editor.getCursor(); // 获取光标位置
79
+ this.editor.editor.setSelection(cursor, cursor); // 取消选中
80
+ }
81
+
82
+ setSelection() {
83
+ const allLi = Array.from(this.previewerDom.querySelectorAll('li.cherry-list-item')); // 预览区域内所有的li
84
+ const targetLiIdx = allLi.findIndex((li) => li === this.target.parentElement);
85
+ if (targetLiIdx === -1) {
86
+ return; // 没有找到li
87
+ }
88
+ const contents = getValueWithoutCode(this?.editor.editor.getValue())?.split('\n') ?? [];
89
+ let contentsLiCount = 0; // 编辑器中是列表的数量
90
+ let targetLine = -1; // 行
91
+ let targetCh = -1; // 列
92
+ const targetContent = []; // 当前点击的li的内容
93
+ for (let lineIdx = 0; lineIdx < contents.length; lineIdx++) {
94
+ const lineContent = contents[lineIdx];
95
+ if (!lineContent || lineContent === '/n') {
96
+ if (targetContent.length <= 0) {
97
+ continue;
98
+ } else {
99
+ break;
100
+ }
101
+ }
102
+ // 匹配是否符合列表的正则
103
+ const regRes = this.regList.exec(lineContent);
104
+ if (regRes !== null) {
105
+ if (targetContent.length > 0) {
106
+ break;
107
+ }
108
+ const [, indent, identifier, checkbox, content] = regRes;
109
+ if (contentsLiCount === targetLiIdx && indent !== undefined) {
110
+ targetLine = lineIdx;
111
+ // eslint-disable-next-line prefer-destructuring
112
+ targetContent.push(content); // 这里只取一个没必要解构
113
+ targetCh = lineContent.indexOf(content);
114
+ // 1. 这种需要特殊处理,需要跳过一个空格位,否则层级会错乱
115
+ if (identifier?.endsWith('.')) {
116
+ targetCh += 1;
117
+ }
118
+ // checkbox 编辑的元素内以选中的checkbox开头时需要去掉,否则会被解析
119
+ if (checkbox) {
120
+ this.isCheckbox = true;
121
+ }
122
+ }
123
+ contentsLiCount += 1;
124
+ } else if (targetContent.length > 0) {
125
+ targetContent.push(lineContent);
126
+ }
127
+ }
128
+ const from = { line: targetLine, ch: targetCh };
129
+ const to = {
130
+ line: targetLine + targetContent.length - 1,
131
+ ch: targetCh + targetContent[targetContent.length - 1].length,
132
+ };
133
+ this.editor.editor.setSelection(from, to);
134
+ this.range = [from, to];
135
+ this.position = this.editor.editor.getCursor(); // 输入就获取光标位置,防止后面点到编辑器dom的时候光标位置不对
136
+ }
137
+
138
+ /**
139
+ * 处理contenteditable元素的输入事件
140
+ * @param {InputEvent} event
141
+ */
142
+ handleEditablesInput(event) {
143
+ this.input = true;
144
+ event.stopPropagation();
145
+ event.preventDefault();
146
+ /** @typedef {'insertText'|'insertFromPaste'|'insertParagraph'|'insertLineBreak'|'deleteContentBackward'|'deleteContentForward'|'deleteByCut'|'deleteContentForward'|'deleteWordBackward'} InputType*/
147
+ if (event.target instanceof HTMLParagraphElement) {
148
+ if (event.inputType === 'insertParagraph' || event.inputType === 'insertLineBreak') {
149
+ this.insertLineBreak = true;
150
+ this.handleInsertLineBreak(event);
151
+ }
152
+ }
153
+ }
154
+
155
+ /**
156
+ * 处理contenteditable元素的失去焦点事件
157
+ * @param {FocusEvent} event
158
+ */
159
+ handleEditablesUnfocus(event) {
160
+ event.stopPropagation();
161
+ event.preventDefault();
162
+ if (event.target instanceof HTMLParagraphElement) {
163
+ if (this.input) {
164
+ if (!this.insertLineBreak) {
165
+ const replaceHtml = !this.isCheckbox
166
+ ? event.target.innerHTML
167
+ : event.target.innerHTML.replace(/<input[^>]*>/g, '');
168
+ const md = this.editor.$cherry.engine.makeMarkdown(replaceHtml);
169
+ const [from, to] = this.range;
170
+ this.editor.editor.replaceRange(md, from, to);
171
+ }
172
+ this.isCheckbox = false;
173
+ this.input = false;
174
+ this.insertLineBreak = false;
175
+ }
176
+ this.remove();
177
+ }
178
+ }
179
+
180
+ /**
181
+ * @param {InputEvent} event
182
+ */
183
+ handleInsertLineBreak(event) {
184
+ /** @type {Array<string>} */
185
+ let splitInnerText = [];
186
+ // @ts-ignore
187
+ if ('innerText' in event.target && typeof event.target.innerText === 'string') {
188
+ // @ts-ignore
189
+ splitInnerText = event.target.innerText.split('\n');
190
+ }
191
+ // 只有第一段是换行,后面的换行都应该认为是另一行
192
+ const [before, ...after] = splitInnerText;
193
+ // 获取当前光标位置
194
+ const cursor = this.editor.editor.getCursor();
195
+ // 获取光标行的内容
196
+ const lineContent = this.editor.editor.getLine(cursor.line);
197
+ const regRes = this.regList.exec(lineContent);
198
+ let insertContent = '\n- ';
199
+ if (regRes !== null) {
200
+ // 存在选中的checkbox则替换为未选中的checkbox,其他的保持原样
201
+ insertContent = `\n${regRes[1]}${regRes[2]?.replace('[x]', '[ ] ')}`;
202
+ }
203
+ insertContent += after?.join('') ?? '';
204
+ // 把当前行内容剪掉
205
+ this.editor.editor.replaceRange(
206
+ before,
207
+ {
208
+ line: cursor.line,
209
+ ch: regRes[2]?.length ?? 0,
210
+ },
211
+ {
212
+ line: cursor.line,
213
+ ch: lineContent.length,
214
+ },
215
+ );
216
+ // 在当前行的末尾插入一个换行符,这会创建一个新行
217
+ this.editor.editor.replaceRange(insertContent, {
218
+ line: cursor.line,
219
+ ch: lineContent.length,
220
+ });
221
+ // 将光标移动到新行
222
+ this.editor.editor.setCursor({ line: cursor.line + 1, ch: insertContent.length + 1 });
223
+ // 将光标聚焦到编辑器上
224
+ this.editor.editor.focus();
225
+ this.remove();
226
+ }
227
+ }