@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,608 @@
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 imgSizeHandler from '@/utils/imgSizeHandler';
18
+ import TableHandler from '@/utils/tableContentHandler';
19
+ import CodeHandler from '@/utils/codeBlockContentHandler';
20
+ import { drawioDialog } from '@/utils/dialog';
21
+ import { imgDrawioReg, getValueWithoutCode } from '@/utils/regexp';
22
+ import debounce from 'lodash/debounce';
23
+ import FormulaHandler from '@/utils/formulaUtilsHandler';
24
+ import ListHandler from '@/utils/listContentHandler';
25
+
26
+ /**
27
+ * 预览区域的响应式工具栏
28
+ */
29
+ export default class PreviewerBubble {
30
+ /**
31
+ *
32
+ * @param {import('../Previewer').default} previewer
33
+ */
34
+ constructor(previewer) {
35
+ /**
36
+ * @property
37
+ * @type {import('../Previewer').default}
38
+ */
39
+ this.previewer = previewer;
40
+ /**
41
+ * @property
42
+ * @type {import('../Editor').default}
43
+ */
44
+ this.editor = previewer.editor;
45
+ this.previewerDom = this.previewer.getDom();
46
+ this.$cherry = previewer.$cherry;
47
+ /**
48
+ * @property
49
+ * @type {{ [key: string]: HTMLDivElement}}
50
+ */
51
+ this.bubble = {};
52
+ /**
53
+ * @property
54
+ * @type {{ [key: string]: { emit: (...args: any[]) => any, [key:string]: any }}}
55
+ */
56
+ this.bubbleHandler = {};
57
+ this.init();
58
+ }
59
+
60
+ init() {
61
+ // 记录cherry外层容器的overflow属性,在后续的操作中会临时修改overflow属性,所以需要先记录
62
+ this.oldWrapperDomOverflow = this.previewer.$cherry.wrapperDom.style.overflow;
63
+ this.previewerDom.addEventListener('click', this.$onClick.bind(this));
64
+ this.previewerDom.addEventListener('mouseover', this.$onMouseOver.bind(this));
65
+ // this.previewerDom.addEventListener('mouseout', this.$onMouseOut.bind(this));
66
+
67
+ document.addEventListener('mousedown', (event) => {
68
+ Object.values(this.bubbleHandler).forEach((handler) => handler.emit('mousedown', event));
69
+ });
70
+ document.addEventListener('mouseup', (event) => {
71
+ Object.values(this.bubbleHandler).forEach((handler) =>
72
+ handler.emit('mouseup', event, () => this.$removeAllPreviewerBubbles('click')),
73
+ );
74
+ });
75
+ document.addEventListener('mousemove', (event) => {
76
+ Object.values(this.bubbleHandler).forEach((handler) => handler.emit('mousemove', event));
77
+ });
78
+ document.addEventListener('keyup', (event) => {
79
+ Object.values(this.bubbleHandler).forEach((handler) => handler.emit('keyup', event));
80
+ });
81
+ this.previewerDom.addEventListener(
82
+ 'scroll',
83
+ (event) => {
84
+ Object.values(this.bubbleHandler).forEach((handler) => handler.emit('scroll', event));
85
+ },
86
+ true,
87
+ );
88
+ this.$cherry.$event.on('previewerClose', () => this.$removeAllPreviewerBubbles());
89
+ this.previewer.options.afterUpdateCallBack.push(() => {
90
+ Object.values(this.bubbleHandler).forEach((handler) =>
91
+ handler.emit('previewUpdate', () => this.$removeAllPreviewerBubbles()),
92
+ );
93
+ });
94
+ this.previewerDom.addEventListener('change', this.$onChange.bind(this));
95
+ this.removeHoverBubble = debounce(() => this.$removeAllPreviewerBubbles('hover'), 400);
96
+ }
97
+
98
+ /**
99
+ * 判断是否为代码块
100
+ * @param {HTMLElement} element
101
+ * @returns {boolean|HTMLElement}
102
+ */
103
+ isCherryCodeBlock(element) {
104
+ // 引用里的代码块先不支持所见即所得编辑
105
+ if (this.$getClosestNode(element, 'BLOCKQUOTE') !== false) {
106
+ return false;
107
+ }
108
+ if (element.nodeName === 'DIV' && element.dataset.type === 'codeBlock') {
109
+ return element;
110
+ }
111
+ const container = this.$getClosestNode(element, 'DIV');
112
+ if (container === false) {
113
+ return false;
114
+ }
115
+ if (container.dataset.type === 'codeBlock') {
116
+ return container;
117
+ }
118
+ return false;
119
+ }
120
+
121
+ /**
122
+ * 是否为由cherry生成的表格,且不是简单表格
123
+ * @param {HTMLElement} element
124
+ * @returns {boolean}
125
+ */
126
+ isCherryTable(element) {
127
+ const container = this.$getClosestNode(element, 'DIV');
128
+ if (container === false) {
129
+ return false;
130
+ }
131
+ if (/simple-table/.test(container.className) || !/cherry-table-container/.test(container.className)) {
132
+ return false;
133
+ }
134
+ // 引用里的表格先不支持所见即所得编辑
135
+ if (this.$getClosestNode(element, 'BLOCKQUOTE') !== false) {
136
+ return false;
137
+ }
138
+ return container;
139
+ }
140
+
141
+ /**
142
+ * 是否开启了预览区操作 && 是否有编辑区
143
+ * @returns {boolean}
144
+ */
145
+ $isEnableBubbleAndEditorShow() {
146
+ if (!this.previewer.options.enablePreviewerBubble) {
147
+ return false;
148
+ }
149
+ const cherryStatus = this.previewer.$cherry.getStatus();
150
+ if (cherryStatus.editor === 'hide') {
151
+ return false;
152
+ }
153
+ return true;
154
+ }
155
+
156
+ $onMouseOver(e) {
157
+ /** @type {Event} */
158
+ const { target } = e;
159
+ // 这里要用Element,而不是HTMLElement
160
+ if (!(target instanceof Element) || typeof target.tagName === 'undefined') {
161
+ return;
162
+ }
163
+ switch (target.tagName) {
164
+ case 'TD':
165
+ case 'TH':
166
+ if (!this.$isEnableBubbleAndEditorShow()) {
167
+ return;
168
+ }
169
+ // eslint-disable-next-line no-case-declarations
170
+ const table = this.isCherryTable(e.target);
171
+ if (false === table) {
172
+ return;
173
+ }
174
+ this.removeHoverBubble.cancel();
175
+ this.$removeAllPreviewerBubbles('hover');
176
+ // @ts-ignore
177
+ this.$showTablePreviewerBubbles('hover', e.target, table);
178
+ return;
179
+ case 'PRE':
180
+ case 'CODE':
181
+ case 'SPAN':
182
+ case 'DIV':
183
+ // eslint-disable-next-line no-case-declarations
184
+ const codeBlock = this.isCherryCodeBlock(e.target);
185
+ if (codeBlock === false) {
186
+ return;
187
+ }
188
+ this.showCodeBlockPreviewerBubbles('hover', codeBlock);
189
+ return;
190
+ }
191
+ this.removeHoverBubble();
192
+ }
193
+
194
+ $onMouseOut() {
195
+ if (!this.previewer.options.enablePreviewerBubble) {
196
+ return;
197
+ }
198
+ const cherryStatus = this.previewer.$cherry.getStatus();
199
+ // 左侧编辑器被隐藏时不再提供后续功能
200
+ if (cherryStatus.editor === 'hide') {
201
+ return;
202
+ }
203
+ // this.removeHoverBubble();
204
+ }
205
+
206
+ $dealCheckboxClick(e) {
207
+ const { target } = e;
208
+ // 先计算是previewer中第几个checkbox
209
+ const list = Array.from(this.previewerDom.querySelectorAll('.ch-icon-square, .ch-icon-check'));
210
+ this.checkboxIdx = list.indexOf(target);
211
+
212
+ // 然后找到Editor中对应的`- []`或者`- [ ]`进行修改
213
+ const contents = getValueWithoutCode(this.editor.editor.getValue()).split('\n');
214
+
215
+ let editorCheckboxCount = 0;
216
+ // [ ]中的空格,或者[x]中的x的位置
217
+ let targetLine = -1;
218
+ let targetCh = -1;
219
+ contents.forEach((lineContent, lineIdx) => {
220
+ const tmp = lineContent.trim(); // 去掉句首的空格和制表符
221
+ if (tmp.startsWith('- [ ]') || tmp.startsWith('- [x]')) {
222
+ // 如果是个checkbox
223
+ if (editorCheckboxCount === this.checkboxIdx) {
224
+ targetLine = lineIdx;
225
+ targetCh = lineContent.indexOf('- [') + 3;
226
+ }
227
+ editorCheckboxCount += 1;
228
+ }
229
+ });
230
+ if (targetLine === -1) {
231
+ // 无法找到对应的checkbox
232
+ return;
233
+ }
234
+ this.editor.editor.setSelection({ line: targetLine, ch: targetCh }, { line: targetLine, ch: targetCh + 1 });
235
+ this.editor.editor.replaceSelection(this.editor.editor.getSelection() === ' ' ? 'x' : ' ', 'around');
236
+ }
237
+
238
+ /**
239
+ * 点击预览区域的事件处理
240
+ * @param {MouseEvent} e
241
+ * @returns
242
+ */
243
+ $onClick(e) {
244
+ const { target } = e;
245
+ if (!(target instanceof Element)) {
246
+ return;
247
+ }
248
+
249
+ // 编辑draw.io不受previewer.options.enablePreviewerBubble配置的影响
250
+ if (target instanceof HTMLImageElement) {
251
+ if (
252
+ target.tagName === 'IMG' &&
253
+ target.getAttribute('data-type') === 'drawio' &&
254
+ this.$cherry.status.editor === 'show'
255
+ ) {
256
+ if (!this.beginChangeDrawioImg(target)) {
257
+ return;
258
+ }
259
+ const xmlData = decodeURI(target.getAttribute('data-xml'));
260
+ drawioDialog(
261
+ this.previewer.$cherry.options.drawioIframeUrl,
262
+ this.previewer.$cherry.options.drawioIframeStyle,
263
+ xmlData,
264
+ (newData) => {
265
+ const { xmlData, base64 } = newData;
266
+ this.editor.editor.replaceSelection(
267
+ `(${base64}){data-type=drawio data-xml=${encodeURI(xmlData)}}`,
268
+ 'around',
269
+ );
270
+ },
271
+ );
272
+ return;
273
+ }
274
+ }
275
+
276
+ // 点击展开代码块操作
277
+ if (target.className === 'expand-btn ' || target.className === 'ch-icon ch-icon-expand') {
278
+ const expandBtnDom = this.$getClosestNode(target, 'DIV');
279
+ expandBtnDom.parentNode.parentNode.classList.remove('cherry-code-unExpand');
280
+ expandBtnDom.parentNode.parentNode.classList.add('cherry-code-expand');
281
+ if (this.bubbleHandler?.hover?.unExpandDom) {
282
+ this.bubbleHandler.hover.unExpandDom.classList.remove('hidden');
283
+ }
284
+ }
285
+
286
+ if (!this.previewer.options.enablePreviewerBubble) {
287
+ return;
288
+ }
289
+ // 只有双栏编辑模式才出现下面的功能
290
+ // checkbox所见即所得编辑操作
291
+ if (target.className === 'ch-icon ch-icon-square' || target.className === 'ch-icon ch-icon-check') {
292
+ this.$dealCheckboxClick(e);
293
+ }
294
+ this.$removeAllPreviewerBubbles('click');
295
+ if (typeof target.tagName === 'undefined') {
296
+ return;
297
+ }
298
+
299
+ switch (target.tagName) {
300
+ case 'IMG':
301
+ if (target instanceof HTMLImageElement) {
302
+ this.$showImgPreviewerBubbles(target);
303
+ }
304
+ break;
305
+ case 'TD':
306
+ case 'TH':
307
+ if (target instanceof HTMLElement) {
308
+ const table = this.isCherryTable(target);
309
+ if (false === table) {
310
+ return;
311
+ }
312
+ // @ts-ignore
313
+ this.$showTablePreviewerBubbles('click', target, table);
314
+ }
315
+ break;
316
+ case 'svg':
317
+ if (target?.parentElement?.tagName === 'MJX-CONTAINER') {
318
+ this.$showFormulaPreviewerBubbles('click', target, { x: e.pageX, y: e.pageY });
319
+ }
320
+ break;
321
+ case 'A':
322
+ e.stopPropagation(); // 阻止冒泡,避免触发预览区域的点击事件
323
+ break;
324
+ case 'P':
325
+ if (
326
+ target instanceof HTMLParagraphElement &&
327
+ target.parentElement instanceof HTMLLIElement &&
328
+ // 引用里的列表先不支持所见即所得编辑
329
+ this.$getClosestNode(target, 'BLOCKQUOTE') === false
330
+ ) {
331
+ if (target.children.length !== 0) {
332
+ // 富文本
333
+ e.preventDefault();
334
+ e.stopPropagation();
335
+ }
336
+ // 鼠标点击在列表的某项上时,为target增加contenteditable属性,使其可编辑
337
+ target.setAttribute('contenteditable', 'true');
338
+ target.focus();
339
+ this.$showListPreviewerBubbles('click', target);
340
+ }
341
+ break;
342
+ }
343
+ }
344
+
345
+ $onChange(e) {
346
+ return;
347
+ }
348
+
349
+ $getClosestNode(node, targetNodeName) {
350
+ if (!node || !node.tagName) {
351
+ return false;
352
+ }
353
+ if (node.tagName === targetNodeName) {
354
+ return node;
355
+ }
356
+ if (node.parentNode.tagName === 'BODY') {
357
+ return false;
358
+ }
359
+ return this.$getClosestNode(node.parentNode, targetNodeName);
360
+ }
361
+
362
+ /**
363
+ * 隐藏预览区域已经激活的工具栏
364
+ * @param {string} trigger 移除指定的触发方式,不传默认全部移除
365
+ */
366
+ $removeAllPreviewerBubbles(trigger = '') {
367
+ Object.entries(this.bubble)
368
+ .filter(([key]) => !trigger || trigger === key)
369
+ .forEach(([key, value]) => {
370
+ value.remove();
371
+ delete this.bubble[key];
372
+ });
373
+ Object.entries(this.bubbleHandler)
374
+ .filter(([key]) => !trigger || trigger === key)
375
+ .forEach(([key, value]) => {
376
+ value.emit('remove');
377
+ delete this.bubbleHandler[key];
378
+ });
379
+ if (Object.keys(this.bubbleHandler).length <= 0) {
380
+ this.previewer.$cherry.wrapperDom.style.overflow = this.oldWrapperDomOverflow || '';
381
+ }
382
+ }
383
+
384
+ /**
385
+ * 为触发的table增加操作工具栏
386
+ * @param {string} trigger 触发方式
387
+ * @param {HTMLElement} htmlElement 用户触发的table dom
388
+ */
389
+ $showTablePreviewerBubbles(trigger, htmlElement, tableElement) {
390
+ if (this.bubbleHandler[trigger]) {
391
+ if (this.bubbleHandler[trigger].tableElement === tableElement) {
392
+ // 已经存在相同的target,直接返回
393
+ this.bubbleHandler[trigger].showBubble();
394
+ return;
395
+ }
396
+ }
397
+ this.$createPreviewerBubbles(trigger, trigger === 'click' ? 'table-content-handler' : 'table-hover-handler');
398
+ const handler = new TableHandler(
399
+ trigger,
400
+ htmlElement,
401
+ this.bubble[trigger],
402
+ this.previewerDom,
403
+ this.editor.editor,
404
+ tableElement,
405
+ this.previewer.$cherry,
406
+ );
407
+ handler.showBubble();
408
+ this.bubbleHandler[trigger] = handler;
409
+ }
410
+
411
+ showCodeBlockPreviewerBubbles(trigger, htmlElement) {
412
+ if (this.bubbleHandler[trigger]) {
413
+ if (this.bubbleHandler[trigger].target === htmlElement) {
414
+ // 已经存在相同的target,直接返回
415
+ this.removeHoverBubble.cancel();
416
+ return;
417
+ }
418
+ }
419
+ this.$removeAllPreviewerBubbles('hover');
420
+ this.$createPreviewerBubbles(trigger, `codeBlock-${trigger}-handler`);
421
+ const handler = new CodeHandler(
422
+ trigger,
423
+ htmlElement,
424
+ this.bubble[trigger],
425
+ this.previewerDom,
426
+ this.editor.editor,
427
+ this,
428
+ );
429
+ handler.showBubble(this.$isEnableBubbleAndEditorShow());
430
+ this.bubbleHandler[trigger] = handler;
431
+ }
432
+
433
+ /**
434
+ * 为选中的图片增加操作工具栏
435
+ * @param {HTMLImageElement} htmlElement 用户点击的图片dom
436
+ */
437
+ $showImgPreviewerBubbles(htmlElement) {
438
+ this.$createPreviewerBubbles();
439
+ const list = Array.from(this.previewerDom.querySelectorAll('img'));
440
+ this.totalImgs = list.length;
441
+ this.imgIndex = list.indexOf(htmlElement);
442
+ if (!this.beginChangeImgValue(htmlElement)) {
443
+ return { emit: () => {} };
444
+ }
445
+ imgSizeHandler.showBubble(htmlElement, this.bubble.click, this.previewerDom);
446
+ imgSizeHandler.bindChange(this.changeImgValue.bind(this));
447
+ this.bubbleHandler.click = imgSizeHandler;
448
+ }
449
+
450
+ /**
451
+ * 为触发的公式增加操作工具栏
452
+ * @param {string} trigger 触发方式
453
+ * @param {Element} target 用户触发的公式dom
454
+ * @param {{x?: number, y?: number}} options 额外参数
455
+ */
456
+ $showFormulaPreviewerBubbles(trigger, target, options = {}) {
457
+ this.$createPreviewerBubbles(trigger, 'formula-hover-handler');
458
+ const formulaHandler = new FormulaHandler(trigger, target, this.bubble[trigger], this.previewerDom, this.editor);
459
+ formulaHandler.showBubble(options?.x || 0, options?.y || 0);
460
+ this.bubbleHandler[trigger] = formulaHandler;
461
+ }
462
+
463
+ /**
464
+ * 为触发的列表增加操作工具栏
465
+ * @param {string} trigger 触发方式
466
+ * @param {HTMLParagraphElement} target 用户触发的列表dom
467
+ */
468
+ $showListPreviewerBubbles(trigger, target, options = {}) {
469
+ this.$createPreviewerBubbles(trigger, 'list-hover-handler');
470
+ const listHandler = new ListHandler(trigger, target, this.bubble[trigger], this.previewerDom, this.editor);
471
+ this.bubbleHandler[trigger] = listHandler;
472
+ }
473
+
474
+ /**
475
+ * TODO: beginChangeDrawioImg 和 beginChangeImgValue 代码高度重合,后面有时间重构下,抽成一个可以复用的,可以避开代码块、行内代码影响的通用方法
476
+ * 修改draw.io图片时选中编辑区域的对应文本
477
+ * @param {*} htmlElement 图片node
478
+ */
479
+ beginChangeDrawioImg(htmlElement) {
480
+ const allDrawioImgs = Array.from(this.previewerDom.querySelectorAll('img[data-type="drawio"]'));
481
+ const totalDrawioImgs = allDrawioImgs.length;
482
+ const drawioImgIndex = allDrawioImgs.indexOf(htmlElement);
483
+ const content = getValueWithoutCode(this.editor.editor.getValue());
484
+ const drawioImgsCode = content.match(imgDrawioReg);
485
+ const testSrc = drawioImgsCode[drawioImgIndex]
486
+ ? drawioImgsCode[drawioImgIndex].replace(/^!\[.*?\]\((.*?)\)/, '$1').trim()
487
+ : '';
488
+ if (drawioImgsCode.length === totalDrawioImgs || htmlElement.getAttribute('src') === testSrc) {
489
+ // 如果drawio语法数量和预览区域的一样多
490
+ const totalValue = content.split(imgDrawioReg);
491
+ let line = 0;
492
+ let beginCh = 0;
493
+ let endCh = 0;
494
+ let testIndex = 0;
495
+ for (let i = 0; i < totalValue.length; i++) {
496
+ const targetString = totalValue[i];
497
+ if (targetString === drawioImgsCode[testIndex]) {
498
+ // 如果找到目标代码
499
+ if (testIndex === drawioImgIndex) {
500
+ endCh = beginCh + targetString.length;
501
+ beginCh += targetString.replace(/^(!\[[^\]]*])[^\n]*$/, '$1').length;
502
+ this.editor.editor.setSelection({ line, ch: beginCh }, { line, ch: endCh });
503
+ // 更新后需要再调用一次markText机制
504
+ this.editor.dealSpecialWords();
505
+ return true;
506
+ }
507
+ testIndex += 1;
508
+ } else {
509
+ line += targetString.match(/\n/g)?.length ?? 0;
510
+ if (/\n/.test(targetString)) {
511
+ // 如果有换行,则开始位置的字符计数从最后一个换行开始计数
512
+ beginCh = targetString.replace(/^[\w\W]*\n([^\n]*)$/, '$1').length;
513
+ } else {
514
+ // 如果没有换行,则继续按上次的beginCh为起始开始计数
515
+ beginCh += targetString.length;
516
+ }
517
+ }
518
+ }
519
+ }
520
+ return false;
521
+ }
522
+
523
+ /**
524
+ * 选中图片对应的MD语法
525
+ * @param {*} htmlElement 图片node
526
+ * @returns {boolean}
527
+ */
528
+ beginChangeImgValue(htmlElement) {
529
+ const content = getValueWithoutCode(this.editor.editor.getValue());
530
+ const src = htmlElement.getAttribute('src');
531
+ const imgReg = /(!\[[^\n]*?\]\([^)]+\))/g;
532
+ const contentImgs = content.match(imgReg);
533
+ const testSrc = contentImgs[this.imgIndex]
534
+ ? contentImgs[this.imgIndex].replace(/^!\[.*?\]\((.*?)\)/, '$1').trim()
535
+ : '';
536
+ if (contentImgs.length === this.totalImgs || src === testSrc) {
537
+ // 如果图片语法数量和预览区域的一样多
538
+ // 暂时不需要考虑手动输入img标签的场景 和 引用图片的场景
539
+ const totalValue = content.split(imgReg);
540
+ const imgAppendReg =
541
+ /^!\[.*?((?:#center|#right|#left|#float-right|#float-left|#border|#B|#shadow|#S|#radius|#R)+).*?\].*$/;
542
+ let line = 0;
543
+ let beginCh = 0;
544
+ let endCh = 0;
545
+ let testIndex = 0;
546
+ for (let i = 0; i < totalValue.length; i++) {
547
+ const targetString = totalValue[i];
548
+ if (targetString === contentImgs[testIndex]) {
549
+ // 如果找到目标代码
550
+ if (testIndex === this.imgIndex) {
551
+ this.imgAppend = imgAppendReg.test(targetString) ? targetString.replace(imgAppendReg, '$1') : false;
552
+ beginCh += targetString.replace(/^(!\[[^#\]]*).*$/, '$1').length;
553
+ endCh = beginCh + targetString.replace(/^(!\[[^#\]]*)([^\]]*?)\].*$/, '$2').length;
554
+ this.editor.editor.setSelection({ line, ch: beginCh }, { line, ch: endCh });
555
+ return true;
556
+ }
557
+ testIndex += 1;
558
+ }
559
+ line += targetString.match(/\n/g)?.length ?? 0;
560
+ if (/\n/.test(targetString)) {
561
+ // 如果有换行,则开始位置的字符计数从最后一个换行开始计数
562
+ beginCh = targetString.replace(/^[\w\W]*\n([^\n]*)$/, '$1').length;
563
+ } else {
564
+ // 如果没有换行,则继续按上次的beginCh为起始开始计数
565
+ beginCh += targetString.length;
566
+ }
567
+ }
568
+ }
569
+ return false;
570
+ }
571
+
572
+ /**
573
+ * 修改图片尺寸时的回调
574
+ * @param {HTMLElement} htmlElement 被拖拽的图片标签
575
+ * @param {Object} style 图片的属性(宽高、对齐方式)
576
+ */
577
+ changeImgValue(htmlElement, style) {
578
+ const append = this.imgAppend ? ` ${this.imgAppend}` : '';
579
+ this.editor.editor.replaceSelection(
580
+ `#${Math.round(style.width)}px #${Math.round(style.height)}px${append}`,
581
+ 'around',
582
+ );
583
+ }
584
+
585
+ /**
586
+ * 预览区域编辑器的容器
587
+ * @param {string} trigger 触发方式
588
+ * @param {string} type 容器类型(用作样式名:cherry-previewer-{type})
589
+ */
590
+ $createPreviewerBubbles(trigger = 'click', type = 'img-size-handler') {
591
+ if (!this.bubble[trigger]) {
592
+ this.bubble[trigger] = document.createElement('div');
593
+ this.bubble[trigger].className = `cherry-previewer-${type}`;
594
+ this.previewerDom.after(this.bubble[trigger]);
595
+
596
+ if (trigger === 'hover') {
597
+ this.bubble[trigger].addEventListener('mouseover', this.removeHoverBubble.cancel);
598
+ this.bubble[trigger].addEventListener('mouseout', this.removeHoverBubble);
599
+ }
600
+ // 暂时让最上层容器的overflow变成hidden
601
+ this.previewer.$cherry.wrapperDom.style.overflow = 'hidden';
602
+ }
603
+ }
604
+
605
+ $showBorderBubbles() {}
606
+
607
+ $showBtnBubbles() {}
608
+ }