Furaffinity-Custom-Settings

Library to create Custom settings on Furaffinitiy

目前為 2025-02-02 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/475041/1530885/Furaffinity-Custom-Settings.js

  1. // ==UserScript==
  2. // @name Furaffinity-Custom-Settings
  3. // @namespace Violentmonkey Scripts
  4. // @grant none
  5. // @version 4.1.0
  6. // @author Midori Dragon
  7. // @description Library to create Custom settings on Furaffinitiy
  8. // @icon https://www.furaffinity.net/themes/beta/img/banners/fa_logo.png
  9. // @license MIT
  10. // ==/UserScript==
  11. // jshint esversion: 8
  12. (() => {
  13. "use strict";
  14. var __webpack_modules__ = {
  15. 950: (module, __webpack_exports__, __webpack_require__) => {
  16. __webpack_require__.d(__webpack_exports__, {
  17. A: () => __WEBPACK_DEFAULT_EXPORT__
  18. });
  19. var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(601), _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__), _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(314), ___CSS_LOADER_EXPORT___ = __webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__)()(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default());
  20. ___CSS_LOADER_EXPORT___.push([ module.id, '.switch {\n position: relative;\n display: inline-block;\n width: 52px;\n height: 28px;\n margin: 6px 8px 6px 0;\n}\n\n.switch input {\n opacity: 0;\n width: 0;\n height: 0;\n}\n\n.slider {\n position: absolute;\n cursor: pointer;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: #ccc;\n transition: .4s;\n border-radius: 34px;\n}\n\n.slider:before {\n position: absolute;\n content: "";\n height: 20px;\n width: 20px;\n left: 4px;\n bottom: 4px;\n background-color: white;\n transition: .4s;\n border-radius: 50%;\n}\n\ninput:checked+.slider {\n background-color: #4CAF50;\n}\n\ninput:checked+.slider:before {\n transform: translateX(26px);\n}\n\n.section-header {\n display: flex;\n align-items: center;\n}\n\n.section-body {\n opacity: 1;\n transition: opacity 0.3s linear;\n}\n\n.section-body.collapsed {\n opacity: 0.4;\n pointer-events: none;\n}\n', "" ]);
  21. const __WEBPACK_DEFAULT_EXPORT__ = ___CSS_LOADER_EXPORT___;
  22. },
  23. 314: module => {
  24. module.exports = function(cssWithMappingToString) {
  25. var list = [];
  26. list.toString = function toString() {
  27. return this.map((function(item) {
  28. var content = "", needLayer = void 0 !== item[5];
  29. if (item[4]) content += "@supports (".concat(item[4], ") {");
  30. if (item[2]) content += "@media ".concat(item[2], " {");
  31. if (needLayer) content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
  32. content += cssWithMappingToString(item);
  33. if (needLayer) content += "}";
  34. if (item[2]) content += "}";
  35. if (item[4]) content += "}";
  36. return content;
  37. })).join("");
  38. };
  39. list.i = function i(modules, media, dedupe, supports, layer) {
  40. if ("string" == typeof modules) modules = [ [ null, modules, void 0 ] ];
  41. var alreadyImportedModules = {};
  42. if (dedupe) for (var k = 0; k < this.length; k++) {
  43. var id = this[k][0];
  44. if (null != id) alreadyImportedModules[id] = true;
  45. }
  46. for (var _k = 0; _k < modules.length; _k++) {
  47. var item = [].concat(modules[_k]);
  48. if (!dedupe || !alreadyImportedModules[item[0]]) {
  49. if (void 0 !== layer) if (void 0 === item[5]) item[5] = layer; else {
  50. item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
  51. item[5] = layer;
  52. }
  53. if (media) if (!item[2]) item[2] = media; else {
  54. item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
  55. item[2] = media;
  56. }
  57. if (supports) if (!item[4]) item[4] = "".concat(supports); else {
  58. item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
  59. item[4] = supports;
  60. }
  61. list.push(item);
  62. }
  63. }
  64. };
  65. return list;
  66. };
  67. },
  68. 601: module => {
  69. module.exports = function(i) {
  70. return i[1];
  71. };
  72. },
  73. 72: module => {
  74. var stylesInDOM = [];
  75. function getIndexByIdentifier(identifier) {
  76. for (var result = -1, i = 0; i < stylesInDOM.length; i++) if (stylesInDOM[i].identifier === identifier) {
  77. result = i;
  78. break;
  79. }
  80. return result;
  81. }
  82. function modulesToDom(list, options) {
  83. for (var idCountMap = {}, identifiers = [], i = 0; i < list.length; i++) {
  84. var item = list[i], id = options.base ? item[0] + options.base : item[0], count = idCountMap[id] || 0, identifier = "".concat(id, " ").concat(count);
  85. idCountMap[id] = count + 1;
  86. var indexByIdentifier = getIndexByIdentifier(identifier), obj = {
  87. css: item[1],
  88. media: item[2],
  89. sourceMap: item[3],
  90. supports: item[4],
  91. layer: item[5]
  92. };
  93. if (-1 !== indexByIdentifier) {
  94. stylesInDOM[indexByIdentifier].references++;
  95. stylesInDOM[indexByIdentifier].updater(obj);
  96. } else {
  97. var updater = addElementStyle(obj, options);
  98. options.byIndex = i;
  99. stylesInDOM.splice(i, 0, {
  100. identifier,
  101. updater,
  102. references: 1
  103. });
  104. }
  105. identifiers.push(identifier);
  106. }
  107. return identifiers;
  108. }
  109. function addElementStyle(obj, options) {
  110. var api = options.domAPI(options);
  111. api.update(obj);
  112. return function updater(newObj) {
  113. if (newObj) {
  114. if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) return;
  115. api.update(obj = newObj);
  116. } else api.remove();
  117. };
  118. }
  119. module.exports = function(list, options) {
  120. var lastIdentifiers = modulesToDom(list = list || [], options = options || {});
  121. return function update(newList) {
  122. newList = newList || [];
  123. for (var i = 0; i < lastIdentifiers.length; i++) {
  124. var index = getIndexByIdentifier(lastIdentifiers[i]);
  125. stylesInDOM[index].references--;
  126. }
  127. for (var newLastIdentifiers = modulesToDom(newList, options), _i = 0; _i < lastIdentifiers.length; _i++) {
  128. var _index = getIndexByIdentifier(lastIdentifiers[_i]);
  129. if (0 === stylesInDOM[_index].references) {
  130. stylesInDOM[_index].updater();
  131. stylesInDOM.splice(_index, 1);
  132. }
  133. }
  134. lastIdentifiers = newLastIdentifiers;
  135. };
  136. };
  137. },
  138. 659: module => {
  139. var memo = {};
  140. module.exports = function insertBySelector(insert, style) {
  141. var target = function getTarget(target) {
  142. if (void 0 === memo[target]) {
  143. var styleTarget = document.querySelector(target);
  144. if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) try {
  145. styleTarget = styleTarget.contentDocument.head;
  146. } catch (e) {
  147. styleTarget = null;
  148. }
  149. memo[target] = styleTarget;
  150. }
  151. return memo[target];
  152. }(insert);
  153. if (!target) throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
  154. target.appendChild(style);
  155. };
  156. },
  157. 540: module => {
  158. module.exports = function insertStyleElement(options) {
  159. var element = document.createElement("style");
  160. options.setAttributes(element, options.attributes);
  161. options.insert(element, options.options);
  162. return element;
  163. };
  164. },
  165. 56: (module, __unused_webpack_exports, __webpack_require__) => {
  166. module.exports = function setAttributesWithoutAttributes(styleElement) {
  167. var nonce = true ? __webpack_require__.nc : 0;
  168. if (nonce) styleElement.setAttribute("nonce", nonce);
  169. };
  170. },
  171. 825: module => {
  172. module.exports = function domAPI(options) {
  173. if ("undefined" == typeof document) return {
  174. update: function update() {},
  175. remove: function remove() {}
  176. };
  177. var styleElement = options.insertStyleElement(options);
  178. return {
  179. update: function update(obj) {
  180. !function apply(styleElement, options, obj) {
  181. var css = "";
  182. if (obj.supports) css += "@supports (".concat(obj.supports, ") {");
  183. if (obj.media) css += "@media ".concat(obj.media, " {");
  184. var needLayer = void 0 !== obj.layer;
  185. if (needLayer) css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
  186. css += obj.css;
  187. if (needLayer) css += "}";
  188. if (obj.media) css += "}";
  189. if (obj.supports) css += "}";
  190. var sourceMap = obj.sourceMap;
  191. if (sourceMap && "undefined" != typeof btoa) css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
  192. options.styleTagTransform(css, styleElement, options.options);
  193. }(styleElement, options, obj);
  194. },
  195. remove: function remove() {
  196. !function removeStyleElement(styleElement) {
  197. if (null === styleElement.parentNode) return false;
  198. styleElement.parentNode.removeChild(styleElement);
  199. }(styleElement);
  200. }
  201. };
  202. };
  203. },
  204. 113: module => {
  205. module.exports = function styleTagTransform(css, styleElement) {
  206. if (styleElement.styleSheet) styleElement.styleSheet.cssText = css; else {
  207. for (;styleElement.firstChild; ) styleElement.removeChild(styleElement.firstChild);
  208. styleElement.appendChild(document.createTextNode(css));
  209. }
  210. };
  211. }
  212. }, __webpack_module_cache__ = {};
  213. function __webpack_require__(moduleId) {
  214. var cachedModule = __webpack_module_cache__[moduleId];
  215. if (void 0 !== cachedModule) return cachedModule.exports;
  216. var module = __webpack_module_cache__[moduleId] = {
  217. id: moduleId,
  218. exports: {}
  219. };
  220. __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  221. return module.exports;
  222. }
  223. __webpack_require__.n = module => {
  224. var getter = module && module.__esModule ? () => module.default : () => module;
  225. __webpack_require__.d(getter, {
  226. a: getter
  227. });
  228. return getter;
  229. };
  230. __webpack_require__.d = (exports, definition) => {
  231. for (var key in definition) if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
  232. enumerable: true,
  233. get: definition[key]
  234. });
  235. };
  236. __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
  237. __webpack_require__.nc = void 0;
  238. var LogLevel, SettingType;
  239. __webpack_require__.d({}, {
  240. x: () => showResetButtonSetting
  241. });
  242. !function(LogLevel) {
  243. LogLevel[LogLevel.Error = 1] = "Error";
  244. LogLevel[LogLevel.Warning = 2] = "Warning";
  245. LogLevel[LogLevel.Info = 3] = "Info";
  246. }(LogLevel || (LogLevel = {}));
  247. class Logger {
  248. static log(logLevel = LogLevel.Warning, ...args) {
  249. if (null == window.__FF_GLOBAL_LOG_LEVEL__) window.__FF_GLOBAL_LOG_LEVEL__ = LogLevel.Error;
  250. if (!(logLevel > window.__FF_GLOBAL_LOG_LEVEL__)) switch (logLevel) {
  251. case LogLevel.Error:
  252. console.error(...args);
  253. break;
  254.  
  255. case LogLevel.Warning:
  256. console.warn(...args);
  257. break;
  258.  
  259. case LogLevel.Info:
  260. console.log(...args);
  261. }
  262. }
  263. static setLogLevel(logLevel) {
  264. window.__FF_GLOBAL_LOG_LEVEL__ = logLevel;
  265. }
  266. static logError(...args) {
  267. Logger.log(LogLevel.Error, ...args);
  268. }
  269. static logWarning(...args) {
  270. Logger.log(LogLevel.Warning, ...args);
  271. }
  272. static logInfo(...args) {
  273. Logger.log(LogLevel.Info, ...args);
  274. }
  275. }
  276. !function(SettingType) {
  277. SettingType[SettingType.Number = 0] = "Number";
  278. SettingType[SettingType.Boolean = 1] = "Boolean";
  279. SettingType[SettingType.Action = 2] = "Action";
  280. SettingType[SettingType.Text = 3] = "Text";
  281. }(SettingType || (SettingType = {}));
  282. function makeIdCompatible(id) {
  283. const sanitizedString = id.replace(/[^a-zA-Z0-9-_\.]/g, "-").replace(/^-+|-+$/g, "").replace(/^-*(?=\d)/, "id-");
  284. return /^[0-9]/.test(sanitizedString) ? "id-" + sanitizedString : sanitizedString;
  285. }
  286. class SettingAction extends EventTarget {
  287. constructor(providerId, name) {
  288. super();
  289. this.description = "";
  290. Object.setPrototypeOf(this, SettingAction.prototype);
  291. this.name = name;
  292. this.id = providerId + "-" + makeIdCompatible(this.name);
  293. this.type = SettingType.Action;
  294. this.defaultValue = "";
  295. this.loadFromSyncedStorage();
  296. this.settingElem = this._settingInputElem = this.create();
  297. }
  298. get value() {
  299. var _a;
  300. return null !== (_a = this._settingInputElem.textContent) && void 0 !== _a ? _a : "";
  301. }
  302. set value(newValue) {
  303. this._settingInputElem.textContent = newValue;
  304. }
  305. get onInput() {
  306. return this._onInput;
  307. }
  308. set onInput(handler) {
  309. this._onInput = handler;
  310. }
  311. create() {
  312. const settingElem = document.createElement("button");
  313. settingElem.id = this.id;
  314. settingElem.type = "button";
  315. settingElem.className = "button standard mobile-fix";
  316. settingElem.textContent = this.name;
  317. settingElem.addEventListener("click", this.invokeInput.bind(this));
  318. return settingElem;
  319. }
  320. loadFromSyncedStorage() {}
  321. toString() {
  322. return `${this.name} = ${this.value}`;
  323. }
  324. invokeInput() {
  325. var _a;
  326. null === (_a = this._onInput) || void 0 === _a || _a.call(this, this._settingInputElem);
  327. this.dispatchEvent(new Event("input"));
  328. }
  329. }
  330. class GMInfo {
  331. static isBrowserEnvironment() {
  332. return "undefined" != typeof browser && void 0 !== browser.runtime || "undefined" != typeof chrome && void 0 !== chrome.runtime;
  333. }
  334. static getBrowserAPI() {
  335. if ("undefined" != typeof GM_info && null != GM_info) return GM_info; else if ("undefined" != typeof browser && void 0 !== browser.runtime) return browser; else if ("undefined" != typeof chrome && void 0 !== chrome.runtime) return chrome; else throw new Error("Unsupported browser for SyncedStorage.");
  336. }
  337. static get scriptName() {
  338. if (GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().runtime.getManifest().name; else return GMInfo.getBrowserAPI().script.name;
  339. }
  340. static get scriptVersion() {
  341. if (GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().runtime.getManifest().version; else return GMInfo.getBrowserAPI().script.version;
  342. }
  343. static get scriptDescription() {
  344. if (GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().runtime.getManifest().description; else return GMInfo.getBrowserAPI().script.description;
  345. }
  346. static get scriptAuthor() {
  347. if (GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().runtime.getManifest().author; else return GMInfo.getBrowserAPI().script.author;
  348. }
  349. static get scriptNamespace() {
  350. if (!GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().script.namespace;
  351. }
  352. static get scriptSource() {
  353. if (!GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().script.source;
  354. }
  355. static get scriptIcon() {
  356. if (GMInfo.isBrowserEnvironment()) {
  357. const manifest = GMInfo.getBrowserAPI().runtime.getManifest();
  358. let largestIcon = 0;
  359. for (const key of Object.keys(manifest.icons)) {
  360. const size = parseInt(key);
  361. if (size > largestIcon) largestIcon = size;
  362. }
  363. return manifest.icons[largestIcon.toString()];
  364. } else return GMInfo.getBrowserAPI().script.icon;
  365. }
  366. static get scriptIcon64() {
  367. if (GMInfo.isBrowserEnvironment()) {
  368. const manifest = GMInfo.getBrowserAPI().runtime.getManifest();
  369. return null == manifest.icons ? void 0 : manifest.icons[64];
  370. } else return GMInfo.getBrowserAPI().script.icon64;
  371. }
  372. static get scriptAntifeature() {
  373. if (!GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().script.antifeature;
  374. }
  375. static get scriptOptions() {
  376. if (!GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().script.options;
  377. }
  378. static get scriptMetaStr() {
  379. if (GMInfo.isBrowserEnvironment()) return JSON.stringify(GMInfo.getBrowserAPI().runtime.getManifest()); else return GMInfo.getBrowserAPI().scriptMetaStr;
  380. }
  381. static get scriptHandler() {
  382. if (GMInfo.isBrowserEnvironment()) return "undefined" != typeof browser ? "Firefox" : "Chrome"; else return GMInfo.getBrowserAPI().scriptHandler;
  383. }
  384. static get scriptUpdateURL() {
  385. if (GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().runtime.getManifest().update_url; else return GMInfo.getBrowserAPI().scriptUpdateURL;
  386. }
  387. static get scriptWillUpdate() {
  388. if (!GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().scriptWillUpdate;
  389. }
  390. static get scriptResources() {
  391. if (!GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().scriptResources;
  392. }
  393. static get downloadMode() {
  394. if (!GMInfo.isBrowserEnvironment()) return GMInfo.getBrowserAPI().downloadMode;
  395. }
  396. }
  397. var __awaiter = function(thisArg, _arguments, P, generator) {
  398. return new (P || (P = Promise))((function(resolve, reject) {
  399. function fulfilled(value) {
  400. try {
  401. step(generator.next(value));
  402. } catch (e) {
  403. reject(e);
  404. }
  405. }
  406. function rejected(value) {
  407. try {
  408. step(generator.throw(value));
  409. } catch (e) {
  410. reject(e);
  411. }
  412. }
  413. function step(result) {
  414. result.done ? resolve(result.value) : function adopt(value) {
  415. return value instanceof P ? value : new P((function(resolve) {
  416. resolve(value);
  417. }));
  418. }(result.value).then(fulfilled, rejected);
  419. }
  420. step((generator = generator.apply(thisArg, _arguments || [])).next());
  421. }));
  422. };
  423. class SyncedStorage {
  424. static setItem(key, value) {
  425. return __awaiter(this, void 0, void 0, (function*() {
  426. if (!GMInfo.isBrowserEnvironment()) {
  427. Logger.logWarning("SyncedStorage is only available in browser extensions.");
  428. return;
  429. }
  430. Logger.logInfo(`Setting item in synced storage: ${key}=${value}`);
  431. const api = GMInfo.getBrowserAPI();
  432. if (null != api.storage) return new Promise(((resolve, reject) => {
  433. api.storage.sync.set({
  434. [key]: value
  435. }, (() => {
  436. if (null != api.runtime.lastError) return reject(api.runtime.lastError);
  437. resolve();
  438. }));
  439. })); else Logger.logError("Unsupported storage API.");
  440. }));
  441. }
  442. static getItem(key) {
  443. return __awaiter(this, void 0, void 0, (function*() {
  444. if (!GMInfo.isBrowserEnvironment()) {
  445. Logger.logWarning("SyncedStorage is only available in browser extensions.");
  446. return;
  447. }
  448. Logger.logInfo(`Getting item from synced storage: ${key}`);
  449. const api = GMInfo.getBrowserAPI();
  450. if (null != api.storage) return new Promise(((resolve, reject) => {
  451. api.storage.sync.get(key, (result => {
  452. if (null != api.runtime.lastError) return reject(api.runtime.lastError);
  453. resolve(result[key]);
  454. }));
  455. })); else Logger.logError("Unsupported storage API.");
  456. }));
  457. }
  458. static removeItem(key) {
  459. return __awaiter(this, void 0, void 0, (function*() {
  460. if (!GMInfo.isBrowserEnvironment()) {
  461. Logger.logWarning("SyncedStorage is only available in browser extensions.");
  462. return;
  463. }
  464. Logger.logInfo(`Removing item from synced storage: ${key}`);
  465. const api = GMInfo.getBrowserAPI();
  466. if (null != api.storage) return new Promise(((resolve, reject) => {
  467. api.storage.sync.remove(key, (() => {
  468. if (null != api.runtime.lastError) return reject(api.runtime.lastError);
  469. resolve();
  470. }));
  471. })); else Logger.logError("Unsupported storage API.");
  472. }));
  473. }
  474. }
  475. class SettingBoolean extends EventTarget {
  476. constructor(providerId, name) {
  477. super();
  478. this.description = "";
  479. Object.setPrototypeOf(this, SettingBoolean.prototype);
  480. this.name = name;
  481. this.id = providerId + "-" + makeIdCompatible(this.name);
  482. this.type = SettingType.Boolean;
  483. this._defaultValue = false;
  484. this.loadFromSyncedStorage();
  485. this.settingElem = this.create();
  486. this._settingInputElem = this.settingElem.querySelector("input");
  487. }
  488. get value() {
  489. const localValue = localStorage.getItem(this.id);
  490. if (null == localValue) return this.defaultValue; else return "true" === localValue || "1" === localValue;
  491. }
  492. set value(newValue) {
  493. if (newValue === this.defaultValue) {
  494. localStorage.removeItem(this.id);
  495. SyncedStorage.removeItem(this.id);
  496. } else {
  497. localStorage.setItem(this.id, newValue.toString());
  498. SyncedStorage.setItem(this.id, newValue);
  499. }
  500. this._settingInputElem.checked = newValue;
  501. this.invokeInput(this._settingInputElem);
  502. }
  503. get defaultValue() {
  504. return this._defaultValue;
  505. }
  506. set defaultValue(value) {
  507. this._defaultValue = value;
  508. this.value = this.value;
  509. }
  510. get onInput() {
  511. return this._onInput;
  512. }
  513. set onInput(handler) {
  514. this._onInput = handler;
  515. }
  516. create() {
  517. const container = document.createElement("div"), settingElem = document.createElement("input");
  518. settingElem.id = this.id;
  519. settingElem.type = "checkbox";
  520. settingElem.style.cursor = "pointer";
  521. settingElem.style.marginRight = "4px";
  522. settingElem.addEventListener("change", (() => this.value = settingElem.checked));
  523. container.appendChild(settingElem);
  524. const settingElemLabel = document.createElement("label");
  525. settingElemLabel.textContent = this.name;
  526. settingElemLabel.style.cursor = "pointer";
  527. settingElemLabel.style.userSelect = "none";
  528. settingElemLabel.htmlFor = this.id;
  529. container.appendChild(settingElemLabel);
  530. return container;
  531. }
  532. loadFromSyncedStorage() {
  533. SyncedStorage.getItem(this.id).then((value => {
  534. if (null != value) localStorage.setItem(this.id, value.toString());
  535. }));
  536. }
  537. toString() {
  538. return `${this.name} = ${this.value}`;
  539. }
  540. invokeInput(elem) {
  541. var _a;
  542. null === (_a = this.onInput) || void 0 === _a || _a.call(this, elem);
  543. this.dispatchEvent(new CustomEvent("input", {
  544. detail: elem
  545. }));
  546. }
  547. }
  548. class SettingNumber extends EventTarget {
  549. constructor(providerId, name) {
  550. super();
  551. this.description = "";
  552. Object.setPrototypeOf(this, SettingNumber.prototype);
  553. this.name = name;
  554. this.id = providerId + "-" + makeIdCompatible(this.name);
  555. this.type = SettingType.Number;
  556. this._defaultValue = 0;
  557. this.min = 0;
  558. this.max = 32767;
  559. this.step = 1;
  560. this.loadFromSyncedStorage();
  561. this.settingElem = this._settingInputElem = this.create();
  562. }
  563. get value() {
  564. var _a;
  565. return parseInt(null !== (_a = localStorage.getItem(this.id)) && void 0 !== _a ? _a : this.defaultValue.toString()) || this.defaultValue;
  566. }
  567. set value(newValue) {
  568. if ((newValue = Math.min(Math.max(newValue, this.min), this.max)) === this.defaultValue) {
  569. localStorage.removeItem(this.id);
  570. SyncedStorage.removeItem(this.id);
  571. } else {
  572. localStorage.setItem(this.id, newValue.toString());
  573. SyncedStorage.setItem(this.id, newValue);
  574. }
  575. this._settingInputElem.value = newValue.toString();
  576. this.invokeInput(this._settingInputElem);
  577. }
  578. get defaultValue() {
  579. return this._defaultValue;
  580. }
  581. set defaultValue(value) {
  582. this._defaultValue = value;
  583. this.value = this.value;
  584. }
  585. get onInput() {
  586. return this._onInput;
  587. }
  588. set onInput(handler) {
  589. this._onInput = handler;
  590. }
  591. create() {
  592. const settingElem = document.createElement("input");
  593. settingElem.id = this.id;
  594. settingElem.type = "text";
  595. settingElem.className = "textbox";
  596. settingElem.addEventListener("keydown", (event => {
  597. const currentValue = parseInt(settingElem.value) || this.defaultValue;
  598. if ("ArrowUp" === event.key) this.value = Math.min(currentValue + this.step, this.max); else if ("ArrowDown" === event.key) this.value = Math.max(currentValue - this.step, this.min);
  599. }));
  600. settingElem.addEventListener("input", (() => {
  601. const inputValue = settingElem.value.replace(/[^0-9]/g, ""), numericValue = parseInt(inputValue) || this.defaultValue;
  602. this.value = Math.min(Math.max(numericValue, this.min), this.max);
  603. }));
  604. return settingElem;
  605. }
  606. loadFromSyncedStorage() {
  607. SyncedStorage.getItem(this.id).then((value => {
  608. if (null != value) localStorage.setItem(this.id, value.toString());
  609. }));
  610. }
  611. toString() {
  612. return `${this.name} = ${this.value}`;
  613. }
  614. invokeInput(elem) {
  615. var _a;
  616. null === (_a = this.onInput) || void 0 === _a || _a.call(this, elem);
  617. this.dispatchEvent(new CustomEvent("input", {
  618. detail: elem
  619. }));
  620. }
  621. }
  622. class SettingText extends EventTarget {
  623. constructor(providerId, name) {
  624. super();
  625. this.description = "";
  626. Object.setPrototypeOf(this, SettingText.prototype);
  627. this.name = name;
  628. this.id = providerId + "-" + makeIdCompatible(this.name);
  629. this.type = SettingType.Text;
  630. this._defaultValue = "";
  631. this.loadFromSyncedStorage();
  632. this.settingElem = this.create();
  633. this._settingInputElem = this.settingElem.querySelector("input");
  634. this._errorMessage = this.settingElem.querySelector(".error-message");
  635. }
  636. get value() {
  637. var _a;
  638. return null !== (_a = localStorage.getItem(this.id)) && void 0 !== _a ? _a : this.defaultValue;
  639. }
  640. set value(newValue) {
  641. if (null == this.verifyRegex || this.verifyRegex.test(newValue)) {
  642. this._errorMessage.style.display = "none";
  643. try {
  644. if (newValue === this.defaultValue) {
  645. localStorage.removeItem(this.id);
  646. SyncedStorage.removeItem(this.id);
  647. } else {
  648. localStorage.setItem(this.id, newValue);
  649. SyncedStorage.setItem(this.id, newValue);
  650. }
  651. } catch (error) {
  652. Logger.logError(error);
  653. }
  654. this._settingInputElem.value = newValue;
  655. this.invokeInput(this._settingInputElem);
  656. } else this._errorMessage.style.display = "block";
  657. }
  658. get defaultValue() {
  659. return this._defaultValue;
  660. }
  661. set defaultValue(value) {
  662. this._defaultValue = value;
  663. this.value = this.value;
  664. }
  665. get onInput() {
  666. return this._onInput;
  667. }
  668. set onInput(handler) {
  669. this._onInput = handler;
  670. }
  671. create() {
  672. const container = document.createElement("div");
  673. container.style.position = "relative";
  674. const settingElem = document.createElement("input");
  675. settingElem.id = this.id;
  676. settingElem.type = "text";
  677. settingElem.className = "textbox";
  678. settingElem.addEventListener("input", (() => {
  679. if (null != this.verifyRegex && !this.verifyRegex.test(settingElem.value)) this._errorMessage.style.display = "block"; else this._errorMessage.style.display = "none";
  680. this.value = settingElem.value;
  681. }));
  682. container.appendChild(settingElem);
  683. const errorMessage = document.createElement("div");
  684. errorMessage.className = "error-message";
  685. errorMessage.style.color = "red";
  686. errorMessage.style.display = "none";
  687. errorMessage.style.position = "absolute";
  688. errorMessage.style.top = "100%";
  689. errorMessage.style.left = "0";
  690. errorMessage.textContent = "Invalid input";
  691. container.appendChild(errorMessage);
  692. return container;
  693. }
  694. loadFromSyncedStorage() {
  695. SyncedStorage.getItem(this.id).then((value => {
  696. if (null != value) localStorage.setItem(this.id, value.toString());
  697. }));
  698. }
  699. toString() {
  700. return `${this.name} = ${this.value}`;
  701. }
  702. invokeInput(elem) {
  703. var _a;
  704. null === (_a = this.onInput) || void 0 === _a || _a.call(this, elem);
  705. this.dispatchEvent(new CustomEvent("input", {
  706. detail: elem
  707. }));
  708. }
  709. }
  710. var injectStylesIntoStyleTag = __webpack_require__(72), injectStylesIntoStyleTag_default = __webpack_require__.n(injectStylesIntoStyleTag), styleDomAPI = __webpack_require__(825), styleDomAPI_default = __webpack_require__.n(styleDomAPI), insertBySelector = __webpack_require__(659), insertBySelector_default = __webpack_require__.n(insertBySelector), setAttributesWithoutAttributes = __webpack_require__(56), setAttributesWithoutAttributes_default = __webpack_require__.n(setAttributesWithoutAttributes), insertStyleElement = __webpack_require__(540), insertStyleElement_default = __webpack_require__.n(insertStyleElement), styleTagTransform = __webpack_require__(113), styleTagTransform_default = __webpack_require__.n(styleTagTransform), Style = __webpack_require__(950), options = {};
  711. options.styleTagTransform = styleTagTransform_default();
  712. options.setAttributes = setAttributesWithoutAttributes_default();
  713. options.insert = insertBySelector_default().bind(null, "head");
  714. options.domAPI = styleDomAPI_default();
  715. options.insertStyleElement = insertStyleElement_default();
  716. injectStylesIntoStyleTag_default()(Style.A, options);
  717. Style.A && Style.A.locals && Style.A.locals;
  718. class Settings {
  719. constructor(provider, headerName) {
  720. this.settings = {};
  721. this.showFeatureEnabledSetting = true;
  722. this._settingClassMapping = {
  723. [SettingType.Number]: SettingNumber,
  724. [SettingType.Boolean]: SettingBoolean,
  725. [SettingType.Action]: SettingAction,
  726. [SettingType.Text]: SettingText
  727. };
  728. this._menuName = "Extension Settings";
  729. this._menuNameId = makeIdCompatible("Extension Settings");
  730. this._provider = provider;
  731. this._providerId = makeIdCompatible(provider);
  732. this._headerName = headerName;
  733. this._isFeatureEnabledSetting = new SettingBoolean(this.providerId, `${headerName} Enabled`);
  734. this._isFeatureEnabledSetting.defaultValue = true;
  735. }
  736. get menuName() {
  737. return this._menuName;
  738. }
  739. get menuNameId() {
  740. return this._menuNameId;
  741. }
  742. get provider() {
  743. return this._provider;
  744. }
  745. get providerId() {
  746. return this._providerId;
  747. }
  748. get headerName() {
  749. return this._headerName;
  750. }
  751. get isFeatureEnabled() {
  752. return this._isFeatureEnabledSetting.value;
  753. }
  754. newSetting(type, name) {
  755. const newSetting = new (0, this._settingClassMapping[type])(this.providerId, name);
  756. this.settings[name] = newSetting;
  757. return newSetting;
  758. }
  759. loadSettings() {
  760. try {
  761. this.addExSettingsMenu(this.menuName, this.provider, this.menuNameId, this.providerId);
  762. if (window.location.toString().includes("controls/settings")) {
  763. this.addExSettingsMenuSidebar(this.menuName, this.provider, this.menuNameId, this.providerId);
  764. if (window.location.toString().includes("?extension=" + this.providerId)) this.loadSettingValues(this.headerName, Object.values(this.settings));
  765. }
  766. } catch (error) {
  767. Logger.logError(error);
  768. }
  769. }
  770. loadSettingValues(headerName, settings) {
  771. var _a;
  772. if (0 === settings.length) return;
  773. if (null != document.getElementById(headerName + "_settingscontainer")) return;
  774. const columnPage = document.getElementById("columnpage"), content = null == columnPage ? void 0 : columnPage.querySelector('div[class="content"]');
  775. if (null == content) {
  776. Logger.logError("Failed to load settings. No content found.");
  777. return;
  778. }
  779. const nonExSettings = content.querySelectorAll('section:not([class="exsettings"])');
  780. for (const section of Array.from(null != nonExSettings ? nonExSettings : [])) null === (_a = section.parentNode) || void 0 === _a || _a.removeChild(section);
  781. const section = document.createElement("section");
  782. section.id = headerName + "_settingscontainer";
  783. section.className = "exsettings";
  784. const headerContainer = document.createElement("div");
  785. headerContainer.className = "section-header";
  786. const header = document.createElement("h2");
  787. header.textContent = headerName;
  788. const bodyContainer = document.createElement("div");
  789. bodyContainer.className = "section-body";
  790. if (this._isFeatureEnabledSetting.value) bodyContainer.classList.remove("collapsed"); else bodyContainer.classList.add("collapsed");
  791. if (this.showFeatureEnabledSetting) headerContainer.appendChild(this.createFeatureEnableSetting(bodyContainer));
  792. headerContainer.appendChild(header);
  793. section.appendChild(headerContainer);
  794. for (const setting of settings) bodyContainer.appendChild(this.createSettingContainer(setting));
  795. section.appendChild(bodyContainer);
  796. content.appendChild(section);
  797. }
  798. createFeatureEnableSetting(bodyContainer) {
  799. const enableFeatureSettingContainerElem = document.createElement("label");
  800. enableFeatureSettingContainerElem.classList.add("switch");
  801. const enableFeatureSettingInput = document.createElement("input");
  802. enableFeatureSettingInput.type = "checkbox";
  803. enableFeatureSettingInput.id = "toggleSwitch";
  804. enableFeatureSettingInput.checked = this._isFeatureEnabledSetting.value;
  805. enableFeatureSettingInput.addEventListener("input", (() => {
  806. this._isFeatureEnabledSetting.value = enableFeatureSettingInput.checked;
  807. if (enableFeatureSettingInput.checked) bodyContainer.classList.remove("collapsed"); else bodyContainer.classList.add("collapsed");
  808. }));
  809. const enableFeatureSettingSpan = document.createElement("span");
  810. enableFeatureSettingSpan.classList.add("slider");
  811. enableFeatureSettingContainerElem.appendChild(enableFeatureSettingInput);
  812. enableFeatureSettingContainerElem.appendChild(enableFeatureSettingSpan);
  813. return enableFeatureSettingContainerElem;
  814. }
  815. toString() {
  816. if (0 === Object.keys(this.settings).length) return `${this.menuName} has no settings.`;
  817. let settingsString = "(";
  818. Object.keys(this.settings).forEach((key => {
  819. if (this.settings[key].type !== SettingType.Action) settingsString += `"${this.settings[key].toString()}", `;
  820. }));
  821. settingsString = settingsString.slice(0, -2) + ")";
  822. return settingsString;
  823. }
  824. createSettingContainer(setting) {
  825. const settingContainer = document.createElement("div");
  826. settingContainer.className = "control-panel-item-container";
  827. const settingName = document.createElement("div");
  828. settingName.className = "control-panel-item-name";
  829. const settingNameText = document.createElement("h4");
  830. settingNameText.textContent = setting.name;
  831. settingName.appendChild(settingNameText);
  832. settingContainer.appendChild(settingName);
  833. const settingDesc = document.createElement("div");
  834. settingDesc.className = "control-panel-item-description";
  835. const settingDescText = document.createTextNode(setting.description);
  836. settingDesc.appendChild(settingDescText);
  837. settingContainer.appendChild(settingDesc);
  838. if (showResetButtonSetting.value) {
  839. settingDesc.appendChild(document.createElement("br"));
  840. settingDesc.appendChild(this.createSettingReset(setting));
  841. }
  842. const settingOption = document.createElement("div");
  843. settingOption.className = "control-panel-item-options";
  844. settingOption.appendChild(setting.settingElem);
  845. settingContainer.appendChild(settingOption);
  846. return settingContainer;
  847. }
  848. createSettingReset(setting) {
  849. const settingDescReset = document.createElement("a");
  850. settingDescReset.id = setting.id + "_settingreset";
  851. settingDescReset.textContent = "Reset this Setting";
  852. settingDescReset.style.cursor = "pointer";
  853. settingDescReset.style.color = "aqua";
  854. settingDescReset.style.textDecoration = "underline";
  855. settingDescReset.style.fontStyle = "italic";
  856. settingDescReset.style.fontSize = "14px";
  857. settingDescReset.addEventListener("click", (() => {
  858. setting.value = setting.defaultValue;
  859. }));
  860. return settingDescReset;
  861. }
  862. addExSettingsMenu(name, provider, nameId, providerId) {
  863. var _a;
  864. try {
  865. const navBar = document.querySelector('ul[class="navhideonmobile"]'), settings = null === (_a = null == navBar ? void 0 : navBar.querySelector('a[href="/controls/settings/"]')) || void 0 === _a ? void 0 : _a.parentNode;
  866. if (null == settings) {
  867. Logger.logError(`Failed to add extension ${name} to settings menu`);
  868. return;
  869. }
  870. const exSettingNamePresent = null != document.getElementById(nameId), exSettingProviderPresent = null != document.getElementById(providerId);
  871. if (!exSettingNamePresent) {
  872. const exSettingsHeader = document.createElement("h3");
  873. exSettingsHeader.id = nameId;
  874. exSettingsHeader.textContent = name;
  875. settings.appendChild(exSettingsHeader);
  876. }
  877. if (!exSettingProviderPresent) {
  878. const currExSettings = document.createElement("a");
  879. currExSettings.id = providerId;
  880. currExSettings.textContent = provider;
  881. currExSettings.href = "/controls/settings?extension=" + providerId;
  882. currExSettings.style.cursor = "pointer";
  883. settings.appendChild(currExSettings);
  884. }
  885. } catch (error) {
  886. Logger.logError(error);
  887. }
  888. }
  889. addExSettingsMenuSidebar(name, provider, nameId, providerId) {
  890. try {
  891. const settings = document.getElementById("controlpanelnav");
  892. if (null == settings) {
  893. Logger.logError(`Failed to add extension ${name} to settings sidebar`);
  894. return;
  895. }
  896. const exSettingNamePresent = null != document.getElementById(nameId + "_side"), exSettingProviderPresent = null != document.getElementById(providerId + "_side");
  897. if (!exSettingNamePresent) {
  898. const exSettingsHeader = document.createElement("h3");
  899. exSettingsHeader.id = nameId + "_side";
  900. exSettingsHeader.textContent = name;
  901. settings.appendChild(exSettingsHeader);
  902. }
  903. if (!exSettingProviderPresent) {
  904. const currExSettings = document.createElement("a");
  905. currExSettings.id = providerId + "_side";
  906. currExSettings.textContent = provider;
  907. currExSettings.href = "/controls/settings?extension=" + providerId;
  908. currExSettings.style.cursor = "pointer";
  909. settings.appendChild(currExSettings);
  910. settings.appendChild(document.createElement("br"));
  911. }
  912. } catch (error) {
  913. Logger.logError(error);
  914. }
  915. }
  916. }
  917. var _a;
  918. Object.defineProperties(window, {
  919. FACustomSettings: {
  920. get: () => Settings
  921. },
  922. FASettingType: {
  923. get: () => SettingType
  924. }
  925. });
  926. const customSettings = new Settings("Custom-Furaffinity-Settings", "Global Custom-Furaffinity-Settings");
  927. customSettings.showFeatureEnabledSetting = false;
  928. const loggingSetting = customSettings.newSetting(window.FASettingType.Number, "Logging");
  929. loggingSetting.description = "The logging level. 0 = none, 1 = errors, 2 = warnings, 3 = infos.";
  930. loggingSetting.defaultValue = LogLevel.Error;
  931. loggingSetting.addEventListener("input", (() => Logger.setLogLevel(loggingSetting.value)));
  932. Logger.setLogLevel(loggingSetting.value);
  933. const showResetButtonSetting = customSettings.newSetting(SettingType.Boolean, "Show Reset Button");
  934. showResetButtonSetting.description = 'Set wether the "Reset this Setting" button is shown in other Settings.';
  935. showResetButtonSetting.defaultValue = true;
  936. customSettings.loadSettings();
  937. let color = "color: blue";
  938. if (null === (_a = window.matchMedia) || void 0 === _a ? void 0 : _a.call(window, "(prefers-color-scheme: dark)").matches) color = "color: aqua";
  939. const settingString = `GlobalSettings: ${customSettings.toString()}`;
  940. console.info(`%c${settingString}`, color);
  941. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址