From 3973e6b92fb9da0af5cf280f5484ae6831d39ae2 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Fri, 16 Feb 2024 00:11:51 -0600 Subject: [PATCH 01/13] Add helper functions to update puzzle properties (numbers/symbols etc) and record them to the undo history --- docs/js/class_p.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index 56cdc3b3..eddabd7b 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -7118,6 +7118,18 @@ class Puzzle { } } + set_value(prop, key, value) { + this.record(prop, key, this.undoredo_counter); + this[this.mode.qa][prop][key] = value; + this.record_replay(prop, key, this.undoredo_counter); + } + + remove_value(prop, key) { + this.record(prop, key, this.undoredo_counter); + delete this[this.mode.qa][prop][key]; + this.record_replay(prop, key, this.undoredo_counter); + } + ///////////////////////////// // Key Event // From 8668d720b17aad5e0e67b57647dd02a6bebcd28d Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Fri, 15 Sep 2023 12:25:09 -0500 Subject: [PATCH 02/13] Some minor refactoring: reduce duplication in puzzle.reset(), add variables for long edit_mode expressions, add grid_is_square() helper --- docs/js/class_p.js | 184 +++++++++++++--------------------------- docs/js/class_square.js | 41 +++++---- 2 files changed, 84 insertions(+), 141 deletions(-) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index eddabd7b..604c99e6 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -185,61 +185,37 @@ class Puzzle { this.isReplay = false; } + reset_puzzle(p) { + this[p] = {}; + this[p].command_redo = new Stack(); + this[p].command_undo = new Stack(); + this[p].command_replay = new Stack(); + this[p].surface = {}; + this[p].number = {}; + this[p].numberS = {}; + this[p].symbol = {}; + this[p].freeline = {}; + this[p].freelineE = {}; + this[p].thermo = []; + this[p].arrows = []; + this[p].direction = []; + this[p].squareframe = []; + this[p].polygon = []; + this[p].line = {}; + this[p].lineE = {}; + this[p].wall = {}; + this[p].cage = {}; + this[p].deletelineE = {}; + this[p].killercages = []; + this[p].nobulbthermo = []; + } + reset() { - let pu_qa = ["pu_q", "pu_a"], - pu_qa_col = ["pu_q_col", "pu_a_col"]; - - // Object and Array initialization - for (var i of pu_qa) { - this[i] = {}; - this[i].command_redo = new Stack(); - this[i].command_undo = new Stack(); - this[i].command_replay = new Stack(); - this[i].surface = {}; - this[i].number = {}; - this[i].numberS = {}; - this[i].symbol = {}; - this[i].freeline = {}; - this[i].freelineE = {}; - this[i].thermo = []; - this[i].arrows = []; - this[i].direction = []; - this[i].squareframe = []; - this[i].polygon = []; - this[i].line = {}; - this[i].lineE = {}; - this[i].wall = {}; - this[i].cage = {}; - this[i].deletelineE = {}; - this[i].killercages = []; - this[i].nobulbthermo = []; - } - - // Object and Array initialization for custom colors - for (var i of pu_qa_col) { - this[i] = {}; - this[i].command_redo = new Stack(); - this[i].command_undo = new Stack(); - this[i].command_replay = new Stack(); - this[i].surface = {}; - this[i].number = {}; - this[i].numberS = {}; - this[i].symbol = {}; - this[i].freeline = {}; - this[i].freelineE = {}; - this[i].thermo = []; - this[i].arrows = []; - this[i].direction = []; - this[i].squareframe = []; - this[i].polygon = []; - this[i].line = {}; - this[i].lineE = {}; - this[i].wall = {}; - this[i].cage = {}; - this[i].deletelineE = {}; - this[i].killercages = []; - this[i].nobulbthermo = []; - } + // Object and Array initialization for problem/solution mode plus their custom colors + this.reset_puzzle("pu_q"); + this.reset_puzzle("pu_q_col"); + this.reset_puzzle("pu_a"); + this.reset_puzzle("pu_a_col"); this.frame = {}; this.freelinecircle_g = [-1, -1]; @@ -247,52 +223,8 @@ class Puzzle { } reset_board() { - this[this.mode.qa] = {}; - this[this.mode.qa].command_redo = new Stack(); - this[this.mode.qa].command_undo = new Stack(); - this[this.mode.qa].command_replay = new Stack(); - this[this.mode.qa].surface = {}; - this[this.mode.qa].number = {}; - this[this.mode.qa].numberS = {}; - this[this.mode.qa].symbol = {}; - this[this.mode.qa].freeline = {}; - this[this.mode.qa].freelineE = {}; - this[this.mode.qa].thermo = []; - this[this.mode.qa].arrows = []; - this[this.mode.qa].direction = []; - this[this.mode.qa].squareframe = []; - this[this.mode.qa].polygon = []; - this[this.mode.qa].line = {}; - this[this.mode.qa].lineE = {}; - this[this.mode.qa].wall = {}; - this[this.mode.qa].cage = {}; - this[this.mode.qa].deletelineE = {}; - this[this.mode.qa].killercages = []; - this[this.mode.qa].nobulbthermo = []; - - // Object and Array initialization for custom colors - this[this.mode.qa + "_col"] = {}; - this[this.mode.qa + "_col"].command_redo = new Stack(); - this[this.mode.qa + "_col"].command_undo = new Stack(); - this[this.mode.qa + "_col"].command_replay = new Stack(); - this[this.mode.qa + "_col"].surface = {}; - this[this.mode.qa + "_col"].number = {}; - this[this.mode.qa + "_col"].numberS = {}; - this[this.mode.qa + "_col"].symbol = {}; - this[this.mode.qa + "_col"].freeline = {}; - this[this.mode.qa + "_col"].freelineE = {}; - this[this.mode.qa + "_col"].thermo = []; - this[this.mode.qa + "_col"].arrows = []; - this[this.mode.qa + "_col"].direction = []; - this[this.mode.qa + "_col"].squareframe = []; - this[this.mode.qa + "_col"].polygon = []; - this[this.mode.qa + "_col"].line = {}; - this[this.mode.qa + "_col"].lineE = {}; - this[this.mode.qa + "_col"].wall = {}; - this[this.mode.qa + "_col"].cage = {}; - this[this.mode.qa + "_col"].deletelineE = {}; - this[this.mode.qa + "_col"].killercages = []; - this[this.mode.qa + "_col"].nobulbthermo = []; + this.reset_puzzle(this.mode.qa); + this.reset_puzzle(this.mode.qa + "_col"); } reset_arr() { @@ -460,9 +392,7 @@ class Puzzle { } } this.cellsoutsideFrame = []; - if (this.gridtype === "square" || - this.gridtype === "sudoku" || - this.gridtype === "kakuro") { + if (this.grid_is_square()) { for (var i = 1; i < this.nx0 - 1; i++) { // Cell Center let cell_firstrow = i + 1 * this.nx0; @@ -1313,6 +1243,10 @@ class Puzzle { return obj; } + grid_is_square() { + return (this.gridtype == "square" || this.gridtype == "kakuro" || this.gridtype == "sudoku"); + } + mode_set(mode, loadtype = 'new') { this.mode[this.mode.qa].edit_mode = mode; this.submode_reset(); @@ -7422,7 +7356,7 @@ class Puzzle { } } - if (this.gridtype === "square" || this.gridtype === "sudoku" || this.gridtype === "kakuro") { + if (this.grid_is_square()) { // not reliable, every access, the order is changing and hence sorting var adjacent_cursor = this.get_neighbors(k, 'adjacent').sort(); @@ -7471,7 +7405,7 @@ class Puzzle { } break; case "2": // Corner mode - if (this.gridtype === "square" || this.gridtype === "sudoku" || this.gridtype === "kakuro") { + if (this.grid_is_square()) { if (this.selection.length > 0 && str_all.indexOf(key) != -1) { if (this.selection.length === 1) { @@ -7986,23 +7920,24 @@ class Puzzle { mouseevent(x, y, num, ctrl_key = false) { if (!pu.replay) { num = this.recalculate_num(x, y, num); //for uniform tiling + let submode = this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]; switch (this.mode[this.mode.qa].edit_mode) { case "surface": this.mouse_surface(x, y, num); break; case "line": - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "3") { + if (submode === "3") { this.mouse_linefree(x, y, num); - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "4") { + } else if (submode === "4") { this.mouse_lineX(x, y, num); } else { this.mouse_line(x, y, num); } break; case "lineE": - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "3") { + if (submode === "3") { this.mouse_lineEfree(x, y, num); - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "4") { + } else if (submode === "4") { this.mouse_lineEX(x, y, num); } else { this.mouse_lineE(x, y, num); @@ -8019,7 +7954,7 @@ class Puzzle { this.mouse_number(x, y, num); } if (pu.mouse_mode === "down_left") { - let isNumberS = ["3", "9", "11"].includes(pu.mode[pu.mode.qa][pu.mode[pu.mode.qa].edit_mode][0]) + let isNumberS = ["3", "9", "11"].includes(submode) let enableLoadButton = (!isNumberS && pu[pu.mode.qa].number[pu.cursol]) || (isNumberS && pu[pu.mode.qa].numberS[pu.cursolS]); document.getElementById("closeBtn_input3").disabled = !enableLoadButton; } @@ -8660,7 +8595,7 @@ class Puzzle { this.cursol = num; // Remember cursolS - if (this.gridtype == "square" || this.gridtype == "kakuro" || this.gridtype == "sudoku") { + if (this.grid_is_square()) { if (!this.cellsoutsideFrame.includes(this.cursol)) { this.cursolS = 4 * (this.cursol + this.nx0 * this.ny0); } @@ -8670,7 +8605,7 @@ class Puzzle { this.cursol = num; // Remember cursolS - if (this.gridtype == "square" || this.gridtype == "kakuro" || this.gridtype == "sudoku") { + if (this.grid_is_square()) { if (!this.cellsoutsideFrame.includes(this.cursol)) { this.cursolS = 4 * (this.cursol + this.nx0 * this.ny0); } @@ -8694,7 +8629,7 @@ class Puzzle { this.cursolS = num; // Remember cursol - if (this.gridtype == "square" || this.gridtype == "kakuro" || this.gridtype == "sudoku") { + if (this.grid_is_square()) { if (submode === "3") { this.cursol = parseInt(this.cursolS / 4) - this.nx0 * this.ny0; } else if (submode === "9") { @@ -8727,7 +8662,7 @@ class Puzzle { this.cursol = num; // Remember cursolS - if (this.gridtype == "square" || this.gridtype == "kakuro" || this.gridtype == "sudoku") { + if (this.grid_is_square()) { if (!this.cellsoutsideFrame.includes(this.cursol)) { this.cursolS = 4 * (this.cursol + this.nx0 * this.ny0); } @@ -10435,7 +10370,7 @@ class Puzzle { } else { // Ignore if edge already exist // Do this only for square grids for now - if (this.gridtype === "square") { + if (this.grid_is_square()) { let neighbor1 = this.point[num].neighbor[0]; let neighbor2 = this.point[num].neighbor[1]; @@ -12036,14 +11971,16 @@ class Puzzle { } draw_cursol() { + let edit_mode = this.mode[this.mode.qa].edit_mode; /*cursol*/ - if (this.mode[this.mode.qa].edit_mode === "number" || this.mode[this.mode.qa].edit_mode === "symbol") { + if (edit_mode === "number" || edit_mode === "symbol") { set_line_style(this.ctx, 99); - if (this.mode[this.mode.qa].edit_mode === "symbol" && UserSettings.panel_shown && !pu.onoff_symbolmode_list[pu.mode[this.mode.qa].symbol[0]]) { + if (edit_mode === "symbol" && UserSettings.panel_shown && !pu.onoff_symbolmode_list[pu.mode[this.mode.qa].symbol[0]]) { this.ctx.strokeStyle = Color.BLUE_DARK_VERY; } this.ctx.fillStyle = Color.TRANSPARENTBLACK; - if (this.mode[this.mode.qa].edit_mode === "number" && (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "3" || this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "9")) { + let submode = this.mode[this.mode.qa][edit_mode][0]; + if (edit_mode === "number" && (submode === "3" || submode === "9")) { if (this.cursolS) { this.draw_polygon(this.ctx, this.point[this.cursolS].x, this.point[this.cursolS].y, 0.2, 4, 45); } else { @@ -12085,8 +12022,9 @@ class Puzzle { } draw_selection() { - if (this.mode[this.mode.qa].edit_mode === "sudoku" || - (this.mode[this.mode.qa].edit_mode === "cage" && document.getElementById("sub_cage1").checked)) { + let edit_mode = this.mode[this.mode.qa].edit_mode; + if (edit_mode === "sudoku" || + (edit_mode === "cage" && document.getElementById("sub_cage1").checked)) { // since we dont want single cell highlighed while in killer submode if (this.selection.length === 0 && this.mode[this.mode.qa].edit_mode === "sudoku") { // check if cursor is in centerlist, to avoid border/edge case @@ -12118,7 +12056,7 @@ class Puzzle { } for (var k of this.selection) { let factor, offset; - if (this.gridtype === "square" || this.gridtype === "sudoku" || this.gridtype === "kakuro") { + if (this.grid_is_square()) { factor = parseInt(k / (this.nx0 * this.ny0)); offset = 3; } else if (this.gridtype === "iso") { @@ -12166,7 +12104,7 @@ class Puzzle { } else { let r, n, th; let tol = 0.01; // error tolerance - if (this.gridtype === "square" || this.gridtype === "sudoku" || this.gridtype === "kakuro") { + if (this.grid_is_square()) { r = 0.2; n = 4; th = 45; @@ -12643,4 +12581,4 @@ class Puzzle { return !this.version_gt(major, minor, revision); } -} \ No newline at end of file +} diff --git a/docs/js/class_square.js b/docs/js/class_square.js index 48a24a76..50fc2424 100644 --- a/docs/js/class_square.js +++ b/docs/js/class_square.js @@ -180,6 +180,7 @@ class Puzzle_square extends Puzzle { type_set() { var type + let submode = this.mode[this.mode.qa][edit_mode][0]; switch (this.mode[this.mode.qa].edit_mode) { case "surface": case "board": @@ -194,11 +195,11 @@ class Puzzle_square extends Puzzle { } break; case "number": - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "2") { + if (submode === "2") { type = [0]; - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "3") { + } else if (submode === "3") { type = [4]; - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "9") { + } else if (submode === "9") { type = [5]; } else { if (!UserSettings.draw_edges) { @@ -209,20 +210,20 @@ class Puzzle_square extends Puzzle { } break; case "line": - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "4") { + if (submode === "4") { type = [2, 3]; - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "2") { + } else if (submode === "2") { type = [0, 1]; - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "5") { + } else if (submode === "5") { type = [0, 2, 3]; } else { type = [0]; } break; case "lineE": - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "4") { + if (submode === "4") { type = [2, 3]; - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "2") { + } else if (submode === "2") { type = [0, 1]; } else { type = [1]; @@ -236,21 +237,21 @@ class Puzzle_square extends Puzzle { } break; case "cage": - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "1") { + if (submode === "1") { type = [0]; - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "2") { + } else if (submode === "2") { type = [4]; } break; case "special": - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "polygon") { + if (submode === "polygon") { type = [1]; } else { type = [0, 1]; } break; case "combi": - switch (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]) { + switch (submode) { case "tents": case "linex": case "linedir": @@ -347,11 +348,13 @@ class Puzzle_square extends Puzzle { } cursolcheck() { - if (this.mode[this.mode.qa].edit_mode === "number" && this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "3") { + let edit_mode = this.mode[this.mode.qa].edit_mode; + let submode = this.mode[this.mode.qa][edit_mode][0]; + if (edit_mode === "number" && submode === "3") { if (this.cursolS > 8 * (this.nx0) * (this.ny0)) { this.cursolS -= 4 * (this.nx0) * (this.ny0); } - } else if (this.mode[this.mode.qa].edit_mode === "number" && this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "9") { + } else if (edit_mode === "number" && submode === "9") { if (this.cursolS < 8 * (this.nx0) * (this.ny0)) { this.cursolS += 4 * (this.nx0) * (this.ny0); } @@ -386,8 +389,10 @@ class Puzzle_square extends Puzzle { c = b[3]; break; } - if (this.mode[this.mode.qa].edit_mode === "number" || this.mode[this.mode.qa].edit_mode === "symbol" || this.mode[this.mode.qa].edit_mode === "sudoku") { - if (this.mode[this.mode.qa].edit_mode === "number" && this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "3") { + let edit_mode = this.mode[this.mode.qa].edit_mode; + let submode = this.mode[this.mode.qa][edit_mode][0]; + if (edit_mode === "number" || edit_mode === "symbol" || edit_mode === "sudoku") { + if (edit_mode === "number" && submode === "3") { switch (c) { case 0: a = this.cursolS % 2 === 0 ? this.cursolS - 3 : this.cursolS - 1; @@ -412,7 +417,7 @@ class Puzzle_square extends Puzzle { if (!this.selection.includes(this.cursol)) { this.selection.push(this.cursol); } - } else if (this.mode[this.mode.qa].edit_mode === "number" && this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "9") { + } else if (edit_mode === "number" && submode === "9") { switch (c) { case 0: a = this.cursolS % 4 === 2 ? this.cursolS - 4 : this.cursolS - this.cursolS % 4 + 2; @@ -437,7 +442,7 @@ class Puzzle_square extends Puzzle { if (!this.selection.includes(this.cursol)) { this.selection.push(this.cursol); } - } else if (this.mode[this.mode.qa].edit_mode === "sudoku") { + } else if (edit_mode === "sudoku") { if (this.selection.length >= 1) { var current_cursor = this.cursol; switch (c) { From e63104073c2fc0d565e1fb402fcc74275e6220c0 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Sun, 20 Aug 2023 16:36:46 -0500 Subject: [PATCH 03/13] Add reverse mapping from Point objects back to normal puzzle indexing (only normal square puzzles for now) --- docs/js/class_p.js | 3 ++- docs/js/class_square.js | 27 ++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index 604c99e6..395c6148 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -1,7 +1,7 @@ const MAX_EXPORT_LENGTH = 7360; class Point { - constructor(x, y, type, adjacent, surround, use, neighbor = [], adjacent_dia = [], type2 = 0) { + constructor(x, y, type, adjacent, surround, use, neighbor = [], adjacent_dia = [], type2 = 0, index = null) { this.x = x; this.y = y; this.type = type; @@ -11,6 +11,7 @@ class Point { this.surround = surround; this.neighbor = neighbor; this.use = use; + this.index = index; } } diff --git a/docs/js/class_square.js b/docs/js/class_square.js index 50fc2424..40a8c71a 100644 --- a/docs/js/class_square.js +++ b/docs/js/class_square.js @@ -50,6 +50,7 @@ class Puzzle_square extends Puzzle { var ny = this.ny0; var adjacent, surround, type, use, neighbor, adjacent_dia; var point = []; + const index = (x, y) => [x, y, this.nx0 * y + x]; //center type = 0; for (var j = 0; j < ny; j++) { @@ -59,7 +60,7 @@ class Puzzle_square extends Puzzle { adjacent_dia = [k - nx - 1, k - nx + 1, k + nx - 1, k + nx + 1]; surround = [k + nx * ny - nx - 1, k + nx * ny - nx, k + nx * ny, k + nx * ny - 1]; neighbor = [k + 2 * nx * ny - nx, k + 2 * nx * ny, k + 3 * nx * ny - 1, k + 3 * nx * ny]; - point[k] = new Point((i + 0.5) * this.size, (j + 0.5) * this.size, type, adjacent, surround, use, neighbor, adjacent_dia); + point[k] = new Point((i + 0.5) * this.size, (j + 0.5) * this.size, type, adjacent, surround, use, neighbor, adjacent_dia, 0, index(i, j)); k++; } } @@ -71,7 +72,7 @@ class Puzzle_square extends Puzzle { adjacent = [k - nx, k - 1, k + 1, k + nx]; adjacent_dia = [k - nx - 1, k - nx + 1, k + nx - 1, k + nx + 1]; surround = []; - point[k] = new Point(point[i + j * nx].x + 0.5 * this.size, point[i + j * nx].y + 0.5 * this.size, type, adjacent, surround, use, [], adjacent_dia); + point[k] = new Point(point[i + j * nx].x + 0.5 * this.size, point[i + j * nx].y + 0.5 * this.size, type, adjacent, surround, use, [], adjacent_dia, 0, index(i, j)); k++; } } @@ -85,7 +86,7 @@ class Puzzle_square extends Puzzle { adjacent = [k + nx, k - nx]; surround = []; neighbor = [k - 2 * nx * ny, k - 2 * nx * ny + nx]; - point[k] = new Point(point[i + j * nx].x, point[i + j * nx].y + 0.5 * this.size, type, adjacent, surround, use, neighbor); + point[k] = new Point(point[i + j * nx].x, point[i + j * nx].y + 0.5 * this.size, type, adjacent, surround, use, neighbor, [], 0, index(i, j)); k++; } } @@ -96,7 +97,7 @@ class Puzzle_square extends Puzzle { adjacent = [k + 1, k - 1]; surround = []; neighbor = [k - 3 * nx * ny, k - 3 * nx * ny + 1]; - point[k] = new Point(point[i + j * nx].x + 0.5 * this.size, point[i + j * nx].y, type, adjacent, surround, use, neighbor); + point[k] = new Point(point[i + j * nx].x + 0.5 * this.size, point[i + j * nx].y, type, adjacent, surround, use, neighbor, [], 0, index(i, j)); k++; } } @@ -109,16 +110,16 @@ class Puzzle_square extends Puzzle { if (i === 0 || i === nx - 1 || j === 0 || j === ny - 1) { use = -1; } else { use = 1; } surround = []; adjacent = [k - 4 * nx + 2, k - 3, k + 1, k + 2]; - point[k] = new Point(point[i + j * nx].x - r * this.size, point[i + j * nx].y - r * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x - r * this.size, point[i + j * nx].y - r * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; adjacent = [k - 4 * nx + 2, k - 1, k + 3, k + 2]; - point[k] = new Point(point[i + j * nx].x + r * this.size, point[i + j * nx].y - r * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x + r * this.size, point[i + j * nx].y - r * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; adjacent = [k - 2, k - 3, k + 1, k + 4 * nx - 2]; - point[k] = new Point(point[i + j * nx].x - r * this.size, point[i + j * nx].y + r * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x - r * this.size, point[i + j * nx].y + r * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; adjacent = [k - 2, k - 1, k + 3, k + 4 * nx - 2]; - point[k] = new Point(point[i + j * nx].x + r * this.size, point[i + j * nx].y + r * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x + r * this.size, point[i + j * nx].y + r * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; } } @@ -131,13 +132,13 @@ class Puzzle_square extends Puzzle { if (i === 0 || i === nx - 1 || j === 0 || j === ny - 1) { use = -1; } else { use = 1; } adjacent = []; surround = []; - point[k] = new Point(point[i + j * nx].x - 0 * this.size, point[i + j * nx].y - r * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x - 0 * this.size, point[i + j * nx].y - r * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; - point[k] = new Point(point[i + j * nx].x + r * this.size, point[i + j * nx].y - 0 * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x + r * this.size, point[i + j * nx].y - 0 * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; - point[k] = new Point(point[i + j * nx].x - r * this.size, point[i + j * nx].y + 0 * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x - r * this.size, point[i + j * nx].y + 0 * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; - point[k] = new Point(point[i + j * nx].x + 0 * this.size, point[i + j * nx].y + r * this.size, type, adjacent, surround, use); + point[k] = new Point(point[i + j * nx].x + 0 * this.size, point[i + j * nx].y + r * this.size, type, adjacent, surround, use, [], [], 0, index(i, j)); k++; } } @@ -4180,4 +4181,4 @@ class Puzzle_kakuro extends Puzzle_square { this[this.mode.qa].symbol[(i + 2) + ((j + 2) * this.nx0)] = [1, "kakuro", 2]; } } -} \ No newline at end of file +} From 0efad5fe7ea999c43aef1cef629f08e38a2d5b9a Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Sat, 19 Aug 2023 18:26:22 -0500 Subject: [PATCH 04/13] Allow shift-click to add to current selection --- docs/js/main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/js/main.js b/docs/js/main.js index 7f12a612..0969e0d9 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -136,7 +136,7 @@ onload = function() { pu.mouse_mode = "down_left"; pu.mouse_click = 0; pu.mouse_click_last = 1; - pu.mouseevent(x, y, num, isCtrlKeyHeld(e)); + pu.mouseevent(x, y, num, isCtrlKeyHeld(e) || isShiftKeyHeld(e)); } } } @@ -994,7 +994,7 @@ onload = function() { // This segment of code I added for a purpose but don't recollect the reason. // After the new improvements maybe this is not needed but for now retaining it as it doesn't impact anything. if (pu.selection.length > 0 && e.target.id.indexOf("sub_sudoku") == -1 && e.target.id.indexOf("st_sudoku") == -1 && - e.target.id != "float-canvas" && !isCtrlKeyHeld(e)) { + e.target.id != "float-canvas" && !isCtrlKeyHeld(e) && !isShiftKeyHeld(e)) { // clear selection pu.selection = []; pu.redraw(); @@ -2334,4 +2334,4 @@ function clear_storage_all() { html: '

