Path: blob/main/assets/js/emfed/core.js
1678 views
import { getToots } from "./client.js";1import DOMPurify from "../dompurify/purify.es.js";2/**3* Mark a string as safe for inclusion in HTML.4*/5function safe(s) {6return Object.assign(new String(s), { __safe: null });7}8/**9* Format a value as a string for templating.10*/11function flat(v) {12if (typeof v === "undefined" || v === null) {13return "";14}15else if (typeof v === "string" || v instanceof String) {16if (v.hasOwnProperty("__safe")) {17return v;18}19else {20// Escape strings for inclusion in HTML.21return v22.replaceAll("&", "&")23.replaceAll("<", "<")24.replaceAll(">", ">")25.replaceAll('"', """)26.replaceAll("'", "'");27}28}29else {30return v.map(flat).join("");31}32}33/**34* The world's dumbest templating system.35*/36function html(strings, ...subs) {37let out = strings[0];38for (let i = 1; i < strings.length; ++i) {39out += flat(subs[i - 1]);40out += strings[i];41}42return safe(out);43}44/**45* Render a single toot object as an HTML string.46*/47function renderToot(toot) {48// Is this a boost (reblog)?49let boost = null;50if (toot.reblog) {51boost = {52avatar: toot.account.avatar,53username: toot.account.username,54display_name: toot.account.display_name,55user_url: toot.account.url,56};57toot = toot.reblog; // Show the "inner" toot instead.58}59const date = new Date(toot.created_at).toLocaleString();60const images = toot.media_attachments.filter((att) => att.type === "image");61return html `<li class="toot">62<a class="permalink" href="${toot.url}">63<time datetime="${toot.created_at}">${date}</time>64</a>65${boost &&66html ` <a class="user boost" href="${boost.user_url}">67<img class="avatar" width="23" height="23" src="${boost.avatar}" />68<span class="display-name">${boost.display_name}</span>69<span class="username">@${boost.username}</span>70</a>`}71<a class="user" href="${toot.account.url}">72<img class="avatar" width="46" height="46" src="${toot.account.avatar}" />73<span class="display-name">${toot.account.display_name}</span>74<span class="username">@${toot.account.username}</span>75</a>76<div class="body">${safe(DOMPurify.sanitize(toot.content))}</div>77${images.map((att) => html ` <a78class="attachment"79href="${att.url}"80target="_blank"81rel="noopener noreferrer"82>83<img84class="attachment"85src="${att.preview_url}"86alt="${att.description}"87/>88</a>`)}89</li>`.toString();90}91/**92* Get the toots for an HTML element and replace that element with the93* rendered toot list.94*/95export async function loadToots(element) {96// Fetch toots based on the element's `data-toot-*` attributes.97const el = element;98const toots = await getToots(el.href, el.dataset.tootAccountId, Number(el.dataset.tootLimit ?? 5), el.dataset.excludeReplies === "true");99// Construct the HTML content.100const list = document.createElement("ol");101list.classList.add("toots");102el.replaceWith(list);103for (const toot of toots) {104const html = renderToot(toot);105list.insertAdjacentHTML("beforeend", html);106}107}108/**109* Transform all links on the page marked with the `mastodon-feed` class.110*/111export function loadAll() {112document.querySelectorAll("a.mastodon-feed").forEach(loadToots);113}114115116