1
0
Fork 0
mastodon-interact.js/embed/index.html

755 lines
24 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<!--
SPDX-License-Identifier: Zlib
Copyright (C) 2022 smpl <smpl@slamkode.ml>
-->
<head profile="http://www.w3.org/2005/10/profile">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Mastodon Interact Widget | Embed</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="">
<script id="icon-follow-template" type="text/plain"><?xml version="1.0" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="57.883163mm"
height="45.014904mm"
viewBox="0 0 57.883163 45.014904">
<rect
width="56.164337"
height="43.296078"
x="0.85941321"
y="0.85940945"
ry="6.0133438"
style="fill:${color.0};stroke:${color.1};stroke-width:1.71882558;" />
<path
d="m 36.173854,25.50177 c -0.412679,2.12307 -3.696111,4.44656 -7.467135,4.89685 -1.966427,0.23464 -3.902531,0.4503 -5.96706,0.3556 -3.376344,-0.15469 -6.040514,-0.80589 -6.040514,-0.80589 0,0.32868 0.02027,0.64164 0.06081,0.93432 0.438947,3.33208 3.304025,3.53169 6.017975,3.62476 2.739247,0.0937 5.178349,-0.67536 5.178349,-0.67536 l 0.112534,2.47639 c 0,0 -1.915997,1.02886 -5.329151,1.2181 -1.882107,0.10345 -4.219053,-0.0474 -6.940949,-0.7678 -5.903334,-1.5625 -6.918572,-7.85516 -7.073914,-14.24009 -0.04735,-1.89573 -0.01816,-3.6833 -0.01816,-5.17835 0,-6.52892 4.277753,-8.44265 4.277753,-8.44265 2.156956,-0.99058 5.858093,-1.40716 9.705815,-1.43862 h 0.09453 c 3.847723,0.0315 7.551293,0.44804 9.708087,1.43862 0,0 4.27759,1.91373 4.27759,8.44265 0,0 0.05367,4.81707 -0.59656,8.16147"
style="fill:#3088d4;" />
<path
d="m 31.724706,17.8468 v 7.90543 h -3.131981 v -7.67307 c 0,-1.61747 -0.680555,-2.43845 -2.041827,-2.43845 -1.505102,0 -2.259437,0.97389 -2.259437,2.89962 v 4.19991 h -3.113495 v -4.19991 c 0,-1.92573 -0.754497,-2.89962 -2.259599,-2.89962 -1.361272,0 -2.041827,0.82098 -2.041827,2.43845 v 7.67307 H 13.744559 V 17.8468 c 0,-1.61569 0.411381,-2.89962 1.237712,-3.84951 0.852113,-0.94989 1.968048,-1.43683 3.353319,-1.43683 1.602719,0 2.816432,0.61602 3.618925,1.84822 l 0.780117,1.30776 0.780279,-1.30776 c 0.802332,-1.2322 2.016046,-1.84822 3.618926,-1.84822 1.385109,0 2.501044,0.48694 3.353319,1.43683 0.826168,0.94989 1.23755,2.23382 1.23755,3.84951"
style="fill:#ffffff;" />
<path
d="m 43.314163,32.1437 c 1.49508,0 2.70606,-1.21095 2.70606,-2.70606 0,-1.4951 -1.21098,-2.70606 -2.70606,-2.70606 -1.495103,0 -2.706063,1.21096 -2.706063,2.70606 0,1.49511 1.21096,2.70606 2.706063,2.70606 z m -6.088655,-1.35302 v -2.02955 h -1.353051 v 2.02955 h -2.02955 v 1.35302 h 2.02955 v 2.02955 h 1.353051 V 32.1437 h 2.029552 v -1.35302 z m 6.088655,2.70607 c -1.806313,0 -5.412155,0.90652 -5.412155,2.70606 v 1.35304 h 10.824279 v -1.35304 c 0,-1.79954 -3.605834,-2.70606 -5.412124,-2.70606 z"
style="fill:#3088d4;" />
</svg></script>
<script id="icon-share-template" type="text/plain"><?xml version="1.0" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
width="57.883163mm"
height="45.014904mm"
viewBox="0 0 57.883163 45.014904"
version="1.1">
<rect
style="fill:${color.0};stroke:${color.1};stroke-width:1.71882558;"
width="56.164337"
height="43.296078"
x="0.85941321"
y="0.85940945"
ry="6.0133438" />
<path
d="m 36.173854,25.50177 c -0.412679,2.12307 -3.696111,4.44656 -7.467135,4.89685 -1.966427,0.23464 -3.902531,0.4503 -5.96706,0.3556 -3.376344,-0.15469 -6.040514,-0.80589 -6.040514,-0.80589 0,0.32868 0.02027,0.64164 0.06081,0.93432 0.438947,3.33208 3.304025,3.53169 6.017975,3.62476 2.739247,0.0937 5.178349,-0.67536 5.178349,-0.67536 l 0.112534,2.47639 c 0,0 -1.915997,1.02886 -5.329151,1.2181 -1.882107,0.10345 -4.219053,-0.0474 -6.940949,-0.7678 -5.903334,-1.5625 -6.918572,-7.85516 -7.073914,-14.24009 -0.04735,-1.89573 -0.01816,-3.6833 -0.01816,-5.17835 0,-6.52892 4.277753,-8.44265 4.277753,-8.44265 2.156956,-0.99058 5.858093,-1.40716 9.705815,-1.43862 h 0.09453 c 3.847723,0.0315 7.551293,0.44804 9.708087,1.43862 0,0 4.27759,1.91373 4.27759,8.44265 0,0 0.05367,4.81707 -0.59656,8.16147"
style="fill:#3088d4;" />
<path
d="m 31.724706,17.8468 v 7.90543 h -3.131981 v -7.67307 c 0,-1.61747 -0.680555,-2.43845 -2.041827,-2.43845 -1.505102,0 -2.259437,0.97389 -2.259437,2.89962 v 4.19991 h -3.113495 v -4.19991 c 0,-1.92573 -0.754497,-2.89962 -2.259599,-2.89962 -1.361272,0 -2.041827,0.82098 -2.041827,2.43845 v 7.67307 H 13.744559 V 17.8468 c 0,-1.61569 0.411381,-2.89962 1.237712,-3.84951 0.852113,-0.94989 1.968048,-1.43683 3.353319,-1.43683 1.602719,0 2.816432,0.61602 3.618925,1.84822 l 0.780117,1.30776 0.780279,-1.30776 c 0.802332,-1.2322 2.016046,-1.84822 3.618926,-1.84822 1.385109,0 2.501044,0.48694 3.353319,1.43683 0.826168,0.94989 1.23755,2.23382 1.23755,3.84951"
style="fill:#ffffff;" />
<path
d="m 48.419887,25.72787 -9.710952,6.3391 11.059692,3.91135"
style="fill:none;stroke:#3088d4;stroke-width:2.11666656;" />
<g style="fill:#3088d4;">
<circle
cx="49.161694"
cy="25.12093"
r="3.5741699" />
<circle
r="3.5741699"
cy="32.134388"
cx="38.911243" />
<circle
cx="49.688114"
cy="36.044575"
r="3.5741699" />
</g>
</svg></script>
<script id="embed-follow-template" type="text/plain"><div style="display: inline-block; position: relative;">
<a href="${portal}">
<img id="mastodon-widget-follow" style="height: 32px;" src="mastodon-follow-${theme}.svg"/>
</a>
</div></script>
<script id="embed-share-template" type="text/plain"><div style="display: inline-block; position: relative;">
<a href="#">
<img id="mastodon-widget-share" style="height: 32px;" src="mastodon-share-${theme}.svg"/>
</a>
</div></script>
<script id="script-template" type="text/plain">/* SPDX-License-Identifier: Zlib
Copyright (C) 2022 smpl <smpl@slamkode.ml> */
const MastodonInteract = function() {
settings = {
"account": "${account}",
"portal": "${portal}",
"width": "${width}",
"theme": "${theme}"
};
strings = {
"share": {
"title": "${share.title}",
"button": "${share.button}",
"text": window.location
},
"follow": {
"title": "${follow.title}",
"button": "${follow.button}"
},
"common": {
"create": "${common.create}",
"create_url": "${common.create_url}",
"error": {
"handle": "${common.error.handle}"
}
}
};
var widget = {
style: null,
dialogs: {
follow: null,
share: null
},
buttons: {
follow: null,
share: null
}
};
var CreateStyle = function() {
widget.style = document.createElement('style');
widget.style.id = "mastodon-widget-style";
widget.style.innerHTML =
".mastodon-widget-container {" +
// "display: block;" +
"z-index: 1;" +
"border: 0;" +
"border-radius: 5px;" +
"position: absolute;" +
"top:110%;" +
"left: 0;" +
"width: " + settings.width + ";" +
'font-family: "Roboto",Roboto,sans-serif;' +
"margin: 0 auto;" +
"padding: 20px;" +
"font-weight: 400;" +
"font-size: 13px;" +
"line-height: 18px;" +
"}" +
".mastodon-widget-container.light {" +
"background: #d9e1e8;" +
"color: #282c37;" +
"}" +
".mastodon-widget-container.dark {" +
"background: #282c37;" +
"color: #9baec8;" +
"}" +
".mastodon-widget-title {" +
"text-align: center;" +
"font-size: 18px;" +
"font-weight: 500;" +
"margin: 0;" +
"}" +
".mastodon-widget-title.light {" +
"color: #282c37;" +
"}" +
".mastodon-widget-title.dark {" +
"color: #fff;" +
"}" +
".mastodon-widget-input {" +
"border: 0;" +
"border-radius: 4px;" +
"margin-top: 20px;" +
"padding: 10px;" +
"width: 100%;" +
"box-sizing: border-box;" +
"font-family: inherit;" +
"font-weight: 500;" +
"font-size: 18px;" +
"}" +
".mastodon-widget-input.light {" +
"background: #f9fafb;" +
"color: #000;" +
"}" +
".mastodon-widget-input.dark {" +
"background: #131419;" +
"color: #fff;" +
"}" +
".mastodon-widget-input::placeholder {" +
"color: #c83737;" +
"}" +
".mastodon-widget-button {" +
"display: block;" +
"width: 100%;" +
"border: 0;" +
"border-radius: 4px;" +
"margin-right: 10px;" +
"margin-top: 20px;" +
"padding: 10px;" +
"background: #2b90d9;" +
"font-family: inherit;" +
"font-weight: 500;" +
"font-size: 18px;" +
"color: #fff;" +
"line-height: inherit;" +
"text-align: center;" +
"text-transform: uppercase;" +
"box-sizing: border-box;" +
"cursor: pointer;" +
"}";
document.head.appendChild(widget.style);
};
var CreateDialog = function(o) {
var t;
if(o === widget.buttons.follow)
t = strings.follow;
else if(o === widget.buttons.share)
t = strings.share;
obj = document.createElement('div');
obj.setAttribute('class', 'mastodon-widget-container ' + settings.theme);
var title = document.createElement('p');
title.setAttribute('class', 'mastodon-widget-title ' + settings.theme);
title.innerHTML = t.title;
var input = document.createElement('input');
input.setAttribute('name', 'mastodon-handle');
input.setAttribute('type', 'text');
input.setAttribute('class', 'mastodon-widget-input ' + settings.theme);
var button = document.createElement('button');
button.innerHTML = t.button;
button.setAttribute('class', 'mastodon-widget-button');
button.onclick = cbAction;
var create_div = document.createElement('div');
create_div.style = "text-align: right;";
var create_a = document.createElement('a');
create_a.setAttribute('href', strings.common.create_url);
create_a.innerHTML = strings.common.create;
create_div.appendChild(create_a);
obj.appendChild(title);
obj.appendChild(input);
obj.appendChild(button);
obj.appendChild(create_div);
return obj;
};
var cbCreateDialog = function(e) {
var dialog;
if(widget.style == null)
CreateStyle();
dialog = CreateDialog(e.target);
e.target.parentElement.appendChild(dialog);
e.target.onclick = cbToggle;
if(widget.buttons.share && e.target === widget.buttons.share) {
widget.dialogs.share = dialog;
} else if(widget.buttons.follow && e.target === widget.buttons.follow) {
widget.dialogs.follow = dialog;
}
window.addEventListener('click', cbHide, true);
};
var cbHide = function(e) {
if(widget.dialogs.follow) {
if( e.target === widget.buttons.follow ||
e.target === widget.dialogs.follow ||
e.target.parentNode === widget.dialogs.follow ) {
return;
}
widget.dialogs.follow.style.display = 'none';
}
if(widget.dialogs.share) {
if( e.target === widget.buttons.share ||
e.target === widget.dialogs.share ||
e.target.parentNode === widget.dialogs.share ) {
return;
}
widget.dialogs.share.style.display = 'none';
}
window.removeEventListener('click', cbHide, true);
}
var cbToggle = function(e) {
if(e.target === widget.buttons.follow) {
if(widget.dialogs.follow.style.display === 'none') {
widget.dialogs.follow.style.display = '';
if(widget.dialogs.share)
widget.dialogs.share.style.display = 'none';
window.addEventListener('click', cbHide, true);
} else {
widget.dialogs.follow.style.display = 'none';
}
} else if(e.target === widget.buttons.share) {
if(widget.dialogs.share.style.display === 'none') {
widget.dialogs.share.style.display = '';
if(widget.dialogs.follow)
widget.dialogs.follow.style.display = 'none';
window.addEventListener('click', cbHide, true);
} else {
widget.dialogs.share.style.display = 'none';
}
}
};
var cbAction = function(e) {
var input = e.target.parentNode.querySelector('input[name="mastodon-handle"]');
var handle = input.value;
// remove any initial @
if(handle.charAt(0) == '@') {
handle = handle.substring(1);
}
// split into nick and host
var uriparts = handle.split('@');
if(uriparts.length < 2) {
input.value = '';
input.setAttribute('placeholder', strings.common.error.handle);
return;
}
if(e.target.parentNode === widget.dialogs.follow) {
var oReq = new XMLHttpRequest();
oReq.addEventListener("loadend", cbQueryResponse);
oReq.open("GET", "https://" + uriparts[1] + "/.well-known/webfinger?resource=acct:" + encodeURIComponent(handle));
oReq.send();
} else if(e.target.parentNode === widget.dialogs.share) {
window.location = "https://" + uriparts[1] + "/share?text=" + encodeURIComponent(strings.share.text);
}
};
// follow only
var cbQueryResponse = function(e) {
var data;
console.log(e.type);
if (e.readyState === e.DONE) {
if (e.status === 200) {
try {
data = JSON.parse(e.responseText);
} catch {
window.location = settings.portal;
return;
}
data.links.forEach( function(l) {
if(l.rel === "http://ostatus.org/schema/1.0/subscribe") {
window.location = l.template.replace('{uri}', encodeURIComponent(settings.account));
return;
}
});
}
}
window.location = settings.portal;
return;
};
widget.buttons.follow = document.getElementById('mastodon-widget-follow');
widget.buttons.share = document.getElementById('mastodon-widget-share');
if(widget.buttons.follow) {
widget.buttons.follow.onclick = cbCreateDialog;
widget.buttons.follow.style.cursor = "pointer";
let a = widget.buttons.follow.parentNode;
let div = a.parentNode;
div.appendChild(widget.buttons.follow);
a.remove();
}
if(widget.buttons.share) {
widget.buttons.share.onclick = cbCreateDialog;
widget.buttons.share.style.cursor = "pointer";
let a = widget.buttons.share.parentNode;
let div = a.parentNode;
div.appendChild(widget.buttons.share);
a.remove();
}
return this;
};
var mastodon_widget;
window.addEventListener("load", function(e) {
mastodon_widget = MastodonInteract();
});</script>
<style>
body {
max-width: 800px;
margin: auto;
background: #282c37;
color: #9baec8;
font-family: "Roboto",Roboto,sans-serif;
}
#description h1, h2 {
color: #fff;
}
a[id$="download"], a[id$="download"]:hover, a[id$="download"]:active {
text-decoration: none;
color: #fff;
}
table, #output {
min-width: 600px;
max-width: 800px;
}
table,tr,td {
padding: 0; margin: 0; border: 0;
vertical-align: middle;
}
td:first-child {
white-space: nowrap;
width: 1%;
padding: 10px;
color: #fff;
font-weight: bold;
}
input[type="text"] {
border: 0;
border-radius: 4px;
padding: 10px;
width: 100%;
box-sizing: border-box;
font-family: inherit;
font-weight: 500;
font-size: 18px;
background: #131419;
color: #fff;
}
textarea {
border: 0;
border-radius: 4px;
margin-top: 20px;
padding: 10px;
width: 100%;
box-sizing: border-box;
background: #131419;
color: #9baec8;
}
select, button {
border: 0;
border-radius: 4px;
padding: 10px;
background-color: #2b90d9;
color: #fff;
box-sizing: border-box;
min-width: 200px;
font-family: inherit;
}
#embed-follow-output, #embed-share-output {
height: 200px;
}
#script-output {
height: 256px;
}
</style>
<script>
var defaults = {
settings: {
account: 'user@localhost',
portal: '#',
width: '256px',
theme: 'light'
},
lang: {
da: {
"share": {
"title": "Tast dit Mastodon id",
"button": "Del"
},
"follow": {
"title": "Tast dit Mastodon id",
"button": "Følg"
},
"common": {
"create": "Opret konto",
"create_url": "https://joinmastodon.org/communities",
"error": {
"handle": "Forkert id"
}
}
},
en: {
"share": {
"title": "Enter your Mastodon handle",
"button": "Share"
},
"follow": {
"title": "Enter your Mastodon handle",
"button": "Follow"
},
"common": {
"create": "Create account",
"create_url": "https://joinmastodon.org/communities",
"error": {
"handle": "Invalid handle"
}
}
}
}
};
var settings;
var lang;
var elements;
function replacer(match)
{
switch(match) {
case '${account}': {
return settings.account;
}
case '${portal}': {
return settings.portal;
}
case '${width}': {
return settings.width;
}
case '${theme}': {
return settings.theme;
}
case '${share.title}': {
return lang.share.title;
}
case '${share.button}': {
return lang.share.button;
}
case '${follow.title}': {
return lang.follow.title;
}
case '${follow.button}': {
return lang.follow.button;
}
case '${common.create}': {
return lang.common.create;
}
case '${common.create_url}': {
return lang.common.create_url;
}
case '${common.error.handle}': {
return lang.common.error.handle;
}
}
}
function svg_replace(match)
{
var theme = {
dark: ['#282c37', '#1b1e25'],
light: ['#d9e1e8', '#c6d2dc']
};
switch(match) {
case '${color.0}': {
return theme[settings.theme][0];
}
case '${color.1}': {
return theme[settings.theme][1];
}
}
}
function generate()
{
var account = elements.handle.value;
if(account.charAt(0) == '@')
account = account.substring(1);
var uriparts = account.split('@');
var e = document.querySelector('select[name=theme]');
settings = {
account: account,
portal: "https://" + uriparts[1] + "/users/" + uriparts[0] + "/remote_follow",
width: defaults.settings.width,
theme: e.options[e.selectedIndex].value //defaults.settings.theme //TODO
};
var e = document.querySelector('select[name=lang]');
switch(e.options[e.selectedIndex].value) {
case 'en': {
lang = defaults.lang.en;
break;
}
case 'da': {
lang = defaults.lang.da;
break;
}
case 'custom': {
lang = {
"share": {
"title": document.querySelector('input[name="share.title"]').value,
"button": document.querySelector('input[name="share.button"]').value
},
"follow": {
"title": document.querySelector('input[name="follow.title"]').value,
"button": document.querySelector('input[name="follow.button"]').value
},
"common": {
"create": document.querySelector('input[name="common.create"]').value,
"create_url": document.querySelector('input[name="common.create_url"]').value,
"error": {
"handle": document.querySelector('input[name="common.error.handle"]').value
}
}
};
break;
}
}
var tmpl = document.getElementById('script-template');
var embed = tmpl.innerHTML.replace(/\${[^}]*}/g, replacer);
var script_file = URL.createObjectURL(new Blob(Array.from(embed), {type: 'octet/stream'}));
document.getElementById('script-download').setAttribute('href', script_file);
document.getElementById('script-download').setAttribute('download', 'mastodon-interact.js');
document.getElementById('script-output').innerHTML = embed;
var tmpl = document.getElementById('embed-follow-template');
var embed = tmpl.innerHTML.replace(/\${[^}]*}/g, replacer);
document.getElementById('embed-follow-output').innerHTML = embed;
var tmpl = document.getElementById('icon-follow-template');
var embed = tmpl.innerHTML.replace(/\${[^}]*}/g, svg_replace);
var icon_follow_file = URL.createObjectURL(new Blob(Array.from(embed), {type: 'octet/stream'}));
document.getElementById('follow-icon-download').setAttribute('href', icon_follow_file);
document.getElementById('follow-icon-download').setAttribute('download', 'mastodon-follow-' + settings.theme + '.svg');
var tmpl = document.getElementById('embed-share-template');
var embed = tmpl.innerHTML.replace(/\${[^}]*}/g, replacer);
document.getElementById('embed-share-output').innerHTML = embed;
var tmpl = document.getElementById('icon-share-template');
var embed = tmpl.innerHTML.replace(/\${[^}]*}/g, svg_replace);
var icon_share_file = URL.createObjectURL(new Blob(Array.from(embed), {type: 'octet/stream'}));
document.getElementById('share-icon-download').setAttribute('href', icon_share_file);
document.getElementById('share-icon-download').setAttribute('download', 'mastodon-share-' + settings.theme + '.svg');
}
function cb_select(e)
{
console.log(e.target.value);
if(e.target.value === 'custom') {
elements.custom.style.display = '';
} else {
elements.custom.style.display = 'none';
}
}
window.onload = function() {
elements = {
follow: document.querySelector('input[name=follow]'),
share: document.querySelector('input[name=share]'),
lang: document.querySelector('select[name=lang]'),
theme: document.querySelector('select[name=theme]'),
handle: document.querySelector('input[name=handle]'),
custom: document.getElementById('custom-lang')
};
elements.lang.onchange = cb_select;
};
</script>
</head>
<body>
<div id="description">
<h1>Mastodon Interact embedding</h1>
<p>Enter your Mastodon account, pick your settings and click on
generate. This page will generate a script to be embedded in
the <tt>&lt;head&gt;</tt> section of your page and two HTML
snippets you can insert where you want each button on your
page. Also remember to download the icon files for the buttons
unless you supply your own.</p>
<p>You can easily tweak the script after download. The settings
and strings are in the beginning of the script. In the HTML
snippets you can modify the size of the button in the
<tt>style</tt> attribute of the <tt>&lt;img&gt;</tt> tag, which
is set to <tt>height: 32px</tt> and change the <tt>src</tt>
attribute to adjust the path to the button image. You can also
adjust the fallback links, but you can't remove the
<tt>&lt;a&gt;</tt> tags.</p>
</div>
<table>
<tr>
<td>Mastodon handle</td>
<td>
<input type="text" name="handle" />
</td>
</tr>
<tr>
<td>Language</td>
<td>
<select name="lang">
<option value="en">English</option>
<option value="da">Danish</option>
<option value="custom">Custom</option>
</select>
</td>
</tr>
<tr>
<td>Theme</td>
<td>
<select name="theme">
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</td>
</tr>
</table>
<div id="custom-lang" style="display: none;">
<table>
<tr>
<td>share.title:</td>
<td><input type="text" name="share.title" value="Enter your Mastodon handle" /></td>
</tr>
<tr>
<td>share.button:</td>
<td><input type="text" name="share.button" value="Share" /></td>
</tr>
<tr>
<td>follow.title:</td>
<td><input type="text" name="follow.title" value="Enter your Mastodon handle" /></td>
</tr>
<tr>
<td>follow.button:</td>
<td><input type="text" name="follow.button" value="Follow" /></td>
</tr>
<tr>
<td>common.create:</td>
<td><input type="text" name="common.create" value="Create account" /></td>
</tr>
<tr>
<td>common.create_url:</td>
<td><input type="text" name="common.create_url" value="https://joinmastodon.org/communities" /></td>
</tr>
<tr>
<td>common.error.handle:</td>
<td><input type="text" name="common.error.handle" value="Invalid handle" /></td>
</tr>
</table>
</div>
<div>
<button onclick="generate()">Generate embed</button>
</div>
<div id="output">
<h2>Script</h2>
<div><a id="script-download" href="#">Download script</a></div>
<textarea id="script-output"></textarea>
<h2>Follow Embed</h2>
<div><a id="follow-icon-download" href="#">Download icon</a></div>
<textarea id="embed-follow-output"></textarea>
<h2>Share Embed</h2>
<div><a id="share-icon-download" href="#">Download icon</a></div>
<textarea id="embed-share-output"></textarea>
</div>
<div style="padding: 64px; text-align:center">
<p>Copyright (C) 2022 smpl &lt;smpl@slamkode.ml&gt;</p>
<a style="text-decoration: none; color: #fff;" href="https://git.data.coop/smpl/mastodon-interact.js">Source code</a>
</div>
</body>
</html>