EP-WachatApp/eaterplanet_ecommerce/components/parser/parser.js

204 lines
6.1 KiB
JavaScript

/**
* Parser 富文本组件
* @tutorial https://github.com/jin-yufeng/Parser
* @version 20200630
* @author JinYufeng
* @listens MIT
*/
var cache = {},
Parser = require('./libs/MpHtmlParser.js'),
fs = wx.getFileSystemManager && wx.getFileSystemManager();
var dom;
// 计算 cache 的 key
function hash(str) {
for (var i = str.length, val = 5381; i--;)
val += (val << 5) + str.charCodeAt(i);
return val;
}
Component({
options: {
pureDataPattern: /^[acdgtu]|W/
},
data: {
nodes: []
},
properties: {
html: {
type: String,
observer(html) {
this.setContent(html);
}
},
autopause: {
type: Boolean,
value: true
},
autoscroll: Boolean,
autosetTitle: {
type: Boolean,
value: true
},
compress: Number,
domain: String,
lazyLoad: Boolean,
loadingImg: String,
selectable: Boolean,
tagStyle: Object,
showWithAnimation: Boolean,
useAnchor: Boolean,
useCache: Boolean
},
relations: {
'../parser-group/parser-group': {
type: 'ancestor'
}
},
created() {
// 图片数组
this.imgList = [];
this.imgList.setItem = function(i, src) {
if (!i || !src) return;
// 去重
if (src.indexOf('http') == 0 && this.includes(src)) {
var newSrc = '';
for (var j = 0, c; c = src[j]; j++) {
if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
}
newSrc += src.substr(j);
return this[i] = newSrc;
}
this[i] = src;
// 暂存 data src
if (src.includes('data:image')) {
var info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
if (!info) return;
var filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
fs && fs.writeFile({
filePath,
data: info[3],
encoding: info[2],
success: () => this[i] = filePath
})
}
}
this.imgList.each = function(f) {
for (var i = 0, len = this.length; i < len; i++)
this.setItem(i, f(this[i], i, this));
}
if (dom) this.document = new dom(this);
},
detached() {
// 删除暂存
this.imgList.each(src => {
if (src && src.includes(wx.env.USER_DATA_PATH) && fs)
fs.unlink({
filePath: src
})
})
clearInterval(this._timer);
},
methods: {
// 锚点跳转
navigateTo(obj) {
if (!this.data.useAnchor)
return obj.fail && obj.fail({
errMsg: 'Anchor is disabled'
})
this.createSelectorQuery()
.select('.top' + (obj.id ? '>>>#' + obj.id : '')).boundingClientRect()
.selectViewport().scrollOffset().exec(res => {
if (!res[0])
return this.group ? this.group.navigateTo(this.i, obj) :
obj.fail && obj.fail({
errMsg: 'Label not found'
});
obj.scrollTop = res[1].scrollTop + res[0].top + (obj.offset || 0);
wx.pageScrollTo(obj);
})
},
// 获取文本
getText(ns = this.data.html) {
var txt = '';
for (var i = 0, n; n = ns[i++];) {
if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
else if (n.type == 'br') txt += '\n';
else {
// 块级标签前后加换行
var br = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] > '0' && n.name[1] < '7');
if (br && txt && txt[txt.length - 1] != '\n') txt += '\n';
if (n.children) txt += this.getText(n.children);
if (br && txt[txt.length - 1] != '\n') txt += '\n';
else if (n.name == 'td' || n.name == 'th') txt += '\t';
}
}
return txt;
},
// 获取视频 context
getVideoContext(id) {
if (!id) return this.videoContexts;
for (var i = this.videoContexts.length; i--;)
if (this.videoContexts[i].id == id) return this.videoContexts[i];
},
// 渲染富文本
setContent(html, append) {
var nodes, parser = new Parser(html, this.data);
// 缓存读取
if (this.data.useCache) {
var hashVal = hash(html);
if (cache[hashVal]) nodes = cache[hashVal];
else cache[hashVal] = nodes = parser.parse();
} else nodes = parser.parse();
this.triggerEvent('parse', nodes);
var data = {};
if (append)
for (let i = this.data.nodes.length, j = nodes.length; j--;)
data[`nodes[${i + j}]`] = nodes[j];
else data.nodes = nodes;
if (this.showWithAnimation) data.showAm = 'animation: show .5s';
this.setData(data, () => {
this.triggerEvent('load')
});
// 设置标题
if (nodes.title && this.data.autosetTitle)
wx.setNavigationBarTitle({
title: nodes.title
})
this.imgList.length = 0;
this.videoContexts = [];
var ns = this.selectAllComponents('.top,.top>>>._node');
for (let i = 0, n; n = ns[i++];) {
n.top = this;
for (let j = 0, item; item = n.data.nodes[j++];) {
if (item.c) continue;
// 获取图片列表
if (item.name == 'img')
this.imgList.setItem(item.attrs.i, item.attrs.src);
// 音视频控制
else if (item.name == 'video' || item.name == 'audio') {
var ctx;
if (item.name == 'video') ctx = wx.createVideoContext(item.attrs.id, n);
else ctx = n.selectComponent('#' + item.attrs.id);
if (ctx) {
ctx.id = item.attrs.id;
this.videoContexts.push(ctx);
}
}
}
}
var height;
clearInterval(this._timer);
this._timer = setInterval(() => {
this.createSelectorQuery().select('.top').boundingClientRect(res => {
if (!res) return;
this.rect = res;
if (res.height == height) {
this.triggerEvent('ready', res)
clearInterval(this._timer);
}
height = res.height;
}).exec();
}, 350)
}
}
})