请求解析
This commit is contained in:
1 parent
0b9c772cfc
commit
bb18cb696a
14 files changed
+1241
No files matched your search
@@ -0,0 +1,114 @@
|
|||||||
|
//CssTokenizer.js
|
||||||
|
function CssTokenizer(style = '', tagStyle = {}) {
|
||||||
|
this.res = tagStyle;
|
||||||
|
this._state = "SPACE";
|
||||||
|
this._buffer = style;
|
||||||
|
this._sectionStart = 0;
|
||||||
|
this._index = 0;
|
||||||
|
this._name = '';
|
||||||
|
this._content = '';
|
||||||
|
this._list = [];
|
||||||
|
this._comma = false;
|
||||||
|
}
|
||||||
|
CssTokenizer.prototype.SPACE = function(c) {
|
||||||
|
if (/[a-zA-Z.#]/.test(c)) {
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
this._state = "InName";
|
||||||
|
} else if (c == '@') this._state = "Ignore1";
|
||||||
|
else if (c == '/') this._state = "BeforeComment";
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.BeforeComment = function(c) {
|
||||||
|
if (c == '*') this._state = "InComment";
|
||||||
|
else {
|
||||||
|
this._index--;
|
||||||
|
this._state = "SPACE";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.InComment = function(c) {
|
||||||
|
if (c == '*') this._state = "AfterComment";
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.AfterComment = function(c) {
|
||||||
|
if (c == '/') this._state = "SPACE";
|
||||||
|
else {
|
||||||
|
this._index--;
|
||||||
|
this._state = "InComment"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.InName = function(c) {
|
||||||
|
if (c == '{') {
|
||||||
|
this._list.push(this._buffer.substring(this._sectionStart, this._index))
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
this._state = "InContent";
|
||||||
|
} else if (c == ',') {
|
||||||
|
this._list.push(this._buffer.substring(this._sectionStart, this._index));
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
this._comma = true;
|
||||||
|
} else if ((c == '.' || c == '#') && !this._comma) {
|
||||||
|
this._buffer = this._buffer.splice(this._index, 1, ' ');
|
||||||
|
} else if (/\s/.test(c)) {
|
||||||
|
this._name = this._buffer.substring(this._sectionStart, this._index);
|
||||||
|
this._state = "NameSpace";
|
||||||
|
} else if (/[>:\[]/.test(c)) {
|
||||||
|
if (this._list.length) this._state = "IgnoreName";
|
||||||
|
else this._state = "Ignore1";
|
||||||
|
} else this._comma = false;
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.NameSpace = function(c) {
|
||||||
|
if (c == '{') {
|
||||||
|
this._list.push(this._name);
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
this._state = "InContent";
|
||||||
|
} else if (c == ',') {
|
||||||
|
this._comma = true;
|
||||||
|
this._list.push(this._name);
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
this._state = "InName"
|
||||||
|
} else if (/\S/.test(c)) {
|
||||||
|
if (this._comma) {
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
this._index--;
|
||||||
|
this._state = "InName";
|
||||||
|
} else if (this._list.length) this._state = "IgnoreName";
|
||||||
|
else this._state = "Ignore1"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.InContent = function(c) {
|
||||||
|
if (c == '}') {
|
||||||
|
this._content = this._buffer.substring(this._sectionStart, this._index);
|
||||||
|
for (let item of this._list)
|
||||||
|
this.res[item] = (this.res[item] || '') + this._content;
|
||||||
|
this._list = [];
|
||||||
|
this._comma = false;
|
||||||
|
this._state = "SPACE";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.IgnoreName = function(c) {
|
||||||
|
if (c == ',') {
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
this._state = "InName";
|
||||||
|
} else if (c == '{') {
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
this._state = "InContent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CssTokenizer.prototype.Ignore1 = function(c) {
|
||||||
|
if (c == ';') {
|
||||||
|
this._state = "SPACE";
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else if (c == '{') this._state = "Ignore2";
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.Ignore2 = function(c) {
|
||||||
|
if (c == '}') {
|
||||||
|
this._state = "SPACE";
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else if (c == '{') this._state = "Ignore3";
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.Ignore3 = function(c) {
|
||||||
|
if (c == '}') this._state = "Ignore2";
|
||||||
|
};
|
||||||
|
CssTokenizer.prototype.parse = function() {
|
||||||
|
for (; this._index < this._buffer.length; this._index++)
|
||||||
|
this[this._state](this._buffer[this._index]);
|
||||||
|
return this.res;
|
||||||
|
};
|
||||||
|
module.exports = CssTokenizer;
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
//DomHandler.js
|
||||||
|
const CssTokenizer = require('./CssTokenizer.js');
|
||||||
|
const CanIUse = require('./api.js').versionHigherThan('2.7.1');
|
||||||
|
const Common = 1,
|
||||||
|
Rich = 2;
|
||||||
|
const trustTag = {
|
||||||
|
a: Rich,
|
||||||
|
abbr: Common,
|
||||||
|
audio: Rich,
|
||||||
|
b: Common,
|
||||||
|
blockquote: Common,
|
||||||
|
br: Rich,
|
||||||
|
code: Common,
|
||||||
|
col: Rich,
|
||||||
|
colgroup: Rich,
|
||||||
|
dd: Common,
|
||||||
|
del: Common,
|
||||||
|
dl: Common,
|
||||||
|
dt: Common,
|
||||||
|
div: Common,
|
||||||
|
em: Common,
|
||||||
|
fieldset: Rich,
|
||||||
|
font: Common,
|
||||||
|
h1: Rich,
|
||||||
|
h2: Rich,
|
||||||
|
h3: Rich,
|
||||||
|
h4: Rich,
|
||||||
|
h5: Rich,
|
||||||
|
h6: Rich,
|
||||||
|
hr: Rich,
|
||||||
|
i: Common,
|
||||||
|
img: Common,
|
||||||
|
ins: Common,
|
||||||
|
label: Common,
|
||||||
|
legend: Rich,
|
||||||
|
li: Rich,
|
||||||
|
ol: Rich,
|
||||||
|
p: Common,
|
||||||
|
q: Common,
|
||||||
|
source: Rich,
|
||||||
|
span: Common,
|
||||||
|
strong: Common,
|
||||||
|
sub: Rich,
|
||||||
|
sup: Rich,
|
||||||
|
table: Rich,
|
||||||
|
tbody: Rich,
|
||||||
|
td: Rich,
|
||||||
|
tfoot: Rich,
|
||||||
|
th: Rich,
|
||||||
|
thead: Rich,
|
||||||
|
tr: Rich,
|
||||||
|
u: Common,
|
||||||
|
ul: Rich,
|
||||||
|
video: Common
|
||||||
|
};
|
||||||
|
const blockTag = {
|
||||||
|
address: true,
|
||||||
|
article: true,
|
||||||
|
aside: true,
|
||||||
|
body: true,
|
||||||
|
center: true,
|
||||||
|
cite: true,
|
||||||
|
footer: true,
|
||||||
|
header: true,
|
||||||
|
html: true,
|
||||||
|
nav: true,
|
||||||
|
pre: true,
|
||||||
|
section: true
|
||||||
|
}
|
||||||
|
const textTag = {
|
||||||
|
a: true,
|
||||||
|
abbr: true,
|
||||||
|
b: true,
|
||||||
|
big: true,
|
||||||
|
code: true,
|
||||||
|
del: true,
|
||||||
|
em: true,
|
||||||
|
font: true,
|
||||||
|
i: true,
|
||||||
|
ins: true,
|
||||||
|
label: true,
|
||||||
|
mark: true,
|
||||||
|
q: true,
|
||||||
|
s: true,
|
||||||
|
small: true,
|
||||||
|
span: true,
|
||||||
|
strong: true,
|
||||||
|
u: true
|
||||||
|
};
|
||||||
|
const ignoreTag = {
|
||||||
|
area: true,
|
||||||
|
base: true,
|
||||||
|
basefont: true,
|
||||||
|
canvas: true,
|
||||||
|
circle: true,
|
||||||
|
command: true,
|
||||||
|
ellipse: true,
|
||||||
|
embed: true,
|
||||||
|
frame: true,
|
||||||
|
head: true,
|
||||||
|
iframe: true,
|
||||||
|
input: true,
|
||||||
|
isindex: true,
|
||||||
|
keygen: true,
|
||||||
|
line: true,
|
||||||
|
link: true,
|
||||||
|
map: true,
|
||||||
|
meta: true,
|
||||||
|
param: true,
|
||||||
|
path: true,
|
||||||
|
polygon: true,
|
||||||
|
polyline: true,
|
||||||
|
rect: true,
|
||||||
|
script: true,
|
||||||
|
stop: true,
|
||||||
|
textarea: true,
|
||||||
|
title: true,
|
||||||
|
track: true,
|
||||||
|
use: true,
|
||||||
|
wbr: true
|
||||||
|
};
|
||||||
|
if (CanIUse) {
|
||||||
|
trustTag.bdi = Rich;
|
||||||
|
trustTag.bdo = Rich;
|
||||||
|
trustTag.caption = Rich;
|
||||||
|
trustTag.rt = Rich;
|
||||||
|
trustTag.ruby = Rich;
|
||||||
|
ignoreTag.rp = true;
|
||||||
|
trustTag.big = Common;
|
||||||
|
trustTag.small = Common;
|
||||||
|
trustTag.pre = Rich;
|
||||||
|
delete blockTag.pre;
|
||||||
|
}
|
||||||
|
//添加默认值
|
||||||
|
function initStyle(tagStyle) {
|
||||||
|
tagStyle.a = "display:inline;color:#366092;word-break:break-all;" + (tagStyle.a || "");
|
||||||
|
tagStyle.address = "font-style:italic;" + (tagStyle.address || "");
|
||||||
|
tagStyle.blockquote = tagStyle.blockquote || 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px';
|
||||||
|
tagStyle.center = 'text-align:center;' + (tagStyle.center || "");
|
||||||
|
tagStyle.cite = "font-style:italic;" + (tagStyle.cite || "");
|
||||||
|
tagStyle.code = tagStyle.code || 'padding:0 1px 0 1px;margin-left:2px;margin-right:2px;background-color:#f8f8f8;border:1px solid #cccccc;border-radius:3px';
|
||||||
|
tagStyle.dd = "margin-left:40px;" + (tagStyle.dd || "");
|
||||||
|
tagStyle.img = "max-width:100%;" + (tagStyle.img || "");
|
||||||
|
tagStyle.mark = "display:inline;background-color:yellow;" + (tagStyle.mark || "");
|
||||||
|
tagStyle.pre = "overflow:scroll;" + (tagStyle.pre || 'background-color:#f6f8fa;padding:5px;border-radius:5px;');
|
||||||
|
tagStyle.s = "display:inline;text-decoration:line-through;" + (tagStyle.s || "");
|
||||||
|
tagStyle.u = "display:inline;text-decoration:underline;" + (tagStyle.u || "");
|
||||||
|
//低版本兼容
|
||||||
|
if (!CanIUse) {
|
||||||
|
blockTag.caption = true;
|
||||||
|
tagStyle.big = "display:inline;font-size:1.2em;" + (tagStyle.big || "");
|
||||||
|
tagStyle.small = "display:inline;font-size:0.8em;" + (tagStyle.small || "");
|
||||||
|
tagStyle.pre = "font-family:monospace;white-space:pre;" + tagStyle.pre;
|
||||||
|
}
|
||||||
|
return tagStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DomHandler(style, tagStyle = {}) {
|
||||||
|
this.imgList = [];
|
||||||
|
this.nodes = [];
|
||||||
|
this.title = "";
|
||||||
|
this._videoNum = 0;
|
||||||
|
this._audioNum = 0;
|
||||||
|
this._style = new CssTokenizer(style, initStyle(tagStyle)).parse();
|
||||||
|
this._tagStack = [];
|
||||||
|
}
|
||||||
|
DomHandler.prototype._addDomElement = function(element) {
|
||||||
|
let parent = this._tagStack[this._tagStack.length - 1];
|
||||||
|
let siblings = parent ? parent.children : this.nodes;
|
||||||
|
siblings.push(element);
|
||||||
|
};
|
||||||
|
DomHandler.prototype._bubbling = function() {
|
||||||
|
for (let i = this._tagStack.length - 1; i >= 0; i--) {
|
||||||
|
if (trustTag[this._tagStack[i].name] == Common) this._tagStack[i].continue = true;
|
||||||
|
else return this._tagStack[i].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DomHandler.prototype.onopentag = function(name, attrs) {
|
||||||
|
let element = {
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
//匹配样式
|
||||||
|
let matched = this._style[name] ? (this._style[name] + ';') : '';
|
||||||
|
if (attrs.id) {
|
||||||
|
matched += (this._style['#' + attrs.id] ? (this._style['#' + attrs.id] + ';') : '');
|
||||||
|
delete attrs.id;
|
||||||
|
}
|
||||||
|
if (attrs.class) {
|
||||||
|
for (var Class of attrs.class.split(' ')) {
|
||||||
|
matched += (this._style['.' + Class] ? (this._style['.' + Class] + ';') : '');
|
||||||
|
}
|
||||||
|
delete attrs.class;
|
||||||
|
}
|
||||||
|
//处理属性
|
||||||
|
switch (name) {
|
||||||
|
case 'div':
|
||||||
|
case 'p':
|
||||||
|
if (attrs.align) {
|
||||||
|
attrs.style += (';text-align:' + attrs.align);
|
||||||
|
delete attrs.align;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'img':
|
||||||
|
if (attrs.width) {
|
||||||
|
attrs.style = 'width:' + attrs.width + (/[0-9]/.test(attrs.width[attrs.width.length - 1]) ? 'px' : '') + ';' + attrs.style;
|
||||||
|
delete attrs.width;
|
||||||
|
}
|
||||||
|
if (attrs['data-src']) {
|
||||||
|
attrs.src = attrs.src || attrs['data-src'];
|
||||||
|
delete attrs['data-src'];
|
||||||
|
}
|
||||||
|
if (!attrs.hasOwnProperty('ignore') && attrs.src) {
|
||||||
|
this.imgList.push(attrs.src);
|
||||||
|
if (this._bubbling() == 'a') attrs.ignore = "";
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'font':
|
||||||
|
name = 'span';
|
||||||
|
if (attrs.color) {
|
||||||
|
attrs.style += (';color:' + attrs.color);
|
||||||
|
delete attrs.color;
|
||||||
|
}
|
||||||
|
if (attrs.face) {
|
||||||
|
attrs.style += (";font-family:" + attrs.face);
|
||||||
|
delete attrs.face;
|
||||||
|
}
|
||||||
|
if (attrs.size) {
|
||||||
|
var size = parseInt(attrs.size);
|
||||||
|
if (size < 1) size = 1;
|
||||||
|
else if (size > 7) size = 7;
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
size = 10;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
size = 13;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
size = 16;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
size = 18;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
size = 24;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
size = 32;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
size = 48;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
attrs.style += (";font-size:" + size + "px");
|
||||||
|
delete attrs.size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
this._bubbling();
|
||||||
|
break;
|
||||||
|
case 'video':
|
||||||
|
case 'audio':
|
||||||
|
attrs.loop = attrs.hasOwnProperty('loop');
|
||||||
|
attrs.controls = attrs.hasOwnProperty('controls');
|
||||||
|
attrs.autoplay = attrs.hasOwnProperty('autoplay');
|
||||||
|
if (name == 'video') {
|
||||||
|
attrs.muted = attrs.hasOwnProperty('muted');
|
||||||
|
if (attrs.width) {
|
||||||
|
attrs.style = 'width:' + parseFloat(attrs.width) + 'px;' + attrs.style;
|
||||||
|
delete attrs.width;
|
||||||
|
}
|
||||||
|
if (attrs.height) {
|
||||||
|
attrs.style = 'height:' + parseFloat(attrs.height) + 'px;' + attrs.style;
|
||||||
|
delete attrs.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attrs.id = (name + (++this['_' + name + 'Num']));
|
||||||
|
attrs.source = [];
|
||||||
|
if (attrs.src) attrs.source.push(attrs.src);
|
||||||
|
if (!attrs.controls && !attrs.autoplay)
|
||||||
|
console.warn('存在没有controls属性的' + name + '标签,可能导致无法播放', attrs);
|
||||||
|
this._bubbling();
|
||||||
|
break;
|
||||||
|
case 'source':
|
||||||
|
let parent = this._tagStack[this._tagStack.length - 1];
|
||||||
|
if (parent && (parent.name == 'video' || parent.name == 'audio')) {
|
||||||
|
parent.attrs.source.push(attrs.src);
|
||||||
|
if (!parent.attrs.src) parent.attrs.src = attrs.src;
|
||||||
|
}
|
||||||
|
this._tagStack.push(element);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
attrs.style = matched + attrs.style;
|
||||||
|
if (textTag[name]) element.continue = true;
|
||||||
|
else if (blockTag[name]) name = 'div';
|
||||||
|
else if (!trustTag[name]) name = 'span';
|
||||||
|
element.name = name;
|
||||||
|
element.attrs = attrs;
|
||||||
|
this._addDomElement(element);
|
||||||
|
this._tagStack.push(element);
|
||||||
|
};
|
||||||
|
DomHandler.prototype.ontext = function(data) {
|
||||||
|
if (/\S/.test(data)) {
|
||||||
|
let element = {
|
||||||
|
text: data.replace(/ /g, '\u00A0'),
|
||||||
|
type: 'text'
|
||||||
|
};
|
||||||
|
if (/&#*((?!sp|lt|gt).){2,5};/.test(data)) element.decode = true;
|
||||||
|
this._addDomElement(element);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
DomHandler.prototype.onclosetag = function(name) {
|
||||||
|
let element = this._tagStack.pop();
|
||||||
|
if (ignoreTag[name]) {
|
||||||
|
if (name == 'title') {
|
||||||
|
try {
|
||||||
|
this.title = element.children[0].text;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
let parent = this._tagStack[this._tagStack.length - 1];
|
||||||
|
let siblings = parent ? parent.children : this.nodes;
|
||||||
|
siblings.pop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
module.exports = DomHandler;
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
//Parser.js
|
||||||
|
const Tokenizer = require("./Tokenizer.js");
|
||||||
|
const DomHandler = require("./DomHandler.js");
|
||||||
|
const trustAttrs = {
|
||||||
|
align: true,
|
||||||
|
alt: true,
|
||||||
|
author: true,
|
||||||
|
autoplay: true,
|
||||||
|
class: true,
|
||||||
|
color: true,
|
||||||
|
colspan: true,
|
||||||
|
controls: true,
|
||||||
|
"data-src": true,
|
||||||
|
dir: true,
|
||||||
|
face: true,
|
||||||
|
height: true,
|
||||||
|
href: true,
|
||||||
|
id: true,
|
||||||
|
ignore: true,
|
||||||
|
loop: true,
|
||||||
|
muted: true,
|
||||||
|
name: true,
|
||||||
|
poster: true,
|
||||||
|
rowspan: true,
|
||||||
|
size: true,
|
||||||
|
span: true,
|
||||||
|
src: true,
|
||||||
|
start: true,
|
||||||
|
style: true,
|
||||||
|
type: true,
|
||||||
|
width: true,
|
||||||
|
};
|
||||||
|
const voidTag = {
|
||||||
|
area: true,
|
||||||
|
base: true,
|
||||||
|
basefont: true,
|
||||||
|
br: true,
|
||||||
|
col: true,
|
||||||
|
circle: true,
|
||||||
|
command: true,
|
||||||
|
ellipse: true,
|
||||||
|
embed: true,
|
||||||
|
frame: true,
|
||||||
|
hr: true,
|
||||||
|
img: true,
|
||||||
|
input: true,
|
||||||
|
isindex: true,
|
||||||
|
keygen: true,
|
||||||
|
line: true,
|
||||||
|
link: true,
|
||||||
|
meta: true,
|
||||||
|
param: true,
|
||||||
|
path: true,
|
||||||
|
polygon: true,
|
||||||
|
polyline: true,
|
||||||
|
rect: true,
|
||||||
|
source: true,
|
||||||
|
stop: true,
|
||||||
|
track: true,
|
||||||
|
use: true,
|
||||||
|
wbr: true
|
||||||
|
};
|
||||||
|
|
||||||
|
function Parser(cbs, callback) {
|
||||||
|
this._cbs = cbs;
|
||||||
|
this._callback = callback;
|
||||||
|
this._tagname = "";
|
||||||
|
this._attribname = "";
|
||||||
|
this._attribvalue = "";
|
||||||
|
this._attribs = null;
|
||||||
|
this._stack = [];
|
||||||
|
this._tokenizer = new Tokenizer(this);
|
||||||
|
}
|
||||||
|
Parser.prototype.ontext = function(data) {
|
||||||
|
this._cbs.ontext(data);
|
||||||
|
};
|
||||||
|
Parser.prototype.onopentagname = function(name) {
|
||||||
|
name = name.toLowerCase();
|
||||||
|
this._tagname = name;
|
||||||
|
this._attribs = {
|
||||||
|
style: ''
|
||||||
|
};
|
||||||
|
if (!voidTag[name]) this._stack.push(name);
|
||||||
|
};
|
||||||
|
Parser.prototype.onopentagend = function() {
|
||||||
|
if (this._attribs) {
|
||||||
|
this._cbs.onopentag(this._tagname, this._attribs);
|
||||||
|
this._attribs = null;
|
||||||
|
}
|
||||||
|
if (voidTag[this._tagname]) this._cbs.onclosetag(this._tagname);
|
||||||
|
this._tagname = "";
|
||||||
|
};
|
||||||
|
Parser.prototype.onclosetag = function(name) {
|
||||||
|
name = name.toLowerCase();
|
||||||
|
if (this._stack.length && !voidTag[name]) {
|
||||||
|
var pos = this._stack.lastIndexOf(name);
|
||||||
|
if (pos !== -1) {
|
||||||
|
pos = this._stack.length - pos;
|
||||||
|
while (pos--) this._cbs.onclosetag(this._stack.pop());
|
||||||
|
} else if (name === "p") {
|
||||||
|
this.onopentagname(name);
|
||||||
|
this._closeCurrentTag();
|
||||||
|
}
|
||||||
|
} else if (name === "br" || name === "hr" || name === "p") {
|
||||||
|
this.onopentagname(name);
|
||||||
|
this._closeCurrentTag();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Parser.prototype._closeCurrentTag = function() {
|
||||||
|
let name = this._tagname;
|
||||||
|
this.onopentagend();
|
||||||
|
if (this._stack[this._stack.length - 1] === name) {
|
||||||
|
this._cbs.onclosetag(name);
|
||||||
|
this._stack.pop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Parser.prototype.onattribend = function() {
|
||||||
|
this._attribvalue = this._attribvalue.replace(/"/g, '"');
|
||||||
|
if (this._attribs && trustAttrs[this._attribname]) {
|
||||||
|
this._attribs[this._attribname] = this._attribvalue;
|
||||||
|
}
|
||||||
|
this._attribname = "";
|
||||||
|
this._attribvalue = "";
|
||||||
|
};
|
||||||
|
Parser.prototype.onend = function() {
|
||||||
|
for (
|
||||||
|
var i = this._stack.length; i > 0; this._cbs.onclosetag(this._stack[--i])
|
||||||
|
);
|
||||||
|
this._callback({
|
||||||
|
'nodes': this._cbs.nodes,
|
||||||
|
'title': this._cbs.title,
|
||||||
|
'imgList': this._cbs.imgList
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Parser.prototype.write = function(chunk) {
|
||||||
|
this._tokenizer.parse(chunk);
|
||||||
|
};
|
||||||
|
|
||||||
|
function html2nodes(data, tagStyle) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
try {
|
||||||
|
let style = '';
|
||||||
|
data = data.replace(/<style.*?>([\s\S]*?)<\/style>/gi, function() {
|
||||||
|
style += arguments[1];
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
let handler = new DomHandler(style, tagStyle);
|
||||||
|
new Parser(handler, (res) => {
|
||||||
|
return resolve(res);
|
||||||
|
}).write(data);
|
||||||
|
} catch (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
module.exports = html2nodes;
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
//Tokenizer.js
|
||||||
|
function Tokenizer(cbs) {
|
||||||
|
this._state = "TEXT";
|
||||||
|
this._buffer = "";
|
||||||
|
this._sectionStart = 0;
|
||||||
|
this._index = 0;
|
||||||
|
this._cbs = cbs;
|
||||||
|
}
|
||||||
|
Tokenizer.prototype.TEXT = function(c) {
|
||||||
|
var index = this._buffer.indexOf("<", this._index);
|
||||||
|
if (index != -1) {
|
||||||
|
this._index = index;
|
||||||
|
this._cbs.ontext(this._getSection());
|
||||||
|
this._state = "BeforeTag";
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
} else this._index = this._buffer.length;
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeTag = function(c) {
|
||||||
|
switch (c) {
|
||||||
|
case "/":
|
||||||
|
this._state = "BeforeCloseTag";
|
||||||
|
break;
|
||||||
|
case "!":
|
||||||
|
this._state = "BeforeDeclaration";
|
||||||
|
break;
|
||||||
|
case "?":
|
||||||
|
let index = this._buffer.indexOf(">", this._index);
|
||||||
|
if (index != -1) {
|
||||||
|
this._index = index;
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else this._sectionStart = this._index = this._buffer.length;
|
||||||
|
this._state = "TEXT";
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
this._state = "TEXT";
|
||||||
|
break;
|
||||||
|
case "<":
|
||||||
|
this._cbs.ontext(this._getSection());
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (/\s/.test(c)) this._state = "TEXT";
|
||||||
|
else {
|
||||||
|
this._state = "InTag";
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InTag = function(c) {
|
||||||
|
if (c === "/" || c === ">" || /\s/.test(c)) {
|
||||||
|
this._cbs.onopentagname(this._getSection());
|
||||||
|
this._state = "BeforeAttrsName";
|
||||||
|
this._index--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeAttrsName = function(c) {
|
||||||
|
if (c === ">") {
|
||||||
|
this._cbs.onopentagend();
|
||||||
|
this._state = "TEXT";
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else if (c === "/") {
|
||||||
|
this._state = "InSelfCloseTag";
|
||||||
|
} else if (!(/\s/.test(c))) {
|
||||||
|
this._state = "InAttrsName";
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InAttrsName = function(c) {
|
||||||
|
if (c === "=" || c === "/" || c === ">" || /\s/.test(c)) {
|
||||||
|
this._cbs._attribname = this._getSection().toLowerCase();
|
||||||
|
this._sectionStart = -1;
|
||||||
|
this._state = "AfterAttrsName";
|
||||||
|
this._index--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.AfterAttrsName = function(c) {
|
||||||
|
if (c === "=") {
|
||||||
|
this._state = "BeforeAttrsValue";
|
||||||
|
} else if (c === "/" || c === ">") {
|
||||||
|
this._cbs.onattribend();
|
||||||
|
this._state = "BeforeAttrsName";
|
||||||
|
this._index--;
|
||||||
|
} else if (!(/\s/.test(c))) {
|
||||||
|
this._cbs.onattribend();
|
||||||
|
this._state = "InAttrsName";
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeAttrsValue = function(c) {
|
||||||
|
if (c === '"') {
|
||||||
|
this._state = "InAttrsValueDQ";
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else if (c === "'") {
|
||||||
|
this._state = "InAttrsValueSQ";
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else if (!(/\s/.test(c))) {
|
||||||
|
this._state = "InAttrsValueNQ";
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
this._index--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InAttrsValueDQ = function(c) {
|
||||||
|
if (c === '"') {
|
||||||
|
this._cbs._attribvalue += this._getSection();
|
||||||
|
this._cbs.onattribend();
|
||||||
|
this._state = "BeforeAttrsName";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InAttrsValueSQ = function(c) {
|
||||||
|
if (c === "'") {
|
||||||
|
this._cbs._attribvalue += this._getSection();
|
||||||
|
this._cbs.onattribend();
|
||||||
|
this._state = "BeforeAttrsName";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InAttrsValueNQ = function(c) {
|
||||||
|
if (/\s/.test(c) || c === ">") {
|
||||||
|
this._cbs._attribvalue += this._getSection();
|
||||||
|
this._cbs.onattribend();
|
||||||
|
this._state = "BeforeAttrsName";
|
||||||
|
this._index--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeCloseTag = function(c) {
|
||||||
|
if (/\s/.test(c));
|
||||||
|
else if (c === ">") {
|
||||||
|
this._state = "TEXT";
|
||||||
|
} else {
|
||||||
|
this._state = "InCloseTag";
|
||||||
|
this._sectionStart = this._index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InCloseTag = function(c) {
|
||||||
|
if (c === ">" || /\s/.test(c)) {
|
||||||
|
this._cbs.onclosetag(this._getSection());
|
||||||
|
this._state = "AfterCloseTag";
|
||||||
|
this._index--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InSelfCloseTag = function(c) {
|
||||||
|
if (c === ">") {
|
||||||
|
this._cbs.onopentagend();
|
||||||
|
this._state = "TEXT";
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else if (!(/\s/.test(c))) {
|
||||||
|
this._state = "BeforeAttrsName";
|
||||||
|
this._index--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.AfterCloseTag = function(c) {
|
||||||
|
if (c === ">") {
|
||||||
|
this._state = "TEXT";
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeDeclaration = function(c) {
|
||||||
|
if (c == '-') this._state = "InComment";
|
||||||
|
else if (c == '[') this._state = "BeforeCDATA1";
|
||||||
|
else this._state = "InDeclaration";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InDeclaration = function(c) {
|
||||||
|
var index = this._buffer.indexOf(">", this._index);
|
||||||
|
if (index != -1) {
|
||||||
|
this._index = index;
|
||||||
|
this._sectionStart = index + 1;
|
||||||
|
} else this._sectionStart = this._index = this._buffer.length;
|
||||||
|
this._state = "TEXT";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InComment = function(c) {
|
||||||
|
let key = (c == '-' ? '-->' : '>');
|
||||||
|
let index = this._buffer.indexOf(key, this._index);
|
||||||
|
if (index != -1) {
|
||||||
|
this._index = index + key.length - 1;
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else this._sectionStart = this._index = this._buffer.length;
|
||||||
|
this._state = "TEXT";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeCDATA1 = function(c) {
|
||||||
|
if (c == 'C') this._state = "BeforeCDATA2";
|
||||||
|
else this._state = "InDeclaration";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeCDATA2 = function(c) {
|
||||||
|
if (c == 'D') this._state = "BeforeCDATA3";
|
||||||
|
else this._state = "InDeclaration";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeCDATA3 = function(c) {
|
||||||
|
if (c == 'A') this._state = "BeforeCDATA4";
|
||||||
|
else this._state = "InDeclaration";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeCDATA4 = function(c) {
|
||||||
|
if (c == 'T') this._state = "BeforeCDATA5";
|
||||||
|
else this._state = "InDeclaration";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.BeforeCDATA5 = function(c) {
|
||||||
|
if (c == 'A') this._state = "InCDATA";
|
||||||
|
else this._state = "InDeclaration";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.InCDATA = function(c) {
|
||||||
|
let key = (c == '[' ? ']]>' : '>');
|
||||||
|
let index = this._buffer.indexOf(key, this._index);
|
||||||
|
if (index != -1) {
|
||||||
|
this._index = index + key.length - 1;
|
||||||
|
this._sectionStart = this._index + 1;
|
||||||
|
} else this._sectionStart = this._index = this._buffer.length;
|
||||||
|
this._state = "TEXT";
|
||||||
|
};
|
||||||
|
Tokenizer.prototype.parse = function(chunk) {
|
||||||
|
this._buffer += chunk;
|
||||||
|
for (; this._index < this._buffer.length; this._index++)
|
||||||
|
this[this._state](this._buffer[this._index]);
|
||||||
|
if (this._state === "TEXT" && this._sectionStart !== this._index)
|
||||||
|
this._cbs.ontext(this._buffer.substr(this._sectionStart));
|
||||||
|
this._cbs.onend();
|
||||||
|
};
|
||||||
|
Tokenizer.prototype._getSection = function() {
|
||||||
|
return this._buffer.substring(this._sectionStart, this._index);
|
||||||
|
};
|
||||||
|
module.exports = Tokenizer;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
String.prototype.splice = function(start = 0, deleteCount = 0, addStr = '') {
|
||||||
|
if (start < 0) start = this.length + start;
|
||||||
|
if (deleteCount < 0) deleteCount = 0;
|
||||||
|
return this.substring(0, start) + addStr + this.substring(start + deleteCount);
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
versionHigherThan(version = '') {
|
||||||
|
var v1 = wx.getSystemInfoSync().SDKVersion.split('.');
|
||||||
|
var v2 = version.split('.');
|
||||||
|
const len = Math.max(v1.length, v2.length);
|
||||||
|
while (v1.length < len) {
|
||||||
|
v1.push('0');
|
||||||
|
}
|
||||||
|
while (v2.length < len) {
|
||||||
|
v2.push('0');
|
||||||
|
}
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
const num1 = parseInt(v1[i]);
|
||||||
|
const num2 = parseInt(v2[i]);
|
||||||
|
if (num1 > num2) {
|
||||||
|
return true;
|
||||||
|
} else if (num1 < num2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
html2nodes(html, tagStyle) {
|
||||||
|
const Parser = require('./Parser.js');
|
||||||
|
return Parser(html, tagStyle);
|
||||||
|
},
|
||||||
|
css2object(style, tagStyle) {
|
||||||
|
const CssTokenizer = require('./CssTokenizer.js');
|
||||||
|
return new CssTokenizer(style, tagStyle).parse();
|
||||||
|
}
|
||||||
|
}
|
||||||
+169
@@ -0,0 +1,169 @@
|
|||||||
|
//Parser组件
|
||||||
|
const html2nodes = require('./Parser.js');
|
||||||
|
const initData = function(Component) {
|
||||||
|
setTimeout(() => {
|
||||||
|
Component.createSelectorQuery().select('#contain').boundingClientRect(res => {
|
||||||
|
Component.triggerEvent('ready', res);
|
||||||
|
}).exec();
|
||||||
|
Component.videoContext = [];
|
||||||
|
let nodes = [Component.selectComponent('#contain')];
|
||||||
|
nodes = nodes.concat(Component.selectAllComponents('#contain>>>#node'));
|
||||||
|
for (let node of nodes) {
|
||||||
|
for (let item of node.data.nodes) {
|
||||||
|
if (item.name == 'video') {
|
||||||
|
Component.videoContext.push({
|
||||||
|
id: item.attrs.id,
|
||||||
|
context: wx.createVideoContext(item.attrs.id, node)
|
||||||
|
});
|
||||||
|
} else if (item.name == 'audio' && item.attrs.autoplay)
|
||||||
|
wx.createAudioContext(item.attrs.id, node).play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
Component({
|
||||||
|
properties: {
|
||||||
|
'html': {
|
||||||
|
type: String,
|
||||||
|
value: '',
|
||||||
|
observer: function(html) {
|
||||||
|
let hideAnimation = {},
|
||||||
|
showAnimation = {};
|
||||||
|
if (this.data.showWithAnimation) {
|
||||||
|
hideAnimation = wx.createAnimation({
|
||||||
|
duration: this.data.animationDuration,
|
||||||
|
timingFunction: "ease"
|
||||||
|
}).opacity(0).step().export();
|
||||||
|
showAnimation = wx.createAnimation({
|
||||||
|
duration: this.data.animationDuration,
|
||||||
|
timingFunction: "ease"
|
||||||
|
}).opacity(1).step().export();
|
||||||
|
}
|
||||||
|
if (!html) {
|
||||||
|
this.setData({
|
||||||
|
nodes: []
|
||||||
|
})
|
||||||
|
} else if (typeof html == 'string') {
|
||||||
|
html2nodes(html, this.data.tagStyle).then(res => {
|
||||||
|
this.setData({
|
||||||
|
nodes: res.nodes,
|
||||||
|
controls: {
|
||||||
|
imgMode: this.data.imgMode
|
||||||
|
},
|
||||||
|
showAnimation,
|
||||||
|
hideAnimation
|
||||||
|
}, initData(this))
|
||||||
|
if (res.title && this.data.autosetTitle) {
|
||||||
|
wx.setNavigationBarTitle({
|
||||||
|
title: res.title
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.imgList = res.imgList;
|
||||||
|
this.triggerEvent('parse', res);
|
||||||
|
}).catch(err => {
|
||||||
|
this.triggerEvent('error', err);
|
||||||
|
})
|
||||||
|
} else if (html.constructor == Array) {
|
||||||
|
this.setData({
|
||||||
|
controls: {
|
||||||
|
imgMode: this.data.imgMode
|
||||||
|
},
|
||||||
|
showAnimation,
|
||||||
|
hideAnimation
|
||||||
|
}, initData(this))
|
||||||
|
this.imgList = [];
|
||||||
|
} else if (typeof html == 'object') {
|
||||||
|
if (!html.nodes || html.nodes.constructor != Array) {
|
||||||
|
this.triggerEvent('error', {
|
||||||
|
message: "传入的nodes数组格式不正确!应该传入的类型是array,实际传入的类型是:" + typeof html.nodes
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setData({
|
||||||
|
controls: {
|
||||||
|
imgMode: this.data.imgMode
|
||||||
|
},
|
||||||
|
showAnimation,
|
||||||
|
hideAnimation
|
||||||
|
}, initData(this))
|
||||||
|
if (html.title && this.data.autosetTitle) {
|
||||||
|
wx.setNavigationBarTitle({
|
||||||
|
title: html.title
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.imgList = html.imgList || [];
|
||||||
|
} else {
|
||||||
|
this.triggerEvent('error', {
|
||||||
|
message: "错误的html类型:" + typeof html
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'autocopy': {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
'autopause': {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
'autosetTitle':{
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
'imgMode': {
|
||||||
|
type: String,
|
||||||
|
value: "default"
|
||||||
|
},
|
||||||
|
'selectable': {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
'tagStyle': {
|
||||||
|
type: Object,
|
||||||
|
value: { video:"width:100%" }
|
||||||
|
},
|
||||||
|
'showWithAnimation': {
|
||||||
|
type: Boolean,
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
'animationDuration': {
|
||||||
|
type: Number,
|
||||||
|
value: 400
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
//事件
|
||||||
|
tapEvent(e) {
|
||||||
|
if (this.data.autocopy && e.detail && /^http/.test(e.detail)) {
|
||||||
|
wx.setClipboardData({
|
||||||
|
data: e.detail,
|
||||||
|
success() {
|
||||||
|
wx.showToast({
|
||||||
|
title: '链接已复制',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.triggerEvent('linkpress', e.detail);
|
||||||
|
},
|
||||||
|
errorEvent(e) {
|
||||||
|
this.triggerEvent('error', e.detail);
|
||||||
|
},
|
||||||
|
//内部方法
|
||||||
|
_previewImg(e) {
|
||||||
|
wx.previewImage({
|
||||||
|
current: e.detail,
|
||||||
|
urls: this.imgList.length ? this.imgList : [e.detail],
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_playVideo(e) {
|
||||||
|
if (this.videoContext.length > 1 && this.data.autopause) {
|
||||||
|
for (let video of this.videoContext) {
|
||||||
|
if (video.id == e.detail) continue;
|
||||||
|
video.context.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"component": true,
|
||||||
|
"usingComponents": {
|
||||||
|
"trees": "./trees/trees"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<!--Parser-->
|
||||||
|
<slot wx:if="{{!(html.nodes||((html[0].name||html[0].type)?1:nodes.length))}}" animation="{{hideAnimation}}"></slot>
|
||||||
|
<trees id="contain" style="{{(showWithAnimation?'opacity:0;':'')+(selectable?'user-select:text;-webkit-user-select:text':'')}}" animation="{{showAnimation}}" nodes="{{html.nodes||((html[0].name||html[0].type)?html:nodes)}}" controls="{{controls}}" catchpreview="_previewImg"
|
||||||
|
catchplay="_playVideo" catchlinkpress="tapEvent" catcherror="errorEvent" />
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
overflow: scroll;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Parser/trees/cssHandler.wxs
|
||||||
|
module.exports = {
|
||||||
|
getStyle: function(style, display) {
|
||||||
|
var res = "";
|
||||||
|
var reg = getRegExp("float\s*:\s*[^;]*", "i");
|
||||||
|
if (reg.test(style)) res += reg.exec(style)[0];
|
||||||
|
reg = getRegExp("margin[^;]*auto", "i");
|
||||||
|
if (reg.test(style)) res += (";" + reg.exec(style)[0]);
|
||||||
|
reg = getRegExp("display\s*:\s*([^;]*)", "i");
|
||||||
|
if (reg.test(style) && reg.exec(style)[1]!="flex") res += (';' + reg.exec(style)[0]);
|
||||||
|
else res += (';display:' + display);
|
||||||
|
reg = getRegExp("[^;\s]*width\s*:\s*[^;]*", "ig");
|
||||||
|
var width = reg.exec(style);
|
||||||
|
while (width) {
|
||||||
|
res += (';' + width[0]);
|
||||||
|
width = reg.exec(style);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
setImgStyle: function(item, imgMode) {
|
||||||
|
if (getRegExp("[^-]width\s*:\s*[^;]*", "i").test(';' + item.attrs.style))
|
||||||
|
item.attrs.style += ';width:100%';
|
||||||
|
if (imgMode == "widthFix")
|
||||||
|
item.attrs.style += ";height:auto !important";
|
||||||
|
return [item];
|
||||||
|
},
|
||||||
|
setStyle: function(item) {
|
||||||
|
if (getRegExp("[^-]width\s*:\s*[^;]*", "i").test(';' + item.attrs.style))
|
||||||
|
item.attrs.style += ';width:100%';
|
||||||
|
return [item];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// Parser/trees/trees.js
|
||||||
|
Component({
|
||||||
|
properties: {
|
||||||
|
nodes: {
|
||||||
|
type: Array,
|
||||||
|
value: []
|
||||||
|
},
|
||||||
|
controls: {
|
||||||
|
type: Object,
|
||||||
|
value: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
//冒泡事件
|
||||||
|
playEvent(e) {
|
||||||
|
this.triggerEvent('play', e.currentTarget.dataset.id, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
previewEvent(e) {
|
||||||
|
if (!e.target.dataset.hasOwnProperty('ignore')) {
|
||||||
|
this.triggerEvent('preview', e.currentTarget.dataset.src, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tapEvent(e) {
|
||||||
|
this.triggerEvent('linkpress', e.currentTarget.dataset.href, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
errorEvent(e) {
|
||||||
|
//尝试加载其他源
|
||||||
|
if (!this.data.controls[e.currentTarget.dataset.id] && e.currentTarget.dataset.source.length > 1) {
|
||||||
|
this.data.controls[e.currentTarget.dataset.id] = {
|
||||||
|
play: false,
|
||||||
|
index: 1
|
||||||
|
}
|
||||||
|
} else if (this.data.controls[e.currentTarget.dataset.id] && e.currentTarget.dataset.source.length > (this.data.controls[e.currentTarget.dataset.id].index + 1)) {
|
||||||
|
this.data.controls[e.currentTarget.dataset.id].index++;
|
||||||
|
}
|
||||||
|
this.setData({
|
||||||
|
controls: this.data.controls
|
||||||
|
})
|
||||||
|
this.triggerEvent('error', {
|
||||||
|
target: e.currentTarget,
|
||||||
|
message: e.detail.errMsg
|
||||||
|
}, {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//内部方法:加载视频
|
||||||
|
_loadVideo(e) {
|
||||||
|
this.data.controls[e.currentTarget.dataset.id] = {
|
||||||
|
play: true,
|
||||||
|
index: 0
|
||||||
|
}
|
||||||
|
this.setData({
|
||||||
|
controls: this.data.controls
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"component": true,
|
||||||
|
"usingComponents": {
|
||||||
|
"trees":"./trees"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<!--Parser/trees/trees.wxml-->
|
||||||
|
<wxs module="cssHandler" src="./cssHandler.wxs" />
|
||||||
|
<block wx:for='{{nodes}}' wx:key="index">
|
||||||
|
<block wx:if="{{!item.continue}}">
|
||||||
|
<!--图片-->
|
||||||
|
<rich-text wx:if="{{item.name=='img'}}" style="{{cssHandler.getStyle(item.attrs.style,'inline-block')}}" nodes='{{cssHandler.setImgStyle(item,controls.imgMode)}}' data-ignore='{{item.attrs.ignore}}' data-src='{{item.attrs.src}}' bindtap='previewEvent' />
|
||||||
|
<!--文本-->
|
||||||
|
<block wx:elif="{{item.type=='text'}}">
|
||||||
|
<text wx:if="{{!item.decode}}" decode>{{item.text}}</text>
|
||||||
|
<rich-text wx:else style="display:inline-block" nodes="{{[item]}}"></rich-text>
|
||||||
|
</block>
|
||||||
|
<text wx:elif="{{item.name=='br'}}">\n</text>
|
||||||
|
<!--视频-->
|
||||||
|
<block wx:elif="{{item.name=='video'}}">
|
||||||
|
<view wx:if="{{item.attrs.id>'video3'&&!controls[item.attrs.id].play}}" class="video" style="{{item.attrs.style+(item.attrs.width?(';width:'+item.attrs.width):'')+(item.attrs.height?('height:'+item.attrs.height):'')}}" data-id="{{item.attrs.id}}" bindtap="_loadVideo">
|
||||||
|
<view class="video-triangle" />
|
||||||
|
</view>
|
||||||
|
<video wx:else src='{{controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index]:item.attrs.src}}' id="{{item.attrs.id}}" loop='{{item.attrs.loop}}' controls='{{item.attrs.controls}}' autoplay="{{item.attrs.autoplay||controls[item.attrs.id].play}}"
|
||||||
|
class="v" muted="{{item.attrs.muted}}" style="{{item.attrs.style+(item.attrs.width?(';width:'+item.attrs.width):'')+(item.attrs.height?('height:'+item.attrs.height):'')}}" data-id="{{item.attrs.id}}" data-source="{{item.attrs.source}}" bindplay='playEvent'
|
||||||
|
binderror="errorEvent" />
|
||||||
|
</block>
|
||||||
|
<!--音频-->
|
||||||
|
<audio wx:elif="{{item.name=='audio'}}" src='{{controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index]:item.attrs.src}}' id="{{item.attrs.id}}" loop='{{item.attrs.loop}}' controls='{{item.attrs.controls}}' poster='{{item.attrs.poster}}'
|
||||||
|
name='{{item.attrs.name}}' author='{{item.attrs.author}}' style="{{item.attrs.style}}" data-id="{{item.attrs.id}}" data-source="{{item.attrs.source}}" binderror="errorEvent" />
|
||||||
|
<!--富文本-->
|
||||||
|
<rich-text wx:else class="{{item.name}}" style="{{cssHandler.getStyle(item.attrs.style,'block')}}" nodes="{{cssHandler.setStyle(item)}}" />
|
||||||
|
</block>
|
||||||
|
<!--链接-->
|
||||||
|
<navigator wx:elif="{{item.name=='a'}}" url="{{item.attrs.href}}" style="{{item.attrs.style}}" data-href='{{item.attrs.href}}' hover-class="navigator-hover" hover-start-time="25" hover-stay-time="300" bindtap="tapEvent">
|
||||||
|
<trees id="node" nodes="{{item.children}}" controls="{{controls}}" />
|
||||||
|
</navigator>
|
||||||
|
<!--继续递归-->
|
||||||
|
<trees wx:else id="node" class="{{item.name}}" style="{{item.attrs.style}}" nodes="{{item.children}}" controls="{{controls}}" />
|
||||||
|
</block>
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/* 链接受到点击的hover-class,可自定义修改 */
|
||||||
|
.navigator-hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
/* 以下内容不建议修改 */
|
||||||
|
:host {
|
||||||
|
display: inherit;
|
||||||
|
float: inherit;
|
||||||
|
}
|
||||||
|
.sub, .sup, .bdo, .bdi, .ruby, .rt {
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
.div, .blockquote, .p{
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
.b, .strong {
|
||||||
|
display: inline;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.em, .i {
|
||||||
|
display: inline;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.del {
|
||||||
|
display: inline;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.ins {
|
||||||
|
display: inline;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.code {
|
||||||
|
display: inline;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
.big {
|
||||||
|
font-size:1.2em;
|
||||||
|
display:inline;
|
||||||
|
}
|
||||||
|
.small {
|
||||||
|
font-size:0.8em;
|
||||||
|
display:inline;
|
||||||
|
}
|
||||||
|
.q, .span, .label, .abbr {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
.q::before {
|
||||||
|
content: '"';
|
||||||
|
}
|
||||||
|
.q::after {
|
||||||
|
content: '"';
|
||||||
|
}
|
||||||
|
.video {
|
||||||
|
background-color: rgb(0, 0, 0);
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.video-triangle {
|
||||||
|
border-width: 15px 0 15px 30px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent transparent transparent white;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
margin: -15px 0 0 -15px;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user