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,592 @@
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 { getTableRule, getCodeBlockRule } from '@/utils/regexp';
17
+ /**
18
+ * 用于在表格上出现编辑区,并提供拖拽行列的功能
19
+ */
20
+ export default class TableHandler {
21
+ /**
22
+ * 用来存放所有的数据
23
+ */
24
+ tableEditor = {
25
+ info: {}, // 当前点击的预览区域table的相关信息
26
+ tableCodes: [], // 编辑器内所有的表格语法
27
+ editorDom: {}, // 编辑器容器
28
+ };
29
+
30
+ constructor(trigger, target, container, previewerDom, codeMirror, tableElement) {
31
+ // 触发方式 click / hover
32
+ this.trigger = trigger;
33
+ this.target = target;
34
+ this.previewerDom = previewerDom;
35
+ this.container = container;
36
+ this.codeMirror = codeMirror;
37
+ this.$initReg();
38
+ this.$findTableInEditor();
39
+ this.tableElement = tableElement;
40
+ }
41
+
42
+ emit(type, event = {}, callback = () => {}) {
43
+ switch (type) {
44
+ case 'keyup':
45
+ return this.trigger === 'click' && this.$onInputChange(event);
46
+ case 'remove':
47
+ return this.$remove();
48
+ case 'scroll':
49
+ return this.$refreshPosition();
50
+ case 'previewUpdate':
51
+ return this.$refreshPosition();
52
+ case 'mousedown':
53
+ // return this.trigger !== 'click' && this.$drawDrag();
54
+ return;
55
+ case 'mouseup':
56
+ return this.trigger === 'click' && this.$tryRemoveMe(event, callback);
57
+ }
58
+ }
59
+
60
+ $tryRemoveMe(event, callback) {
61
+ if (!/textarea/i.test(event.target.tagName)) {
62
+ this.$remove();
63
+ callback();
64
+ }
65
+ }
66
+
67
+ /**
68
+ * 获取目标dom的位置信息和尺寸信息
69
+ */
70
+ $getPosition(node = this.tableEditor.info.tdNode) {
71
+ const position = node.getBoundingClientRect();
72
+ const editorPosition = this.previewerDom.parentNode.getBoundingClientRect();
73
+ return {
74
+ top: position.top - editorPosition.top,
75
+ height: position.height,
76
+ width: position.width,
77
+ left: position.left - editorPosition.left,
78
+ maxHeight: editorPosition.height,
79
+ };
80
+ }
81
+
82
+ setStyle(element, property, value) {
83
+ const info = element.getBoundingClientRect();
84
+ if (info[property] !== value) {
85
+ element.style[property] = value;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * TODO: 这里是分别对文本框、操作符号和选项设置偏移,应该作为一个整体来设置
91
+ */
92
+ $setInputOffset() {
93
+ const tdInfo = this.$getPosition();
94
+ const { inputDiv } = this.tableEditor.editorDom;
95
+ // 设置文本框的偏移及大小
96
+ this.setStyle(inputDiv, 'width', `${tdInfo.width}px`);
97
+ this.setStyle(inputDiv, 'height', `${tdInfo.height}px`);
98
+ this.setStyle(inputDiv, 'top', `${tdInfo.top}px`);
99
+ this.setStyle(inputDiv, 'left', `${tdInfo.left}px`);
100
+
101
+ // 根据是否超出边界来显示或者隐藏元素
102
+ const isWithinBounds = tdInfo.top >= 0 && tdInfo.top + tdInfo.height <= tdInfo.maxHeight;
103
+ this.setStyle(inputDiv, 'display', isWithinBounds ? '' : 'none');
104
+ }
105
+
106
+ /**
107
+ * 刷新操作符位置
108
+ */
109
+ $setSymbolOffset() {
110
+ const container = this.tableEditor.editorDom.symbolContainer;
111
+ const { tableNode, trNode, isTHead } = this.tableEditor.info;
112
+ const tableInfo = this.$getPosition(tableNode);
113
+ const trInfo = this.$getPosition(trNode);
114
+ const tdInfo = this.$getPosition();
115
+ const previewerRect = this.previewerDom.getBoundingClientRect();
116
+ // 设置容器宽高
117
+ this.setStyle(this.container, 'width', `${tableInfo.width}px`);
118
+ this.setStyle(this.container, 'height', `${tableInfo.height}px`);
119
+ this.setStyle(this.container, 'top', `${tableInfo.top}px`);
120
+ this.setStyle(this.container, 'left', `${tableInfo.left}px`);
121
+
122
+ // 判断是否在预览区内
123
+ const isWithinBounds = (symbol) => {
124
+ const symbolRect = symbol.getBoundingClientRect();
125
+ const boundMap = {
126
+ top: [previewerRect.top, previewerRect.top + previewerRect.height - symbolRect.height],
127
+ left: [previewerRect.left, previewerRect.left + previewerRect.width - symbolRect.width],
128
+ };
129
+ return Object.entries(boundMap).every(([key, [min, max]]) => symbolRect[key] >= min && symbolRect[key] <= max);
130
+ };
131
+
132
+ // 设置操作符位置与控制显隐
133
+ container.childNodes.forEach((node) => {
134
+ const { index, type, dir } = node.dataset;
135
+ const propDict = {
136
+ Row: ['left', 'right'],
137
+ Col: ['top', 'bottom'],
138
+ };
139
+ const offset = {
140
+ outer: 20,
141
+ radius: 7,
142
+ };
143
+ this.setStyle(node, propDict[dir][index], `-${offset.outer}px`);
144
+ this.setStyle(node, 'display', '');
145
+ const refreshMap = {
146
+ LastRow: () => this.setStyle(node, 'top', `${trInfo.top - tableInfo.top - offset.radius}px`),
147
+ NextRow: () => this.setStyle(node, 'top', `${trInfo.top - tableInfo.top + trInfo.height - offset.radius}px`),
148
+ LastCol: () => this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left - offset.radius}px`),
149
+ NextCol: () => this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left + tdInfo.width - offset.radius}px`),
150
+ };
151
+ const oper = `${type}${dir}`;
152
+ refreshMap[oper]();
153
+ this.setStyle(node, 'display', isWithinBounds(node) ? '' : 'none');
154
+ if (isTHead && oper === 'LastRow') {
155
+ this.setStyle(node, 'display', 'none');
156
+ }
157
+ });
158
+ }
159
+
160
+ /**
161
+ * 刷新定位
162
+ */
163
+ $refreshPosition() {
164
+ if (this.trigger === 'click') {
165
+ this.$setInputOffset();
166
+ return;
167
+ }
168
+ this.$setSymbolOffset();
169
+ }
170
+
171
+ $remove() {
172
+ this.tableEditor = { info: {}, tableCodes: [], editorDom: {} };
173
+ }
174
+
175
+ /**
176
+ * 收集编辑器中的表格语法,并记录表格语法的开始的offset
177
+ */
178
+ $collectTableCode() {
179
+ const tableCodes = [];
180
+ this.codeMirror
181
+ .getValue()
182
+ .replace(this.codeBlockReg, (whole, ...args) => {
183
+ // 先把代码块里的表格语法关键字干掉
184
+ return whole.replace(/\|/g, '.');
185
+ })
186
+ .replace(this.tableReg, function (whole, ...args) {
187
+ const match = whole.replace(/^\n*/, '');
188
+ const offsetBegin = args[args.length - 2] + whole.match(/^\n*/)[0].length;
189
+ tableCodes.push({
190
+ code: match,
191
+ offset: offsetBegin,
192
+ });
193
+ });
194
+ this.tableEditor.tableCodes = tableCodes;
195
+ }
196
+
197
+ /**
198
+ * 获取预览区域被点击的table对象,并记录table的顺位
199
+ */
200
+ $collectTableDom() {
201
+ const list = Array.from(this.previewerDom.querySelectorAll('table.cherry-table'));
202
+ const tableNode = this.$getClosestNode(this.target, 'TABLE');
203
+ if (tableNode === false) {
204
+ return false;
205
+ }
206
+ const columns = Array.from(this.target.parentElement.childNodes).filter((child) => {
207
+ // 计算列数
208
+ return child.tagName.toLowerCase() === 'td';
209
+ }).length;
210
+
211
+ this.tableEditor.info = {
212
+ tableNode,
213
+ tdNode: this.target,
214
+ trNode: this.target.parentElement,
215
+ tdIndex: Array.from(this.target.parentElement.childNodes).indexOf(this.target),
216
+ trIndex: Array.from(this.target.parentElement.parentElement.childNodes).indexOf(this.target.parentElement),
217
+ isTHead: this.target.parentElement.parentElement.tagName !== 'TBODY',
218
+ totalTables: list.length,
219
+ tableIndex: list.indexOf(tableNode),
220
+ tableText: tableNode.textContent.replace(/[\s]/g, ''),
221
+ columns,
222
+ };
223
+ }
224
+
225
+ /**
226
+ * 选中对应单元格、所在行、所在列的内容
227
+ * @param {Number} index
228
+ * @param {String} type 'td': 当前单元格, 'table': 当前表格
229
+ * @param {Boolean} select 是否选中编辑器中的代码
230
+ */
231
+ $setSelection(index, type = 'table', select = true) {
232
+ const tableCode = this.tableEditor.tableCodes[index];
233
+ const whole = this.codeMirror.getValue();
234
+ const selectTdInfo = this.tableEditor.info;
235
+ const beginLine = whole.slice(0, tableCode.offset).match(/\n/g)?.length ?? 0;
236
+ const { preLine, preCh, plusCh, currentTd } = this.$getTdOffset(
237
+ tableCode.code,
238
+ selectTdInfo.isTHead,
239
+ selectTdInfo.trIndex,
240
+ selectTdInfo.tdIndex,
241
+ );
242
+ if (type === 'table') {
243
+ const endLine = beginLine + tableCode.code.match(/\n/g).length;
244
+ const endCh = tableCode.code.match(/[^\n]+\n*$/)[0].length;
245
+ this.tableEditor.info.selection = [
246
+ { line: beginLine, ch: 0 },
247
+ { line: endLine, ch: endCh },
248
+ ];
249
+ } else {
250
+ this.tableEditor.info.selection = [
251
+ { line: beginLine + preLine, ch: preCh },
252
+ { line: beginLine + preLine, ch: preCh + plusCh },
253
+ ];
254
+ }
255
+ select && this.codeMirror.setSelection(...this.tableEditor.info.selection);
256
+ this.tableEditor.info.code = currentTd;
257
+ }
258
+
259
+ /**
260
+ * 获取对应单元格的偏移量
261
+ * @param {String} tableCode
262
+ * @param {Boolean} isTHead
263
+ * @param {Number} trIndex
264
+ * @param {Number} tdIndex
265
+ */
266
+ $getTdOffset(tableCode, isTHead, trIndex, tdIndex) {
267
+ const codes = tableCode.split(/\n/);
268
+ const targetTr = isTHead ? 0 : trIndex + 2;
269
+ const tds = codes[targetTr].split(/\|/);
270
+ const needPlus1 = /^\s*$/.test(tds[0]);
271
+ const targetTd = needPlus1 ? tdIndex + 1 : tdIndex;
272
+ const current = tds[targetTd];
273
+ const pre = [];
274
+ for (let i = 0; i < targetTd; i++) {
275
+ pre.push(tds[i]);
276
+ }
277
+ return {
278
+ preLine: targetTr,
279
+ preCh: needPlus1 ? pre.join('|').length + 1 : pre.join('|').length,
280
+ plusCh: current.length,
281
+ currentTd: current,
282
+ };
283
+ }
284
+
285
+ /**
286
+ * 在编辑器里找到对应的表格源码,并让编辑器选中
287
+ */
288
+ $findTableInEditor() {
289
+ this.$collectTableDom();
290
+ this.$collectTableCode();
291
+ // 暂时不考虑代码块中包含表格、人为输入表格html语法、tapd特色表格语法的情况
292
+ // 也就是说,出现上述情况时,表格的所见即所得编辑功能失效
293
+ if (this.tableEditor.info.totalTables !== this.tableEditor.tableCodes.length) {
294
+ return false;
295
+ }
296
+ this.$setSelection(this.tableEditor.info.tableIndex, 'td', this.trigger === 'click');
297
+ }
298
+
299
+ $initReg() {
300
+ this.tableReg = this.tableReg ? this.tableReg : getTableRule(true);
301
+ this.codeBlockReg = this.codeBlockReg ? this.codeBlockReg : getCodeBlockRule().reg;
302
+ }
303
+
304
+ showBubble() {
305
+ if (this.trigger === 'click') {
306
+ this.$drawEditor();
307
+ return;
308
+ }
309
+ this.$drawSymbol();
310
+ }
311
+
312
+ /**
313
+ * 判断是否处于编辑状态
314
+ * @returns {boolean}
315
+ */
316
+ $isEditing() {
317
+ return this.tableEditor.editing;
318
+ }
319
+
320
+ /**
321
+ * 把表格上的input单行文本框和操作符号画出来
322
+ */
323
+ $drawEditor() {
324
+ const dom = document.createElement('div');
325
+ dom.className = 'cherry-previewer-table-content-handler__input';
326
+ const input = document.createElement('textarea');
327
+ dom.appendChild(input);
328
+ this.tableEditor.editorDom.inputDiv = dom;
329
+ this.tableEditor.editorDom.inputDom = input;
330
+ this.$updateEditorPosition();
331
+ this.container.appendChild(this.tableEditor.editorDom.inputDiv);
332
+ this.tableEditor.editorDom.inputDom.value = this.tableEditor.info.code.replace(/<br>/g, '\n');
333
+ this.tableEditor.editorDom.inputDom.focus();
334
+ }
335
+
336
+ $onInputChange(e) {
337
+ if (e.target.tagName !== 'TEXTAREA') {
338
+ return;
339
+ }
340
+ this.codeMirror.replaceSelection(e.target.value.replace(/\n/g, '<br>'), 'around');
341
+ }
342
+
343
+ /**
344
+ * 更新编辑器的位置(尺寸和位置)
345
+ */
346
+ $updateEditorPosition() {
347
+ this.$setInputOffset();
348
+ const tdStyle = getComputedStyle(this.tableEditor.info.tdNode);
349
+ this.tableEditor.editorDom.inputDom.style.textAlign = tdStyle.textAlign || 'left';
350
+ this.tableEditor.editorDom.inputDom.style.fontSize = tdStyle.fontSize || '16px';
351
+ this.tableEditor.editorDom.inputDom.style.fontFamily = tdStyle.fontFamily;
352
+ this.tableEditor.editorDom.inputDom.style.lineHeight = tdStyle.lineHeight;
353
+ this.tableEditor.editorDom.inputDom.style.padding = tdStyle.padding;
354
+ // 左对齐的时候,paddingRight设置成0,反之paddingLeft设置成0
355
+ if (/left/.test(tdStyle.textAlign)) {
356
+ this.tableEditor.editorDom.inputDom.style.paddingRight = '0px';
357
+ }
358
+ if (/right/.test(tdStyle.textAlign)) {
359
+ this.tableEditor.editorDom.inputDom.style.paddingLeft = '0px';
360
+ }
361
+ if (/center/.test(tdStyle.textAlign)) {
362
+ this.tableEditor.editorDom.inputDom.style.paddingLeft = '0px';
363
+ this.tableEditor.editorDom.inputDom.style.paddingRight = '0px';
364
+ }
365
+ this.tableEditor.editorDom.inputDom.style.paddingBottom = '0px';
366
+ }
367
+
368
+ $getClosestNode(node, targetNodeName) {
369
+ if (node.tagName === targetNodeName) {
370
+ return node;
371
+ }
372
+ if (node.parentNode.tagName === 'BODY') {
373
+ return false;
374
+ }
375
+ return this.$getClosestNode(node.parentNode, targetNodeName);
376
+ }
377
+
378
+ /**
379
+ * 绘制操作符号
380
+ */
381
+ $drawSymbol() {
382
+ const types = ['Last', 'Next'];
383
+ const dirs = ['Row', 'Col'];
384
+ const textDict = {
385
+ Row: '行',
386
+ Col: '列',
387
+ };
388
+ const symbols = dirs.map((_, index) => types.map((type) => dirs.map((dir) => [`${index}`, type, dir]))).flat(2);
389
+ const container = document.createElement('ul');
390
+ container.className = 'cherry-previewer-table-hover-handler-container';
391
+ symbols.forEach(([index, type, dir]) => {
392
+ const li = document.createElement('li');
393
+ li.setAttribute('data-index', index);
394
+ li.setAttribute('data-type', type);
395
+ li.setAttribute('data-dir', dir);
396
+ li.className = 'cherry-previewer-table-hover-handler__symbol';
397
+ li.title = `添加${textDict[dir]}`;
398
+ li.innerHTML = '+';
399
+ li.addEventListener('click', (e) => {
400
+ const { target } = e;
401
+ if (!(target instanceof HTMLElement)) {
402
+ return;
403
+ }
404
+ const { type, dir } = target.dataset;
405
+ this[`$add${type}${dir}`]();
406
+ });
407
+ container.appendChild(li);
408
+ }, true);
409
+ this.tableEditor.editorDom.symbolContainer = container;
410
+ this.container.appendChild(this.tableEditor.editorDom.symbolContainer);
411
+ this.$setSymbolOffset();
412
+ }
413
+
414
+ /**
415
+ * 添加上一行
416
+ */
417
+ $addLastRow() {
418
+ const [{ line }] = this.tableEditor.info.selection;
419
+ const newRow = `${'|'.repeat(this.tableEditor.info.columns)}\n`;
420
+ this.codeMirror.replaceRange(newRow, { line, ch: 0 });
421
+ this.$findTableInEditor();
422
+ this.$setSelection(this.tableEditor.info.tableIndex, 'td');
423
+ }
424
+
425
+ /**
426
+ * 添加下一行
427
+ */
428
+ $addNextRow() {
429
+ const [, { line }] = this.tableEditor.info.selection;
430
+ const newRow = `${'|'.repeat(this.tableEditor.info.columns)}\n`;
431
+ this.codeMirror.replaceRange(newRow, { line: line + 1, ch: 0 });
432
+ this.$findTableInEditor();
433
+ this.$setSelection(this.tableEditor.info.tableIndex, 'td');
434
+ }
435
+
436
+ /**
437
+ * 添加上一列
438
+ */
439
+ $addLastCol() {
440
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
441
+ const selection = this.codeMirror.getSelection();
442
+ const lines = selection.split('\n');
443
+ const newLines = lines.map((line, index) => {
444
+ const cells = line.split('|');
445
+ const replaceItem = 1 === index ? ':-:' : '';
446
+ cells.splice(this.tableEditor.info.tdIndex + 1, 0, replaceItem);
447
+ return cells.join('|');
448
+ });
449
+ const newText = newLines.join('\n');
450
+ this.codeMirror.replaceSelection(newText);
451
+ this.$findTableInEditor();
452
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
453
+ }
454
+
455
+ /**
456
+ * 添加下一列
457
+ */
458
+ $addNextCol() {
459
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
460
+ const selection = this.codeMirror.getSelection();
461
+ const lines = selection.split('\n');
462
+ const newLines = lines.map((line, index) => {
463
+ const cells = line.split('|');
464
+ const replaceItem = 1 === index ? ':-:' : '';
465
+ cells.splice(this.tableEditor.info.tdIndex + 2, 0, replaceItem);
466
+ return cells.join('|');
467
+ });
468
+ const newText = newLines.join('\n');
469
+ this.codeMirror.replaceSelection(newText);
470
+ this.$findTableInEditor();
471
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
472
+ }
473
+
474
+ /**
475
+ * 拖拽
476
+ */
477
+ $drawDrag() {
478
+ const { isTHead } = this.tableEditor.info;
479
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
480
+ if (isTHead) {
481
+ this.$dragCol();
482
+ } else {
483
+ this.$dragLine();
484
+ }
485
+ }
486
+
487
+ /**
488
+ * 拖拽列
489
+ */
490
+ $dragCol() {
491
+ const oldTdIndex = this.tableEditor.info.tdIndex;
492
+ const thNode = this.target.parentElement;
493
+ const lines = this.codeMirror.getSelection().split(/\n/);
494
+ const { tdNode } = this.tableEditor.info;
495
+ const that = this;
496
+ tdNode.setAttribute('draggable', true);
497
+
498
+ // 离开需要还原边框
499
+ thNode.addEventListener('dragleave', function (event) {
500
+ that.setStyle(event.target, 'border', '1px solid #dfe6ee');
501
+ });
502
+ // 改变将要放到的位置边框的颜色
503
+ thNode.addEventListener('dragover', function (event) {
504
+ event.preventDefault();
505
+ const tdIndex = Array.from(event.target.parentElement.childNodes).indexOf(event.target);
506
+ that.$dragSymbol(event.target, oldTdIndex, tdIndex);
507
+ });
508
+ thNode.addEventListener('drop', function (event) {
509
+ event.preventDefault();
510
+ const tdIndex = Array.from(event.target.parentElement.childNodes).indexOf(event.target);
511
+ const newLines = lines.map((line, index) => {
512
+ const cells = line.split('|').filter((item, i) => item !== '');
513
+ return `|${that.$operateLines(oldTdIndex, tdIndex, cells).join('|')}|`;
514
+ });
515
+ const newText = newLines.join('\n');
516
+ that.codeMirror.replaceSelection(newText);
517
+ that.setStyle(event.target, 'border', '1px solid #dfe6ee');
518
+ that.$findTableInEditor();
519
+ that.$setSelection(that.tableEditor.info.tableIndex, 'table');
520
+ });
521
+ }
522
+
523
+ /**
524
+ * 拖拽行
525
+ */
526
+ $dragLine() {
527
+ const { trNode } = this.tableEditor.info;
528
+ trNode.setAttribute('draggable', true);
529
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
530
+ const oldTrIndex = this.tableEditor.info.trIndex + 2;
531
+ const tBody = trNode.parentElement;
532
+ const lines = this.codeMirror.getSelection().split(/\n/);
533
+ const that = this;
534
+
535
+ tBody.addEventListener('dragleave', function (event) {
536
+ that.setStyle(event.target.parentElement, 'border', '1px solid #dfe6ee');
537
+ });
538
+ tBody.addEventListener('dragover', function (event) {
539
+ // 默认元素不可放置,这里取消默认,将放置目标修改为可放置的
540
+ event.preventDefault();
541
+ const trIndex =
542
+ Array.from(event.target.parentElement.parentElement.childNodes).indexOf(event.target.parentElement) + 2;
543
+ that.$dragSymbol(event.target, oldTrIndex, trIndex);
544
+ });
545
+ tBody.addEventListener('drop', function (event) {
546
+ event.preventDefault();
547
+ const trIndex =
548
+ Array.from(event.target.parentElement.parentElement.childNodes).indexOf(event.target.parentElement) + 2;
549
+ const newText = that.$operateLines(oldTrIndex, trIndex, lines).join('\n');
550
+ that.codeMirror.replaceSelection(newText);
551
+
552
+ that.$findTableInEditor();
553
+ that.$setSelection(that.tableEditor.info.tableIndex, 'table');
554
+ that.setStyle(event.target.parentElement, 'border', '1px solid #dfe6ee');
555
+ });
556
+ }
557
+
558
+ $dragSymbol(objTarget, oldIndex, index) {
559
+ const { target } = this;
560
+ if (target !== objTarget && oldIndex !== index) {
561
+ if (target.tagName === 'TH') {
562
+ if (oldIndex < index) {
563
+ this.setStyle(objTarget, 'border', `1px solid #dfe6ee`);
564
+ this.setStyle(objTarget, 'border-right', `2px solid #6897bb`);
565
+ } else if (oldIndex > index) {
566
+ this.setStyle(objTarget, 'border', `1px solid #dfe6ee`);
567
+ this.setStyle(objTarget, 'border-left', `2px solid #6897bb`);
568
+ }
569
+ } else if (target.tagName === 'TD') {
570
+ if (oldIndex < index) {
571
+ this.setStyle(objTarget.parentElement, 'border', `1px solid #dfe6ee`);
572
+ this.setStyle(objTarget.parentElement, 'border-bottom', `2px solid #6897bb`);
573
+ } else if (oldIndex > index) {
574
+ this.setStyle(objTarget.parentElement, 'border', `1px solid #dfe6ee`);
575
+ this.setStyle(objTarget.parentElement, 'border-top', `2px solid #6897bb`);
576
+ }
577
+ }
578
+ }
579
+ }
580
+
581
+ $operateLines(oldIndex, index, lines) {
582
+ if (oldIndex < index) {
583
+ lines.splice(index + 1, 0, lines[oldIndex]);
584
+ lines.splice(oldIndex, 1);
585
+ } else if (oldIndex > index) {
586
+ const line = lines[oldIndex];
587
+ lines.splice(oldIndex, 1);
588
+ lines.splice(index, 0, line);
589
+ }
590
+ return lines;
591
+ }
592
+ }
@@ -0,0 +1,3 @@
1
+ # Emoji处理
2
+
3
+ 数据来源:[gemoji](https://github.com/github/gemoji/tree/master/db)