Skip to content

Commit

Permalink
feat(parser): Text#toHTML (#300)
Browse files Browse the repository at this point in the history
Added support to render Text nodes as HTML for use in web applications.
  • Loading branch information
Wykerd authored Feb 1, 2023
1 parent f62c66d commit e82e23d
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/parser/classes/NavigationEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
12 changes: 11 additions & 1 deletion src/parser/classes/misc/EmojiRun.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
19 changes: 19 additions & 0 deletions src/parser/classes/misc/Text.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -17,6 +32,10 @@ class Text {
}
}

toHTML() {
return this.runs ? this.runs.map((run) => run.toHTML()).join('') : this.text;
}

toString() {
return this.text;
}
Expand Down
22 changes: 21 additions & 1 deletion src/parser/classes/misc/TextRun.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

0 comments on commit e82e23d

Please sign in to comment.