Simulate keypress as keydown and keyup - javascript

I build a game and have an issue.
My game looks for a few keydown which move the character.
I added an A* algorithm to let the players the option to move their character with a mouse click (It gives the keys for destination point).
My issue is:
The code is looking for keydown which allowing continuous click and the algorithm return the keys for destination point which should be keypress as per-key.
How should I simulate the keypress for this? (The main question is in AStarWalk function)
Code:
class Event {
constructor(engine) {
this.engine = engine;
this.engine.event = this;
this.focus = false;
this.pressedKeys = {};
this.events = {};
// - - - - - - - - - -
// Events
this.onKey('keydown', 'gameWindow', ['KeyA', 'ArrowLeft'], () => {
this.engine.player.movePlayer(1);
}); // Left
this.onKey('keydown', 'gameWindow', ['KeyW', 'ArrowUp'], () => {
this.engine.player.movePlayer(3);
}); // Up
this.onKey('keydown', 'gameWindow', ['KeyD', 'ArrowRight'], () => {
this.engine.player.movePlayer(2);
}); // Right
this.onKey('keydown', 'gameWindow', ['KeyS', 'ArrowDown'], () => {
this.engine.player.movePlayer(0);
}); // Down
this.onKey('keydown', 'gameWindow', ['KeyR'], () => {
this.engine.player.running = true;
}); // Start running
this.onKey('keyup', 'gameWindow', ['KeyR'], () => {
this.engine.player.running = false;
}); // Stop running
this.onKey('keyup', 'gameWindow', ['PrintScreen'], () => {
console.log('save a picture');
});
this.onKey('focus', 'gameWindow', () => {
this.focus = true;
}); // On focus the game
this.onKey('focusout', 'gameWindow', () => {
this.focus = false;
this.pressedKeys = {};
}); // Focus has out from game
this.onKey('mousedown','gameWindow', [0], (e) => {
let x = Math.floor(e.layerX / this.engine.mapSettings.pxPerSquare);
let y = Math.floor(e.layerY / this.engine.mapSettings.pxPerSquare);
let node = [x, y];
let path = this.engine.astar.findPath(node);
this.AStarWalk(path);
});
// - - - - - - - - - -
// addEventListener
// code - for keyboard
// botton - for mouse
for (let id in this.events) {
for (let type in this.events[id]) {
if (!this.events[id].hasOwnProperty(type))
continue;
document.getElementById(id).addEventListener(type, (event) => {
let e = event;
if (this.inArray(type, ['keydown', 'keyup']))
event.preventDefault();
// Define variables
let key = e.code || e.button; // e.keyCode || e.which;
// Check if the key has event
if (!this.events[id][type].hasOwnProperty(key) && !this.events[id]['keyup'].hasOwnProperty(key))
return;
// Check if the key already pressed
if (this.pressedKeys.hasOwnProperty(key) && type === 'keydown')
return;
// Do
if (type === 'keydown')
this.pressedKeys[key] = e;
else if (type === 'keyup') {
this.runCallbacks(this.events[id][type][key]);
}
else if (this.inArray(type, ['focus', 'focusout'])) {
for (let callback in this.events[id][type]) {
if (!this.events[id][type].hasOwnProperty(callback))
continue;
this.events[id][type][callback](e);
}
}
else if (this.inArray(type, ['mousedown', 'mouseup'])) {
this.runCallbacks(this.events[id][type][key], e);
}
});
document.getElementById(id).addEventListener('contextmenu', (e) => {
e.preventDefault();
});
}
}
// - - - - - - - - - -
// Thread
this.engine.thread.thread(true, 0, (1000 / this.engine.graphic.lastfps), () => {
for (let id in this.events) {
for (let key in this.pressedKeys) {
this.runCallbacks(this.events[id].keydown[key], key);
}
}
});
}
runCallbacks(array, key) {
let e;
for (let callback in array) {
if (!array.hasOwnProperty(callback))
continue;
e = (this.pressedKeys[key] instanceof Event) ? this.pressedKeys[key] : key;
array[callback](e);
}
}
inArray(key, array) {
for (let i in array)
if (array.hasOwnProperty(i) && array[i] === key)
return true;
return false;
}
onKey() {
let event = arguments[0];
let id = arguments[1];
let keyCode = (this.inArray(event, ['keydown', 'keyup', 'mousedown', 'mouseup'])) ? arguments[2] : null;
let callback = (!this.inArray(event, ['keydown', 'keyup', 'mousedown', 'mouseup'])) ? arguments[2] : arguments[3];
// Convert keyCodeList to an array based on keyCode.
if (keyCode != null) {
if (!keyCode instanceof Array)
keyCode = [keyCode];
}
// Create event id if is not exist.
if (!this.events.hasOwnProperty(id)) {
this.events[id] = {
keydown: {},
keyup: {},
focus: [],
focusout: [],
mousedown: {},
mouseup: {}
};
}
// Add event to an array
if (keyCode !== null) {
let done = false;
for (let k in keyCode) {
if (!keyCode.hasOwnProperty(k))
continue;
k = keyCode[k].toString();
for (let key in this.events[id][event]) {
if (key === k) {
if (callback !== undefined)
this.events[id][event][k].push(callback);
if (event === 'keydown')
this.events[id]['keyup'][k].push(() => { delete this.pressedKeys[k]; });
done = true;
break;
}
}
if (!done) {
if (callback !== undefined)
this.events[id][event][k] = [callback];
if (event === 'keydown')
this.events[id]['keyup'][k] = [() => { delete this.pressedKeys[k]; }];
}
}
}
else {
this.events[id][event].push(callback);
}
}
AStarGetKey(state) {
if (state === 'LEFT') return 'ArrowLeft';
if (state === 'UP') return 'ArrowUp';
if (state === 'RIGHT') return 'ArrowRight';
if (state === 'DOWN') return 'ArrowDown';
if (state === 'LEFTUP') return ['ArrowLeft', 'ArrowUp'];
if (state === 'LEFTDOWN') return ['ArrowLeft', 'ArrowDown'];
if (state === 'RIGHTUP') return ['ArrowRight', 'ArrowUp'];
if (state === 'RIGHTDOWN') return ['ArrowRight', 'ArrowDown'];
return false;
}
AStarWalk(path) {
for (let i in path) {
let key = this.AStarGetKey(path[i]);
if (!key) break;
// How to simulate the keypress per key which works with keydown ?
document.getElementById('gameWindow').dispatchEvent(new KeyboardEvent('keypress',{'code': key}));
}
}
}
Sorry for my English guys, is not my native language. I hope you will find the power to understand me ;-)

