From 5f99b7384ad2d844ddc460b4c198696510d23110 Mon Sep 17 00:00:00 2001
From: TonyFox <the_tonyfox@yahoo.com>
Date: Wed, 12 Oct 2022 12:38:37 -0400
Subject: [PATCH 1/5] Files req'd for linting standards - Updated to 2019 -
 Updated to 160 char - Limit beyond 2019 - New ESLINT plugin for such

---
 .eslintrc.cjs     |  19 +--
 .prettierrc.json  |   2 +-
 package-lock.json | 307 ++++++++++++++++++++++++++++++++++++----------
 package.json      |   3 +-
 4 files changed, 255 insertions(+), 76 deletions(-)

diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index b33016b600..ee83d866c0 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -157,18 +157,20 @@ module.exports = {
 	],
 
 	parserOptions: {
-		// Support back to ES2018 to cover old mobile devices with outdated WebView versions that fail on 2019 and up functions
-		// ecmaVersion: "2018", (taken care of by env es2018)
+		// Support back to ES2019 to cover old mobile devices with outdated WebView versions that fail on 2020 and up functions
+		// ecmaVersion: "2019", (taken care of by env es2019)
 		sourceType: "module",
 	},
 
 	env: {
 		browser: true,
-		es2018: true,
+		es2019: true,
 		jquery: true,
 	},
 
-	extends: ["eslint:recommended", "plugin:jsdoc/recommended", "prettier-standard/prettier-file"],
+	plugins: ["es"],
+
+	extends: ["eslint:recommended", "plugin:jsdoc/recommended", "prettier-standard/prettier-file", "plugin:es/restrict-to-es2019"],
 
 	settings: {
 		jsdoc: {
@@ -187,11 +189,7 @@ module.exports = {
 
 		// Descriptions should be sentence-like not comment-like
 		"jsdoc/require-description-complete-sentence": "warn",
-		"jsdoc/require-hyphen-before-param-description": [
-			"error",
-			"never",
-			{ tags: { property: "never" } },
-		],
+		"jsdoc/require-hyphen-before-param-description": ["error", "never", { tags: { property: "never" } }],
 		// Adding JSDoc is preferable but not required
 		"jsdoc/require-jsdoc": "off",
 		"jsdoc/require-param-description": "off",
@@ -202,5 +200,8 @@ module.exports = {
 		/* eslint-plugin-prettier */
 
 		"prettier/prettier": "warn",
+
+		// Rule to fix Safari/Webkit not supporting a 4+ year old function you idiots
+		"es/no-regexp-lookbehind-assertions": "error",
 	},
 };
diff --git a/.prettierrc.json b/.prettierrc.json
index c8ef8fe4cc..7c1e8f62ad 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,5 +1,5 @@
 {
-	"printWidth": 100,
+	"printWidth": 160,
 	"useTabs": true,
 	"semi": true,
 	"endOfLine": "lf",
diff --git a/package-lock.json b/package-lock.json
index 6721b2fc6b..c9d6040fd7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,10 +9,11 @@
       "version": "1.0.0",
       "devDependencies": {
         "@types/jquery": "^3.5.14",
-        "eslint": "^8.18.0",
+        "eslint": "^8.25.0",
         "eslint-config-prettier": "^8.5.0",
         "eslint-config-prettier-standard": "^4.0.1",
         "eslint-config-standard": "^17.0.0",
+        "eslint-plugin-es": "^4.1.0",
         "eslint-plugin-import": "^2.26.0",
         "eslint-plugin-jsdoc": "^39.3.3",
         "eslint-plugin-n": "^15.2.3",
@@ -168,14 +169,14 @@
       }
     },
     "node_modules/@eslint/eslintrc": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
-      "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
+      "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
       "dev": true,
       "dependencies": {
         "ajv": "^6.12.4",
         "debug": "^4.3.2",
-        "espree": "^9.3.2",
+        "espree": "^9.4.0",
         "globals": "^13.15.0",
         "ignore": "^5.2.0",
         "import-fresh": "^3.2.1",
@@ -185,12 +186,15 @@
       },
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
     "node_modules/@humanwhocodes/config-array": {
-      "version": "0.9.5",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
-      "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+      "version": "0.10.7",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
+      "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
       "dev": true,
       "dependencies": {
         "@humanwhocodes/object-schema": "^1.2.1",
@@ -201,6 +205,19 @@
         "node": ">=10.10.0"
       }
     },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
     "node_modules/@humanwhocodes/object-schema": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
@@ -288,9 +305,9 @@
       "dev": true
     },
     "node_modules/acorn": {
-      "version": "8.7.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
-      "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
+      "version": "8.8.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
+      "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
       "dev": true,
       "bin": {
         "acorn": "bin/acorn"
@@ -1113,13 +1130,14 @@
       }
     },
     "node_modules/eslint": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz",
-      "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==",
+      "version": "8.25.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz",
+      "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==",
       "dev": true,
       "dependencies": {
-        "@eslint/eslintrc": "^1.3.0",
-        "@humanwhocodes/config-array": "^0.9.2",
+        "@eslint/eslintrc": "^1.3.3",
+        "@humanwhocodes/config-array": "^0.10.5",
+        "@humanwhocodes/module-importer": "^1.0.1",
         "ajv": "^6.10.0",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
@@ -1129,18 +1147,21 @@
         "eslint-scope": "^7.1.1",
         "eslint-utils": "^3.0.0",
         "eslint-visitor-keys": "^3.3.0",
-        "espree": "^9.3.2",
+        "espree": "^9.4.0",
         "esquery": "^1.4.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^6.0.1",
-        "functional-red-black-tree": "^1.0.1",
+        "find-up": "^5.0.0",
         "glob-parent": "^6.0.1",
         "globals": "^13.15.0",
+        "globby": "^11.1.0",
+        "grapheme-splitter": "^1.0.4",
         "ignore": "^5.2.0",
         "import-fresh": "^3.0.0",
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
+        "js-sdsl": "^4.1.4",
         "js-yaml": "^4.1.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
         "levn": "^0.4.1",
@@ -1151,8 +1172,7 @@
         "regexpp": "^3.2.0",
         "strip-ansi": "^6.0.1",
         "strip-json-comments": "^3.1.0",
-        "text-table": "^0.2.0",
-        "v8-compile-cache": "^2.0.3"
+        "text-table": "^0.2.0"
       },
       "bin": {
         "eslint": "bin/eslint.js"
@@ -1480,18 +1500,91 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
+    "node_modules/eslint/node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint/node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint/node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint/node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint/node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/espree": {
-      "version": "9.3.2",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
-      "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
+      "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==",
       "dev": true,
       "dependencies": {
-        "acorn": "^8.7.1",
+        "acorn": "^8.8.0",
         "acorn-jsx": "^5.3.2",
         "eslint-visitor-keys": "^3.3.0"
       },
       "engines": {
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
       }
     },
     "node_modules/esquery": {
@@ -1723,12 +1816,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/functional-red-black-tree": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
-      "dev": true
-    },
     "node_modules/functions-have-names": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
@@ -1872,9 +1959,9 @@
       }
     },
     "node_modules/globals": {
-      "version": "13.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
-      "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
+      "version": "13.17.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
+      "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
       "dev": true,
       "dependencies": {
         "type-fest": "^0.20.2"
@@ -1912,6 +1999,12 @@
       "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
       "dev": true
     },
+    "node_modules/grapheme-splitter": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+      "dev": true
+    },
     "node_modules/hard-rejection": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
@@ -2389,6 +2482,12 @@
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
       "dev": true
     },
+    "node_modules/js-sdsl": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
+      "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==",
+      "dev": true
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -4801,6 +4900,18 @@
       "engines": {
         "node": ">=8"
       }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
     }
   },
   "dependencies": {
@@ -4907,14 +5018,14 @@
       }
     },
     "@eslint/eslintrc": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
-      "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
+      "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
       "dev": true,
       "requires": {
         "ajv": "^6.12.4",
         "debug": "^4.3.2",
-        "espree": "^9.3.2",
+        "espree": "^9.4.0",
         "globals": "^13.15.0",
         "ignore": "^5.2.0",
         "import-fresh": "^3.2.1",
@@ -4924,9 +5035,9 @@
       }
     },
     "@humanwhocodes/config-array": {
-      "version": "0.9.5",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
-      "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+      "version": "0.10.7",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
+      "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
       "dev": true,
       "requires": {
         "@humanwhocodes/object-schema": "^1.2.1",
@@ -4934,6 +5045,12 @@
         "minimatch": "^3.0.4"
       }
     },
+    "@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true
+    },
     "@humanwhocodes/object-schema": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
@@ -5012,9 +5129,9 @@
       "dev": true
     },
     "acorn": {
-      "version": "8.7.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
-      "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
+      "version": "8.8.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
+      "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
       "dev": true
     },
     "acorn-jsx": {
@@ -5630,13 +5747,14 @@
       "dev": true
     },
     "eslint": {
-      "version": "8.18.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.18.0.tgz",
-      "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==",
+      "version": "8.25.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz",
+      "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==",
       "dev": true,
       "requires": {
-        "@eslint/eslintrc": "^1.3.0",
-        "@humanwhocodes/config-array": "^0.9.2",
+        "@eslint/eslintrc": "^1.3.3",
+        "@humanwhocodes/config-array": "^0.10.5",
+        "@humanwhocodes/module-importer": "^1.0.1",
         "ajv": "^6.10.0",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
@@ -5646,18 +5764,21 @@
         "eslint-scope": "^7.1.1",
         "eslint-utils": "^3.0.0",
         "eslint-visitor-keys": "^3.3.0",
-        "espree": "^9.3.2",
+        "espree": "^9.4.0",
         "esquery": "^1.4.0",
         "esutils": "^2.0.2",
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^6.0.1",
-        "functional-red-black-tree": "^1.0.1",
+        "find-up": "^5.0.0",
         "glob-parent": "^6.0.1",
         "globals": "^13.15.0",
+        "globby": "^11.1.0",
+        "grapheme-splitter": "^1.0.4",
         "ignore": "^5.2.0",
         "import-fresh": "^3.0.0",
         "imurmurhash": "^0.1.4",
         "is-glob": "^4.0.0",
+        "js-sdsl": "^4.1.4",
         "js-yaml": "^4.1.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
         "levn": "^0.4.1",
@@ -5668,8 +5789,52 @@
         "regexpp": "^3.2.0",
         "strip-ansi": "^6.0.1",
         "strip-json-comments": "^3.1.0",
-        "text-table": "^0.2.0",
-        "v8-compile-cache": "^2.0.3"
+        "text-table": "^0.2.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+          "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^6.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+          "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^5.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+          "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^0.1.0"
+          }
+        },
+        "p-locate": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+          "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^3.0.2"
+          }
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+          "dev": true
+        }
       }
     },
     "eslint-config-prettier": {
@@ -5890,12 +6055,12 @@
       "dev": true
     },
     "espree": {
-      "version": "9.3.2",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
-      "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
+      "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==",
       "dev": true,
       "requires": {
-        "acorn": "^8.7.1",
+        "acorn": "^8.8.0",
         "acorn-jsx": "^5.3.2",
         "eslint-visitor-keys": "^3.3.0"
       }
@@ -6086,12 +6251,6 @@
         "functions-have-names": "^1.2.2"
       }
     },
