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