Related

Javascript RangeError on website proxy RangeError: Failed to construct 'Response': The status provided (0) is outside the range [200, 599]

Image Here
Hello! I am trying to add a search bar that acts like a proxy at the top of my website. Whenever I input a website like https://stackoverflow.com I receive an error on the website. RangeError: Failed to construct 'Response': The status provided (0) is outside the range [200, 599] (Note: I am using the ultraviolet service) If you'd like to test yourself the website is https://lun.netlify.app also no errors seem to appear in the console log.
importScripts('/uv/uv.bundle.js');
importScripts('/uv/uv.config.js');
class UVServiceWorker extends EventEmitter {
constructor(config = __uv$config) {
super();
if (!config.bare) config.bare = '/bare/';
this.addresses = typeof config.bare === 'string' ? [ new URL(config.bare, location) ] : config.bare.map(str => new URL(str, location));
this.headers = {
csp: [
'cross-origin-embedder-policy',
'cross-origin-opener-policy',
'cross-origin-resource-policy',
'content-security-policy',
'content-security-policy-report-only',
'expect-ct',
'feature-policy',
'origin-isolation',
'strict-transport-security',
'upgrade-insecure-requests',
'x-content-type-options',
'x-download-options',
'x-frame-options',
'x-permitted-cross-domain-policies',
'x-powered-by',
'x-xss-protection',
],
forward: [
'accept-encoding',
'connection',
'content-length',
'content-type',
'user-agent',
],
};
this.method = {
empty: [
'GET',
'HEAD'
]
};
this.statusCode = {
empty: [
204,
304,
],
};
this.config = config;
};
async fetch({ request }) {
if (!request.url.startsWith(location.origin + (this.config.prefix || '/service/'))) {
return fetch(request);
};
try {
const ultraviolet = new Ultraviolet(this.config);
if (typeof this.config.construct === 'function') {
this.config.construct(ultraviolet, 'service');
};
const db = await ultraviolet.cookie.db();
ultraviolet.meta.origin = location.origin;
ultraviolet.meta.base = ultraviolet.meta.url = new URL(ultraviolet.sourceUrl(request.url));
const requestCtx = new RequestContext(
request,
this,
ultraviolet,
!this.method.empty.includes(request.method.toUpperCase()) ? await request.blob() : null
);
if (ultraviolet.meta.url.protocol === 'blob:') {
requestCtx.blob = true;
requestCtx.base = requestCtx.url = new URL(requestCtx.url.pathname);
};
if (request.referrer && request.referrer.startsWith(location.origin)) {
const referer = new URL(ultraviolet.sourceUrl(request.referrer));
if (ultraviolet.meta.url.origin !== referer.origin && request.mode === 'cors') {
requestCtx.headers.origin = referer.origin;
};
requestCtx.headers.referer = referer.href;
};
const cookies = await ultraviolet.cookie.getCookies(db) || [];
const cookieStr = ultraviolet.cookie.serialize(cookies, ultraviolet.meta, false);
const browser = Ultraviolet.Bowser.getParser(self.navigator.userAgent).getBrowserName();
if (browser === 'Firefox' && !(request.destination === 'iframe' || request.destination === 'document')) {
requestCtx.forward.shift();
};
if (cookieStr) requestCtx.headers.cookie = cookieStr;
const reqEvent = new HookEvent(requestCtx, null, null);
this.emit('request', reqEvent);
if (reqEvent.intercepted) return reqEvent.returnValue;
const response = await fetch(requestCtx.send);
if (response.status === 500) {
return Promise.reject('');
};
const responseCtx = new ResponseContext(requestCtx, response, this);
const resEvent = new HookEvent(responseCtx, null, null);
this.emit('beforemod', resEvent);
if (resEvent.intercepted) return resEvent.returnValue;
for (const name of this.headers.csp) {
if (responseCtx.headers[name]) delete responseCtx.headers[name];
};
if (responseCtx.headers.location) {
responseCtx.headers.location = ultraviolet.rewriteUrl(responseCtx.headers.location);
};
if (responseCtx.headers['set-cookie']) {
Promise.resolve(ultraviolet.cookie.setCookies(responseCtx.headers['set-cookie'], db, ultraviolet.meta)).then(() => {
self.clients.matchAll().then(function (clients){
clients.forEach(function(client){
client.postMessage({
msg: 'updateCookies',
url: ultraviolet.meta.url.href,
});
});
});
});
delete responseCtx.headers['set-cookie'];
};
if (responseCtx.body) {
switch(request.destination) {
case 'script':
case 'worker':
responseCtx.body = `if (!self.__uv && self.importScripts) importScripts('${__uv$config.bundle}', '${__uv$config.config}', '${__uv$config.handler}');\n`;
responseCtx.body += ultraviolet.js.rewrite(
await response.text()
);
break;
case 'style':
responseCtx.body = ultraviolet.rewriteCSS(
await response.text()
);
break;
case 'iframe':
case 'document':
if (isHtml(ultraviolet.meta.url, (responseCtx.headers['content-type'] || ''))) {
responseCtx.body = ultraviolet.rewriteHtml(
await response.text(),
{
document: true ,
injectHead: ultraviolet.createHtmlInject(
this.config.handler,
this.config.bundle,
this.config.config,
ultraviolet.cookie.serialize(cookies, ultraviolet.meta, true),
request.referrer
)
}
);
};
};
};
if (requestCtx.headers.accept === 'text/event-stream') {
responseCtx.headers['content-type'] = 'text/event-stream';
};
this.emit('response', resEvent);
if (resEvent.intercepted) return resEvent.returnValue;
return new Response(responseCtx.body, {
headers: responseCtx.headers,
status: responseCtx.status,
statusText: responseCtx.statusText,
});
} catch(err) {
return new Response(err.toString(), {
status: 500,
});
};
};
getBarerResponse(response) {
const headers = {};
const raw = JSON.parse(response.headers.get('x-bare-headers'));
for (const key in raw) {
headers[key.toLowerCase()] = raw[key];
};
return {
headers,
status: +response.headers.get('x-bare-status'),
statusText: response.headers.get('x-bare-status-text'),
body: !this.statusCode.empty.includes(+response.headers.get('x-bare-status')) ? response.body : null,
};
};
get address() {
return this.addresses[Math.floor(Math.random() * this.addresses.length)];
};
static Ultraviolet = Ultraviolet;
};
self.UVServiceWorker = UVServiceWorker;
class ResponseContext {
constructor(request, response, worker) {
const { headers, status, statusText, body } = !request.blob ? worker.getBarerResponse(response) : {
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries([...response.headers.entries()]),
body: response.body,
};
this.request = request;
this.raw = response;
this.ultraviolet = request.ultraviolet;
this.headers = headers;
this.status = status;
this.statusText = statusText;
this.body = body;
};
get url() {
return this.request.url;
}
get base() {
return this.request.base;
};
set base(val) {
this.request.base = val;
};
};
class RequestContext {
constructor(request, worker, ultraviolet, body = null) {
this.ultraviolet = ultraviolet;
this.request = request;
this.headers = Object.fromEntries([...request.headers.entries()]);
this.method = request.method;
this.forward = [...worker.headers.forward];
this.address = worker.address;
this.body = body || null;
this.redirect = request.redirect;
this.credentials = 'omit';
this.mode = request.mode === 'cors' ? request.mode : 'same-origin';
this.blob = false;
};
get send() {
return new Request((!this.blob ? this.address.href + 'v1/' : 'blob:' + location.origin + this.url.pathname), {
method: this.method,
headers: {
'x-bare-protocol': this.url.protocol,
'x-bare-host': this.url.hostname,
'x-bare-path': this.url.pathname + this.url.search,
'x-bare-port': this.url.port || (this.url.protocol === 'https:' ? '443' : '80'),
'x-bare-headers': JSON.stringify(this.headers),
'x-bare-forward-headers': JSON.stringify(this.forward),
},
redirect: this.redirect,
credentials: this.credentials,
mode: location.origin !== this.address.origin ? 'cors' : this.mode,
body: this.body
});
};
get url() {
return this.ultraviolet.meta.url;
};
set url(val) {
this.ultraviolet.meta.url = val;
};
get base() {
return this.ultraviolet.meta.base;
};
set base(val) {
this.ultraviolet.meta.base = val;
};
}
function isHtml(url, contentType = '') {
return (Ultraviolet.mime.contentType((contentType || url.pathname)) || 'text/html').split(';')[0] === 'text/html';
};
class HookEvent {
#intercepted;
#returnValue;
constructor(data = {}, target = null, that = null) {
this.#intercepted = false;
this.#returnValue = null;
this.data = data;
this.target = target;
this.that = that;
};
get intercepted() {
return this.#intercepted;
};
get returnValue() {
return this.#returnValue;
};
respondWith(input) {
this.#returnValue = input;
this.#intercepted = true;
};
};
var R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function'
? R.apply
: function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
}
var ReflectOwnKeys
if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys
} else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target)
.concat(Object.getOwnPropertySymbols(target));
};
} else {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target);
};
}
function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning);
}
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
return value !== value;
}
function EventEmitter() {
EventEmitter.init.call(this);
}
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;
var defaultMaxListeners = 10;
function checkListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
}
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
}
defaultMaxListeners = arg;
}
});
EventEmitter.init = function() {
if (this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
}
this._maxListeners = n;
return this;
};
function _getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this);
};
EventEmitter.prototype.emit = function emit(type) {
var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error');
var events = this._events;
if (events !== undefined)
doError = (doError && events.error === undefined);
else if (!doError)
return false;
if (doError) {
var er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) {
throw er;
}
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
err.context = er;
throw err;
}
var handler = events[type];
if (handler === undefined)
return false;
if (typeof handler === 'function') {
ReflectApply(handler, this, args);
} else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
ReflectApply(listeners[i], this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
checkListener(listener);
events = target._events;
if (events === undefined) {
events = target._events = Object.create(null);
target._eventsCount = 0;
} else {
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
events = target._events;
}
existing = events[type];
}
if (existing === undefined) {
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// Check for listener leak
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + String(type) + ' listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
ProcessEmitWarning(w);
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0)
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments);
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
checkListener(listener);
events = this._events;
if (events === undefined)
return this;
list = events[type];
if (list === undefined)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else {
spliceOne(list, position);
}
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i;
events = this._events;
if (events === undefined)
return this;
if (events.removeListener === undefined) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else
delete events[type];
}
return this;
}
if (arguments.length === 0) {
var keys = Object.keys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners !== undefined) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (events === undefined)
return [];
var evlistener = events[type];
if (evlistener === undefined)
return [];
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events !== undefined) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener !== undefined) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1];
list.pop();
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
function once(emitter, name) {
return new Promise(function (resolve, reject) {
function errorListener(err) {
emitter.removeListener(name, resolver);
reject(err);
}
function resolver() {
if (typeof emitter.removeListener === 'function') {
emitter.removeListener('error', errorListener);
}
resolve([].slice.call(arguments));
};
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== 'error') {
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
}
});
}
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
if (typeof emitter.on === 'function') {
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
}
}
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
if (typeof emitter.on === 'function') {
if (flags.once) {
emitter.once(name, listener);
} else {
emitter.on(name, listener);
}
} else if (typeof emitter.addEventListener === 'function') {
emitter.addEventListener(name, function wrapListener(arg) {
if (flags.once) {
emitter.removeEventListener(name, wrapListener);
}
listener(arg);
});
} else {
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
}
}

