Furaffinity-Custom-Settings

Helper Script to create Custom settings on Furaffinitiy

À partir de 2023-09-12. Voir la dernière version.

Ce script ne devrait pas être installé directement. C'est une librairie créée pour d'autres scripts. Elle doit être inclus avec la commande // @require https://update.greatest.deepsurf.us/scripts/475041/1249303/Furaffinity-Custom-Settings.js

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name        Furaffinity-Custom-Settings
// @namespace   Violentmonkey Scripts
// @grant       none
// @version     4.0.6
// @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://greatest.deepsurf.us/de/scripts/475041-furaffinity-custom-settings
// @supportURL  https://greatest.deepsurf.us/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) => {
        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: "Number",
    Boolean: "Boolean",
    Action: "Action",
    Text: "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;
  }
})();