-    "functional-red-black-tree": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
-      "dev": true
-    },
     "functions-have-names": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
@@ -6192,9 +6351,9 @@
       }
     },
     "globals": {
-      "version": "13.15.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
-      "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
+      "version": "13.17.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
+      "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
       "dev": true,
       "requires": {
         "type-fest": "^0.20.2"
@@ -6220,6 +6379,12 @@
       "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
       "dev": true
     },
+    "grapheme-splitter": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+      "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+      "dev": true
+    },
     "hard-rejection": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
@@ -6538,6 +6703,12 @@
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
       "dev": true
     },
+    "js-sdsl": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
+      "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==",
+      "dev": true
+    },
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -8318,6 +8489,12 @@
       "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
       "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
       "dev": true
+    },
+    "yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true
     }
   }
 }
diff --git a/package.json b/package.json
index d55c7175ed..058779a96c 100644
--- a/package.json
+++ b/package.json
@@ -9,10 +9,11 @@
   },
   "devDependencies": {
     "@types/jquery": "^3.5.14",
-    "eslint": "^8.18.0",
+    "eslint": "^8.25.0",
     "eslint-config-prettier": "^8.5.0",
     "eslint-config-prettier-standard": "^4.0.1",
     "eslint-config-standard": "^17.0.0",
+    "eslint-plugin-es": "^4.1.0",
     "eslint-plugin-import": "^2.26.0",
     "eslint-plugin-jsdoc": "^39.3.3",
     "eslint-plugin-n": "^15.2.3",
-- 
GitLab


From 8f12efe37473c425f1a88d7e1634d23ab7cacf7e Mon Sep 17 00:00:00 2001
From: TonyFox <the_tonyfox@yahoo.com>
Date: Wed, 12 Oct 2022 12:40:50 -0400
Subject: [PATCH 2/5] namespace, init, eyesRelated

---
 .../00-namespace/namespace.js                 | 55 ++++++++-------
 game/00-framework-tools/02-version/.init.js   | 68 ++++++++++---------
 game/03-JavaScript/eyesRelated.js             | 64 ++++-------------
 3 files changed, 79 insertions(+), 108 deletions(-)

diff --git a/game/00-framework-tools/00-namespace/namespace.js b/game/00-framework-tools/00-namespace/namespace.js
index 81fa6fce30..c57452a357 100644
--- a/game/00-framework-tools/00-namespace/namespace.js
+++ b/game/00-framework-tools/00-namespace/namespace.js
@@ -1,26 +1,22 @@
-/**
- * As part of my various refactors, I ended up introducing a bunch of
- * global namespace-esk variables.
- *
- * Having a single spot to document them makes sense. By convention,
- * if you need to make a new top-level namespace, declare it here.
+/*
+ * As part of various refactors, many global namespace variables were created.
+ * This is a single spot to document them, and create top-level namespaces.
  */
 
 /**
- * Declare everything in a root namespace, so that things can still be found
- * if shadowed, and for "documentation" purposes
+ * Declare everything in a root namespace, so that things can still be found if shadowed, and for "documentation" purposes.
  */
 window.DOL = {
 	// In pseudo-load order
+
 	/**
-	 * Mini application reporter app
-	 * helps get detailed error messages to devs
-	 * {@link ./01-error/error.js
+	 * This is a miniature application reporter app.  It helps get detailed error messages to devs.
+	 * See file {@link ./01-error/error.js} for more.
 	 */
 	Errors: {},
 	/**
-	 * Registry of state schema, used for migrating Dol to new versions
-	 * {@link ./02-version/.init.js
+	 * Registry of state schema, used for migrating DoL to new versions.
+	 * See {@link ./02-version/.init.js} for more.
 	 */
 	Versions: {},
 	Perflog: {},
@@ -29,23 +25,24 @@ window.DOL = {
 	 */
 	Stack: [],
 
-	/** Patch to make javascript execution more consistent (see comment below) */
-	State: State,
-	/** Patch to make javascript execution more consistent (see comment below) */
-	setup: setup,
-	/** Patch to make javascript execution more consistent (see comment below) */
-	Wikifier: Wikifier,
-	/** Patch to make javascript execution more consistent (see comment below) */
-	Template: Template
-}
+	// The following are patches to make javascript execution more consistent (see comment below).
+	State,
+	setup,
+	Wikifier,
+	Template,
+};
+
 /* Make each of these namespaces available at the top level as well */
-window.defineGlobalNamespaces = (namespaces) => {
+window.defineGlobalNamespaces = namespaces => {
 	Object.entries(namespaces).forEach(([name, namespaceObject]) => {
 		try {
 			if (window[name] && window[name] !== namespaceObject) {
-				console.warn(`Attempted to set ${name} in the global namespace, but it's already in use. Skipping this assignment. Existing Object:`, window[name])
+				console.warn(
+					`Attempted to set ${name} in the global namespace, but it's already in use. Skipping this assignment. Existing Object:`,
+					window[name]
+				);
 			} else {
-				/* Make it more difficult to shadow/overwrite things (users can still Object.defineProperty if they really mean it) */
+				// Make it more difficult to shadow/overwrite things (users can still Object.defineProperty if they really mean it
 				Object.defineProperty(window, name, { value: namespaceObject, writeable: false });
 			}
 		} catch (e) {
@@ -54,10 +51,10 @@ window.defineGlobalNamespaces = (namespaces) => {
 			}
 		}
 	});
-}
+};
 defineGlobalNamespaces(DOL);
 
-/**
+/*
  * Patches to make javascript execution more consistent
  * OR: Why we alias SugarCube.State as State:
  *
@@ -72,7 +69,9 @@ defineGlobalNamespaces(DOL);
  * sugarcube execution. `SugarCube.State` (currently) does not exist, but `State` (note, *not* window.State)
  * exists.
  */
-/** Uncomment the following lines to get a better idea about how sugarcube makes certain globals available */
+
+/** Uncomment the following lines to get a better idea about how sugarcube makes certain globals available. */
+
 /*
 function sugarCubeGlobals() {
 	return {
diff --git a/game/00-framework-tools/02-version/.init.js b/game/00-framework-tools/02-version/.init.js
index c62a6d87c1..f6e4f0360a 100644
--- a/game/00-framework-tools/02-version/.init.js
+++ b/game/00-framework-tools/02-version/.init.js
@@ -1,42 +1,45 @@
 Versions.registry = [];
 Versions.log = [];
-Versions.recordStep = (step) => {
+Versions.recordStep = step => {
 	Versions.log.push(step);
-}
+};
 // this will be updated automatically
 Versions.latest = 0;
 Versions.register = (versionId, migration) => {
 	console.log(`Registering version schema`, typeof versionId, versionId, migration);
-	if (versionId !== (Versions.registry.length + 1)) {
-		// If we get all versions in order, we don't have to try to
-		// verify them after the fact.
-		Errors.report(`Invalid migration registration. Next expected version id was ${Versions.registry.length + 1}, but got ${versionId} instead. Version migrations might not work as expected`)
+	if (versionId !== Versions.registry.length + 1) {
+		// If we get all versions in order, we don't have to try to verify them after the fact.
+		Errors.report(
+			`Invalid migration registration. Next expected version id was ${Versions.registry.length + 1},
+			but got ${versionId} instead. Version migrations might not work as expected`
+		);
 	}
 	Versions.registry[versionId - 1] = {
-		migration, versionId
-	}
+		migration,
+		versionId,
+	};
 	Versions.latest = Math.max(Versions.latest, versionId);
-}
+};
 Versions.migrate = (originalVersion, state) => {
 	Versions.log = [];
 	let currentVersion = originalVersion;
 	let migrationFailed = false;
 	let nextMigration = Versions.registry[currentVersion];
-	while(nextMigration && !migrationFailed) {
+	while (nextMigration && !migrationFailed) {
 		migrationFailed = nextMigration.migration(originalVersion, state);
 		currentVersion = nextMigration.versionId;
 		nextMigration = Versions.registry[currentVersion];
-	};
+	}
 	if (migrationFailed) {
 		Errors.report(`One or more errors occurred while attempting to migrate from schema version ${originalVersion} to the latest`, Versions.log);
 	}
 	if (currentVersion !== Versions.latest && !migrationFailed) {
-		Errors.report(`Migration steps missing for schema version: ${originalVersion}`, Versions.log)
+		Errors.report(`Migration steps missing for schema version: ${originalVersion}`, Versions.log);
 	}
 	return currentVersion;
-}
+};
 /**
- * Utility for migrations to use. Make more robust migrations + provides logging in case of later errors
+ * Utility for migrations to use. Make more robust migrations + provides logging in case of later errors.
  *
  * Usage:
  * Versions.stepper(originalVersion, <youridhere>)
@@ -51,9 +54,9 @@ Versions.migrate = (originalVersion, state) => {
  *	  // ensures that the step did what was intended to be done.
  *	  // this is useful in that if the update step fails, but we're already in a good state
  *	  // then we know we can (probably) continue successfully, despite the earlier failure
- *  .step(() => ..., { requires: () => ... })
+ *  .step(() => ..., { requires: () => ... }).
  *
- * Note, by convention, a single stepper should be used
+ * Note, by convention, a single stepper should be used.
  */
 Versions.Stepper = class Stepper {
 	constructor(originalVersion, currentStage) {
@@ -62,20 +65,23 @@ Versions.Stepper = class Stepper {
 		this.nextStepIdNumber = 0;
 		this.shouldContinue = true;
 	}
-	step(statement, { critical, requires, name }={}) {
+
+	step(statement, { critical, requires, name } = {}) {
 		const errors = [];
 		let requirementsComplete = !requires;
 		if (this.shouldContinue) {
-			if (name === undefined) { name = statement.toString(); }
-			const stepId = `${this.currentStage}-${this.nextStepIdNumber++}`
+			if (name === undefined) {
+				name = statement.toString();
+			}
+			const stepId = `${this.currentStage}-${this.nextStepIdNumber++}`;
 			try {
 				statement();
-			} catch(e) {
+			} catch (e) {
 				errors.push(e);
 			} finally {
 				try {
 					requirementsComplete = requires();
-				} catch(e) {
+				} catch (e) {
 					errors.push(e);
 				}
 			}
@@ -84,30 +90,30 @@ Versions.Stepper = class Stepper {
 				originalVersion: this.originalVersion,
 				currentStage: this.currentStage,
 				name,
-				errors
-			})
+				errors,
+			});
 			this.shouldContinue = requirementsComplete && !(errors.length > 0 && critical);
 		}
 		return this;
 	}
-}
+};
+
 Versions.stepper = (originalVersion, currentStage) => {
 	return new Versions.Stepper(originalVersion, currentStage);
-}
+};
 
 $(document).on(":archiverevive", function (e) {
 	const state = e.archive.state;
 	const version = state.variables.version;
 
 	if (!version || version.schema === undefined) {
-		// The sugarcube baseline version has yet to be applied. Move to a special passage to
-		// apply it. The user will be able to resume afterwords.
-		if (state.title !== 'Upgrade Waiting Room' && state.title !== 'Start') {
-			console.log(`Schema version number not found. Banishing the player to the waiting room.`)
-			state.variables.navigation = state.variables.navigation || { }
+		// The sugarcube baseline version has yet to be applied. Move to a special passage to apply it. The user will be able to resume afterwords.
+		if (state.title !== "Upgrade Waiting Room" && state.title !== "Start") {
+			console.log(`Schema version number not found. Banishing the player to the waiting room.`);
+			state.variables.navigation = state.variables.navigation || {};
 			state.variables.navigation.stack = state.variables.navigation.stack || [];
 			state.variables.navigation.stack.push(state.title);
-			state.title = 'Upgrade Waiting Room';
+			state.title = "Upgrade Waiting Room";
 		}
 	} else if (version.schema !== Versions.latest) {
 		console.log(`Migrating game state from ${schemaVersion} to ${Versions.latest}`);
diff --git a/game/03-JavaScript/eyesRelated.js b/game/03-JavaScript/eyesRelated.js
index d9c482e7ff..7b22e4b1e9 100644
--- a/game/03-JavaScript/eyesRelated.js
+++ b/game/03-JavaScript/eyesRelated.js
@@ -55,8 +55,7 @@ function restructureEyeColourVariable() {
 			/* Both $leftEyeColour and $rightEyeColour should be the original colours for a character's eyes.
 				This function below sets it to $eyecolour, if that fails, $eyeselect, then defaults to purple, the default.
 				For it to fail, it must be undefined, it is unlikely that $eyeselect is undefined, but it's likely possible. */
-			const getColour = () =>
-				(typeof V.eyecolour === "string" ? V.eyecolour : V.eyeselect) || "purple";
+			const getColour = () => (typeof V.eyecolour === "string" ? V.eyecolour : V.eyeselect) || "purple";
 			if (!V.leftEyeColour) {
 				V.leftEyeColour = getColour();
 			}
@@ -74,8 +73,7 @@ function restructureEyeColourVariable() {
 
 			let lenses = V.makeup.eyelenses;
 			/* If lenses is not string object or number, or null, it's bad and we hard set */
-			if (!["string", "object", "number"].includes(typeof lenses) || !lenses)
-				V.makeup.eyelenses = { left: 0, right: 0 };
+			if (!["string", "object", "number"].includes(typeof lenses) || !lenses) V.makeup.eyelenses = { left: 0, right: 0 };
 			else if (typeof lenses !== "object") {
 				/* String or Number so we assign in to new object */
 				V.makeup.eyelenses = {
@@ -97,10 +95,7 @@ window.restructureEyeColourVariable = restructureEyeColourVariable;
 window.patchCorruptLensesColors = function () {
 	if (V.custom_eyecolours != null) {
 		for (const index in V.custom_eyecolours)
-			V.custom_eyecolours[index].canvasfilter.blend = window.colorNameTranslate(
-				V.custom_eyecolours[index].variable,
-				"hex"
-			);
+			V.custom_eyecolours[index].canvasfilter.blend = window.colorNameTranslate(V.custom_eyecolours[index].variable, "hex");
 	}
 };
 
@@ -120,9 +115,7 @@ window.initCustomLenses = initCustomLenses;
 
 function hexToRgbArray(hex) {
 	const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
-	return result
-		? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)]
-		: null;
+	return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null;
 }
 
 function ColorToHex(color) {
@@ -138,11 +131,7 @@ const eyeColourGradient = function (rgbBegin, rgbEnd, p) {
 
 	if (typeof rgbBegin === "string" && rgbBegin[0] === "#") rgbBegin = hexToRgbArray(rgbBegin);
 	if (typeof rgbEnd === "string" && rgbEnd[0] === "#") rgbEnd = hexToRgbArray(rgbEnd);
-	const rgb = [
-		parseInt(rgbBegin[0] * w2 + rgbEnd[0] * w1),
-		parseInt(rgbBegin[1] * w2 + rgbEnd[1] * w1),
-		parseInt(rgbBegin[2] * w2 + rgbEnd[2] * w1),
-	];
+	const rgb = [parseInt(rgbBegin[0] * w2 + rgbEnd[0] * w1), parseInt(rgbBegin[1] * w2 + rgbEnd[1] * w1), parseInt(rgbBegin[2] * w2 + rgbEnd[2] * w1)];
 	return "#" + ColorToHex(rgb[0]) + ColorToHex(rgb[1]) + ColorToHex(rgb[2]);
 };
 
@@ -169,10 +158,7 @@ window.determineCatEyeStages = function () {
 	const totalPercentage = 0.5 + Math.random() / 4; // 50%-75%
 	/* How much of targetColour the current eye colour needs to adapt into.
 	0.0 to 1.0 (0%-100%) */
-	const baseColour = [
-		setup.colours.eyes_map[V.leftEyeColour].canvasfilter.blend,
-		setup.colours.eyes_map[V.rightEyeColour].canvasfilter.blend,
-	]; // base eye colours [left, right]
+	const baseColour = [setup.colours.eyes_map[V.leftEyeColour].canvasfilter.blend, setup.colours.eyes_map[V.rightEyeColour].canvasfilter.blend]; // base eye colours [left, right]
 
 	let targetColours = shuffleArray([green, yellow, blue, orange]); // can add or edit colours
 	/* We've already shuffled these so they're already random */
@@ -181,20 +167,11 @@ window.determineCatEyeStages = function () {
 	/* The right eye is the one we see the most.
 	   If this eye is light blue and the target colour is blue, the player
 	   might not notice a difference.  So, swap the two eyes */
-	if (V.rightEyeColour === "light blue" && targetColours[1] === blue)
-		targetColours = [targetColours[1], targetColours[0]];
+	if (V.rightEyeColour === "light blue" && targetColours[1] === blue) targetColours = [targetColours[1], targetColours[0]];
 	for (let index = 1; index <= stages; index++) {
 		const eyesResult = {
-			left: eyeColourGradient(
-				baseColour[0],
-				targetColours[0],
-				(totalPercentage / stages) * index
-			), // left eye
-			right: eyeColourGradient(
-				baseColour[1],
-				targetColours[1],
-				(totalPercentage / stages) * index
-			), // right eye
+			left: eyeColourGradient(baseColour[0], targetColours[0], (totalPercentage / stages) * index), // left eye
+			right: eyeColourGradient(baseColour[1], targetColours[1], (totalPercentage / stages) * index), // right eye
 		};
 		/* i goes between "left" and "right" here with these keys */
 		for (const i in eyesResult) {
@@ -215,10 +192,8 @@ window.determineCatEyeStages = function () {
 				/* create new object for our new colour eye */
 				/* again, 'i' is either "left" or "right" here */
 				/* and x is actually a string */
-				if (V.custom_eyecolours[x].variable === "cat_tf_stage_" + (index - 1) + "_" + i)
-					V.custom_eyecolours[x] = colourObject;
-				else if (x === (V.custom_eyecolours.length - 1).toString())
-					V.custom_eyecolours.push(colourObject);
+				if (V.custom_eyecolours[x].variable === "cat_tf_stage_" + (index - 1) + "_" + i) V.custom_eyecolours[x] = colourObject;
+				else if (x === (V.custom_eyecolours.length - 1).toString()) V.custom_eyecolours.push(colourObject);
 			}
 			if (V.custom_eyecolours.length === 0) V.custom_eyecolours.push(colourObject);
 		}
@@ -234,9 +209,7 @@ window.defineCustomEyeColourStyle = function () {
 
 		if (V.makeup.eyelenses[side] !== 0) {
 			/* custom eyes colours */
-			const colourArray = V.makeup.eyelenses[side].includes("colorWheelTemporary")
-				? setup.colours.eyes
-				: V.custom_eyecolours;
+			const colourArray = V.makeup.eyelenses[side].includes("colorWheelTemporary") ? setup.colours.eyes : V.custom_eyecolours;
 			for (const colour of colourArray) {
 				/* this does this for left and right */
 				if (colour.variable === V.makeup.eyelenses[side])
@@ -246,19 +219,12 @@ window.defineCustomEyeColourStyle = function () {
 							? window.colorNameTranslate(colour.csstext, "hue")
 							: window.colorNameTranslate(V.makeup.eyelenses[side], "hue"));
 			}
-		} else if (
-			normalEyes[side] !== 0 &&
-			(normalEyes[side].includes("colorWheelTemporary") ||
-				normalEyes[side].includes("cat_tf_stage"))
-		) {
+		} else if (normalEyes[side] !== 0 && (normalEyes[side].includes("colorWheelTemporary") || normalEyes[side].includes("cat_tf_stage"))) {
 			// normal eyes colours
-			const colourArray = normalEyes[side].includes("cat_tf_stage")
-				? V.custom_eyecolours
-				: setup.colours.eyes;
+			const colourArray = normalEyes[side].includes("cat_tf_stage") ? V.custom_eyecolours : setup.colours.eyes;
 			for (const colour of colourArray) {
 				/* this does this for left and right */
-				if (colour.variable === normalEyes[side])
-					T[varSideStyle] = "filter: " + window.colorNameTranslate(colour.csstext, "hue");
+				if (colour.variable === normalEyes[side]) T[varSideStyle] = "filter: " + window.colorNameTranslate(colour.csstext, "hue");
 			}
 		}
 	}
-- 
GitLab


From b26a6e42a7961b624fe67a800c656ff0a2e9f0ef Mon Sep 17 00:00:00 2001
From: TonyFox <the_tonyfox@yahoo.com>
Date: Thu, 13 Oct 2022 09:53:24 -0400
Subject: [PATCH 3/5] base

---
 game/03-JavaScript/base.js | 54 +++++++++++++++++++++++++-------------
 1 file changed, 36 insertions(+), 18 deletions(-)

diff --git a/game/03-JavaScript/base.js b/game/03-JavaScript/base.js
index bffef298be..f8e6727e3f 100644
--- a/game/03-JavaScript/base.js
+++ b/game/03-JavaScript/base.js
@@ -85,8 +85,9 @@ Macro.add("twinescript", {
  * Can iterate over
  *
  * Copied from SugarCube sources.
- * @param range
- * @param {function(key,value):void} handler
+ *
+ * @param {any} range Can be String or Object.
+ * @param {function(string,any):void} handler Function for each key and value pair.
  */
 function rangeIterate(range, handler) {
 	let list;
@@ -123,7 +124,12 @@ function rangeIterate(range, handler) {
 window.rangeIterate = rangeIterate;
 
 /**
- * Define macro, passing arguments to function and store them in $args, preserving & restoring previous $args
+ * Define macro, passing arguments to function and store them in $args, preserving & restoring previous $args.
+ *
+ * @param {string} macroName
+ * @param {Function} macroFunction
+ * @param {object} tags
+ * @param {boolean} skipArgs
  */
 function DefineMacro(macroName, macroFunction, tags, skipArgs) {
 	Macro.add(macroName, {
@@ -149,7 +155,15 @@ function DefineMacro(macroName, macroFunction, tags, skipArgs) {
 }
 
 /**
- * Define macro, where macroFunction returns text to wikify & print
+ * Define macro, passing arguments to function and store them in $args, preserving & restoring previous $args.
+ *
+ * Expectation: macroFunction returns text to wikify & print.
+ *
+ * @param {string} macroName
+ * @param {Function} macroFunction
+ * @param {object} tags
+ * @param {boolean} skipArgs
+ * @param {boolean} maintainContext
  */
 function DefineMacroS(macroName, macroFunction, tags, skipArgs, maintainContext) {
 	DefineMacro(
@@ -163,8 +177,10 @@ function DefineMacroS(macroName, macroFunction, tags, skipArgs, maintainContext)
 }
 
 /**
- * @param worn clothing article, State.variables.worn.XXXX
- * @param slot clothing article slot used
+ * Creates and returns the keyword describing the integrity of a clothing article.
+ *
+ * @param {object} worn clothing article, State.variables.worn.XXXX
+ * @param {string} slot clothing article slot used
  * @returns {string} condition key word ("tattered"|"torn|"frayed"|"full")
  */
 function integrityKeyword(worn, slot) {
@@ -182,8 +198,10 @@ function integrityKeyword(worn, slot) {
 window.integrityKeyword = integrityKeyword;
 
 /**
- * @param worn clothing article, State.variables.worn.XXXX
- * @param slot clothing article, State.variables.worn.XXXX
+ * Returns the integrity prefix of the clothing object.
+ *
+ * @param {object} worn clothing article, State.variables.worn.XXXX
+ * @param {string} slot clothing article slot used
  * @returns {string} printable integrity prefix
  */
 function integrityWord(worn, slot) {
@@ -244,7 +262,7 @@ function faceintegrity() {
 DefineMacroS("faceintegrity", faceintegrity);
 
 /**
- * @param worn clothing article, State.variables.worn.XXXX
+ * @param {object} worn clothing article, State.variables.worn.XXXX
  * @returns {string} printable clothing colour
  */
 function clothesColour(worn) {
@@ -278,15 +296,12 @@ function outfitChecks() {
 window.outfitChecks = outfitChecks;
 
 /**
- * @return {boolean} whether or not any main-body clothing is out of place or wet
+ * @returns {boolean} whether or not any main-body clothing is out of place or wet
  */
 function checkForExposedClothing() {
 	return setup.clothingLayer.torso.some(clothingLayer => {
 		const wetstage = V[clothingLayer.replace("_", "") + "wetstage"];
-		return (
-			V.worn[clothingLayer].state !== setup.clothes[clothingLayer][clothesIndex(clothingLayer, V.worn[clothingLayer])].state_base ||
-			wetstage >= 3
-		);
+		return V.worn[clothingLayer].state !== setup.clothes[clothingLayer][clothesIndex(clothingLayer, V.worn[clothingLayer])].state_base || wetstage >= 3;
 	});
 }
 window.checkForExposedClothing = checkForExposedClothing;
@@ -607,11 +622,14 @@ Macro.add("foldout", {
 });
 
 /**
- * @returns 30 for November, 31 for December, 28 for February (29 if leap year), et cetera 
- * Uses current in-game month and year when no arguments provided
+ * If no arguments provided, uses in-game current month and year.
+ *
+ * @param {number} month
+ * @param {number} year
+ * @returns {number} 30 for November, 31 for December, 28 for February (29 if leap year), et cetera.
  */
 function getLastDayOfMonth(month, year) {
-    let monthNumber = new Date(Date.parse((month || V.month) + ' 1 ' + (year || V.year))).getMonth() + 1;
-    return new Date((year || V.year), monthNumber, 0).getDate();
+	const monthNumber = new Date(Date.parse((month || V.month) + " 1 " + (year || V.year))).getMonth() + 1;
+	return new Date(year || V.year, monthNumber, 0).getDate();
 }
 window.getLastDayOfMonth = getLastDayOfMonth;
-- 
GitLab


From 1227b666c8d09c5ed8690357c963a2fa381636cc Mon Sep 17 00:00:00 2001
From: TonyFox <the_tonyfox@yahoo.com>
Date: Wed, 9 Nov 2022 10:48:51 -0500
Subject: [PATCH 4/5] ingame - enable dot-notation alerts - disable no-undef
 and prettier - - Prettier disabled as many layouts are by design - - - and I
 don't want to change those layouts - most all changes are formatting and line
 length - A few improvements to match 2019 - Convert non-printing MacroS's to
 Macro's - correct JSDocs formatting - Add some JSDocs and a few comments -
 Improve DefaultActions.addMany - drastically improve getTimeString and
 msToTime funcs

---
 game/03-JavaScript/ingame.js | 356 ++++++++++++++++-------------------
 1 file changed, 162 insertions(+), 194 deletions(-)

diff --git a/game/03-JavaScript/ingame.js b/game/03-JavaScript/ingame.js
index cc604b972b..7256341f59 100644
--- a/game/03-JavaScript/ingame.js
+++ b/game/03-JavaScript/ingame.js
@@ -1,4 +1,5 @@
-/* eslint-disable dot-notation */
+/* eslint-disable prettier/prettier */
+/* eslint-disable no-undef */
 function mapMove(moveTo) {
 	const currentPassage = V.passage;
 	const destinationTable = [];
@@ -11,10 +12,7 @@ function mapMove(moveTo) {
 	const available = V.map.available;
 
 	// if(V.debug == 1 || available[currentPassage].includes(moveTo))
-	if (
-		V.debug === 1 ||
-		(available[currentPassage].includes(moveTo) && destinationTable.includes(moveTo))
-	) {
+	if (V.debug === 1 || (available[currentPassage].includes(moveTo) && destinationTable.includes(moveTo))) {
 		Wikifier.wikifyEval("<<pass 5>>");
 		SugarCube.State.display(moveTo);
 	}
@@ -35,12 +33,8 @@ function shopClothingFilterToggleTrait(trait) {
 window.shopClothingFilterToggleTrait = shopClothingFilterToggleTrait;
 
 function shopClothingFilterSortOnDescription(traitOne, traitTwo) {
-	const descriptionOne = Wikifier.wikifyEval(
-		`<<shopTraitDescription ${traitOne}>>`
-	).textContent.trim();
-	const descriptionTwo = Wikifier.wikifyEval(
-		`<<shopTraitDescription ${traitTwo}>>`
-	).textContent.trim();
+	const descriptionOne = Wikifier.wikifyEval(`<<shopTraitDescription ${traitOne}>>`).textContent.trim();
+	const descriptionTwo = Wikifier.wikifyEval(`<<shopTraitDescription ${traitTwo}>>`).textContent.trim();
 
 	return descriptionOne > descriptionTwo;
 }
@@ -66,33 +60,19 @@ window.wikifier2 = function (str) {
 function actionsreplace(bodypart) {
 	const check = bodypart + "target";
 	if (V[check] === "tentacles") {
-		Wikifier.wikifyEval(
-			"<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitTentacle>><</replace>>"
-		);
+		Wikifier.wikifyEval("<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitTentacle>><</replace>>");
 	} else if (V[check] === "swarm") {
-		Wikifier.wikifyEval(
-			"<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitSwarm>><</replace>>"
-		);
+		Wikifier.wikifyEval("<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitSwarm>><</replace>>");
 	} else if (V[check] === "vore") {
-		Wikifier.wikifyEval(
-			"<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitVore>><</replace>>"
-		);
+		Wikifier.wikifyEval("<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitVore>><</replace>>");
 	} else if (V[check] === "struggle") {
-		Wikifier.wikifyEval(
-			"<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitStruggle>><</replace>>"
-		);
+		Wikifier.wikifyEval("<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitStruggle>><</replace>>");
 	} else if (V[check] === "machine") {
-		Wikifier.wikifyEval(
-			"<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitMachine>><</replace>>"
-		);
+		Wikifier.wikifyEval("<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitMachine>><</replace>>");
 	} else if (V[check] === "self") {
-		Wikifier.wikifyEval(
-			"<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitSelf>><</replace>>"
-		);
+		Wikifier.wikifyEval("<<replace #" + bodypart + "action>><<" + bodypart + "ActionInitSelf>><</replace>>");
 	} else {
-		Wikifier.wikifyEval(
-			"<<replace #" + bodypart + "action>><<" + bodypart + "ActionInit>><</replace>>"
-		);
+		Wikifier.wikifyEval("<<replace #" + bodypart + "action>><<" + bodypart + "ActionInit>><</replace>>");
 	}
 }
 window.actionsreplace = actionsreplace;
@@ -124,7 +104,7 @@ const combatActionColours = {
 		meek: [
 			/* leftaction or rightaction */
 			"behind", "fold", "leftcovervaginameek", "leftcoverpenismeek", "leftcoveranusmeek", "rightcovervaginameek", "rightcoverpenismeek", "rightcoveranusmeek", "leftprotect", "rightprotect", "leftgrip", "rightgrip", "leftcurl", "rightcurl", "pickupSexToy", "leftcamerapose", "rightcamerapose",
-			/*feetaction*/
+			/* feetaction */
 			"strut",
 			/* mouthaction */
 			"grasp", "plead", "forgive", "down", "letout", "letoutorgasm", "noises", "pay",
@@ -286,7 +266,7 @@ window.combatListColor = combatListColor;
 DefineMacroS("combatListColor", combatListColor);
 
 function combatButtonAdjustments(name, extra) {
-	jQuery(document).on("change", "#listbox-" + name, { name: name, extra: extra }, function (e) {
+	jQuery(document).on("change", "#listbox-" + name, { name, extra }, function (e) {
 		/* console.log(e.data); */
 		Wikifier.wikifyEval("<<replace #" + e.data.name + "Difficulty>><<" + e.data.name + "Difficulty" + e.data.extra + ">><</replace>>");
 		$("#" + e.data.name + "Select").removeClass("whiteList bratList meekList defList subList");
@@ -298,7 +278,7 @@ DefineMacroS("combatButtonAdjustments", combatButtonAdjustments);
 
 function combatDefaults() {
 	jQuery(document).on("change", "#listbox--defaultoption", function (e) {
-		Wikifier.wikifyEval('<<replace #othersFeelings>><<othersFeelings ' + this.value + '>><</replace>>');
+		Wikifier.wikifyEval("<<replace #othersFeelings>><<othersFeelings " + this.value + ">><</replace>>");
 	});
 	return "";
 }
@@ -365,38 +345,38 @@ function combatSkillCheck(skillname, targetid = 0, basedifficulty = 1000, multip
 window.combatSkillCheck = combatSkillCheck;
 
 function hairdressersReset() {
-	$(() => $("#hairDressers").on("change", ".macro-listbox", function (e) {
-		Wikifier.wikifyEval("<<replace #hairDressers>><<hairDressersOptions>><</replace>>");
-		Wikifier.wikifyEval("<<replace #currentCost>>To pay: £<<print _currentCost / 100>><</replace>>");
-	}));
-	return "";
+	$(() =>
+		$("#hairDressers").on("change", ".macro-listbox", function (e) {
+			Wikifier.wikifyEval("<<replace #hairDressers>><<hairDressersOptions>><</replace>>");
+			Wikifier.wikifyEval("<<replace #currentCost>>To pay: £<<print _currentCost / 100>><</replace>>");
+		})
+	);
 }
-DefineMacroS("hairdressersReset", hairdressersReset);
+DefineMacro("hairdressersReset", hairdressersReset);
 
 function hairdressersResetAlt() {
-	$(() => $("#hairDressersSydney").on("click", ".macro-cycle", function (e) {
-		Wikifier.wikifyEval("<<replace #hairDressersSydney>><<hairDressersOptionsSydney>><</replace>>");
-		Wikifier.wikifyEval("<<replace #currentCost>>To pay: £<<print _currentCost / 100>><</replace>>");
-	}));
-	return "";
+	$(() =>
+		$("#hairDressersSydney").on("click", ".macro-cycle", function (e) {
+			Wikifier.wikifyEval("<<replace #hairDressersSydney>><<hairDressersOptionsSydney>><</replace>>");
+			Wikifier.wikifyEval("<<replace #currentCost>>To pay: £<<print _currentCost / 100>><</replace>>");
+		})
+	);
 }
-DefineMacroS("hairdressersResetAlt", hairdressersResetAlt);
+DefineMacro("hairdressersResetAlt", hairdressersResetAlt);
 
 function browsDyeReset() {
 	jQuery(document).on("change", "#listbox-browsdyeoption", function (e) {
 		Wikifier.wikifyEval("<<replace #browsColourPreview>><<browsColourPreview>><</replace>>");
 	});
-	return "";
 }
-DefineMacroS("browsDyeReset", browsDyeReset);
+DefineMacro("browsDyeReset", browsDyeReset);
 
 function NPCSettingsReset() {
 	jQuery(".passage").on("change", "#listbox--npcid", function (e) {
 		Wikifier.wikifyEval("<<replace #npcSettingsMenu>><<npcSettingsMenu>><</replace>>");
 	});
-	return "";
 }
-DefineMacroS("NPCSettingsReset", NPCSettingsReset);
+DefineMacro("NPCSettingsReset", NPCSettingsReset);
 
 function loveInterestFunction() {
 	jQuery(document).on("change", "#listbox-loveinterestprimary", function (e) {
@@ -405,9 +385,8 @@ function loveInterestFunction() {
 	jQuery(document).on("change", "#listbox-loveinterestsecondary", function (e) {
 		Wikifier.wikifyEval("<<replace #loveInterest>><<loveInterest>><</replace>>");
 	});
-	return "";
 }
-DefineMacroS("loveInterestFunction", loveInterestFunction);
+DefineMacro("loveInterestFunction", loveInterestFunction);
 
 function between(x, min, max) {
 	return x >= min && x <= max;
@@ -423,13 +402,9 @@ function featsPointsMenuReset() {
 DefineMacroS("featsPointsMenuReset", featsPointsMenuReset);
 
 function startingPlayerImageReset() {
-	jQuery(document).on(
-		"change",
-		"#settingsDiv .macro-radiobutton,#settingsDiv .macro-numberslider,#settingsDiv .macro-checkbox",
-		() => {
-			Wikifier.wikifyEval("<<startingPlayerImageUpdate>>");
-		}
-	);
+	jQuery(document).on("change", "#settingsDiv .macro-radiobutton,#settingsDiv .macro-numberslider,#settingsDiv .macro-checkbox", () => {
+		Wikifier.wikifyEval("<<startingPlayerImageUpdate>>");
+	});
 	return "";
 }
 DefineMacroS("startingPlayerImageReset", startingPlayerImageReset);
@@ -484,7 +459,7 @@ function nCr(n, r) {
 	// https://stackoverflow.com/questions/11809502/which-is-better-way-to-calculate-ncr
 	if (r > n - r) {
 		// because C(n, r) == C(n, n - r)
-		r = n - r
+		r = n - r;
 	}
 
 	let ans = 1;
@@ -508,19 +483,19 @@ window.nCr = nCr;
  *   the chance that they will see at least 1 card at the start of a round is calculateMarkedChance(52, 8, 3, 1) = 0.4 (which means they'll see at least 1 marked card in 40% of their games,
  *   the first round at least).
  * If that's too high of a chance, we could, for example, decrease their depth by 1, or decrease the max marked count by 2.
- *   calculateMarkedChance(52, 8, 2, 1) = 0.28, so 28%, and calculateMarkedChance(52, 6, 3, 1) = 0.31, so 31%.
+ *   Thus, calculateMarkedChance(52, 8, 2, 1) = 0.28, so 28%, and calculateMarkedChance(52, 6, 3, 1) = 0.31, so 31%.
  *
  * Arguably, seeing what card is third from the top is also less useful than being able to more consistently see the top card or the dealer's hole card, so
  *   it's worth assuming the REAL depth is 1/2 even if the value is passed as 3 (so while debugging, always also calculate with depth=1 or depth=2 and see if the number still seems fair).
  *
  * Also, note that the dealer's hole card is included in the depth. This means that if the depth is 3, then it calculates the chance the player will either see one of the top 2 cards or the dealer's second card.
  *
- * @param {Number} deckCount  The number of cards in the deck
- * @param {Number} markedCount  The number of cards the player has (or can) mark in a deck
- * @param {Number} depth  How many cards the player can see from the top of the deck (including the dealer's hole card) (to identify if they're marked or not)
- * @param {Number} atLeast  At least how many cards the player will see (from <depth> cards from the top of the deck)
- * @param {Boolean} doLog = false, if true - logs the steps of the solution
- * @returns
+ * @param {number} deckCount  The number of cards in the deck.
+ * @param {number} markedCount  The number of cards the player has (or can) mark in a deck.
+ * @param {number} depth  How many cards the player can see from the top of the deck (including the dealer's hole card) (to identify if they're marked or not).
+ * @param {number} atLeast  At least how many cards the player will see (from <depth> cards from the top of the deck).
+ * @param {boolean} doLog = false, if true - logs the steps of the solution.
+ * @returns {number} Percentage chance.
  */
 function calculateMarkedChance(deckCount, markedCount, depth, atLeast, doLog = false) {
 	// we calculate how many possible ways we can pull DEPTH amount of cards from the deck (and put them in the front of the deck)
@@ -531,41 +506,28 @@ function calculateMarkedChance(deckCount, markedCount, depth, atLeast, doLog = f
 			logMessages.push(m);
 		}
 	};
-	log(
-		`DEPTH=${depth} cards can be placed in front of the deck from a deck of ${deckCount} cards in ${deckCount}c${depth} = ${totalEvents} ways.`
-	);
+	log(`DEPTH=${depth} cards can be placed in front of the deck from a deck of ${deckCount} cards in ${deckCount}c${depth} = ${totalEvents} ways.`);
 
 	let favorableEvents = 0;
 
 	// as per the algorithm, we go from how many marked cards we need at the very least, to either how many marked cards there are, or to how deep we can go (whichever is the limit)
 	const possibleMarkedCardsVisibleLimit = Math.min(markedCount, depth);
-	for (
-		let nMarkedPicked = atLeast;
-		nMarkedPicked <= possibleMarkedCardsVisibleLimit;
-		++nMarkedPicked
-	) {
+	for (let nMarkedPicked = atLeast; nMarkedPicked <= possibleMarkedCardsVisibleLimit; ++nMarkedPicked) {
 		// we calculate how many possible ways we can pull a valid number of marked cards from the deck
 		//   by dividing the cards into a pool of
 		//    * marked cards (and calculating how many ways we can pull the valid nMarkedPicked cards from the pool of markedCount marked cards),  nCr(markedCount, nMarkedPicked)
 		//    * unmarked cards (and calculating how many ways we can pull the remaining possibleMarkedCardsVisibleLimit-nMarkedPicked non-marked cards from the pool of deck-markedCount unmarked cards), ncr(deck-markedCount, possibleMarkedCardsVisibleLimit-nMarkedPicked)
 		//   and then we multiply the mutually exclusive combinations to get all possible combinations (cross-joins) of the two (since for each way we can pull (say) 1 marked card, there's the second number of ways we can pull the remaining non marked ones)
 		const markedPoolWays = nCr(markedCount, nMarkedPicked);
-		const unmarkedPoolWays = nCr(
-			deckCount - markedCount,
-			possibleMarkedCardsVisibleLimit - nMarkedPicked
-		);
+		const unmarkedPoolWays = nCr(deckCount - markedCount, possibleMarkedCardsVisibleLimit - nMarkedPicked);
 		const totalWays = markedPoolWays * unmarkedPoolWays;
 		favorableEvents += totalWays;
 		// log(`One marked card can be picked from MARKED=1 cards in 1c1 = 1 ways, and the remaining three (which are not marked) can be picked from DECK=5-MARKED=1 = 4 cards in 4c3 = 4 ways.
 		log(
 			`${nMarkedPicked} marked cards can be picked from MARKED=${markedCount} cards in ${markedCount}c${nMarkedPicked} = ${markedPoolWays} ways,` +
-				`and the remaining ${
+				`and the remaining ${deckCount - markedCount} (which are not marked) can be picked from DECK=${deckCount}-MARKED=${markedCount} = ${
 					deckCount - markedCount
-				} (which are not marked) can be picked from DECK=${deckCount}-MARKED=${markedCount} = ${
-					deckCount - markedCount
-				} cards in ${deckCount - markedCount}c${
-					possibleMarkedCardsVisibleLimit - nMarkedPicked
-				} = ${unmarkedPoolWays} ways.\n` +
+				} cards in ${deckCount - markedCount}c${possibleMarkedCardsVisibleLimit - nMarkedPicked} = ${unmarkedPoolWays} ways.\n` +
 				`${markedPoolWays}*${unmarkedPoolWays} = ${totalWays} ways.`
 		);
 	}
@@ -629,14 +591,15 @@ function getRobinLocation() {
 		T.robin_location = V.robinlocationoverride.location;
 	} else if (["docks", "landfill", "dinner", "pillory"].includes(V.robinmissing)) {
 		T.robin_location = V.robinmissing;
-	} else if (!between(V.hour, 7, 20)){ //if hour is 6 or lower, or 21 or higher
+	} else if (!between(V.hour, 7, 20)) {
+		// if hour is 6 or lower, or 21 or higher.
 		T.robin_location = "sleep";
 	} else if (V.schoolday === 1 && between(V.hour, 8, 15)) {
 		T.robin_location = "school";
 	} else if (V.halloween === 1 && between(V.hour, 16, 18) && V.monthday === 31) {
 		T.robin_location = "halloween";
 	} else if ((V.weekday === 7 || V.weekday === 1) && between(V.hour, 9, 16) && C.npc.Robin.trauma < 80) {
-		T.robin_location = (V.season === "winter" ? "park" : "beach");
+		T.robin_location = V.season === "winter" ? "park" : "beach";
 	} else {
 		T.robin_location = "orphanage";
 	}
@@ -721,7 +684,7 @@ window.DefaultActions = {
 		if (storage === undefined) {
 			return;
 		}
-		if (storage["consensual"] === undefined || storage["rape"] === undefined) {
+		if (storage.consensual === undefined || storage.rape === undefined) {
 			storage = this.create(true, true);
 		}
 		return storage;
@@ -810,15 +773,16 @@ window.DefaultActions = {
 		to.value[type][person][part].pushUnique(action);
 	},
 	addMany(type, person, part, actions, to = { value: V.actionDefaults }) {
-		const filteredActions = actions.map(action => {
-			if (part === "regrab") {
-				return action ? 1 : 0;
-			}
-			if (action !== "rest") {
-				return action;
-			}
-		});
-		if (filteredActions.length <= 0) {
+		// This function should take a list of actions, for a given type (rape/consensual)
+		// and a given person (submissive, defiant, tentacles?)
+		// and a given part (leftaction, rightaction, etc.. down to regrab?)
+		// and add them to the actionDefaults.
+
+		// This filters actions down to any actions that aren't "rest",
+		// or where part is regrab -> truthy/falsy value of each action.
+		const filteredActions = part === "regrab" ? actions.map(action => !!action) : actions.filter(action => action !== "rest");
+
+		if (!filteredActions.length) {
 			return;
 		}
 		if (to.value[type][person] === undefined) {
@@ -832,11 +796,7 @@ window.DefaultActions = {
 		});
 	},
 	get(type, person, part, from = V.actionDefaults, defaultValue = "rest") {
-		if (
-			from[type] === undefined ||
-			from[type][person] === undefined ||
-			from[type][person][part] === undefined
-		) {
+		if (from[type] === undefined || from[type][person] === undefined || from[type][person][part] === undefined) {
 			return [defaultValue];
 		}
 		return from[type][person][part];
@@ -878,7 +838,7 @@ window.DefaultActions = {
 };
 
 function selectWardrobe(targetLocation = V.wardrobe_location) {
-	return ((!targetLocation || targetLocation === "wardrobe" || !V.wardrobes[targetLocation]) ? V.wardrobe : V.wardrobes[targetLocation]);
+	return (!targetLocation || targetLocation === "wardrobe" || !V.wardrobes[targetLocation]) ? V.wardrobe : V.wardrobes[targetLocation];
 }
 window.selectWardrobe = selectWardrobe;
 
@@ -1046,7 +1006,7 @@ function clothingInStorage(loc) {
 	for (const slot of setup.clothingLayer.all) {
 		const item = V.store[slot].find(item => item.location === loc);
 		if (item && !item.outfitSecondary) {
-			item["slot"] = slot;
+			item.slot = slot;
 			clothing.push(item);
 		}
 	}
@@ -1081,7 +1041,10 @@ function isConnectedToHood(slot) {
 	if (V.worn[slot].outfitSecondary && V.worn[slot].outfitSecondary[1] !== "broken") {
 		slot = V.worn[slot].outfitSecondary[0];
 	}
-	if (V.worn[slot].hoodposition && (V.worn[slot].hoodposition === "down" || (V.worn[slot].hoodposition === "up" && V.worn[slot].outfitPrimary.head !== "broken" && V.worn.head.hood === 1))){
+	if (
+		V.worn[slot].hoodposition &&
+		(V.worn[slot].hoodposition === "down" || (V.worn[slot].hoodposition === "up" && V.worn[slot].outfitPrimary.head !== "broken" && V.worn.head.hood === 1))
+	) {
 		return true;
 	}
 	return false;
@@ -1099,13 +1062,9 @@ function clothesIndex(slot, itemToIndex) {
 		});
 		return 0;
 	}
-	let index = setup.clothes[slot].findIndex(
-		item => item.variable === itemToIndex.variable && item.modder === itemToIndex.modder
-	);
+	let index = setup.clothes[slot].findIndex(item => item.variable === itemToIndex.variable && item.modder === itemToIndex.modder);
 	if (index === -1) {
-		console.log(
-			`clothesIndex - ${slot} clothing item index not found for the '${itemToIndex.name}' with the modder set to '${itemToIndex.modder}'`
-		);
+		console.log(`clothesIndex - ${slot} clothing item index not found for the '${itemToIndex.name}' with the modder set to '${itemToIndex.modder}'`);
 		/* try and correct .modder mismatches */
 		const matches = setup.clothes[slot].filter(item => item.variable === itemToIndex.variable);
 		if (matches.length === 1) {
@@ -1135,26 +1094,13 @@ function currentSkillValue(skill) {
 	}
 	if (
 		[
-			"skulduggery",
-			"physique",
-			"danceskill",
-			"swimmingskill",
-			"athletics",
-			"willpower",
-			"tending",
-			"science",
-			"maths",
-			"english",
-			"history",
+			"skulduggery", "physique", "danceskill", "swimmingskill", "athletics", "willpower", "tending", "science", "maths", "english", "history",
 		].includes(skill) &&
 		V.moorLuck > 0
 	) {
 		result = Math.floor(result * (1 + V.moorLuck / 100));
 	}
-	if (
-		["physique", "danceskill", "swimmingskill", "athletics"].includes(skill) &&
-		playerBellySize() >= 10
-	) {
+	if (["physique", "danceskill", "swimmingskill", "athletics"].includes(skill) && playerBellySize() >= 10) {
 		switch (V.pregnancyStats.mother) {
 			case 0:
 				T.pregnancyModifier = 30;
@@ -1172,9 +1118,7 @@ function currentSkillValue(skill) {
 				T.pregnancyModifier = 100;
 				break;
 		}
-		result = Math.floor(
-			result * (1 - playerBellySize() / T.pregnancyModifier)
-		);
+		result = Math.floor(result * (1 - playerBellySize() / T.pregnancyModifier));
 	}
 	let modifier = 1;
 	switch (skill) {
@@ -1238,7 +1182,7 @@ function currentSkillValue(skill) {
 			if (V.worn.feet.type.includes("shackle")) result /= 10;
 			break;
 		case "willpower":
-			if (V.parasite.left_ear.name === V.parasite.right_ear.name === "slime") {
+			if (V.parasite.left_ear.name === V.parasite.right_ear.name && V.parasite.left_ear.name === "slime") {
 				result = Math.floor(result * 0.9);
 			}
 			break;
@@ -1253,26 +1197,17 @@ function currentSkillValue(skill) {
 window.currentSkillValue = currentSkillValue;
 
 function playerIsPenetrated() {
-	return [V.mouthstate, V.vaginastate, V.anusstate].some(s =>
-		["penetrated", "doublepenetrated", "tentacle", "tentacledeep"].includes(s)
-	);
+	return [V.mouthstate, V.vaginastate, V.anusstate].some(s => ["penetrated", "doublepenetrated", "tentacle", "tentacledeep"].includes(s));
 }
 window.playerIsPenetrated = playerIsPenetrated;
 
 function getTimeString(minutes = 0) {
-	if (minutes < 0) {
-		// come on don't try negative numbers, that's silly
-		return "0:00";
-	}
-	if (minutes < 10) {
-		return "0:0" + minutes;
-	} else if (minutes < 60) {
-		return "0:" + minutes;
-	} else {
-		const hours = Math.trunc(minutes / 60);
-		minutes = ("" + (minutes % 60)).padStart(2, "0");
-		return hours + ":" + minutes;
-	}
+	minutes = minutes > 0 ? minutes : 0;
+	// Doing it this way instead of Math.min to enforce number if an invalid (string) passed in
+	const hours = Math.trunc(minutes / 60);
+	const mins = (minutes % 60).toString().padStart(2, "0");
+
+	return (hours || 0) + ":" + mins;
 }
 window.getTimeString = getTimeString;
 
@@ -1318,9 +1253,7 @@ function npcSpecifiedClothes(npc, name) {
 	if (clothingItem.length > 0) {
 		npcEquipSet(npc, clothingItem[0]);
 	} else {
-		console.log(
-			`npcSpecifiedClothes - unable to find a clothing item with the name '${name}' for '${npc.fullDescription}'`
-		);
+		console.log(`npcSpecifiedClothes - unable to find a clothing item with the name '${name}' for '${npc.fullDescription}'`);
 	}
 }
 window.npcSpecifiedClothes = npcSpecifiedClothes;
@@ -1336,9 +1269,7 @@ function npcClothes(npc, type) {
 
 	if (crossdressing < 2) gender.push(npc.pronoun);
 	if (crossdressing > 0) gender.push(npc.pronoun === "m" ? "f" : "m");
-	let clothingOptions = setup.npcClothesSets.filter(
-		set => (set.type === type || !type) && gender.includes(set.gender)
-	);
+	let clothingOptions = setup.npcClothesSets.filter(set => (set.type === type || !type) && gender.includes(set.gender));
 
 	if (npc.outfits) {
 		const namedNpcClothing = clothingOptions.filter(set => npc.outfits.includes(set.name));
@@ -1352,9 +1283,7 @@ function npcClothes(npc, type) {
 		// Allows you to record the clothing set selected
 		return clothesSet.name;
 	} else {
-		console.log(
-			`npcClothes - unable to find a clothing set with the options for '${npc.fullDescription}' with type '${type}'`
-		);
+		console.log(`npcClothes - unable to find a clothing set with the options for '${npc.fullDescription}' with type '${type}'`);
 	}
 }
 window.npcClothes = npcClothes;
@@ -1385,24 +1314,27 @@ function getMoonState() {
 	let moonstate = V.moonstate;
 
 	if (nightstate === "evening") {
-		if (V.monthday === getLastDayOfMonth()) { // blood moon happens on the last night of the month
+		if (V.monthday === getLastDayOfMonth()) {
+			// blood moon happens on the last night of the month
 			moonstate = "evening";
 		} else {
 			moonstate = 0; // moonstate will stay "morning" until the night after the blood moon
 		}
 	} else if (nightstate === "morning") {
-		if (V.monthday === 1) { // blood moon happens on the first morning of the month
+		if (V.monthday === 1) {
+			// blood moon happens on the first morning of the month
 			moonstate = "morning";
 		} else {
 			moonstate = 0;
 		}
 	}
-	return V.moonstate = moonstate;
+	V.moonstate = moonstate;
+	return moonstate;
 }
 window.getMoonState = getMoonState;
 
 function isBloodmoon() {
-	return (V.daystate === "night" && getMoonState() === T.nightstate); // it's only a blood moon if it's night, and the current moon state matches the current night state
+	return V.daystate === "night" && getMoonState() === T.nightstate; // it's only a blood moon if it's night, and the current moon state matches the current night state
 }
 window.isBloodmoon = isBloodmoon;
 
@@ -1434,10 +1366,13 @@ window.fameSum = fameSum;
 
 function checkTFparts() {
 	const tfParts = {};
-	Object.entries(V.transformationParts).forEach(([tfName,tf]) => /* Iterate over each transformation */
-		Object.entries(tf).forEach(([pName, pStatus]) => { /* Iterate over each part of each transformation */
-			if (pStatus !== "disabled" && pStatus !== "hidden"){ /* Filter out the parts that the player doesn't have or is suppressing */
-				tfParts[tfName+pName.toUpperFirst()] = true; /* Assign properties with camelCase names for each tf part that is visible */
+	// Iterate over each transformation
+	Object.entries(V.transformationParts).forEach(([tfName, tf]) =>
+		Object.entries(tf).forEach(([pName, pStatus]) => {
+			/* Iterate over each part of each transformation */
+			if (pStatus !== "disabled" && pStatus !== "hidden") {
+				/* Filter out the parts that the player doesn't have or is suppressing */
+				tfParts[tfName + pName.toUpperFirst()] = true; /* Assign properties with camelCase names for each tf part that is visible */
 			}
 		})
 	);
@@ -1460,11 +1395,16 @@ function getSexesFromRandomGroup() {
 }
 window.getSexesFromRandomGroup = getSexesFromRandomGroup;
 
+/**
+ * Pick the right colour to use when colouring various things.  Primarily sidebar stats.
+ * When using this function, try to keep in mind what value of your input variable you want "red" to be at.
+ *
+ * Example: $drugged goes higher than 500, but we want the bar to become red at 500, so we call this function as getColourClassFromPercentage($drugged / 5).
+ *
+ * @param {number} percentage The percentage of the desired bar colour.
+ * @returns {string} Colour name to use.
+ */
 function getColourClassFromPercentage(percentage) {
-	/* This function is for picking the right color to use when coloring various things, primarily the sidebar stats. */
-	/* When using this function, try to keep in mind what value of your input variable you want "red" to be at.
-	 * Example: $drugged goes higher than 500, but we want the bar to become red at 500, so we call this function as getColourClassFromPercentage($drugged / 5).
-	*/
 	if (percentage <= 0) return "green";
 	if (percentage < 20) return "teal";
 	if (percentage < 40) return "lblue";
@@ -1475,11 +1415,18 @@ function getColourClassFromPercentage(percentage) {
 }
 window.getColourClassFromPercentage = getColourClassFromPercentage;
 
+/**
+ * Determine whether the player and the given NPC can legitimately breed together.
+ * Accepts either the named NPC's name, or an NPC object from NPCList or NPCName.
+ *
+ * Examples: playerCanBreedWith("Kylar"), or playerCanBreedWith($NPCList[0]) or playerCanBreedWith($NPCName[$NPCNameList.indexOf("Kylar")]).
+ *
+ * Note that if the npc argument is invalid (wrong name, non-existent object) it will return false, so be careful of silent failures.
+ *
+ * @param {any} npc String or Object as detailed above.
+ * @returns {boolean} Whether the given NPC is compatible to breed with the player.
+ */
 function playerCanBreedWith(npc) {
-	/* This function can accept either a named NPC's name, or an NPC object from either NPCList or NPCName.
-	 * Examples: playerCanBreedWith("Kylar"), or playerCanBreedWith($NPCList[0]) or playerCanBreedWith($NPCName[$NPCNameList.indexOf("Kylar")])
-	 * Returns true or false. If you give it garbage, like a totally wrong name, it'll return false, so be careful about silent failures like that.
-	 */
 	if (typeof npc === "string") npc = V.NPCName[V.NPCNameList.indexOf(npc)];
 
 	return (V.player.vaginaExist && npc.penis !== "none") || (V.player.penisExist && npc.vagina !== "none");
@@ -1496,7 +1443,11 @@ function outfitHoodPosition(outfit) {
 	if (outfit.head !== hoodie.outfitPrimary.head) return "down";
 	if (!outfit.colors) return "up";
 	if (outfit.colors.head[0] !== outfit.colors.upper[0] || outfit.colors.head[1] !== outfit.colors.upper[1]) return "down";
-	if ((outfit.colors.headcustom && outfit.colors.headcustom[0] !== outfit.colors.uppercustom[0]) || (outfit.colors.headcustom && outfit.colors.headcustom[1] !== outfit.colors.uppercustom[1])) return "down";
+	if (
+		(outfit.colors.headcustom && outfit.colors.headcustom[0] !== outfit.colors.uppercustom[0]) ||
+		(outfit.colors.headcustom && outfit.colors.headcustom[1] !== outfit.colors.uppercustom[1])
+	)
+		return "down";
 	return "up";
 }
 window.outfitHoodPosition = outfitHoodPosition;
@@ -1507,20 +1458,36 @@ function combatCharacterShadow() {
 	const mainDiv = ".char_combat";
 
 	$(() => {
-		$(mainDiv).find('img').filter((i, n) => n.className.match(new RegExp("layer-(" + setup.shadowImage[V.position === "doggy" ? "doggy" : "missionary"].join("|") + ")( |$)", 'i')))
-		.clone(true).removeClass((i, n) => (n.match(/(^|\s)(colour|layer)-\S+/g) || []).join(' '))
-		.addClass(targetClass).removeAttr('style').appendTo($(mainDiv).last());
+		$(mainDiv)
+			.find("img")
+			.filter((i, n) =>
+				n.className.match(new RegExp("layer-(" + setup.shadowImage[V.position === "doggy" ? "doggy" : "missionary"].join("|") + ")( |$)", "i"))
+			)
+			.clone(true)
+			.removeClass((i, n) => (n.match(/(^|\s)(colour|layer)-\S+/g) || []).join(" "))
+			.addClass(targetClass)
+			.removeAttr("style")
+			.appendTo($(mainDiv).last());
 	});
 }
 window.combatCharacterShadow = combatCharacterShadow;
 
 /**
- * For usage with tears calculation, converts pain stat [0..200] to 0..4 range (maxes out at pain = 80)
+ * For usage with tears calculation, converts pain stat [0..200] to 0..4 range (maxes out at pain = 80).
+ *
+ * @param {number} pain Pain value.
+ * @returns {number} 0-4 range of tears amount.
  */
-const painToTearsLvl = (pain) => Math.floor(Math.clamp((pain || V.pain), 0, 99) / 20);
+const painToTearsLvl = pain => Math.floor(Math.clamp(pain || V.pain, 0, 99) / 20);
 window.painToTearsLvl = painToTearsLvl;
 
-const mascaraNameToCSS = (name) => nullable(setup.colours.mascara.find(x => x.variable === name)).csstext;
+/**
+ * Get the CSS Name for a mascara colour name.
+ *
+ * @param {string} name Name of the mascara colour.
+ * @returns {string} CSS Name "csstext" of the given colour.
+ */
+const mascaraNameToCSS = name => nullable(setup.colours.mascara.find(x => x.variable === name)).csstext;
 window.mascaraNameToCSS = mascaraNameToCSS;
 
 function isPubfameTaskAccepted(task, status) {
@@ -1528,20 +1495,16 @@ function isPubfameTaskAccepted(task, status) {
 }
 window.isPubfameTaskAccepted = isPubfameTaskAccepted;
 
-window.msToTime = (s) => {
-	function pad(n, z) {
-		z = z || 2;
-		return ('00' + n).slice(-z);
-	}
-	var ms = s % 1000;
-	s = (s - ms) / 1000;
-	var secs = s % 60;
-	s = (s - secs) / 60;
-	var mins = s % 60;
-	var hrs = (s - mins) / 60;
+function msToTime(s) {
+	s = Math.floor(s / 1000);
+	const secs = (s % 60).toString().padStart(2, "0");
+	s = Math.floor(s / 60);
+	const mins = (s % 60).toString().padStart(2, "0");
+	const hrs = Math.floor(s / 60);
 
-	return (hrs ? hrs : 0) + ':' + pad(mins) + ':' + pad(secs);
+	return (hrs || 0) + ":" + mins + ":" + secs;
 }
+window.msToTime = msToTime;
 
 function getHalloweenCostume() {
 	const upper = V.worn.upper;
@@ -1589,7 +1552,7 @@ function getHalloweenCostume() {
 		return "sailor";
 	} else if (upper.name === "skeleton outfit" && lower.name === "skeleton bottoms") {
 		return "skeleton";
-	
+
 	/* Transformations */
 	} else if (T.tf.angelHalo && T.tf.angelWings) {
 		return "angel TF";
@@ -1605,9 +1568,14 @@ function getHalloweenCostume() {
 		return "cow TF";
 	} else if (T.tf.birdWings && T.tf.birdEyes) {
 		return "harpy TF";
-	
+
 	/* Misc outcomes */
-	} else if (V.worn.upper.type.includes("costume") || V.worn.lower.type.includes("costume") || (V.worn.upper.type.includes("naked") && V.worn.under_upper.type.includes("costume")) || (V.worn.lower.type.includes("naked") && V.worn.under_lower.type.includes("costume"))) {
+	} else if (
+		V.worn.upper.type.includes("costume") ||
+		V.worn.lower.type.includes("costume") ||
+		(V.worn.upper.type.includes("naked") && V.worn.under_upper.type.includes("costume")) ||
+		(V.worn.lower.type.includes("naked") && V.worn.under_lower.type.includes("costume"))
+	) {
 		return "mixed";
 	} else if (V.exposed >= 2) {
 		return "fully naked";
-- 
GitLab


From a58ee2152f73e1a959fb17eee274cb904c7cad0f Mon Sep 17 00:00:00 2001
From: TonyFox <the_tonyfox@yahoo.com>
Date: Wed, 9 Nov 2022 11:19:10 -0500
Subject: [PATCH 5/5] save.js

---
 game/03-JavaScript/save.js | 60 +++++---------------------------------
 1 file changed, 8 insertions(+), 52 deletions(-)

diff --git a/game/03-JavaScript/save.js b/game/03-JavaScript/save.js
index f0ff26564b..6ed4f3fa96 100644
--- a/game/03-JavaScript/save.js
+++ b/game/03-JavaScript/save.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-undef */
 const DoLSave = ((Story, Save) => {
 	"use strict";
 
@@ -493,7 +494,7 @@ const importSettingsData = function (data) {
 		if (S.general != null) {
 			const listObject = settingsObjects("general");
 			const listKey = Object.keys(listObject);
-			const namedObjects = ["map", "skinColor", "shopDefaults","options"];
+			const namedObjects = ["map", "skinColor", "shopDefaults", "options"];
 			// correct swapped min/max values
 			if (S.general.breastsizemin > S.general.breastsizemax) {
 				const temp = S.general.breastsizemin;
@@ -536,11 +537,7 @@ const importSettingsData = function (data) {
 					// eslint-disable-next-line no-var
 					for (let j = 0; j < listKey.length; j++) {
 						// Overwrite to allow for "none" default value in the start passage to allow for rng to decide
-						if (
-							V.passage === "Start" &&
-							["pronoun", "gender"].includes(listKey[j]) &&
-							S.npc[V.NPCNameList[i]][listKey[j]] === "none"
-						) {
+						if (V.passage === "Start" && ["pronoun", "gender"].includes(listKey[j]) && S.npc[V.NPCNameList[i]][listKey[j]] === "none") {
 							V.NPCName[i][listKey[j]] = S.npc[V.NPCNameList[i]][listKey[j]];
 						} else if (validateValue(listObject[listKey[j]], S.npc[V.NPCNameList[i]][listKey[j]])) {
 							V.NPCName[i][listKey[j]] = S.npc[V.NPCNameList[i]][listKey[j]];
@@ -633,7 +630,7 @@ function exportSettings(data, type) {
 
 	listObject = settingsObjects("general");
 	listKey = Object.keys(listObject);
-	namedObjects = ["map", "skinColor", "shopDefaults","options"];
+	namedObjects = ["map", "skinColor", "shopDefaults", "options"];
 
 	for (let i = 0; i < listKey.length; i++) {
 		if (namedObjects.includes(listKey[i]) && V[listKey[i]] != null) {
@@ -696,20 +693,7 @@ function settingsObjects(type) {
 				mouthsensitivity: { min: 1, max: 3, decimals: 0, randomize: "characterTrait" },
 				bottomsensitivity: { min: 1, max: 3, decimals: 0, randomize: "characterTrait" },
 				eyeselect: {
-					strings: [
-						"purple",
-						"dark blue",
-						"light blue",
-						"amber",
-						"hazel",
-						"green",
-						"lime green",
-						"red",
-						"pink",
-						"grey",
-						"light grey",
-						"random",
-					],
+					strings: ["purple", "dark blue", "light blue", "amber", "hazel", "green", "lime green", "red", "pink", "grey", "light grey", "random"],
 					randomize: "characterAppearance",
 				},
 				hairselect: {
@@ -899,21 +883,7 @@ function settingsObjects(type) {
 				shopDefaults: {
 					alwaysBackToShopButton: { bool: true },
 					color: {
-						strings: [
-							"black",
-							"blue",
-							"brown",
-							"green",
-							"pink",
-							"purple",
-							"red",
-							"tangerine",
-							"teal",
-							"white",
-							"yellow",
-							"custom",
-							"random",
-						],
+						strings: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom", "random"],
 					},
 					colourItems: { strings: ["disable", "random", "default"] },
 					compactMode: { bool: true },
@@ -924,21 +894,7 @@ function settingsObjects(type) {
 					noHelp: { bool: true },
 					noTraits: { bool: true },
 					secColor: {
-						strings: [
-							"black",
-							"blue",
-							"brown",
-							"green",
-							"pink",
-							"purple",
-							"red",
-							"tangerine",
-							"teal",
-							"white",
-							"yellow",
-							"custom",
-							"random",
-						],
+						strings: ["black", "blue", "brown", "green", "pink", "purple", "red", "tangerine", "teal", "white", "yellow", "custom", "random"],
 					},
 				},
 			};
@@ -963,7 +919,7 @@ function settingsConvert(exportType, type, settings) {
 	const keys = Object.keys(listObject);
 	for (let i = 0; i < keys.length; i++) {
 		if (result[keys[i]] === undefined) continue;
-		if (["map", "skinColor", "player", "shopDefaults","options"].includes(keys[i])) {
+		if (["map", "skinColor", "player", "shopDefaults", "options"].includes(keys[i])) {
 			const itemKey = Object.keys(listObject[keys[i]]);
 			for (let j = 0; j < itemKey.length; j++) {
 				if (result[keys[i]][itemKey[j]] === undefined) continue;
-- 
GitLab