const id = getElement('metadata-post').innerText; if (id) { const host = getElement('metadata-instance').innerText; const i18nreplies = getElement('i18n--is-replies').innerHTML; const i18nreblogs = getElement('i18n--is-reblogs').innerHTML; const i18nfavourites = getElement('i18n--is-favourites').innerHTML; const style = document.createElement('style'); style.textContent = ` #comments > * {width: var(--golden-ratio)} #comments noscript {margin: var(--medskip) 0} #discussion-starter {margin-bottom: var(--medskip)} #discussion-starter > footer {display: flex; align-items: center; justify-content: space-between} .mastodon-comment {margin: 1rem 0 1rem calc(var(--mul) * var(--indent)); border: 1pt solid #fff4; border-left: 2pt solid var(--ac); background: #80808008; padding: 1rem 1rem 1ex; box-shadow: 0 .5pt 1pt 0 var(--g18s); overflow: auto} .mastodon-comment .content {margin-left: 4rem; line-height: calc(var(--baselineStretch) * 1.272)} .mastodon-comment .par a {max-width: 100%; vertical-align: bottom; white-space: break-spaces} .mastodon-comment .attachments * {width: 100%; height: auto} .mastodon-comment > footer {margin-top: 1rem; margin-left: 3.5rem} .mastodon-comment > footer .stat {display: inline-flex; flex-shrink: 0; gap: 5pt} .stat a {display: inline-flex; align-items: center; padding: 2pt; color: var(--mid); gap: 2pt} .stat a::before {vertical-align: text-top} a.replies.active, a.reblogs.active {color: var(--ac)} a.favourites.active {color: var(--i3i)} .mastodon-comment .date {margin-left: auto; padding-left: 1rem; color: var(--mid); font-size: calc(10pt * var(--fontScale))} @media only screen and (max-width: 960px) { .mastodon-comment .content, .mastodon-comment > footer {margin-left: 0} } `; document.head.appendChild(style); function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } let commentsLoaded = false; const toot_active = (toot, what) => { const count = toot[`${what}_count`]; return count > 0 ? 'active' : ''; }; const toot_count = (toot, what) => { const count = toot[`${what}_count`]; return count > 0 ? count : ''; }; const user_account = (account) => { let result = `@${account.acct}`; if (!account.acct.includes('@')) { const domain = new URL(account.url); result += `@${domain.hostname}`; } return result; }; const render_toots = (toots, in_reply_to, depth) => { const 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 => render_toot(toots, toot, depth)); }; const render_toot = (toots, toot, depth) => { toot.account.display_name = 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 renderAttachment = attachment => { const attachmentTypes = { image: () => `${attachment.description}`, video: () => ``, gifv: () => ``, audio: () => ``, default: () => `${attachment.type}` }; return (attachmentTypes[attachment.type] || attachmentTypes.default)(); }; const mastodonComment = `
${user_account(toot.account)} ${toot.account.display_name}
${toot.content}
${toot.media_attachments.map(renderAttachment).join('')}
`; getElement('mastodon-comments-list') .appendChild(DOMPurify.sanitize(mastodonComment, {'RETURN_DOM_FRAGMENT': true})); render_toots(toots, toot.id, depth + 1); }; const toot_stats = toot => ` ${toot_count(toot, 'replies')} ${toot_count(toot, 'reblogs')} ${toot_count(toot, 'favourites')} `; const loadComments = async () => { if (commentsLoaded) return; const commentsList = getElement('mastodon-comments-list'); commentsList.innerHTML = getElement('i18n--is-loading').innerHTML; try { const [tootResponse, contextResponse] = await Promise.all([ fetch(`https://${host}/api/v1/statuses/${id}`), fetch(`https://${host}/api/v1/statuses/${id}/context`) ]); const [toot, data] = await Promise.all([ tootResponse.json(), contextResponse.json() ]); getElement("mastodon-stats").innerHTML = toot_stats(toot); if (data.descendants?.length > 0) { commentsList.innerHTML = ""; render_toots(data.descendants, id, 0); } else { commentsList.innerHTML = getElement('i18n--no-comment').innerHTML; } commentsLoaded = true; commentsList.setAttribute('aria-busy', 'false'); } catch (error) { console.error('Error loading comments:', error); commentsList.innerHTML = 'Error loading comments'; } }; const respondToVisibility = (element, callback) => { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.intersectionRatio > 0) { callback(); } }); }, { root: null }); observer.observe(element); }; const comments = getElement("mastodon-comments-list"); respondToVisibility(comments, loadComments); }