const styles = ` :root { --font-color: #5d686f; --font-size: 1.0rem; --block-border-width: 1px; --block-border-radius: 3px; --block-border-color: #ededf0; --block-background-color: #f7f8f8; --comment-indent: 40px; } #mastodon-stats { text-align: center; font-size: calc(var(--font-size) * 2) } #mastodon-comments-list { margin: 0 auto; margin-top: 1rem; } .mastodon-comment { background-color: var(--block-background-color); border-radius: var(--block-border-radius); border: var(--block-border-width) var(--block-border-color) solid; padding: 20px; margin-bottom: 1.5rem; display: flex; flex-direction: column; color: var(--font-color); font-size: var(--font-size); } .mastodon-comment p { margin-bottom: 0px; } .mastodon-comment .author { padding-top:0; display:flex; } .mastodon-comment .author a { text-decoration: none; } .mastodon-comment .author .avatar img { margin-right:1rem; min-width:60px; border-radius: 5px; } .mastodon-comment .author .details { display: flex; flex-direction: column; } .mastodon-comment .author .details .name { font-weight: bold; } .mastodon-comment .author .details .user { color: #5d686f; font-size: medium; } .mastodon-comment .author .date { margin-left: auto; font-size: small; } .mastodon-comment .content { margin: 15px 20px; } .mastodon-comment .attachments { margin: 0px 10px; } .mastodon-comment .attachments > * { margin: 0px 10px; } .mastodon-comment .attachments img { max-width: 100%; } .mastodon-comment .content p:first-child { margin-top:0; margin-bottom:0; } .mastodon-comment .status > div, #mastodon-stats > div { display: inline-block; margin-right: 15px; } .mastodon-comment .status a, #mastodon-stats a { color: #5d686f; text-decoration: none; } .mastodon-comment .status .replies.active a, #mastodon-stats .replies.active a { color: #003eaa; } .mastodon-comment .status .reblogs.active a, #mastodon-stats .reblogs.active a { color: #8c8dff; } .mastodon-comment .status .favourites.active a, #mastodon-stats .favourites.active a { color: #ca8f04; } `; class MastodonComments extends HTMLElement { constructor() { super(); // Retrieve the host, user, and tootId from global variables this.host = mastodonHost; // Previously this.getAttribute("host") this.user = mastodonUser; // Previously this.getAttribute("user") this.tootId = mastodonTootId; // Previously this.getAttribute("tootId") this.commentsLoaded = false; const styleElem = document.createElement("style"); styleElem.innerHTML = styles; document.head.appendChild(styleElem); } connectedCallback() { this.innerHTML = `

Comments

You can use your Fediverse (i.e. Mastodon, among many others) account to reply to this post.

`; const comments = document.getElementById("mastodon-comments-list"); const rootStyle = this.getAttribute("style"); if (rootStyle) { comments.setAttribute("style", rootStyle); } this.respondToVisibility(comments, this.loadComments.bind(this)); } escapeHtml(unsafe) { return (unsafe || "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } toot_active(toot, what) { var count = toot[what + "_count"]; return count > 0 ? "active" : ""; } toot_count(toot, what) { var count = toot[what + "_count"]; return count > 0 ? count : ""; } toot_stats(toot) { return `
${this.toot_count( toot, "replies", )}
${this.toot_count( toot, "reblogs", )}
${this.toot_count( toot, "favourites", )}
`; } user_account(account) { var result = `@${account.acct}`; if (account.acct.indexOf("@") === -1) { var domain = new URL(account.url); result += `@${domain.hostname}`; } return result; } render_toots(toots, in_reply_to, depth) { var tootsToRender = toots .filter((toot) => toot.in_reply_to_id === in_reply_to) .sort((a, b) => a.created_at.localeCompare(b.created_at)); tootsToRender.forEach((toot) => this.render_toot(toots, toot, depth)); } render_toot(toots, toot, depth) { toot.account.display_name = this.escapeHtml(toot.account.display_name); toot.account.emojis.forEach((emoji) => { toot.account.display_name = toot.account.display_name.replace( `:${emoji.shortcode}:`, `Emoji ${
          emoji.shortcode
        }`, ); }); const mastodonComment = `
${toot.created_at.substr( 0, 10, )} ${toot.created_at.substr(11, 8)}
${toot.content}
${toot.media_attachments .map((attachment) => { if (attachment.type === "image") { return `${this.escapeHtml(attachment.description)}`; } else if (attachment.type === "video") { return ``; } else if (attachment.type === "gifv") { return ``; } else if (attachment.type === "audio") { return ``; } else { return `${attachment.type}`; } }) .join("")}
${this.toot_stats(toot)}
`; var div = document.createElement("div"); div.innerHTML = typeof DOMPurify !== "undefined" ? DOMPurify.sanitize(mastodonComment.trim()) : mastodonComment.trim(); document .getElementById("mastodon-comments-list") .appendChild(div.firstChild); this.render_toots(toots, toot.id, depth + 1); } loadComments() { if (this.commentsLoaded) return; document.getElementById("mastodon-comments-list").innerHTML = "Loading comments from the Fediverse..."; let _this = this; fetch("https://" + this.host + "/api/v1/statuses/" + this.tootId) .then((response) => response.json()) .then((toot) => { document.getElementById("mastodon-stats").innerHTML = this.toot_stats(toot); }); fetch( "https://" + this.host + "/api/v1/statuses/" + this.tootId + "/context", ) .then((response) => response.json()) .then((data) => { if ( data["descendants"] && Array.isArray(data["descendants"]) && data["descendants"].length > 0 ) { document.getElementById("mastodon-comments-list").innerHTML = ""; _this.render_toots(data["descendants"], _this.tootId, 0); } else { document.getElementById("mastodon-comments-list").innerHTML = "

No comments found

"; } _this.commentsLoaded = true; }); } respondToVisibility(element, callback) { var options = { root: null, }; var observer = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { if (entry.intersectionRatio > 0) { callback(); } }); }, options); observer.observe(element); } } customElements.define("mastodon-comments", MastodonComments);