✨ feat: add support for webmentions
This commit is contained in:
43
config.toml
43
config.toml
@@ -278,6 +278,49 @@ voting = true
|
|||||||
page_author_hashes = "" # hash (or list of hashes) of the author.
|
page_author_hashes = "" # hash (or list of hashes) of the author.
|
||||||
lazy_loading = true # Loads when the comments are in the viewport (using the Intersection Observer API).
|
lazy_loading = true # Loads when the comments are in the viewport (using the Intersection Observer API).
|
||||||
|
|
||||||
|
|
||||||
|
[extra.webmentions]
|
||||||
|
# To disable for a specific section or page, set webmentions = false in that page/section's front matter's [extra] section.
|
||||||
|
enable = false
|
||||||
|
# Specify the domain registered with webmention.io.
|
||||||
|
domain = ""
|
||||||
|
|
||||||
|
# The HTML ID for the object to fill in with the webmention data.
|
||||||
|
# Defaults to "webmentions"
|
||||||
|
# id = "webmentions"
|
||||||
|
|
||||||
|
# data configuration for the webmention.min.js script
|
||||||
|
# The base URL to use for this page. Defaults to window.location
|
||||||
|
# page_url =
|
||||||
|
|
||||||
|
# Additional URLs to check, separated by |s
|
||||||
|
# add_urls
|
||||||
|
|
||||||
|
# The maximum number of words to render in reply mentions.
|
||||||
|
# wordcount = 20
|
||||||
|
|
||||||
|
# The maximum number of mentions to retrieve. Defaults to 30.
|
||||||
|
# max_webmentions = 30
|
||||||
|
|
||||||
|
# By default, Webmentions render using the mf2 'url' element, which plays
|
||||||
|
# nicely with webmention bridges (such as brid.gy and telegraph)
|
||||||
|
# but allows certain spoofing attacks. If you would like to prevent
|
||||||
|
# spoofing, set this to a non-empty string (e.g. "true").
|
||||||
|
# prevent_spoofing
|
||||||
|
|
||||||
|
# What to order the responses by; defaults to 'published'. See
|
||||||
|
# https://github.com/aaronpk/webmention.io#api
|
||||||
|
# sort_by
|
||||||
|
|
||||||
|
# The order to sort the responses by; defaults to 'up' (i.e. oldest
|
||||||
|
# first). See https://github.com/aaronpk/webmention.io#api
|
||||||
|
# sort_dir
|
||||||
|
|
||||||
|
# If set to a non-empty string (e.g. "true"), will display comment-type responses
|
||||||
|
# (replies/mentions/etc.) as being part of the reactions
|
||||||
|
# (favorites/bookmarks/etc.) instead of in a separate comment list.
|
||||||
|
# comments_are_reactions = "true"
|
||||||
|
|
||||||
# h-card configuration
|
# h-card configuration
|
||||||
# Will identify you on the indieweb (see https://microformats.org/wiki/h-card)
|
# Will identify you on the indieweb (see https://microformats.org/wiki/h-card)
|
||||||
[extra.hcard]
|
[extra.hcard]
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
@use 'parts/_tags.scss';
|
@use 'parts/_tags.scss';
|
||||||
@use 'parts/_theme-switch.scss';
|
@use 'parts/_theme-switch.scss';
|
||||||
@use 'parts/_zola-error.scss';
|
@use 'parts/_zola-error.scss';
|
||||||
|
@use 'parts/_webmention.scss';
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
src: local('Open Sans'),
|
src: local('Open Sans'),
|
||||||
|
|||||||
149
themes/tabi/sass/parts/_webmention.scss
Normal file
149
themes/tabi/sass/parts/_webmention.scss
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#webmentions {
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
margin: 0;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
line-height: 1.2em;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-inline-end: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-icon,
|
||||||
|
span {
|
||||||
|
margin-inline-end: .3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li,
|
||||||
|
p {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.likes {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
transition: transform 0.8s ease-out, z-index 0s linear 0.4s;
|
||||||
|
margin-bottom: .375rem;
|
||||||
|
margin-inline-start: -.75rem;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.3) translateY(-4px);
|
||||||
|
z-index: 10;
|
||||||
|
transition: transform 0.05s ease-out, z-index 0s linear 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
border: 2px solid var(--background-color, white);
|
||||||
|
border-radius: 50%;
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--bg-0);
|
||||||
|
padding: 1rem;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 80%;
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-author {
|
||||||
|
font-style: bold;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-url {
|
||||||
|
font-style: italic;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-author {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
margin-inline-end: .625rem;
|
||||||
|
width: 2rem;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
input {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 20px 0px 0px 20px;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
padding-inline: 1rem 1rem;
|
||||||
|
padding-block: .75rem;
|
||||||
|
width: calc(60% - 2rem);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 0px 20px 20px 0px;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
padding-inline: 0.7rem 0.7rem;
|
||||||
|
padding-block: .75rem;
|
||||||
|
width: 7rem;
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--hover-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
412
themes/tabi/static/js/webmention.js
Normal file
412
themes/tabi/static/js/webmention.js
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
/* webmention.js
|
||||||
|
|
||||||
|
Simple thing for embedding webmentions from webmention.io into a page, client-side.
|
||||||
|
|
||||||
|
(c)2018-2022 fluffy (http://beesbuzz.biz)
|
||||||
|
2025 mmai (https://misc.rhumbs.fr)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Basic usage:
|
||||||
|
|
||||||
|
<script src="/path/to/webmention.js" data-param="val" ... async />
|
||||||
|
<div id="webmentions"></div>
|
||||||
|
|
||||||
|
Allowed parameters:
|
||||||
|
|
||||||
|
page-url:
|
||||||
|
|
||||||
|
The base URL to use for this page. Defaults to window.location
|
||||||
|
|
||||||
|
add-urls:
|
||||||
|
|
||||||
|
Additional URLs to check, separated by |s
|
||||||
|
|
||||||
|
id:
|
||||||
|
|
||||||
|
The HTML ID for the object to fill in with the webmention data.
|
||||||
|
Defaults to "webmentions"
|
||||||
|
|
||||||
|
wordcount:
|
||||||
|
|
||||||
|
The maximum number of words to render in reply mentions.
|
||||||
|
|
||||||
|
max-webmentions:
|
||||||
|
|
||||||
|
The maximum number of mentions to retrieve. Defaults to 30.
|
||||||
|
|
||||||
|
prevent-spoofing:
|
||||||
|
|
||||||
|
By default, Webmentions render using the mf2 'url' element, which plays
|
||||||
|
nicely with webmention bridges (such as brid.gy and telegraph)
|
||||||
|
but allows certain spoofing attacks. If you would like to prevent
|
||||||
|
spoofing, set this to a non-empty string (e.g. "true").
|
||||||
|
|
||||||
|
sort-by:
|
||||||
|
|
||||||
|
What to order the responses by; defaults to 'published'. See
|
||||||
|
https://github.com/aaronpk/webmention.io#api
|
||||||
|
|
||||||
|
sort-dir:
|
||||||
|
|
||||||
|
The order to sort the responses by; defaults to 'up' (i.e. oldest
|
||||||
|
first). See https://github.com/aaronpk/webmention.io#api
|
||||||
|
|
||||||
|
comments-are-reactions:
|
||||||
|
|
||||||
|
If set to a non-empty string (e.g. "true"), will display comment-type responses
|
||||||
|
(replies/mentions/etc.) as being part of the reactions
|
||||||
|
(favorites/bookmarks/etc.) instead of in a separate comment list.
|
||||||
|
|
||||||
|
A more detailed example:
|
||||||
|
|
||||||
|
<!-- If you want to translate the UI -->
|
||||||
|
<script src="/path/to/umd/i18next.js"></script>
|
||||||
|
<script>
|
||||||
|
// Setup i18next as described in https://www.i18next.com/overview/getting-started#basic-sample
|
||||||
|
</script>
|
||||||
|
<!-- Otherwise, only using the following is fine -->
|
||||||
|
<script src="/path/to/webmention.min.js"
|
||||||
|
data-id="webmentionContainer"
|
||||||
|
data-wordcount="30"
|
||||||
|
data-prevent-spoofing="true"
|
||||||
|
data-comments-are-reactions="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Begin LibreJS code licensing
|
||||||
|
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Shim i18next
|
||||||
|
window.i18next = window.i18next || {
|
||||||
|
t: function t(/** @type {string} */key) { return key; }
|
||||||
|
}
|
||||||
|
const t = window.i18next.t.bind(window.i18next);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the configuration value.
|
||||||
|
*
|
||||||
|
* @param {string} key The configuration key.
|
||||||
|
* @param {string} dfl The default value.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function getCfg(key, dfl) {
|
||||||
|
return document.currentScript.getAttribute("data-" + key) || dfl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const refurl = getCfg("page-url", window.location.href.replace(/#.*$/, ""));
|
||||||
|
const addurls = getCfg("add-urls", undefined);
|
||||||
|
const containerID = getCfg("id", "webmentions");
|
||||||
|
/** @type {Number} */
|
||||||
|
const textMaxWords = getCfg("wordcount");
|
||||||
|
const maxWebmentions = getCfg("max-webmentions", 30);
|
||||||
|
const mentionSource = getCfg("prevent-spoofing") ? "wm-source" : "url";
|
||||||
|
const sortBy = getCfg("sort-by", "published");
|
||||||
|
const sortDir = getCfg("sort-dir", "up");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip the protocol off a URL.
|
||||||
|
*
|
||||||
|
* @param {string} url The URL to strip protocol off.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function stripurl(url) {
|
||||||
|
return url.substr(url.indexOf('//'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deduplicate multiple mentions from the same source URL.
|
||||||
|
*
|
||||||
|
* @param {Array<Reaction>} mentions Mentions of the source URL.
|
||||||
|
* @return {Array<Reaction>}
|
||||||
|
*/
|
||||||
|
function dedupe(mentions) {
|
||||||
|
/** @type {Array<Reaction>} */
|
||||||
|
const filtered = [];
|
||||||
|
/** @type {Record<string, boolean>} */
|
||||||
|
const seen = {};
|
||||||
|
|
||||||
|
mentions.forEach(function (r) {
|
||||||
|
// Strip off the protocol (i.e. treat http and https the same)
|
||||||
|
const source = stripurl(r.url);
|
||||||
|
if (!seen[source]) {
|
||||||
|
filtered.push(r);
|
||||||
|
seen[source] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format comments as HTML.
|
||||||
|
*
|
||||||
|
* @param {Array<Reaction>} comments The comments to format.
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
function formatComments(type, comments) {
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class="webcomments">
|
||||||
|
<h3 id="replies-header">` + getIcon('comment') + ` <span>` + comments.length + `</span> ` + type + `s </h3>
|
||||||
|
<ol aria-labelledby="replies-header" role="list">`;
|
||||||
|
comments.forEach(function (comment) {
|
||||||
|
let content = '';
|
||||||
|
if (comment.hasOwnProperty('content')) {
|
||||||
|
if (comment.content.hasOwnProperty('html')) {
|
||||||
|
content = comment.content.html;
|
||||||
|
} else if (comment.content.hasOwnProperty('text')) {
|
||||||
|
content = comment.content.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<li class="comment h-entry">
|
||||||
|
<div>
|
||||||
|
<a class="comment_author u-author"
|
||||||
|
href="`+ comment.author.url + `"
|
||||||
|
target="_blank"
|
||||||
|
title="`+ comment.author.name + `"
|
||||||
|
rel="noreferrer">
|
||||||
|
<img
|
||||||
|
src="`+ comment.author.photo + `"
|
||||||
|
alt=""
|
||||||
|
class="u-photo"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
>
|
||||||
|
<span class="p-author">`+ comment.author.name + `</span>
|
||||||
|
</a>`;
|
||||||
|
if (comment.published) {
|
||||||
|
const published = new Date(comment.published);
|
||||||
|
html += `
|
||||||
|
<time class="dt-published" datetime="`+ comment.published + `">` + published.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }) + `</time>`;
|
||||||
|
}
|
||||||
|
html += `
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="e-entry">`+ content + `
|
||||||
|
<a class="u-url"
|
||||||
|
href="`+ comment.url + `"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer">
|
||||||
|
source
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
html += `
|
||||||
|
</ol >
|
||||||
|
</div >
|
||||||
|
`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Reaction
|
||||||
|
* @property {string} url
|
||||||
|
* @property {Object?} author
|
||||||
|
* @property {string?} author.name
|
||||||
|
* @property {string?} author.photo
|
||||||
|
* @property {Object?} content
|
||||||
|
* @property {string?} content.text
|
||||||
|
* @property {RSVPEmoji?} rsvp
|
||||||
|
* @property {MentionType?} wm-property
|
||||||
|
* @property {string?} wm-source
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getIcon(name) {
|
||||||
|
|
||||||
|
if (name == 'like') {
|
||||||
|
return `<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M95.997 41.986l-.026-.035C85.746 28.36 68.428 21.423 51.165 24.881 30.138 29.094 15.004 47.558 15 69.003c0 24.413 14.906 47.964 39.486 70.086 8.43 7.586 17.437 14.468 26.444 20.533.728.49 1.444.967 2.148 1.43l1.39.909 1.355.872 1.317.835.645.403 1.259.78 1.194.726 1.032.619 1.38.807.418.236a6 6 0 005.864 0l1.138-.654 1.154-.684 1.118-.675.614-.376 1.26-.779a212 212 0 00.644-.403l1.317-.835 1.355-.872 1.39-.909c.704-.463 1.42-.94 2.148-1.43 9.007-6.065 18.015-12.947 26.444-20.533C162.094 116.967 177 93.416 177 69.004c-.004-21.446-15.138-39.91-36.165-44.123-17.07-3.42-34.174 3.323-44.43 16.568l-.408.537zm42.48-5.338c15.421 3.09 26.52 16.63 26.523 32.357 0 19.607-12.438 39.847-33.532 59.357l-1.316 1.205c-.22.201-.443.402-.666.603-7.977 7.18-16.548 13.727-25.118 19.498l-.745.5c-.74.494-1.466.973-2.177 1.437l-1.402.906-1.359.864-.662.416-1.292.8-.732.446-.73-.446-1.292-.8-.662-.416-1.36-.864-1.4-.906a235.406 235.406 0 01-2.923-1.937c-8.57-5.77-17.14-12.319-25.118-19.498l-.666-.603-1.316-1.205C39.438 108.852 27 88.612 27 69.004c.003-15.726 11.102-29.267 26.523-32.356 15.253-3.056 30.565 4.954 36.756 19.208l.204.478c2.084 4.878 9.009 4.85 11.053-.045 6.062-14.511 21.52-22.73 36.941-19.641z" fill="currentColor" /></svg> `;
|
||||||
|
} else if (name == 'repost') {
|
||||||
|
return `<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M18.472 146.335l-.075-.184a5.968 5.968 0 01-.216-.684l-.014-.056a5.643 5.643 0 01-.082-.397l-.013-.083a5.886 5.886 0 01-.072-.96V144c0-.157.006-.313.018-.467l.006-.075c.012-.132.028-.261.048-.39l.016-.095c.008-.05.017-.1.027-.149.005-.019.008-.038.012-.058.028-.133.06-.264.096-.393l.026-.088a5.86 5.86 0 01.482-1.159l.043-.077a5.642 5.642 0 01.31-.49l.015-.022.076-.104.044-.059a3.856 3.856 0 01.165-.208l.052-.061c.102-.12.21-.236.321-.348l18-18a6 6 0 018.661 8.303l-.175.183L38.484 138H120c23.196 0 42-18.804 42-42a6 6 0 0112 0c0 29.525-23.696 53.516-53.107 53.993L120 150H38.486l7.757 7.757a6 6 0 01.175 8.303l-.175.183a6 6 0 01-8.303.175l-.183-.175-18-18-.145-.151a6.036 6.036 0 01-.829-1.125l-.058-.105a4.08 4.08 0 01-.06-.114l-.04-.077a4.409 4.409 0 01-.139-.3l-.014-.036zM154.06 25.582l.183.175 18 18a6.036 6.036 0 01.974 1.276l.058.105c.02.035.038.07.056.105l.043.086a4.411 4.411 0 01.14.3l.014.036a5.965 5.965 0 01.291.868l.014.056c.032.13.059.263.082.397l.013.083a5.886 5.886 0 01.067.692v.014a6.11 6.11 0 01-.013.692l-.006.075a5.856 5.856 0 01-.048.39l-.016.095c-.008.05-.017.1-.027.149-.005.019-.008.038-.012.058-.028.133-.06.264-.096.393l-.026.088a5.86 5.86 0 01-.482 1.159l-.043.077-.052.09-.029.048a6.006 6.006 0 01-.32.478l-.044.059a3.857 3.857 0 01-.165.208l-.052.061a6.34 6.34 0 01-.176.197l-.145.15-18 18a6 6 0 01-8.661-8.302l.175-.183L153.514 54H72c-23.196 0-42 18.804-42 42a6 6 0 11-12 0c0-29.525 23.696-53.516 53.107-53.993L72 42h81.516l-7.759-7.757a6 6 0 01-.175-8.303l.175-.183a6 6 0 018.303-.175z" fill="currentColor" /></svg> `;
|
||||||
|
} else if (name == 'comment') {
|
||||||
|
return `<svg width = "24" height = "24" viewBox = "0 0 150 150" xmlns = "http://www.w3.org/2000/svg" > <path d="M75-.006a75 75 0 0174.997 74.31l.003.69c0 41.422-33.579 75-75 75H11.75c-6.49 0-11.75-5.26-11.75-11.75v-63.25a75 75 0 0175-75zm0 12a63 63 0 00-63 63v63h63c34.446 0 62.435-27.645 62.992-61.93l.008-1.041-.003-.633A63 63 0 0075 11.994zm21 72a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45zm0-24a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45z" fill="currentColor" /></svg> `;
|
||||||
|
} else if (name == 'bookmark') {
|
||||||
|
return `<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path d="M 6.0097656 2 C 4.9143111 2 4.0097656 2.9025988 4.0097656 3.9980469 L 4 22 L 12 19 L 20 22 L 20 20.556641 L 20 4 C 20 2.9069372 19.093063 2 18 2 L 6.0097656 2 z M 6.0097656 4 L 18 4 L 18 19.113281 L 12 16.863281 L 6.0019531 19.113281 L 6.0097656 4 z" fill="currentColor"></path>
|
||||||
|
</svg>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a list of reactions as HTML.
|
||||||
|
*
|
||||||
|
* @param {Array<Reaction>} reacts List of reactions to format
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
function formatReactions(type, reacts) {
|
||||||
|
let html = `
|
||||||
|
<div class="color--primary" >
|
||||||
|
<h3 id=`+ type + ` - header"> ` + getIcon(type) + ` <span>` + reacts.length + `</span> ` + type + `s </h3>
|
||||||
|
|
||||||
|
<ol class="likes" role = "list" aria - labelledby="`+ type + `-header"> `;
|
||||||
|
|
||||||
|
reacts.forEach(function (react) {
|
||||||
|
html += `
|
||||||
|
<li class="h-card">
|
||||||
|
<a class="u-url"
|
||||||
|
href="`+ react.author.url + `
|
||||||
|
target="_blank"
|
||||||
|
rel = "noreferrer"
|
||||||
|
title = "`+ react.author.name + `" >
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
class="lazy mentions__image u-photo"
|
||||||
|
src="`+ react.author.photo + `"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
>
|
||||||
|
<span class="p-author visually-hidden" aria-hidden="true">{{ author }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
|
||||||
|
html += `
|
||||||
|
</ol >
|
||||||
|
</div >
|
||||||
|
`;
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef WebmentionResponse
|
||||||
|
* @type {Object}
|
||||||
|
* @property {Array<Reaction>} children
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register event listener.
|
||||||
|
*/
|
||||||
|
window.addEventListener("load", async function () {
|
||||||
|
const container = document.getElementById(containerID);
|
||||||
|
if (!container) {
|
||||||
|
// no container, so do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages = [stripurl(refurl)];
|
||||||
|
if (!!addurls) {
|
||||||
|
addurls.split('|').forEach(function (url) {
|
||||||
|
pages.push(stripurl(url));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let apiURL = `https://webmention.io/api/mentions.jf2?per-page=${maxWebmentions}&sort-by=${sortBy}&sort-dir=${sortDir}`;
|
||||||
|
|
||||||
|
pages.forEach(function (path) {
|
||||||
|
apiURL += `&target[]=${encodeURIComponent('http:' + path)}&target[]=${encodeURIComponent('https:' + path)}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// apiURL = 'http://127.0.0.1:1111/test_webmentions.jf2';
|
||||||
|
/** @type {WebmentionResponse} */
|
||||||
|
let json = {};
|
||||||
|
try {
|
||||||
|
// const response = await window.fetch(apiURL);
|
||||||
|
const response = await window.fetch(apiURL);
|
||||||
|
if (response.status >= 200 && response.status < 300) {
|
||||||
|
json = await response.json();
|
||||||
|
} else {
|
||||||
|
console.error("Could not parse response");
|
||||||
|
new Error(response.statusText);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Purposefully not escalate further, i.e. no UI update
|
||||||
|
console.error("Request failed", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Array<Reaction>} */
|
||||||
|
let comments = [];
|
||||||
|
/** @type {Array<Reaction>} */
|
||||||
|
let mentions = [];
|
||||||
|
/** @type {Array<Reaction>} */
|
||||||
|
const bookmarks = [];
|
||||||
|
/** @type {Array<Reaction>} */
|
||||||
|
const likes = [];
|
||||||
|
/** @type {Array<Reaction>} */
|
||||||
|
const reposts = [];
|
||||||
|
/** @type {Array<Reaction>} */
|
||||||
|
const follows = [];
|
||||||
|
|
||||||
|
/** @type {Record<MentionType, Array<Reaction>>} */
|
||||||
|
const mapping = {
|
||||||
|
"in-reply-to": comments,
|
||||||
|
"like-of": likes,
|
||||||
|
"repost-of": reposts,
|
||||||
|
"bookmark-of": bookmarks,
|
||||||
|
"follow-of": follows,
|
||||||
|
"mention-of": mentions,
|
||||||
|
"rsvp": comments
|
||||||
|
};
|
||||||
|
|
||||||
|
json.children.forEach(function (child) {
|
||||||
|
// Map each mention into its respective container
|
||||||
|
const store = mapping[child['wm-property']];
|
||||||
|
if (store) {
|
||||||
|
store.push(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// format the comment-type things
|
||||||
|
let formattedMentions = '';
|
||||||
|
if (mentions.length > 0) {
|
||||||
|
formattedMentions = formatComments('mention', dedupe(mentions));
|
||||||
|
}
|
||||||
|
|
||||||
|
let formattedComments = '';
|
||||||
|
if (comments.length > 0) {
|
||||||
|
formattedComments = formatComments('comment', dedupe(comments));
|
||||||
|
}
|
||||||
|
|
||||||
|
// format likes
|
||||||
|
let likesStr = '';
|
||||||
|
if (likes.length > 0) {
|
||||||
|
likesStr = formatReactions('like', dedupe(likes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// format reposts
|
||||||
|
let repostsStr = '';
|
||||||
|
if (reposts.length > 0) {
|
||||||
|
repostsStr = formatReactions('repost', dedupe(reposts));
|
||||||
|
}
|
||||||
|
|
||||||
|
// format bookmarks
|
||||||
|
let bookmarksStr = '';
|
||||||
|
if (bookmarks.length > 0) {
|
||||||
|
bookmarksStr = formatReactions('bookmark', dedupe(bookmarks));
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = `<div id="webmentions">${repostsStr}${likesStr}${bookmarksStr}${formattedComments}${formattedMentions}</div>`;
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
|
||||||
|
// End-of-file marker for LibreJS
|
||||||
|
// @license-end
|
||||||
66
themes/tabi/static/js/webmention.min.js
vendored
Normal file
66
themes/tabi/static/js/webmention.min.js
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
(()=>{function t(t,e){return document.currentScript.getAttribute("data-"+t)||e}window.i18next=window.i18next||{t:function(t){return t}},window.i18next.t.bind(window.i18next);let m=t("page-url",window.location.href.replace(/#.*$/,"")),f=t("add-urls",void 0),e=t("id","webmentions"),g=(t("wordcount"),t("max-webmentions",30)),v=(t("prevent-spoofing"),t("sort-by","published")),b=t("sort-dir","up");function y(t){return t.substr(t.indexOf("//"))}function x(t){let l=[],a={};return t.forEach(function(t){var e=y(t.url);a[e]||(l.push(t),a[e]=!0)}),l}function L(t,e){let a=`
|
||||||
|
<div class="webcomments">
|
||||||
|
<h3 id="replies-header">`+o("comment")+" <span>"+e.length+"</span> "+t+`s </h3>
|
||||||
|
<ol aria-labelledby="replies-header" role="list">`;return e.forEach(function(t){let e="";var l;t.hasOwnProperty("content")&&(t.content.hasOwnProperty("html")?e=t.content.html:t.content.hasOwnProperty("text")&&(e=t.content.text)),a+=`
|
||||||
|
<li class="comment h-entry">
|
||||||
|
<div>
|
||||||
|
<a class="comment_author u-author"
|
||||||
|
href="`+t.author.url+`"
|
||||||
|
target="_blank"
|
||||||
|
title="`+t.author.name+`"
|
||||||
|
rel="noreferrer">
|
||||||
|
<img
|
||||||
|
src="`+t.author.photo+`"
|
||||||
|
alt=""
|
||||||
|
class="u-photo"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
>
|
||||||
|
<span class="p-author">`+t.author.name+`</span>
|
||||||
|
</a>`,t.published&&(l=new Date(t.published),a+=`
|
||||||
|
<time class="dt-published" datetime="`+t.published+'">'+l.toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"})+"</time>"),a+=`
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="e-entry">`+e+`
|
||||||
|
<a class="u-url"
|
||||||
|
href="`+t.url+`"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer">
|
||||||
|
source
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
`}),a+=`
|
||||||
|
</ol >
|
||||||
|
</div >
|
||||||
|
`}function o(t){return"like"==t?'<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M95.997 41.986l-.026-.035C85.746 28.36 68.428 21.423 51.165 24.881 30.138 29.094 15.004 47.558 15 69.003c0 24.413 14.906 47.964 39.486 70.086 8.43 7.586 17.437 14.468 26.444 20.533.728.49 1.444.967 2.148 1.43l1.39.909 1.355.872 1.317.835.645.403 1.259.78 1.194.726 1.032.619 1.38.807.418.236a6 6 0 005.864 0l1.138-.654 1.154-.684 1.118-.675.614-.376 1.26-.779a212 212 0 00.644-.403l1.317-.835 1.355-.872 1.39-.909c.704-.463 1.42-.94 2.148-1.43 9.007-6.065 18.015-12.947 26.444-20.533C162.094 116.967 177 93.416 177 69.004c-.004-21.446-15.138-39.91-36.165-44.123-17.07-3.42-34.174 3.323-44.43 16.568l-.408.537zm42.48-5.338c15.421 3.09 26.52 16.63 26.523 32.357 0 19.607-12.438 39.847-33.532 59.357l-1.316 1.205c-.22.201-.443.402-.666.603-7.977 7.18-16.548 13.727-25.118 19.498l-.745.5c-.74.494-1.466.973-2.177 1.437l-1.402.906-1.359.864-.662.416-1.292.8-.732.446-.73-.446-1.292-.8-.662-.416-1.36-.864-1.4-.906a235.406 235.406 0 01-2.923-1.937c-8.57-5.77-17.14-12.319-25.118-19.498l-.666-.603-1.316-1.205C39.438 108.852 27 88.612 27 69.004c.003-15.726 11.102-29.267 26.523-32.356 15.253-3.056 30.565 4.954 36.756 19.208l.204.478c2.084 4.878 9.009 4.85 11.053-.045 6.062-14.511 21.52-22.73 36.941-19.641z" fill="currentColor" /></svg> ':"repost"==t?'<svg focusable = "false" width = "24" height = "24" viewBox = "0 0 192 192" xmlns = "http://www.w3.org/2000/svg" > <path d="M18.472 146.335l-.075-.184a5.968 5.968 0 01-.216-.684l-.014-.056a5.643 5.643 0 01-.082-.397l-.013-.083a5.886 5.886 0 01-.072-.96V144c0-.157.006-.313.018-.467l.006-.075c.012-.132.028-.261.048-.39l.016-.095c.008-.05.017-.1.027-.149.005-.019.008-.038.012-.058.028-.133.06-.264.096-.393l.026-.088a5.86 5.86 0 01.482-1.159l.043-.077a5.642 5.642 0 01.31-.49l.015-.022.076-.104.044-.059a3.856 3.856 0 01.165-.208l.052-.061c.102-.12.21-.236.321-.348l18-18a6 6 0 018.661 8.303l-.175.183L38.484 138H120c23.196 0 42-18.804 42-42a6 6 0 0112 0c0 29.525-23.696 53.516-53.107 53.993L120 150H38.486l7.757 7.757a6 6 0 01.175 8.303l-.175.183a6 6 0 01-8.303.175l-.183-.175-18-18-.145-.151a6.036 6.036 0 01-.829-1.125l-.058-.105a4.08 4.08 0 01-.06-.114l-.04-.077a4.409 4.409 0 01-.139-.3l-.014-.036zM154.06 25.582l.183.175 18 18a6.036 6.036 0 01.974 1.276l.058.105c.02.035.038.07.056.105l.043.086a4.411 4.411 0 01.14.3l.014.036a5.965 5.965 0 01.291.868l.014.056c.032.13.059.263.082.397l.013.083a5.886 5.886 0 01.067.692v.014a6.11 6.11 0 01-.013.692l-.006.075a5.856 5.856 0 01-.048.39l-.016.095c-.008.05-.017.1-.027.149-.005.019-.008.038-.012.058-.028.133-.06.264-.096.393l-.026.088a5.86 5.86 0 01-.482 1.159l-.043.077-.052.09-.029.048a6.006 6.006 0 01-.32.478l-.044.059a3.857 3.857 0 01-.165.208l-.052.061a6.34 6.34 0 01-.176.197l-.145.15-18 18a6 6 0 01-8.661-8.302l.175-.183L153.514 54H72c-23.196 0-42 18.804-42 42a6 6 0 11-12 0c0-29.525 23.696-53.516 53.107-53.993L72 42h81.516l-7.759-7.757a6 6 0 01-.175-8.303l.175-.183a6 6 0 018.303-.175z" fill="currentColor" /></svg> ':"comment"==t?'<svg width = "24" height = "24" viewBox = "0 0 150 150" xmlns = "http://www.w3.org/2000/svg" > <path d="M75-.006a75 75 0 0174.997 74.31l.003.69c0 41.422-33.579 75-75 75H11.75c-6.49 0-11.75-5.26-11.75-11.75v-63.25a75 75 0 0175-75zm0 12a63 63 0 00-63 63v63h63c34.446 0 62.435-27.645 62.992-61.93l.008-1.041-.003-.633A63 63 0 0075 11.994zm21 72a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45zm0-24a6 6 0 01.225 11.996l-.225.004H51a6 6 0 01-.225-11.996l.225-.004h45z" fill="currentColor" /></svg> ':"bookmark"==t?`<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path d="M 6.0097656 2 C 4.9143111 2 4.0097656 2.9025988 4.0097656 3.9980469 L 4 22 L 12 19 L 20 22 L 20 20.556641 L 20 4 C 20 2.9069372 19.093063 2 18 2 L 6.0097656 2 z M 6.0097656 4 L 18 4 L 18 19.113281 L 12 16.863281 L 6.0019531 19.113281 L 6.0097656 4 z" fill="currentColor"></path>
|
||||||
|
</svg>`:void 0}function k(t,e){let l=`
|
||||||
|
<div class="color--primary" >
|
||||||
|
<h3 id=`+t+' - header"> '+o(t)+" <span>"+e.length+"</span> "+t+`s </h3>
|
||||||
|
|
||||||
|
<ol class="likes" role = "list" aria - labelledby="`+t+'-header"> ';return e.forEach(function(t){l+=`
|
||||||
|
<li class="h-card">
|
||||||
|
<a class="u-url"
|
||||||
|
href="`+t.author.url+`
|
||||||
|
target="_blank"
|
||||||
|
rel = "noreferrer"
|
||||||
|
title = "`+t.author.name+`" >
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
class="lazy mentions__image u-photo"
|
||||||
|
src="`+t.author.photo+`"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
>
|
||||||
|
<span class="p-author visually-hidden" aria-hidden="true">{{ author }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
`}),l+=`
|
||||||
|
</ol >
|
||||||
|
</div >
|
||||||
|
`}window.addEventListener("load",async function(){var c=document.getElementById(e);if(c){let e=[y(m)],l=(f&&f.split("|").forEach(function(t){e.push(y(t))}),`https://webmention.io/api/mentions.jf2?per-page=${g}&sort-by=${v}&sort-dir=`+b),t=(e.forEach(function(t){l+=`&target[]=${encodeURIComponent("http:"+t)}&target[]=`+encodeURIComponent("https:"+t)}),{});try{200<=(h=await window.fetch(l)).status&&h.status<300?t=await h.json():(console.error("Could not parse response"),new Error(h.statusText))}catch(t){console.error("Request failed",t)}var h,d=[],u=[],p=[],w=[];let a={"in-reply-to":h=[],"like-of":p,"repost-of":w,"bookmark-of":u,"follow-of":[],"mention-of":d,rsvp:h},o=(t.children.forEach(function(t){var e=a[t["wm-property"]];e&&e.push(t)}),""),n=(0<d.length&&(o=L("mention",x(d))),""),r=(0<h.length&&(n=L("comment",x(h))),""),i=(0<p.length&&(r=k("like",x(p))),""),s=(0<w.length&&(i=k("repost",x(w))),"");0<u.length&&(s=k("bookmark",x(u))),c.innerHTML=`<div id="webmentions">${i}${r}${s}${n}${o}</div>`}})})();
|
||||||
@@ -102,10 +102,11 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||||||
{# End debugging #}
|
{# End debugging #}
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<article>
|
<article class="h-entry">
|
||||||
<h1 class="article-title">
|
<h1 class="p-name article-title">
|
||||||
{{ page.title | markdown(inline=true) | safe }}
|
{{ page.title | markdown(inline=true) | safe }}
|
||||||
</h1>
|
</h1>
|
||||||
|
<a class="u-url u-uid" href="{{ page.permalink | safe }}"></a>
|
||||||
|
|
||||||
<ul class="meta">
|
<ul class="meta">
|
||||||
{#- Draft indicator -#}
|
{#- Draft indicator -#}
|
||||||
@@ -122,14 +123,17 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
||||||
{%- if author_list | length == 1 -%}
|
{%- if author_list | length == 1 -%}
|
||||||
{%- set author_string = author_list.0 -%}
|
{%- set author_string = '<span class="p-author">' ~ author_list.0 ~ '</span>' -%}
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
{%- set last_author = author_list | last -%}
|
{%- set last_author = author_list | last -%}
|
||||||
{%- set other_authors = author_list | slice(end=-1) -%}
|
{%- set other_authors = author_list | slice(end=-1) -%}
|
||||||
{%- set author_separator = macros_translate::translate(key="author_separator", default=", ", language_strings=language_strings) -%}
|
{%- set author_separator = macros_translate::translate(key="author_separator", default=", ", language_strings=language_strings) -%}
|
||||||
|
{%- set author_separator = '</span>' ~ author_separator ~ '<span class="p-author">' -%}
|
||||||
{%- set conjunction = macros_translate::translate(key="author_conjunction", default=" and ", language_strings=language_strings) -%}
|
{%- set conjunction = macros_translate::translate(key="author_conjunction", default=" and ", language_strings=language_strings) -%}
|
||||||
|
{%- set conjunction = '</span>' ~ conjunction ~ '<span class="p-author">' -%}
|
||||||
{%- set author_string = other_authors | join(sep=author_separator) -%}
|
{%- set author_string = other_authors | join(sep=author_separator) -%}
|
||||||
{%- set author_string = author_string ~ conjunction ~ last_author -%}
|
{%- set author_string = author_string ~ conjunction ~ last_author -%}
|
||||||
|
{%- set author_string = '<span class="p-author">' ~ author_string ~ '</span>' -%}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
||||||
{%- set by_author = macros_translate::translate(key="by_author", default="By $AUTHOR", language_strings=language_strings) -%}
|
{%- set by_author = macros_translate::translate(key="by_author", default="By $AUTHOR", language_strings=language_strings) -%}
|
||||||
@@ -145,7 +149,7 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||||||
|
|
||||||
{#- Date -#}
|
{#- Date -#}
|
||||||
{% if page.date and macros_settings::evaluate_setting_priority(setting="show_date", page=page, default_global_value=true) == "true" %}
|
{% if page.date and macros_settings::evaluate_setting_priority(setting="show_date", page=page, default_global_value=true) == "true" %}
|
||||||
<li>{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}{{ macros_format_date::format_date(date=page.date, short=true, language_strings=language_strings) }}</li>
|
<li><time class="dt-published" datetime="{{ page.date }}">{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}{{ macros_format_date::format_date(date=page.date, short=true, language_strings=language_strings) }}</time></li>
|
||||||
{#- Variable to keep track of whether we've shown a section, to avoid separators as the first element -#}
|
{#- Variable to keep track of whether we've shown a section, to avoid separators as the first element -#}
|
||||||
{%- set previous_visible = true -%}
|
{%- set previous_visible = true -%}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -160,7 +164,7 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||||||
{%- if page.taxonomies and page.taxonomies.tags -%}
|
{%- if page.taxonomies and page.taxonomies.tags -%}
|
||||||
<li class="tag">{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}{{- macros_translate::translate(key="tags", default="tags", language_strings=language_strings) | capitalize -}}: </li>
|
<li class="tag">{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}{{- macros_translate::translate(key="tags", default="tags", language_strings=language_strings) | capitalize -}}: </li>
|
||||||
{%- for tag in page.taxonomies.tags -%}
|
{%- for tag in page.taxonomies.tags -%}
|
||||||
<li class="tag"><a href="{{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}">{{ tag }}</a>
|
<li class="tag"><a class="p-category" href="{{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}">{{ tag }}</a>
|
||||||
{%- if not loop.last -%}
|
{%- if not loop.last -%}
|
||||||
,
|
,
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
@@ -175,7 +179,7 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||||||
{%- set formatted_date = macros_format_date::format_date(date=page.updated, short=true, language_strings=language_strings) -%}
|
{%- set formatted_date = macros_format_date::format_date(date=page.updated, short=true, language_strings=language_strings) -%}
|
||||||
{%- set updated_str = last_updated_str | replace(from="$DATE", to=formatted_date) -%}
|
{%- set updated_str = last_updated_str | replace(from="$DATE", to=formatted_date) -%}
|
||||||
{%- set previous_visible = true -%}
|
{%- set previous_visible = true -%}
|
||||||
</ul><ul class="meta last-updated"><li>{{ updated_str }}</li>
|
</ul><ul class="meta last-updated"><li><time class="dt-updated" datetime="{{ page.updated }}">{{ updated_str }}</time></li>
|
||||||
{#- Show link to remote changes if enabled -#}
|
{#- Show link to remote changes if enabled -#}
|
||||||
{%- if config.extra.remote_repository_url and macros_settings::evaluate_setting_priority(setting="show_remote_changes", page=page, default_global_value=true) == "true" -%}
|
{%- if config.extra.remote_repository_url and macros_settings::evaluate_setting_priority(setting="show_remote_changes", page=page, default_global_value=true) == "true" -%}
|
||||||
<li>{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}<a class="external" href="{% include "partials/history_url.html" %}" {{ blank_target }} rel="{{ rel_attributes }}">{{ macros_translate::translate(key="see_changes", default="See changes", language_strings=language_strings) }}</a></li>
|
<li>{%- if previous_visible -%}{{ separator_with_class | safe }}{%- endif -%}<a class="external" href="{% include "partials/history_url.html" %}" {{ blank_target }} rel="{{ rel_attributes }}">{{ macros_translate::translate(key="see_changes", default="See changes", language_strings=language_strings) }}</a></li>
|
||||||
@@ -227,7 +231,12 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||||||
{{ macros_toc::toc(page=page, header=true, language_strings=language_strings) }}
|
{{ macros_toc::toc(page=page, header=true, language_strings=language_strings) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<section class="body">
|
{#- Optional Summary paragraph for readers -#}
|
||||||
|
{% if page.description %}
|
||||||
|
<p class="p-summary" hidden>{{ page.description }}</p>
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
<section class="e-content body">
|
||||||
{#- Replace series_intro placeholder -#}
|
{#- Replace series_intro placeholder -#}
|
||||||
{%- set content_with_intro = page.content -%}
|
{%- set content_with_intro = page.content -%}
|
||||||
{%- if "<!-- series_intro -->" in page.content -%}
|
{%- if "<!-- series_intro -->" in page.content -%}
|
||||||
@@ -333,6 +342,13 @@ Current section extra: {% if current_section %}{{ current_section.extra | json_e
|
|||||||
{% if comment_system %}
|
{% if comment_system %}
|
||||||
{% include "partials/comments.html" %}
|
{% include "partials/comments.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{#- Webmentions -#}
|
||||||
|
{%- set global_webmentions_enabled = config.extra.webmentions.enable | default(value=false) -%}
|
||||||
|
{%- set page_webmentions_enabled = page.extra.webmentions | default(value=global_webmentions_enabled) -%}
|
||||||
|
{%- set webmentions_enabled = global_webmentions_enabled and page_webmentions_enabled != false or page_webmentions_enabled == true -%}
|
||||||
|
{%- if webmentions_enabled -%}
|
||||||
|
{%- include "partials/webmentions.html" -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ content="default-src 'self'
|
|||||||
{%- set script_src = script_src ~ " " ~ " cdn.jsdelivr.net" -%}
|
{%- set script_src = script_src ~ " " ~ " cdn.jsdelivr.net" -%}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
||||||
|
{#- Check if a webmention system is enabled to allow the necessary domains and directives -#}
|
||||||
|
{%- if config.extra.webmentions.enable -%}
|
||||||
|
{%- set connect_src = connect_src ~ " webmention.io" -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
{#- Append WebSocket for Zola serve mode -#}
|
{#- Append WebSocket for Zola serve mode -#}
|
||||||
{%- if config.mode == "serve" -%}
|
{%- if config.mode == "serve" -%}
|
||||||
{%- set connect_src = connect_src ~ " ws:" -%}
|
{%- set connect_src = connect_src ~ " ws:" -%}
|
||||||
|
|||||||
@@ -21,9 +21,16 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# Button to go to the comment section #}
|
{# Button to go to the comment/webmentions section #}
|
||||||
{% if comment_system %}
|
{% if comment_system or config.extra.webmentions.enable %}
|
||||||
<a href="#comments" id="comments-button" class="no-hover-padding" title="{{ macros_translate::translate(key="go_to_comments", default="Go to comments section", language_strings=language_strings) }}">
|
{%- if comment_system -%}
|
||||||
|
{#- Comments are shown above webmentions -#}
|
||||||
|
{%- set comments_id = "comments" -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- set comments_id = "webmentions" -%}
|
||||||
|
{%- endif -%}
|
||||||
|
|
||||||
|
<a href="#{{- comments_id -}}" id="comments-button" class="no-hover-padding" title="{{ macros_translate::translate(key="go_to_comments", default="Go to comments section", language_strings=language_strings) }}">
|
||||||
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clip-rule="evenodd" fill-rule="evenodd"/></svg>
|
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clip-rule="evenodd" fill-rule="evenodd"/></svg>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
51
themes/tabi/templates/partials/webmentions.html
Normal file
51
themes/tabi/templates/partials/webmentions.html
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{# Incorporate webmention.io links and script into the page head.
|
||||||
|
1. Provide the link to the webmention data in the at webmention.io.
|
||||||
|
2. Link to the stylesheet for styling webmentions on a page.
|
||||||
|
3. Add and configure the javascript to fetch and display the webmentions collected at webmention.io. #}
|
||||||
|
|
||||||
|
<link rel="webmention" href="https://webmention.io/{{ config.extra.webmentions.domain }}/webmention" />
|
||||||
|
|
||||||
|
{# Calculate the configured data for the script, if any #}
|
||||||
|
|
||||||
|
{% set script_data = "" %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.id %}
|
||||||
|
{% set script_data = script_data ~ "data-id=" ~ config.extra.webmentions.id %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.page_url %}
|
||||||
|
{% set script_data = script_data ~ " data-page-url=" ~ config.extra.webmentions.page_url %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.add_urls %}
|
||||||
|
{% set script_data = script_data ~ "data-add-urls=" ~ config.extra.webmentions.add_urls %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.wordcount %}
|
||||||
|
{% set script_data = script_data ~ " data-wordcount=" ~ config.extra.webmentions.wordcount %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.max_webmentions %}
|
||||||
|
{% set script_data = script_data ~ "data-max-webmentions=" ~ config.extra.webmentions.max_webmentions %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.prevent_spoofing %}
|
||||||
|
{% set script_data = script_data ~ "data-prevent-spoofing=" ~ config.extra.webmentions.prevent_spoofing %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.sort_by %}
|
||||||
|
{% set script_data = script_data ~ "data-sort-by=" ~ config.extra.webmentions.sort_by %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.sort_dir %}
|
||||||
|
{% set script_data = script_data ~ "data-sort-dir=" ~ config.extra.webmentions.sort_dir %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config.extra.webmentions.comments_are_reactions %}
|
||||||
|
{% set script_data = script_data ~ " data-comments-are-reactions=" ~ config.extra.webmentions.comments_are_reactions %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<script async src="{{ get_url(path='js/webmention.min.js', trailing_slash=false, cachebust=true) | safe }}" {{ script_data }}>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="webmentions-container" id="webmentions"></div>
|
||||||
@@ -376,6 +376,49 @@ custom_subset = true
|
|||||||
# page_author_hashes = "" # hash (or list of hashes) of the author.
|
# page_author_hashes = "" # hash (or list of hashes) of the author.
|
||||||
# lazy_loading = true # Loads when the comments are in the viewport (using the Intersection Observer API).
|
# lazy_loading = true # Loads when the comments are in the viewport (using the Intersection Observer API).
|
||||||
|
|
||||||
|
[extra.webmentions]
|
||||||
|
# To disable for a specific section or page, set webmentions = false in that page/section's front matter's [extra] section.
|
||||||
|
enable = false
|
||||||
|
# Specify the domain registered with webmention.io.
|
||||||
|
# domain = ""
|
||||||
|
|
||||||
|
# The HTML ID for the object to fill in with the webmention data.
|
||||||
|
# Defaults to "webmentions"
|
||||||
|
# id = "webmentions"
|
||||||
|
|
||||||
|
# data configuration for the webmention.min.js script
|
||||||
|
# The base URL to use for this page. Defaults to window.location
|
||||||
|
# page_url =
|
||||||
|
|
||||||
|
# Additional URLs to check, separated by |s
|
||||||
|
# add_urls
|
||||||
|
|
||||||
|
# The maximum number of words to render in reply mentions.
|
||||||
|
# wordcount = 20
|
||||||
|
|
||||||
|
# The maximum number of mentions to retrieve. Defaults to 30.
|
||||||
|
# max_webmentions = 30
|
||||||
|
|
||||||
|
# By default, Webmentions render using the mf2 'url' element, which plays
|
||||||
|
# nicely with webmention bridges (such as brid.gy and telegraph)
|
||||||
|
# but allows certain spoofing attacks. If you would like to prevent
|
||||||
|
# spoofing, set this to a non-empty string (e.g. "true").
|
||||||
|
# prevent_spoofing
|
||||||
|
|
||||||
|
# What to order the responses by; defaults to 'published'. See
|
||||||
|
# https://github.com/aaronpk/webmention.io#api
|
||||||
|
# sort_by
|
||||||
|
|
||||||
|
# The order to sort the responses by; defaults to 'up' (i.e. oldest
|
||||||
|
# first). See https://github.com/aaronpk/webmention.io#api
|
||||||
|
# sort_dir
|
||||||
|
|
||||||
|
# If set to a non-empty string (e.g. "true"), will display comment-type responses
|
||||||
|
# (replies/mentions/etc.) as being part of the reactions
|
||||||
|
# (favorites/bookmarks/etc.) instead of in a separate comment list.
|
||||||
|
# comments_are_reactions = "true"
|
||||||
|
|
||||||
|
|
||||||
# h-card configuration
|
# h-card configuration
|
||||||
# Will identify you on the indieweb (see https://microformats.org/wiki/h-card)
|
# Will identify you on the indieweb (see https://microformats.org/wiki/h-card)
|
||||||
[extra.hcard]
|
[extra.hcard]
|
||||||
|
|||||||
Reference in New Issue
Block a user