diff --git a/mods/zoom.js b/mods/zoom.js
index da7a037b..2a781625 100644
--- a/mods/zoom.js
+++ b/mods/zoom.js
@@ -1,322 +1,567 @@
-const zoom_levels = [
- 0.5,
- 1,
- 2,
- 3,
- 6,
- 12
-]
-window.zoom_data_div = null
-window.zoom_level = 1
-window.zoom_panning = [0,0]
-
-let colour_setting;
-
-dependOn("betterSettings.js", () => {
- const settings_tab = new SettingsTab("zoom.js");
- colour_setting = new Setting(
- "Canvas background",
- "canvas_bkg",
- settingType.COLOR,
- false,
- defaultValue="#252525"
- );
-
- settings_tab.registerSettings(undefined, colour_setting)
- settingsManager.registerTab(settings_tab)
-})
-
-function handle_zoom(direction){
- switch (direction){
- case "in":
- if (!(zoom_level+1 in zoom_levels)) { break; }
- window.zoom_level += 1
- break;
- case "out":
- if (!(zoom_level-1 in zoom_levels)) { break; }
- window.zoom_level -= 1
- break;
- }
- rescale()
-}
-
-function handle_pan(direction, speed){
- switch (direction){
- case "right":
- zoom_panning[0] -= speed
- break;
- case "left":
- zoom_panning[0] += speed
- break;
- case "up":
- zoom_panning[1] += speed
- break;
- case "down":
- zoom_panning[1] -= speed
- break;
- }
- rescale()
-}
-
-function gen_button(row, col, html, click, nopos, id){
- const elem = document.createElement("button")
-
-
- if (!nopos){
- elem.style.gridColumn = row
- elem.style.gridRow = col
+// zoom.js
+"use strict";
+(() => {
+ // src/custom_setting_types.ts
+ var def_classes = () => {
+ class Numlist2 extends Setting {
+ step;
+ input_container = null;
+ push_btn = null;
+ pop_btn = null;
+ constructor(name, storage_name, default_values = [], desc = "", step = 1, custom_validator = () => true, disabled = false) {
+ super(
+ name,
+ storage_name,
+ [5, 0],
+ disabled,
+ default_values ?? [],
+ desc,
+ custom_validator
+ );
+ this.step = step;
+ }
+ #new_input(value, i) {
+ const elem = document.createElement("input");
+ elem.type = "number";
+ elem.value = value.toString();
+ elem.step = this.step.toString();
+ elem.onchange = (ev) => {
+ const parsed = Number.parseFloat(ev.target.value);
+ if (!Number.isNaN(parsed)) {
+ this.value[i] = parsed;
+ this.set(this.value);
+ }
+ };
+ return elem;
+ }
+ #push_pop_btns() {
+ this.push_btn = document.createElement("button");
+ this.push_btn.style.color = "#0F0";
+ this.push_btn.innerText = "+";
+ this.pop_btn = document.createElement("button");
+ this.pop_btn.style.color = "#F00";
+ this.pop_btn.innerText = "-";
+ this.push_btn.onclick = () => {
+ this.value.push(1);
+ this.input_container.append(this.#new_input(1, this.value.length));
+ };
+ this.pop_btn.onclick = () => {
+ this.value.pop();
+ if (this.input_container.lastChild) {
+ this.input_container.removeChild(this.input_container.lastChild);
+ }
+ };
+ return [this.push_btn, this.pop_btn];
+ }
+ disable() {
+ this.push_btn?.setAttribute("disabled", "true");
+ this.pop_btn?.setAttribute("disabled", "true");
+ }
+ enable() {
+ this.push_btn?.removeAttribute("disabled");
+ this.pop_btn?.removeAttribute("disabled");
+ }
+ build() {
+ const value = this.get();
+ const container = document.createElement("span");
+ container.classList.add("setting-span", "zm_nml_setting");
+ const l_container = document.createElement("span");
+ const label = document.createElement("span");
+ label.innerText = this.name;
+ const btn_container = document.createElement("span");
+ btn_container.classList.add("zm_nml_btn_container");
+ btn_container.append(...this.#push_pop_btns());
+ l_container.append(label, document.createElement("br"), btn_container);
+ this.input_container = document.createElement("span");
+ this.input_container.classList.add("zm_nml_icontainer");
+ const elems = [];
+ value.forEach((x, i) => {
+ elems.push(this.#new_input(x, i));
+ });
+ this.input_container.append(...elems);
+ container.append(l_container, this.input_container);
+ return container;
+ }
}
- if (id) { elem.id = id }
-
- // Table for the data-pos to assign (row major). If null, don't add.
- const data_pos_map = [
- ["tl", null, "tr"],
- [null, null, null],
- ["bl", null, "br"]
- ]
-
- elem.innerHTML = html
- elem.onclick = click
-
- if (data_pos_map[row-1][col-1] !== null) {
- elem.dataset.pos = data_pos_map[row-1][col-1]
+ class MultiSetting extends Setting {
+ settings;
+ elements = [];
+ multi_input_name;
+ rows = [];
+ constructor(name, storage_name, extra_opts, ...settings) {
+ super(
+ name,
+ storage_name,
+ [255],
+ extra_opts.disabled,
+ extra_opts.default_value ?? 0,
+ extra_opts.desc,
+ void 0
+ );
+ this.settings = settings;
+ this.multi_input_name = crypto.randomUUID();
+ }
+ build() {
+ const container = document.createElement("span");
+ this.settings.forEach((setting, i) => {
+ const row_container = document.createElement("div");
+ row_container.classList.add("zm_ms_row");
+ this.rows.push(row_container);
+ const select_btn = document.createElement("button");
+ select_btn.classList.add("zm_ms_selbtn");
+ select_btn.innerText = "#";
+ const built_item = setting.build();
+ built_item.classList.add("zm_ms_item");
+ built_item.dataset.index = i.toString();
+ row_container.dataset.current = i == this.value ? "true" : "false";
+ select_btn.onclick = () => {
+ this.set(i);
+ setting.enable();
+ for (const setting2 of this.settings) setting2.disable();
+ for (const row of this.rows) {
+ row.dataset.current = "false";
+ row.querySelectorAll(".zm_ms_item input").forEach((x) => x.setAttribute("disabled", "true"));
+ }
+ built_item.querySelectorAll("input").forEach((x) => x.removeAttribute("disabled"));
+ row_container.dataset.current = "true";
+ };
+ row_container.append(select_btn, built_item);
+ container.appendChild(row_container);
+ });
+ return container;
+ }
}
-
- return elem
-}
-
-function add_css(){
- const CSS = `
- #zm_data_div { margin-bottom: 10px }
- #canvasDiv { overflow: hidden; background-color: var(--opac-85) }
-
- @media(pointer=coarse){
- #zm_floater_container#zm_floater_container {
- width: 40%;
- height: auto;
+ class SettingGroup extends Setting {
+ settings;
+ constructor(settings) {
+ super(
+ "",
+ "",
+ [2763],
+ false
+ );
+ this.settings = settings;
+ }
+ enable() {
+ for (const x of Object.values(this.settings)) {
+ x.enable();
}
- #zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
- width: calc(40% / 3);
+ }
+ disable() {
+ for (const x of Object.values(this.settings)) {
+ x.disable();
}
- }
-
- @media(pointer:coarse) and (orientation:landscape){
- #zm_floater_container#zm_floater_container {
- width: auto;
- top: 5px;
- }
- #zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
- width: calc(40% / 3);
+ }
+ build() {
+ const container = document.createElement("div");
+ for (const x of Object.values(this.settings)) {
+ container.appendChild(x.build());
}
+ return container;
+ }
+ get() {
+ return this.settings;
+ }
+ // Override these so the defaults don't do anything
+ set() {
+ }
+ update() {
+ }
+ onUpdate() {
+ }
}
+ return {
+ Numlist: Numlist2,
+ MultiSetting,
+ SettingGroup
+ };
+ };
- #colorSelector { z-index: 1; right: 5px }
- #zm_floater_container {
- position: absolute;
- display: grid;
-
- right: 5px;
- bottom: 5px;
- height: 100px;
- aspect-ratio: 1;
-
- max-width: 200px;
- max-height: 200px;
-
- border: 2px solid white;
- background-color: black;
- font-size: 120%;
-
- button { text-align: center; border: 0px solid white }
-
- button:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px };
- button:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px };
- button:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px };
- button:where([data-pos="br"]) { border-width: 2px 0px 0px 2px };
- }
- #zm_floater_container:has(#zm_collapse[data-collapsed="true"]) {
- height: 50px;
-
- button:not(#zm_collapse) {
- display: none;
- }
- }
- #canvasDiv:has(#colorSelector[style *= "block"]) #zm_floater_container {
- bottom: 50px;
- }
-
- .zm_corner { border: 2px solid white; }
-
- #zm_collapse {
- grid-row: 3;
- grid-column: 3;
- }
- #zm_collapse[data-collapsed="true"] {
- grid-row: 1;
- grid-column: 1;
- border-width: 0px;
- }
- `
-
- const style_div = document.createElement("style")
- style_div.innerHTML = CSS
-
- document.head.appendChild(style_div)
-}
-
-function add_zoom_floaters(){
- const container = document.createElement("div")
- container.id = "zm_floater_container"
-
- // Pan mode selector (C: Coarse F: Fine)
- const pan_mode_sel = gen_button(
- 1,3, "C",
- (evt) => {
- evt.target.dataset.mode = evt.target.dataset.mode == "F" ? "C" : "F"
- evt.target.innerText = evt.target.dataset.mode
- },
+ // src/custom_settings.ts
+ var CustomSettingsManager = class {
+ canvas_bkg;
+ zoom;
+ unl_zoom;
+ show_floater;
+ fpan_speed;
+ cpan_speed;
+ upan_speed;
+ constructor(on_edit) {
+ const { Numlist: Numlist2, MultiSetting, SettingGroup } = def_classes();
+ const settings_tab = new SettingsTab("zoom.js");
+ const validator = () => {
+ on_edit.cb(this);
+ return true;
+ };
+ this.canvas_bkg = new Setting(
+ "Canvas background",
+ "canvas_bkg",
+ settingType.COLOR,
false,
- "zm_panmode_sel"
- )
-
- const speed = () =>
- (window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels
- (pan_mode_sel.dataset.mode == "F" ? 0.25 : 1) // Increase granularity in fine mode
-
- container.append(
- // Direction buttons
- gen_button(2,1, "↑", () => handle_pan("up" ,speed())),
- gen_button(1,2, "←", () => handle_pan("left" ,speed())),
- gen_button(3,2, "→", () => handle_pan("right" ,speed())),
- gen_button(2,3, "↓", () => handle_pan("down" ,speed())),
-
- // Zoom buttons
- gen_button(1,1, "+", () => handle_zoom("in")),
- gen_button(3,1, "-", () => handle_zoom("out")),
-
- // Collapse button
- gen_button(
- 3,3, "#",
- (evt) => {
- evt.target.dataset.collapsed = evt.target.dataset.collapsed == "true"
- ? "false"
- : "true"
- },
- true,
- "zm_collapse"
+ "#252525",
+ "The colour for the area around the canvas",
+ validator
+ );
+ this.cpan_speed = new Setting(
+ "Coarse pan speed",
+ "cpan_speed",
+ settingType.NUMBER,
+ false,
+ 10,
+ "The default pan speed",
+ validator
+ );
+ this.fpan_speed = new Setting(
+ "Fine pan speed",
+ "fpan_speed",
+ settingType.NUMBER,
+ false,
+ 3,
+ "The pan speed when holding shift (F in the floater)",
+ validator
+ );
+ this.upan_speed = new Setting(
+ "Ultrafine pan speed",
+ "upan_speed",
+ settingType.NUMBER,
+ false,
+ 1,
+ "The pan speed when holding alt (U in the floater)",
+ validator
+ );
+ this.show_floater = new Setting(
+ "Show floater",
+ "show_floater",
+ settingType.BOOLEAN,
+ false,
+ true,
+ "Whether to show the floater or not",
+ validator
+ );
+ const zoom_levels = new Numlist2(
+ "Zoom levels",
+ "zoom_levels",
+ [0.5, 1, 2, 3, 6, 12],
+ "Zoom levels",
+ 1,
+ validator
+ );
+ this.unl_zoom = new SettingGroup({
+ speed: new Setting(
+ "Zoom speed",
+ "unl_zoom_speed",
+ settingType.NUMBER,
+ false,
+ 2,
+ "The zoom magnitude (as the multiplier to the zoom level every time zoom is used)",
+ validator
),
- pan_mode_sel
- )
-
- const canvas_div = document.getElementById("canvasDiv")
- canvas_div.style.backgroundColor = colour_setting?.value ?? "#252525"
- canvas_div.appendChild(container)
-}
-
-function rescale(){
- log_info()
-
- const scale = zoom_levels[zoom_level]
- const x = zoom_panning[0] * (pixelSize * scale)
- const y = zoom_panning[1] * (pixelSize * scale)
-
- gameCanvas.style.transform = `translate(${x}px, ${y}px) translateX(-50%) scale(${scale})`
-}
-
-function log_info(){
- // Values are negated to make them more intuitive
- const x_pan = (-zoom_panning[0]).toString().padEnd(4)
- const y_pan = (-zoom_panning[1]).toString().padEnd(4)
-
- if (zoom_data_div === null){ return; }
-
- zoom_data_div.innerText = ""
- zoom_data_div.innerText += `Scale: ${zoom_levels[zoom_level]}x\n`
- zoom_data_div.innerText += `Pan : ${x_pan}, ${y_pan}`
-}
-
-function patch_keybinds(){
- // Be more granular at higher zoom levels
- const speed_a = () => zoom_level > 3 ? 5 : 10
- const speed_b = () => zoom_level > 3 ? 10 : 20
-
- keybinds["9"] = () => handle_zoom("in")
- keybinds["0"] = () => handle_zoom("out")
-
- keybinds["w"] = () => handle_pan("up", speed_a())
- keybinds["a"] = () => handle_pan("left", speed_a())
- keybinds["s"] = () => handle_pan("down", speed_a())
- keybinds["d"] = () => handle_pan("right", speed_a())
-
- keybinds["W"] = () => handle_pan("up", speed_b())
- keybinds["A"] = () => handle_pan("left", speed_b())
- keybinds["S"] = () => handle_pan("down", speed_b())
- keybinds["D"] = () => handle_pan("right", speed_b())
-}
-
-function patch_ui(){
- add_css()
- add_zoom_floaters()
-
- zoom_data_div = document.createElement("div")
- zoom_data_div.id = "zm_data_div"
- document.getElementById("logDiv").prepend(zoom_data_div)
-
- const controls_table = document.getElementById("controlsTable").lastElementChild
- controls_table.insertAdjacentHTML("beforeBegin",`
-
- | Zoom in/out |
-
- 9/
- 0
- |
-
-
- | Pan |
-
- W
- A
- S
- D
- |
-
-
- | Pan (fast) |
-
- Shift +
- W
- A
- S
- D
- |
-
- `)
-}
-
-// Redefine to give correct numbers when zoomed
-window.getMousePos = (canvas, evt) => {
- if (evt.touches) {
- evt.preventDefault();
- evt = evt.touches[0];
- isMobile = true;
+ min: new Setting(
+ "Zoom limit (min)",
+ "unl_zlim_min",
+ settingType.NUMBER,
+ false,
+ 0.25,
+ "The lower zoom limit (reducing may lead to rounding error coming back from very low levels)",
+ validator
+ ),
+ max: new Setting(
+ "Zoom limit (max)",
+ "unl_zlim_max",
+ settingType.NUMBER,
+ false,
+ 25,
+ "The upper zoom limit (reducing may lead to rounding error coming back from very high levels)",
+ validator
+ )
+ });
+ this.zoom = new MultiSetting(
+ "Zoom mode",
+ "zoom_mode",
+ {},
+ zoom_levels,
+ this.unl_zoom
+ );
+ settings_tab.registerSettings(
+ void 0,
+ this.canvas_bkg,
+ this.show_floater,
+ this.zoom
+ );
+ settings_tab.registerSettings(
+ "Panning",
+ this.cpan_speed,
+ this.fpan_speed,
+ this.upan_speed
+ );
+ settingsManager.registerTab(settings_tab);
}
- const rect = canvas.getBoundingClientRect();
+ };
- let x = (evt.clientX - rect.left) / zoom_levels[zoom_level];
- let y = (evt.clientY - rect.top) / zoom_levels[zoom_level];
+ // src/handler.ts
+ var Handler = class {
+ settings;
+ patcher;
+ zoom_panning = [0, 0];
+ zoom_level;
+ constructor(settings, patcher) {
+ this.settings = settings;
+ this.patcher = patcher;
+ this.zoom_level = 1;
+ this.patch_keybinds();
+ this.patch_floater();
+ window.getMousePos = (canvas2, evt) => {
+ if (evt.touches) {
+ evt.preventDefault();
+ evt = evt.touches[0];
+ isMobile = true;
+ }
+ const rect = canvas2.getBoundingClientRect();
+ const clx = evt.clientX;
+ const cly = evt.clientY;
+ let x = (clx - rect.left) / this.scale();
+ let y = (cly - rect.top) / this.scale();
+ x = Math.floor(x / canvas2.clientWidth * (width + 1));
+ y = Math.floor(y / canvas2.clientHeight * (height + 1));
+ return { x, y };
+ };
+ runAfterReset(() => {
+ this.zoom_level = 1;
+ this.zoom_panning = [0, 0];
+ this.rescale();
+ });
+ }
+ handle_zoom(direction) {
+ if (this.settings.zoom.value == 0) {
+ switch (direction) {
+ case "in":
+ if (!(this.zoom_level + 1 in this.settings.zoom.settings[0].value)) {
+ break;
+ }
+ this.zoom_level += 1;
+ break;
+ case "out":
+ if (!(this.zoom_level - 1 in this.settings.zoom.settings[0].value)) {
+ break;
+ }
+ this.zoom_level -= 1;
+ break;
+ }
+ } else {
+ const settings = this.settings.zoom.settings[1].settings;
+ const speed = settings.speed.value;
+ const min = settings.min.value;
+ const max = settings.max.value;
+ switch (direction) {
+ case "in":
+ if (this.zoom_level * speed > max) break;
+ this.zoom_level *= speed;
+ break;
+ case "out":
+ if (this.zoom_level / speed < min) break;
+ this.zoom_level /= speed;
+ break;
+ }
+ this.zoom_level = Number(this.zoom_level.toPrecision(3));
+ }
+ this.rescale();
+ }
+ handle_pan(direction, speed) {
+ switch (direction) {
+ case "right":
+ this.zoom_panning[0] -= speed;
+ break;
+ case "left":
+ this.zoom_panning[0] += speed;
+ break;
+ case "up":
+ this.zoom_panning[1] += speed;
+ break;
+ case "down":
+ this.zoom_panning[1] -= speed;
+ break;
+ }
+ this.rescale();
+ }
+ scale() {
+ return this.settings.zoom.value == 0 ? this.settings.zoom.settings[0].value[this.zoom_level] : this.zoom_level;
+ }
+ rescale() {
+ this.log_info();
+ const x = this.zoom_panning[0] * (pixelSize * this.scale());
+ const y = this.zoom_panning[1] * (pixelSize * this.scale());
+ canvas.style.transform = `translate(${x}px, ${y}px) translateX(-50%) scale(${this.scale()})`;
+ }
+ log_info() {
+ const x_pan = (-this.zoom_panning[0]).toString().padEnd(4);
+ const y_pan = (-this.zoom_panning[1]).toString().padEnd(4);
+ this.patcher.zoom_data_div.innerText = "";
+ this.patcher.zoom_data_div.innerText += `Scale: ${this.scale()}x
+`;
+ this.patcher.zoom_data_div.innerText += `Pan : ${x_pan}, ${y_pan}`;
+ }
+ patch_keybinds() {
+ keybinds["9"] = () => this.handle_zoom("in");
+ keybinds["0"] = () => this.handle_zoom("out");
+ keybinds["w"] = (ev) => this.handle_pan("up", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
+ keybinds["a"] = (ev) => this.handle_pan("left", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
+ keybinds["s"] = (ev) => this.handle_pan("down", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
+ keybinds["d"] = (ev) => this.handle_pan("right", ev.altKey ? this.settings.upan_speed.value : this.settings.cpan_speed.value);
+ keybinds["W"] = () => this.handle_pan("up", this.settings.fpan_speed.value);
+ keybinds["A"] = () => this.handle_pan("left", this.settings.fpan_speed.value);
+ keybinds["S"] = () => this.handle_pan("down", this.settings.fpan_speed.value);
+ keybinds["D"] = () => this.handle_pan("right", this.settings.fpan_speed.value);
+ }
+ speed() {
+ switch (this.patcher.panmode_sel.innerText) {
+ case "C":
+ return this.settings.cpan_speed.value;
+ case "F":
+ return this.settings.fpan_speed.value;
+ case "U":
+ return this.settings.upan_speed.value;
+ default:
+ return 0;
+ }
+ }
+ patch_floater() {
+ document.getElementById("zm_floater_u").onclick = () => this.handle_pan("up", this.speed());
+ document.getElementById("zm_floater_d").onclick = () => this.handle_pan("down", this.speed());
+ document.getElementById("zm_floater_l").onclick = () => this.handle_pan("left", this.speed());
+ document.getElementById("zm_floater_r").onclick = () => this.handle_pan("right", this.speed());
+ }
+ };
- x = Math.floor((x / canvas.clientWidth) * (width+1));
- y = Math.floor((y / canvas.clientHeight) * (height+1));
+ // assets/ctrl_info.html
+ var ctrl_info_default = "\r\n | Zoom in/out | \r\n \r\n 9/\r\n 0\r\n | \r\n
\r\n\r\n | Pan | \r\n \r\n W\r\n A\r\n S\r\n D\r\n | \r\n
\r\n\r\n | Pan (fast) | \r\n \r\n Shift + \r\n W\r\n A\r\n S\r\n D\r\n | \r\n
";
- return {x:x, y:y};
+ // assets/floater.html
+ var floater_default = '\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n \r\n
';
+
+ // assets/main.css
+ var main_default = '#zm_data_div { margin-bottom: 10px }\r\n#canvasDiv { overflow: hidden; background-color: var(--opac-85) }\r\n\r\n@media(pointer=coarse){\r\n#zm_floater_container#zm_floater_container { \r\n width: 40%;\r\n height: auto;\r\n}\r\n#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){\r\n width: calc(40% / 3);\r\n}\r\n}\r\n\r\n@media(pointer:coarse) and (orientation:landscape){\r\n#zm_floater_container#zm_floater_container {\r\n width: auto;\r\n top: 5px;\r\n}\r\n#zm_floater_container:has(#zm_collapse[data-collapsed="true"]){\r\n width: calc(40% / 3);\r\n}\r\n}\r\n\r\n#colorSelector { z-index: 1; right: 5px }\r\n#zm_floater_container {\r\nposition: absolute;\r\ndisplay: grid;\r\n\r\nright: 5px;\r\nbottom: 5px;\r\nheight: 100px;\r\naspect-ratio: 1;\r\n\r\nmax-width: 200px;\r\nmax-height: 200px;\r\n\r\nborder: 2px solid white;\r\nbackground-color: black;\r\nfont-size: 120%;\r\n\r\nbutton { text-align: center; border: 0px solid white }\r\n\r\nbutton:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px };\r\nbutton:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px };\r\nbutton:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px };\r\nbutton:where([data-pos="br"]) { border-width: 2px 0px 0px 2px };\r\n}\r\n#zm_floater_container:has(#zm_collapse[data-collapsed="true"]) {\r\nheight: 50px;\r\n\r\nbutton:not(#zm_collapse) {\r\n display: none;\r\n}\r\n}\r\n#canvasDiv:has(#colorSelector[style *= "block"]) #zm_floater_container {\r\nbottom: 50px;\r\n}\r\n\r\n.zm_corner { border: 2px solid white; }\r\n\r\n#zm_collapse {\r\ngrid-row: 3;\r\ngrid-column: 3;\r\n}\r\n#zm_collapse[data-collapsed="true"] {\r\ngrid-row: 1;\r\ngrid-column: 1;\r\nborder-width: 0px;\r\n}';
+
+ // assets/nlist_spinner.png
+ var nlist_spinner_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAYCAYAAADH2bwQAAAAcklEQVQokcWSXQrAIAyDv8oOoPe/49wJzB7coIg/jA2WPhQ1TdOiATsThNmjJ8QV4XjdokIkRHqiEAF2NAgoSw8GlCuD3K3zYEzgFdSQBbA15LaYQN1i9lU+3y16CgDqjSl/MKBoMIk5DyNk46sf9SfhBITwI86iGhy9AAAAAElFTkSuQmCC";
+
+ // assets/numlist.css.ts
+ var CSS = `
+#settingsMenu .zm_nml_btn_container button {
+ font-size: 2em;
+ padding: 0px;
+ margin: 0px;
}
-runAfterReset(() => {
- window.zoom_level = 1
- rescale()
-})
+#settingsMenu .zm_nml_icontainer {
+ align-self: center
+}
-runAfterLoad(() => {
- patch_keybinds()
- patch_ui()
-})
+#settingsMenu .zm_nml_setting {
+ display: grid;
+ grid-template-columns: 7em 1fr;
+}
+
+#settingsMenu .zm_nml_setting span {
+ input {
+ width: 2em;
+ margin-right: 4px;
+ margin-bottom: 4px;
+ }
+
+ input:focus {
+ outline: none;
+ box-shadow: none;
+ border-color: white;
+ }
+
+ /* Sorry, firefox users */
+ input::-webkit-inner-spin-button,
+ input::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 0.75em;
+
+ background: #000 url(${nlist_spinner_default}) no-repeat center center;
+ background-size: 100%;
+ border-left: 2px solid var(--theme);
+
+ image-rendering: pixelated;
+ opacity: 0.8;
+ }
+
+ input::-webkit-inner-spin-button:hover,
+ input::-webkit-outer-spin-button:active {
+ border-left: 2px solid white;
+ opacity: 1;
+ }
+}
+`;
+ var numlist_css_default = CSS;
+
+ // assets/multisetting.css
+ var multisetting_default = '.zm_ms_row {\r\n display: grid;\r\n grid-template-columns: 2.2em 1fr; \r\n}\r\n\r\n.zm_ms_row[data-current="false"] {\r\n .zm_ms_selbtn { color: transparent }\r\n}\r\n\r\n.zm_ms_selbtn.zm_ms_selbtn:not(#_) {\r\n height: 100%;\r\n width: calc(100% - 10px);\r\n\r\n margin-right: 2px;\r\n padding: 0px;\r\n\r\n border: 2px solid var(--theme);\r\n font-size: 1.5em;\r\n}';
+
+ // src/patcher.ts
+ var Patcher = class {
+ zoom_data_div;
+ floater_div;
+ canvas_div;
+ settings;
+ panmode_sel;
+ constructor(settings) {
+ this.settings = settings;
+ const style_div = document.createElement("style");
+ style_div.innerHTML = main_default;
+ document.head.appendChild(style_div);
+ dependOn("betterSettings.js", () => {
+ const style_div2 = document.createElement("style");
+ style_div2.innerHTML = numlist_css_default + multisetting_default;
+ document.head.appendChild(style_div2);
+ });
+ this.canvas_div = document.getElementById("canvasDiv");
+ this.canvas_div.insertAdjacentHTML("beforeend", floater_default);
+ this.floater_div = document.getElementById("zm_floater_container");
+ this.panmode_sel = document.getElementById("zm_panmode_sel");
+ this.panmode_sel.onclick = () => {
+ switch (this.panmode_sel.innerText) {
+ case "C":
+ this.panmode_sel.innerText = "F";
+ break;
+ case "F":
+ this.panmode_sel.innerText = "U";
+ break;
+ case "U":
+ this.panmode_sel.innerText = "C";
+ break;
+ }
+ };
+ this.zoom_data_div = document.createElement("div");
+ this.zoom_data_div.id = "zm_data_div";
+ document.getElementById("logDiv")?.prepend(this.zoom_data_div);
+ document.getElementById("controlsTable")?.lastElementChild?.insertAdjacentHTML("beforebegin", ctrl_info_default);
+ this.update_from_settings();
+ runAfterLoad(() => {
+ const cb = this.update_from_settings.bind(this);
+ for (const elem of document.querySelectorAll("#betterSettings\\/div\\/zoom\\.js span.setting-span input")) {
+ elem.addEventListener(elem.classList.contains("toggleInput") ? "click" : "change", cb);
+ }
+ });
+ }
+ update_from_settings() {
+ this.floater_div.style.display = this.settings.show_floater.value ? "grid" : "none";
+ this.canvas_div.style.backgroundColor = this.settings.canvas_bkg.value ?? "#252525";
+ }
+ };
+
+ // src/main.ts
+ dependOn("betterSettings.js", () => {
+ const on_change = { cb: () => {
+ } };
+ const settings_manager = new CustomSettingsManager(on_change);
+ const patcher = new Patcher(settings_manager);
+ const handler = new Handler(settings_manager, patcher);
+ }, true);
+})();
diff --git a/mods/zoom_legacy.js b/mods/zoom_legacy.js
new file mode 100644
index 00000000..28016110
--- /dev/null
+++ b/mods/zoom_legacy.js
@@ -0,0 +1,322 @@
+const zoom_levels = [
+ 0.5,
+ 1,
+ 2,
+ 3,
+ 6,
+ 12
+]
+window.zoom_data_div = null
+window.zoom_level = 1
+window.zoom_panning = [0,0]
+
+let colour_setting;
+
+dependOn("betterSettings.js", () => {
+ const settings_tab = new SettingsTab("zoom.js");
+ colour_setting = new Setting(
+ "Canvas background",
+ "canvas_bkg",
+ settingType.COLOR,
+ false,
+ defaultValue="#252525"
+ );
+
+ settings_tab.registerSettings(undefined, colour_setting)
+ settingsManager.registerTab(settings_tab)
+})
+
+function handle_zoom(direction){
+ switch (direction){
+ case "in":
+ if (!(zoom_level+1 in zoom_levels)) { break; }
+ window.zoom_level += 1
+ break;
+ case "out":
+ if (!(zoom_level-1 in zoom_levels)) { break; }
+ window.zoom_level -= 1
+ break;
+ }
+ rescale()
+}
+
+function handle_pan(direction, speed){
+ switch (direction){
+ case "right":
+ zoom_panning[0] -= speed
+ break;
+ case "left":
+ zoom_panning[0] += speed
+ break;
+ case "up":
+ zoom_panning[1] += speed
+ break;
+ case "down":
+ zoom_panning[1] -= speed
+ break;
+ }
+ rescale()
+}
+
+function gen_button(row, col, html, click, nopos, id){
+ const elem = document.createElement("button")
+
+
+ if (!nopos){
+ elem.style.gridColumn = row
+ elem.style.gridRow = col
+ }
+ if (id) { elem.id = id }
+
+ // Table for the data-pos to assign (row major). If null, don't add.
+ const data_pos_map = [
+ ["tl", null, "tr"],
+ [null, null, null],
+ ["bl", null, "br"]
+ ]
+
+ elem.innerHTML = html
+ elem.onclick = click
+
+ if (data_pos_map[row-1][col-1] !== null) {
+ elem.dataset.pos = data_pos_map[row-1][col-1]
+ }
+
+ return elem
+}
+
+function add_css(){
+ const CSS = `
+ #zm_data_div { margin-bottom: 10px }
+ #canvasDiv { overflow: hidden; background-color: var(--opac-85) }
+
+ @media(pointer=coarse){
+ #zm_floater_container#zm_floater_container {
+ width: 40%;
+ height: auto;
+ }
+ #zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
+ width: calc(40% / 3);
+ }
+ }
+
+ @media(pointer:coarse) and (orientation:landscape){
+ #zm_floater_container#zm_floater_container {
+ width: auto;
+ top: 5px;
+ }
+ #zm_floater_container:has(#zm_collapse[data-collapsed="true"]){
+ width: calc(40% / 3);
+ }
+ }
+
+ #colorSelector { z-index: 1; right: 5px }
+ #zm_floater_container {
+ position: absolute;
+ display: grid;
+
+ right: 5px;
+ bottom: 5px;
+ height: 100px;
+ aspect-ratio: 1;
+
+ max-width: 200px;
+ max-height: 200px;
+
+ border: 2px solid white;
+ background-color: black;
+ font-size: 120%;
+
+ button { text-align: center; border: 0px solid white }
+
+ button:where([data-pos="tl"]) { border-width: 0px 2px 2px 0px };
+ button:where([data-pos="tr"]) { border-width: 2px 2px 0px 0px };
+ button:where([data-pos="bl"]) { border-width: 0px 0px 2px 2px };
+ button:where([data-pos="br"]) { border-width: 2px 0px 0px 2px };
+ }
+ #zm_floater_container:has(#zm_collapse[data-collapsed="true"]) {
+ height: 50px;
+
+ button:not(#zm_collapse) {
+ display: none;
+ }
+ }
+ #canvasDiv:has(#colorSelector[style *= "block"]) #zm_floater_container {
+ bottom: 50px;
+ }
+
+ .zm_corner { border: 2px solid white; }
+
+ #zm_collapse {
+ grid-row: 3;
+ grid-column: 3;
+ }
+ #zm_collapse[data-collapsed="true"] {
+ grid-row: 1;
+ grid-column: 1;
+ border-width: 0px;
+ }
+ `
+
+ const style_div = document.createElement("style")
+ style_div.innerHTML = CSS
+
+ document.head.appendChild(style_div)
+}
+
+function add_zoom_floaters(){
+ const container = document.createElement("div")
+ container.id = "zm_floater_container"
+
+ // Pan mode selector (C: Coarse F: Fine)
+ const pan_mode_sel = gen_button(
+ 1,3, "C",
+ (evt) => {
+ evt.target.dataset.mode = evt.target.dataset.mode == "F" ? "C" : "F"
+ evt.target.innerText = evt.target.dataset.mode
+ },
+ false,
+ "zm_panmode_sel"
+ )
+
+ const speed = () =>
+ (window.zoom_level > 3 ? 5 : 10) * // More granular at higher zoom levels
+ (pan_mode_sel.dataset.mode == "F" ? 0.25 : 1) // Increase granularity in fine mode
+
+ container.append(
+ // Direction buttons
+ gen_button(2,1, "↑", () => handle_pan("up" ,speed())),
+ gen_button(1,2, "←", () => handle_pan("left" ,speed())),
+ gen_button(3,2, "→", () => handle_pan("right" ,speed())),
+ gen_button(2,3, "↓", () => handle_pan("down" ,speed())),
+
+ // Zoom buttons
+ gen_button(1,1, "+", () => handle_zoom("in")),
+ gen_button(3,1, "-", () => handle_zoom("out")),
+
+ // Collapse button
+ gen_button(
+ 3,3, "#",
+ (evt) => {
+ evt.target.dataset.collapsed = evt.target.dataset.collapsed == "true"
+ ? "false"
+ : "true"
+ },
+ true,
+ "zm_collapse"
+ ),
+ pan_mode_sel
+ )
+
+ const canvas_div = document.getElementById("canvasDiv")
+ canvas_div.style.backgroundColor = colour_setting?.value ?? "#252525"
+ canvas_div.appendChild(container)
+}
+
+function rescale(){
+ log_info()
+
+ const scale = zoom_levels[zoom_level]
+ const x = zoom_panning[0] * (pixelSize * scale)
+ const y = zoom_panning[1] * (pixelSize * scale)
+
+ gameCanvas.style.transform = `translate(${x}px, ${y}px) translateX(-50%) scale(${scale})`
+}
+
+function log_info(){
+ // Values are negated to make them more intuitive
+ const x_pan = (-zoom_panning[0]).toString().padEnd(4)
+ const y_pan = (-zoom_panning[1]).toString().padEnd(4)
+
+ if (zoom_data_div === null){ return; }
+
+ zoom_data_div.innerText = ""
+ zoom_data_div.innerText += `Scale: ${zoom_levels[zoom_level]}x\n`
+ zoom_data_div.innerText += `Pan : ${x_pan}, ${y_pan}`
+}
+
+function patch_keybinds(){
+ // Be more granular at higher zoom levels
+ const speed_a = () => zoom_level > 3 ? 5 : 10
+ const speed_b = () => zoom_level > 3 ? 10 : 20
+
+ keybinds["9"] = () => handle_zoom("in")
+ keybinds["0"] = () => handle_zoom("out")
+
+ keybinds["w"] = () => handle_pan("up", speed_a())
+ keybinds["a"] = () => handle_pan("left", speed_a())
+ keybinds["s"] = () => handle_pan("down", speed_a())
+ keybinds["d"] = () => handle_pan("right", speed_a())
+
+ keybinds["W"] = () => handle_pan("up", speed_b())
+ keybinds["A"] = () => handle_pan("left", speed_b())
+ keybinds["S"] = () => handle_pan("down", speed_b())
+ keybinds["D"] = () => handle_pan("right", speed_b())
+}
+
+function patch_ui(){
+ add_css()
+ add_zoom_floaters()
+
+ zoom_data_div = document.createElement("div")
+ zoom_data_div.id = "zm_data_div"
+ document.getElementById("logDiv").prepend(zoom_data_div)
+
+ const controls_table = document.getElementById("controlsTable").lastElementChild
+ controls_table.insertAdjacentHTML("beforeBegin",`
+
+ | Zoom in/out |
+
+ 9/
+ 0
+ |
+
+
+ | Pan |
+
+ W
+ A
+ S
+ D
+ |
+
+
+ | Pan (fast) |
+
+ Shift +
+ W
+ A
+ S
+ D
+ |
+
+ `)
+}
+
+// Redefine to give correct numbers when zoomed
+window.getMousePos = (canvas, evt) => {
+ if (evt.touches) {
+ evt.preventDefault();
+ evt = evt.touches[0];
+ isMobile = true;
+ }
+ const rect = canvas.getBoundingClientRect();
+
+ let x = (evt.clientX - rect.left) / zoom_levels[zoom_level];
+ let y = (evt.clientY - rect.top) / zoom_levels[zoom_level];
+
+ x = Math.floor((x / canvas.clientWidth) * (width+1));
+ y = Math.floor((y / canvas.clientHeight) * (height+1));
+
+ return {x:x, y:y};
+}
+
+runAfterReset(() => {
+ window.zoom_level = 1
+ rescale()
+})
+
+runAfterLoad(() => {
+ patch_keybinds()
+ patch_ui()
+})
\ No newline at end of file