- // ==UserScript==
- // @name Furaffinity-Custom-Settings
- // @namespace Violentmonkey Scripts
- // @grant none
- // @version 4.0.5
- // @author Midori Dragon
- // @description Helper Script to create Custom settings on Furaffinitiy
- // @icon https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png?v2
- // @homepageURL https://gf.qytechs.cn/de/scripts/475041-furaffinity-custom-settings
- // @supportURL https://gf.qytechs.cn/de/scripts/475041-furaffinity-custom-settings/feedback
- // @license MIT
- // ==/UserScript==
-
- // jshint esversion: 8
-
- (() => {
- window.Settings = class Settings {
- constructor() {
- this._name = "Extension Settings";
- this._nameId = makeIdCompatible(this._name);
- this._provider = "Custom Furaffinity Settings";
- this._providerId = makeIdCompatible(this._provider);
- this.headerName = "Extension Settings";
- this.settings = [];
- }
-
- set name(value) {
- this._name = value;
- this._nameId = makeIdCompatible(value);
- }
- get name() {
- return this._name;
- }
- get nameId() {
- return this._nameId;
- }
-
- set provider(value) {
- this._provider = value;
- this._providerId = makeIdCompatible(value);
- }
- get provider() {
- return this._provider;
- }
- get providerId() {
- return this._providerId;
- }
-
- newSetting(name, description, type, typeDescription, defaultValue, action) {
- const setting = new Setting(name, description, type, typeDescription, defaultValue, action);
- setting.id = this._providerId + "_" + makeIdCompatible(setting.name);
- setting.document = createSetting(setting, (target) => {
- console.log(setting.type);
- console.log(setting.type == SettingsTypes.Boolean);
- let value;
- switch (setting.type) {
- case SettingTypes.Number:
- value = +target.value;
- if (value == setting.defaultValue) localStorage.removeItem(setting.id);
- else localStorage.setItem(setting.id, value);
- break;
- case SettingTypes.Text:
- value = target.value;
- if (value == setting.defaultValue) localStorage.removeItem(setting.id);
- else localStorage.setItem(setting.id, value);
- break;
- case SettingTypes.Boolean:
- value = target.checked;
- if (value == setting.defaultValue) localStorage.removeItem(setting.id);
- else localStorage.setItem(setting.id, value);
- break;
- }
- if (setting.action) setting.action(target);
- });
- this.settings.push(setting);
- return setting;
- }
-
- async loadSettings() {
- try {
- addExSettings(this._name, this._provider, this._nameId, this._providerId);
- if (window.location.toString().includes("controls/settings")) {
- addExSettingsSidebar(this._name, this._provider, this._nameId, this._providerId);
- if (window.location.toString().includes("?extension=" + this._providerId)) loadSettings(this.headerName, this.settings);
- }
- } catch (e) {
- console.error(e);
- }
- }
-
- toString() {
- if (!this.settings || this.settings.length === 0) return "";
- let settingsString = "(";
- for (const setting of this.settings) {
- if (setting.type !== SettingTypes.Action) settingsString += `"${setting.toString()}", `;
- }
- settingsString = settingsString.slice(0, -2);
- settingsString += ")";
- return settingsString;
- }
- };
-
- let localSettingsCreated = false;
- window.CustomSettings = new Settings();
- window.SettingTypes = Object.freeze({
- Number: Symbol("Number"),
- Boolean: Symbol("Boolean"),
- Action: Symbol("Action"),
- Text: Symbol("Text"),
- });
- //#endregion
-
- class Setting {
- constructor(name, description, type, typeDescription, defaultValue, action) {
- this._id;
- this.name = name;
- this.description = description;
- this.type = type;
- this.typeDescription = typeDescription;
- this.defaultValue = defaultValue;
- this.action = action;
- this._idFirstSet = true;
- }
-
- set id(newValue) {
- if (this._idFirstSet) {
- this._id = newValue;
- this._idFirstSet = false;
- } else throw new Error("Can't set Id of a Setting that was already been set.");
- }
- get id() {
- return this._id;
- }
-
- set value(newValue) {
- if (newValue == this.defaultValue) localStorage.removeItem(this._id);
- else localStorage.setItem(this._id, newValue);
- const elem = document.getElementById(this._id);
- if (elem) {
- switch (this.type) {
- case SettingTypes.Number:
- case SettingTypes.Text:
- elem.value = newValue;
- break;
- case SettingTypes.Boolean:
- elem.checked = newValue;
- break;
- }
- }
- }
- get value() {
- const newValue = localStorage.getItem(this._id);
- if (newValue == null || newValue == undefined) return this.defaultValue;
- return convertStringToValue(newValue);
- }
-
- toString() {
- return `${this.name} = ${this.value}`;
- }
- }
-
- const FuraffinitySettingsSettings = new Settings();
- FuraffinitySettingsSettings.name = "Extension Settings";
- FuraffinitySettingsSettings.provider = "Custom-Furaffinity-Settings";
- FuraffinitySettingsSettings.headerName = "Global Custom-Furaffinity-Settings";
- const showResetButtonSetting = FuraffinitySettingsSettings.newSetting("Show Reset Button", 'Set wether the "Reset this Setting" button is shown in other Settings.', SettingTypes.Boolean, "Show Reset Button", true);
- localSettingsCreated = true;
- FuraffinitySettingsSettings.loadSettings();
-
- async function addExSettings(name, provider, nameId, providerId) {
- const settings = document.querySelector('ul[class="navhideonmobile"]').querySelector('a[href="/controls/settings/"]').parentNode;
-
- if (!document.getElementById(nameId)) {
- const exSettingsHeader = document.createElement("h3");
- exSettingsHeader.id = nameId;
- exSettingsHeader.textContent = name;
- settings.appendChild(exSettingsHeader);
- }
-
- if (!document.getElementById(providerId)) {
- const currExSettings = document.createElement("a");
- currExSettings.id = providerId;
- currExSettings.textContent = provider;
- currExSettings.href = "/controls/settings?extension=" + providerId;
- currExSettings.style.cursor = "pointer";
- settings.appendChild(currExSettings);
- }
- }
-
- async function addExSettingsSidebar(name, provider, nameId, providerId) {
- const settings = document.getElementById("controlpanelnav");
-
- if (!document.getElementById(nameId + "_side")) {
- const exSettingsHeader = document.createElement("h3");
- exSettingsHeader.id = nameId + "_side";
- exSettingsHeader.textContent = name;
- settings.appendChild(exSettingsHeader);
- }
-
- if (!document.getElementById(providerId + "_side")) {
- const currExSettings = document.createElement("a");
- currExSettings.id = providerId + "_side";
- currExSettings.textContent = provider;
- currExSettings.href = "/controls/settings?extension=" + providerId;
- currExSettings.style.cursor = "pointer";
- settings.appendChild(currExSettings);
- }
- }
-
- async function loadSettings(headerName, settings) {
- if (!settings || settings.length === 0) return;
- if (document.getElementById(headerName + "_settingscontainer")) return;
-
- const columnPage = document.getElementById("columnpage");
- const content = columnPage.querySelector('div[class="content"]');
-
- for (const section of content.querySelectorAll('section:not([class="exsettings"])')) {
- section.parentNode.removeChild(section);
- }
-
- const section = document.createElement("section");
- section.id = headerName + "_settingscontainer";
- section.className = "exsettings";
- const headerContainer = document.createElement("div");
- headerContainer.className = "section-header";
- const header = document.createElement("h2");
- header.textContent = headerName;
- headerContainer.appendChild(header);
- section.appendChild(headerContainer);
- const bodyContainer = document.createElement("div");
- bodyContainer.className = "section-body";
-
- for (const setting of settings) {
- const settingElem = setting.document.querySelector(`[id="${setting.id}"]`);
- switch (setting.type) {
- case SettingTypes.Number:
- settingElem.value = setting.value;
- break;
- case SettingTypes.Text:
- settingElem.value = setting.value;
- break;
- case SettingTypes.Boolean:
- settingElem.checked = setting.value;
- break;
- }
- bodyContainer.appendChild(setting.document);
- }
-
- section.appendChild(bodyContainer);
- content.appendChild(section);
- }
-
- function createSetting(setting, action) {
- const settingContainer = document.createElement("div");
- settingContainer.className = "control-panel-item-container";
-
- const settingName = document.createElement("div");
- settingName.className = "control-panel-item-name";
- const settingNameText = document.createElement("h4");
- settingNameText.textContent = setting.name;
- settingName.appendChild(settingNameText);
- settingContainer.appendChild(settingName);
-
- const settingDesc = document.createElement("div");
- settingDesc.className = "control-panel-item-description";
- const settingDescText = document.createTextNode(setting.description);
- settingDesc.appendChild(settingDescText);
- settingContainer.appendChild(settingDesc);
-
- if (localSettingsCreated && showResetButtonSetting.value) {
- settingDesc.appendChild(document.createElement("br"));
- settingDesc.appendChild(createSettingReset(setting));
- }
-
- const settingOption = document.createElement("div");
- settingOption.className = "control-panel-item-options";
-
- switch (setting.type) {
- case SettingTypes.Number:
- settingOption.appendChild(createSettingNumber(setting.id, action));
- break;
- case SettingTypes.Boolean:
- settingOption.appendChild(createSettingBoolean(setting.id, setting.typeDescription, action));
- break;
- case SettingTypes.Action:
- settingOption.appendChild(createSettingAction(setting.id, setting.typeDescription, action));
- break;
- case SettingTypes.Text:
- settingOption.appendChild(createSettingText(setting.id, action));
- break;
- }
-
- settingContainer.appendChild(settingOption);
- return settingContainer;
- }
-
- function createSettingReset(setting) {
- const settingDescReset = document.createElement("a");
- settingDescReset.id = setting.id + "_settingreset";
- settingDescReset.textContent = "Reset this Setting";
- settingDescReset.style.cursor = "pointer";
- settingDescReset.style.color = "aqua";
- settingDescReset.style.textDecoration = "underline";
- settingDescReset.style.fontStyle = "italic";
- settingDescReset.style.fontSize = "14px";
- settingDescReset.onclick = () => {
- const userConfirmed = window.confirm(`Are you sure you want to Reset the "${setting.name}" Setting to its default value?`);
- if (userConfirmed) setting.value = setting.defaultValue;
- };
- return settingDescReset;
- }
-
- function createSettingNumber(id, action) {
- const settingElem = document.createElement("input");
- settingElem.id = id;
- settingElem.type = "text";
- settingElem.className = "textbox";
- settingElem.addEventListener("keydown", (event) => {
- const currentValue = parseInt(settingElem.value) || 0;
- if (event.key === "ArrowUp") {
- settingElem.value = (currentValue + 1).toString();
- action(settingElem);
- } else if (event.key === "ArrowDown") {
- if (currentValue != 0) settingElem.value = (currentValue - 1).toString();
- action(settingElem);
- }
- });
- settingElem.addEventListener("input", () => {
- settingElem.value = settingElem.value.replace(/[^0-9]/g, "");
- if (settingElem.value < 0) settingElem.value = 0;
- });
- settingElem.addEventListener("input", () => action(settingElem));
- return settingElem;
- }
-
- function createSettingText(id, action) {
- const settingElem = document.createElement("input");
- settingElem.id = id;
- settingElem.type = "text";
- settingElem.className = "textbox";
- settingElem.addEventListener("keydown", (event) => action(settingElem));
- settingElem.addEventListener("input", () => action(settingElem));
- return settingElem;
- }
-
- function createSettingBoolean(id, typeDescription, action) {
- const container = document.createElement("div");
- const settingElem = document.createElement("input");
- settingElem.id = id;
- settingElem.type = "checkbox";
- settingElem.style.cursor = "pointer";
- settingElem.style.marginRight = "4px";
- settingElem.addEventListener("change", () => action(settingElem));
- container.appendChild(settingElem);
- const settingElemLabel = document.createElement("label");
- settingElemLabel.textContent = typeDescription;
- settingElemLabel.style.cursor = "pointer";
- settingElemLabel.style.userSelect = "none";
- settingElemLabel.addEventListener("click", () => {
- settingElem.checked = !settingElem.checked;
- action(settingElem);
- });
- container.appendChild(settingElemLabel);
- return container;
- }
-
- function createSettingAction(id, typeDescription, action) {
- const settingElem = document.createElement("button");
- settingElem.id = id;
- settingElem.type = "button";
- settingElem.className = "button standard mobile-fix";
- settingElem.textContent = typeDescription;
- settingElem.addEventListener("click", () => action(settingElem));
- return settingElem;
- }
-
- function makeIdCompatible(inputString) {
- const sanitizedString = inputString
- .replace(/[^a-zA-Z0-9-_\.]/g, "-")
- .replace(/^-+|-+$/g, "")
- .replace(/^-*(?=\d)/, "id-");
- return /^[0-9]/.test(sanitizedString) ? "id-" + sanitizedString : sanitizedString;
- }
-
- function convertStringToValue(value) {
- if (value === "true" || value === "false") {
- return value === "true";
- }
-
- const parsedNumber = parseFloat(value);
- if (!isNaN(parsedNumber)) {
- return parsedNumber;
- }
- return value;
- }
- })();