How to pass correct state value into callback function inside useEffect hook?

I'm trying to change a state of a variable that holds photo ID when a user presses arrow keys or clicks on an image, then I render my image depending on that photo ID.
CODE:
const Lightbox = ({ filteredPhotos }) => {
const [currentPhotoId, setCurrentPhotoId] = useState(null);
const currentPhoto = filteredPhotos.filter((photo) => photo.strapiId === currentPhotoId)[0];
let lightbox = "";
const getLastPhotoId = (filteredPhotos) => {
const ids = filteredPhotos.map((item) => item.strapiId).sort((a, b) => a - b);
const result = ids.slice(-1)[0];
return result;
};
//Select image on click
const selectImage = useCallback(
(e) => {
const divId = parseInt(e.target.parentElement.parentElement.className.split(" ")[0]);
const imgSelected = e.target.parentElement.parentElement.className.includes("gatsby-image-wrapper");
imgSelected && divId !== NaN ? setCurrentPhotoId(divId) : setCurrentPhotoId(null);
},
[setCurrentPhotoId]
);
//Change image on keypress
const changeImage = useCallback(
(e, currentPhotoId) => {
if (document.location.pathname !== "/portfolio" || currentPhotoId === null) return;
const key = e.keyCode;
console.log("changeImage start: ", currentPhotoId);
if (key === 27) {
setCurrentPhotoId(null);
} else if (key === 39) {
setCurrentPhotoId(currentPhotoId + 1);
} else if (key === 37) {
if (currentPhotoId - 1 <= 0) {
setCurrentPhotoId(getLastPhotoId(filteredPhotos))
} else {
setCurrentPhotoId(currentPhotoId - 1)
}
}
},
[setCurrentPhotoId]
);
useEffect(() => {
const gallery = document.getElementById("portfolio-gallery");
gallery.addEventListener("click", (e) => selectImage(e));
document.addEventListener("keydown", (e) => changeImage(e, currentPhotoId));
}, [selectImage, changeImage]);
useEffect(() => {
console.log(currentPhotoId);
}, [currentPhotoId]);
return currentPhotoId === null ? (
<div id="lightbox" className="lightbox">
<h4>Nothing to display</h4>
</div>
) : (
<div id="lightbox" className="lightbox lightbox-active">
<img src={currentPhoto.photo.childImageSharp.fluid.src} alt={currentPhoto.categories[0].name} />
</div>
);
};
export default Lightbox;
Setting up a state by clicking/un-clicking on an image works without a problem, the state is set to a correct number.
But my function that handles keydown events is returned because my state currentPhotoId is null, and I don't get it why when I've set my state by selecting an image.
If I add currentPhotoId in useEffect dependency array
useEffect(() => {
const gallery = document.getElementById("portfolio-gallery");
gallery.addEventListener("click", (e) => selectImage(e));
document.addEventListener("keydown", (e) => changeImage(e, currentPhotoId));
}, [selectImage, changeImage, currentPhotoId]); //here
it breaks my selectImage (click) function. And also the more times user presses right arrow key, the more times it updates the state, resulting into so many updates it crashes down site eventually.
What am I doing wrong? Why my state isn't updating correctly?
FIX:
useEffect(() => {
document.addEventListener("keydown", changeImage); //invoking not calling
return () => {
document.removeEventListener("keydown", changeImage);
};
}, [changeImage, currentPhotoId]);
const changeImage = useCallback(
(e) => {
if (document.location.pathname !== "/portfolio" || currentPhotoId === null) return;
const key = e.keyCode;
if (key === 27) {
setCurrentPhotoId(null);
} else if (key === 39) {
setCurrentPhotoId(currentPhotoId + 1);
} else if (key === 37) {
if (currentPhotoId - 1 <= 0) {
setCurrentPhotoId(getLastPhotoId(filteredPhotos));
} else {
setCurrentPhotoId(currentPhotoId - 1);
}
}
},
[setCurrentPhotoId, currentPhotoId] //added currentPhotoId as dependency
);
So in useEffect I was making a mistake with calling my function, instead of invoking it and this was just adding event listeners to infinite.
And in my callback function instead of passing the state as an argument, I've added id as a dependency.
Also, I've separated selectImage and changeImage into two useEffects, for selectImage useEffect I don't have currentPhotoId as a dependency.
If somebody wants to elaborate more on the details, feel free to do so.

