@cherry-markdown/cherry-markdown-dev 0.8.58-dev

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 (319) hide show
  1. package/package.json +149 -0
  2. package/src/Cherry.config.js +625 -0
  3. package/src/Cherry.js +1104 -0
  4. package/src/CherryStatic.js +70 -0
  5. package/src/Editor.js +748 -0
  6. package/src/Engine.js +381 -0
  7. package/src/Event.js +140 -0
  8. package/src/Factory.js +180 -0
  9. package/src/Logger.js +31 -0
  10. package/src/Previewer.js +1183 -0
  11. package/src/Sanitizer.js +4 -0
  12. package/src/Sanitizer.node.js +7 -0
  13. package/src/UrlCache.js +98 -0
  14. package/src/addons/advance/cherry-table-echarts-plugin.js +170 -0
  15. package/src/addons/cherry-code-block-mermaid-plugin.js +158 -0
  16. package/src/addons/cherry-code-block-plantuml-plugin.js +106 -0
  17. package/src/core/HookCenter.js +297 -0
  18. package/src/core/HooksConfig.js +100 -0
  19. package/src/core/ParagraphBase.js +332 -0
  20. package/src/core/SentenceBase.js +65 -0
  21. package/src/core/SyntaxBase.js +194 -0
  22. package/src/core/hooks/AutoLink.js +232 -0
  23. package/src/core/hooks/BackgroundColor.js +46 -0
  24. package/src/core/hooks/Blockquote.js +70 -0
  25. package/src/core/hooks/Br.js +85 -0
  26. package/src/core/hooks/CodeBlock.js +446 -0
  27. package/src/core/hooks/Color.js +46 -0
  28. package/src/core/hooks/CommentReference.js +96 -0
  29. package/src/core/hooks/Detail.js +108 -0
  30. package/src/core/hooks/Emoji.config.js +1825 -0
  31. package/src/core/hooks/Emoji.js +119 -0
  32. package/src/core/hooks/Emphasis.js +113 -0
  33. package/src/core/hooks/Footnote.js +125 -0
  34. package/src/core/hooks/FrontMatter.js +51 -0
  35. package/src/core/hooks/Header.js +234 -0
  36. package/src/core/hooks/HighLight.js +37 -0
  37. package/src/core/hooks/Hr.js +52 -0
  38. package/src/core/hooks/HtmlBlock.js +184 -0
  39. package/src/core/hooks/Image.js +174 -0
  40. package/src/core/hooks/InlineCode.js +48 -0
  41. package/src/core/hooks/InlineMath.js +107 -0
  42. package/src/core/hooks/Link.js +160 -0
  43. package/src/core/hooks/List.js +264 -0
  44. package/src/core/hooks/MathBlock.js +103 -0
  45. package/src/core/hooks/Panel.js +145 -0
  46. package/src/core/hooks/Paragraph.js +84 -0
  47. package/src/core/hooks/Ruby.js +34 -0
  48. package/src/core/hooks/Size.js +51 -0
  49. package/src/core/hooks/Strikethrough.js +54 -0
  50. package/src/core/hooks/Sub.js +47 -0
  51. package/src/core/hooks/SuggestList.js +333 -0
  52. package/src/core/hooks/Suggester.js +707 -0
  53. package/src/core/hooks/Sup.js +47 -0
  54. package/src/core/hooks/Table.js +275 -0
  55. package/src/core/hooks/Toc.js +292 -0
  56. package/src/core/hooks/Transfer.js +47 -0
  57. package/src/core/hooks/Underline.js +37 -0
  58. package/src/index.core.js +29 -0
  59. package/src/index.engine.core.js +68 -0
  60. package/src/index.engine.js +28 -0
  61. package/src/index.js +32 -0
  62. package/src/libs/mermaidAPI.8.4.8.js +1 -0
  63. package/src/libs/mermaidAPI.8.5.2.js +42 -0
  64. package/src/libs/rawdeflate.js +1663 -0
  65. package/src/locales/en_US.js +139 -0
  66. package/src/locales/index.js +25 -0
  67. package/src/locales/ru_RU.js +139 -0
  68. package/src/locales/zh_CN.js +142 -0
  69. package/src/sass/base.scss +26 -0
  70. package/src/sass/bubble_formula.scss +166 -0
  71. package/src/sass/ch-icon.scss +118 -0
  72. package/src/sass/cherry.scss +1116 -0
  73. package/src/sass/components/bubble.scss +173 -0
  74. package/src/sass/components/shortcut_key_config.scss +108 -0
  75. package/src/sass/formula_utils_bubble.scss +82 -0
  76. package/src/sass/icon_template.scss +24 -0
  77. package/src/sass/icons/uEA03-list.svg +19 -0
  78. package/src/sass/icons/uEA04-check.svg +14 -0
  79. package/src/sass/icons/uEA09-square.svg +10 -0
  80. package/src/sass/icons/uEA0A-bold.svg +20 -0
  81. package/src/sass/icons/uEA0B-code.svg +18 -0
  82. package/src/sass/icons/uEA0C-color.svg +13 -0
  83. package/src/sass/icons/uEA0D-header.svg +8 -0
  84. package/src/sass/icons/uEA0E-image.svg +15 -0
  85. package/src/sass/icons/uEA0F-italic.svg +8 -0
  86. package/src/sass/icons/uEA10-link.svg +16 -0
  87. package/src/sass/icons/uEA11-ol.svg +21 -0
  88. package/src/sass/icons/uEA12-size.svg +11 -0
  89. package/src/sass/icons/uEA13-strike.svg +16 -0
  90. package/src/sass/icons/uEA14-table.svg +12 -0
  91. package/src/sass/icons/uEA15-ul.svg +17 -0
  92. package/src/sass/icons/uEA16-underline.svg +13 -0
  93. package/src/sass/icons/uEA17-word.svg +16 -0
  94. package/src/sass/icons/uEA18-blockquote.svg +11 -0
  95. package/src/sass/icons/uEA19-font.svg +10 -0
  96. package/src/sass/icons/uEA1F-insertClass.svg +39 -0
  97. package/src/sass/icons/uEA20-insertFlow.svg +8 -0
  98. package/src/sass/icons/uEA21-insertFormula.svg +23 -0
  99. package/src/sass/icons/uEA22-insertGantt.svg +13 -0
  100. package/src/sass/icons/uEA23-insertGraph.svg +13 -0
  101. package/src/sass/icons/uEA24-insertPie.svg +19 -0
  102. package/src/sass/icons/uEA25-insertSeq.svg +20 -0
  103. package/src/sass/icons/uEA26-insertState.svg +35 -0
  104. package/src/sass/icons/uEA27-line.svg +11 -0
  105. package/src/sass/icons/uEA28-preview.svg +18 -0
  106. package/src/sass/icons/uEA29-previewClose.svg +24 -0
  107. package/src/sass/icons/uEA2A-toc.svg +24 -0
  108. package/src/sass/icons/uEA2D-sub.svg +15 -0
  109. package/src/sass/icons/uEA2E-sup.svg +15 -0
  110. package/src/sass/icons/uEA2F-h1.svg +16 -0
  111. package/src/sass/icons/uEA30-h2.svg +20 -0
  112. package/src/sass/icons/uEA31-h3.svg +23 -0
  113. package/src/sass/icons/uEA32-h4.svg +16 -0
  114. package/src/sass/icons/uEA33-h5.svg +20 -0
  115. package/src/sass/icons/uEA34-h6.svg +17 -0
  116. package/src/sass/icons/uEA35-video.svg +20 -0
  117. package/src/sass/icons/uEA36-insert.svg +25 -0
  118. package/src/sass/icons/uEA37-little_table.svg +30 -0
  119. package/src/sass/icons/uEA38-pdf.svg +27 -0
  120. package/src/sass/icons/uEA39-checklist.svg +22 -0
  121. package/src/sass/icons/uEA40-close.svg +12 -0
  122. package/src/sass/icons/uEA41-fullscreen.svg +81 -0
  123. package/src/sass/icons/uEA42-minscreen.svg +77 -0
  124. package/src/sass/icons/uEA43-insertChart.svg +23 -0
  125. package/src/sass/icons/uEA44-question.svg +25 -0
  126. package/src/sass/icons/uEA45-settings.svg +32 -0
  127. package/src/sass/icons/uEA46-ok.svg +7 -0
  128. package/src/sass/icons/uEA47-br.svg +22 -0
  129. package/src/sass/icons/uEA48-normal.svg +15 -0
  130. package/src/sass/icons/uEA49-undo.svg +19 -0
  131. package/src/sass/icons/uEA50-redo.svg +21 -0
  132. package/src/sass/icons/uEA51-copy.svg +6 -0
  133. package/src/sass/icons/uEA52-phone.svg +5 -0
  134. package/src/sass/icons/uEA53-cherry-table-delete.svg +17 -0
  135. package/src/sass/icons/uEA54-cherry-table-insert-bottom.svg +16 -0
  136. package/src/sass/icons/uEA55-cherry-table-insert-left.svg +15 -0
  137. package/src/sass/icons/uEA56-cherry-table-insert-right.svg +16 -0
  138. package/src/sass/icons/uEA57-cherry-table-insert-top.svg +16 -0
  139. package/src/sass/icons/uEA58-sort-s.svg +13 -0
  140. package/src/sass/icons/uEA59-pinyin.svg +1 -0
  141. package/src/sass/icons/uEA5A-create.svg +24 -0
  142. package/src/sass/icons/uEA5B-download.svg +34 -0
  143. package/src/sass/icons/uEA5C-edit.svg +3 -0
  144. package/src/sass/icons/uEA5D-export.svg +53 -0
  145. package/src/sass/icons/uEA5E-folder-open.svg +3 -0
  146. package/src/sass/icons/uEA5F-folder.svg +3 -0
  147. package/src/sass/icons/uEA60-help.svg +5 -0
  148. package/src/sass/icons/uEA61-pen-fill.svg +13 -0
  149. package/src/sass/icons/uEA62-pen.svg +3 -0
  150. package/src/sass/icons/uEA64-tips.svg +5 -0
  151. package/src/sass/icons/uEA65-warn.svg +5 -0
  152. package/src/sass/icons/uEA66-mistake.svg +4 -0
  153. package/src/sass/icons/uEA67-success.svg +4 -0
  154. package/src/sass/icons/uEA68-danger.svg +4 -0
  155. package/src/sass/icons/uEA69-info.svg +5 -0
  156. package/src/sass/icons/uEA6A-primary.svg +5 -0
  157. package/src/sass/icons/uEA6B-warning.svg +5 -0
  158. package/src/sass/icons/uEA6C-justify.svg +19 -0
  159. package/src/sass/icons/uEA6D-justifyCenter.svg +19 -0
  160. package/src/sass/icons/uEA6E-justifyLeft.svg +19 -0
  161. package/src/sass/icons/uEA6F-justifyRight.svg +19 -0
  162. package/src/sass/icons/uEA70-chevronsLeft.svg +1 -0
  163. package/src/sass/icons/uEA71-chevronsRight.svg +1 -0
  164. package/src/sass/icons/uEA72-trendingUp.svg +1 -0
  165. package/src/sass/icons/uEA74-codeBlock.svg +1 -0
  166. package/src/sass/icons/uEA75-expand.svg +3 -0
  167. package/src/sass/icons/uEA76-unExpand.svg +3 -0
  168. package/src/sass/icons/uEA77-swap-vert.svg +1 -0
  169. package/src/sass/icons/uEA78-swap.svg +1 -0
  170. package/src/sass/icons/uEA79-keyboard.svg +1 -0
  171. package/src/sass/icons/uEA7A-command.svg +1 -0
  172. package/src/sass/icons/uEA7B-search.svg +1 -0
  173. package/src/sass/index.scss +3 -0
  174. package/src/sass/markdown.scss +668 -0
  175. package/src/sass/markdown_pure.scss +9 -0
  176. package/src/sass/prettyprint/prettyprint.scss +118 -0
  177. package/src/sass/previewer.scss +179 -0
  178. package/src/sass/print.scss +13 -0
  179. package/src/sass/prism/coy.scss +220 -0
  180. package/src/sass/prism/dark.scss +132 -0
  181. package/src/sass/prism/default.scss +143 -0
  182. package/src/sass/prism/funky.scss +133 -0
  183. package/src/sass/prism/okaidia.scss +126 -0
  184. package/src/sass/prism/one-dark.scss +440 -0
  185. package/src/sass/prism/one-light.scss +428 -0
  186. package/src/sass/prism/solarized-light.scss +153 -0
  187. package/src/sass/prism/tomorrow-night.scss +125 -0
  188. package/src/sass/prism/twilight.scss +202 -0
  189. package/src/sass/prism/vs-dark.scss +275 -0
  190. package/src/sass/prism/vs-light.scss +168 -0
  191. package/src/sass/themes/blue.scss +411 -0
  192. package/src/sass/themes/dark.scss +517 -0
  193. package/src/sass/themes/default.scss +255 -0
  194. package/src/sass/themes/green.scss +395 -0
  195. package/src/sass/themes/light.scss +368 -0
  196. package/src/sass/themes/red.scss +397 -0
  197. package/src/sass/themes/violet.scss +410 -0
  198. package/src/sass/variable.scss +84 -0
  199. package/src/toolbars/Bubble.js +234 -0
  200. package/src/toolbars/BubbleFormula.js +298 -0
  201. package/src/toolbars/BubbleTable.js +147 -0
  202. package/src/toolbars/FloatMenu.js +131 -0
  203. package/src/toolbars/HiddenToolbar.js +36 -0
  204. package/src/toolbars/HookCenter.js +234 -0
  205. package/src/toolbars/MenuBase.js +569 -0
  206. package/src/toolbars/PreviewerBubble.js +608 -0
  207. package/src/toolbars/ShortcutKeyConfigPanel.js +345 -0
  208. package/src/toolbars/Sidebar.js +36 -0
  209. package/src/toolbars/Toc.js +242 -0
  210. package/src/toolbars/Toolbar.js +449 -0
  211. package/src/toolbars/ToolbarRight.js +37 -0
  212. package/src/toolbars/hooks/Audio.js +79 -0
  213. package/src/toolbars/hooks/BarTable.js +41 -0
  214. package/src/toolbars/hooks/Bold.js +73 -0
  215. package/src/toolbars/hooks/Br.js +34 -0
  216. package/src/toolbars/hooks/ChangeLocale.js +62 -0
  217. package/src/toolbars/hooks/ChatGpt.js +182 -0
  218. package/src/toolbars/hooks/CheckList.js +41 -0
  219. package/src/toolbars/hooks/Code.js +49 -0
  220. package/src/toolbars/hooks/CodeTheme.js +66 -0
  221. package/src/toolbars/hooks/Color.js +298 -0
  222. package/src/toolbars/hooks/Copy.js +141 -0
  223. package/src/toolbars/hooks/Detail.js +69 -0
  224. package/src/toolbars/hooks/DrawIo.js +57 -0
  225. package/src/toolbars/hooks/Export.js +49 -0
  226. package/src/toolbars/hooks/File.js +79 -0
  227. package/src/toolbars/hooks/Formula.js +69 -0
  228. package/src/toolbars/hooks/FullScreen.js +50 -0
  229. package/src/toolbars/hooks/Graph.js +263 -0
  230. package/src/toolbars/hooks/H1.js +71 -0
  231. package/src/toolbars/hooks/H2.js +71 -0
  232. package/src/toolbars/hooks/H3.js +71 -0
  233. package/src/toolbars/hooks/Header.js +118 -0
  234. package/src/toolbars/hooks/Hr.js +35 -0
  235. package/src/toolbars/hooks/Image.js +91 -0
  236. package/src/toolbars/hooks/InlineCode.js +53 -0
  237. package/src/toolbars/hooks/Insert.js +193 -0
  238. package/src/toolbars/hooks/Italic.js +72 -0
  239. package/src/toolbars/hooks/Justify.js +49 -0
  240. package/src/toolbars/hooks/LineTable.js +41 -0
  241. package/src/toolbars/hooks/Link.js +49 -0
  242. package/src/toolbars/hooks/List.js +55 -0
  243. package/src/toolbars/hooks/MobilePreview.js +44 -0
  244. package/src/toolbars/hooks/Ol.js +41 -0
  245. package/src/toolbars/hooks/Panel.js +140 -0
  246. package/src/toolbars/hooks/Pdf.js +78 -0
  247. package/src/toolbars/hooks/Publish.js +123 -0
  248. package/src/toolbars/hooks/QuickTable.js +43 -0
  249. package/src/toolbars/hooks/Quote.js +45 -0
  250. package/src/toolbars/hooks/Redo.js +33 -0
  251. package/src/toolbars/hooks/Ruby.js +59 -0
  252. package/src/toolbars/hooks/Search.js +53 -0
  253. package/src/toolbars/hooks/Settings.js +220 -0
  254. package/src/toolbars/hooks/ShortcutKey.js +62 -0
  255. package/src/toolbars/hooks/Size.js +118 -0
  256. package/src/toolbars/hooks/Split.js +37 -0
  257. package/src/toolbars/hooks/Strikethrough.js +71 -0
  258. package/src/toolbars/hooks/Sub.js +58 -0
  259. package/src/toolbars/hooks/Sup.js +58 -0
  260. package/src/toolbars/hooks/SwitchModel.js +56 -0
  261. package/src/toolbars/hooks/Table.js +56 -0
  262. package/src/toolbars/hooks/Theme.js +62 -0
  263. package/src/toolbars/hooks/Toc.js +35 -0
  264. package/src/toolbars/hooks/TogglePreview.js +91 -0
  265. package/src/toolbars/hooks/Ul.js +41 -0
  266. package/src/toolbars/hooks/Underline.js +68 -0
  267. package/src/toolbars/hooks/Undo.js +30 -0
  268. package/src/toolbars/hooks/Video.js +79 -0
  269. package/src/toolbars/hooks/Word.js +78 -0
  270. package/src/toolbars/hooks/WordCount.js +106 -0
  271. package/src/utils/autoindent.js +58 -0
  272. package/src/utils/cm-search-replace.js +794 -0
  273. package/src/utils/code-preview-language-setting.js +180 -0
  274. package/src/utils/codeBlockContentHandler.js +400 -0
  275. package/src/utils/config.js +174 -0
  276. package/src/utils/copy.js +55 -0
  277. package/src/utils/dialog.js +214 -0
  278. package/src/utils/dom.js +163 -0
  279. package/src/utils/downloadUtil.js +23 -0
  280. package/src/utils/env.js +22 -0
  281. package/src/utils/error.js +61 -0
  282. package/src/utils/event.js +38 -0
  283. package/src/utils/export.js +166 -0
  284. package/src/utils/file.js +164 -0
  285. package/src/utils/formulaUtilsHandler.js +232 -0
  286. package/src/utils/htmlparser.js +976 -0
  287. package/src/utils/image.js +99 -0
  288. package/src/utils/imgSizeHandler.js +279 -0
  289. package/src/utils/lazyLoadImg.js +327 -0
  290. package/src/utils/lineFeed.js +49 -0
  291. package/src/utils/listContentHandler.js +227 -0
  292. package/src/utils/lookbehind-replace.js +81 -0
  293. package/src/utils/mathjax.js +89 -0
  294. package/src/utils/myersDiff.js +211 -0
  295. package/src/utils/pasteHelper.js +253 -0
  296. package/src/utils/platformTransform.js +71 -0
  297. package/src/utils/recount-pos.js +59 -0
  298. package/src/utils/regexp.js +295 -0
  299. package/src/utils/sanitize.js +477 -0
  300. package/src/utils/selection.js +50 -0
  301. package/src/utils/shortcutKey.js +291 -0
  302. package/src/utils/svgUtils.js +96 -0
  303. package/src/utils/tableContentHandler.js +876 -0
  304. package/test/core/CommonMark.spec.ts +62 -0
  305. package/test/core/hooks/AutoLink.spec.ts +28 -0
  306. package/test/core/hooks/List.spec.ts +79 -0
  307. package/test/core/hooks/__snapshots__/List.spec.ts.snap +11 -0
  308. package/test/example.md +778 -0
  309. package/test/node.js +10 -0
  310. package/test/suites/commonmark.spec.json +5218 -0
  311. package/test/tsconfig.test.json +6 -0
  312. package/test/utils/regexp.spec.ts +28 -0
  313. package/types/cherry.d.ts +675 -0
  314. package/types/codemirror.d.ts +22 -0
  315. package/types/editor.d.ts +72 -0
  316. package/types/global.d.ts +16 -0
  317. package/types/menus.d.ts +24 -0
  318. package/types/previewer.d.ts +53 -0
  319. package/types/syntax.d.ts +52 -0
