@tencentcloud/ai-desk-customer-vue 0.1.0

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 (351) hide show
  1. package/.code.yml +17 -0
  2. package/.eslintignore +6 -0
  3. package/.stylelintrc.json +40 -0
  4. package/CHANGELOG.md +5 -0
  5. package/README.md +12 -0
  6. package/adapter-vue-web.ts +73 -0
  7. package/adapter-vue.ts +10 -0
  8. package/assets/audio-before-delete.svg +4 -0
  9. package/assets/audio-blue.png +0 -0
  10. package/assets/audio-bubble-blue.svg +3 -0
  11. package/assets/audio-bubble-red.svg +3 -0
  12. package/assets/audio-delete.svg +10 -0
  13. package/assets/audio.svg +6 -0
  14. package/assets/back.svg +16 -0
  15. package/assets/background_mobile.png +0 -0
  16. package/assets/camera-uni.png +0 -0
  17. package/assets/close-quote-icon.svg +3 -0
  18. package/assets/customer_avatar.png +0 -0
  19. package/assets/customer_avatar_mobile.png +0 -0
  20. package/assets/dialog-close.png +0 -0
  21. package/assets/double-arrow.svg +1 -0
  22. package/assets/download.svg +6 -0
  23. package/assets/emoji.png +0 -0
  24. package/assets/face-uni.png +0 -0
  25. package/assets/face.png +0 -0
  26. package/assets/file-h5.png +0 -0
  27. package/assets/files.png +0 -0
  28. package/assets/form-dialog-bg.png +0 -0
  29. package/assets/icon-arrow-left.svg +7 -0
  30. package/assets/icon-close.svg +6 -0
  31. package/assets/iconRight.svg +3 -0
  32. package/assets/icon_form.png +0 -0
  33. package/assets/icon_form_filled.png +0 -0
  34. package/assets/icon_question.png +0 -0
  35. package/assets/icon_refresh.png +0 -0
  36. package/assets/icon_success.png +0 -0
  37. package/assets/imRobotGuess.svg +4 -0
  38. package/assets/image-uni.png +0 -0
  39. package/assets/image.png +0 -0
  40. package/assets/keyboard_icon.png +0 -0
  41. package/assets/loading.png +0 -0
  42. package/assets/more-uni.png +0 -0
  43. package/assets/more_tools.png +0 -0
  44. package/assets/msg-audio.svg +1 -0
  45. package/assets/msg-copy.svg +30 -0
  46. package/assets/msg-del.svg +33 -0
  47. package/assets/msg-quote.svg +8 -0
  48. package/assets/msg-revoke.svg +29 -0
  49. package/assets/radio-check.png +0 -0
  50. package/assets/radio-uncheck.png +0 -0
  51. package/assets/radio.svg +6 -0
  52. package/assets/refresh.svg +4 -0
  53. package/assets/rotate-left.svg +7 -0
  54. package/assets/rotate-right.svg +7 -0
  55. package/assets/star.png +0 -0
  56. package/assets/starLine.png +0 -0
  57. package/assets/translate.svg +12 -0
  58. package/assets/video-play.png +0 -0
  59. package/assets/video-uni.png +0 -0
  60. package/assets/video.png +0 -0
  61. package/assets/zoom-in.svg +9 -0
  62. package/assets/zoom-out.svg +9 -0
  63. package/components/CustomerServiceChat/chat-header/index-web.vue +141 -0
  64. package/components/CustomerServiceChat/emoji-config/custom-emoji.ts +15 -0
  65. package/components/CustomerServiceChat/emoji-config/default-emoji.ts +114 -0
  66. package/components/CustomerServiceChat/emoji-config/index.ts +140 -0
  67. package/components/CustomerServiceChat/emoji-config/locales/en.ts +66 -0
  68. package/components/CustomerServiceChat/emoji-config/locales/zh_cn.ts +66 -0
  69. package/components/CustomerServiceChat/index-web.vue +250 -0
  70. package/components/CustomerServiceChat/message-input/index-web.vue +214 -0
  71. package/components/CustomerServiceChat/message-input/index.ts +2 -0
  72. package/components/CustomerServiceChat/message-input/message-input-button.vue +95 -0
  73. package/components/CustomerServiceChat/message-input/message-input-editor-web.vue +884 -0
  74. package/components/CustomerServiceChat/message-input/message-input-file-web.ts +38 -0
  75. package/components/CustomerServiceChat/message-input/message-input-quote/index.vue +204 -0
  76. package/components/CustomerServiceChat/message-input-toolbar/emoji-dialog-mobile/emoji-dialog-mobile.vue +145 -0
  77. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/emoji-picker-dialog.vue +180 -0
  78. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/index.ts +2 -0
  79. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/index.vue +90 -0
  80. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/style/h5.scss +26 -0
  81. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/style/index.scss +4 -0
  82. package/components/CustomerServiceChat/message-input-toolbar/emoji-picker/style/web.scss +55 -0
  83. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.ts +2 -0
  84. package/components/CustomerServiceChat/message-input-toolbar/file-upload/index.vue +77 -0
  85. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.ts +2 -0
  86. package/components/CustomerServiceChat/message-input-toolbar/image-upload/index.vue +185 -0
  87. package/components/CustomerServiceChat/message-input-toolbar/index-web.vue +191 -0
  88. package/components/CustomerServiceChat/message-input-toolbar/index.ts +2 -0
  89. package/components/CustomerServiceChat/message-input-toolbar/style/uni.scss +111 -0
  90. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/index.vue +149 -0
  91. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/color.scss +6 -0
  92. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/h5.scss +20 -0
  93. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/index.scss +5 -0
  94. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/uni.scss +36 -0
  95. package/components/CustomerServiceChat/message-input-toolbar/toolbar-item-container/style/web.scss +19 -0
  96. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.ts +2 -0
  97. package/components/CustomerServiceChat/message-input-toolbar/video-upload/index.vue +146 -0
  98. package/components/CustomerServiceChat/message-list/index-web.vue +712 -0
  99. package/components/CustomerServiceChat/message-list/link/index.ts +23 -0
  100. package/components/CustomerServiceChat/message-list/message-elements/message-audio-web.vue +223 -0
  101. package/components/CustomerServiceChat/message-list/message-elements/message-bubble-web.vue +504 -0
  102. package/components/CustomerServiceChat/message-list/message-elements/message-custom.vue +5 -0
  103. package/components/CustomerServiceChat/message-list/message-elements/message-face.vue +45 -0
  104. package/components/CustomerServiceChat/message-list/message-elements/message-file.vue +151 -0
  105. package/components/CustomerServiceChat/message-list/message-elements/message-image-web.vue +97 -0
  106. package/components/CustomerServiceChat/message-list/message-elements/message-location.vue +34 -0
  107. package/components/CustomerServiceChat/message-list/message-elements/message-quote/index-web.vue +286 -0
  108. package/components/CustomerServiceChat/message-list/message-elements/message-quote/interface.ts +60 -0
  109. package/components/CustomerServiceChat/message-list/message-elements/message-record/index.vue +139 -0
  110. package/components/CustomerServiceChat/message-list/message-elements/message-text.vue +94 -0
  111. package/components/CustomerServiceChat/message-list/message-elements/message-timestamp.vue +76 -0
  112. package/components/CustomerServiceChat/message-list/message-elements/message-video-web.vue +319 -0
  113. package/components/CustomerServiceChat/message-list/message-elements/plugins/plugin-components/index.ts +9 -0
  114. package/components/CustomerServiceChat/message-list/message-elements/plugins/plugin-components/message-customer/index.ts +5 -0
  115. package/components/CustomerServiceChat/message-list/message-elements/plugins/plugin-components/message-customer/message-customer-service.vue +22 -0
  116. package/components/CustomerServiceChat/message-list/message-elements/plugins/plugin-components/message-plugin-layout-web.vue +133 -0
  117. package/components/CustomerServiceChat/message-list/message-elements/plugins/plugin-components/message-plugin-web.vue +83 -0
  118. package/components/CustomerServiceChat/message-list/message-elements/read-status/index.vue +193 -0
  119. package/components/CustomerServiceChat/message-list/message-elements/simple-message-list/index.vue +462 -0
  120. package/components/CustomerServiceChat/message-list/message-elements/simple-message-list/message-container.vue +105 -0
  121. package/components/CustomerServiceChat/message-list/message-elements/video-play.vue +59 -0
  122. package/components/CustomerServiceChat/message-list/message-tool/index-web.vue +312 -0
  123. package/components/CustomerServiceChat/message-list/message-tool/message-revoked.vue +63 -0
  124. package/components/CustomerServiceChat/message-list/scroll-button/index.vue +201 -0
  125. package/components/CustomerServiceChat/message-list/style/color.scss +32 -0
  126. package/components/CustomerServiceChat/message-list/style/h5.scss +16 -0
  127. package/components/CustomerServiceChat/message-list/style/index.scss +11 -0
  128. package/components/CustomerServiceChat/message-list/style/web.scss +180 -0
  129. package/components/CustomerServiceChat/style/common.scss +59 -0
  130. package/components/CustomerServiceChat/style/h5.scss +51 -0
  131. package/components/CustomerServiceChat/style/index.scss +12 -0
  132. package/components/CustomerServiceChat/style/uni.scss +13 -0
  133. package/components/CustomerServiceChat/style/web.scss +46 -0
  134. package/components/CustomerServiceChat/style/wx.scss +5 -0
  135. package/components/CustomerServiceChat/utils/conversationDraft.ts +86 -0
  136. package/components/CustomerServiceChat/utils/sendMessage.ts +140 -0
  137. package/components/common/Avatar/index.vue +148 -0
  138. package/components/common/BottomPopup/index.ts +3 -0
  139. package/components/common/BottomPopup/index.vue +160 -0
  140. package/components/common/BottomPopup/style/h5.scss +62 -0
  141. package/components/common/BottomPopup/style/index.scss +3 -0
  142. package/components/common/BottomPopup/style/modal.scss +5 -0
  143. package/components/common/Dialog/index.ts +3 -0
  144. package/components/common/Dialog/index.vue +120 -0
  145. package/components/common/Dialog/style/color.scss +43 -0
  146. package/components/common/Dialog/style/dialog.scss +4 -0
  147. package/components/common/Dialog/style/h5.scss +56 -0
  148. package/components/common/Dialog/style/web.scss +61 -0
  149. package/components/common/Drawer/index.vue +164 -0
  150. package/components/common/Icon.vue +83 -0
  151. package/components/common/ImagePreviewer/image-item-web.vue +42 -0
  152. package/components/common/ImagePreviewer/index-web.vue +682 -0
  153. package/components/common/ImagePreviewer/index.ts +3 -0
  154. package/components/common/Overlay/index.vue +126 -0
  155. package/components/common/ProgressMessage/index.vue +95 -0
  156. package/components/common/RadioSelect/index.vue +59 -0
  157. package/components/common/RichText/LICENSE +21 -0
  158. package/components/common/RichText/README.md +244 -0
  159. package/components/common/RichText/dist/mp-alipay/index.acss +1 -0
  160. package/components/common/RichText/dist/mp-alipay/index.axml +1 -0
  161. package/components/common/RichText/dist/mp-alipay/index.js +8 -0
  162. package/components/common/RichText/dist/mp-alipay/index.json +1 -0
  163. package/components/common/RichText/dist/mp-alipay/node/node.acss +1 -0
  164. package/components/common/RichText/dist/mp-alipay/node/node.axml +1 -0
  165. package/components/common/RichText/dist/mp-alipay/node/node.js +1 -0
  166. package/components/common/RichText/dist/mp-alipay/node/node.json +1 -0
  167. package/components/common/RichText/dist/mp-alipay/parser.js +1 -0
  168. package/components/common/RichText/dist/mp-baidu/index.css +1 -0
  169. package/components/common/RichText/dist/mp-baidu/index.js +8 -0
  170. package/components/common/RichText/dist/mp-baidu/index.json +1 -0
  171. package/components/common/RichText/dist/mp-baidu/index.swan +1 -0
  172. package/components/common/RichText/dist/mp-baidu/node/node.css +1 -0
  173. package/components/common/RichText/dist/mp-baidu/node/node.js +1 -0
  174. package/components/common/RichText/dist/mp-baidu/node/node.json +1 -0
  175. package/components/common/RichText/dist/mp-baidu/node/node.swan +1 -0
  176. package/components/common/RichText/dist/mp-baidu/parser.js +1 -0
  177. package/components/common/RichText/dist/mp-qq/index.js +8 -0
  178. package/components/common/RichText/dist/mp-qq/index.json +1 -0
  179. package/components/common/RichText/dist/mp-qq/index.qml +1 -0
  180. package/components/common/RichText/dist/mp-qq/index.qss +1 -0
  181. package/components/common/RichText/dist/mp-qq/node/node.js +1 -0
  182. package/components/common/RichText/dist/mp-qq/node/node.json +1 -0
  183. package/components/common/RichText/dist/mp-qq/node/node.qml +1 -0
  184. package/components/common/RichText/dist/mp-qq/node/node.qss +1 -0
  185. package/components/common/RichText/dist/mp-qq/parser.js +1 -0
  186. package/components/common/RichText/dist/mp-toutiao/index.js +8 -0
  187. package/components/common/RichText/dist/mp-toutiao/index.json +1 -0
  188. package/components/common/RichText/dist/mp-toutiao/index.ttml +1 -0
  189. package/components/common/RichText/dist/mp-toutiao/index.ttss +1 -0
  190. package/components/common/RichText/dist/mp-toutiao/node/node.js +1 -0
  191. package/components/common/RichText/dist/mp-toutiao/node/node.json +1 -0
  192. package/components/common/RichText/dist/mp-toutiao/node/node.ttml +1 -0
  193. package/components/common/RichText/dist/mp-toutiao/node/node.ttss +1 -0
  194. package/components/common/RichText/dist/mp-toutiao/parser.js +1 -0
  195. package/components/common/RichText/dist/mp-weixin/index.js +8 -0
  196. package/components/common/RichText/dist/mp-weixin/index.json +1 -0
  197. package/components/common/RichText/dist/mp-weixin/index.wxml +1 -0
  198. package/components/common/RichText/dist/mp-weixin/index.wxss +1 -0
  199. package/components/common/RichText/dist/mp-weixin/node/node.js +1 -0
  200. package/components/common/RichText/dist/mp-weixin/node/node.json +1 -0
  201. package/components/common/RichText/dist/mp-weixin/node/node.wxml +1 -0
  202. package/components/common/RichText/dist/mp-weixin/node/node.wxss +1 -0
  203. package/components/common/RichText/dist/mp-weixin/parser.js +1 -0
  204. package/components/common/RichText/dist/uni-app/components/mp-html/mp-html.vue +498 -0
  205. package/components/common/RichText/dist/uni-app/components/mp-html/node/node.vue +587 -0
  206. package/components/common/RichText/dist/uni-app/components/mp-html/parser.js +1393 -0
  207. package/components/common/RichText/dist/uni-app/static/app-plus/mp-html/js/handler.js +1 -0
  208. package/components/common/RichText/dist/uni-app/static/app-plus/mp-html/js/uni.webview.min.js +1 -0
  209. package/components/common/RichText/dist/uni-app/static/app-plus/mp-html/local.html +1 -0
  210. package/components/common/RichText/docs/.nojekyll +0 -0
  211. package/components/common/RichText/docs/index.html +50 -0
  212. package/components/common/RichText/docs/lib/docsify.min.js +1 -0
  213. package/components/common/RichText/docs/lib/prism-bash.min.js +1 -0
  214. package/components/common/RichText/docs/lib/search.min.js +1 -0
  215. package/components/common/RichText/docs/lib/vue.css +858 -0
  216. package/components/common/RichText/gulpfile.js +113 -0
  217. package/components/common/RichText/lint.js +63 -0
  218. package/components/common/RichText/package.json +74 -0
  219. package/components/common/RichText/plugins/README.md +2 -0
  220. package/components/common/RichText/plugins/audio/README.md +25 -0
  221. package/components/common/RichText/plugins/audio/build.js +11 -0
  222. package/components/common/RichText/plugins/audio/context.js +7 -0
  223. package/components/common/RichText/plugins/audio/index.js +34 -0
  224. package/components/common/RichText/plugins/audio/miniprogram/audio.js +189 -0
  225. package/components/common/RichText/plugins/audio/miniprogram/audio.json +3 -0
  226. package/components/common/RichText/plugins/audio/miniprogram/audio.wxml +17 -0
  227. package/components/common/RichText/plugins/audio/miniprogram/audio.wxss +127 -0
  228. package/components/common/RichText/plugins/audio/miniprogram/build.js +3 -0
  229. package/components/common/RichText/plugins/audio/uni-app/audio.vue +269 -0
  230. package/components/common/RichText/plugins/audio/uni-app/build.js +3 -0
  231. package/components/common/RichText/plugins/card/README.md +30 -0
  232. package/components/common/RichText/plugins/card/build.js +14 -0
  233. package/components/common/RichText/plugins/card/index.js +7 -0
  234. package/components/common/RichText/plugins/card/miniprogram/build.js +3 -0
  235. package/components/common/RichText/plugins/card/miniprogram/card.js +26 -0
  236. package/components/common/RichText/plugins/card/miniprogram/card.json +3 -0
  237. package/components/common/RichText/plugins/card/miniprogram/card.wxml +9 -0
  238. package/components/common/RichText/plugins/card/miniprogram/card.wxss +55 -0
  239. package/components/common/RichText/plugins/card/uni-app/build.js +3 -0
  240. package/components/common/RichText/plugins/card/uni-app/card.vue +122 -0
  241. package/components/common/RichText/plugins/editable/README.md +137 -0
  242. package/components/common/RichText/plugins/editable/config.js +15 -0
  243. package/components/common/RichText/plugins/editable/miniprogram/build.js +813 -0
  244. package/components/common/RichText/plugins/editable/miniprogram/index.js +551 -0
  245. package/components/common/RichText/plugins/editable/uni-app/build.js +744 -0
  246. package/components/common/RichText/plugins/editable/uni-app/index.js +553 -0
  247. package/components/common/RichText/plugins/emoji/README.md +15 -0
  248. package/components/common/RichText/plugins/emoji/index.js +203 -0
  249. package/components/common/RichText/plugins/highlight/README.md +26 -0
  250. package/components/common/RichText/plugins/highlight/config.js +5 -0
  251. package/components/common/RichText/plugins/highlight/index.js +96 -0
  252. package/components/common/RichText/plugins/highlight/miniprogram/build.js +88 -0
  253. package/components/common/RichText/plugins/highlight/prism.css +125 -0
  254. package/components/common/RichText/plugins/highlight/prism.min.js +7 -0
  255. package/components/common/RichText/plugins/highlight/uni-app/build.js +88 -0
  256. package/components/common/RichText/plugins/img-cache/README.md +24 -0
  257. package/components/common/RichText/plugins/img-cache/build.js +16 -0
  258. package/components/common/RichText/plugins/img-cache/index.js +138 -0
  259. package/components/common/RichText/plugins/latex/README.md +16 -0
  260. package/components/common/RichText/plugins/latex/build.js +14 -0
  261. package/components/common/RichText/plugins/latex/index.js +77 -0
  262. package/components/common/RichText/plugins/latex/katex.css +1070 -0
  263. package/components/common/RichText/plugins/latex/katex.min.js +1 -0
  264. package/components/common/RichText/plugins/markdown/README.md +17 -0
  265. package/components/common/RichText/plugins/markdown/index.js +34 -0
  266. package/components/common/RichText/plugins/markdown/marked.min.js +6 -0
  267. package/components/common/RichText/plugins/markdown/miniprogram/build.js +70 -0
  268. package/components/common/RichText/plugins/markdown/uni-app/build.js +68 -0
  269. package/components/common/RichText/plugins/search/README.md +46 -0
  270. package/components/common/RichText/plugins/search/miniprogram/index.js +137 -0
  271. package/components/common/RichText/plugins/search/uni-app/index.js +132 -0
  272. package/components/common/RichText/plugins/style/README.md +30 -0
  273. package/components/common/RichText/plugins/style/index.js +129 -0
  274. package/components/common/RichText/plugins/style/parser.js +175 -0
  275. package/components/common/RichText/plugins/template/README.md +2 -0
  276. package/components/common/RichText/plugins/template/build.js +65 -0
  277. package/components/common/RichText/plugins/template/index.js +67 -0
  278. package/components/common/RichText/plugins/txv-video/README.md +18 -0
  279. package/components/common/RichText/plugins/txv-video/build.js +3 -0
  280. package/components/common/RichText/plugins/txv-video/index.js +46 -0
  281. package/components/common/RichText/plugins/txv-video/miniprogram/build.js +6 -0
  282. package/components/common/RichText/plugins/txv-video/uni-app/build.js +3 -0
  283. package/components/common/RichText/src/miniprogram/index.js +396 -0
  284. package/components/common/RichText/src/miniprogram/index.json +6 -0
  285. package/components/common/RichText/src/miniprogram/index.wxml +7 -0
  286. package/components/common/RichText/src/miniprogram/index.wxss +13 -0
  287. package/components/common/RichText/src/miniprogram/node/node.js +247 -0
  288. package/components/common/RichText/src/miniprogram/node/node.json +6 -0
  289. package/components/common/RichText/src/miniprogram/node/node.wxml +112 -0
  290. package/components/common/RichText/src/miniprogram/node/node.wxss +164 -0
  291. package/components/common/RichText/src/miniprogram/parser.js +1269 -0
  292. package/components/common/RichText/src/uni-app/components/mp-html/mp-html.vue +498 -0
  293. package/components/common/RichText/src/uni-app/components/mp-html/node/node.vue +585 -0
  294. package/components/common/RichText/src/uni-app/components/mp-html/parser.js +1393 -0
  295. package/components/common/RichText/src/uni-app/static/app-plus/mp-html/js/handler.js +254 -0
  296. package/components/common/RichText/src/uni-app/static/app-plus/mp-html/js/uni.webview.min.js +1 -0
  297. package/components/common/RichText/src/uni-app/static/app-plus/mp-html/local.html +33 -0
  298. package/components/common/RichText/tools/config.js +82 -0
  299. package/components/common/RichText/tools/converter.js +205 -0
  300. package/components/common/RichText/tools/ifdef.js +115 -0
  301. package/components/common/RichText/tools/minifier.js +40 -0
  302. package/components/common/RichText/tools/plugin.js +248 -0
  303. package/components/common/Toast/index-web.ts +122 -0
  304. package/components/common/Toast/index-web.vue +178 -0
  305. package/components/common/Toast/type.ts +8 -0
  306. package/components/common/common.scss +59 -0
  307. package/components/customer-icon.vue +56 -0
  308. package/components/message-branch.vue +120 -0
  309. package/components/message-customer-service.vue +114 -0
  310. package/components/message-form/form-branch.vue +68 -0
  311. package/components/message-form/form-input.vue +242 -0
  312. package/components/message-form/index.vue +80 -0
  313. package/components/message-multi-branch/branch-pc.vue +79 -0
  314. package/components/message-multi-branch/index.vue +60 -0
  315. package/components/message-multi-form/component-mobile/form-popup.vue +37 -0
  316. package/components/message-multi-form/component-mobile/input-mobile.vue +102 -0
  317. package/components/message-multi-form/component-mobile/label-mobile.vue +32 -0
  318. package/components/message-multi-form/component-mobile/radios-mobile.vue +161 -0
  319. package/components/message-multi-form/component-pc/input-pc.vue +93 -0
  320. package/components/message-multi-form/component-pc/label-pc.vue +33 -0
  321. package/components/message-multi-form/component-pc/radio-pc.vue +98 -0
  322. package/components/message-multi-form/form-mobile.vue +385 -0
  323. package/components/message-multi-form/form-pc.vue +184 -0
  324. package/components/message-multi-form/index.vue +61 -0
  325. package/components/message-product-card.vue +129 -0
  326. package/components/message-rating/index.vue +56 -0
  327. package/components/message-rating/message-rating-number.vue +247 -0
  328. package/components/message-rating/message-rating-star.vue +237 -0
  329. package/components/message-rich-text.vue +155 -0
  330. package/components/message-robot-welcome.vue +181 -0
  331. package/components/message-stream.vue +109 -0
  332. package/constant.ts +122 -0
  333. package/excluded-list.txt +6 -0
  334. package/index.ts +30 -0
  335. package/index.vue +60 -0
  336. package/interface.ts +176 -0
  337. package/logger/index.ts +12 -0
  338. package/logger/main.ts +120 -0
  339. package/package.json +62 -0
  340. package/script/fileCopy.js +60 -0
  341. package/script/syncVersion.js +35 -0
  342. package/server.ts +128 -0
  343. package/styles/common.scss +116 -0
  344. package/tsconfig.json +34 -0
  345. package/typings.d.ts +20 -0
  346. package/utils/chatStorage.ts +70 -0
  347. package/utils/copy-web.ts +141 -0
  348. package/utils/enableSampleTaskStatus.ts +8 -0
  349. package/utils/env.ts +15 -0
  350. package/utils/index.ts +59 -0
  351. package/utils/utils.ts +162 -0
