请求解析
This commit is contained in:
1 parent
0b9c772cfc
commit
bb18cb696a
14 files changed
+1241
No files matched your search
@@ -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;
|
||||
Reference in new issue
Block a user