// ==UserScript==
// @name Tassel
// @version 1.7.5
// @description Pillowfort Extension Manager. Makes the use of a variety of extensions easier.
// @author Aki108
// @match https://www.pillowfort.social/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=pillowfort.social
// @supportURL https://www.pillowfort.social/Tassel
// @grant none
// @namespace https://greasyfork.org/users/1012189
// ==/UserScript==
(function() {
'use strict';
let extensionsIndexURL = "https://cdn.jsdelivr.net/gh/Aki-108/Tassel@2ca5662912154a616db1208bb7bfa94a991606a5/extensionsIndex.js";
let toastsURL = "https://cdn.jsdelivr.net/gh/Aki-108/Tassel@5716332e94d08b1a0662a799ac2dba905f8f1f11/toasts.js";
let styleURL = "https://cdn.jsdelivr.net/gh/Aki-108/Tassel@870c41b2942f50e0051f9b1a6e2971d868e9e035/style.css";
let jsonManager = "https://cdn.jsdelivr.net/gh/Aki-108/Tassel@c0d746e8d72e4d13fb29536f3155d648b9a99a6a/jsonManager.js";
let icon = document.createElement("div");
icon.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="tasselIconColor" width="20" height="20" viewBox="0 0 20 20">
<title>Tassel</title>
<path xmlns="http://www.w3.org/2000/svg" style="fill:none;stroke:#58b6dd;stroke-width:1.2px" d="
M 8 7 Q 6.5 8 6.5 12 Q 6.5 16 4 19
M 12 7 Q 13.5 8 13.5 12 Q 13.5 16 16 19
M 8 2.5 L 8 0.5 L 12 0.5 L 12 2.5
M 6 16 L 8 18.5 L 10 16 L 12 18.5 L 14 16
"/>
<circle cx="10" fill="none" stroke-width="1.2px" r="3" stroke="#58b6dd" cy="5"/>
</svg>`;
let settings2 = (JSON.parse(localStorage.getItem("tasselSettings2")) || {
"tassel": {
"extensions": [],
"highlightComments": false,
"notify": {
"active": true,
"inactive": true,
"new": true
},
"shortenSidebar": false,
"showWIP": false,
"stickyIcons": false,
"stickyToolbar": false,
"toastRead": -1
}
}).tassel;
if (!settings2.sidebar) settings2.sidebar = {};
if (!settings2.postFooter) settings2.postFooter = {};
let sortOrder = "new";//order in which to display extensions in the list
//src: https://aaronsmith.online/easily-load-an-external-script-using-javascript/
const loadScript_xcajbuzn = src => {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.type = 'text/javascript'
script.onload = resolve
script.onerror = reject
script.src = src
document.head.append(script)
})
}
const loadStyle_xcajbuzn = src => {
return new Promise((resolve, reject) => {
const style = document.createElement('link')
style.type = 'text/css'
style.rel = "stylesheet"
style.onload = resolve
style.onerror = reject
style.href = src
document.head.append(style)
})
}
//source: https://stackoverflow.com/a/43027791
var waitForJQuery = setInterval(function () {
if (typeof $ != 'undefined') {
clearInterval(waitForJQuery);
loadScript_xcajbuzn("https://cdn.jsdelivr.net/gh/vnausea/waitForKeyElements@f50495d44441c0c5d153d7a5ff229eeaace0bf9e/waitForKeyElements.js")
.then(() => init_xcajbuzn());
}
}, 10);
/* Initialize */
function init_xcajbuzn() {
loadStyle_xcajbuzn(styleURL);
loadAppearance_xcajbuzn();
initJsonManager_xcajbuzn();
loadScript_xcajbuzn(extensionsIndexURL)
.then(() => loadExtensions_xcajbuzn());
initToast_xcajbuzn();
createModal_xcajbuzn();
waitForKeyElements(".sidebar-expanded", initSidebar_xcajbuzn);
if (settings2.rememberPostSettings) setPrivacySettings_xcajbuzn();
let eventFeed = document.createElement("div");
eventFeed.id = "tasselEvents";
document.getElementsByTagName("body")[0].appendChild(eventFeed);
}
function initJsonManager_xcajbuzn() {
let modalReady = document.createElement("button");
modalReady.id = "tasselJsonManagerModalReady";
modalReady.style.display = "none";
document.body.appendChild(modalReady);
modalReady.addEventListener("click", function() {console.log("modal data ready")});
if (settings2.bottomPermalink) modalReady.addEventListener("click", function() {
let modal = document.getElementById("post-view-modal") || document.getElementById("reblog-modal");
let nav = modal.getElementsByClassName("post-nav-left");
Object.values(nav).forEach(function(item, index) {
if (!tasselJsonManager.modal.ready) return;
if (item.classList.contains("tasselPermalinked")) {
item.getElementsByClassName("tasselPermalinked")[0].href = `/posts/${tasselJsonManager.modal.json.original_post_id || tasselJsonManager.modal.json.id}`;
return;
}
let link = document.createElement("a");
link.setAttribute("target", "_blank");
link.title = "link to post";
link.classList.add("link_post", "svg-blue", "tasselPermalinked");
link.href = `/posts/${tasselJsonManager.modal.json.original_post_id || tasselJsonManager.modal.json.id}`;
link.style = "margin: 0 0 0 20px;";
link.innerHTML = `<img src="https://cdn.jsdelivr.net/gh/Aki-108/Tassel@50f03c59507325d27ccf9adb1a6fa46cdb6c5604/icons/link.svg" style="height: 20px;">`;
item.appendChild(link);
item.classList.add("tasselPermalinked");
});
});
let postReady = document.createElement("button");
postReady.id = "tasselJsonManagerPostReady";
postReady.style.display = "none";
document.body.appendChild(postReady);
postReady.addEventListener("click", function() {console.log("post data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"post data ready"});});
let commentReady = document.createElement("button");
commentReady.id = "tasselJsonManagerCommentReady";
commentReady.style.display = "none";
document.body.appendChild(commentReady);
commentReady.addEventListener("click", function() {console.log("comment data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"comment data ready"});});
let reblogReady = document.createElement("button");
reblogReady.id = "tasselJsonManagerReblogReady";
reblogReady.style.display = "none";
document.body.appendChild(reblogReady);
reblogReady.addEventListener("click", function() {console.log("reblog data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"reblog data ready"});});
let likeReady = document.createElement("button");
likeReady.id = "tasselJsonManagerLikeReady";
likeReady.style.display = "none";
document.body.appendChild(likeReady);
likeReady.addEventListener("click", function() {console.log("like data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"like data ready"});});
let feedReady = document.createElement("button");
feedReady.id = "tasselJsonManagerFeedReady";
feedReady.style.display = "none";
document.body.appendChild(feedReady);
feedReady.addEventListener("click", function() {console.log("feed data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"feed data ready"});});
if (settings2.bottomPermalink) feedReady.addEventListener("click", function() {
addBottomPermalink_xcajbuzn();
});
let followersReady = document.createElement("button");
followersReady.id = "tasselJsonManagerFollowersReady";
followersReady.style.display = "none";
document.body.appendChild(followersReady);
followersReady.addEventListener("click", function() {console.log("followers data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"list of followers ready"});});
let followingReady = document.createElement("button");
followingReady.id = "tasselJsonManagerFollowingReady";
followingReady.style.display = "none";
document.body.appendChild(followingReady);
followingReady.addEventListener("click", function() {console.log("following data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"list of following ready"});});
let mutualsReady = document.createElement("button");
mutualsReady.id = "tasselJsonManagerMutualsReady";
mutualsReady.style.display = "none";
document.body.appendChild(mutualsReady);
mutualsReady.addEventListener("click", function() {console.log("mutuals data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"list of mutals ready"});});
let communitiesReady = document.createElement("button");
communitiesReady.id = "tasselJsonManagerCommunitiesReady";
communitiesReady.style.display = "none";
document.body.appendChild(communitiesReady);
communitiesReady.addEventListener("click", function() {console.log("community data ready");pushEvent_xcajbuzn({source:"JSON Manager",text:"list of communities ready"});});
loadScript_xcajbuzn(jsonManager);
}
/* Add buttons to sidebar */
function initSidebar_xcajbuzn() {
if (document.getElementsByClassName("tasselSidebarBig").length > 0) return; //stop if sidebar has already been initialized
//add button to collapsed sidebar
let sidebarSmall = document.getElementsByClassName("sidebar-collapsed")[1];
let settingsSmall = document.createElement("a");
settingsSmall.href = "";//add a link to comply with accessibility requirements but don't open the link
if (settings2.sidebar.collapsedTassel) settingsSmall.classList.add("tasselRemoveSidebarElement");
settingsSmall.addEventListener("click", function(event) {
event.preventDefault();
});
settingsSmall.classList.add("sidebar-icon", "tasselSidebarSmall");
settingsSmall.title = "Tassel";
let imageSmall = icon.cloneNode(true);
settingsSmall.appendChild(imageSmall);
settingsSmall.addEventListener("click", openModal_xcajbuzn);
sidebarSmall.appendChild(settingsSmall);
//add button to expanded sidebar
let sidebarBig = document.getElementsByClassName("sidebar-expanded")[1];
for (let child of sidebarBig.children) {
if (child.href !== "https://www.pillowfort.social/settings") continue;
if (child.firstChild.style === undefined) continue;
child.firstChild.style.paddingBottom = "3px";
}
let settingsBigWrapper = document.createElement("a");
settingsBigWrapper.href = "";//add a link to comply with accessibility requirements but don't open the link
settingsBigWrapper.addEventListener("click", function(event) {
event.preventDefault();
});
settingsBigWrapper.addEventListener("click", openModal_xcajbuzn);
if (settings2.sidebar.expandedTassel) settingsBigWrapper.classList.add("tasselRemoveSidebarElement");
let settingsBig = document.createElement("div");
settingsBig.classList.add("sidebar-topic", "tasselSidebarBig");
let image = icon.children[0];
image.classList.add("sidebar-img");
image.style.transform = "scale(1.2)";
settingsBig.appendChild(image);
settingsBig.innerHTML += "Tassel";
settingsBigWrapper.appendChild(settingsBig);
sidebarBig.appendChild(settingsBigWrapper);
if (!settings2.sidebar.expandedEpander) return;
let anyHidden = Object.values(settings2.sidebar).some(function(item) {
return item;
});
if (anyHidden) {
let sidebarBig = document.getElementById("expanded-bar-container");
let sidebarBottom = sidebarBig.getElementsByClassName("sidebar-bottom")[0];
sidebarBottom.children[6].style.display = "none";
let button = document.createElement("button");
button.id = "tasselExpandedSidebarExpander";
button.setAttribute("aria-label", "show hidden elements");
button.innerHTML = `<div></div>`;
sidebarBottom.appendChild(button);
button.addEventListener("click", function() {
let sidebar = document.getElementById("expanded-bar-container").parentNode;
if (sidebar.classList.contains("visible")) sidebar.classList.remove("visible");
else sidebar.classList.add("visible");
});
let button2 = document.createElement("button");
button2.id = "tasselCollapsedSidebarExpander";
button2.setAttribute("aria-label", "show hidden elements");
button2.innerHTML = `<div></div>`;
sidebarBig.parentNode.getElementsByClassName("sidebar-collapsed")[1].appendChild(button2);
button2.addEventListener("click", function() {
let sidebar = document.getElementById("expanded-bar-container").parentNode;
if (sidebar.classList.contains("visible")) sidebar.classList.remove("visible");
else sidebar.classList.add("visible");
});
}
}
/* Create the modal basis with sidebar */
function createModal_xcajbuzn() {
let modal = document.createElement("div");
modal.id = "tasselModal";
modal.classList.add("modal", "in");
//create the stucture with header, sidebar and content-box
modal.innerHTML = `
<div id='tasselModalDialog1' class='modal-dialog'>
<div id='tasselModalDialog2' class='modal-content'>
<header id='tasselModalHeader'>
<button id='tasselModalClose' class='close' type='button' title='Close'>
<span style='color:var(--postFontColor);'>x</span>
</button>
<h1 class='modal-title'>Tassel</h4>
</header>
<div id='tasselModalGrid'>
<nav id='tasselModalSidebar' aria-label='Tassel Navigation'>
<button class='tasselModalSidebarEntry' id='tasselModalSidebarExtensions'>Extensions</button>
<button class='tasselModalSidebarEntry' id='tasselModalSidebarSettings'>Settings</button>
<button class='tasselModalSidebarEntry' id='tasselModalSidebarAbout'>About</button>
</nav>
<main id='tasselModalContent'></main>
</div>
</div>
</div>
<div id='tasselModalBackground' class='modal-backdrop in'></div>`;
document.getElementsByTagName("body")[0].appendChild(modal);
document.getElementById("tasselModalSidebarExtensions").addEventListener("click", displayExtensions_xcajbuzn);
document.getElementById("tasselModalSidebarSettings").addEventListener("click", displaySettings_xcajbuzn);
document.getElementById("tasselModalSidebarAbout").addEventListener("click", displayAbout_xcajbuzn);
document.getElementById("tasselModalBackground").addEventListener("click", closeModal_xcajbuzn);
document.getElementById("tasselModalClose").addEventListener("click", closeModal_xcajbuzn);
}
/* Load extensions from external file */
function loadExtensions_xcajbuzn() {
settings2.extensions.forEach(function(value) {
let extension = extensionsIndex.find(function(data) {
return data.id == value.id;
});
if (!extension) return;
if (extension.css) loadStyle_xcajbuzn(extension.css);
if (extension.src) loadScript_xcajbuzn(extension.src);
});
evaluateURLParameter_xcajbuzn();
}
/* Load selected appearance changes */
function loadAppearance_xcajbuzn() {
let css = "";
if (settings2.shortenSidebar) css += ".sidebar-topic{margin-top:8px !important;margin-bottom:8px !important;padding-bottom:0 !important;}.sidebar-indent{padding-bottom:4px !important;}.sidebar-bottom-left{padding-top:10px;padding-bottom:8px;}";
if (settings2.stickyIcons) css += ".side-info{position:sticky;top:70px;margin-bottom:10px;}";
if (settings2.stickyToolbar) css += ".gray-theme.fr-toolbar.fr-sticky-off,.gray-theme.fr-toolbar.fr-sticky-on{position:sticky;top:50px !important;z-index:5;}.fr-sticky-dummy{display:none !important;}";
if (settings2.stickyCommentHeader) css += ".comments-container .header{position:sticky;top:50px;z-index:3;}";
if (settings2.goldToBlue) css += ".svg-gold{filter:brightness(0) saturate(100%) invert(65%) sepia(86%) saturate(377%) hue-rotate(166deg) brightness(87%) contrast(98%);}";
if (settings2.noFrames) css += ".post-container .avatar-frame {display: none;} .post-container .avatar img.with-frame {border: none !important; background-color: #fff !important;} body.dark-theme .post-container .avatar img {background-color: #d9dbe0 !important;}";
if (settings2.expandNotes) css += "body.dark-theme #notifs-dashboard .notif-mini {background-color: #1b1d20;} #notifs-dashboard .repeat-notifs {max-height: 500px; transition: max-height 0.5s ease-in; overflow-y: auto;} #notifs-dashboard .repeat-notifs.expanded {max-height: 0px; overflow: hidden; transition: max-height 0.25s ease-out;}";
//Post footer
if (settings2.postFooter.swapLeftRight) css += ".post .post-nav .post-nav-left {float: right; padding-right: 20px;} .post .post-nav .post-nav-right {float: left; padding-right: 0;}";
css += ".post .post-nav .post-nav-left, .post .post-nav .post-nav-right {display: grid; grid-auto-flow: column; align-items: baseline;} .post-nav .post-nav-left {float: left;}";//prepare for reordering
if (settings2.postFooter.comments !== 0) css += `.post .post-nav .post-nav-left a.nav-tab {order: ${settings2.postFooter.comments || 0};}`;
if (settings2.postFooter.reblog !== 0) css += `.post .post-nav .post-nav-left span:has(> a.nav-tab) {order: ${settings2.postFooter.reblog || 0};}`;
if (settings2.postFooter.like !== 0) css += `.post .post-nav .post-nav-left span:has(> span a.like-button), .post .post-nav .post-nav-left span:has(> a.like-button) {order: ${settings2.postFooter.like || 0};}`;
if (settings2.postFooter.permalink !== 0) css += `.post .post-nav .post-nav-left .tasselPermalinked {order: ${settings2.postFooter.permalink || 0};}`;
if (settings2.postFooter.subscribe !== 0) css += `.post .post-nav .post-nav-left #tasselPostSubscriberModalSubscribe {order: ${settings2.postFooter.subscribe || 0};}`;
if (settings2.postFooter.activity !== 0) css += `.post .post-nav .post-nav-left .tasselTimeFormatActivity {order: ${settings2.postFooter.activity || 0};}`;
if (settings2.postFooter.flag !== 0) css += `.post .post-nav .post-nav-right span:has(> a.flag-button) {order: ${settings2.postFooter.flag || 0};}`;
if (settings2.postFooter.edit !== 0) css += `.post .post-nav .post-nav-right span:has(> a img.edit-img) {order: ${settings2.postFooter.edit || 0};}`;
if (settings2.postFooter.delete !== 0) css += `.post .post-nav .post-nav-right span:has(> a.pointer-cursor) {order: ${settings2.postFooter.delete || 0};}`;
if (settings2.postFooter.blockPost !== 0) css += `.post .post-nav .post-nav-right span:has(> a:not([id^='post'])) {order: ${settings2.postFooter.blockPost || 0};}`;
//Apply new styling to page
//src: https://stackoverflow.com/q/3922139
let style = document.createElement("style");
style.setAttribute('type', 'text/css');
if (style.styleSheet) {//IE
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
document.head.appendChild(style);
//Sidebar
if (settings2.sidebar.expandedPost) getSidebarElement_xcajbuzn("https://www.pillowfort.social/posts/new").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedDrafts && getSidebarElement_xcajbuzn("https://www.pillowfort.social/drafts")) getSidebarElement_xcajbuzn("https://www.pillowfort.social/drafts").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedQueue && getSidebarElement_xcajbuzn("https://www.pillowfort.social/queued_posts")) getSidebarElement_xcajbuzn("https://www.pillowfort.social/queued_posts").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedInbox) getSidebarElement_xcajbuzn("https://www.pillowfort.social/messages").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedNotifications) getSidebarElement_xcajbuzn("https://www.pillowfort.social/notifs_dash").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedCommunities) getSidebarElement_xcajbuzn("https://www.pillowfort.social/communities").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedSearch) document.getElementById("searchme").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedFilters) {
Object.values(document.getElementById("expanded-bar-container").getElementsByTagName("a")).find(function(item) {
return item.getAttribute("data-target") === "#filtersModal"
}).classList.add("tasselRemoveSidebarElement");
}
if (settings2.sidebar.expandedBlocklist) getSidebarElement_xcajbuzn("https://www.pillowfort.social/block_list").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedPremium) getSidebarElement_xcajbuzn("https://www.pillowfort.social/subscriptions/show").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedSettings) getSidebarElement_xcajbuzn("https://www.pillowfort.social/settings").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedFollowers) getSidebarElement_xcajbuzn("https://www.pillowfort.social/followers").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedFollowing) getSidebarElement_xcajbuzn("https://www.pillowfort.social/following").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedMutuals) getSidebarElement_xcajbuzn("https://www.pillowfort.social/mutuals").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedInvites) getSidebarElement_xcajbuzn("https://www.pillowfort.social/generate_invites").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedDonations) getSidebarElement_xcajbuzn("https://www.pillowfort.social/donations").classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.expandedAbout) getSidebarElement_xcajbuzn("https://www.pillowfort.social/about").parentNode.classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.collapsedPost) getSidebarElement_xcajbuzn("https://www.pillowfort.social/posts/new", true).classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.collapsedInbox) getSidebarElement_xcajbuzn("https://www.pillowfort.social/messages", true).classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.collapsedNotifications) getSidebarElement_xcajbuzn("https://www.pillowfort.social/notifs_dash", true).classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.collapsedCommunities) getSidebarElement_xcajbuzn("https://www.pillowfort.social/communities", true).classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.collapsedFilters) {
Object.values(document.getElementsByClassName("sidebar-collapsed")[1].children).find(function(item) {
return item.getAttribute("data-target") === "#filtersModal"
}).classList.add("tasselRemoveSidebarElement");
}
if (settings2.sidebar.collapsedBlocklist) getSidebarElement_xcajbuzn("https://www.pillowfort.social/block_list", true).classList.add("tasselRemoveSidebarElement");
if (settings2.sidebar.collapsedSettings) getSidebarElement_xcajbuzn("https://www.pillowfort.social/settings", true).classList.add("tasselRemoveSidebarElement");
}
/* Create the basis for toasts */
function initToast_xcajbuzn() {
let toastFrame = document.createElement("div");
toastFrame.id = "tasselToast";
document.getElementsByTagName("body")[0].appendChild(toastFrame);
loadScript_xcajbuzn(toastsURL)
.then(() => loadToasts_xcajbuzn());
}
/* Check which toasts to display */
function loadToasts_xcajbuzn() {
if (!toasts) return;
toasts.forEach(function(toast, index) {
if (toast.timestamp <= settings2.toastRead) return;//only show new toasts
let extension = settings2.extensions.find(function(item) {
return item.id == toast.extension;
});
if (
//show toast for active extensions
(extension//only when the extension is active
&& settings2.notify.active//only when active is wanted
&& extension.since < toast.timestamp//only if it's been active before the toast
&& !toast.new)//only if the extension is not new
||
//show toast for inactive extensions
(!extension//only when the extension is inactive
&& settings2.notify.inactive//only when inactive is wanted
&& !toast.new)//only when the extension is not new
||
//show toast for new extensions
(toast.new//only when the extension is new
&& settings2.notify.new)//only when new is wanted
||
//show toast that don't belong to an extension
(toast.extension == "0")
) {
pushToast_xcajbuzn(toast);
}
});
}
/* Create toast */
function pushToast_xcajbuzn(data) {
let toast = document.createElement("div");
toast.innerHTML = `
<h1>${data.title}</h1>
<span>${data.text}</span>
`;
toast.setAttribute("timestamp", data.timestamp);
document.getElementById("tasselToast").appendChild(toast);
//add relativ links
if (document.getElementById("tasselToast").lastChild.getElementsByTagName("a").length > 0) {
let links = Object.values(document.getElementById("tasselToast").lastChild.getElementsByTagName("a"));
links.forEach(function(link) {
if (link.tagName == "A" && link.href == "" && link.hasAttribute("linkRel")) {
let url = new URL(window.location);
url.searchParams.set("tassel", link.getAttribute("linkRel"));
link.href = url;
}
});
}
document.getElementById("tasselToast").lastChild.style.height = document.getElementById("tasselToast").lastChild.clientHeight + "px";
//mark as read when clicked
document.getElementById("tasselToast").lastChild.addEventListener("click", function() {
if (this.getAttribute("timestamp") > settings2.toastRead) settings2.toastRead = this.getAttribute("timestamp")*1;
saveSettings_xcajbuzn();
this.classList.add("fade-out");
});
}
/* Create event log for debug more */
function pushEvent_xcajbuzn(data) {
if (!settings2.debug) return;
let event = document.createElement("div");
event.innerHTML = `
<p><b>${data.source}:</b> ${data.text}</p>
`;
event.id = "event" + Math.random();
document.getElementById("tasselEvents").appendChild(event);
window.setTimeout(function() {
event.classList.add("fade-out");
window.setTimeout(function() {
event.remove();
}, 5000);
}, 30000);
}
/* Open modal when URL parameters say so and highlight specific elements */
function evaluateURLParameter_xcajbuzn() {
let queryString = new URLSearchParams(window.location.search.substring(1));
for (let pair of queryString.entries()) {
if (pair[0] == "tassel") {
openModal_xcajbuzn(pair[1]);
} else if (pair[0] == "tExtension") {
let extension = document.getElementById(pair[1]);
if (!extension) {
let list = document.getElementById("tasselModalContentExtensionsList");
extension = document.createElement("section");
extension.id = pair[1];
extension.classList.add("tasselExtension");
extension.innerHTML = `
<div>
<label>Coming Soon</label>
</div>
`;
list.insertBefore(extension, list.firstChild);
}
extension.scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"})
extension.style.animationDuration = "2s";
extension.style.animationIterationCount = "2";
extension.style.animationName = "blink";
} else if (pair[0] == "tSwitch") {
let setting = document.getElementsByClassName(pair[1])[0].parentNode;
setting.scrollIntoView({behavior: "smooth", block: "center", inline: "nearest"})
setting.style.animationDuration = "2s";
setting.style.animationIterationCount = "2";
setting.style.animationName = "blink";
} else if (pair[0] == "comment" && settings2.highlightComments) {
window.setTimeout(function() {
let comment = document.getElementById(pair[1]);
comment.style.animationDuration = "2s";
comment.style.animationIterationCount = "2";
comment.style.animationName = "blink";
}, 2000);
} else if (pair[0] === "tExtensionSettings") {
window.setTimeout(function() {
let button = document.getElementById(pair[1]);
if (button) button.click();
}, 500);
}
}
}
/* Make modal visible */
function openModal_xcajbuzn(tab) {
document.getElementsByTagName("body")[0].classList.add("modal-open");
document.getElementsByTagName("nav")[0].style.paddingRight = "11px";
document.getElementById("tasselModal").style.display = "block";
switch (tab) {
case "settings": displaySettings_xcajbuzn(); break;
case "about": displayAbout_xcajbuzn(); break;
default: displayExtensions_xcajbuzn();
}
}
/* Make modal invisible */
function closeModal_xcajbuzn() {
document.getElementsByTagName("body")[0].classList.remove("modal-open");
document.getElementsByTagName("nav")[0].style.paddingRight = "0";
document.getElementById("tasselModal").style.display = "none";
}
/* Create a list of all extensions in the modal */
function displayExtensions_xcajbuzn() {
//reset modal
let content = document.getElementById("tasselModalContent");
content.innerHTML = "";
let sidebarEntries = document.getElementsByClassName("tasselModalSidebarEntry");
Object.values(sidebarEntries).forEach(function(data, index) {
data.classList.remove("active");
});
document.getElementById("tasselModalSidebarExtensions").classList.add("active");
//create content
let header = document.createElement("div");
header.id = "tasselModalContentExtensionsHeader";
let note = document.createElement("p");
note.innerHTML = "Select the extensions you want to use. Changes might need a page reload to become active.";
header.appendChild(note);
let sortLabel = document.createElement("label");
sortLabel.innerHTML = "Sort by ";
let sorter = document.createElement("select");
sorter.addEventListener("change", function() {
sortOrder = this.value;
displayExtensions_xcajbuzn();
});
sorter.innerHTML = `
<option value="a">A > Z</option>
<option value="z">Z > A</option>
<option value="new" selected>new > old</option>
<option value="old">old > new</option>
<option value="1.0">V1.0 > V0.1</option>
<option value="0.1">V0.1 > V1.0</option>
`;
for (let a = 0; a < sorter.children.length; a++) {
if (sortOrder == sorter.children[a].value) sorter.children[a].selected = true;
}
sortLabel.appendChild(sorter);
header.appendChild(sortLabel);
content.appendChild(header);
//sort before displaying
extensionsIndex.sort(function(one, two) {
switch (sortOrder) {
case "a": return one.name > two.name ? 1 : -1;
case "z": return one.name < two.name ? 1 : -1;
case "new": return one.updated < two.updated ? 1 : -1;
case "old": return one.updated > two.updated ? 1 : -1;
case "1.0": {
let one_ = one.version.split(".");
let two_ = two.version.split(".");
if (one_[0]*1 === two_[0]*1)
if (one_[1]*1 === two_[1]*1) return two_[2] - one_[2];
else return two_[1] - one_[1];
else return two_[0] - one_[0];
}
case "0.1": {
let one_ = one.version.split(".");
let two_ = two.version.split(".");
if (one_[0]*1 === two_[0]*1)
if (one_[1]*1 === two_[1]*1) return one_[2] - two_[2];
else return one_[1] - two_[1];
else return one_[0] - two_[0];
}
default: return one.updated < two.updated ? 1 : -1;
}
});
let extensionsList = document.createElement("div");
extensionsList.id = "tasselModalContentExtensionsList"
//create extension entries in modal
extensionsIndex.forEach(function(data, index) {
let frame = document.createElement("section");
frame.id = "extension"+data.id;
frame.classList.add("tasselExtension");
let checkboxID = "checkbox"+data.name;
let info = document.createElement("div");
let title = document.createElement("label");
title.innerHTML = data.name;
title.setAttribute("for", checkboxID);
info.appendChild(title);
let description = document.createElement("p");
description.innerHTML = data.description;
description.style.margin = "0";
info.appendChild(description);
if (data.features != null && data.features.length > 0) {
let details = document.createElement("details");
details.style.margin = "10px 0 0 10px";
let summary = document.createElement("summary");
summary.innerHTML = "Features...";
summary.style.marginBottom = ".5em";
details.appendChild(summary);
let list = document.createElement("ul");
list.style.paddingLeft = "14px";
list.style.margin = "0";
data.features.forEach(function(feature) {
let text = document.createElement("li");
text.innerHTML = feature;
list.appendChild(text);
});
details.appendChild(list);
info.appendChild(details);
}
frame.appendChild(info);
let sidebar = document.createElement("div");
sidebar.classList.add("tasselExtensionSidebar");
let checkbox = document.createElement("input");
checkbox.id = checkboxID;
checkbox.title = "activate";
checkbox.type = "checkbox";
checkbox.setAttribute("extension", data.id);
checkbox.addEventListener("click", function() {
let id = this.getAttribute("extension");
toggleExtension_xcajbuzn(id);
if (this.checked) {
let extension = extensionsIndex.find(function(data) {
return data.id == id;
});
if (!extension) return;
if (extension.css) loadStyle_xcajbuzn(extension.css);
if (extension.src) loadScript_xcajbuzn(extension.src);
}
});
let entry = settings2.extensions.find(function(value) {
return value.id == data.id;
});
if (entry != null) {
checkbox.checked = true;
}
sidebar.appendChild(checkbox);
let link = document.createElement("a");
link.classList.add("link_post");
link.target = "_blank";
link.title = "link to post";
link.href = data.post;
link.innerHTML = `<img alt="link to post" style="width:100%;" src="https://cdn.jsdelivr.net/gh/Aki-108/Tassel@50f03c59507325d27ccf9adb1a6fa46cdb6c5604/icons/link.svg">`;
if (data.post) sidebar.appendChild(link);
let version = document.createElement("span");
version.classList.add("tasselExtensionVersion");
version.innerHTML = data.version;
version.title = `version ${data.version}, published ${new Date(data.created).toLocaleDateString()}, updated ${new Date(data.updated).toLocaleDateString()}`;
sidebar.appendChild(version);
let author = document.createElement("span");
author.classList.add("tasselExtensionAuthor");
author.innerHTML = "3rd";
author.title = "This is a third-party extension. The author is " + data.author;
if (data.author !== "Aki108") sidebar.appendChild(author);
let wip = document.createElement("span");
wip.classList.add("tasselExtensionWIP");
wip.innerHTML = "WIP";
wip.title = "This extension is currently in development and might not work as intended. It might be removed in the future. Use at your own risk.";
if (data.version.split(".")[0] < 1) sidebar.appendChild(wip);
frame.appendChild(sidebar);
if (settings2.showWIP || data.version >= 1) extensionsList.appendChild(frame);//only display extension if it's a full version or WIPs are wanted
});
content.appendChild(extensionsList);
content.appendChild(document.createElement("hr"));
let info2 = document.createElement("div");
info2.innerHTML = `
<p>Icon Legend:</p>
<ul>
<li>
<img alt="link to post" style="width:25px;" src="https://cdn.jsdelivr.net/gh/Aki-108/Tassel@50f03c59507325d27ccf9adb1a6fa46cdb6c5604/icons/link.svg">
Link to Post: This link will take you to the announcement post of the extension.
</li>
<li>
<span style="cursor: normal;display: inline;margin: 0;" class="tasselExtensionVersion">1.0</span>
Version: This is the version of the extension.
</li>
<li>
<span style="cursor: normal;display: inline-block;padding-top:3px;" class="tasselExtensionAuthor">3rd</span>
Third-Party: This extension is from a third-party author.
</li>
<li>
<span style="cursor: normal;display: inline-block;padding-top:6px;" class="tasselExtensionWIP">WIP</span>
Work in Progress: This extension is currently in development and might not work as intended. It might be removed in the future. Use at your own risk.
</li>
</ul>
`;
content.appendChild(info2);
content.appendChild(document.createElement("hr"));
let info3 = document.createElement("p");
info3.innerHTML = "If you enjoy an extension, consider commenting / reblogging / liking the corresponding announcement post by opening the link of the extension.";
content.appendChild(info3);
}
/* Create the About page in the modal */
function displayAbout_xcajbuzn() {
//reset modal
let content = document.getElementById("tasselModalContent");
content.innerHTML = "";
let sidebarEntries = document.getElementsByClassName("tasselModalSidebarEntry");
Object.values(sidebarEntries).forEach(function(data, index) {
data.classList.remove("active");
});
document.getElementById("tasselModalSidebarAbout").classList.add("active");
content.innerHTML = `
<div style="justify-content:center;display:grid;text-align:center;">
<h2 style="margin:0;">Tassel</h2>
<span style="margin-bottom:.5em;">by Aki108</span>
<span style="margin-bottom:.5em;">Version ${GM_info.script.version}</span>
<span style="margin-bottom:2.5em;">since 11th Dec 2022</span>
<a style="margin-bottom:1em;" href="https://www.pillowfort.social/Tassel">Visit Tassel's Fort</a>
<a style="margin-bottom:1em;" href="https://github.com/Aki-108/Tassel">Tassel on GitHub</a>
<a style="margin-bottom:1em;" href="https://github.com/Aki-108/Tassel/wiki/Version-History">Version History</a>
</div>
`;
}
/* Create the Tassel Settings page in the modal */
function displaySettings_xcajbuzn() {
//reset modal
let content = document.getElementById("tasselModalContent");
content.innerHTML = "";
let sidebarEntries = document.getElementsByClassName("tasselModalSidebarEntry");
Object.values(sidebarEntries).forEach(function(data, index) {
data.classList.remove("active");
});
document.getElementById("tasselModalSidebarSettings").classList.add("active");
//header
let info0 = document.createElement("p");
info0.innerHTML = "Changes will become active after a page reload.";
content.appendChild(info0);
content.appendChild(document.createElement("hr"));
//Notifications
let title1 = document.createElement("h2");
title1.innerHTML = "Notifications";
content.appendChild(title1);
let info1 = document.createElement("p");
info1.innerHTML = "Notifications will show up in the bottom right corner on any Pillowfort page. They can be marked as 'read' by clicking on them.";
content.appendChild(info1);
content.appendChild(createSwitch_xcajbuzn("Get Notifications for Active Extensions", settings2.notify.active ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.notify.active = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Get Notifications for Inactive Extensions", settings2.notify.inactive ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.notify.inactive = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Get Notifications for New Extensions", settings2.notify.new ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.notify.new = this.checked;
saveSettings_xcajbuzn();
});
//Appearance
content.appendChild(document.createElement("hr"));
let title2 = document.createElement("h2");
title2.innerHTML = "Appearance";
content.appendChild(title2);
content.appendChild(createSwitch_xcajbuzn("Highlight Linked Comments", settings2.highlightComments ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.highlightComments = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Sticky Icons", settings2.stickyIcons ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.stickyIcons = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Sticky Toolbars", settings2.stickyToolbar ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.stickyToolbar = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Sticky Comment Headers", settings2.stickyCommentHeader ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.stickyCommentHeader = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Turn Golden Icons Blue", settings2.goldToBlue ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.goldToBlue = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Hide Avatar Frames", settings2.noFrames ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.noFrames = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Expand Notifications", settings2.expandNotes ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.expandNotes = this.checked;
saveSettings_xcajbuzn();
});
let section1 = document.createElement("details");
section1.id = "tasselSettingsSidebarSection";
content.appendChild(section1);
section1.innerHTML = `<summary><h3>Sidebar</h3>${createTooltip_xcajbuzn("Click the arrow to view more sidebar options.").outerHTML}</summary>`;
section1.appendChild(createSwitch_xcajbuzn("Shorten Expanded Sidebar", settings2.shortenSidebar ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.shortenSidebar = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Add a button to view the full sidebar", settings2.sidebar.expandedEpander ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedEpander = this.checked;
saveSettings_xcajbuzn();
});
let heading1 = document.createElement("h4");
heading1.innerHTML = 'Expanded'
section1.appendChild(heading1);
section1.appendChild(createSwitch_xcajbuzn("Remove 'Post'", settings2.sidebar.expandedPost ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedPost = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Drafts'", settings2.sidebar.expandedDrafts ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedDrafts = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Queue'", settings2.sidebar.expandedQueue ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedQueue = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Inbox'", settings2.sidebar.expandedInbox ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedInbox = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Notifications'", settings2.sidebar.expandedNotifications ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedNotifications = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Subscriptions'" + createTooltip_xcajbuzn("This is part of the Post Subscriber extension.").outerHTML, settings2.sidebar.expandedSubscriptions ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedSubscriptions = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Communities'", settings2.sidebar.expandedCommunities ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedCommunities = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Search'", settings2.sidebar.expandedSearch ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedSearch = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Filters & Blacklist' / 'Advanced Blacklist'", settings2.sidebar.expandedFilters ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedFilters = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Blocked Users'", settings2.sidebar.expandedBlocklist ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedBlocklist = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'PF Premium'", settings2.sidebar.expandedPremium ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedPremium = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Settings'", settings2.sidebar.expandedSettings ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedSettings = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Tassel'", settings2.sidebar.expandedTassel ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedTassel = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Followers'", settings2.sidebar.expandedFollowers ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedFollowers = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Following'", settings2.sidebar.expandedFollowing ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedFollowing = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Mutuals'", settings2.sidebar.expandedMutuals ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedMutuals = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Invites'", settings2.sidebar.expandedInvites ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedInvites = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Donate!'", settings2.sidebar.expandedDonations ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedDonations = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'About & Contact'", settings2.sidebar.expandedAbout ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.expandedAbout = this.checked;
saveSettings_xcajbuzn();
});
let heading2 = document.createElement("h4");
heading2.innerHTML = 'Collapsed'
section1.appendChild(heading2);
section1.appendChild(createSwitch_xcajbuzn("Remove 'New Post'", settings2.sidebar.collapsedPost ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedPost = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Inbox'", settings2.sidebar.collapsedInbox ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedInbox = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Notifications'", settings2.sidebar.collapsedNotifications ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedNotifications = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Subscriptions'" + createTooltip_xcajbuzn("This is part of the Post Subscriber extension.").outerHTML, settings2.sidebar.collapsedSubscriptions ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedSubscriptions = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Communities'", settings2.sidebar.collapsedCommunities ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedCommunities = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Filters & Blacklist' / 'Advanced Blacklist'", settings2.sidebar.collapsedFilters ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedFilters = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Blocked Users'", settings2.sidebar.collapsedBlocklist ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedBlocklist = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Settings'", settings2.sidebar.collapsedSettings ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedSettings = this.checked;
saveSettings_xcajbuzn();
});
section1.appendChild(createSwitch_xcajbuzn("Remove 'Tassel'", settings2.sidebar.collapsedTassel ? "checked" : ""));
section1.lastChild.children[0].addEventListener("change", function() {
settings2.sidebar.collapsedTassel = this.checked;
saveSettings_xcajbuzn();
});
let section2 = document.createElement("details");
section2.id = "tasselSettingsFooterSection";
content.appendChild(section2);
section2.innerHTML = `<summary><h3>Post Footer</h3>${createTooltip_xcajbuzn("Click the arrow to view more post footer options.").outerHTML}</summary>`;
section2.appendChild(createSwitch_xcajbuzn("Swap left and right", settings2.postFooter.swapLeftRight ? "checked" : ""));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.swapLeftRight = this.checked;
saveSettings_xcajbuzn();
});
let info2 = document.createElement("p");
info2.innerHTML = "Position values of zero are the default and have no effect.";
section2.appendChild(info2);
let heading3 = document.createElement("h4");
heading3.innerHTML = 'Interaction Area'
section2.appendChild(heading3);
section2.appendChild(createNumericInput_xcajbuzn("Comment Position", settings2.postFooter.comments));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.comments = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Reblog Position", settings2.postFooter.reblog));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.reblog = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Like Position", settings2.postFooter.like));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.like = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Permalink Position" + createTooltip_xcajbuzn("This refers to the bottom permalink you can add with Tassel.").outerHTML, settings2.postFooter.permalink));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.permalink = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Subscribe Position" + createTooltip_xcajbuzn("Post subscribing is part of the Post Subscriber extension.").outerHTML, settings2.postFooter.subscribe));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.subscribe = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Activity Position" + createTooltip_xcajbuzn("The activity timestamp is part of the Time Format extension.").outerHTML, settings2.postFooter.activity));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.activity = this.value;
saveSettings_xcajbuzn();
});
let heading4 = document.createElement("h4");
heading4.innerHTML = 'Flagging Area'
section2.appendChild(heading4);
section2.appendChild(createNumericInput_xcajbuzn("Flag Position", settings2.postFooter.flag));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.flag = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Edit Position", settings2.postFooter.edit));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.edit = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Delete Position", settings2.postFooter.delete));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.delete = this.value;
saveSettings_xcajbuzn();
});
section2.appendChild(createNumericInput_xcajbuzn("Block Position" + createTooltip_xcajbuzn("Post blocking is part of the Advanced Blacklist extension.").outerHTML, settings2.postFooter.blockPost));
section2.lastChild.children[0].addEventListener("change", function() {
settings2.postFooter.blockPost = this.value;
saveSettings_xcajbuzn();
});
//Other
content.appendChild(document.createElement("hr"));
let title4 = document.createElement("h2");
title4.innerHTML = "Other";
content.appendChild(title4);
content.appendChild(createSwitch_xcajbuzn("Show Experimental Extensions in the List", settings2.showWIP ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.showWIP = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Add Permalink to Post-Footer", settings2.bottomPermalink ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.bottomPermalink = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Remember Privacy Settings", settings2.rememberPostSettings ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.rememberPostSettings = this.checked;
saveSettings_xcajbuzn();
});
content.appendChild(createSwitch_xcajbuzn("Debug Mode", settings2.debug ? "checked" : ""));
content.lastChild.children[0].addEventListener("change", function() {
settings2.debug = this.checked;
saveSettings_xcajbuzn();
});
//Data Management
content.appendChild(document.createElement("hr"));
let title3 = document.createElement("h2");
title3.innerHTML = "Data";
content.appendChild(title3);
let grid3 = document.createElement("div");
grid3.id = "tasselSettingsData";
let select3 = document.createElement("select");
select3.id = "tasselResetSelect";
select3.setAttribute("aria-label", "data group");
select3.innerHTML = `
<option value="tasselSettings2">Tassel Settings</option>
<option value="tasselAdvancedBlacklist">Advanced Blacklist</option>
<option value="tasselBlocklistAnnotations">Blocklist Annotations</option>
<option value="tasselJsonManager">JSON Manager</option>
<option value="tasselModerationAid">Moderation Aid</option>
<option value="tasselNoteDetails">Note Details</option>
<option value="tasselPostSubscriber">Post Subscriber</option>
<option value="tasselSidebarCounts">Sidebar Counts</option>
<option value="tasselTaggingTools">Tagging Tools</option>
<option value="tasselUserMuting">User Muting</option>
`;
grid3.appendChild(select3);
let button2 = document.createElement("button");
button2.id = "tasselJSONViewButton";
button2.innerHTML = "View";
button2.classList.add("tasselButton");
button2.addEventListener("click", function() {
let viewFrame = document.getElementById("tasselJSONView");
if (viewFrame) {
viewFrame.value = JSON.stringify(JSON.parse(localStorage.getItem(document.getElementById("tasselResetSelect").value)), null, 2);
} else {
viewFrame = document.createElement("textarea");
viewFrame.id = "tasselJSONView";
viewFrame.disabled = true;
viewFrame.rows = 200;
viewFrame.value = JSON.stringify(JSON.parse(localStorage.getItem(document.getElementById("tasselResetSelect").value)), null, 2);
document.getElementById("tasselModalContent").appendChild(viewFrame);
}
});
grid3.appendChild(button2);
let button3 = document.createElement("button");
button3.innerHTML = "Edit";
button3.classList.add("tasselButton");
button3.addEventListener("click", function() {
if (this.classList.contains("tasselSaveButton")) {
let viewFrame = document.getElementById("tasselJSONView");
let editedData;
try {
editedData = JSON.parse(viewFrame.value);
viewFrame.disabled = true;
localStorage.setItem(document.getElementById("tasselResetSelect").value, JSON.stringify(editedData));
this.innerHTML = "Edit";
this.classList.remove("tasselSaveButton");
} catch {
if (confirm("Error: Data invalid. Revert changes?") === true) {
document.getElementById("tasselJSONViewButton").click();
}
}
} else {
document.getElementById("tasselJSONViewButton").click();
if (confirm("Warning: Editing any value might break parts of Tassel. Do you want to continue?") === true) {
let viewFrame = document.getElementById("tasselJSONView");
viewFrame.disabled = false;
this.innerHTML = "Save";
this.classList.add("tasselSaveButton");
}
}
});
grid3.appendChild(button3);
let button4 = document.createElement("button");
button4.classList.add("tasselButton");
button4.innerHTML = "export selected"
button4.addEventListener("click", function() {
let selected = document.getElementById("tasselResetSelect").value;
let data = JSON.parse(localStorage.getItem(selected));
let formated = {};
formated[selected] = data;
let d = new Date();
downloadObject_xcajbuzn(JSON.stringify(formated), `tassel_export_${selected}_${d.getDate()}-${d.getMonth()}-${d.getFullYear()}_${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}.json`);
});
grid3.appendChild(button4);
let button5 = document.createElement("button");
button5.classList.add("tasselButton");
button5.innerHTML = "export all"
button5.addEventListener("click", function() {
let options = Object.values(document.getElementById("tasselResetSelect").options);
let formated = {};
for (let option of options) {
let data = JSON.parse(localStorage.getItem(option.value));
formated[option.value] = data;
}
let d = new Date();
downloadObject_xcajbuzn(JSON.stringify(formated), `tassel_export_${d.getDate()}-${d.getMonth()}-${d.getFullYear()}_${d.getHours()}-${d.getMinutes()}-${d.getSeconds()}.json`);
});
grid3.appendChild(button5);
let button6 = document.createElement("input");
button6.id = "tasselSettingsImport";
button6.classList.add("hidden");
button6.innerHTML = "import"
button6.setAttribute("type", "file");
button6.setAttribute("accept", ".json, .txt");
button6.addEventListener("change", function(e) {
let file = e.target.files[0];
if (!file) return;
readFile_xcajbuzn(file);
});
grid3.appendChild(button6);
let label6 = document.createElement("label");
label6.style = "text-align: center;align-content: center;cursor: pointer;";
label6.classList.add("tasselButton");
label6.innerHTML = "import";
label6.setAttribute("for", "tasselSettingsImport");
label6.addEventListener("dragenter", function(e) {
this.classList.add("dragenter");
});
label6.addEventListener("dragleave", function() {
this.classList.remove("dragenter");
});
label6.addEventListener("dragover", function(e) {
e.preventDefault();
});
label6.addEventListener("drop", function(e) {
e.preventDefault();
this.classList.remove("dragenter");
//source: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop#process_the_drop
if (e.dataTransfer.items) {
[...e.dataTransfer.items].forEach((item, i) => {
if (item.kind !== "file") return;
const file = item.getAsFile();
readFile_xcajbuzn(file);
});
} else {
[...e.dataTransfer.files].forEach((file, i) => {
readFile_xcajbuzn(file);
});
}
});
grid3.appendChild(label6);
content.appendChild(grid3);
}
function getSidebarElement_xcajbuzn(href, collapsed) {
let sidebar = Object.values(document.getElementById("expanded-bar-container").getElementsByTagName("a"));
if (collapsed) sidebar = Object.values(document.getElementsByClassName("sidebar-collapsed")[1].children);
for (let child of sidebar) {
if (child.href !== href) continue;
return child;
}
return null;
}
/* Read a JSON file and save it's data as localStorage */
//source: https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText#javascript
function readFile_xcajbuzn(file) {
const reader = new FileReader();
reader.addEventListener("load", () => {
let data = {};
try {
data = JSON.parse(JSON.parse(reader.result));
} catch {
alert("Error: Data invalid");
return;
}
let options = Object.values(document.getElementById("tasselResetSelect").options);
for (let option of options) {
if (!data[option.value]) continue;
localStorage.setItem(option.value, JSON.stringify(data[option.value]));
}
location.reload();
},false,);
if (file) reader.readAsText(file);
}
/* Download JSON as a file */
/* https://stackoverflow.com/a/47821215 */
function downloadObject_xcajbuzn(object, filename) {
var blob = new Blob([JSON.stringify(object)], {type: "application/json;charset=utf-8"});
var url = URL.createObjectURL(blob);
var elem = document.createElement("a");
elem.href = url;
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
/* Activate / deactivate extensions */
function toggleExtension_xcajbuzn(id) {
let index = -1;
let entry = settings2.extensions.find(function(item, index_) {
if (item.id === id*1) {
index = index_;
return true;
}
});
if (index === -1) {//activate
settings2.extensions.push({"id": id*1, "since": Date.now()});
} else {//deactivate
settings2.extensions.splice(index, 1);
}
saveSettings_xcajbuzn();
}
/* Save list of active extensions to local storage */
function saveSettings_xcajbuzn() {
let file = JSON.parse(localStorage.getItem("tasselSettings2") || "{}");
file.tassel = settings2;
localStorage.setItem("tasselSettings2", JSON.stringify(file));
}
function addBottomPermalink_xcajbuzn() {
if (tasselJsonManager.feed.type === 'drafts') return;
if (tasselJsonManager.feed.type === 'queue') return;
if (tasselJsonManager.feed.type === 'schedule') return;
let links = Object.values(document.getElementsByClassName("link_post"));
links.forEach(function(item) {
let post = item;
for (let a = 0; a < 100 && !post.classList.contains("post-container"); a++) {
post = post.parentNode;
}
let nav = post.getElementsByClassName("post-nav-left")[0];
if (nav.classList.contains("tasselPermalinked")) return;
nav.classList.add("tasselPermalinked");
let link = item.cloneNode(true);
link.classList.add("tasselPermalinked");
link.style = "margin: 0 0 0 20px;";
nav.appendChild(link);
});
}
function setPrivacySettings_xcajbuzn() {
if (!document.getElementById("privacy")) return;
//init settings
if (!settings2.postSettings) settings2.postSettings = {};
//add events to save changes
document.getElementById("privacy").addEventListener("change", function() {
settings2.postSettings.viewable = this.selectedIndex;
saveSettings_xcajbuzn();
});
document.getElementsByClassName("privacy-post")[0].getElementsByTagName("input").rebloggable.parentNode.addEventListener("click", function() {
settings2.postSettings.rebloggable = !this.firstChild.checked;
saveSettings_xcajbuzn();
});
document.getElementsByClassName("privacy-post")[0].getElementsByTagName("input").commentable.parentNode.addEventListener("click", function() {
settings2.postSettings.commentable = !this.firstChild.checked;
saveSettings_xcajbuzn();
});
document.getElementsByClassName("privacy-post")[0].getElementsByTagName("input").nsfw.parentNode.addEventListener("click", function() {
settings2.postSettings.nsfw = !this.firstChild.checked;
saveSettings_xcajbuzn();
});
//set settings
document.getElementById("privacy").selectedIndex = (settings2.postSettings.viewable < document.getElementById("privacy").length ? settings2.postSettings.viewable : 0) || 0;
if (settings2.postSettings.rebloggable === false) document.getElementsByClassName("toggle")[2].click();
if (settings2.postSettings.commentable === false) document.getElementsByClassName("toggle")[3].click();
if (settings2.postSettings.nsfw === true) document.getElementsByClassName("toggle")[4].click();
}
/* Create an icon with hover popup */
function createTooltip_xcajbuzn(content) {
let icon = document.createElement("div");
icon.classList.add("tasselInfo");
icon.innerHTML = `
<div class='tasselTooltip'>
<div class='tasselTooltipBubble'>
${content}
</div>
</div>
`;
return icon;
}
/* Create an HTML element of a checkbox with lable */
function createSwitch_xcajbuzn(title="", state="", _class=Math.random()) {
let id = "tasselSwitch" + Math.random();
let toggle = document.createElement("div");
toggle.classList.add("tasselToggle");
toggle.innerHTML = `
<input id="${id}" type="checkbox" class="${_class}" ${state}>
<label for="${id}">${title}</label>
`;
return toggle;
}
/* Create an HTML element of a numeric input field with a label */
function createNumericInput_xcajbuzn(title="", value=0, _class=Math.random()) {
let id = "tasselNumeric" + Math.random();
let frame = document.createElement("div");
frame.classList.add("tasselNumeric");
frame.innerHTML = `
<input id="${id}" type="number" class="${_class}" value="${value}">
<label for="${id}">${title}</label>
`;
return frame;
}
})();