@@ -0,0 +1,1393 @@
1
+ /**
2
+ * @fileoverview html 解析器
3
+ */
4
+
5
+ // 配置
6
+ const config = {
7
+ // 信任的标签(保持标签名不变)
8
+ trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
9
+
10
+ // 块级标签(转为 div,其他的非信任标签转为 span)
11
+ blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
12
+
13
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
14
+ // 行内标签
15
+ inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
16
+ // #endif
17
+
18
+ // 要移除的标签
19
+ ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
20
+
21
+ // 自闭合的标签
22
+ voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
23
+
24
+ // html 实体
25
+ entities: {
26
+ lt: '<',
27
+ gt: '>',
28
+ quot: '"',
29
+ apos: "'",
30
+ ensp: '\u2002',
31
+ emsp: '\u2003',
32
+ nbsp: '\xA0',
33
+ semi: ';',
34
+ ndash: '–',
35
+ mdash: '—',
36
+ middot: '·',
37
+ lsquo: '‘',
38
+ rsquo: '’',
39
+ ldquo: '“',
40
+ rdquo: '”',
41
+ bull: '•',
42
+ hellip: '…',
43
+ larr: '←',
44
+ uarr: '↑',
45
+ rarr: '→',
46
+ darr: '↓'
47
+ },
48
+
49
+ // 默认的标签样式
50
+ tagStyle: {
51
+ // #ifndef APP-PLUS-NVUE
52
+ address: 'font-style:italic',
53
+ big: 'display:inline;font-size:1.2em',
54
+ caption: 'display:table-caption;text-align:center',
55
+ center: 'text-align:center',
56
+ cite: 'font-style:italic',
57
+ dd: 'margin-left:40px',
58
+ mark: 'background-color:yellow',
59
+ pre: 'font-family:monospace;white-space:pre',
60
+ s: 'text-decoration:line-through',
61
+ small: 'display:inline;font-size:0.8em',
62
+ strike: 'text-decoration:line-through',
63
+ u: 'text-decoration:underline'
64
+ // #endif
65
+ },
66
+
67
+ // svg 大小写对照表
68
+ svgDict: {
69
+ animatetransform: 'animateTransform',
70
+ lineargradient: 'linearGradient',
71
+ viewbox: 'viewBox',
72
+ attributename: 'attributeName',
73
+ repeatcount: 'repeatCount',
74
+ repeatdur: 'repeatDur',
75
+ foreignobject: 'foreignObject'
76
+ }
77
+ }
78
+ const tagSelector = {}
79
+ const {
80
+ windowWidth,
81
+ // #ifdef MP-WEIXIN
82
+ system
83
+ // #endif
84
+ } = uni.getSystemInfoSync()
85
+ const blankChar = makeMap(' ,\r,\n,\t,\f')
86
+ let idIndex = 0
87
+
88
+ // #ifdef H5 || APP-PLUS
89
+ config.ignoreTags.iframe = undefined
90
+ config.trustTags.iframe = true
91
+ config.ignoreTags.embed = undefined
92
+ config.trustTags.embed = true
93
+ // #endif
94
+ // #ifdef APP-PLUS-NVUE
95
+ config.ignoreTags.source = undefined
96
+ config.ignoreTags.style = undefined
97
+ // #endif
98
+
99
+ /**
100
+ * @description 创建 map
101
+ * @param {String} str 逗号分隔
102
+ */
103
+ function makeMap (str) {
104
+ const map = Object.create(null)
105
+ const list = str.split(',')
106
+ for (let i = list.length; i--;) {
107
+ map[list[i]] = true
108
+ }
109
+ return map
110
+ }
111
+
112
+ /**
113
+ * @description 解码 html 实体
114
+ * @param {String} str 要解码的字符串
115
+ * @param {Boolean} amp 要不要解码 &amp;
116
+ * @returns {String} 解码后的字符串
117
+ */
118
+ function decodeEntity (str, amp) {
119
+ let i = str.indexOf('&')
120
+ while (i !== -1) {
121
+ const j = str.indexOf(';', i + 3)
122
+ let code
123
+ if (j === -1) break
124
+ if (str[i + 1] === '#') {
125
+ // &#123; 形式的实体
126
+ code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
127
+ if (!isNaN(code)) {
128
+ str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
129
+ }
130
+ } else {
131
+ // &nbsp; 形式的实体
132
+ code = str.substring(i + 1, j)
133
+ if (config.entities[code] || (code === 'amp' && amp)) {
134
+ str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
135
+ }
136
+ }
137
+ i = str.indexOf('&', i + 1)
138
+ }
139
+ return str
140
+ }
141
+
142
+ /**
143
+ * @description 合并多个块级标签,加快长内容渲染
144
+ * @param {Array} nodes 要合并的标签数组
145
+ */
146
+ function mergeNodes (nodes) {
147
+ let i = nodes.length - 1
148
+ for (let j = i; j >= -1; j--) {
149
+ if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
150
+ if (i - j >= 5) {
151
+ nodes.splice(j + 1, i - j, {
152
+ name: 'div',
153
+ attrs: {},
154
+ children: nodes.slice(j + 1, i + 1)
155
+ })
156
+ }
157
+ i = j - 1
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * @description html 解析器
164
+ * @param {Object} vm 组件实例
165
+ */
166
+ function Parser (vm) {
167
+ this.options = vm || {}
168
+ this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
169
+ this.imgList = vm.imgList || []
170
+ this.imgList._unloadimgs = 0
171
+ this.plugins = vm.plugins || []
172
+ this.attrs = Object.create(null)
173
+ this.stack = []
174
+ this.nodes = []
175
+ this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
176
+ }
177
+
178
+ /**
179
+ * @description 执行解析
180
+ * @param {String} content 要解析的文本
181
+ */
182
+ Parser.prototype.parse = function (content) {
183
+ // 插件处理
184
+ for (let i = this.plugins.length; i--;) {
185
+ if (this.plugins[i].onUpdate) {
186
+ content = this.plugins[i].onUpdate(content, config) || content
187
+ }
188
+ }
189
+
190
+ new Lexer(this).parse(content)
191
+ // 出栈未闭合的标签
192
+ while (this.stack.length) {
193
+ this.popNode()
194
+ }
195
+ if (this.nodes.length > 50) {
196
+ mergeNodes(this.nodes)
197
+ }
198
+ return this.nodes
199
+ }
200
+
201
+ /**
202
+ * @description 将标签暴露出来(不被 rich-text 包含)
203
+ */
204
+ Parser.prototype.expose = function () {
205
+ // #ifndef APP-PLUS-NVUE
206
+ for (let i = this.stack.length; i--;) {
207
+ const item = this.stack[i]
208
+ if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
209
+ item.c = 1
210
+ }
211
+ // #endif
212
+ }
213
+
214
+ /**
215
+ * @description 处理插件
216
+ * @param {Object} node 要处理的标签
217
+ * @returns {Boolean} 是否要移除此标签
218
+ */
219
+ Parser.prototype.hook = function (node) {
220
+ for (let i = this.plugins.length; i--;) {
221
+ if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
222
+ return false
223
+ }
224
+ }
225
+ return true
226
+ }
227
+
228
+ /**
229
+ * @description 将链接拼接上主域名
230
+ * @param {String} url 需要拼接的链接
231
+ * @returns {String} 拼接后的链接
232
+ */
233
+ Parser.prototype.getUrl = function (url) {
234
+ const domain = this.options.domain
235
+ if (url[0] === '/') {
236
+ if (url[1] === '/') {
237
+ // // 开头的补充协议名
238
+ url = (domain ? domain.split('://')[0] : 'http') + ':' + url
239
+ } else if (domain) {
240
+ // 否则补充整个域名
241
+ url = domain + url
242
+ } /* #ifdef APP-PLUS */ else {
243
+ url = plus.io.convertLocalFileSystemURL(url)
244
+ } /* #endif */
245
+ } else if (!url.includes('data:') && !url.includes('://')) {
246
+ if (domain) {
247
+ url = domain + '/' + url
248
+ } /* #ifdef APP-PLUS */ else {
249
+ url = plus.io.convertLocalFileSystemURL(url)
250
+ } /* #endif */
251
+ }
252
+ return url
253
+ }
254
+
255
+ /**
256
+ * @description 解析样式表
257
+ * @param {Object} node 标签
258
+ * @returns {Object}
259
+ */
260
+ Parser.prototype.parseStyle = function (node) {
261
+ const attrs = node.attrs
262
+ const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
263
+ const styleObj = {}
264
+ let tmp = ''
265
+
266
+ if (attrs.id && !this.xml) {
267
+ // 暴露锚点
268
+ if (this.options.useAnchor) {
269
+ this.expose()
270
+ } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
271
+ attrs.id = undefined
272
+ }
273
+ }
274
+
275
+ // 转换 width 和 height 属性
276
+ if (attrs.width) {
277
+ styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
278
+ attrs.width = undefined
279
+ }
280
+ if (attrs.height) {
281
+ styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
282
+ attrs.height = undefined
283
+ }
284
+
285
+ for (let i = 0, len = list.length; i < len; i++) {
286
+ const info = list[i].split(':')
287
+ if (info.length < 2) continue
288
+ const key = info.shift().trim().toLowerCase()
289
+ let value = info.join(':').trim()
290
+ if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
291
+ // 兼容性的 css 不压缩
292
+ tmp += `;${key}:${value}`
293
+ } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
294
+ // 重复的样式进行覆盖
295
+ if (value.includes('url')) {
296
+ // 填充链接
297
+ let j = value.indexOf('(') + 1
298
+ if (j) {
299
+ while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
300
+ j++
301
+ }
302
+ value = value.substr(0, j) + this.getUrl(value.substr(j))
303
+ }
304
+ } else if (value.includes('rpx')) {
305
+ // 转换 rpx(rich-text 内部不支持 rpx)
306
+ value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
307
+ }
308
+ styleObj[key] = value
309
+ }
310
+ }
311
+
312
+ node.attrs.style = tmp
313
+ return styleObj
314
+ }
315
+
316
+ /**
317
+ * @description 解析到标签名
318
+ * @param {String} name 标签名
319
+ * @private
320
+ */
321
+ Parser.prototype.onTagName = function (name) {
322
+ this.tagName = this.xml ? name : name.toLowerCase()
323
+ if (this.tagName === 'svg') {
324
+ this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
325
+ config.ignoreTags.style = undefined // svg 标签内 style 可用
326
+ }
327
+ }
328
+
329
+ /**
330
+ * @description 解析到属性名
331
+ * @param {String} name 属性名
332
+ * @private
333
+ */
334
+ Parser.prototype.onAttrName = function (name) {
335
+ name = this.xml ? name : name.toLowerCase()
336
+ // #ifdef (VUE3 && (H5 || APP-PLUS)) || APP-PLUS-NVUE
337
+ if (name.includes('?') || name.includes(';')) {
338
+ this.attrName = undefined
339
+ return
340
+ }
341
+ // #endif
342
+ if (name.substr(0, 5) === 'data-') {
343
+ if (name === 'data-src' && !this.attrs.src) {
344
+ // data-src 自动转为 src
345
+ this.attrName = 'src'
346
+ } else if (this.tagName === 'img' || this.tagName === 'a') {
347
+ // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用
348
+ this.attrName = name
349
+ } else {
350
+ // 剩余的移除以减小大小
351
+ this.attrName = undefined
352
+ }
353
+ } else {
354
+ this.attrName = name
355
+ this.attrs[name] = 'T' // boolean 型属性缺省设置
356
+ }
357
+ }
358
+
359
+ /**
360
+ * @description 解析到属性值
361
+ * @param {String} val 属性值
362
+ * @private
363
+ */
364
+ Parser.prototype.onAttrVal = function (val) {
365
+ const name = this.attrName || ''
366
+ if (name === 'style' || name === 'href') {
367
+ // 部分属性进行实体解码
368
+ this.attrs[name] = decodeEntity(val, true)
369
+ } else if (name.includes('src')) {
370
+ // 拼接主域名
371
+ this.attrs[name] = this.getUrl(decodeEntity(val, true))
372
+ } else if (name) {
373
+ this.attrs[name] = val
374
+ }
375
+ }
376
+
377
+ /**
378
+ * @description 解析到标签开始
379
+ * @param {Boolean} selfClose 是否有自闭合标识 />
380
+ * @private
381
+ */
382
+ Parser.prototype.onOpenTag = function (selfClose) {
383
+ // 拼装 node
384
+ const node = Object.create(null)
385
+ node.name = this.tagName
386
+ node.attrs = this.attrs
387
+ // 避免因为自动 diff 使得 type 被设置为 null 导致部分内容不显示
388
+ if (this.options.nodes.length) {
389
+ node.type = 'node'
390
+ }
391
+ this.attrs = Object.create(null)
392
+
393
+ const attrs = node.attrs
394
+ const parent = this.stack[this.stack.length - 1]
395
+ const siblings = parent ? parent.children : this.nodes
396
+ const close = this.xml ? selfClose : config.voidTags[node.name]
397
+
398
+ // 替换标签名选择器
399
+ if (tagSelector[node.name]) {
400
+ attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
401
+ }
402
+
403
+ // 转换 embed 标签
404
+ if (node.name === 'embed') {
405
+ // #ifndef H5 || APP-PLUS
406
+ const src = attrs.src || ''
407
+ // 按照后缀名和 type 将 embed 转为 video 或 audio
408
+ if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
409
+ node.name = 'video'
410
+ } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
411
+ node.name = 'audio'
412
+ }
413
+ if (attrs.autostart) {
414
+ attrs.autoplay = 'T'
415
+ }
416
+ attrs.controls = 'T'
417
+ // #endif
418
+ // #ifdef H5 || APP-PLUS
419
+ this.expose()
420
+ // #endif
421
+ }
422
+
423
+ // #ifndef APP-PLUS-NVUE
424
+ // 处理音视频
425
+ if (node.name === 'video' || node.name === 'audio') {
426
+ // 设置 id 以便获取 context
427
+ if (node.name === 'video' && !attrs.id) {
428
+ attrs.id = 'v' + idIndex++
429
+ }
430
+ // 没有设置 controls 也没有设置 autoplay 的自动设置 controls
431
+ if (!attrs.controls && !attrs.autoplay) {
432
+ attrs.controls = 'T'
433
+ }
434
+ // 用数组存储所有可用的 source
435
+ node.src = []
436
+ if (attrs.src) {
437
+ node.src.push(attrs.src)
438
+ attrs.src = undefined
439
+ }
440
+ this.expose()
441
+ }
442
+ // #endif
443
+
444
+ // 处理自闭合标签
445
+ if (close) {
446
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
447
+ // 通过 base 标签设置主域名
448
+ if (node.name === 'base' && !this.options.domain) {
449
+ this.options.domain = attrs.href
450
+ } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
451
+ // 设置 source 标签(仅父节点为 video 或 audio 时有效)
452
+ parent.src.push(attrs.src)
453
+ } /* #endif */
454
+ return
455
+ }
456
+
457
+ // 解析 style
458
+ const styleObj = this.parseStyle(node)
459
+
460
+ // 处理图片
461
+ if (node.name === 'img') {
462
+ if (attrs.src) {
463
+ // 标记 webp
464
+ if (attrs.src.includes('webp')) {
465
+ node.webp = 'T'
466
+ }
467
+ // data url 图片如果没有设置 original-src 默认为不可预览的小图片
468
+ if (attrs.src.includes('data:') && this.options.previewImg !== 'all' && !attrs['original-src']) {
469
+ attrs.ignore = 'T'
470
+ }
471
+ if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
472
+ for (let i = this.stack.length; i--;) {
473
+ const item = this.stack[i]
474
+ if (item.name === 'a') {
475
+ node.a = item.attrs
476
+ }
477
+ if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
478
+ if (!styleObj.display || styleObj.display.includes('inline')) {
479
+ node.t = 'inline-block'
480
+ } else {
481
+ node.t = styleObj.display
482
+ }
483
+ styleObj.display = undefined
484
+ }
485
+ // #ifndef H5 || APP-PLUS
486
+ const style = item.attrs.style || ''
487
+ if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
488
+ styleObj.width = '100% !important'
489
+ styleObj.height = ''
490
+ for (let j = i + 1; j < this.stack.length; j++) {
491
+ this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
492
+ }
493
+ } else if (style.includes('flex') && styleObj.width === '100%') {
494
+ for (let j = i + 1; j < this.stack.length; j++) {
495
+ const style = this.stack[j].attrs.style || ''
496
+ if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
497
+ styleObj.width = ''
498
+ break
499
+ }
500
+ }
501
+ } else if (style.includes('inline-block')) {
502
+ if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
503
+ item.attrs.style += ';max-width:' + styleObj.width
504
+ styleObj.width = ''
505
+ } else {
506
+ item.attrs.style += ';max-width:100%'
507
+ }
508
+ }
509
+ // #endif
510
+ item.c = 1
511
+ }
512
+ attrs.i = this.imgList.length.toString()
513
+ let src = attrs['original-src'] || attrs.src
514
+ // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
515
+ if (this.imgList.includes(src)) {
516
+ // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位
517
+ let i = src.indexOf('://')
518
+ if (i !== -1) {
519
+ i += 3
520
+ let newSrc = src.substr(0, i)
521
+ for (; i < src.length; i++) {
522
+ if (src[i] === '/') break
523
+ newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
524
+ }
525
+ newSrc += src.substr(i)
526
+ src = newSrc
527
+ }
528
+ }
529
+ // #endif
530
+ this.imgList.push(src)
531
+ if (!node.t) {
532
+ this.imgList._unloadimgs += 1
533
+ }
534
+ // #ifdef H5 || APP-PLUS
535
+ if (this.options.lazyLoad) {
536
+ attrs['data-src'] = attrs.src
537
+ attrs.src = undefined
538
+ }
539
+ // #endif
540
+ }
541
+ }
542
+ if (styleObj.display === 'inline') {
543
+ styleObj.display = ''
544
+ }
545
+ // #ifndef APP-PLUS-NVUE
546
+ if (attrs.ignore) {
547
+ styleObj['max-width'] = styleObj['max-width'] || '100%'
548
+ attrs.style += ';-webkit-touch-callout:none'
549
+ }
550
+ // #endif
551
+ // 设置的宽度超出屏幕,为避免变形,高度转为自动
552
+ if (parseInt(styleObj.width) > windowWidth) {
553
+ styleObj.height = undefined
554
+ }
555
+ // 记录是否设置了宽高
556
+ if (!isNaN(parseInt(styleObj.width))) {
557
+ node.w = 'T'
558
+ }
559
+ if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
560
+ node.h = 'T'
561
+ }
562
+ if (node.w && node.h && styleObj['object-fit']) {
563
+ if (styleObj['object-fit'] === 'contain') {
564
+ node.m = 'aspectFit'
565
+ } else if (styleObj['object-fit'] === 'cover') {
566
+ node.m = 'aspectFill'
567
+ }
568
+ }
569
+ } else if (node.name === 'svg') {
570
+ siblings.push(node)
571
+ this.stack.push(node)
572
+ this.popNode()
573
+ return
574
+ }
575
+ for (const key in styleObj) {
576
+ if (styleObj[key]) {
577
+ attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
578
+ }
579
+ }
580
+ attrs.style = attrs.style.substr(1) || undefined
581
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
582
+ if (!attrs.style) {
583
+ delete attrs.style
584
+ }
585
+ // #endif
586
+ } else {
587
+ if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
588
+ this.pre = node.pre = 1
589
+ }
590
+ node.children = []
591
+ this.stack.push(node)
592
+ }
593
+
594
+ // 加入节点树
595
+ siblings.push(node)
596
+ }
597
+
598
+ /**
599
+ * @description 解析到标签结束
600
+ * @param {String} name 标签名
601
+ * @private
602
+ */
603
+ Parser.prototype.onCloseTag = function (name) {
604
+ // 依次出栈到匹配为止
605
+ name = this.xml ? name : name.toLowerCase()
606
+ let i
607
+ for (i = this.stack.length; i--;) {
608
+ if (this.stack[i].name === name) break
609
+ }
610
+ if (i !== -1) {
611
+ while (this.stack.length > i) {
612
+ this.popNode()
613
+ }
614
+ } else if (name === 'p' || name === 'br') {
615
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
616
+ siblings.push({
617
+ name,
618
+ attrs: {
619
+ class: tagSelector[name] || '',
620
+ style: this.tagStyle[name] || ''
621
+ }
622
+ })
623
+ }
624
+ }
625
+
626
+ /**
627
+ * @description 处理标签出栈
628
+ * @private
629
+ */
630
+ Parser.prototype.popNode = function () {
631
+ const node = this.stack.pop()
632
+ let attrs = node.attrs
633
+ const children = node.children
634
+ const parent = this.stack[this.stack.length - 1]
635
+ const siblings = parent ? parent.children : this.nodes
636
+
637
+ if (!this.hook(node) || config.ignoreTags[node.name]) {
638
+ // 获取标题
639
+ if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
640
+ uni.setNavigationBarTitle({
641
+ title: children[0].text
642
+ })
643
+ }
644
+ siblings.pop()
645
+ return
646
+ }
647
+
648
+ if (node.pre && this.pre !== 2) {
649
+ // 是否合并空白符标识
650
+ this.pre = node.pre = undefined
651
+ for (let i = this.stack.length; i--;) {
652
+ if (this.stack[i].pre) {
653
+ this.pre = 1
654
+ }
655
+ }
656
+ }
657
+
658
+ const styleObj = {}
659
+
660
+ // 转换 svg
661
+ if (node.name === 'svg') {
662
+ if (this.xml > 1) {
663
+ // 多层 svg 嵌套
664
+ this.xml--
665
+ return
666
+ }
667
+ // #ifdef APP-PLUS-NVUE
668
+ (function traversal (node) {
669
+ if (node.name) {
670
+ // 调整 svg 的大小写
671
+ node.name = config.svgDict[node.name] || node.name
672
+ for (const item in node.attrs) {
673
+ if (config.svgDict[item]) {
674
+ node.attrs[config.svgDict[item]] = node.attrs[item]
675
+ node.attrs[item] = undefined
676
+ }
677
+ }
678
+ for (let i = 0; i < (node.children || []).length; i++) {
679
+ traversal(node.children[i])
680
+ }
681
+ }
682
+ })(node)
683
+ // #endif
684
+ // #ifndef APP-PLUS-NVUE
685
+ let src = ''
686
+ const style = attrs.style
687
+ attrs.style = ''
688
+ attrs.xmlns = 'http://www.w3.org/2000/svg';
689
+ (function traversal (node) {
690
+ if (node.type === 'text') {
691
+ src += node.text
692
+ return
693
+ }
694
+ const name = config.svgDict[node.name] || node.name
695
+ if (name === 'foreignObject') {
696
+ for (const child of (node.children || [])) {
697
+ if (child.attrs && !child.attrs.xmlns) {
698
+ child.attrs.xmlns = 'http://www.w3.org/1999/xhtml'
699
+ break
700
+ }
701
+ }
702
+ }
703
+ src += '<' + name
704
+ for (const item in node.attrs) {
705
+ const val = node.attrs[item]
706
+ if (val) {
707
+ src += ` ${config.svgDict[item] || item}="${val.replace(/"/g, '')}"`
708
+ }
709
+ }
710
+ if (!node.children) {
711
+ src += '/>'
712
+ } else {
713
+ src += '>'
714
+ for (let i = 0; i < node.children.length; i++) {
715
+ traversal(node.children[i])
716
+ }
717
+ src += '</' + name + '>'
718
+ }
719
+ })(node)
720
+ node.name = 'img'
721
+ node.attrs = {
722
+ src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
723
+ style,
724
+ ignore: 'T'
725
+ }
726
+ node.children = undefined
727
+ // #endif
728
+ this.xml = false
729
+ config.ignoreTags.style = true
730
+ return
731
+ }
732
+
733
+ // #ifndef APP-PLUS-NVUE
734
+ // 转换 align 属性
735
+ if (attrs.align) {
736
+ if (node.name === 'table') {
737
+ if (attrs.align === 'center') {
738
+ styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
739
+ } else {
740
+ styleObj.float = attrs.align
741
+ }
742
+ } else {
743
+ styleObj['text-align'] = attrs.align
744
+ }
745
+ attrs.align = undefined
746
+ }
747
+
748
+ // 转换 dir 属性
749
+ if (attrs.dir) {
750
+ styleObj.direction = attrs.dir
751
+ attrs.dir = undefined
752
+ }
753
+
754
+ // 转换 font 标签的属性
755
+ if (node.name === 'font') {
756
+ if (attrs.color) {
757
+ styleObj.color = attrs.color
758
+ attrs.color = undefined
759
+ }
760
+ if (attrs.face) {
761
+ styleObj['font-family'] = attrs.face
762
+ attrs.face = undefined
763
+ }
764
+ if (attrs.size) {
765
+ let size = parseInt(attrs.size)
766
+ if (!isNaN(size)) {
767
+ if (size < 1) {
768
+ size = 1
769
+ } else if (size > 7) {
770
+ size = 7
771
+ }
772
+ styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
773
+ }
774
+ attrs.size = undefined
775
+ }
776
+ }
777
+ // #endif
778
+
779
+ // 一些编辑器的自带 class
780
+ if ((attrs.class || '').includes('align-center')) {
781
+ styleObj['text-align'] = 'center'
782
+ }
783
+
784
+ Object.assign(styleObj, this.parseStyle(node))
785
+
786
+ if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
787
+ styleObj['max-width'] = '100%'
788
+ styleObj['box-sizing'] = 'border-box'
789
+ }
790
+
791
+ // #ifndef APP-PLUS-NVUE
792
+ if (config.blockTags[node.name]) {
793
+ node.name = 'div'
794
+ } else if (!config.trustTags[node.name] && !this.xml) {
795
+ // 未知标签转为 span,避免无法显示
796
+ node.name = 'span'
797
+ }
798
+
799
+ if (node.name === 'a' || node.name === 'ad'
800
+ // #ifdef H5 || APP-PLUS
801
+ || node.name === 'iframe' // eslint-disable-line
802
+ // #endif
803
+ ) {
804
+ this.expose()
805
+ } else if (node.name === 'video') {
806
+ if ((styleObj.height || '').includes('auto')) {
807
+ styleObj.height = undefined
808
+ }
809
+ /* #ifdef APP-PLUS */
810
+ let str = '<video style="width:100%;height:100%"'
811
+ for (const item in attrs) {
812
+ if (attrs[item]) {
813
+ str += ' ' + item + '="' + attrs[item] + '"'
814
+ }
815
+ }
816
+ if (this.options.pauseVideo) {
817
+ str += ' onplay="this.dispatchEvent(new CustomEvent(\'vplay\',{bubbles:!0}));for(var e=document.getElementsByTagName(\'video\'),t=0;t<e.length;t++)e[t]!=this&&e[t].pause()"'
818
+ }
819
+ str += '>'
820
+ for (let i = 0; i < node.src.length; i++) {
821
+ str += '<source src="' + node.src[i] + '">'
822
+ }
823
+ str += '</video>'
824
+ node.html = str
825
+ /* #endif */
826
+ } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
827
+ // 列表处理
828
+ const types = {
829
+ a: 'lower-alpha',
830
+ A: 'upper-alpha',
831
+ i: 'lower-roman',
832
+ I: 'upper-roman'
833
+ }
834
+ if (types[attrs.type]) {
835
+ attrs.style += ';list-style-type:' + types[attrs.type]
836
+ attrs.type = undefined
837
+ }
838
+ for (let i = children.length; i--;) {
839
+ if (children[i].name === 'li') {
840
+ children[i].c = 1
841
+ }
842
+ }
843
+ } else if (node.name === 'table') {
844
+ // 表格处理
845
+ // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现
846
+ let padding = parseFloat(attrs.cellpadding)
847
+ let spacing = parseFloat(attrs.cellspacing)
848
+ const border = parseFloat(attrs.border)
849
+ const bordercolor = styleObj['border-color']
850
+ const borderstyle = styleObj['border-style']
851
+ if (node.c) {
852
+ // padding 和 spacing 默认 2
853
+ if (isNaN(padding)) {
854
+ padding = 2
855
+ }
856
+ if (isNaN(spacing)) {
857
+ spacing = 2
858
+ }
859
+ }
860
+ if (border) {
861
+ attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
862
+ }
863
+ if (node.flag && node.c) {
864
+ // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
865
+ styleObj.display = 'grid'
866
+ if (styleObj['border-collapse'] === 'collapse') {
867
+ styleObj['border-collapse'] = undefined
868
+ spacing = 0
869
+ }
870
+ if (spacing) {
871
+ styleObj['grid-gap'] = spacing + 'px'
872
+ styleObj.padding = spacing + 'px'
873
+ } else if (border) {
874
+ // 无间隔的情况下避免边框重叠
875
+ attrs.style += ';border-left:0;border-top:0'
876
+ }
877
+
878
+ const width = [] // 表格的列宽
879
+ const trList = [] // tr 列表
880
+ const cells = [] // 保存新的单元格
881
+ const map = {}; // 被合并单元格占用的格子
882
+
883
+ (function traversal (nodes) {
884
+ for (let i = 0; i < nodes.length; i++) {
885
+ if (nodes[i].name === 'tr') {
886
+ trList.push(nodes[i])
887
+ } else if (nodes[i].name === 'colgroup') {
888
+ let colI = 1
889
+ for (const col of (nodes[i].children || [])) {
890
+ if (col.name === 'col') {
891
+ const style = col.attrs.style || ''
892
+ const start = style.indexOf('width') ? style.indexOf(';width') : 0
893
+ // 提取出宽度
894
+ if (start !== -1) {
895
+ let end = style.indexOf(';', start + 6)
896
+ if (end === -1) {
897
+ end = style.length
898
+ }
899
+ width[colI] = style.substring(start ? start + 7 : 6, end)
900
+ }
901
+ colI += 1
902
+ }
903
+ }
904
+ } else {
905
+ traversal(nodes[i].children || [])
906
+ }
907
+ }
908
+ })(children)
909
+
910
+ for (let row = 1; row <= trList.length; row++) {
911
+ let col = 1
912
+ for (let j = 0; j < trList[row - 1].children.length; j++) {
913
+ const td = trList[row - 1].children[j]
914
+ if (td.name === 'td' || td.name === 'th') {
915
+ // 这个格子被上面的单元格占用,则列号++
916
+ while (map[row + '.' + col]) {
917
+ col++
918
+ }
919
+ let style = td.attrs.style || ''
920
+ let start = style.indexOf('width') ? style.indexOf(';width') : 0
921
+ // 提取出 td 的宽度
922
+ if (start !== -1) {
923
+ let end = style.indexOf(';', start + 6)
924
+ if (end === -1) {
925
+ end = style.length
926
+ }
927
+ if (!td.attrs.colspan) {
928
+ width[col] = style.substring(start ? start + 7 : 6, end)
929
+ }
930
+ style = style.substr(0, start) + style.substr(end)
931
+ }
932
+ // 设置竖直对齐
933
+ style += ';display:flex'
934
+ start = style.indexOf('vertical-align')
935
+ if (start !== -1) {
936
+ const val = style.substr(start + 15, 10)
937
+ if (val.includes('middle')) {
938
+ style += ';align-items:center'
939
+ } else if (val.includes('bottom')) {
940
+ style += ';align-items:flex-end'
941
+ }
942
+ } else {
943
+ style += ';align-items:center'
944
+ }
945
+ // 设置水平对齐
946
+ start = style.indexOf('text-align')
947
+ if (start !== -1) {
948
+ const val = style.substr(start + 11, 10)
949
+ if (val.includes('center')) {
950
+ style += ';justify-content: center'
951
+ } else if (val.includes('right')) {
952
+ style += ';justify-content: right'
953
+ }
954
+ }
955
+ style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
956
+ // 处理列合并
957
+ if (td.attrs.colspan) {
958
+ style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
959
+ if (!td.attrs.rowspan) {
960
+ style += `;grid-row-start:${row};grid-row-end:${row + 1}`
961
+ }
962
+ col += parseInt(td.attrs.colspan) - 1
963
+ }
964
+ // 处理行合并
965
+ if (td.attrs.rowspan) {
966
+ style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
967
+ if (!td.attrs.colspan) {
968
+ style += `;grid-column-start:${col};grid-column-end:${col + 1}`
969
+ }
970
+ // 记录下方单元格被占用
971
+ for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
972
+ for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
973
+ map[(row + rowspan) + '.' + (col - colspan)] = 1
974
+ }
975
+ }
976
+ }
977
+ if (style) {
978
+ td.attrs.style = style
979
+ }
980
+ cells.push(td)
981
+ col++
982
+ }
983
+ }
984
+ if (row === 1) {
985
+ let temp = ''
986
+ for (let i = 1; i < col; i++) {
987
+ temp += (width[i] ? width[i] : 'auto') + ' '
988
+ }
989
+ styleObj['grid-template-columns'] = temp
990
+ }
991
+ }
992
+ node.children = cells
993
+ } else {
994
+ // 没有使用合并单元格的表格通过 table 布局实现
995
+ if (node.c) {
996
+ styleObj.display = 'table'
997
+ }
998
+ if (!isNaN(spacing)) {
999
+ styleObj['border-spacing'] = spacing + 'px'
1000
+ }
1001
+ if (border || padding) {
1002
+ // 遍历
1003
+ (function traversal (nodes) {
1004
+ for (let i = 0; i < nodes.length; i++) {
1005
+ const td = nodes[i]
1006
+ if (td.name === 'th' || td.name === 'td') {
1007
+ if (border) {
1008
+ td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
1009
+ }
1010
+ if (padding) {
1011
+ td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
1012
+ }
1013
+ } else if (td.children) {
1014
+ traversal(td.children)
1015
+ }
1016
+ }
1017
+ })(children)
1018
+ }
1019
+ }
1020
+ // 给表格添加一个单独的横向滚动层
1021
+ if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
1022
+ const table = Object.assign({}, node)
1023
+ node.name = 'div'
1024
+ node.attrs = {
1025
+ style: 'overflow:auto'
1026
+ }
1027
+ node.children = [table]
1028
+ attrs = table.attrs
1029
+ }
1030
+ } else if ((node.name === 'tbody' || node.name === 'tr') && node.flag && node.c) {
1031
+ node.flag = undefined;
1032
+ (function traversal (nodes) {
1033
+ for (let i = 0; i < nodes.length; i++) {
1034
+ if (nodes[i].name === 'td') {
1035
+ // 颜色样式设置给单元格避免丢失
1036
+ for (const style of ['color', 'background', 'background-color']) {
1037
+ if (styleObj[style]) {
1038
+ nodes[i].attrs.style = style + ':' + styleObj[style] + ';' + (nodes[i].attrs.style || '')
1039
+ }
1040
+ }
1041
+ } else {
1042
+ traversal(nodes[i].children || [])
1043
+ }
1044
+ }
1045
+ })(children)
1046
+ } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
1047
+ for (let i = this.stack.length; i--;) {
1048
+ if (this.stack[i].name === 'table' || this.stack[i].name === 'tbody' || this.stack[i].name === 'tr') {
1049
+ this.stack[i].flag = 1 // 指示含有合并单元格
1050
+ }
1051
+ }
1052
+ } else if (node.name === 'ruby') {
1053
+ // 转换 ruby
1054
+ node.name = 'span'
1055
+ for (let i = 0; i < children.length - 1; i++) {
1056
+ if (children[i].type === 'text' && children[i + 1].name === 'rt') {
1057
+ children[i] = {
1058
+ name: 'div',
1059
+ attrs: {
1060
+ style: 'display:inline-block;text-align:center'
1061
+ },
1062
+ children: [{
1063
+ name: 'div',
1064
+ attrs: {
1065
+ style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
1066
+ },
1067
+ children: children[i + 1].children
1068
+ }, children[i]]
1069
+ }
1070
+ children.splice(i + 1, 1)
1071
+ }
1072
+ }
1073
+ } else if (node.c) {
1074
+ (function traversal (node) {
1075
+ node.c = 2
1076
+ for (let i = node.children.length; i--;) {
1077
+ const child = node.children[i]
1078
+ // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
1079
+ if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
1080
+ traversal(child)
1081
+ }
1082
+ // #endif
1083
+ if (!child.c || child.name === 'table') {
1084
+ node.c = 1
1085
+ }
1086
+ }
1087
+ })(node)
1088
+ }
1089
+
1090
+ if ((styleObj.display || '').includes('flex') && !node.c) {
1091
+ for (let i = children.length; i--;) {
1092
+ const item = children[i]
1093
+ if (item.f) {
1094
+ item.attrs.style = (item.attrs.style || '') + item.f
1095
+ item.f = undefined
1096
+ }
1097
+ }
1098
+ }
1099
+ // flex 布局时部分样式需要提取到 rich-text 外层
1100
+ const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
1101
+ // #ifdef MP-WEIXIN
1102
+ // 检查基础库版本 virtualHost 是否可用
1103
+ && !(node.c && wx.getNFCAdapter) // eslint-disable-line
1104
+ // #endif
1105
+ // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
1106
+ && !node.c // eslint-disable-line
1107
+ // #endif
1108
+ if (flex) {
1109
+ node.f = ';max-width:100%'
1110
+ }
1111
+
1112
+ if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
1113
+ mergeNodes(children)
1114
+ }
1115
+ // #endif
1116
+
1117
+ for (const key in styleObj) {
1118
+ if (styleObj[key]) {
1119
+ const val = `;${key}:${styleObj[key].replace(' !important', '')}`
1120
+ /* #ifndef APP-PLUS-NVUE */
1121
+ if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
1122
+ node.f += val
1123
+ if (key === 'width') {
1124
+ attrs.style += ';width:100%'
1125
+ }
1126
+ } else /* #endif */ {
1127
+ attrs.style += val
1128
+ }
1129
+ }
1130
+ }
1131
+ attrs.style = attrs.style.substr(1) || undefined
1132
+ // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
1133
+ for (const key in attrs) {
1134
+ if (!attrs[key]) {
1135
+ delete attrs[key]
1136
+ }
1137
+ }
1138
+ // #endif
1139
+ }
1140
+
1141
+ /**
1142
+ * @description 解析到文本
1143
+ * @param {String} text 文本内容
1144
+ */
1145
+ Parser.prototype.onText = function (text) {
1146
+ if (!this.pre) {
1147
+ // 合并空白符
1148
+ let trim = ''
1149
+ let flag
1150
+ for (let i = 0, len = text.length; i < len; i++) {
1151
+ if (!blankChar[text[i]]) {
1152
+ trim += text[i]
1153
+ } else {
1154
+ if (trim[trim.length - 1] !== ' ') {
1155
+ trim += ' '
1156
+ }
1157
+ if (text[i] === '\n' && !flag) {
1158
+ flag = true
1159
+ }
1160
+ }
1161
+ }
1162
+ // 去除含有换行符的空串
1163
+ if (trim === ' ') {
1164
+ if (flag) return
1165
+ // #ifdef VUE3
1166
+ else {
1167
+ const parent = this.stack[this.stack.length - 1]
1168
+ if (parent && parent.name[0] === 't') return
1169
+ }
1170
+ // #endif
1171
+ }
1172
+ text = trim
1173
+ }
1174
+ const node = Object.create(null)
1175
+ node.type = 'text'
1176
+ // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
1177
+ node.attrs = {}
1178
+ // #endif
1179
+ node.text = decodeEntity(text)
1180
+ if (this.hook(node)) {
1181
+ // #ifdef MP-WEIXIN
1182
+ if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
1183
+ this.expose()
1184
+ }
1185
+ // #endif
1186
+ const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
1187
+ siblings.push(node)
1188
+ }
1189
+ }
1190
+
1191
+ /**
1192
+ * @description html 词法分析器
1193
+ * @param {Object} handler 高层处理器
1194
+ */
1195
+ function Lexer (handler) {
1196
+ this.handler = handler
1197
+ }
1198
+
1199
+ /**
1200
+ * @description 执行解析
1201
+ * @param {String} content 要解析的文本
1202
+ */
1203
+ Lexer.prototype.parse = function (content) {
1204
+ this.content = content || ''
1205
+ this.i = 0 // 标记解析位置
1206
+ this.start = 0 // 标记一个单词的开始位置
1207
+ this.state = this.text // 当前状态
1208
+ for (let len = this.content.length; this.i !== -1 && this.i < len;) {
1209
+ this.state()
1210
+ }
1211
+ }
1212
+
1213
+ /**
1214
+ * @description 检查标签是否闭合
1215
+ * @param {String} method 如果闭合要进行的操作
1216
+ * @returns {Boolean} 是否闭合
1217
+ * @private
1218
+ */
1219
+ Lexer.prototype.checkClose = function (method) {
1220
+ const selfClose = this.content[this.i] === '/'
1221
+ if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
1222
+ if (method) {
1223
+ this.handler[method](this.content.substring(this.start, this.i))
1224
+ }
1225
+ this.i += selfClose ? 2 : 1
1226
+ this.start = this.i
1227
+ this.handler.onOpenTag(selfClose)
1228
+ if (this.handler.tagName === 'script') {
1229
+ this.i = this.content.indexOf('</', this.i)
1230
+ if (this.i !== -1) {
1231
+ this.i += 2
1232
+ this.start = this.i
1233
+ }
1234
+ this.state = this.endTag
1235
+ } else {
1236
+ this.state = this.text
1237
+ }
1238
+ return true
1239
+ }
1240
+ return false
1241
+ }
1242
+
1243
+ /**
1244
+ * @description 文本状态
1245
+ * @private
1246
+ */
1247
+ Lexer.prototype.text = function () {
1248
+ this.i = this.content.indexOf('<', this.i) // 查找最近的标签
1249
+ if (this.i === -1) {
1250
+ // 没有标签了
1251
+ if (this.start < this.content.length) {
1252
+ this.handler.onText(this.content.substring(this.start, this.content.length))
1253
+ }
1254
+ return
1255
+ }
1256
+ const c = this.content[this.i + 1]
1257
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
1258
+ // 标签开头
1259
+ if (this.start !== this.i) {
1260
+ this.handler.onText(this.content.substring(this.start, this.i))
1261
+ }
1262
+ this.start = ++this.i
1263
+ this.state = this.tagName
1264
+ } else if (c === '/' || c === '!' || c === '?') {
1265
+ if (this.start !== this.i) {
1266
+ this.handler.onText(this.content.substring(this.start, this.i))
1267
+ }
1268
+ const next = this.content[this.i + 2]
1269
+ if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
1270
+ // 标签结尾
1271
+ this.i += 2
1272
+ this.start = this.i
1273
+ this.state = this.endTag
1274
+ return
1275
+ }
1276
+ // 处理注释
1277
+ let end = '-->'
1278
+ if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
1279
+ end = '>'
1280
+ }
1281
+ this.i = this.content.indexOf(end, this.i)
1282
+ if (this.i !== -1) {
1283
+ this.i += end.length
1284
+ this.start = this.i
1285
+ }
1286
+ } else {
1287
+ this.i++
1288
+ }
1289
+ }
1290
+
1291
+ /**
1292
+ * @description 标签名状态
1293
+ * @private
1294
+ */
1295
+ Lexer.prototype.tagName = function () {
1296
+ if (blankChar[this.content[this.i]]) {
1297
+ // 解析到标签名
1298
+ this.handler.onTagName(this.content.substring(this.start, this.i))
1299
+ while (blankChar[this.content[++this.i]]);
1300
+ if (this.i < this.content.length && !this.checkClose()) {
1301
+ this.start = this.i
1302
+ this.state = this.attrName
1303
+ }
1304
+ } else if (!this.checkClose('onTagName')) {
1305
+ this.i++
1306
+ }
1307
+ }
1308
+
1309
+ /**
1310
+ * @description 属性名状态
1311
+ * @private
1312
+ */
1313
+ Lexer.prototype.attrName = function () {
1314
+ let c = this.content[this.i]
1315
+ if (blankChar[c] || c === '=') {
1316
+ // 解析到属性名
1317
+ this.handler.onAttrName(this.content.substring(this.start, this.i))
1318
+ let needVal = c === '='
1319
+ const len = this.content.length
1320
+ while (++this.i < len) {
1321
+ c = this.content[this.i]
1322
+ if (!blankChar[c]) {
1323
+ if (this.checkClose()) return
1324
+ if (needVal) {
1325
+ // 等号后遇到第一个非空字符
1326
+ this.start = this.i
1327
+ this.state = this.attrVal
1328
+ return
1329
+ }
1330
+ if (this.content[this.i] === '=') {
1331
+ needVal = true
1332
+ } else {
1333
+ this.start = this.i
1334
+ this.state = this.attrName
1335
+ return
1336
+ }
1337
+ }
1338
+ }
1339
+ } else if (!this.checkClose('onAttrName')) {
1340
+ this.i++
1341
+ }
1342
+ }
1343
+
1344
+ /**
1345
+ * @description 属性值状态
1346
+ * @private
1347
+ */
1348
+ Lexer.prototype.attrVal = function () {
1349
+ const c = this.content[this.i]
1350
+ const len = this.content.length
1351
+ if (c === '"' || c === "'") {
1352
+ // 有冒号的属性
1353
+ this.start = ++this.i
1354
+ this.i = this.content.indexOf(c, this.i)
1355
+ if (this.i === -1) return
1356
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
1357
+ } else {
1358
+ // 没有冒号的属性
1359
+ for (; this.i < len; this.i++) {
1360
+ if (blankChar[this.content[this.i]]) {
1361
+ this.handler.onAttrVal(this.content.substring(this.start, this.i))
1362
+ break
1363
+ } else if (this.checkClose('onAttrVal')) return
1364
+ }
1365
+ }
1366
+ while (blankChar[this.content[++this.i]]);
1367
+ if (this.i < len && !this.checkClose()) {
1368
+ this.start = this.i
1369
+ this.state = this.attrName
1370
+ }
1371
+ }
1372
+
1373
+ /**
1374
+ * @description 结束标签状态
1375
+ * @returns {String} 结束的标签名
1376
+ * @private
1377
+ */
1378
+ Lexer.prototype.endTag = function () {
1379
+ const c = this.content[this.i]
1380
+ if (blankChar[c] || c === '>' || c === '/') {
1381
+ this.handler.onCloseTag(this.content.substring(this.start, this.i))
1382
+ if (c !== '>') {
1383
+ this.i = this.content.indexOf('>', this.i)
1384
+ if (this.i === -1) return
1385
+ }
1386
+ this.start = ++this.i
1387
+ this.state = this.text
1388
+ } else {
1389
+ this.i++
1390
+ }
1391
+ }
1392
+
1393
+ export default Parser