@arayutw/matrix-pointer
Version:
This is a library that assists in focusing on any position of a matrix and moving relative or absolute to that position.
392 lines (390 loc) • 11 kB
JavaScript
/**!
* matrix-pointer 0.0.1
* MIT License
* Copyright (c) 2023 Yuta Arai
**/
'use strict';
class Emitter {
constructor() {
this.Emitter$items = [];
}
on(name, handler, options) {
this.off(name, handler);
this.Emitter$items.push([name, handler, {
once: !!(options === null || options === void 0 ? void 0 : options.once)
}]);
}
off(name, handler) {
for (let a = this.Emitter$items, i = 0; a.length > i; i++) {
if ("*" === name || a[i][0] === name && a[i][1] === handler) {
a.splice(i--, 1);
}
}
}
emit(name, event) {
this.Emitter$items.forEach(entry => {
var _a;
if (name === entry[0]) {
entry[1](Object.assign(Object.assign({}, event), {
target: this,
type: name
}));
if ((_a = entry[2]) === null || _a === void 0 ? void 0 : _a.once) this.off(name, entry[1]);
}
});
}
}
function isId(id) {
return null !== id;
}
function findArroundX(row, sx, ny) {
if (Array.isArray(row)) {
const ex = row.length - 1;
sx = Math.min(Math.max(0, sx), ex);
let id = row[sx];
let diff = 0;
let nx = sx;
while (!isId(id) && (sx - diff > 0 || ex > sx + diff)) {
id = row[nx = sx - ++diff];
if (!isId(id)) {
id = row[nx = sx + diff];
}
}
if (isId(id)) {
return {
id,
x: nx,
y: ny
};
}
}
return null;
}
function findArroundY(matrix, nx, sy) {
if (Array.isArray(matrix)) {
const ey = matrix.length - 1;
sy = Math.min(Math.max(0, sy), ey);
let id;
const row = matrix[sy];
if (row) id = row[nx];
let diff = 0;
let ny = sy;
while (!isId(id) && (sy - diff > 0 || ey > sy + diff)) {
const row2 = matrix[ny = sy - ++diff];
if (row2) id = row2[nx];
if (!isId(id)) {
const row3 = matrix[ny = sy + diff];
if (row3) id = row3[nx];
}
}
if (isId(id)) {
return {
id,
x: nx,
y: ny
};
}
}
return null;
}
function moveTo(matrix, sx, sy) {
if (Array.isArray(matrix)) {
const ey = matrix.length - 1;
sx = Math.max(0, sx);
sy = Math.min(Math.max(0, sy), ey);
let diff = 0;
const row = matrix[sy];
if (row) {
const position = findArroundX(row, sx, sy);
if (position) return position;
}
while (sy - diff > 0 || ey > sy + diff) {
{
const ny = sy - ++diff;
const row2 = matrix[ny];
if (row2) {
const position = findArroundX(row2, sx, ny);
if (position) return position;
}
}
{
const ny = sy + diff;
const row2 = matrix[ny];
if (row2) {
const position = findArroundX(row2, sx, ny);
if (position) return position;
}
}
}
}
return null;
}
function getPosition(matrix, id, fallback) {
let defaultPosition = null;
const hasId = isId(id);
for (let ny = 0; matrix.length > ny; ny++) {
const row = matrix[ny];
if (Array.isArray(row)) {
for (let nx = 0; row.length > nx; nx++) {
const column = row[nx];
if (isId(column)) {
if (!defaultPosition) {
defaultPosition = {
id: column,
x: nx,
y: ny
};
if (!hasId && fallback) return defaultPosition;
}
if (column === id) {
return {
id: column,
x: nx,
y: ny
};
}
}
}
}
}
return fallback ? defaultPosition : null;
}
function findOneDirectionY(matrix, nx, sy, next) {
if (Array.isArray(matrix)) {
for (let ey = matrix.length - 1, ny = null === sy ? next ? 0 : ey : sy; next ? ey >= ny : ny >= 0; next ? ny++ : ny--) {
const row = matrix[ny];
if (Array.isArray(row)) {
const column = row[nx];
if (isId(column)) {
return {
id: column,
x: nx,
y: ny
};
}
}
}
}
return null;
}
function findOneDirectionX(row, sx, ny, next) {
if (Array.isArray(row)) {
for (let ex = row.length - 1, nx = null === sx ? next ? 0 : ex : sx; next ? ex >= nx : nx >= 0; next ? nx++ : nx--) {
const column = row[nx];
if (isId(column)) {
return {
id: column,
x: nx,
y: ny
};
}
}
}
return null;
}
function moveBy(matrix, startPosition, direction, next, loop, loose, jump) {
if (startPosition) {
const canLoopX = true === loop || loop && loop.x;
const canLoopY = true === loop || loop && loop.y;
const canLooseX = true === loose || loose && loose.x;
const canLooseY = true === loose || loose && loose.y;
const canJumpX = true === jump || jump && jump.x;
const canJumpY = true === jump || jump && jump.y;
let nx = startPosition.x;
let ny = startPosition.y;
let lx = nx;
let ly = ny;
const ey = matrix.length - 1;
if ("x" === direction) {
if (canLooseY) {
while (true) {
nx += next ? 1 : -1;
const ex = matrix[ny].length - 1;
const overEnd = nx > ex;
const overStart = 0 > nx;
if (overStart || overEnd) {
if (canLoopX) {
if (canJumpY) {
ny += next ? 1 : -1;
const overEnd2 = ny > ey;
const overStart2 = 0 > ny;
if (overStart2 || overEnd2) {
if (canLoopY) {
if (overEnd2) {
ny = 0;
} else {
ny = ey;
}
} else {
ny = ly;
}
}
ly = ny;
}
if (overEnd) {
nx = 0;
} else {
nx = matrix[ny].length - 1;
}
} else {
nx = lx;
}
}
if (nx === lx) return startPosition;
lx = nx;
const position = findArroundY(matrix, nx, ny);
if (position) return position;
}
} else {
{
const row = matrix[ny];
const position = findOneDirectionX(row, startPosition.x + (next ? 1 : -1), ny, next);
if (position) return position;
}
if (!canLoopX) {
return startPosition;
}
if (!canJumpY) {
const position = findOneDirectionX(matrix[ny], null, ny, next);
return position || startPosition;
}
while (true) {
ny += next ? 1 : -1;
const overEnd = ny > ey;
const overStart = 0 > ny;
if (overStart || overEnd) {
if (canLoopY) {
if (overEnd) {
ny = 0;
} else {
ny = ey;
}
} else {
ny = ly;
}
}
if (ny === ly) return startPosition;
ly = ny;
const position = findOneDirectionX(matrix[ny], null, ny, next);
if (position) return position;
}
}
} else {
if (canLooseX) {
while (true) {
ny += next ? 1 : -1;
const overEnd = ny > ey;
const overStart = 0 > ny;
if (overStart || overEnd) {
if (canLoopY) {
if (overEnd) {
ny = 0;
} else {
ny = ey;
}
if (canJumpX) {
nx += next ? 1 : -1;
const ex = matrix[ny].length - 1;
const overEnd2 = nx > ex;
const overStart2 = 0 > nx;
if (overStart2 || overEnd2) {
if (canLoopX) {
if (overEnd2) {
nx = 0;
} else {
nx = ex;
}
} else {
nx = lx;
}
}
lx = nx;
}
} else {
ny = ly;
}
}
if (ny === ly) return startPosition;
ly = ny;
const position = findArroundX(matrix[ny], nx, ny);
if (position) return position;
}
} else {
{
const position = findOneDirectionY(matrix, nx, startPosition.y + (next ? 1 : -1), next);
if (position) return position;
}
if (!canLoopY) {
return startPosition;
}
if (!canJumpX) {
const position = findOneDirectionY(matrix, nx, null, next);
return position || startPosition;
}
while (true) {
nx += next ? 1 : -1;
const row = matrix[ny];
const ex = row.length - 1;
const overEnd = nx > ex;
const overStart = 0 > nx;
if (overStart || overEnd) {
if (canLoopX) {
if (overEnd) {
nx = 0;
} else {
nx = ex;
}
} else {
nx = lx;
}
}
if (nx === lx) return startPosition;
lx = nx;
const position = findOneDirectionY(matrix, nx, null, next);
if (position) return position;
}
}
}
}
return null;
}
class MatrixPointer extends Emitter {
constructor(options) {
super();
this.position = null;
this.caches = {
matrix: []
};
this.jump = void 0 !== options.jump ? options.jump : true;
this.loop = void 0 !== options.loop ? options.loop : true;
this.loose = void 0 !== options.loose ? options.loose : true;
this.matrix = options.matrix;
}
update() {
const matrix = "function" === typeof this.matrix ? this.matrix() : this.matrix;
return this.caches.matrix = Array.isArray(matrix) ? matrix.filter(row => Array.isArray(row)).map(row => row.map(column => isId(column) ? column : null)) : [];
}
focus(newPosition) {
const oldPosition = this.position;
this.position = newPosition;
if (!(!oldPosition && !newPosition || (oldPosition === null || oldPosition === void 0 ? void 0 : oldPosition.id) === (newPosition === null || newPosition === void 0 ? void 0 : newPosition.id))) {
if (oldPosition) this.emit("blur", oldPosition);
if (newPosition) this.emit("focus", newPosition);
}
return newPosition;
}
moveTo(x, y) {
const matrix = this.update();
return this.focus("number" === typeof x && "number" === typeof y ? moveTo(matrix, x, y) : getPosition(matrix, x, false));
}
moveBy(direction, next) {
var _a;
const matrix = this.update();
return this.focus(moveBy(matrix, getPosition(matrix, (_a = this.position) === null || _a === void 0 ? void 0 : _a.id, true), direction, next, this.loop, this.loose, this.jump));
}
destroy() {
this.caches.matrix = [];
this.off("*");
}
}
module.exports = MatrixPointer;