Move one item in object up / down

I have following code and I was wondering, if that could be solved better?
The objective is to move an item within an object up / down.
moveTab = (action, tab) => event => {
event.stopPropagation();
let order = { ...this.state.settings.layout }
let sorted = false;
let sortedArray = Object.keys(order).sort((a, b) => {
if (!sorted && ((action === 'up' && b === tab) || (action === 'down' && a === tab))) {
sorted = true;
return 1;
}
return 0;
});
let sortedObject = {}
sortedArray.map(key => sortedObject[key] = order[key]);
console.log(order);
console.log(sortedObject);
// ... more code ...
}

How to make a function that is wrapped inside a function returns on behalf of the parent function

I have a function that inside a function. I want the parent function to return an updated object (after the loop ends), currently, it returns undefined because the parent function returns nothing, only the child function function(items) returns data. How can I make the parent function returns updated return_data? Thanks a lot.
reportParser: (report) => {
const return_data = {
"click": "[id^='AUTOGENBOOKMARK_75_'].style_12",
"clicker": "[id^='AUTOGENBOOKMARK_74_'].style_12",
"openning": ".style_25 > .style_24",
"openner": ".style_25 > .style_24",
"name": "[id^='AUTOGENBOOKMARK_7_'].style_12",
"subject": "[id^='AUTOGENBOOKMARK_9_'].style_12",
"audience": "[id^='AUTOGENBOOKMARK_11_'].style_12",
"sent_mails": "[id^='AUTOGENBOOKMARK_20_'].style_12",
"send_date": "[id^='AUTOGENBOOKMARK_32_'].style_12",
"cancel_subscription_click": ".style_25 > .style_24",
"cancel_subscription_clicker": ".style_25 > .style_24"
};
let remaining_keys = Object.keys(return_data).length;
for (let key in return_data) {
if (return_data.hasOwnProperty(key)) {
html2json.parse(report, function () {
return this.map(return_data[key], function ($item) {
return $item.text();
});
}).done(function (items) {
if (key === "click" || key === "clicker" || key === "sent_mails") {
items[0] = items[0].replace(/,/g, "");
return_data[key] = parseInt(items[0]);
} else if (key === "openning") {
items[items.length - 2] = items[items.length - 2].replace(/,/g, "");
return_data[key] = parseInt(items[items.length - 2]);
} else if (key === "openner") {
items[items.length - 3] = items[items.length - 3].replace(/,/g, "");
return_data[key] = parseInt(items[items.length - 3]);
} else if (key === "cancel_subscription_click") {
return_data[key] = parseInt(items[13]) + parseInt(items[18]) + parseInt(items[23]);
} else if (key === "cancel_subscription_clicker") {
return_data[key] = parseInt(items[11]) + parseInt(items[16]) + parseInt(items[21]);
} else {
return_data[key] = items[0];
}
remaining_keys--;
if (remaining_keys === 0) {
return_data["click"] -= return_data["cancel_subscription_click"];
return_data["clicker"] -= return_data["cancel_subscription_clicker"];
delete return_data.cancel_subscription_click;
delete return_data.cancel_subscription_clicker;
logger.debug(return_data);
return return_data;
}
}, function (err) {
// Handle error
});
}
}
}
The execution would be function -> object init -> wait for loop to update object -> return object
You can either use a callback function or write this function as a promise.
Callback is a function you will pass into your function to execute after the data is done.
For the callback function:
https://developer.mozilla.org/en-US/docs/Glossary/Callback_function
reportParser = (report, callback) => {
//... process data
html2json.parse(report, function() {
//...
}).done(function(items) {
//after you have done process and get return_data, use callback
callback(return_data);
})
}
So when you using reportParser:
reportParser(report, function(return_data) {
//whatever you want to do with return_data
})
For the promise:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
reportParser = (report) => {
return new Promise(function(resolve, reject) {
//... process data
html2json.parse(report, function() {
//...
}).done(function(items) {
//after you have done process and get return_data, use callback
resolve(return_data);
})
})
}
And when you using reportParser function:
reportParse(report).then(return_data => {
//whatever you want to do with return_data
})
Seems html2json.parse return a Promise, so in your case you have to return a Promise as well in your parent function
reportParser: (report) => {
const return_data = {
"click": "[id^='AUTOGENBOOKMARK_75_'].style_12",
"clicker": "[id^='AUTOGENBOOKMARK_74_'].style_12",
"openning": ".style_25 > .style_24",
"openner": ".style_25 > .style_24",
"name": "[id^='AUTOGENBOOKMARK_7_'].style_12",
"subject": "[id^='AUTOGENBOOKMARK_9_'].style_12",
"audience": "[id^='AUTOGENBOOKMARK_11_'].style_12",
"sent_mails": "[id^='AUTOGENBOOKMARK_20_'].style_12",
"send_date": "[id^='AUTOGENBOOKMARK_32_'].style_12",
"cancel_subscription_click": ".style_25 > .style_24",
"cancel_subscription_clicker": ".style_25 > .style_24"
};
let remaining_keys = Object.keys(return_data).length;
/* here return a Promise */
return new Promise((resolve, reject) => {
for (let key in return_data) {
if (return_data.hasOwnProperty(key)) {
html2json.parse(report, function () {
return this.map(return_data[key], function ($item) {
return $item.text();
});
}).done(function (items) {
if (key === "click" || key === "clicker" || key === "sent_mails") {
items[0] = items[0].replace(/,/g, "");
return_data[key] = parseInt(items[0]);
} else if (key === "openning") {
items[items.length - 2] = items[items.length - 2].replace(/,/g, "");
return_data[key] = parseInt(items[items.length - 2]);
} else if (key === "openner") {
items[items.length - 3] = items[items.length - 3].replace(/,/g, "");
return_data[key] = parseInt(items[items.length - 3]);
} else if (key === "cancel_subscription_click") {
return_data[key] = parseInt(items[13]) + parseInt(items[18]) + parseInt(items[23]);
} else if (key === "cancel_subscription_clicker") {
return_data[key] = parseInt(items[11]) + parseInt(items[16]) + parseInt(items[21]);
} else {
return_data[key] = items[0];
}
remaining_keys--;
if (remaining_keys === 0) {
return_data["click"] -= return_data["cancel_subscription_click"];
return_data["clicker"] -= return_data["cancel_subscription_clicker"];
delete return_data.cancel_subscription_click;
delete return_data.cancel_subscription_clicker;
logger.debug(return_data);
/* RESOLVE THE PROMISE */
resolve(return_data);
return return_data;
}
}, function (err) {
// Handle error
/* REJECT THE PROMISE ON ERROR */
reject(err);
});
}
}
});
}
than you will able to use your function as a regular Promise
reportParser(report).then((data) => {
// Work with the returned data
}).catch((err) => {
// Handle errors
})