@@ -0,0 +1,876 @@
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
+ */
21
+ export default class TableHandler {
22
+ /**
23
+ * 用来存放所有的数据
24
+ */
25
+ tableEditor = {
26
+ info: {}, // 当前点击的预览区域table的相关信息
27
+ tableCodes: [], // 编辑器内所有的表格语法
28
+ editorDom: {}, // 编辑器容器
29
+ };
30
+
31
+ constructor(trigger, target, container, previewerDom, codeMirror, tableElement, cherry) {
32
+ // 触发方式 click / hover
33
+ this.trigger = trigger;
34
+ this.target = target;
35
+ this.previewerDom = previewerDom;
36
+ this.container = container;
37
+ this.codeMirror = codeMirror;
38
+ this.$initReg();
39
+ this.$findTableInEditor();
40
+ this.tableElement = tableElement;
41
+ this.$cherry = cherry;
42
+ }
43
+
44
+ emit(type, event = {}, callback = () => {}) {
45
+ switch (type) {
46
+ case 'keyup':
47
+ return this.trigger === 'click' && this.$onInputChange(event);
48
+ case 'remove':
49
+ return this.$remove();
50
+ case 'scroll':
51
+ return this.$refreshPosition();
52
+ case 'previewUpdate':
53
+ return this.$refreshPosition();
54
+ case 'mousedown':
55
+ return;
56
+ case 'mouseup':
57
+ return this.trigger === 'click' && this.$tryRemoveMe(event, callback);
58
+ }
59
+ }
60
+
61
+ $tryRemoveMe(event, callback) {
62
+ if (!/textarea/i.test(event.target.tagName)) {
63
+ this.$remove();
64
+ callback();
65
+ }
66
+ }
67
+
68
+ /**
69
+ * 获取目标dom的位置信息和尺寸信息
70
+ */
71
+ $getPosition(node = this.tableEditor.info.tdNode) {
72
+ const position = node.getBoundingClientRect();
73
+ const editorPosition = this.previewerDom.parentNode.getBoundingClientRect();
74
+ return {
75
+ top: position.top - editorPosition.top,
76
+ height: position.height,
77
+ width: position.width,
78
+ left: position.left - editorPosition.left,
79
+ maxHeight: editorPosition.height,
80
+ };
81
+ }
82
+
83
+ setStyle(element, property, value) {
84
+ const info = element.getBoundingClientRect();
85
+ if (info[property] !== value) {
86
+ element.style[property] = value;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * TODO: 这里是分别对文本框、操作符号和选项设置偏移,应该作为一个整体来设置
92
+ */
93
+ $setInputOffset() {
94
+ const tdInfo = this.$getPosition();
95
+ const { inputDiv } = this.tableEditor.editorDom;
96
+ // 设置文本框的偏移及大小
97
+ this.setStyle(inputDiv, 'width', `${tdInfo.width}px`);
98
+ this.setStyle(inputDiv, 'height', `${tdInfo.height}px`);
99
+ this.setStyle(inputDiv, 'top', `${tdInfo.top}px`);
100
+ this.setStyle(inputDiv, 'left', `${tdInfo.left}px`);
101
+
102
+ // 根据是否超出边界来显示或者隐藏元素
103
+ const isWithinBounds = tdInfo.top >= 0 && tdInfo.top + tdInfo.height <= tdInfo.maxHeight;
104
+ this.setStyle(inputDiv, 'display', isWithinBounds ? '' : 'none');
105
+ }
106
+
107
+ /**
108
+ * 刷新操作符位置
109
+ */
110
+ $setSymbolOffset() {
111
+ const container = this.tableEditor.editorDom.symbolContainer;
112
+ const { tableNode, trNode, isTHead } = this.tableEditor.info;
113
+ const tableInfo = this.$getPosition(tableNode);
114
+ const trInfo = this.$getPosition(trNode);
115
+ const tdInfo = this.$getPosition();
116
+ const previewerRect = this.previewerDom.getBoundingClientRect();
117
+ // 设置容器宽高
118
+ this.setStyle(this.container, 'width', `${tableInfo.width}px`);
119
+ this.setStyle(this.container, 'height', `${tableInfo.height}px`);
120
+ this.setStyle(this.container, 'top', `${tableInfo.top}px`);
121
+ this.setStyle(this.container, 'left', `${tableInfo.left}px`);
122
+
123
+ // 判断是否在预览区内
124
+ const isWithinBounds = (symbol) => {
125
+ const symbolRect = symbol.getBoundingClientRect();
126
+ const boundMap = {
127
+ top: [previewerRect.top, previewerRect.top + previewerRect.height - symbolRect.height],
128
+ left: [previewerRect.left, previewerRect.left + previewerRect.width - symbolRect.width],
129
+ };
130
+ return Object.entries(boundMap).every(([key, [min, max]]) => symbolRect[key] >= min && symbolRect[key] <= max);
131
+ };
132
+
133
+ // 设置操作符位置与控制显隐
134
+ container.childNodes.forEach((node) => {
135
+ const { index, type, dir } = node.dataset;
136
+ const propDict = {
137
+ Row: ['left', 'right'],
138
+ Col: ['top', 'bottom'],
139
+ };
140
+ const offset = {
141
+ outer: 20,
142
+ radius: 7,
143
+ };
144
+ this.setStyle(node, propDict[dir][index], `-${offset.outer}px`);
145
+ this.setStyle(node, 'display', '');
146
+ const refreshMap = {
147
+ LastRow: () => this.setStyle(node, 'top', `${trInfo.top - tableInfo.top - offset.radius}px`),
148
+ NextRow: () => this.setStyle(node, 'top', `${trInfo.top - tableInfo.top + trInfo.height - offset.radius}px`),
149
+ LastCol: () => this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left - offset.radius}px`),
150
+ NextCol: () => this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left + tdInfo.width - offset.radius}px`),
151
+ };
152
+ const oper = `${type}${dir}`;
153
+ refreshMap[oper]();
154
+ this.setStyle(node, 'display', isWithinBounds(node) ? '' : 'none');
155
+ if (isTHead && oper === 'LastRow') {
156
+ this.setStyle(node, 'display', 'none');
157
+ }
158
+ });
159
+ }
160
+
161
+ /**
162
+ * 刷新定位
163
+ */
164
+ $refreshPosition() {
165
+ if (this.trigger === 'click') {
166
+ this.$setInputOffset();
167
+ return;
168
+ }
169
+ this.$setSymbolOffset();
170
+ this.$setDeleteButtonPosition();
171
+ }
172
+
173
+ $remove() {
174
+ this.tableEditor = { info: {}, tableCodes: [], editorDom: {} };
175
+ }
176
+
177
+ /**
178
+ * 收集编辑器中的表格语法,并记录表格语法的开始的offset
179
+ */
180
+ $collectTableCode() {
181
+ const tableCodes = [];
182
+ this.codeMirror
183
+ .getValue()
184
+ .replace(this.codeBlockReg, (whole, ...args) => {
185
+ // 先把代码块里的表格语法关键字干掉
186
+ return whole.replace(/\|/g, '.');
187
+ })
188
+ .replace(this.tableReg, function (whole, ...args) {
189
+ const match = whole.replace(/^\n*/, '');
190
+ const offsetBegin = args[args.length - 2] + whole.match(/^\n*/)[0].length;
191
+ tableCodes.push({
192
+ code: match,
193
+ offset: offsetBegin,
194
+ });
195
+ });
196
+ this.tableEditor.tableCodes = tableCodes;
197
+ }
198
+
199
+ /**
200
+ * 获取预览区域被点击的table对象,并记录table的顺位
201
+ */
202
+ $collectTableDom() {
203
+ const list = Array.from(this.previewerDom.querySelectorAll('table.cherry-table'));
204
+ const tableNode = this.$getClosestNode(this.target, 'TABLE');
205
+ if (tableNode === false) {
206
+ return false;
207
+ }
208
+ const columns = Array.from(this.target.parentElement.childNodes).filter((child) => {
209
+ // 计算列数
210
+ return child.tagName.toLowerCase() === 'td';
211
+ }).length;
212
+
213
+ this.tableEditor.info = {
214
+ tableNode,
215
+ tdNode: this.target,
216
+ trNode: this.target.parentElement,
217
+ tdIndex: Array.from(this.target.parentElement.childNodes).indexOf(this.target),
218
+ trIndex: Array.from(this.target.parentElement.parentElement.childNodes).indexOf(this.target.parentElement),
219
+ isTHead: this.target.parentElement.parentElement.tagName !== 'TBODY',
220
+ totalTables: list.length,
221
+ tableIndex: list.indexOf(tableNode),
222
+ tableText: tableNode.textContent.replace(/[\s]/g, ''),
223
+ columns,
224
+ };
225
+ }
226
+
227
+ /**
228
+ * 选中对应单元格、所在行、所在列的内容
229
+ * @param {Number} index
230
+ * @param {String} type 'td': 当前单元格, 'table': 当前表格
231
+ * @param {Boolean} select 是否选中编辑器中的代码
232
+ */
233
+ $setSelection(index, type = 'table', select = true) {
234
+ const tableCode = this.tableEditor.tableCodes[index];
235
+ const whole = this.codeMirror.getValue();
236
+ const selectTdInfo = this.tableEditor.info;
237
+ const beginLine = whole.slice(0, tableCode.offset).match(/\n/g)?.length ?? 0;
238
+ const { preLine, preCh, plusCh, currentTd } = this.$getTdOffset(
239
+ tableCode.code,
240
+ selectTdInfo.isTHead,
241
+ selectTdInfo.trIndex,
242
+ selectTdInfo.tdIndex,
243
+ );
244
+ if (type === 'table') {
245
+ const endLine = beginLine + tableCode.code.match(/\n/g).length;
246
+ const endCh = tableCode.code.match(/[^\n]+\n*$/)[0].length;
247
+ this.tableEditor.info.selection = [
248
+ { line: beginLine, ch: 0 },
249
+ { line: endLine, ch: endCh },
250
+ ];
251
+ } else {
252
+ this.tableEditor.info.selection = [
253
+ { line: beginLine + preLine, ch: preCh },
254
+ { line: beginLine + preLine, ch: preCh + plusCh },
255
+ ];
256
+ }
257
+ select && this.codeMirror.setSelection(...this.tableEditor.info.selection);
258
+ this.tableEditor.info.code = currentTd;
259
+ }
260
+
261
+ /**
262
+ * 获取对应单元格的偏移量
263
+ * @param {String} tableCode
264
+ * @param {Boolean} isTHead
265
+ * @param {Number} trIndex
266
+ * @param {Number} tdIndex
267
+ */
268
+ $getTdOffset(tableCode, isTHead, trIndex, tdIndex) {
269
+ const codes = tableCode.split(/\n/);
270
+ const targetTr = isTHead ? 0 : trIndex + 2;
271
+ const tds = codes[targetTr].split(/\|/);
272
+ const needPlus1 = /^\s*$/.test(tds[0]);
273
+ const targetTd = needPlus1 ? tdIndex + 1 : tdIndex;
274
+ const current = tds[targetTd];
275
+ const pre = [];
276
+ for (let i = 0; i < targetTd; i++) {
277
+ pre.push(tds[i]);
278
+ }
279
+ return {
280
+ preLine: targetTr,
281
+ preCh: needPlus1 ? pre.join('|').length + 1 : pre.join('|').length,
282
+ plusCh: current.length,
283
+ currentTd: current,
284
+ };
285
+ }
286
+
287
+ /**
288
+ * 在编辑器里找到对应的表格源码,并让编辑器选中
289
+ */
290
+ $findTableInEditor() {
291
+ this.$collectTableDom();
292
+ this.$collectTableCode();
293
+ // 暂时不考虑代码块中包含表格、人为输入表格html语法、tapd特色表格语法的情况
294
+ // 也就是说,出现上述情况时,表格的所见即所得编辑功能失效
295
+ if (this.tableEditor.info.totalTables !== this.tableEditor.tableCodes.length) {
296
+ return false;
297
+ }
298
+ this.$setSelection(this.tableEditor.info.tableIndex, 'td', this.trigger === 'click');
299
+ }
300
+
301
+ $initReg() {
302
+ this.tableReg = this.tableReg ? this.tableReg : getTableRule(true);
303
+ this.codeBlockReg = this.codeBlockReg ? this.codeBlockReg : getCodeBlockRule().reg;
304
+ }
305
+
306
+ showBubble() {
307
+ if (this.trigger === 'click') {
308
+ this.$drawEditor();
309
+ return;
310
+ }
311
+ this.$drawSymbol();
312
+ this.$drawSortSymbol();
313
+ this.$drawDelete();
314
+ }
315
+
316
+ /**
317
+ * 判断是否处于编辑状态
318
+ * @returns {boolean}
319
+ */
320
+ $isEditing() {
321
+ return this.tableEditor.editing;
322
+ }
323
+
324
+ /**
325
+ * 把表格上的input单行文本框和操作符号画出来
326
+ */
327
+ $drawEditor() {
328
+ const dom = document.createElement('div');
329
+ dom.className = 'cherry-previewer-table-content-handler__input';
330
+ const input = document.createElement('textarea');
331
+ dom.appendChild(input);
332
+ this.tableEditor.editorDom.inputDiv = dom;
333
+ this.tableEditor.editorDom.inputDom = input;
334
+ this.$updateEditorPosition();
335
+ this.container.appendChild(this.tableEditor.editorDom.inputDiv);
336
+ this.tableEditor.editorDom.inputDom.value = this.tableEditor.info.code.replace(/<br>/g, '\n');
337
+ this.tableEditor.editorDom.inputDom.focus();
338
+ }
339
+
340
+ $onInputChange(e) {
341
+ if (e.target.tagName !== 'TEXTAREA') {
342
+ return;
343
+ }
344
+ this.codeMirror.replaceSelection(e.target.value.replace(/\n/g, '<br>'), 'around');
345
+ }
346
+
347
+ /**
348
+ * 更新编辑器的位置(尺寸和位置)
349
+ */
350
+ $updateEditorPosition() {
351
+ this.$setInputOffset();
352
+ const tdStyle = getComputedStyle(this.tableEditor.info.tdNode);
353
+ this.tableEditor.editorDom.inputDom.style.textAlign = tdStyle.textAlign || 'left';
354
+ this.tableEditor.editorDom.inputDom.style.fontSize = tdStyle.fontSize || '16px';
355
+ this.tableEditor.editorDom.inputDom.style.fontFamily = tdStyle.fontFamily;
356
+ this.tableEditor.editorDom.inputDom.style.lineHeight = tdStyle.lineHeight;
357
+ this.tableEditor.editorDom.inputDom.style.padding = tdStyle.padding;
358
+ // 左对齐的时候,paddingRight设置成0,反之paddingLeft设置成0
359
+ if (/left/.test(tdStyle.textAlign)) {
360
+ this.tableEditor.editorDom.inputDom.style.paddingRight = '0px';
361
+ }
362
+ if (/right/.test(tdStyle.textAlign)) {
363
+ this.tableEditor.editorDom.inputDom.style.paddingLeft = '0px';
364
+ }
365
+ if (/center/.test(tdStyle.textAlign)) {
366
+ this.tableEditor.editorDom.inputDom.style.paddingLeft = '0px';
367
+ this.tableEditor.editorDom.inputDom.style.paddingRight = '0px';
368
+ }
369
+ this.tableEditor.editorDom.inputDom.style.paddingBottom = '0px';
370
+ }
371
+
372
+ $getClosestNode(node, targetNodeName) {
373
+ if (!node || !node.tagName) {
374
+ return false;
375
+ }
376
+ if (node.tagName === targetNodeName) {
377
+ return node;
378
+ }
379
+ if (node.parentNode.tagName === 'BODY') {
380
+ return false;
381
+ }
382
+ return this.$getClosestNode(node.parentNode, targetNodeName);
383
+ }
384
+
385
+ /**
386
+ * 绘制操作符号
387
+ */
388
+ $drawSymbol() {
389
+ const types = ['Last', 'Next'];
390
+ const dirs = ['Row', 'Col'];
391
+ const textDict = {
392
+ Row: 'Row',
393
+ Col: 'Col',
394
+ };
395
+ const symbols = dirs.map((_, index) => types.map((type) => dirs.map((dir) => [`${index}`, type, dir]))).flat(2);
396
+ const container = document.createElement('ul');
397
+ container.className = 'cherry-previewer-table-hover-handler-container';
398
+ symbols.forEach(([index, type, dir]) => {
399
+ const li = document.createElement('li');
400
+ li.setAttribute('data-index', index);
401
+ li.setAttribute('data-type', type);
402
+ li.setAttribute('data-dir', dir);
403
+ li.className = 'cherry-previewer-table-hover-handler__symbol';
404
+ li.title = this.$cherry.locale[`add${textDict[dir]}`];
405
+ li.innerHTML = '+';
406
+ li.addEventListener('click', (e) => {
407
+ const { target } = e;
408
+ if (!(target instanceof HTMLElement)) {
409
+ return;
410
+ }
411
+ const { type, dir } = target.dataset;
412
+ this[`$add${type}${dir}`]();
413
+ });
414
+ container.appendChild(li);
415
+ }, true);
416
+ this.tableEditor.editorDom.symbolContainer = container;
417
+ this.container.appendChild(this.tableEditor.editorDom.symbolContainer);
418
+ this.$setSymbolOffset();
419
+ }
420
+ $drawSortSymbol() {
421
+ // const types = ['RowLeft', 'RowRight', 'ColUp', 'ColDown']; // 不要底部的拖拽按钮了,貌似没啥用
422
+ const types = ['RowLeft', 'RowRight', 'ColUp'];
423
+
424
+ const container = document.createElement('ul');
425
+ container.className = 'cherry-previewer-table-hover-handler-sort-container';
426
+ types.forEach((type) => {
427
+ const sortSymbol = document.createElement('li');
428
+ sortSymbol.setAttribute('data-type', type);
429
+ sortSymbol.className = 'cherry-previewer-table-hover-handler__sort ch-icon';
430
+ sortSymbol.draggable = true;
431
+ if (type.startsWith('Row')) {
432
+ sortSymbol.title = this.$cherry.locale.moveRow;
433
+ sortSymbol.classList.add('ch-icon-swap-vert');
434
+ sortSymbol.addEventListener('mouseover', () => {
435
+ const { tdNode } = this.tableEditor.info;
436
+ tdNode.draggable = true;
437
+
438
+ tdNode.parentNode.style.backgroundColor = 'rgb(206,226,248)';
439
+ });
440
+ sortSymbol.addEventListener('mouseleave', () => {
441
+ const { tdNode } = this.tableEditor.info;
442
+ tdNode.draggable = false;
443
+ tdNode.parentNode.style.backgroundColor = '';
444
+ });
445
+ sortSymbol.addEventListener('mousedown', (e) => {
446
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
447
+ this.$dragLine();
448
+ });
449
+ } else {
450
+ sortSymbol.title = this.$cherry.locale.moveCol;
451
+ sortSymbol.classList.add('ch-icon-swap');
452
+ const highLightTrDom = [];
453
+ sortSymbol.addEventListener('mouseover', () => {
454
+ const { tdNode } = this.tableEditor.info;
455
+ tdNode.draggable = true;
456
+
457
+ const index = Array.from(tdNode.parentNode.children).indexOf(tdNode);
458
+
459
+ Array.from(tdNode.parentNode.parentNode.parentNode.children)
460
+ .map((item) => item.children)
461
+ .forEach((item) => {
462
+ Array.from(item).forEach((tr) => {
463
+ highLightTrDom.push(tr);
464
+ });
465
+ });
466
+ highLightTrDom.forEach((tr) => (tr.children[index].style.backgroundColor = 'rgb(206,226,248)'));
467
+ });
468
+ sortSymbol.addEventListener('mouseleave', () => {
469
+ const { tdNode } = this.tableEditor.info;
470
+ tdNode.draggable = false;
471
+ const index = Array.from(tdNode.parentNode.children).indexOf(tdNode);
472
+ highLightTrDom.forEach((tr) => (tr.children[index].style.backgroundColor = ''));
473
+ });
474
+ sortSymbol.addEventListener('mousedown', (e) => {
475
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
476
+ this.$dragCol();
477
+ });
478
+ }
479
+ container.appendChild(sortSymbol);
480
+ });
481
+ this.tableEditor.editorDom.sortContainer = container;
482
+ this.container.appendChild(this.tableEditor.editorDom.sortContainer);
483
+ this.$setSortSymbolsPosition();
484
+ }
485
+ $setSortSymbolsPosition() {
486
+ const container = this.tableEditor.editorDom.sortContainer;
487
+ const { tableNode, tdNode, isTHead } = this.tableEditor.info;
488
+ const tableInfo = this.$getPosition(tableNode);
489
+ const tdInfo = this.$getPosition(tdNode);
490
+
491
+ this.setStyle(this.container, 'width', `${tableInfo.width}px`);
492
+ this.setStyle(this.container, 'height', `${tableInfo.height}px`);
493
+ this.setStyle(this.container, 'top', `${tableInfo.top}px`);
494
+ this.setStyle(this.container, 'left', `${tableInfo.left}px`);
495
+
496
+ container.childNodes.forEach((node) => {
497
+ const { type } = node.dataset;
498
+
499
+ switch (type) {
500
+ case 'RowLeft':
501
+ this.setStyle(node, 'top', `${tdInfo.top - tableInfo.top + tdInfo.height / 2 - node.offsetHeight / 2}px`);
502
+ this.setStyle(node, 'left', `${-node.offsetWidth / 2}px`);
503
+ break;
504
+ case 'RowRight':
505
+ this.setStyle(node, 'top', `${tdInfo.top - tableInfo.top + tdInfo.height / 2 - node.offsetHeight / 2}px`);
506
+ this.setStyle(node, 'left', `${tableInfo.width - node.offsetWidth / 2}px`);
507
+ break;
508
+ case 'ColUp':
509
+ this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left + tdInfo.width / 2 - node.offsetWidth / 2}px`);
510
+ this.setStyle(node, 'top', `${-node.offsetHeight / 2}px`);
511
+ break;
512
+ case 'ColDown':
513
+ this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left + tdInfo.width / 2 - node.offsetWidth / 2}px`);
514
+ this.setStyle(node, 'top', `${tableInfo.height - node.offsetHeight / 2}px`);
515
+
516
+ break;
517
+ }
518
+ if (isTHead && type.startsWith('Row')) {
519
+ this.setStyle(node, 'display', 'none');
520
+ }
521
+ });
522
+ }
523
+ /**
524
+ * 添加上一行
525
+ */
526
+ $addLastRow() {
527
+ const [{ line }] = this.tableEditor.info.selection;
528
+ const newRow = `${'|'.repeat(this.tableEditor.info.columns)}\n`;
529
+ this.codeMirror.replaceRange(newRow, { line, ch: 0 });
530
+ this.$findTableInEditor();
531
+ this.$setSelection(this.tableEditor.info.tableIndex, 'td');
532
+ }
533
+
534
+ /**
535
+ * 添加下一行
536
+ */
537
+ $addNextRow() {
538
+ const [, { line }] = this.tableEditor.info.selection;
539
+ const newRow = `${'|'.repeat(this.tableEditor.info.columns)}\n`;
540
+ this.codeMirror.replaceRange(newRow, { line: line + 1, ch: 0 });
541
+ this.$findTableInEditor();
542
+ this.$setSelection(this.tableEditor.info.tableIndex, 'td');
543
+ }
544
+
545
+ /**
546
+ * 添加上一列
547
+ */
548
+ $addLastCol() {
549
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
550
+ const selection = this.codeMirror.getSelection();
551
+ const lines = selection.split('\n');
552
+ const newLines = lines.map((line, index) => {
553
+ const cells = line.split('|');
554
+ const replaceItem = 1 === index ? ':-:' : '';
555
+ cells.splice(this.tableEditor.info.tdIndex + 1, 0, replaceItem);
556
+ return cells.join('|');
557
+ });
558
+ const newText = newLines.join('\n');
559
+ this.codeMirror.replaceSelection(newText);
560
+ this.$findTableInEditor();
561
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
562
+ }
563
+
564
+ /**
565
+ * 添加下一列
566
+ */
567
+ $addNextCol() {
568
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
569
+ const selection = this.codeMirror.getSelection();
570
+ const lines = selection.split('\n');
571
+ const newLines = lines.map((line, index) => {
572
+ const cells = line.split('|');
573
+ const replaceItem = 1 === index ? ':-:' : '';
574
+ cells.splice(this.tableEditor.info.tdIndex + 2, 0, replaceItem);
575
+ return cells.join('|');
576
+ });
577
+ const newText = newLines.join('\n');
578
+ this.codeMirror.replaceSelection(newText);
579
+ this.$findTableInEditor();
580
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
581
+ }
582
+
583
+ /**
584
+ * 高亮当前列
585
+ */
586
+ $highlightColumn() {
587
+ const { tableNode, tdIndex } = this.tableEditor.info;
588
+ const { rows } = tableNode;
589
+ rows[0].cells[tdIndex].style.borderTop = '1px solid red';
590
+ rows[rows.length - 1].cells[tdIndex].style.borderBottom = '1px solid red';
591
+ for (let i = 0; i < rows.length; i++) {
592
+ const { cells } = rows[i];
593
+ if (cells[tdIndex]) {
594
+ if (tdIndex === 0) cells[tdIndex].style.borderLeft = '1px solid red';
595
+ else cells[tdIndex - 1].style.borderRight = '1px solid red';
596
+ cells[tdIndex].style.borderRight = '1px solid red';
597
+ }
598
+ }
599
+ }
600
+
601
+ /**
602
+ * 取消高亮当前列
603
+ */
604
+ $cancelHighlightColumn() {
605
+ const { tableNode, tdIndex } = this.tableEditor.info;
606
+ if (tableNode) {
607
+ const { rows } = tableNode;
608
+ for (let i = 0; i < rows.length; i++) {
609
+ const { cells } = rows[i];
610
+ if (cells[tdIndex]) {
611
+ if (tdIndex !== 0) cells[tdIndex - 1].style.border = '';
612
+ cells[tdIndex].style.border = ''; // 恢复原始边框
613
+ }
614
+ }
615
+ }
616
+ }
617
+
618
+ /**
619
+ * 高亮当前行
620
+ */
621
+ $highlightRow() {
622
+ this.$doHighlightRow('1px solid red');
623
+ }
624
+
625
+ /**
626
+ * 取消高亮当前行
627
+ */
628
+ $cancelHighlightRow() {
629
+ this.$doHighlightRow('');
630
+ }
631
+
632
+ $doHighlightRow(style = '') {
633
+ const { trNode, tableNode } = this.tableEditor.info;
634
+ const tds = trNode.cells;
635
+ const preTds = trNode.previousElementSibling?.cells || tableNode.tHead.firstChild.cells;
636
+ for (let i = 0; i < tds.length; i++) {
637
+ if (preTds[i]) preTds[i].style.borderBottom = style;
638
+ tds[i].style.borderBottom = style;
639
+ }
640
+ tds[0].style.borderLeft = style;
641
+ tds[tds.length - 1].style.borderRight = style;
642
+ }
643
+
644
+ /**
645
+ * 添加删除按钮
646
+ */
647
+ $drawDelete() {
648
+ const types = ['top', 'bottom', 'right'];
649
+ const buttons = types.map((type) => [type]);
650
+ const container = document.createElement('div');
651
+ container.className = 'cherry-previewer-table-hover-handler-delete-container';
652
+ buttons.forEach(([type]) => {
653
+ const button = document.createElement('button');
654
+ button.setAttribute('data-type', type);
655
+ button.className = 'cherry-previewer-table-hover-handler__delete ch-icon ch-icon-cherry-table-delete';
656
+ if (/(right|left)/.test(type)) {
657
+ button.title = this.$cherry.locale.deleteRow;
658
+ button.addEventListener('click', () => {
659
+ this.$deleteCurrentRow();
660
+ });
661
+ button.addEventListener('mouseover', () => {
662
+ this.$highlightRow();
663
+ });
664
+ button.addEventListener('mouseout', () => {
665
+ this.$cancelHighlightRow();
666
+ });
667
+ } else {
668
+ button.title = this.$cherry.locale.deleteColumn;
669
+ button.addEventListener('click', () => {
670
+ this.$deleteCurrentColumn();
671
+ });
672
+ button.addEventListener('mouseover', () => {
673
+ this.$highlightColumn();
674
+ });
675
+ button.addEventListener('mouseout', () => {
676
+ this.$cancelHighlightColumn();
677
+ });
678
+ }
679
+ container.appendChild(button);
680
+ });
681
+ this.tableEditor.editorDom.deleteContainer = container;
682
+ this.container.appendChild(this.tableEditor.editorDom.deleteContainer);
683
+ this.$setDeleteButtonPosition();
684
+ }
685
+
686
+ /**
687
+ * 设置删除按钮的位置
688
+ */
689
+ $setDeleteButtonPosition() {
690
+ const container = this.tableEditor.editorDom.deleteContainer;
691
+ const { tableNode, tdNode, isTHead } = this.tableEditor.info;
692
+ const tableInfo = this.$getPosition(tableNode);
693
+ const tdInfo = this.$getPosition(tdNode);
694
+ // 设置容器宽高
695
+ this.setStyle(this.container, 'width', `${tableInfo.width}px`);
696
+ this.setStyle(this.container, 'height', `${tableInfo.height}px`);
697
+ this.setStyle(this.container, 'top', `${tableInfo.top}px`);
698
+ this.setStyle(this.container, 'left', `${tableInfo.left}px`);
699
+
700
+ // 设置删除按钮位置
701
+ container.childNodes.forEach((node) => {
702
+ const { type } = node.dataset;
703
+ const offset = {
704
+ outer: 20,
705
+ };
706
+ if (/(right|left)/.test(type)) {
707
+ if (isTHead) {
708
+ this.setStyle(node, 'display', 'none');
709
+ }
710
+ this.setStyle(node, 'top', `${tdInfo.top - tableInfo.top + tdInfo.height / 2 - node.offsetHeight / 2}px`);
711
+ this.setStyle(node, `${type}`, `-${node.offsetWidth + 5}px`);
712
+ } else {
713
+ this.setStyle(node, `${type}`, `-${offset.outer}px`);
714
+ this.setStyle(node, 'left', `${tdInfo.left - tableInfo.left + tdInfo.width / 2 - node.offsetWidth / 2}px`);
715
+ }
716
+ });
717
+ }
718
+
719
+ /**
720
+ * 删除当前行
721
+ */
722
+ $deleteCurrentRow() {
723
+ const { tableIndex, trIndex } = this.tableEditor.info;
724
+ this.$setSelection(tableIndex, 'table');
725
+ const selection = this.codeMirror.getSelection();
726
+ const table = selection.split('\n');
727
+ table.splice(trIndex + 2, 1);
728
+ const newText = table.join('\n');
729
+ this.codeMirror.replaceSelection(newText);
730
+ }
731
+
732
+ /**
733
+ * 删除当前列
734
+ */
735
+ $deleteCurrentColumn() {
736
+ const { tableIndex, tdIndex } = this.tableEditor.info;
737
+ this.$setSelection(tableIndex, 'table');
738
+ const selection = this.codeMirror.getSelection();
739
+ const table = selection.split('\n');
740
+ const rows = table.map((row) => row.split('|').slice(1, -1));
741
+ rows.forEach((row) => {
742
+ if (tdIndex >= 0 && tdIndex < row.length) {
743
+ row.splice(tdIndex, 1);
744
+ }
745
+ });
746
+ const newTable = rows.map((row) => (row.length === 0 ? '' : `|${row.join('|')}|`));
747
+ const newText = newTable.join('\n');
748
+ this.codeMirror.replaceSelection(newText);
749
+ }
750
+
751
+ /**
752
+ * 拖拽列
753
+ */
754
+ $dragCol() {
755
+ const oldTdIndex = this.tableEditor.info.tdIndex;
756
+ const thNode = this.target.parentElement;
757
+ const lines = this.codeMirror.getSelection().split(/\n/);
758
+ const { tdNode } = this.tableEditor.info;
759
+ const that = this;
760
+ tdNode.setAttribute('draggable', true);
761
+
762
+ function handleDragLeave(event) {
763
+ that.setStyle(event.target, 'border', '1px solid #dfe6ee');
764
+ }
765
+
766
+ function handleDragOver(event) {
767
+ event.preventDefault();
768
+ const tdIndex = Array.from(event.target.parentElement.childNodes).indexOf(event.target);
769
+ that.$dragSymbol(event.target, oldTdIndex, tdIndex, 'Col');
770
+ thNode.setAttribute('draggable', false);
771
+ }
772
+
773
+ function handleDrop(event) {
774
+ event.preventDefault();
775
+ const tdIndex = Array.from(event.target.parentElement.childNodes).indexOf(event.target);
776
+ const newLines = lines.map((line, index) => {
777
+ const cells = line
778
+ .split('|')
779
+ .map((item) => (item === '' ? 'CHERRY_MARKDOWN_PENDING_TEXT_FOR_EMPTY_CELL' : item))
780
+ .slice(1, -1);
781
+ return `|${that.$operateLines(oldTdIndex, tdIndex, cells).join('|')}|`;
782
+ });
783
+ const newText = newLines.join('\n').replace(/CHERRY_MARKDOWN_PENDING_TEXT_FOR_EMPTY_CELL/g, '');
784
+ that.codeMirror.replaceSelection(newText);
785
+ that.setStyle(event.target, 'border', '1px solid #dfe6ee');
786
+ that.$findTableInEditor();
787
+ that.$setSelection(that.tableEditor.info.tableIndex, 'table');
788
+
789
+ thNode.removeEventListener('dragleave', handleDragLeave);
790
+ thNode.removeEventListener('dragover', handleDragOver);
791
+ }
792
+
793
+ thNode.addEventListener('dragleave', handleDragLeave);
794
+ thNode.addEventListener('dragover', handleDragOver);
795
+ thNode.addEventListener('drop', handleDrop, { once: true });
796
+ }
797
+
798
+ /**
799
+ * 拖拽行
800
+ */
801
+ $dragLine() {
802
+ const { trNode } = this.tableEditor.info;
803
+ trNode.setAttribute('draggable', true);
804
+ this.$setSelection(this.tableEditor.info.tableIndex, 'table');
805
+ const oldTrIndex = this.tableEditor.info.trIndex + 2;
806
+ const tBody = trNode.parentElement;
807
+ const lines = this.codeMirror.getSelection().split(/\n/);
808
+ const that = this;
809
+
810
+ function handleDragLeave(event) {
811
+ that.setStyle(event.target.parentElement, 'border', '1px solid #dfe6ee');
812
+ }
813
+
814
+ function handleDragOver(event) {
815
+ event.preventDefault();
816
+ const trIndex =
817
+ Array.from(event.target.parentElement.parentElement.childNodes).indexOf(event.target.parentElement) + 2;
818
+ that.$dragSymbol(event.target, oldTrIndex, trIndex, 'Line');
819
+ trNode.setAttribute('draggable', false);
820
+ }
821
+
822
+ function handleDrop(event) {
823
+ event.preventDefault();
824
+ const trIndex =
825
+ Array.from(event.target.parentElement.parentElement.childNodes).indexOf(event.target.parentElement) + 2;
826
+ const newText = that.$operateLines(oldTrIndex, trIndex, lines).join('\n');
827
+ that.codeMirror.replaceSelection(newText);
828
+
829
+ that.$findTableInEditor();
830
+ that.$setSelection(that.tableEditor.info.tableIndex, 'table');
831
+ that.setStyle(event.target.parentElement, 'border', '1px solid #dfe6ee');
832
+
833
+ tBody.removeEventListener('dragleave', handleDragLeave);
834
+ tBody.removeEventListener('dragover', handleDragOver);
835
+ }
836
+
837
+ tBody.addEventListener('dragleave', handleDragLeave);
838
+ tBody.addEventListener('dragover', handleDragOver);
839
+ tBody.addEventListener('drop', handleDrop, { once: true });
840
+ }
841
+
842
+ $dragSymbol(objTarget, oldIndex, index, type) {
843
+ const { target } = this;
844
+ if (target !== objTarget && oldIndex !== index) {
845
+ if ((target.tagName === 'TH' || target.tagName === 'TD') && type === 'Col') {
846
+ if (oldIndex < index) {
847
+ this.setStyle(objTarget, 'border', `1px solid #dfe6ee`);
848
+ this.setStyle(objTarget, 'border-right', `2px solid #6897bb`);
849
+ } else if (oldIndex > index) {
850
+ this.setStyle(objTarget, 'border', `1px solid #dfe6ee`);
851
+ this.setStyle(objTarget, 'border-left', `2px solid #6897bb`);
852
+ }
853
+ } else if (target.tagName === 'TD' && type === 'Line') {
854
+ if (oldIndex < index) {
855
+ this.setStyle(objTarget.parentElement, 'border', `1px solid #dfe6ee`);
856
+ this.setStyle(objTarget.parentElement, 'border-bottom', `2px solid #6897bb`);
857
+ } else if (oldIndex > index) {
858
+ this.setStyle(objTarget.parentElement, 'border', `1px solid #dfe6ee`);
859
+ this.setStyle(objTarget.parentElement, 'border-top', `2px solid #6897bb`);
860
+ }
861
+ }
862
+ }
863
+ }
864
+
865
+ $operateLines(oldIndex, index, lines) {
866
+ if (oldIndex < index) {
867
+ lines.splice(index + 1, 0, lines[oldIndex]);
868
+ lines.splice(oldIndex, 1);
869
+ } else if (oldIndex > index) {
870
+ const line = lines[oldIndex];
871
+ lines.splice(oldIndex, 1);
872
+ lines.splice(index, 0, line);
873
+ }
874
+ return lines;
875
+ }
876
+ }