feat(parser): Text#toHTML (#300)

Added support to render Text nodes as HTML for use in web applications.
This commit is contained in:
Daniel Wykerd
2023-02-01 21:27:59 +02:00
committed by GitHub
parent f62c66db39
commit e82e23dfbb
4 changed files with 62 additions and 2 deletions

View File

@@ -94,6 +94,17 @@ class NavigationEndpoint extends YTNode {
throw new Error('Expected an api_url, but none was found, this is a bug.');
return actions.execute(this.metadata.api_url, { ...this.payload, ...args });
}
toURL(): string | undefined {
if (!this.metadata.url)
return undefined;
if (!this.metadata.page_type)
return undefined;
return (
this.metadata.page_type === 'WEB_PAGE_TYPE_UNKNOWN' ?
this.metadata.url : `https://www.youtube.com${this.metadata.url}`
);
}
}
export default NavigationEndpoint;

View File

@@ -1,6 +1,7 @@
import { escape, Run } from './Text';
import Thumbnail from './Thumbnail';
class EmojiRun {
class EmojiRun implements Run {
text: string;
emoji: {
emoji_id: string;
@@ -24,6 +25,15 @@ class EmojiRun {
is_custom: !!data.emoji?.isCustomEmoji
};
}
toString() {
return this.text;
}
toHTML(): string {
const escaped_text = escape(this.text);
return `<img src="${this.emoji.image[0].url}" alt="${escaped_text}" title="${escaped_text}" style="display: inline-block; vertical-align: text-top; height: var(--yt-emoji-size, 1rem); width: var(--yt-emoji-size, 1rem);" loading="lazy" crossorigin="anonymous" />`;
}
}
export default EmojiRun;

View File

@@ -1,6 +1,21 @@
import TextRun from './TextRun';
import EmojiRun from './EmojiRun';
export interface Run {
text: string;
toString(): string;
toHTML(): string;
}
export function escape(text: string) {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
class Text {
text: string;
runs;
@@ -17,6 +32,10 @@ class Text {
}
}
toHTML() {
return this.runs ? this.runs.map((run) => run.toHTML()).join('') : this.text;
}
toString() {
return this.text;
}

View File

@@ -1,6 +1,7 @@
import NavigationEndpoint from '../NavigationEndpoint';
import { escape, Run } from './Text';
class TextRun {
class TextRun implements Run {
text: string;
endpoint: NavigationEndpoint | undefined;
bold: boolean;
@@ -14,6 +15,25 @@ class TextRun {
this.strikethrough = Boolean(data.strikethrough);
this.endpoint = data.navigationEndpoint ? new NavigationEndpoint(data.navigationEndpoint) : undefined;
}
toString() {
return this.text;
}
toHTML(): string {
const tags: string[] = [];
if (this.bold) tags.push('b');
if (this.italics) tags.push('i');
if (this.strikethrough) tags.push('s');
const escaped_text = escape(this.text);
const styled_text = tags.map((tag) => `<${tag}>`).join('') + escaped_text + tags.map((tag) => `</${tag}>`).join('');
const wrapped_text = `<span style="white-space: pre-wrap;">${styled_text}</span>`;
if (this.endpoint) {
const url = this.endpoint.toURL();
if (url) return `<a href="${url}">${wrapped_text}</a>`;
}
return wrapped_text;
}
}
export default TextRun;