How to call a function with the opposite boolean to what it was previously called with

Right, so I am making an Agario extension.
I want it so when you press a button (m) it will call a function (the setShowMass) function with the opposite boolean to what is was called with last time.
document.addEventListener('keydown',function(e){
var key = e.keyCode || e.which;
if(key == 71){
window.agar.drawGrid = !window.agar.drawGrid;
}
if(key == 77){
setShowMass(!massShown);
massShown = !massShown;
}
});
You can cache the result:
var oppositebool = true;
document.addEventListener('keydown',function(e){
var key = e.keyCode || e.which;
if(key == 71){
window.agar.drawGrid = !window.agar.drawGrid;
}
if(key == 77){
setShowMass(oppositebool);
oppositebool = !oppositebool;
}
});
You could also create a function that wraps another function and calls that function switching between true and false every time.
const trueFalse = (fn, initial) => {
return () => {
fn(initial);
initial = !initial;
}
};
const log = (bool) => console.log(bool);
const switchingLog = trueFalse(log, true);
switchingLog(); // true
switchingLog(); // false
switchingLog(); // true
You can create a wrapper function that remembers the last value. For example:
function callWithOppositeBoolean(func, startingValue) {
var bool = !startingValue;
return function() {
bool = !bool;
return func(bool);
}
}
Then you can use this to create a wrapped function that calls the original function, alternating with true and false:
var toggleShowMass = callWithOppositeBoolean(showMass, true);
toggleShowMass(); // showMass(true)
toggleShowMass(); // showMass(false)
toggleShowMass(); // showMass(true)

Categories