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