|
/**
|
|
* @fileoverview Rant Content Component for Rantii
|
|
* @author retoor <retoor@molodetz.nl>
|
|
* @description Renders rant text with markdown, images, and media
|
|
* @keywords rant, content, markdown, media, render
|
|
*/
|
|
|
|
import { BaseComponent } from './base-component.js';
|
|
import { categorizeLinks, processTextWithLinks, extractImageUrls, extractYoutubeUrls, extractNonMediaUrls, sanitizeUrl } from '../utils/url.js';
|
|
|
|
class RantContent extends BaseComponent {
|
|
static get observedAttributes() {
|
|
return ['text', 'links'];
|
|
}
|
|
|
|
init() {
|
|
this.linksData = null;
|
|
this.render();
|
|
}
|
|
|
|
setLinks(links) {
|
|
this.linksData = links;
|
|
this.render();
|
|
}
|
|
|
|
render() {
|
|
const text = this.getAttr('text') || '';
|
|
const linksAttr = this.getAttr('links');
|
|
const links = this.linksData || (linksAttr ? JSON.parse(linksAttr) : null);
|
|
|
|
this.addClass('rant-content');
|
|
|
|
let images = [];
|
|
let youtubeLinks = [];
|
|
let otherLinks = [];
|
|
let renderedText = '';
|
|
|
|
if (links && links.length > 0) {
|
|
const categorized = categorizeLinks(links);
|
|
images = categorized.images.map(l => l.url);
|
|
youtubeLinks = categorized.youtube.map(l => l.url);
|
|
otherLinks = categorized.other;
|
|
renderedText = processTextWithLinks(text, links);
|
|
renderedText = renderedText.replace(/\n/g, '<br>');
|
|
} else {
|
|
images = extractImageUrls(text);
|
|
youtubeLinks = extractYoutubeUrls(text);
|
|
const extractedOther = extractNonMediaUrls(text);
|
|
otherLinks = extractedOther.map(url => ({ url, title: url }));
|
|
renderedText = this.escapeHtml(text).replace(/\n/g, '<br>');
|
|
}
|
|
|
|
let html = `<div class="content-text">${renderedText}</div>`;
|
|
|
|
if (images.length > 0) {
|
|
html += `
|
|
<div class="content-images">
|
|
${images.map(url => {
|
|
const safeUrl = sanitizeUrl(url);
|
|
return safeUrl ? `<image-preview src="${safeUrl}"></image-preview>` : '';
|
|
}).join('')}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
if (youtubeLinks.length > 0) {
|
|
html += `
|
|
<div class="content-videos">
|
|
${youtubeLinks.map(url => {
|
|
const safeUrl = sanitizeUrl(url);
|
|
return safeUrl ? `<youtube-embed url="${safeUrl}"></youtube-embed>` : '';
|
|
}).join('')}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
if (otherLinks.length > 0) {
|
|
html += `
|
|
<div class="content-links">
|
|
${otherLinks.slice(0, 3).map(link => {
|
|
const url = typeof link === 'string' ? link : link.url;
|
|
const title = typeof link === 'string' ? link : (link.title || link.url);
|
|
const safeUrl = sanitizeUrl(url);
|
|
return safeUrl ? `<link-preview url="${safeUrl}" title="${this.escapeAttr(title)}"></link-preview>` : '';
|
|
}).join('')}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
this.setHtml(html);
|
|
}
|
|
|
|
escapeHtml(str) {
|
|
if (!str) return '';
|
|
return str
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
escapeAttr(str) {
|
|
if (!str) return '';
|
|
return str
|
|
.replace(/&/g, '&')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>');
|
|
}
|
|
|
|
onAttributeChanged(name, oldValue, newValue) {
|
|
this.render();
|
|
}
|
|
|
|
setText(text, links = null) {
|
|
this.linksData = links;
|
|
this.setAttr('text', text);
|
|
}
|
|
}
|
|
|
|
customElements.define('rant-content', RantContent);
|
|
|
|
export { RantContent };
|