@tencentcloud/ai-desk-customer-vue 0.1.3 → 0.1.4

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