uview-plus 3.1.23 → 3.1.25

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