Added configuration
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
function bindIt(obj, base = '') {
|
||||
function bindIt(obj, base = '', withFuncs = {}, onUpdate = ()=>{}) {
|
||||
let arrayHandler = {
|
||||
apply: function (fn, arr, argumentsList) {
|
||||
obj[prop]._push(argumentsList);
|
||||
triggerUpdate(bindKey, obj[prop]);
|
||||
}
|
||||
};
|
||||
|
||||
for (let key in obj) {
|
||||
// Make sure all child objects are proxied too
|
||||
if (obj.hasOwnProperty(key) && Object.prototype.toString.call(obj[key]) === '[object Object]') {
|
||||
obj[key] = bindIt(obj[key], `${base ? base + '.' : ''}${key}`);
|
||||
obj[key] = bindIt(obj[key], `${base ? base + '.' : ''}${key}`, withFuncs, onUpdate);
|
||||
}
|
||||
// ...as well as arrays
|
||||
if (obj.hasOwnProperty(key) && Object.prototype.toString.call(obj[key]) === '[object Array]') {
|
||||
@@ -11,12 +18,6 @@ function bindIt(obj, base = '') {
|
||||
}
|
||||
|
||||
function wrapArray(obj, prop, bindKey) {
|
||||
let arrayHandler = {
|
||||
apply: function (fn, arr, argumentsList) {
|
||||
obj[prop]._push(argumentsList);
|
||||
triggerUpdate(bindKey, obj[prop]);
|
||||
}
|
||||
};
|
||||
if (Object.prototype.toString.call(obj[prop]) === '[object Array]') {
|
||||
obj[prop]._push = obj[prop].push;
|
||||
obj[prop].push = new Proxy(obj[prop].push, arrayHandler);
|
||||
@@ -32,16 +33,35 @@ function bindIt(obj, base = '') {
|
||||
case 'innerText':
|
||||
element.innerText = value;
|
||||
break;
|
||||
case 'innerHTML':
|
||||
element.innerHTML = value;
|
||||
break;
|
||||
case 'value':
|
||||
element.value = value;
|
||||
break;
|
||||
case 'checked':
|
||||
element.checked = value;
|
||||
break;
|
||||
case 'visible':
|
||||
if (value == false || value == "") {
|
||||
element.style.display = 'none';
|
||||
} else {
|
||||
element.style.display = '';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (typeof window[bindType] === 'function') {
|
||||
if (typeof withFuncs[bindType] === 'function') {
|
||||
withFuncs[bindType].apply(element, [value]);
|
||||
} else if (typeof window[bindType] === 'function') {
|
||||
window[bindType].apply(element, [value]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (onUpdate !== undefined) {
|
||||
requestAnimationFrame(() => {
|
||||
onUpdate();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -50,7 +70,7 @@ function bindIt(obj, base = '') {
|
||||
get: function (obj, key) {
|
||||
if (typeof key === 'string') {
|
||||
// Ensure safe deep get/set (no need to set up the structute)
|
||||
if (obj[key] === undefined) obj[key] = bindIt({}, `${base ? base + '.' : ''}${key}`);
|
||||
if (obj[key] === undefined) obj[key] = bindIt({}, `${base ? base + '.' : ''}${key}`, withFuncs, onUpdate);
|
||||
return obj[key];
|
||||
}
|
||||
},
|
||||
@@ -83,10 +103,33 @@ function bindIt(obj, base = '') {
|
||||
}
|
||||
|
||||
let twoWayElements = document.querySelectorAll(`[bind-it-two-way]`);
|
||||
const setObjPath = (t, path, value) => {
|
||||
if (typeof t != "object") throw Error("non-object")
|
||||
if (path == "") throw Error("empty path")
|
||||
const pos = path.indexOf(".")
|
||||
return pos == -1
|
||||
? (t[path] = value, value)
|
||||
: setObjPath(t[path.slice(0, pos)], path.slice(pos + 1), value)
|
||||
}
|
||||
for (let element of twoWayElements) {
|
||||
element.addEventListener('change', function () {
|
||||
let prop = this.getAttribute('bind-it-to');
|
||||
proxied[prop] = this.value
|
||||
let bindType = this.getAttribute('bind-it-with') || 'value';
|
||||
|
||||
switch (bindType) {
|
||||
case 'innerText':
|
||||
setObjPath(proxied, prop, this.innerText);
|
||||
break;
|
||||
case 'innerHTML':
|
||||
setObjPath(proxied, prop, this.innerHTML);
|
||||
break;
|
||||
case 'checked':
|
||||
setObjPath(proxied, prop, this.checked);
|
||||
break;
|
||||
default:
|
||||
setObjPath(proxied, prop, this.value);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -38,33 +38,34 @@
|
||||
<div class="column">
|
||||
<div class="form">
|
||||
<input type="text" id="post-url" bind-it-to="bskyPost.url" bind-it-with="value" />
|
||||
<button id="submit">Fetch</button>
|
||||
</div>
|
||||
<!-- Config is still WiP -->
|
||||
<!--
|
||||
<details>
|
||||
<details class="config">
|
||||
<summary>Advanced Config</summary>
|
||||
<div>
|
||||
<label>CORS Proxy: <input type="text" bind-it-to="config.corsProxy" bind-it-with="value" bind-it-two-way /></label>
|
||||
<label>Width: <input type="number" bind-it-to="config.width" bind-it-with="value" bind-it-two-way /></label>
|
||||
<label>Window Decoration: <input type="checkbox" bind-it-to="config.windowDecoration" bind-it-with="checked" bind-it-two-way /></label>
|
||||
<label><span>CORS Proxy:</span><input type="text" bind-it-to="config.corsProxy" bind-it-with="value" bind-it-two-way /></label>
|
||||
<label><span>Width:</span><input type="number" bind-it-to="config.width" bind-it-with="value" bind-it-two-way /></label>
|
||||
<label><span>Title:</span><input type="text" bind-it-to="config.windowTitle" bind-it-with="value" bind-it-two-way /></label>
|
||||
<label><span>Window Decoration:</span><input type="checkbox" bind-it-to="config.windowDecoration" bind-it-with="checked" bind-it-two-way /></label>
|
||||
<label><span>Show Border:</span><input type="checkbox" bind-it-to="config.showBorder" bind-it-with="checked" bind-it-two-way /></label>
|
||||
<label><span>Interactions:</span><input type="checkbox" bind-it-to="config.showInteractions" bind-it-with="checked" bind-it-two-way /></label>
|
||||
<label><span>Post Date:</span><input type="checkbox" bind-it-to="config.showDate" bind-it-with="checked" bind-it-two-way /></label>
|
||||
</div>
|
||||
</details>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="column post-wrapper">
|
||||
|
||||
<div id="post" bind-it-to="config.width" bind-it-with="setWidth">
|
||||
<div class="post border" bind-it-to="config.width" bind-it-with="setWidth">
|
||||
<div class="windowDecoration" bind-it-to="config.windowDecoration" bind-it-with="shouldShow">
|
||||
<div id="post" class="border" bind-it-to="config.showBorder" bind-it-with="showBorder">
|
||||
<div class="post" bind-it-to="config.width" bind-it-with="setWidth">
|
||||
<div class="windowDecoration" bind-it-to="config.windowDecoration" bind-it-with="visible">
|
||||
<div class="buttons">
|
||||
<div class="roundButton" style="background-color: #ff544d; border-color: #dd3a33"></div>
|
||||
<div class="roundButton" style="background-color: #feb429; border-color: #da9211"></div>
|
||||
<div class="roundButton" style="background-color: #d9d8d7; border-color: #b7b5b5"></div>
|
||||
</div>
|
||||
<div class="title" bind-it-to="bskyPost.handle"></div>
|
||||
<div class="title"></div>
|
||||
<div class="spacer"></div>
|
||||
</div>
|
||||
<div class="header">
|
||||
@@ -77,21 +78,21 @@
|
||||
<div class="body" bind-it-to="bskyPost.text" bind-it-with="asHTML"></div>
|
||||
|
||||
<div class="embeds">
|
||||
<div class="images" bind-it-to="bskyPost.images.exists" bind-it-with="shouldShow">
|
||||
<div bind-it-to="bskyPost.images.image1" bind-it-with="shouldShow">
|
||||
<div class="images" bind-it-to="bskyPost.images.exists" bind-it-with="visible">
|
||||
<div bind-it-to="bskyPost.images.image1" bind-it-with="visible">
|
||||
<img bind-it-to="bskyPost.images.image1" bind-it-with="imageSrc" />
|
||||
</div>
|
||||
<div bind-it-to="bskyPost.images.image2" bind-it-with="shouldShow">
|
||||
<div bind-it-to="bskyPost.images.image2" bind-it-with="visible">
|
||||
<img bind-it-to="bskyPost.images.image2" bind-it-with="imageSrc" />
|
||||
</div>
|
||||
<div bind-it-to="bskyPost.images.image3" bind-it-with="shouldShow">
|
||||
<div bind-it-to="bskyPost.images.image3" bind-it-with="visible">
|
||||
<img bind-it-to="bskyPost.images.image3" bind-it-with="imageSrc" />
|
||||
</div>
|
||||
<div bind-it-to="bskyPost.images.image4" bind-it-with="shouldShow">
|
||||
<div bind-it-to="bskyPost.images.image4" bind-it-with="visible">
|
||||
<img bind-it-to="bskyPost.images.image4" bind-it-with="imageSrc" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="externalLink" bind-it-to="bskyPost.externalLink.exists" bind-it-with="shouldShow">
|
||||
<div class="externalLink" bind-it-to="bskyPost.externalLink.exists" bind-it-with="visible">
|
||||
<img id="externalLinkImage" bind-it-to="bskyPost.externalLink.image" bind-it-with="imageSrc" />
|
||||
<div class="linkMeta">
|
||||
<div class="linkTitle" bind-it-to="bskyPost.externalLink.title"></div>
|
||||
@@ -105,7 +106,7 @@
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div class="interactions" bind-it-to="config.interactions" bind-it-with="shouldShow">
|
||||
<div class="interactions" bind-it-to="config.showInteractions" bind-it-with="visible">
|
||||
<span class="replies">
|
||||
<svg fill="none" width="18" viewBox="0 0 24 24" height="18" style="color: rgb(111, 134, 159); pointer-events: none;"><path fill="hsl(211, 20%, 53%)" fill-rule="evenodd" clip-rule="evenodd" d="M2.002 6a3 3 0 0 1 3-3h14a3 3 0 0 1 3 3v10a3 3 0 0 1-3 3H12.28l-4.762 2.858A1 1 0 0 1 6.002 21v-2h-1a3 3 0 0 1-3-3V6Zm3-1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h2a1 1 0 0 1 1 1v1.234l3.486-2.092a1 1 0 0 1 .514-.142h7a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-14Z"></path></svg>
|
||||
<span bind-it-to="bskyPost.replies"></span> replies
|
||||
@@ -119,7 +120,9 @@
|
||||
<span bind-it-to="bskyPost.likes"></span> likes
|
||||
</span>
|
||||
</div>
|
||||
<div class="createdAt" bind-it-to="bskyPost.createdAt"></div>
|
||||
<div bind-it-to="config.showDate" bind-it-with="visible">
|
||||
<div class="createdAt" bind-it-to="bskyPost.createdAt"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -127,7 +130,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="column">
|
||||
<img src="" id="result-image" />
|
||||
@@ -139,17 +141,14 @@
|
||||
<button id="download">Download Image</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="column column-60 column-offset-20" style="text-align: center;">
|
||||
Source code available on <a href="https://github.com/AverageMarcus/bsky-screenshot" target="_blank" rel="noopener noreferrer">GitHub</a>, <a href="https://gitlab.com/AverageMarcus/bsky-screenshot" target="_blank" rel="noopener noreferrer">GitLab</a>, <a href="https://bitbucket.org/AverageMarcus/bsky-screenshot/" target="_blank" rel="noopener noreferrer">Bitbucket</a> & <a href="https://git.cluster.fun/AverageMarcus/bsky-screenshot" target="_blank" rel="noopener noreferrer">my own Gitea server</a>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="column column-60 column-offset-20" style="text-align: center;">
|
||||
Source code available on <a href="https://github.com/AverageMarcus/bsky-screenshot" target="_blank" rel="noopener noreferrer">GitHub</a>, <a href="https://gitlab.com/AverageMarcus/bsky-screenshot" target="_blank" rel="noopener noreferrer">GitLab</a>, <a href="https://bitbucket.org/AverageMarcus/bsky-screenshot/" target="_blank" rel="noopener noreferrer">Bitbucket</a> & <a href="https://git.cluster.fun/AverageMarcus/bsky-screenshot" target="_blank" rel="noopener noreferrer">my own Gitea server</a>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="column column-60 column-offset-20">
|
||||
<footer>
|
||||
|
172
src/script.js
172
src/script.js
@@ -1,76 +1,100 @@
|
||||
function imageSrc(value) {
|
||||
if (value) {
|
||||
this.setAttribute('src', value);
|
||||
} else {
|
||||
this.setAttribute('src', "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==");
|
||||
}
|
||||
}
|
||||
|
||||
function asHTML(value) {
|
||||
this.innerHTML = value;
|
||||
}
|
||||
|
||||
function shouldShow(value) {
|
||||
if (value == false || value == "") {
|
||||
this.style.display = 'none';
|
||||
} else {
|
||||
this.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
function setWidth(value) {
|
||||
this.style.width = `${value}px`;
|
||||
}
|
||||
|
||||
var updateImageTimeout;
|
||||
function updateImage() {
|
||||
if (updateImageTimeout) {
|
||||
window.cancelAnimationFrame(updateImageTimeout);
|
||||
}
|
||||
updateImageTimeout = window.requestAnimationFrame(function() {
|
||||
domtoimage.toPng(document.getElementById('post'), { cacheBust: false })
|
||||
.then(function (dataUrl) {
|
||||
document.getElementById('result-image').src = dataUrl;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
[...document.querySelectorAll('#post img'), ...document.querySelectorAll('#input-form input')].forEach(el => el.addEventListener('load', updateImage));
|
||||
|
||||
(() => {
|
||||
let {bskyPost, config} = bindIt({
|
||||
config: {
|
||||
corsProxy: `https://corsproxy.io/?url=`,
|
||||
width: 600,
|
||||
windowDecoration: true,
|
||||
showInteractions: true
|
||||
},
|
||||
bskyPost: {
|
||||
url: 'https://bsky.app/profile/averagemarcus.bsky.social/post/3lwyxxwjnxc2k',
|
||||
handle: '',
|
||||
displayName: '',
|
||||
avatar: '',
|
||||
text: '',
|
||||
createdAt: '',
|
||||
likes: 0,
|
||||
reposts: 0,
|
||||
replies: 0,
|
||||
externalLink: {
|
||||
exists: false,
|
||||
title: "",
|
||||
description: "",
|
||||
image: "",
|
||||
domain: ""
|
||||
let {bskyPost, config} = bindIt(
|
||||
{
|
||||
config: {
|
||||
corsProxy: `https://corsproxy.io/?url=`,
|
||||
width: 600,
|
||||
windowTitle: "$handle", // $handle, $displayName, $url
|
||||
windowDecoration: true,
|
||||
showInteractions: true,
|
||||
showDate: true,
|
||||
showBorder: true
|
||||
},
|
||||
images: {
|
||||
exists: false,
|
||||
image1: "",
|
||||
image2: "",
|
||||
image3: "",
|
||||
image4: "",
|
||||
bskyPost: {
|
||||
url: 'https://bsky.app/profile/averagemarcus.bsky.social/post/3lwyxxwjnxc2k',
|
||||
handle: '',
|
||||
displayName: '',
|
||||
avatar: '',
|
||||
text: '',
|
||||
createdAt: '',
|
||||
likes: 0,
|
||||
reposts: 0,
|
||||
replies: 0,
|
||||
externalLink: {
|
||||
exists: false,
|
||||
title: "",
|
||||
description: "",
|
||||
image: "",
|
||||
domain: ""
|
||||
},
|
||||
images: {
|
||||
exists: false,
|
||||
image1: "",
|
||||
image2: "",
|
||||
image3: "",
|
||||
image4: "",
|
||||
}
|
||||
}
|
||||
},
|
||||
"",
|
||||
{
|
||||
imageSrc: function(value) {
|
||||
if (value) {
|
||||
this.setAttribute('src', value);
|
||||
} else {
|
||||
this.setAttribute('src', "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==");
|
||||
}
|
||||
},
|
||||
asHTML: function(value) {
|
||||
this.innerHTML = value;
|
||||
},
|
||||
setWidth: function(value) {
|
||||
this.style.width = `${value}px`;
|
||||
},
|
||||
showBorder: function(value) {
|
||||
if (value) {
|
||||
this.style.borderStyle = "double";
|
||||
} else {
|
||||
this.style.borderStyle = "none";
|
||||
}
|
||||
}
|
||||
},
|
||||
updateImage
|
||||
);
|
||||
|
||||
var updateImageTimeout;
|
||||
function updateImage() {
|
||||
if (updateImageTimeout) {
|
||||
window.cancelAnimationFrame(updateImageTimeout);
|
||||
}
|
||||
});
|
||||
setWindowTitle();
|
||||
updateImageTimeout = window.requestAnimationFrame(function() {
|
||||
domtoimage.toPng(document.getElementById('post'), { cacheBust: false })
|
||||
.then(function (dataUrl) {
|
||||
document.getElementById('result-image').src = dataUrl;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
[...document.querySelectorAll('#post img')].forEach(el => el.addEventListener('load', updateImage));
|
||||
[...document.querySelectorAll('#input-form input')].forEach(el => el.addEventListener('change', updateImage));
|
||||
|
||||
function setWindowTitle() {
|
||||
switch (config.windowTitle) {
|
||||
case "$handle":
|
||||
document.querySelector('.windowDecoration .title').innerText = bskyPost.handle;
|
||||
break;
|
||||
case "$displayName":
|
||||
document.querySelector('.windowDecoration .title').innerText = bskyPost.displayName;
|
||||
break;
|
||||
case "$url":
|
||||
document.querySelector('.windowDecoration .title').innerText = bskyPost.url.replaceAll("https://", "");
|
||||
break;
|
||||
default:
|
||||
document.querySelector('.windowDecoration .title').innerText = config.windowTitle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function isValidHttpUrl(string) {
|
||||
let url;
|
||||
@@ -176,9 +200,13 @@ function updateImage() {
|
||||
updateImage();
|
||||
}
|
||||
|
||||
document.getElementById('submit').addEventListener('click', loadPost);
|
||||
document.getElementById('post-url').addEventListener('change', loadPost);
|
||||
document.getElementById('post-url').addEventListener('keyup', loadPost);
|
||||
document.getElementById('post-url').addEventListener('paste', (ev) =>{
|
||||
let data = ev.clipboardData.getData("text/plain");
|
||||
document.getElementById('post-url').value = data;
|
||||
loadPost();
|
||||
});
|
||||
window.requestAnimationFrame(loadPost);
|
||||
|
||||
document.getElementById('download').addEventListener('click', function() {
|
||||
@@ -187,4 +215,8 @@ function updateImage() {
|
||||
link.href = document.getElementById('result-image').src;
|
||||
link.click();
|
||||
});
|
||||
|
||||
window.debugBskyPost = function() {
|
||||
console.log(bskyPost, config);
|
||||
}
|
||||
})();
|
||||
|
@@ -11,6 +11,27 @@ body {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.config > div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 24px;
|
||||
justify-content: space-evenly;
|
||||
align-items: flex-start;
|
||||
flex-direction: row;
|
||||
text-align: center;
|
||||
}
|
||||
.config > div label input {
|
||||
margin: 0;
|
||||
display: block;
|
||||
}
|
||||
.config > div label input[type=checkbox] {
|
||||
margin: 15px auto;
|
||||
scale: 2;
|
||||
}
|
||||
.config > div label:nth-of-type(1) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.post-wrapper {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
@@ -20,7 +41,6 @@ body {
|
||||
position: absolute;
|
||||
left: -9999;
|
||||
top: -9999;
|
||||
width: 600px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user