mirror of
https://github.com/QuiteAFancyEmerald/Holy-Unblocker.git
synced 2025-05-12 11:30:01 -04:00
144 lines
No EOL
6 KiB
JavaScript
144 lines
No EOL
6 KiB
JavaScript
// -------------------------------------------------------------
|
|
// WARNING: this file is used by both the client and the server.
|
|
// Do not use any browser or node-specific API!
|
|
// -------------------------------------------------------------
|
|
const parse5 = require('parse5');
|
|
const rawAttrs = require('./attributes');
|
|
const { iterate } = require('./parse5');
|
|
|
|
class HTMLRewriter {
|
|
constructor(ctx) {
|
|
const master = this;
|
|
this.ctx = ctx;
|
|
this.attrs = new Map();
|
|
rawAttrs.forEach(({ tag, attribute, run }) => {
|
|
if (Array.isArray(tag)) {
|
|
for (const entry of tag) processAttribute(entry, attribute, run);
|
|
} else {
|
|
processAttribute(tag, attribute, run);
|
|
};
|
|
});
|
|
function processAttribute(tag, attribute, run) {
|
|
if (!master.attrs.has(tag)) master.attrs.set(tag, {});
|
|
if (Array.isArray(attribute)) {
|
|
for (const entry of attribute) master.attrs.get(tag)[entry] = run;
|
|
} else {
|
|
master.attrs.get(tag)[attribute] = run;
|
|
};
|
|
};
|
|
};
|
|
process(source, config = {}) {
|
|
try {
|
|
const ast = parse5[config.document ? 'parse' : 'parseFragment'](source);
|
|
let head = null;
|
|
iterate(ast, node => {
|
|
if (!node.tagName) return false;
|
|
if (!!node.textContent) {
|
|
switch(node.nodeName) {
|
|
case 'title':
|
|
if (this.ctx.config.title) {
|
|
node.setAttribute('corrosion-text', node.textContent);
|
|
node.textContent = this.ctx.config.title;
|
|
};
|
|
break;
|
|
case 'script':
|
|
node.textContent = this.ctx.js.process(node.textContent);
|
|
break;
|
|
case 'style':
|
|
node.textContent = this.ctx.css.process(node.textContent, { meta: config.meta });
|
|
break;
|
|
case 'noscript':
|
|
node.textContent = this.process(node.textContent, { meta: config.meta || {}, });
|
|
break;
|
|
|
|
};
|
|
};
|
|
if (config.document && node.tagName == 'HEAD') head = node;
|
|
for (const attr of node.attrs) {
|
|
let data = {
|
|
ctx: this.ctx,
|
|
attr,
|
|
node,
|
|
meta: config.meta,
|
|
delete: false,
|
|
};
|
|
if (this.attrs.get('*')[attr.name]) this.attrs.get('*')[attr.name](node, data);
|
|
if (this.attrs.has(node.nodeName) && this.attrs.get(node.nodeName)[attr.name]) this.attrs.get(node.nodeName)[attr.name](node, data);
|
|
};
|
|
});
|
|
if (config.document && head) {
|
|
head.childNodes.unshift(
|
|
{
|
|
nodeName: 'title',
|
|
tagName: 'title',
|
|
childNodes: [
|
|
{
|
|
nodeName: '#text',
|
|
value: this.ctx.config.title || '',
|
|
}
|
|
],
|
|
attrs: [],
|
|
},
|
|
{
|
|
nodeName: 'script',
|
|
tagName: 'script',
|
|
childNodes: [],
|
|
attrs: [
|
|
{
|
|
name: 'src',
|
|
value: this.ctx.prefix + 'index.js',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
nodeName: 'script',
|
|
tagName: 'script',
|
|
childNodes: [
|
|
{
|
|
nodeName: '#text',
|
|
value: `window.$corrosion = new Corrosion({ window, codec: '${this.ctx.config.codec || 'plain'}', prefix: '${this.ctx.prefix}', ws: ${this.ctx.config.ws}, cookie: ${this.ctx.config.cookie}, title: ${typeof this.ctx.config.title == 'boolean' ? this.ctx.config.title : '\'' + this.ctx.config.title + '\''}, }); $corrosion.init(); document.currentScript.remove();`
|
|
},
|
|
],
|
|
attrs: [],
|
|
}
|
|
)
|
|
};
|
|
return parse5.serialize(ast);
|
|
} catch(e) {
|
|
return source;
|
|
};
|
|
};
|
|
source(processed, config = {}) {
|
|
const ast = parse5[config.document ? 'parse' : 'parseFragment'](processed);
|
|
iterate(ast, node => {
|
|
if (node.nodeName == 'noscript') node.textContent = this.source(node.textContent);
|
|
for (const attr of node.attrs) {
|
|
if (attr.name.startsWith('corrosion-attr-')) {
|
|
node.setAttribute(attr.name.slice('corrosion-attr-'.length), attr.value);
|
|
node.removeAttribute(attr.name);
|
|
};
|
|
if (attr.name.startsWith('corrosion-text')) {
|
|
node.textContent = attr.value;
|
|
node.removeAttribute(attr.name);
|
|
};
|
|
};
|
|
});
|
|
return parse5.serialize(ast);
|
|
};
|
|
srcset(val, meta = {}) {
|
|
return val.split(',').map(src => {
|
|
const parts = src.trimStart().split(' ');
|
|
if (parts[0]) parts[0] = this.ctx.url.wrap(parts[0], meta);
|
|
return parts.join(' ');
|
|
}).join(', ');
|
|
};
|
|
unsrcset(val, meta = {}) {
|
|
return val.split(',').map(src => {
|
|
const parts = src.trimStart().split(' ');
|
|
if (parts[0]) parts[0] = this.ctx.url.unwrap(parts[0], meta);
|
|
return parts.join(' ');
|
|
}).join(', ');
|
|
};
|
|
}
|
|
|
|
module.exports = HTMLRewriter; |