Local Storage is Cleared

', icon: 'info' }); -} \ No newline at end of file +} From 467f19b3c1f40c8466f8cc595048334261a35b88 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Sat, 19 Aug 2023 19:14:01 -0500 Subject: [PATCH 05/13] Allow escape key to exit modal dialogs and clear current selection --- docs/js/class_p.js | 15 ++++++++------- docs/js/main.js | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index 395c6148..ffaf49ba 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -12026,14 +12026,15 @@ class Puzzle { let edit_mode = this.mode[this.mode.qa].edit_mode; if (edit_mode === "sudoku" || (edit_mode === "cage" && document.getElementById("sub_cage1").checked)) { + // [ZW] removing this for now, preventing escape to clear selection, not sure what the purpose is // since we dont want single cell highlighed while in killer submode - if (this.selection.length === 0 && this.mode[this.mode.qa].edit_mode === "sudoku") { - // check if cursor is in centerlist, to avoid border/edge case - let cursorexist = this.centerlist.indexOf(this.cursol); - if (cursorexist !== -1) { - this.selection.push(this.cursol); - } - } + //if (this.selection.length === 0 && this.mode[this.mode.qa].edit_mode === "sudoku") { + // // check if cursor is in centerlist, to avoid border/edge case + // let cursorexist = this.centerlist.indexOf(this.cursol); + // if (cursorexist !== -1) { + // this.selection.push(this.cursol); + // } + //} // Handling rotation and reflection of the grid var a = [0, 1, 2, 3], diff --git a/docs/js/main.js b/docs/js/main.js index 0969e0d9..0a51c00a 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -348,6 +348,28 @@ onload = function() { return false; } + if (key === 'Escape') { + // Escape out of any modal dialogs if they're open + + // Weird hack to make sure any sub-dialogs are exited first + const sub_modals = ["modal-save-tag", "modal-save2"]; + let modals = [...document.getElementsByClassName('modal')]; + modals.sort((a, b) => !sub_modals.includes(a.id) && sub_modals.includes(b.id)); + + for (var m of modals) { + if (m.style.display && m.style.display !== 'none') { + e.preventDefault(); + m.style.display = 'none'; + return false; + } + } + + pu.selection = []; + pu.redraw(); + e.returnValue = false; + return false; + } + // All of this is specific to sudoku if (pu.mode[pu.mode.qa].edit_mode === "sudoku") { @@ -802,6 +824,7 @@ onload = function() { } var key = e.key; + const keylocation = e.location; if (isShiftKeyPressed(key) && keylocation !== 3 && pu.mode[pu.mode.qa].edit_mode === "sudoku") { if (present_submode === "1") { From 5e2531e3b76970076708cb250174b35e0677b92c Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Sun, 20 Aug 2023 16:51:36 -0500 Subject: [PATCH 06/13] Allow shift+arrow key to add to selection like ctrl+arrow --- docs/js/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/js/main.js b/docs/js/main.js index 0a51c00a..7782e990 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -339,7 +339,7 @@ onload = function() { if (pu.mode[pu.mode.qa].edit_mode === "sudoku" && keylocation === 3) { // Skip arrow behavior deliberately for sudoku numpad usage. } else { - pu.key_arrow(key, isCtrlKeyHeld(e)); + pu.key_arrow(key, isCtrlKeyHeld(e) || isShiftKeyHeld(e)); e.returnValue = false; } } From 0b9522176afe1b74db6c832caf8f9371af9aa4b0 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Wed, 21 Feb 2024 11:05:57 -0600 Subject: [PATCH 07/13] Fix arrow key movement in triangle grid shapes, make it work in sudoku mode, and make shift-arrow work too --- docs/js/class_tri.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/js/class_tri.js b/docs/js/class_tri.js index 8f9ad4a0..4f2f5837 100644 --- a/docs/js/class_tri.js +++ b/docs/js/class_tri.js @@ -373,7 +373,8 @@ class Puzzle_tri extends Puzzle { c = b[3]; break; } - if (this.mode[this.mode.qa].edit_mode === "number" || this.mode[this.mode.qa].edit_mode === "symbol") { + let edit_mode = this.mode[this.mode.qa].edit_mode; + if (edit_mode === "number" || edit_mode === "sudoku" || edit_mode === "symbol") { if (parseInt(this.cursol / (this.n0) ** 2) === 1) { switch (c) { case 0: @@ -410,6 +411,14 @@ class Puzzle_tri extends Puzzle { } } } + + if (!ctrl_key) { + this.selection = []; + } + if (!this.selection.includes(this.cursol)) { + this.selection.push(this.cursol); + } + this.redraw(); } @@ -2729,4 +2738,4 @@ class Puzzle_tri extends Puzzle { th = th / 180 * Math.PI; return th; } -} \ No newline at end of file +} From 1995250d795445d5dbc05cbe2baf72f5f22979e7 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Mon, 21 Aug 2023 11:37:37 -0500 Subject: [PATCH 08/13] Add double click to select all cells with the same value as the clicked cell --- docs/js/class_p.js | 34 ++++++++++++++++++++++++++++++++++ docs/js/main.js | 20 ++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index ffaf49ba..c2e1b168 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -7985,6 +7985,40 @@ class Puzzle { } } + // Double click: select all cells with the same value as the clicked cell + // XXX: support other cell types + dblmouseevent(x, y, num, ctrl_key = false) { + if (this.mode[this.mode.qa].edit_mode === "sudoku") { + if (!ctrl_key) + this.selection = []; + + let value = this[this.mode.qa].number[num]; + let remove = this.selection.indexOf(num) !== -1; + + // Normal sudoku values + if (value && value[2] == "1") { + let n = value[0]; + + for (let qa of ["pu_q", "pu_a"]) { + let puzzle = this[qa]; + + for (let c of this.centerlist) { + if (puzzle.number[c] && puzzle.number[c][0] == n && + puzzle.number[c][2] == "1") { + if (remove) { + var index = this.selection.indexOf(c); + if (index !== -1) + this.selection.splice(index, 1); + } else if (this.selection.indexOf(c) === -1) + this.selection.push(c); + } + } + } + } + this.redraw(); + } + } + ////////////////////////// // surface ////////////////////////// diff --git a/docs/js/main.js b/docs/js/main.js index 7782e990..1fe5b6ae 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -111,7 +111,7 @@ onload = function() { function onDown(e) { if ((ondown_key === "mousedown" && e.button !== 1) || (ondown_key === "touchstart")) { // Ignore Middle button - if (e.type === "mousedown") { + if (e.type === "mousedown" || e.type === "dblclick") { var event = e; } else { var event = e.changedTouches[0]; @@ -127,7 +127,11 @@ onload = function() { num = obj.num; let skip_mouseevent = restrict_mouse(num); if (pu.point[num].use === 1 && !skip_mouseevent) { - if (event.button === 2) { // right click + if (e.type === "dblclick") { + pu.mouse_mode = "down_left"; + pu.mouse_click = 0; + pu.dblmouseevent(x, y, num, isCtrlKeyHeld(e) || isShiftKeyHeld(e)); + } else if (event.button === 2) { // right click pu.mouse_mode = "down_right"; pu.mouse_click = 2; pu.mouse_click_last = 2; @@ -1921,6 +1925,18 @@ onload = function() { return lines; } + // Double click to select all of a certain element + document.addEventListener("dblclick", window_dblclick, { passive: false }); + function window_dblclick(e) { + if (e.target.id === "canvas") { + document.getElementById("inputtext").blur(); // Remove focus from text box + onDown(e); + if (checkms === 0) { + e.preventDefault(); + } + } + } + //panel(drag_window) var x_window; var y_window; From dd779c7828faa61075535d63b83885e75943e167 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Fri, 15 Sep 2023 13:21:15 -0500 Subject: [PATCH 09/13] Allow multiple cells to be selected in number entry mode (but only in certain submodes for now) --- docs/js/class_p.js | 440 ++++++++++++++++++---------------------- docs/js/class_square.js | 5 +- docs/js/main.js | 44 ++-- 3 files changed, 226 insertions(+), 263 deletions(-) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index c2e1b168..d73bb01b 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -1302,6 +1302,12 @@ class Puzzle { this.redraw(); } + number_multi_enabled() { + let edit_mode = this.mode[this.mode.qa].edit_mode; + let submode = this.mode[this.mode.qa][edit_mode][0]; + return (edit_mode === "number" && !["2"].includes(submode)); + } + submode_check(name) { if (document.getElementById(name)) { document.getElementById(name).checked = true; @@ -7086,185 +7092,150 @@ class Puzzle { // var str_replace = ["+-=*", "+-=*"]; // if (str_replace[0].indexOf(key) != -1) { key = str_replace[1][str_replace[0].indexOf(key)]; } if (this.mode[this.mode.qa].edit_mode === "number") { - switch (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]) { - case "1": - // If the there are corner or sides present then get rid of them - // Only in Answer mode - if (this.mode.qa === "pu_a") { - var corner_cursor = 4 * (this.cursol + this.nx0 * this.ny0); - var side_cursor = 4 * (this.cursol + 2 * this.nx0 * this.ny0); + let submode = this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode]; + if (this.selection.length === 1) { + let clean_flag = this.check_neighbors(this.selection[0]); + if (!clean_flag) { + this.undoredo_counter = 0; + } else { + this.undoredo_counter = this.undoredo_counter + 1; + } + } else { + this.undoredo_counter = this.undoredo_counter + 1; + } + let cells = null; + if (this.number_multi_enabled()) + cells = this.selection; + else + cells = [this.cursol]; + for (var k of cells) { + switch (submode[0]) { + case "1": + // If the there are corner or sides present then get rid of them + // Only in Answer mode + if (this.mode.qa === "pu_a") { + var corner_cursor = 4 * (k + this.nx0 * this.ny0); + var side_cursor = 4 * (k + 2 * this.nx0 * this.ny0); - for (var j = 0; j < 4; j++) { - if (this[this.mode.qa].numberS[corner_cursor + j]) { - this.record("numberS", corner_cursor + j); - delete this[this.mode.qa].numberS[corner_cursor + j]; - this.record_replay("numberS", corner_cursor + j); - } + for (var j = 0; j < 4; j++) + if (this[this.mode.qa].numberS[corner_cursor + j]) + this.remove_value("numberS", corner_cursor + j); + + for (var j = 0; j < 4; j++) + if (this[this.mode.qa].numberS[side_cursor + j]) + this.remove_value("numberS", side_cursor + j); } - for (var j = 0; j < 4; j++) { - if (this[this.mode.qa].numberS[side_cursor + j]) { - this.record("numberS", side_cursor + j); - delete this[this.mode.qa].numberS[side_cursor + j]; - this.record_replay("numberS", side_cursor + j); + if (str_num.indexOf(key) != -1 && this[this.mode.qa].number[k]) { + con = parseInt(this[this.mode.qa].number[k][0], 10); // Convert to number + if (con >= 1 && con <= 9 && this[this.mode.qa].number[k][2] != "7") { // If already 1-9 exist, go to 2nd digit + number = con.toString() + key; + } else { + // It enters here when the cell already contains 2 digits. + number = key; } + } else { + // It enters for first entry in a cell and then for alphabets or special characters i.e. non numbers + number = key; } - } - this.record("number", this.cursol); - if (str_num.indexOf(key) != -1 && this[this.mode.qa].number[this.cursol]) { - con = parseInt(this[this.mode.qa].number[this.cursol][0], 10); // Convert to number - if (con >= 1 && con <= 9 && this[this.mode.qa].number[this.cursol][2] != "7") { // If already 1-9 exist, go to 2nd digit - number = con.toString() + key; + this.set_value("number", k, [number, submode[1], submode[0]]); + break; + case "2": // Arrow + if (this[this.mode.qa].number[k] && this[this.mode.qa].number[k][2] != "7") { + con = this[this.mode.qa].number[k][0]; } else { - // It enters here when the cell already contains 2 digits. - number = key; + con = ""; } - } else { - // It enters for first entry in a cell and then for alphabets or special characters i.e. non numbers - number = key; - } - this[this.mode.qa].number[this.cursol] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - break; - case "2": // Arrow - this.record("number", this.cursol); - if (this[this.mode.qa].number[this.cursol] && this[this.mode.qa].number[this.cursol][2] != "7") { - con = this[this.mode.qa].number[this.cursol][0]; - } else { - con = ""; - } - if (con.slice(-2, -1) === "_") { - conA = parseInt(con.slice(0, -2), 10); - arrow = con.slice(-2); - } else { - conA = parseInt(con, 10); - arrow = ""; - } - if (str_num.indexOf(key) != -1) { - if (conA >= 1 && conA <= 9) { // If 1 to 9 got to the second digit - number = conA.toString() + key; + if (con.slice(-2, -1) === "_") { + conA = parseInt(con.slice(0, -2), 10); + arrow = con.slice(-2); + } else { + conA = parseInt(con, 10); + arrow = ""; + } + if (str_num.indexOf(key) != -1) { + if (conA >= 1 && conA <= 9) { // If 1 to 9 got to the second digit + number = conA.toString() + key; + } else { + number = key; + } } else { number = key; } - } else { - number = key; - } - this[this.mode.qa].number[this.cursol] = [number + arrow, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - break; - case "3": // 1/4, corner - case "9": // Sides - this.record("numberS", this.cursolS); - if (this[this.mode.qa].numberS[this.cursolS]) { - con = this[this.mode.qa].numberS[this.cursolS][0]; - } else { - con = ""; - } - number = con + key; - this[this.mode.qa].numberS[this.cursolS] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1]]; - this.record_replay("numberS", this.cursolS); - break; - case "4": //tapa - if (key === ".") { key = " "; } - this.record("number", this.cursol); - if (this[this.mode.qa].number[this.cursol]) { - con = this[this.mode.qa].number[this.cursol][0]; - mode = this[this.mode.qa].number[this.cursol][2]; - } else { - con = ""; - mode = ""; - } - let con_expand = [...con]; - if (mode != 2 && mode != 7) { // If not arrow mode - if (con_expand.length >= 0 && con_expand.length <= 3) { // Max 4 values - number = con + key; + this.set_value("number", k, [number + arrow, submode[1], submode[0]]); + break; + case "3": // 1/4, corner + case "9": // Sides + if (this[this.mode.qa].numberS[k]) { + con = this[this.mode.qa].numberS[k][0]; } else { - number = con; // Don't update if more than 4 values + con = ""; } - } else { // Overwrite if arrow - number = key; - } - this[this.mode.qa].number[this.cursol] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - break; - case "5": // Small - if (this[this.mode.qa].number[this.cursol] && this[this.mode.qa].number[this.cursol][2] != "2" && this[this.mode.qa].number[this.cursol][2] != "7") { - con = this[this.mode.qa].number[this.cursol][0]; - } else { - con = ""; - } - if (con.length < 10) { - this.record("number", this.cursol); number = con + key; - this[this.mode.qa].number[this.cursol] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - } - break; - case "6": // Medium - if (this[this.mode.qa].number[this.cursol] && this[this.mode.qa].number[this.cursol][2] != "2" && this[this.mode.qa].number[this.cursol][2] != "7") { - con = this[this.mode.qa].number[this.cursol][0]; - } else { - con = ""; - } - if (con.length < 10) { - this.record("number", this.cursol); - number = con + key; - this[this.mode.qa].number[this.cursol] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - } - break; - case "10": //big - if (this[this.mode.qa].number[this.cursol] && this[this.mode.qa].number[this.cursol][2] != "2" && this[this.mode.qa].number[this.cursol][2] != "7") { - con = this[this.mode.qa].number[this.cursol][0]; - } else { - con = ""; - } - if (con.length < 10) { - this.record("number", this.cursol); - number = con + key; - this[this.mode.qa].number[this.cursol] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - } - break; - case "7": // Candidates - if (str_num_no0.indexOf(key) != -1) { - this.record("number", this.cursol); - if (this[this.mode.qa].number[this.cursol] && this[this.mode.qa].number[this.cursol][2] === "7") { - con = this[this.mode.qa].number[this.cursol][0]; + this.set_value("numberS", k, [number, submode[1]]); + break; + case "4": //tapa + if (key === ".") { key = " "; } + if (this[this.mode.qa].number[k]) { + con = this[this.mode.qa].number[k][0]; + mode = this[this.mode.qa].number[k][2]; + } else { + con = ""; + mode = ""; + } + let con_expand = [...con]; + if (mode != 2 && mode != 7) { // If not arrow mode + if (con_expand.length >= 0 && con_expand.length <= 3) { // Max 4 values + number = con + key; + } else { + number = con; // Don't update if more than 4 values + } + } else { // Overwrite if arrow + number = key; + } + this.set_value("number", k, [number, submode[1], submode[0]]); + break; + case "5": // Small + case "6": // Medium + case "10": //big + case "8": // Long + if (this[this.mode.qa].number[k] && this[this.mode.qa].number[k][2] != "2" && this[this.mode.qa].number[k][2] != "7") { + con = this[this.mode.qa].number[k][0]; + } else { + con = ""; + } + // Length limit of 10 except for Long submode which has 50 + const limit = (submode[0] === "8") ? 50 : 10; + if (con.length < limit) { + number = con + key; + this.set_value("number", k, [number, submode[1], submode[0]]); + } + break; + + case "7": // Candidates + if (str_num_no0.indexOf(key) != -1) { + if (this[this.mode.qa].number[k] && this[this.mode.qa].number[k][2] === "7") { + con = this[this.mode.qa].number[k][0]; + } else { + con = ""; + } + number = this.onofftext(9, key, con); + this.set_value("number", k, [number, submode[1], submode[0]]); + } + break; + + case "11": // Killer Sum + var corner_cursor = 4 * (k + this.nx0 * this.ny0); + if (this[this.mode.qa].numberS[corner_cursor]) { + con = " " + this[this.mode.qa].numberS[corner_cursor][0]; } else { con = ""; } - number = this.onofftext(9, key, con); - this[this.mode.qa].number[this.cursol] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - } - break; - case "8": // Long - if (this[this.mode.qa].number[this.cursol] && this[this.mode.qa].number[this.cursol][2] != "2" && this[this.mode.qa].number[this.cursol][2] != "7") { - con = this[this.mode.qa].number[this.cursol][0]; - } else { - con = ""; - } - if (con.length < 50) { - this.record("number", this.cursol); number = con + key; - this[this.mode.qa].number[this.cursol] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1], this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]]; - this.record_replay("number", this.cursol); - } - break; - case "11": // Killer Sum - var corner_cursor = 4 * (this.cursol + this.nx0 * this.ny0); - this.record("numberS", corner_cursor); - if (this[this.mode.qa].numberS[corner_cursor]) { - con = " " + this[this.mode.qa].numberS[corner_cursor][0]; - } else { - con = ""; - } - number = con + key; - this[this.mode.qa].numberS[corner_cursor] = [number, this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][1]]; - this.record_replay("numberS", corner_cursor); - break; + this.set_value("numberS", corner_cursor, [number, submode[1]]); + break; + } } } else if (this.mode[this.mode.qa].edit_mode === "symbol") { if (str_num.indexOf(key) != -1) { @@ -7857,53 +7828,74 @@ class Puzzle { key_backspace() { var number; if (this.mode[this.mode.qa].edit_mode === "number") { - if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "3" || this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "9") { // 1/4 and side - if (this[this.mode.qa].numberS[this.cursolS]) { - this.record("numberS", this.cursolS); - number = this[this.mode.qa].numberS[this.cursolS][0].slice(0, -1); - if (number) { - this[this.mode.qa].numberS[this.cursolS][0] = number; - } else { - delete this[this.mode.qa].numberS[this.cursolS]; - } - this.record_replay("numberS", this.cursolS); - } - } else if (this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0] === "11") { - var corner_cursor = 4 * (this.cursol + this.nx0 * this.ny0); - if (this[this.mode.qa].numberS[corner_cursor]) { - this.record("numberS", corner_cursor); - number = this[this.mode.qa].numberS[corner_cursor][0].slice(1, -1); - if (number) { - this[this.mode.qa].numberS[corner_cursor][0] = number; + let submode = this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]; + if (this.selection.length > 0) { + if (this.selection.length === 1) { + let clean_flag = this.check_neighbors(this.selection[0]); + if (!clean_flag) { + this.undoredo_counter = 0; } else { - delete this[this.mode.qa].numberS[corner_cursor]; + this.undoredo_counter = this.undoredo_counter + 1; } - this.record_replay("numberS", corner_cursor); + } else { + this.undoredo_counter = this.undoredo_counter + 1; } - } else { - if (this[this.mode.qa].number[this.cursol] && this[this.mode.qa].number[this.cursol][2] != 7) { - this.record("number", this.cursol); - number = this[this.mode.qa].number[this.cursol][0]; - if (number) { - if (this[this.mode.qa].number[this.cursol][2] === "2") { - if (number.slice(-2, -1) === "_") { - number = number.slice(0, -2).slice(0, -1) + number.slice(-2); + let cells = null; + if (this.number_multi_enabled()) + cells = this.selection; + else + cells = [this.cursol]; + + for (var k of cells) { + if (submode === "3" || submode === "9") { // 1/4 and side + if (this[this.mode.qa].numberS[k]) { + this.record("numberS", k, this.undoredo_counter); + number = this[this.mode.qa].numberS[k][0].slice(0, -1); + if (number) { + this[this.mode.qa].numberS[k][0] = number; } else { - number = number.slice(0, -1); + delete this[this.mode.qa].numberS[k]; } - } else { - number = number.slice(0, -1); + this.record_replay("numberS", k, this.undoredo_counter); } - if (number || - this[this.mode.qa].number[this.cursol][1] === 6 || - this[this.mode.qa].number[this.cursol][1] === 7 || - this[this.mode.qa].number[this.cursol][1] === 11) { - this[this.mode.qa].number[this.cursol][0] = number; - } else { - delete this[this.mode.qa].number[this.cursol]; + } else if (submode === "11") { + var corner_cursor = 4 * (k + this.nx0 * this.ny0); + if (this[this.mode.qa].numberS[corner_cursor]) { + this.record("numberS", corner_cursor, this.undoredo_counter); + number = this[this.mode.qa].numberS[corner_cursor][0].slice(1, -1); + if (number) { + this[this.mode.qa].numberS[corner_cursor][0] = number; + } else { + delete this[this.mode.qa].numberS[corner_cursor]; + } + this.record_replay("numberS", corner_cursor, this.undoredo_counter); + } + } else { + if (this[this.mode.qa].number[k] && this[this.mode.qa].number[k][2] != 7) { + this.record("number", k, this.undoredo_counter); + number = this[this.mode.qa].number[k][0]; + if (number) { + if (this[this.mode.qa].number[k][2] === "2") { + if (number.slice(-2, -1) === "_") { + number = number.slice(0, -2).slice(0, -1) + number.slice(-2); + } else { + number = number.slice(0, -1); + } + } else { + number = number.slice(0, -1); + } + if (number || + this[this.mode.qa].number[k][1] === 6 || + this[this.mode.qa].number[k][1] === 7 || + this[this.mode.qa].number[k][1] === 11) { + this[this.mode.qa].number[k][0] = number; + } else { + delete this[this.mode.qa].number[k]; + } + } + this.record_replay("number", k, this.undoredo_counter); } } - this.record_replay("number", this.cursol); } } } @@ -7948,12 +7940,12 @@ class Puzzle { this.mouse_wall(x, y, num); break; case "number": - let submode = this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]; - if (submode === "3" || submode === "9") { - this.mouse_numberS(x, y, num, submode); - } else { - this.mouse_number(x, y, num); - } + // Multi-selection mode: treat this just like sudoku mode if we're in + // a submode that can work with multiple cells + if (this.number_multi_enabled()) + this.mouse_sudoku(x, y, num, ctrl_key); + else + this.mouse_number(x, y, num, ctrl_key); if (pu.mouse_mode === "down_left") { let isNumberS = ["3", "9", "11"].includes(submode) let enableLoadButton = (!isNumberS && pu[pu.mode.qa].number[pu.cursol]) || (isNumberS && pu[pu.mode.qa].numberS[pu.cursolS]); @@ -8659,26 +8651,6 @@ class Puzzle { } } - mouse_numberS(x, y, num, submode) { - if (this.mouse_mode === "down_left") { - this.cursolS = num; - - // Remember cursol - if (this.grid_is_square()) { - if (submode === "3") { - this.cursol = parseInt(this.cursolS / 4) - this.nx0 * this.ny0; - } else if (submode === "9") { - this.cursol = parseInt(this.cursolS / 4) - 2 * this.nx0 * this.ny0; - } - } - this.redraw(); - } else if (this.mouse_mode === "down_right") { - this.cursolS = num; - this.redraw(); - } - } - - mouse_sudoku(x, y, num, ctrl_key = false) { // if (this.point[num].type === 0) {} // Add this line, to ignore corners and allow diagonal selection, and set type = [0, 1] if (this.mouse_mode === "down_left") { @@ -8705,12 +8677,12 @@ class Puzzle { this.redraw(); } else if (this.mouse_mode === "move") { // if the first selected position is edge then do not consider move - if (this.selection.length === 1 && parseInt(this.selection[0] / (this.nx0 * this.ny0)) > 0 && + if (this.cursol && this.cursol >= this.nx0 * this.ny0 && this.gridtype !== "iso" && this.gridtype !== "tetrakis_square" && this.gridtype !== "truncated_square" && this.gridtype !== "snub_square" && this.gridtype !== "cairo_pentagonal" && this.gridtype !== "rhombitrihexagonal" && this.gridtype !== "deltoidal_trihexagonal") { // do nothing - } else if (!this.selection.includes(num) & this.drawing) { + } else if (!this.selection.includes(num) && this.drawing) { this.selection.push(num); } this.redraw(); @@ -12008,20 +11980,14 @@ class Puzzle { draw_cursol() { let edit_mode = this.mode[this.mode.qa].edit_mode; /*cursol*/ - if (edit_mode === "number" || edit_mode === "symbol") { + if ((edit_mode === "number" && !this.number_multi_enabled()) || edit_mode === "symbol") { set_line_style(this.ctx, 99); if (edit_mode === "symbol" && UserSettings.panel_shown && !pu.onoff_symbolmode_list[pu.mode[this.mode.qa].symbol[0]]) { this.ctx.strokeStyle = Color.BLUE_DARK_VERY; } this.ctx.fillStyle = Color.TRANSPARENTBLACK; let submode = this.mode[this.mode.qa][edit_mode][0]; - if (edit_mode === "number" && (submode === "3" || submode === "9")) { - if (this.cursolS) { - this.draw_polygon(this.ctx, this.point[this.cursolS].x, this.point[this.cursolS].y, 0.2, 4, 45); - } else { - this.default_cursol(); - } - } else if (UserSettings.draw_edges) { + if (UserSettings.draw_edges) { this.draw_polygon(this.ctx, this.point[this.cursol].x, this.point[this.cursol].y, 0.2, 4, 45); } else { this.default_cursol(); @@ -12058,7 +12024,7 @@ class Puzzle { draw_selection() { let edit_mode = this.mode[this.mode.qa].edit_mode; - if (edit_mode === "sudoku" || + if (edit_mode === "sudoku" || this.number_multi_enabled() || (edit_mode === "cage" && document.getElementById("sub_cage1").checked)) { // [ZW] removing this for now, preventing escape to clear selection, not sure what the purpose is // since we dont want single cell highlighed while in killer submode diff --git a/docs/js/class_square.js b/docs/js/class_square.js index 40a8c71a..7f3fb77c 100644 --- a/docs/js/class_square.js +++ b/docs/js/class_square.js @@ -181,8 +181,9 @@ class Puzzle_square extends Puzzle { type_set() { var type + let edit_mode = this.mode[this.mode.qa].edit_mode; let submode = this.mode[this.mode.qa][edit_mode][0]; - switch (this.mode[this.mode.qa].edit_mode) { + switch (edit_mode) { case "surface": case "board": type = [0]; @@ -443,7 +444,7 @@ class Puzzle_square extends Puzzle { if (!this.selection.includes(this.cursol)) { this.selection.push(this.cursol); } - } else if (edit_mode === "sudoku") { + } else if (edit_mode === "sudoku" || (edit_mode === "number" && this.number_multi_enabled())) { if (this.selection.length >= 1) { var current_cursor = this.cursol; switch (c) { diff --git a/docs/js/main.js b/docs/js/main.js index 1fe5b6ae..6f3b94a2 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -117,7 +117,9 @@ onload = function() { var event = e.changedTouches[0]; e.preventDefault(); // When both mouse and touch start, only touch } - if (ondown_key === "mousedown" && event.button !== 2 && pu.mode[pu.mode.qa].edit_mode !== "sudoku") { // not right click and so improve the coordinate system for certain modes + if (ondown_key === "mousedown" && event.button !== 2 && + pu.mode[pu.mode.qa].edit_mode !== "number" && + pu.mode[pu.mode.qa].edit_mode !== "sudoku") { // not right click and so improve the coordinate system for certain modes var obj = coord_point(event, 'flex'); } else { var obj = coord_point(event); @@ -147,6 +149,7 @@ onload = function() { } function onUp(e) { + let edit_mode = pu.mode[pu.mode.qa].edit_mode; if ((ondown_key === "mousedown" && e.button !== 1) || (ondown_key === "touchstart")) { // Ignore Middle button if (e.type === "mouseup") { var event = e; @@ -154,9 +157,10 @@ onload = function() { var event = e.changedTouches[0]; e.preventDefault(); // When both mouse and touch start, only touch } - if (ondown_key === "mousedown" && (pu.mode[pu.mode.qa].edit_mode === "combi") && // to handle mobile/ipad users for up events for certain modes - (pu.mode[pu.mode.qa][pu.mode[pu.mode.qa].edit_mode][0] === "yajilin" || - pu.mode[pu.mode.qa][pu.mode[pu.mode.qa].edit_mode][0] === "akari")) { + // to handle mobile/ipad users for up events for certain modes + if (ondown_key === "mousedown" && (edit_mode === "sudoku" || edit_mode === "number" || + (edit_mode === "combi" && (pu.mode[pu.mode.qa][edit_mode][0] === "yajilin" || + pu.mode[pu.mode.qa][edit_mode][0] === "akari")))) { var obj = coord_point(event, 'flex'); } else { var obj = coord_point(event); @@ -177,6 +181,7 @@ onload = function() { } function onMove(e) { + let edit_mode = pu.mode[pu.mode.qa].edit_mode; if ((ondown_key === "mousedown" && e.buttons !== 4) || (ondown_key === "touchstart")) { // Ignore Middle button if (e.type === "mousemove") { var event = e; @@ -187,12 +192,13 @@ onload = function() { if (event.buttons === 2) { // Right click and moving pu.mouse_click = 2; var obj = coord_point(event, 'flex'); - } else if ((ondown_key === "touchstart" || event.buttons === 1) && pu.mode[pu.mode.qa].edit_mode === "sudoku") { // Left click/Ipad and moving in Sudoku Mode + } else if ((ondown_key === "touchstart" || event.buttons === 1) && + (edit_mode === "sudoku" || edit_mode === "number")) { // Left click/Ipad and moving in Sudoku Mode pu.mouse_click = 0; var obj = coord_point(event, 'flex'); } else { - if (((pu.mode[pu.mode.qa].edit_mode === "combi") && (pu.mode[pu.mode.qa][pu.mode[pu.mode.qa].edit_mode][0] === "yajilin" || - pu.mode[pu.mode.qa][pu.mode[pu.mode.qa].edit_mode][0] === "akari"))) { + if (((edit_mode === "combi") && (pu.mode[pu.mode.qa][edit_mode][0] === "yajilin" || + pu.mode[pu.mode.qa][edit_mode][0] === "akari"))) { var obj = coord_point(event, 'flex'); } else { var obj = coord_point(event); @@ -870,21 +876,20 @@ onload = function() { var y = e.pageY - canvas.offsetTop; var min0, min = 10e6; var num = 0; - let type; var improve_modes = ["star", "yajilin", "mines", "doublemines", "akari"]; + let edit_mode = pu.mode[pu.mode.qa].edit_mode; + + let type = pu.type; // Improving starbattle composite mode, left click if (fittype === 'flex') { - if (((pu.mode[pu.mode.qa].edit_mode === "combi") && - (improve_modes.includes(pu.mode[pu.mode.qa][pu.mode[pu.mode.qa].edit_mode][0]))) || - (pu.mode[pu.mode.qa].edit_mode === "sudoku")) { - type = pu.type; - pu.type = [0]; - } + if ((edit_mode === "combi" && improve_modes.includes(pu.mode[pu.mode.qa][edit_mode][0])) || + edit_mode === "sudoku" || edit_mode === "number") + type = [0]; } for (var i = 0; i < pu.point.length; i++) { - if (pu.point[i] && pu.type.indexOf(pu.point[i].type) != -1) { + if (pu.point[i] && type.indexOf(pu.point[i].type) != -1) { min0 = (x - pu.point[i].x) ** 2 + (y - pu.point[i].y) ** 2; if (min0 < min) { min = min0; @@ -893,15 +898,6 @@ onload = function() { } } - // resetting the type for starbattle composite mode - if (fittype === 'flex') { - if (((pu.mode[pu.mode.qa].edit_mode === "combi") && - (improve_modes.includes(pu.mode[pu.mode.qa][pu.mode[pu.mode.qa].edit_mode][0]))) || - (pu.mode[pu.mode.qa].edit_mode === "sudoku")) { - pu.type = type; - } - } - //const endTime = performance.now(); //console.log(endTime - startTime); num = parseInt(num); From 0331f457f946c192f0fb753880fe9de49e7d4dd6 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Fri, 15 Sep 2023 13:22:05 -0500 Subject: [PATCH 10/13] Allow double-click to select all cells with the same value in number mode, and make the logic less restrictive on what types of cells this works for --- docs/js/class_p.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index d73bb01b..61f034e9 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -7980,23 +7980,23 @@ class Puzzle { // Double click: select all cells with the same value as the clicked cell // XXX: support other cell types dblmouseevent(x, y, num, ctrl_key = false) { - if (this.mode[this.mode.qa].edit_mode === "sudoku") { + let edit_mode = this.mode[this.mode.qa].edit_mode; + if (edit_mode === "number" || edit_mode === "sudoku") { if (!ctrl_key) this.selection = []; - let value = this[this.mode.qa].number[num]; + let value = this.pu_q.number[num] || this.pu_a.number[num]; let remove = this.selection.indexOf(num) !== -1; // Normal sudoku values - if (value && value[2] == "1") { + if (value) { let n = value[0]; for (let qa of ["pu_q", "pu_a"]) { let puzzle = this[qa]; for (let c of this.centerlist) { - if (puzzle.number[c] && puzzle.number[c][0] == n && - puzzle.number[c][2] == "1") { + if (puzzle.number[c] && puzzle.number[c][0] == n) { if (remove) { var index = this.selection.indexOf(c); if (index !== -1) From 9729e592bb688555f0e4b838d8be458b281ec3c3 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Thu, 28 Mar 2024 08:15:13 -0500 Subject: [PATCH 11/13] Remove code that clears selection in most cases of a mouse click. Might want to clear the selection in some cases but for now having more cells selected when changing modes etc is much better than it clearing all the time --- docs/js/main.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/js/main.js b/docs/js/main.js index 6f3b94a2..d6d46e2c 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -1014,14 +1014,6 @@ onload = function() { if (!pu.ondown_key) { pu.ondown_key = ondown_key; } - // This segment of code I added for a purpose but don't recollect the reason. - // After the new improvements maybe this is not needed but for now retaining it as it doesn't impact anything. - if (pu.selection.length > 0 && e.target.id.indexOf("sub_sudoku") == -1 && e.target.id.indexOf("st_sudoku") == -1 && - e.target.id != "float-canvas" && !isCtrlKeyHeld(e) && !isShiftKeyHeld(e)) { - // clear selection - pu.selection = []; - pu.redraw(); - } // Middle click for switching problem and solution // Applicable only in setter mode if (document.getElementById("title").textContent.toLowerCase().includes("setter")) { From 214cf7841b1d1f267c32532e0b8259391356466c Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Fri, 16 Feb 2024 01:12:53 -0600 Subject: [PATCH 12/13] Shift-click will remove multiple cells from the selection if the user clicks on a selected cell and drags --- docs/js/class_p.js | 5 +++++ docs/js/main.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index 61f034e9..ed87fe8c 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -73,6 +73,7 @@ class Puzzle { this.drawing_mode = -1; this.cursol = 0; this.cursolS = 0; + this.select_remove = false; this.panelflag = false; // Drawing mode this.mmode = ""; // Problem mode @@ -8682,6 +8683,10 @@ class Puzzle { this.gridtype !== "snub_square" && this.gridtype !== "cairo_pentagonal" && this.gridtype !== "rhombitrihexagonal" && this.gridtype !== "deltoidal_trihexagonal") { // do nothing + } else if (this.select_remove && this.drawing) { + let i = this.selection.indexOf(num); + if (i !== -1) + this.selection.splice(i, 1); } else if (!this.selection.includes(num) && this.drawing) { this.selection.push(num); } diff --git a/docs/js/main.js b/docs/js/main.js index d6d46e2c..afde2a69 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -127,6 +127,11 @@ onload = function() { var x = obj.x, y = obj.y, num = obj.num; + + // Remember whether this cell was already in the selection so we can + // remove instead of add cells + pu.select_remove = ctrl && pu.selection.indexOf(num) !== -1; + let skip_mouseevent = restrict_mouse(num); if (pu.point[num].use === 1 && !skip_mouseevent) { if (e.type === "dblclick") { From 5762d72d57f87e824a7558f6f7f65b9a0d6691b0 Mon Sep 17 00:00:00 2001 From: Zach Wegner Date: Fri, 9 Feb 2024 16:24:24 -0600 Subject: [PATCH 13/13] Treat shift-click like right click/ctrl click in certain submodes for convenience --- docs/js/class_p.js | 16 +++++++++++++--- docs/js/main.js | 8 +++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/js/class_p.js b/docs/js/class_p.js index ed87fe8c..31c02017 100644 --- a/docs/js/class_p.js +++ b/docs/js/class_p.js @@ -7914,8 +7914,18 @@ class Puzzle { mouseevent(x, y, num, ctrl_key = false) { if (!pu.replay) { num = this.recalculate_num(x, y, num); //for uniform tiling - let submode = this.mode[this.mode.qa][this.mode[this.mode.qa].edit_mode][0]; - switch (this.mode[this.mode.qa].edit_mode) { + let edit_mode = this.mode[this.mode.qa].edit_mode; + let submode = this.mode[this.mode.qa][edit_mode][0]; + + // Map shift/ctrl-click to right click in certain modes for convenience + if (ctrl_key && this.mouse_mode === "down_left" && + (edit_mode === "surface" || edit_mode === "combi")) { + this.mouse_mode = "down_right"; + this.mouse_click = 2; + this.mouse_click_last = 2; + } + + switch (edit_mode) { case "surface": this.mouse_surface(x, y, num); break; @@ -9572,7 +9582,7 @@ class Puzzle { break; case "edgexoi": case "tents": - if (this.mouse_mode === "down_right" || this.ondown_key === "touchstart") { + if (this.mouse_click === 2 || this.ondown_key === "touchstart") { num = this.coord_p_edgex(x, y, 0.3); } else { num = this.coord_p_edgex(x, y, 0.01); diff --git a/docs/js/main.js b/docs/js/main.js index afde2a69..54cf215c 100644 --- a/docs/js/main.js +++ b/docs/js/main.js @@ -128,6 +128,8 @@ onload = function() { y = obj.y, num = obj.num; + let ctrl = isCtrlKeyHeld(e) || isShiftKeyHeld(e); + // Remember whether this cell was already in the selection so we can // remove instead of add cells pu.select_remove = ctrl && pu.selection.indexOf(num) !== -1; @@ -137,17 +139,17 @@ onload = function() { if (e.type === "dblclick") { pu.mouse_mode = "down_left"; pu.mouse_click = 0; - pu.dblmouseevent(x, y, num, isCtrlKeyHeld(e) || isShiftKeyHeld(e)); + pu.dblmouseevent(x, y, num, ctrl); } else if (event.button === 2) { // right click pu.mouse_mode = "down_right"; pu.mouse_click = 2; pu.mouse_click_last = 2; - pu.mouseevent(x, y, num, isCtrlKeyHeld(e)); + pu.mouseevent(x, y, num, ctrl); } else { // Left click or tap pu.mouse_mode = "down_left"; pu.mouse_click = 0; pu.mouse_click_last = 1; - pu.mouseevent(x, y, num, isCtrlKeyHeld(e) || isShiftKeyHeld(e)); + pu.mouseevent(x, y, num, ctrl); } } }