♻️ 公告解析重构完成

This commit is contained in:
目棃
2024-08-17 18:49:18 +08:00
parent 9e9ba53d99
commit 6bfa1b3cb4
9 changed files with 234 additions and 228 deletions

View File

@@ -6,13 +6,11 @@
import { getAnnoCard } from "./getAnnoCard.js";
import { getRequestHeader } from "./getRequestHeader.js";
import { parseAnnoContent } from "./parseAnno.js";
import { getServerByUid, transCookie } from "./tools.js";
const TGUtils = {
Anno: {
getCard: getAnnoCard,
parseContent: parseAnnoContent,
},
User: {
getHeader: getRequestHeader,

View File

@@ -4,8 +4,85 @@
* @since Beta v0.5.3
*/
import { h, render } from "vue";
import TpText from "../../components/post/tp-text.vue";
import { decodeRegExp } from "./tools.js";
/**
* @description 预处理p
* @since Beta v0.5.2
* @param {HTMLParagraphElement} p p 元素
* @returns {HTMLParagraphElement} 解析后的 p 元素
*/
function handleAnnoP(p: HTMLParagraphElement): HTMLParagraphElement {
if (p.children.length === 0) {
p.innerHTML = decodeRegExp(p.innerHTML);
} else {
p.querySelectorAll("*").forEach((child) => {
child.innerHTML = decodeRegExp(child.innerHTML);
child.querySelectorAll("span").forEach((span) => handleAnnoSpan(span));
});
}
return p;
}
/**
* @description 预处理 span
* @since Beta v0.4.4
* @param {HTMLSpanElement} span span 元素
* @returns {HTMLSpanElement} 解析后的 span 元素
*/
function handleAnnoSpan(span: HTMLSpanElement): HTMLSpanElement {
if (span.style.fontSize) {
span.style.fontSize = "";
}
if (span.children.length === 0) {
span.innerHTML = decodeRegExp(span.innerHTML);
} else {
span.querySelectorAll("*").forEach((child) => {
if (child.children.length === 0) {
child.innerHTML = decodeRegExp(child.innerHTML);
}
if (child.tagName === "T") {
child.outerHTML = child.innerHTML;
}
});
}
return span;
}
/**
* @description 预处理table
* @since Beta v0.4.7
* @param {HTMLTableElement} table table 元素
* @returns {HTMLTableElement} 解析后的 table 元素
*/
function handleAnnoTable(table: HTMLTableElement): HTMLTableElement {
table.style.borderColor = "var(--common-shadow-2)";
table.querySelectorAll("colgroup").forEach((colgroup) => colgroup.remove());
table.querySelectorAll("td").forEach((td) => {
if (td.style.backgroundColor) td.style.backgroundColor = "var(--box-bg-1)";
td.style.textAlign = "center";
});
return table;
}
/**
* @description 预处理公告内容
* @since Beta v0.4.4
* @param {string} data 游戏内公告数据
* @returns {string} 解析后的数据
*/
export function handleAnnoContent(data: string): string {
const htmlBase = new DOMParser().parseFromString(data, "text/html");
htmlBase.querySelectorAll("p").forEach((p) => handleAnnoP(p));
htmlBase.querySelectorAll("span").forEach((span) => handleAnnoSpan(span));
htmlBase.querySelectorAll("table").forEach((table) => handleAnnoTable(table));
return htmlBase.body.innerHTML;
}
/**
* @description 解析公告内容,转换为结构化数据
* @since Beta v0.5.3
@@ -16,14 +93,10 @@ export function parseAnnoContent(
anno: TGApp.BBS.Announcement.ContentItem,
): TGApp.Plugins.Mys.SctPost.Base[] {
const parser = new DOMParser();
const doc = parser.parseFromString(anno.content, "text/html");
const first = handleAnnoContent(anno.content);
const doc = parser.parseFromString(first, "text/html");
const children: TGApp.Plugins.Mys.SctPost.Base[] = [];
const bannerNode: TGApp.Plugins.Mys.SctPost.Base = {
insert: {
image: anno.banner,
},
};
children.push(bannerNode);
if (anno.banner !== "") children.push({ insert: { image: anno.banner } });
doc.body.childNodes.forEach((child) => {
children.push(...parseAnnoNode(child));
});
@@ -34,9 +107,13 @@ export function parseAnnoContent(
* @description 解析公告节点
* @since Beta v0.5.3
* @param {Node} node - 节点
* @param {Record<string, string>} attr - 属性
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
*/
function parseAnnoNode(node: Node): TGApp.Plugins.Mys.SctPost.Base[] {
function parseAnnoNode(
node: Node,
attr?: Record<string, string>,
): TGApp.Plugins.Mys.SctPost.Base[] {
let defaultRes: TGApp.Plugins.Mys.SctPost.Base = {
insert: {
tag: node.nodeName,
@@ -48,7 +125,7 @@ function parseAnnoNode(node: Node): TGApp.Plugins.Mys.SctPost.Base[] {
return [defaultRes];
}
if (node.nodeType === Node.TEXT_NODE) {
return [{ insert: node.textContent }];
return [{ insert: node.textContent, attributes: attr }];
}
const element = <HTMLElement>node;
defaultRes = {
@@ -61,42 +138,56 @@ function parseAnnoNode(node: Node): TGApp.Plugins.Mys.SctPost.Base[] {
},
};
if (element.tagName === "P") {
return [parseAnnoParagraph(element)];
element.innerHTML = decodeRegExp(element.innerHTML);
return [parseAnnoParagraph(element, attr)];
}
if (element.tagName === "OL" || element.tagName === "LI" || element.tagName === "UL") {
const res: TGApp.Plugins.Mys.SctPost.Base[] = [];
Array.from(element.children).forEach((child) => {
res.push(...parseAnnoNode(child));
res.push(...parseAnnoNode(child, attr));
});
return res;
}
if (element.tagName === "DETAILS") {
return [parseAnnoDetails(element)];
}
// todo table解析
if (element.tagName === "TABLE") {
return [defaultRes];
return [parseAnnoTable(element)];
}
if (element.tagName === "DIV") {
if (element.childNodes.length > 1) {
const res: TGApp.Plugins.Mys.SctPost.Base[] = [];
element.childNodes.forEach((child) => {
res.push(...parseAnnoNode(child));
res.push(...parseAnnoNode(child, attr));
});
return res;
}
if (element.childNodes.length === 0) return [defaultRes];
const child = element.childNodes[0];
if (child.nodeType !== Node.ELEMENT_NODE) return [defaultRes];
return parseAnnoNode(child);
return parseAnnoNode(child, attr);
}
if (element.tagName === "SUMMARY") {
const p = document.createElement("p");
p.innerHTML = element.innerHTML;
return [parseAnnoParagraph(p)];
return [parseAnnoParagraph(p, attr)];
}
if (element.tagName === "SPAN") {
return [parseAnnoSpan(element)];
return [parseAnnoSpan(element, attr)];
}
if (element.tagName === "STRONG") {
const res: TGApp.Plugins.Mys.SctPost.Base[] = [];
element.childNodes.forEach((child) => {
res.push(...parseAnnoNode(child, { bold: "true" }));
});
return res;
}
if (element.tagName === "T") {
const res: TGApp.Plugins.Mys.SctPost.Base[] = [];
element.childNodes.forEach((child) => {
res.push(...parseAnnoNode(child, attr));
});
return res;
}
return [defaultRes];
}
@@ -105,9 +196,13 @@ function parseAnnoNode(node: Node): TGApp.Plugins.Mys.SctPost.Base[] {
* @description 解析公告段落
* @since Beta v0.5.3
* @param {HTMLElement} p - 段落元素
* @param {Record<string, string>} attr - 属性
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
*/
function parseAnnoParagraph(p: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
function parseAnnoParagraph(
p: HTMLElement,
attr?: Record<string, string>,
): TGApp.Plugins.Mys.SctPost.Base {
const defaultRes = {
insert: {
tag: p.tagName,
@@ -135,14 +230,14 @@ function parseAnnoParagraph(p: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
span.innerHTML = p.innerHTML;
return {
insert: "",
children: [parseAnnoSpan(span)],
children: [parseAnnoSpan(span, attr)],
};
}
const child = <HTMLElement>p.childNodes[0];
if (child.tagName === "SPAN") {
return {
insert: "",
children: [parseAnnoSpan(child)],
children: [parseAnnoSpan(child, attr)],
};
}
if (child.tagName === "IMG") {
@@ -156,9 +251,10 @@ function parseAnnoParagraph(p: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
};
}
if (child.tagName === "STRONG") {
const res = parseAnnoNode(child, { bold: "true" });
return {
insert: child.innerHTML,
attributes: { bold: true },
insert: "",
children: res,
};
}
return defaultRes;
@@ -184,6 +280,16 @@ function parseAnnoParagraph(p: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
insert: element.innerHTML,
attributes: { bold: true },
};
} else if (element.tagName === "T") {
element.innerHTML = element.outerHTML;
const resE = parseAnnoNode(element);
if (resE.length > 1) {
childRes = { insert: element.outerHTML };
} else {
childRes = resE[0];
}
} else if (element.tagName === "IMG") {
childRes = parseAnnoImage(element);
} else {
throw new Error(`Unknown node type: ${element.tagName}`);
}
@@ -199,9 +305,13 @@ function parseAnnoParagraph(p: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
* @description 解析公告 span
* @since Beta v0.5.3
* @param {HTMLElement} span - span 元素
* @param {Record<string, string>} attr - 属性
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
*/
function parseAnnoSpan(span: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
function parseAnnoSpan(
span: HTMLElement,
attr?: Record<string, string>,
): TGApp.Plugins.Mys.SctPost.Base {
const defaultRes = {
insert: {
tag: span.tagName,
@@ -216,6 +326,7 @@ function parseAnnoSpan(span: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
console.error(span.innerHTML);
return {
insert: span.innerHTML === "" ? "\n" : decodeRegExp(span.innerHTML),
attributes: attr,
};
}
if (span.childNodes.length === 1) {
@@ -230,11 +341,10 @@ function parseAnnoSpan(span: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
if (res.length > 1) return defaultRes;
return res[0];
}
let spanAttrs: Record<string, string> | null = {};
let spanAttrs: Record<string, string> | undefined = attr;
if (!spanAttrs) spanAttrs = {};
if (span.style.color !== "") {
spanAttrs.color = span.style.color;
} else {
spanAttrs = null;
}
const parse = decodeRegExp(span.innerHTML);
if (parse.includes("</t>")) {
@@ -347,3 +457,52 @@ function parseAnnoDetails(details: HTMLElement): TGApp.Plugins.Mys.SctPost.Base
},
};
}
/**
* @description 解析公告表格
* @since Beta v0.5.3
* @param {HTMLElement} table - 表格元素
* @returns {TGApp.Plugins.Mys.SctPost.Base} 结构化数据
*/
function parseAnnoTable(table: HTMLElement): TGApp.Plugins.Mys.SctPost.Base {
const defaultRes = {
insert: {
tag: table.tagName,
text: table.textContent,
style: table.style.cssText,
html: table.innerHTML,
outerHtml: table.outerHTML,
},
};
if (table.tagName !== "TABLE") return defaultRes;
if (table.childNodes.length === 0) return defaultRes;
const tableBody = table.querySelector("tbody");
if (tableBody === null) return defaultRes;
tableBody.childNodes.forEach((tr) => {
tr.childNodes.forEach((td) => {
td.childNodes.forEach((child, index) => {
const cellParsed = parseAnnoNode(child);
const span = document.createElement("div");
span.style.lineHeight = "2";
for (const cell of cellParsed) {
if (cell.children && cell.children.length > 0) {
for (const cellChild of cell.children) {
if (cellChild.attributes && JSON.stringify(cellChild.attributes === "{}")) {
delete cellChild.attributes;
}
const cellSpan = document.createElement("span");
render(h(TpText, { data: cellChild }), cellSpan);
span.appendChild(cellSpan);
}
}
}
td.replaceChild(span, td.childNodes[index]);
});
});
});
return {
insert: {
table: table.outerHTML,
},
};
}

View File

@@ -1,119 +0,0 @@
/**
* @file web/utils/parseAnno.ts
* @description 解析游戏内公告数据
* @since Beta v0.5.2
*/
import { saveImgLocal } from "../../utils/TGShare.js";
import { isColorSimilar } from "../../utils/toolFunc.js";
import { decodeRegExp } from "./tools.js";
/**
* @description 解析 a
* @since Beta v0.5.0
* @param {HTMLAnchorElement} a a 元素
* @returns {HTMLAnchorElement} 解析后的 a 元素
*/
function parseAnnoA(a: HTMLAnchorElement): HTMLAnchorElement {
const regex = /javascript:miHoYoGameJSSDK.openIn(Browser|Webview)\('(.+)'\);/;
if (regex.test(a.getAttribute("href") ?? "")) {
const href = a.getAttribute("href")?.match(regex);
if (href) {
a.setAttribute("href", href[2]);
a.target = "_blank";
a.title = href[2];
}
}
return a;
}
/**
* @description 解析 p
* @since Beta v0.5.2
* @param {HTMLParagraphElement} p p 元素
* @returns {HTMLParagraphElement} 解析后的 p 元素
*/
function parseAnnoP(p: HTMLParagraphElement): HTMLParagraphElement {
if (p.children.length === 0) {
p.innerHTML = decodeRegExp(p.innerHTML);
} else {
p.querySelectorAll("*").forEach((child) => {
child.innerHTML = decodeRegExp(child.innerHTML);
child.querySelectorAll("span").forEach((span) => parseAnnoSpan(span));
});
}
return p;
}
/**
* @description 解析 span
* @since Beta v0.4.4
* @param {HTMLSpanElement} span span 元素
* @returns {HTMLSpanElement} 解析后的 span 元素
*/
function parseAnnoSpan(span: HTMLSpanElement): HTMLSpanElement {
if (span.style.fontSize) {
span.style.fontSize = "";
}
if (span.style.color) {
if (isColorSimilar("#000000", span.style.color)) {
span.style.color = "var(--app-page-content)";
}
}
if (span.children.length === 0) {
span.innerHTML = decodeRegExp(span.innerHTML);
} else {
span.querySelectorAll("*").forEach((child) => {
if (child.children.length === 0) {
child.innerHTML = decodeRegExp(child.innerHTML);
}
if (child.tagName === "T") {
child.outerHTML = child.innerHTML;
}
});
}
return span;
}
/**
* @description 解析 table
* @since Beta v0.4.7
* @param {HTMLTableElement} table table 元素
* @returns {HTMLTableElement} 解析后的 table 元素
*/
function parseAnnoTable(table: HTMLTableElement): HTMLTableElement {
table.style.borderColor = "var(--common-shadow-2)";
table.querySelectorAll("colgroup").forEach((colgroup) => colgroup.remove());
table.querySelectorAll("td").forEach((td) => {
if (td.style.backgroundColor) td.style.backgroundColor = "var(--box-bg-1)";
td.style.textAlign = "center";
});
return table;
}
/**
* @description 解析游戏内公告数据
* @since Beta v0.4.4
* @todo 需要完善
* @param {string} data 游戏内公告数据
* @returns {Promise<string>} 解析后的数据
*/
export async function parseAnnoContent(data: string): Promise<string> {
const htmlBase = new DOMParser().parseFromString(data, "text/html");
htmlBase.querySelectorAll("a").forEach((a) => parseAnnoA(a));
const imgList = Array.from(htmlBase.querySelectorAll("img"));
for (const img of imgList) {
img.style.maxWidth = "100%";
img.style.borderRadius = "10px";
img.style.margin = "10px 0";
const src = img.getAttribute("src");
if (src) {
img.setAttribute("src", await saveImgLocal(src));
}
}
htmlBase.querySelectorAll("p").forEach((p) => parseAnnoP(p));
htmlBase.querySelectorAll("span").forEach((span) => parseAnnoSpan(span));
htmlBase.querySelectorAll("table").forEach((table) => parseAnnoTable(table));
return htmlBase.body.innerHTML;
}