Compare commits
4
Commits
2b68602f86
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebee3479b1
|
||
|
|
ca4231f8b0
|
||
|
|
a28cc7cd39
|
||
|
|
9d6cc301e8
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -3,6 +3,9 @@
|
|||||||
padding: 1em;
|
padding: 1em;
|
||||||
padding-top: 0.3em;
|
padding-top: 0.3em;
|
||||||
}
|
}
|
||||||
|
.mastodon-comments-top {
|
||||||
|
padding-bottom: 1.5em;
|
||||||
|
}
|
||||||
.mastodon-comment {
|
.mastodon-comment {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
border-radius: var(--card-border-radius);
|
border-radius: var(--card-border-radius);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* PrismJS 1.29.0
|
/* PrismJS 1.29.0
|
||||||
https://prismjs.com/download.html#themes=prism-twilight&languages=markup+css+clike+javascript+bash+go+go-module+json+ruby+typescript+yaml&plugins=line-highlight+line-numbers+autoloader+toolbar+copy-to-clipboard */
|
https://prismjs.com/download.html#themes=prism-twilight&languages=markup+css+clike+javascript+bash+go+go-module+handlebars+json+markup-templating+ruby+typescript+yaml&plugins=line-highlight+line-numbers+toolbar+copy-to-clipboard */
|
||||||
code[class*=language-],pre[class*=language-]{color:#fff;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;text-shadow:0 -.1em .2em #000;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}:not(pre)>code[class*=language-],pre[class*=language-]{background:#141414}pre[class*=language-]{border-radius:.5em;border:.3em solid #545454;box-shadow:1px 1px .5em #000 inset;margin:.5em 0;overflow:auto;padding:1em}pre[class*=language-]::-moz-selection{background:#27292a}pre[class*=language-]::selection{background:#27292a}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:hsla(0,0%,93%,.15)}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:hsla(0,0%,93%,.15)}:not(pre)>code[class*=language-]{border-radius:.3em;border:.13em solid #545454;box-shadow:1px 1px .3em -.1em #000 inset;padding:.15em .2em .05em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#777}.token.punctuation{opacity:.7}.token.namespace{opacity:.7}.token.boolean,.token.deleted,.token.number,.token.tag{color:#ce6849}.token.builtin,.token.constant,.token.keyword,.token.property,.token.selector,.token.symbol{color:#f9ed99}.language-css .token.string,.style .token.string,.token.attr-name,.token.attr-value,.token.char,.token.entity,.token.inserted,.token.operator,.token.string,.token.url,.token.variable{color:#909e6a}.token.atrule{color:#7385a5}.token.important,.token.regex{color:#e8c062}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.language-markup .token.attr-name,.language-markup .token.punctuation,.language-markup .token.tag{color:#ac885c}.token{position:relative;z-index:1}.line-highlight.line-highlight{background:hsla(0,0%,33%,.25);background:linear-gradient(to right,hsla(0,0%,33%,.1) 70%,hsla(0,0%,33%,0));border-bottom:1px dashed #545454;border-top:1px dashed #545454;margin-top:.75em;z-index:0}.line-highlight.line-highlight:before,.line-highlight.line-highlight[data-end]:after{background-color:#8693a6;color:#f4f1ef}
|
code[class*=language-],pre[class*=language-]{color:#fff;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;text-shadow:0 -.1em .2em #000;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}:not(pre)>code[class*=language-],pre[class*=language-]{background:#141414}pre[class*=language-]{border-radius:.5em;border:.3em solid #545454;box-shadow:1px 1px .5em #000 inset;margin:.5em 0;overflow:auto;padding:1em}pre[class*=language-]::-moz-selection{background:#27292a}pre[class*=language-]::selection{background:#27292a}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:hsla(0,0%,93%,.15)}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:hsla(0,0%,93%,.15)}:not(pre)>code[class*=language-]{border-radius:.3em;border:.13em solid #545454;box-shadow:1px 1px .3em -.1em #000 inset;padding:.15em .2em .05em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#777}.token.punctuation{opacity:.7}.token.namespace{opacity:.7}.token.boolean,.token.deleted,.token.number,.token.tag{color:#ce6849}.token.builtin,.token.constant,.token.keyword,.token.property,.token.selector,.token.symbol{color:#f9ed99}.language-css .token.string,.style .token.string,.token.attr-name,.token.attr-value,.token.char,.token.entity,.token.inserted,.token.operator,.token.string,.token.url,.token.variable{color:#909e6a}.token.atrule{color:#7385a5}.token.important,.token.regex{color:#e8c062}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.language-markup .token.attr-name,.language-markup .token.punctuation,.language-markup .token.tag{color:#ac885c}.token{position:relative;z-index:1}.line-highlight.line-highlight{background:hsla(0,0%,33%,.25);background:linear-gradient(to right,hsla(0,0%,33%,.1) 70%,hsla(0,0%,33%,0));border-bottom:1px dashed #545454;border-top:1px dashed #545454;margin-top:.75em;z-index:0}.line-highlight.line-highlight:before,.line-highlight.line-highlight[data-end]:after{background-color:#8693a6;color:#f4f1ef}
|
||||||
pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)}
|
pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)}
|
||||||
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
|
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,7 @@
|
|||||||
const MASTODON_ACCOUNT_ID = '109285376472065471'
|
const MASTODON_ACCOUNT_ID = '109285376472065471'
|
||||||
const MASTODON_HOST = 'social.sd.ai'
|
const MASTODON_HOST = 'social.sd.ai'
|
||||||
|
|
||||||
|
// Copies the text from an element to the clipboard, and flashes the element to provide visual feedback
|
||||||
async function copyElementTextToClipboard(e)
|
async function copyElementTextToClipboard(e)
|
||||||
{
|
{
|
||||||
const text = e.textContent
|
const text = e.textContent
|
||||||
@@ -12,6 +13,7 @@ async function copyElementTextToClipboard(e)
|
|||||||
}, 600);
|
}, 600);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitise text content for display
|
||||||
function escapeHtml(unsafe) {
|
function escapeHtml(unsafe) {
|
||||||
return unsafe
|
return unsafe
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
@@ -21,16 +23,24 @@ function escapeHtml(unsafe) {
|
|||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// renders the content - provides an array of toots, the element to add the content to and whether to show a copyable link for replies
|
||||||
function renderMastodonContent(toots, parentElement, showLink) {
|
function renderMastodonContent(toots, parentElement, showLink) {
|
||||||
|
// clear the parent element so that we can add the new content
|
||||||
parentElement.innerHTML = ''
|
parentElement.innerHTML = ''
|
||||||
|
// add a simple "no comments" message if there are no toots
|
||||||
if (!Array.isArray(toots) || toots.length === 0) {
|
if (!Array.isArray(toots) || toots.length === 0) {
|
||||||
document.getElementById('mastodon-comments-list').innerHTML = "<div class='mastodon-comment'>No comments (yet)!</div>"
|
document.getElementById('mastodon-comments-list').innerHTML = "<div class='mastodon-comment'>No comments (yet)!</div>"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render each toot
|
||||||
for (const toot of toots) {
|
for (const toot of toots) {
|
||||||
if (toot.sensitive) {
|
if (toot.sensitive) {
|
||||||
|
// don't display toots marked as sensitive
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sanitise the toot content for display, including correctly rendering custom emojis
|
||||||
toot.account.display_name = escapeHtml(toot.account.display_name)
|
toot.account.display_name = escapeHtml(toot.account.display_name)
|
||||||
toot.account.emojis.forEach(emoji => {
|
toot.account.emojis.forEach(emoji => {
|
||||||
toot.account.display_name = toot.account.display_name.replace(`:${emoji.shortcode}:`,
|
toot.account.display_name = toot.account.display_name.replace(`:${emoji.shortcode}:`,
|
||||||
@@ -40,6 +50,8 @@ function renderMastodonContent(toots, parentElement, showLink) {
|
|||||||
toot.content = toot.content.replace(`:${emoji.shortcode}:`,
|
toot.content = toot.content.replace(`:${emoji.shortcode}:`,
|
||||||
`<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" class="mastodon-emoji" />`);
|
`<img src="${escapeHtml(emoji.static_url)}" alt="Emoji ${emoji.shortcode}" class="mastodon-emoji" />`);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// create a block of HTML content including the toot data
|
||||||
const comment =
|
const comment =
|
||||||
`<div class="mastodon-comment">
|
`<div class="mastodon-comment">
|
||||||
<div class="mastodon-avatar">
|
<div class="mastodon-avatar">
|
||||||
@@ -69,30 +81,44 @@ function renderMastodonContent(toots, parentElement, showLink) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
|
// Use DOMPurify to create a sanitised element for the toot
|
||||||
const child = DOMPurify.sanitize(comment, {'RETURN_DOM_FRAGMENT': true});
|
const child = DOMPurify.sanitize(comment, {'RETURN_DOM_FRAGMENT': true});
|
||||||
|
|
||||||
|
// make all toot links clickable
|
||||||
const links = child.querySelectorAll('.tootlink');
|
const links = child.querySelectorAll('.tootlink');
|
||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
link.onclick = function() { return copyElementTextToClipboard(this); }
|
link.onclick = function() { return copyElementTextToClipboard(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insert the toot into the DOM
|
||||||
parentElement.appendChild(child);
|
parentElement.appendChild(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We set this in the "code injection" footer for any page for which we want to enable comments
|
||||||
let MASTODON_POST_ID
|
let MASTODON_POST_ID
|
||||||
|
|
||||||
|
// when the page has finished loading, send a request for the toots
|
||||||
document.addEventListener("DOMContentLoaded", async (event) => {
|
document.addEventListener("DOMContentLoaded", async (event) => {
|
||||||
let url, isComments
|
let url, isComments
|
||||||
|
// if we're being crawled, don't render comments - may help against spam
|
||||||
const isBot = /bot|google|baidu|bing|msn|teoma|slurp|yandex/i
|
const isBot = /bot|google|baidu|bing|msn|teoma|slurp|yandex/i
|
||||||
.test(navigator.userAgent)
|
.test(navigator.userAgent)
|
||||||
|
|
||||||
|
// if there is a sidebar, we're expecting to load the toots from the main account
|
||||||
if (document.getElementsByClassName('gh-sidebar').length > 0) {
|
if (document.getElementsByClassName('gh-sidebar').length > 0) {
|
||||||
url = `https://${MASTODON_HOST}/api/v1/accounts/${MASTODON_ACCOUNT_ID}/statuses?exclude_replies=true&exclude_reblogs=true`
|
url = `https://${MASTODON_HOST}/api/v1/accounts/${MASTODON_ACCOUNT_ID}/statuses?exclude_replies=true&exclude_reblogs=true`
|
||||||
}
|
}
|
||||||
|
// if there's a post ID and we're not a bot, we're expecting to load the replies from a specific toot
|
||||||
if (MASTODON_POST_ID && !isBot) {
|
if (MASTODON_POST_ID && !isBot) {
|
||||||
url = `https://${MASTODON_HOST}/api/v1/statuses/${MASTODON_POST_ID}/context`
|
url = `https://${MASTODON_HOST}/api/v1/statuses/${MASTODON_POST_ID}/context`
|
||||||
isComments = true
|
isComments = true
|
||||||
}
|
}
|
||||||
|
// find the element to append the content to - if there isn't one, we don't need to query
|
||||||
const element = document.getElementById('mastodon-comments-list')
|
const element = document.getElementById('mastodon-comments-list')
|
||||||
if (url && element) {
|
if (url && element) {
|
||||||
|
// populate the link to the source toot, if necessary (for replies)
|
||||||
const linkElement = document.getElementById('toot-link-top')
|
const linkElement = document.getElementById('toot-link-top')
|
||||||
const clipElement = document.getElementById('toot-link-clip')
|
const clipElement = document.getElementById('toot-link-clip')
|
||||||
const tootUrl = `https://${MASTODON_HOST}/@s/${MASTODON_POST_ID}`
|
const tootUrl = `https://${MASTODON_HOST}/@s/${MASTODON_POST_ID}`
|
||||||
@@ -102,11 +128,13 @@ document.addEventListener("DOMContentLoaded", async (event) => {
|
|||||||
if (clipElement) {
|
if (clipElement) {
|
||||||
clipElement.innerText = tootUrl
|
clipElement.innerText = tootUrl
|
||||||
}
|
}
|
||||||
|
// fetch the data from Mastodon
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
let content = await response.json()
|
let content = await response.json()
|
||||||
if (isComments) {
|
if (isComments) {
|
||||||
content = content.descendants
|
content = content.descendants
|
||||||
}
|
}
|
||||||
|
// render the content into the page
|
||||||
const header = document.getElementById('mastodon-comments-header')
|
const header = document.getElementById('mastodon-comments-header')
|
||||||
if (header) {
|
if (header) {
|
||||||
header.style.display = ''
|
header.style.display = ''
|
||||||
|
|||||||
@@ -49,9 +49,14 @@
|
|||||||
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
|
||||||
<section class="gh-comments gh-canvas">
|
<section class="gh-comments gh-canvas">
|
||||||
|
|
||||||
<div id="mastodon-comments-header" style="display: none;">
|
<div id="mastodon-comments-header" style="display: none;">
|
||||||
|
|
||||||
|
<div class="mastodon-comments-top">
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Comments</h2>
|
<h2>Comments</h2>
|
||||||
<p>Comments powered by Mastodon. Reply to <a id="toot-link-top">this status</a> to comment.</p>
|
<p>Comments powered by Mastodon. Reply to <a id="toot-link-top">this status</a> to comment.</p>
|
||||||
<div class="tootlink-tip">
|
<div class="tootlink-tip">
|
||||||
@@ -74,7 +79,7 @@
|
|||||||
|
|
||||||
{{/post}}
|
{{/post}}
|
||||||
|
|
||||||
{{#get "posts" include="authors" filter="id:-{{post.id}}" limit="4" as |next|}}
|
{{#get "posts" include="authors,tags" filter="id:-{{post.id}}" limit="4" as |next|}}
|
||||||
{{#if next}}
|
{{#if next}}
|
||||||
<section class="gh-container is-grid gh-outer">
|
<section class="gh-container is-grid gh-outer">
|
||||||
<div class="gh-container-inner gh-inner">
|
<div class="gh-container-inner gh-inner">
|
||||||
|
|||||||
Reference in New Issue
Block a user