diff --git a/modules/names-generator.js b/modules/names-generator.js index 3646a0c3..68c2a141 100644 --- a/modules/names-generator.js +++ b/modules/names-generator.js @@ -1,4 +1,5 @@ import {last} from "/src/utils/arrayUtils"; +import {locked} from "/src/scripts/options/lock"; window.Names = (function () { let chains = []; diff --git a/modules/ui/general.js b/modules/ui/general.js index 0fbff4e5..c7bc47dd 100644 --- a/modules/ui/general.js +++ b/modules/ui/general.js @@ -414,57 +414,6 @@ function highlightEmblemElement(type, el) { .remove(); } -// assign lock behavior -document.querySelectorAll("[data-locked]").forEach(function (e) { - e.addEventListener("mouseover", function (event) { - if (this.className === "icon-lock") - tip("Click to unlock the option and allow it to be randomized on new map generation"); - else tip("Click to lock the option and always use the current value on new map generation"); - event.stopPropagation(); - }); - - e.addEventListener("click", function () { - const id = this.id.slice(5); - if (this.className === "icon-lock") unlock(id); - else lock(id); - }); -}); - -// lock option -export function lock(id) { - const input = document.querySelector('[data-stored="' + id + '"]'); - if (input) store(id, input.value); - const el = document.getElementById("lock_" + id); - if (!el) return; - el.dataset.locked = 1; - el.className = "icon-lock"; -} - -// unlock option -function unlock(id) { - localStorage.removeItem(id); - const el = document.getElementById("lock_" + id); - if (!el) return; - el.dataset.locked = 0; - el.className = "icon-lock-open"; -} - -// check if option is locked -export function locked(id) { - const lockEl = document.getElementById("lock_" + id); - return lockEl.dataset.locked == 1; -} - -// return key value stored in localStorage or null -export function stored(key) { - return localStorage.getItem(key) || null; -} - -// store key value in localStorage -function store(key, value) { - return localStorage.setItem(key, value); -} - // assign skeaker behaviour Array.from(document.getElementsByClassName("speaker")).forEach(el => { const input = el.previousElementSibling; diff --git a/modules/ui/options.js b/modules/ui/options.js index 67f90a21..88319c86 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -1,6 +1,7 @@ -import {stored, lock, locked, applyOption} from "./general"; +import {applyOption} from "./general"; import {last} from "/src/utils/arrayUtils"; -import {byId} from "/src/utils/shorthands"; +import {byId, stored} from "/src/utils/shorthands"; +import {lock, locked} from "/src/scripts/options/lock"; $("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"}); $("#exitCustomization").draggable({handle: "div"}); diff --git a/src/main.ts b/src/main.ts index 98d4745d..dcdc17cf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,7 @@ // Azgaar (azgaar.fmg@yandex.com). Minsk, 2017-2022. MIT License // https://github.com/Azgaar/Fantasy-Map-Generator -import {PRODUCTION, UINT16_MAX} from "./constants"; +import {UINT16_MAX} from "./constants"; import {INFO, TIME, WARN, ERROR} from "./config/logging"; import { shouldRegenerateGrid, @@ -15,32 +15,17 @@ import {createTypedArray} from "./utils/arrayUtils"; import {drawRivers, drawStates, drawBorders} from "../modules/ui/layers"; import {invokeActiveZooming} from "../modules/activeZooming"; import {applyStoredOptions, applyMapSize, randomizeOptions} from "../modules/ui/options"; -import {locked} from "../modules/ui/general"; +import {locked} from "./scripts/options/lock"; import {Rulers, Ruler, drawScaleBar} from "./modules/measurers"; import {byId} from "./utils/shorthands"; +import {addGlobalListeners} from "./scripts/listeners"; + +addGlobalListeners(); window.fmg = { modules: {} }; -if (PRODUCTION && "serviceWorker" in navigator) { - window.addEventListener("load", () => { - navigator.serviceWorker.register("../sw.js").catch(err => { - console.error("ServiceWorker registration failed: ", err); - }); - }); - - window.addEventListener( - "beforeinstallprompt", - async event => { - event.preventDefault(); - const Installation = await import("../modules/dynamic/installation.js"); - Installation.init(event); - }, - {once: true} - ); -} - // default options options = { pinNotes: false, diff --git a/src/scripts/listeners.ts b/src/scripts/listeners.ts new file mode 100644 index 00000000..c60c5b7a --- /dev/null +++ b/src/scripts/listeners.ts @@ -0,0 +1,29 @@ +import {PRODUCTION} from "../constants"; +import {assignLockBehavior} from "./options/lock"; + +export function addGlobalListeners() { + PRODUCTION && registerServiceWorker(); + PRODUCTION && addInstallationPrompt(); + assignLockBehavior(); +} + +function registerServiceWorker() { + "serviceWorker" in navigator && + window.addEventListener("load", () => { + navigator.serviceWorker.register("../../sw.js").catch(err => { + console.error("ServiceWorker registration failed: ", err); + }); + }); +} + +function addInstallationPrompt() { + window.addEventListener( + "beforeinstallprompt", + async event => { + event.preventDefault(); + const Installation = await import("../../modules/dynamic/installation.js"); + Installation.init(event); + }, + {once: true} + ); +} diff --git a/src/scripts/options/lock.ts b/src/scripts/options/lock.ts new file mode 100644 index 00000000..cd5ccb63 --- /dev/null +++ b/src/scripts/options/lock.ts @@ -0,0 +1,56 @@ +import {store} from "../../utils/shorthands"; + +export function assignLockBehavior() { + const $lockable = document.querySelectorAll("[data-locked]"); + $lockable.forEach($lockableEl => { + $lockableEl.addEventListener("mouseover", showTooltip); + $lockableEl.on("click", toggleLock); + }); +} + +function toggleLock(this: Element) { + const id = this.id.slice(5); + const isLocked = this.className === "icon-lock"; + const toggle = isLocked ? unlock : lock; + toggle(id); +} + +const lockMessage = "Click to lock the option and always use the current value on new map generation"; +const unlockMessage = "Click to unlock the option and allow it to be randomized on new map generation"; + +function showTooltip(this: Element, event: Event) { + event.stopPropagation(); + const isLocked = this.className === "icon-lock"; + const message = isLocked ? unlockMessage : lockMessage; + tip(message); +} + +// lock option from regeneration on page refresh +export function lock(id: string) { + const $input = document.querySelector('[data-stored="' + id + '"]'); + if ($input && $input instanceof HTMLInputElement === false) { + store(id, ($input as HTMLInputElement).value); + } + + const $lock = document.getElementById("lock_" + id); + if ($lock) { + $lock.dataset.locked = "1"; + $lock.className = "icon-lock"; + } +} + +// unlock option +function unlock(id: string) { + localStorage.removeItem(id); + const $lock = document.getElementById("lock_" + id); + if ($lock) { + $lock.dataset.locked = "0"; + $lock.className = "icon-lock-open"; + } +} + +// check if option is locked +export function locked(id: string) { + const $lock = document.getElementById("lock_" + id); + return Boolean($lock && $lock.dataset.locked === "1"); +} diff --git a/src/utils/shorthands.ts b/src/utils/shorthands.ts index 9a45d053..ee5bd5ab 100644 --- a/src/utils/shorthands.ts +++ b/src/utils/shorthands.ts @@ -7,3 +7,11 @@ Node.prototype.on = function (name, fn, options) { Node.prototype.off = function (name, fn) { this.removeEventListener(name, fn); }; + +export function stored(key: string) { + return localStorage.getItem(key) || null; +} + +export function store(key: string, value: string) { + return localStorage.setItem(key, value); +} diff --git a/utils/languageUtils.js b/utils/languageUtils.js index 87f5d67d..3b0c580e 100644 --- a/utils/languageUtils.js +++ b/utils/languageUtils.js @@ -8,7 +8,7 @@ function vowel(c) { // remove vowels from the end of the string function trimVowels(string, minLength = 3) { - while (string.length > minLength && vowel(last(string))) { + while (string.length > minLength && vowel(string.at(-1))) { string = string.slice(0, -1); } return string;