// ==UserScript==
// @name F95 Latest Update Saver
// @namespace 1330126-edexal
// @match *://f95zone.to/sam/latest_alpha/*
// @grant GM.setValue
// @grant GM.getValue
// @grant GM.deleteValue
// @grant GM.listValues
// @icon https://external-content.duckduckgo.com/ip3/f95zone.to.ico
// @license Unlicense
// @version 2.0
// @author Edexal
// @description Save your filters from the Latest Update Page & load them when you need it!
// ==/UserScript==
(async function () {
/*NOTE: F95 uses FontAwesome v5.15.4*/
const storageKeys = {
A: "Slot A",
B: "Slot B",
C: "Slot C",
D: "Slot D",
LastSlot: "LastSlot",
CanAutoLoad: "CanAutoLoad"
};
//Apply custom styles in a style tag
function applyCSS(css) {
let styleEl = document.querySelector("style");
if (styleEl === null) {
styleEl = document.createElement('style');
}
styleEl.appendChild(document.createTextNode(css));
document.head.appendChild(styleEl);
}
function getStyles() {
return `
@keyframes notice {
0% {opacity:0;}
30% {opacity:1;}
60% {opacity:1;}
100% {opacity:0;};
}
#save-notice {
position: fixed;
z-index: 8;
top: 33%;
left: 40vw;
background-color:#2d2d2d;
color: yellow;
border-radius: 10px;
border: 2pt outset #6ce65b;
box-shadow: -1px 0px 5px #cece92;
width: 120px;
padding-top:15px;
padding-bottom:15px;
font-size: 18px;
font-weight:bold;
text-align:center;
opacity: 0;
}
.save-anim {
animation: 3s notice ease-in-out;
}
.save-bg {
opacity: 0.6;
}
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotA a::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotB a::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotC a::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotD a::before
{
color: orange !important;
}
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotA a:not(.filter-selected).has-save::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotB a:not(.filter-selected).has-save::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotC a:not(.filter-selected).has-save::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotD a:not(.filter-selected).has-save::before {
color: #8cf048 !important;
}
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotA a.filter-selected::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotB a.filter-selected::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotC a.filter-selected::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_SlotD a.filter-selected::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_Load a::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_Save a::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_Delete a::before,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_Auto-Load a::before{
color: yellow !important;
}
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings a.filter-selected::before,
#btn-settings_Delete a:active,
#btn-settings_Load a:active,
#btn-settings_Save a:active,
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings a.auto-selected::before
{
background-color: #641c1c !important;
}
#btn-settings_Delete a:active,
#btn-settings_Load a:active,
#btn-settings_Save a:active {
opacity: 0.6;
}
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_Save a {
border-right: 2px solid #ffe722;
}
#latest-page_filter-wrap #latest-page_filter-wrap_inner #filter-block_settings #btn-settings_Auto-Load a {
border-left: 2px solid #ffe722;
}
div#filter-block_settings h4 {
color: #fc9b46 !important;
}
`;
}
function addSaveNotice() {
let notice = document.createElement('div');
notice.id = 'save-notice';
let txtNode = document.createTextNode('Saved!');
notice.append(txtNode);
document.body.append(notice);
applyCSS(getStyles());
}
function createSection() {
let section = document.createElement('div');
section.id = 'filter-block_settings';
section.classList.add('filter-block');
return section;
}
function createHeader() {
let h = document.createElement('h4');
h.classList.add('filter-block_title');
let txtNode = document.createTextNode('Settings');
h.append(txtNode);
return h;
}
function createSectWrap() {
let outerContainer = document.createElement('div');
outerContainer.classList.add('filter-block_content', 'filter-block_h');
return outerContainer;
}
//Utility function for creating buttons
function createBtn(name, eventFunc, classNames) {
let innerContainer = document.createElement('div');
innerContainer.id = `btn-settings_${name.replace(/ /, "")}`;
innerContainer.classList.add('filter-block_button-wrap');
let a = document.createElement('a');
a.href = "#";
a.setAttribute('data-settings', name);
a.classList.add('filter-block_button');
if (!!classNames) {
a.classList.add(...classNames);
}
a.addEventListener('click', eventFunc);
let label = document.createElement('div');
label.classList.add('filter-block_button-label');
let labelTxtNode = document.createTextNode(`${name[0].toUpperCase()}${name.substring(1)}`);
label.append(labelTxtNode);
innerContainer.append(a, label);
return innerContainer;
}
function getSelectedSlot() {
return document.querySelector("#filter-block_settings a.filter-selected");
}
function getSlotByName(name) {
return document.querySelector(`[data-settings="${name}"]`);
}
function addHasSaveStyle(slot) {
slot.classList.add('has-save');
}
function addAutoLoadStyle() {
const autoBtn = document.querySelector('#btn-settings_Auto-Load a');
autoBtn.classList.add('auto-selected');
}
function removeHasSaveStyle(slot) {
slot.classList.remove('has-save');
}
function removeAutoLoadStyle() {
const autoBtn = document.querySelector('#btn-settings_Auto-Load a');
autoBtn.classList.remove('auto-selected');
}
function saveEvent(e) {
e.preventDefault();
let chosenSlot = getSelectedSlot();
if (!!!chosenSlot) {
return;
}
GM.setValue(chosenSlot.dataset.settings, location.href);
addHasSaveStyle(chosenSlot);
let saveNotice = document.querySelector('#save-notice');
let saveNoticeBG = document.querySelector('#top');
if (!!!saveNotice.classList.contains('save-anim')) {
saveNotice.classList.add('save-anim');
saveNoticeBG.classList.add('save-bg');
setTimeout(() => {
saveNotice.classList.remove('save-anim');
saveNoticeBG.classList.remove('save-bg');
}, 3000)
}
}
async function loadAsyncEvent(e) {
e.preventDefault();
try {
let chosenSlot = getSelectedSlot();
if (!!chosenSlot) {
let url = await GM.getValue(chosenSlot.dataset.settings);
if (!!url) {
location.href = url;
}
}
} catch (err) {
console.error(err);
}
}
function slotSelectEvent(e) {
e.preventDefault();
const classAtrribute = 'filter-selected';
const currentSlot = getSelectedSlot();
if (!!currentSlot && currentSlot.dataset.settings !== e.target.dataset.settings) {
currentSlot.classList.remove(classAtrribute);
}
e.target.classList.add(classAtrribute);
GM.setValue(storageKeys.LastSlot, e.target.dataset.settings);
}
function deleteEvent(e) {
e.preventDefault();
const slot = getSelectedSlot();
if (!!slot) {
GM.deleteValue(slot.dataset.settings);
removeHasSaveStyle(slot);
}
}
async function autoLoadEvent(e) {
e.preventDefault();
const canAutoLoad = await GM.getValue(storageKeys.CanAutoLoad);
GM.setValue(storageKeys.CanAutoLoad, !!!canAutoLoad);
if (canAutoLoad) {
removeAutoLoadStyle();
} else {
addAutoLoadStyle();
}
}
function createBtns(names, eventFunc, classNames) {
let buttons = [];
for (let i = 0; i < names.length; i++) {
buttons.push(createBtn(names[i], eventFunc, classNames));
}
return buttons;
}
async function initHasSave() {
let saveSlotNames = await GM.listValues();
const excludeList = Object.keys(storageKeys).slice(4);
saveSlotNames = saveSlotNames.filter(name => !excludeList.includes(name));
for (const slotName of saveSlotNames) {
let saveSlot = getSlotByName(slotName);
addHasSaveStyle(saveSlot);
}
}
async function initAutoLoad() {
const canAutoLoad = await GM.getValue(storageKeys.CanAutoLoad);
if (!!!canAutoLoad) {
return;
}
const lastSlotName = await GM.getValue(storageKeys.LastSlot);
if (!!lastSlotName) {
const slot = getSlotByName(lastSlotName);
slot.click();
const loadBtn = document.querySelector('#btn-settings_Load a');
loadBtn.click();
}
addAutoLoadStyle();
}
function getSettingsSect() {
let settingsSect = createSection();
let header = createHeader();
let contentWrap = createSectWrap();
let btnList = [createBtn('Load', loadAsyncEvent, ['fas', 'fa-upload']),
createBtn('Save', saveEvent, ['fas', 'fa-save'])];
btnList.push(...createBtns([storageKeys.A, storageKeys.B, storageKeys.C, storageKeys.D], slotSelectEvent, ['fas', 'fa-hdd']));
btnList.push(createBtn('Auto-Load', autoLoadEvent, ['fas', 'fa-magic']));
btnList.push(createBtn('Delete', deleteEvent, ['fas', 'fa-trash-alt']));
contentWrap.append(...btnList);
settingsSect.append(header, contentWrap);
return settingsSect;
}
function addSettingsSect() {
let titleEl = document.querySelector('.content-block_filter-title');
titleEl.after(getSettingsSect());
addSaveNotice();
}
addSettingsSect();
initHasSave();
initAutoLoad();
})().catch(err => console.error(err));