diff --git a/css/chess.css b/css/chess.css index cf688a3..a4f2789 100644 --- a/css/chess.css +++ b/css/chess.css @@ -361,10 +361,6 @@ button:disabled .silhouette { z-index: 1; } -#cb_peers { - color: #cccccc; -} - .badges { position: fixed; bottom: 12px; diff --git a/index.html b/index.html index 1901845..2e76d32 100644 --- a/index.html +++ b/index.html @@ -242,7 +242,6 @@ and the joined piece from the capturing side moves to a new location from its original position as with any other capture.

Pawns are promoted when they reach the final row on the opposite side of the board, even if they were moved there by the other player as part of a joined pair.

-

diff --git a/js/pacosako_io.js b/js/pacosako_io.js new file mode 100644 index 0000000..ee2d556 --- /dev/null +++ b/js/pacosako_io.js @@ -0,0 +1,310 @@ +'use strict'; + +const API_BASE = `https://jessemcdonald.info/pacosako/api`; + +const SHORT_TIMEOUT = 5000/*ms*/; +const LONG_TIMEOUT = 90000/*ms*/; + +const RETRIES = 3; +const RETRY_DELAY = 1000/*ms*/; + +const games = {}; + +const meta = { + games: {}, + lastModified: 0, + listeners: {}, + nextId: 1, + currentRequest: null, +}; + +/* One-time request, no caching or polling */ +function getGameState(gameId, retries) { + if (arguments.length < 2) { + retries = RETRIES; + } + return new Promise((resolve, reject) => { + $.ajax({ + dataType: 'json', + contentType: 'application/json', + url: `${API_BASE}/game/${gameId}`, + cache: false, + timeout: SHORT_TIMEOUT, + }).done((data, textStatus, jqXHR) => { + resolve(data); + }).fail((jqXHR, textStatus, errorThrown) => { + if ((!jqXHR.status || jqXHR.status < 400 || jqXHR.status > 499) && retries > 0) { + setTimeout(() => { + getGameState(gameId, retries - 1).then(resolve, reject); + }, RETRY_DELAY); + } else { + reject({ status: jqXHR.status, textStatus, errorThrown }); + } + }); + }); +} + +/* One-time request, no caching or polling */ +function getGameMeta(gameId, retries) { + if (arguments.length < 2) { + retries = RETRIES; + } + return new Promise((resolve, reject) => { + $.ajax({ + dataType: 'json', + contentType: 'application/json', + url: `${API_BASE}/meta/${gameId}`, + cache: false, + timeout: SHORT_TIMEOUT, + }).done((data, textStatus, jqXHR) => { + resolve(data); + }).fail((jqXHR, textStatus, errorThrown) => { + if ((!jqXHR.status || jqXHR.status < 400 || jqXHR.status > 499) && retries > 0) { + setTimeout(() => { + getGameState(gameId, retries - 1).then(resolve, reject); + }, RETRY_DELAY); + } else { + reject({ status: jqXHR.status, textStatus, errorThrown }); + } + }); + }); +} + +function onGameUpdate(gameId, callback) { + const game = gamePollState(gameId); + + if (Object.keys(game.listeners).length === 0) { + startGamePoll(gameId, game.data.modified); + } + + const cbId = game.nextId; + game.nextId += 1; + game.listeners[cbId] = callback; + + /* delay the initial run of the callback to the microtask queue */ + (Promise.resolve()).then(() => { + if (cbId in game.listeners) { + try { + callback(game.data, gameId); + } catch(err) { + console.error('uncaught exception in callback', err); + } + } + }); + + return function clearGameUpdate(keepPolling) { + if (cbId in game.listeners) { + delete game.listeners[cbId]; + if (!keepPolling && Object.keys(game.listeners).length === 0) { + stopGamePoll(gameId); + } + } + } +} + +function onMetaUpdate(callback) { + if (Object.keys(meta.listeners).length === 0) { + startMetaPoll(meta.lastModified); + } + + const cbId = meta.nextId; + meta.nextId += 1; + meta.listeners[cbId] = callback; + + /* delay the initial run of the callback to the microtask queue */ + (Promise.resolve()).then(() => { + if (cbId in meta.listeners) { + for (const gameId in meta.games) { + try { + callback(meta.games[gameId], gameId); + } catch(err) { + console.error('uncaught exception in callback', err); + } + } + } + }); + + return function clearMetaUpdate(keepPolling) { + if (cbId in meta.listeners) { + delete meta.listeners[cbId]; + if (!keepPolling && Object.keys(meta.listeners).length === 0) { + stopMetaPoll(); + } + } + } +} + +function sendUpdate(gameId, data, retries) { + if (arguments.length < 3) { + retries = RETRIES; + } + return new Promise((resolve, reject) => { + $.ajax({ + dataType: 'json', + contentType: 'application/json', + url: `${API_BASE}/game/${gameId}`, + method: 'POST', + cache: false, + data: JSON.stringify(data), + timeout: SHORT_TIMEOUT, + }).done((responseData, textStatus, jqXHR) => { + resolve({ status: jqXHR.status, data: responseData }); + }).fail((jqXHR, textStatus, errorThrown) => { + if ((!jqXHR.status || jqXHR.status < 400 || jqXHR.status > 499) && retries > 0) { + setTimeout(() => { + getGameState(gameId, retries - 1).then(resolve, reject); + }, RETRY_DELAY); + } else { + reject({ status: jqXHR.status, textStatus, errorThrown }); + } + }); + }); +} + +function gamePollState(gameId) { + if (gameId in games === false) { + games[gameId] = { + currentRequest: null, + data: { gameId, modified: 0 }, + listeners: [], + nextId: 1, + }; + } + + return games[gameId]; +} + +function processGame(gameId, data) { + const game = gamePollState(gameId); + + if (data && data.modified > game.data.modified) { + game.data = data; + for (const cbId in game.listeners) { + try { + game.listeners[cbId](data, gameId); + } catch(err) { + console.error('uncaught exception in callback', err); + } + } + } +} + +function startGamePoll(gameId, afterTime) { + const game = gamePollState(gameId); + + if (game.currentRequest === null) { + if (afterTime === undefined) { + afterTime = game.data.modified; + } + + const thisRequest = game.currentRequest = $.ajax({ + dataType: 'json', + contentType: 'application/json', + url: `${API_BASE}/game/${gameId}/poll/${afterTime}`, + cache: false, + timeout: LONG_TIMEOUT, + }).done((data, textStatus, jqXHR) => { + if (game.currentRequest === thisRequest) { + game.currentRequest = null; + if (jqXHR.status == 204) { + startGamePoll(gameId, afterTime); + } else { + processGame(gameId, data); + startGamePoll(gameId, data.modified); + } + } + }).fail((jqXHR, textStatus, errorThrown) => { + if (game.currentRequest === thisRequest) { + setTimeout(() => { + if (game.currentRequest === thisRequest) { + game.currentRequest = null; + startGamePoll(gameId, 0); + } + }, RETRY_DELAY); + } + }); + } +} + +function stopGamePoll(gameId) { + if (gameId in games === false) { + return; + } + + const game = games[gameId]; + const request = game.currentRequest; + if (request !== null) { + game.currentRequest = null; + request.abort(); + } +} + +function processMeta(data) { + meta.lastModified = data.modified; + + for (const game of data.games) { + if (game.gameId in meta.games === false || game.modified > meta.games[game.gameId].modified) { + meta.games[game.gameId] = game; + for (const cbId in meta.listeners) { + try { + meta.listeners[cbId](game, game.gameId); + } catch(err) { + console.error('uncaught exception in callback', err); + } + } + } + } +} + +function startMetaPoll(afterTime) { + if (meta.currentRequest === null) { + if (afterTime === undefined) { + afterTime = meta.lastModified; + } + + const thisRequest = meta.currentRequest = $.ajax({ + dataType: 'json', + contentType: 'application/json', + url: `https://jessemcdonald.info/pacosako/api/games/poll/${afterTime}`, + cache: false, + timeout: LONG_TIMEOUT, + }).done((data, textStatus, jqXHR) => { + if (meta.currentRequest === thisRequest) { + meta.currentRequest = null; + if (jqXHR.status == 204) { + startMetaPoll(afterTime); + } else { + processMeta(data); + startMetaPoll(data.modified); + } + } + }).fail((jqXHR, textStatus, errorThrown) => { + if (meta.currentRequest === thisRequest) { + setTimeout(() => { + if (meta.currentRequest === thisRequest) { + meta.currentRequest = null; + startMetaPoll(0); + } + }, RETRY_DELAY); + } + }); + } +} + +function stopMetaPoll() { + const request = meta.currentRequest; + if (request !== null) { + meta.currentRequest = null; + request.abort(); + } +} + +module.exports = { + getGameState, + getGameMeta, + onGameUpdate, + onMetaUpdate, + sendUpdate, +}; + +/* vim:set expandtab sw=3 ts=8: */ diff --git a/js/pacosako_ui.js b/js/pacosako_ui.js index 4b89460..3f00703 100644 --- a/js/pacosako_ui.js +++ b/js/pacosako_ui.js @@ -1,11 +1,7 @@ 'use strict'; import PS from './pacosako.js'; - -import Gun from 'gun'; -import 'gun/nts'; -import 'gun/sea'; -import 'gun/lib/webrtc'; +import IO from './pacosako_io.js'; import deepEqual from 'deep-equal'; @@ -16,75 +12,24 @@ import 'webpack-jquery-ui/css'; import escape from 'lodash/escape'; +import jBox from 'jbox'; +import 'jbox/dist/jBox.all.css'; + /* "Waterdrop" by Porphyr (freesound.org/people/Porphyr) / CC BY 3.0 (creativecommons.org/licenses/by/3.0) */ import Waterdrop from '../wav/191678__porphyr__waterdrop.wav'; $(function (){ - /* workaround for persistent errors accumulating which break synchronization */ - if ('localStorage' in window) { - window.localStorage.removeItem('gun/'); - window.localStorage.removeItem('gap/gun/'); - } - - let gun = Gun({ - peers: ['https://jessemcdonald.info/gun'], - }); - - gun.on('in', function(msg) { - const filter = window.logGunIn; - if (filter && ((typeof filter !== 'function') || filter(msg))) - { - console.log('in msg: ', msg); - } - }); - - gun.on('out', function(msg) { - const filter = window.logGunOut; - if (filter && ((typeof filter !== 'function') || filter(msg))) - { - console.log('out msg: ', msg); - } - }); - function debug() { if (window.logDebug) { console.log.apply(console, arguments); } } - window.logGunIn = false; - window.logGunOut = false; window.logDebug = false; let currentGame = new PS.Game(); let visibleGame = new PS.Game(currentGame); let cancelGameCallback = function() {}; - let cancelMetaCallback = function() {}; - - Gun.chain.onWithCancel = (function() { - function cancelCallback(data,key,msg,ev) { - if (ev && typeof ev.off === 'function') { - ev.off(); - } - } - - return function(tag, arg, eas, as) { - if (typeof tag === 'function') { - let callback = tag; - const cancelEv = function() { - callback = cancelCallback; - }; - const wrapper = function() { - return callback.apply(this, arguments); - }; - this.on(wrapper, arg, eas, as); - return cancelEv; - } else { - this.on(tag, arg, eas, as); - return null; - } - }; - })(); function pieceTypeCode(type) { if (type === PS.KING) { @@ -171,7 +116,7 @@ $(function (){ piece.appendTo('#cb_hidden'); try { - const meta = { timestamp: new Date(Gun.state()).getTime() }; + const meta = { timestamp: +new Date() }; currentGame.move(from, to, meta); } catch (err) { debug('unable to move', err); @@ -488,11 +433,10 @@ $(function (){ const gameId = boardElem.data('gameId'); const moves = { past: currentGame.moves, future: currentGame.redoMoves }; boardElem.data('last_state', currentGame.moves); - gun.get(PacoSakoUUID + '/game/' + gameId).get('board').put(JSON.stringify(moves)); - putMeta(); + putMeta({ board: moves }); } - function putMeta(){ + function putMeta(extra) { const gameId = $('#cb_board').data('gameId'); const lightName = $('#cb_light_name').val(); const darkName = $('#cb_dark_name').val(); @@ -501,7 +445,7 @@ $(function (){ const lastMove = currentGame.lastMove || {}; const lastMeta = lastMove.meta || {}; const status = - (currentGame.status === PS.PLAYING) ? null : + (currentGame.status === PS.PLAYING) ? '' : (currentGame.status === PS.CHECKMATE) ? 'checkmate' : (currentGame.status === PS.RESIGNED) ? 'resigned' : 'ended'; @@ -510,17 +454,24 @@ $(function (){ lightName, darkName, moves: turns, - timestamp: lastMeta.timestamp || new Date(Gun.state()).getTime(), + timestamp: lastMeta.timestamp || +new Date(), status, }; - const metaRef = gun.get(PacoSakoUUID + '/meta/' + gameId).put(meta); - - if (lightName !== '' || darkName !== '' || turns !== 0) { - gun.get(PacoSakoUUID + '/meta').get(gameId).put(metaRef); - } else { - gun.get(PacoSakoUUID + '/meta').get(gameId).put(null); + if (extra) { + Object.assign(meta, extra); } + + meta.modified = $('#cb_board').data('modified'); + IO.sendUpdate(gameId, meta).catch((err) => { + new jBox('Notice', { + content: 'Failed to send update to server.', + }); + debug('update error', err); + + /* force a reset back to the latest server data */ + switchGameId(gameId); + }); } function switchGameId(newId){ @@ -533,13 +484,13 @@ $(function (){ } cancelGameCallback(); - cancelMetaCallback(); boardElem.data('gameId', newId); + boardElem.data('modified', 0); location.hash = '#/' + newId; - const notifyAfter = new Date().getTime() + 2000; - $(window).data('notifyAfter', new Date().getTime() + 2000); + const notifyAfter = +new Date() + 2000; + $(window).data('notifyAfter', +new Date() + 2000); window.setTimeout(function() { /* Delete the notification block in case the system time is changed backward */ if ($(window).data('notifyAfter') === notifyAfter) { @@ -556,18 +507,14 @@ $(function (){ $('#cb_light_name').val(''); $('#cb_dark_name').val(''); - cancelGameCallback = gun.get(PacoSakoUUID + '/game/' + newId).onWithCancel(function(d) { - if (d && d.board) { - try { - const received = JSON.parse(d.board); - const moves = { past: [...received.past], future: [...received.future] }; - const oldState = { past: currentGame.moves, future: currentGame.redoMoves }; + cancelGameCallback = IO.onGameUpdate(newId, function(data, gameId) { + const board = data.board || { past: [], future: [] }; - if (deepEqual(moves, oldState)) { - /* we already have this */ - return; - } + try { + const moves = { past: [...board.past], future: [...board.future] }; + const oldState = { past: currentGame.moves, future: currentGame.redoMoves }; + if (!deepEqual(moves, oldState)) { debug('got board', moves); const newGame = new PS.Game(); @@ -596,19 +543,18 @@ $(function (){ } setCurrentGame(newGame, newGame.moves.length > currentGame.moves.length); - } catch (err) { - debug('Error parsing board data', err); } + } catch (err) { + debug('Error parsing board data', err); } - }); - cancelMetaCallback = gun.get(PacoSakoUUID + '/meta').get(newId).onWithCancel(function(d) { - d = d || {}; - debug('got meta', d); + const d = data || {}; $('#cb_board').data('lightName', shortenName(String(d.lightName || 'Light'))); $('#cb_board').data('darkName', shortenName(String(d.darkName || 'Dark'))); $('#cb_light_name').val(String(d.lightName || '')); $('#cb_dark_name').val(String(d.darkName || '')); + + $('#cb_board').data('modified', data.modified); }); let selOpt = $('#cb_game_' + newId); @@ -644,7 +590,7 @@ $(function (){ const notifyAudio = new Audio(Waterdrop); function playNotifySound(){ - const now = new Date().getTime(); + const now = +new Date(); const then = $(window).data('notifyAfter'); if (!then || now >= then) { try { notifyAudio.play(); } catch (err) {} @@ -652,7 +598,7 @@ $(function (){ } function notify(body) { - const now = new Date().getTime(); + const now = +new Date(); const then = $(window).data('notifyAfter'); if (!then || now >= then) { try { @@ -824,7 +770,7 @@ $(function (){ $('#cb_resign').on('click', function(){ try { - const meta = { timestamp: new Date(Gun.state()).getTime() }; + const meta = { timestamp: +new Date() }; currentGame.resign(meta); } catch (err) { debug('unable to resign', err); @@ -910,15 +856,24 @@ $(function (){ } }); - let updateMeta = function() { putMeta(); } - $('#cb_light_name').on('input', updateMeta); - $('#cb_dark_name').on('input', updateMeta); + let idleTimeoutId = undefined; + $('#cb_light_name, #cb_dark_name').on('input', function() { + if (idleTimeoutId !== undefined) { + clearTimeout(idleTimeoutId); + } + idleTimeoutId = setTimeout(function() { putMeta(); }, 500); + }).on('blur', function() { + if (idleTimeoutId !== undefined) { + clearTimeout(idleTimeoutId); + } + putMeta(); + }); function updateTitle(opt){ opt = $(opt); const then = opt.data('then'); - const now = new Date(Gun.state()).getTime(); + const now = +new Date(); let age_str = ''; if (then > now) { age_str = ' (future)'; @@ -971,7 +926,7 @@ $(function (){ opt.data('gameId', key); opt.data('title', lightName + ' vs. ' + darkName + moves + stat); - opt.data('then', d.timestamp || new Date(Gun.state()).getTime()); + opt.data('then', d.timestamp || +new Date()); opt.addClass('cb-game-option'); opt.appendTo('#cb_select_game'); updateTitle(opt); @@ -997,45 +952,22 @@ $(function (){ } } - let cancelAll = function(){}; + let cancelMeta = null; return function() { - cancelAll(); - $('#cb_select_game .cb-game-option').remove(); updateSelectMeta(null, $('#cb_board').data('gameId')); - const cancellers = {}; - let cancelMeta = gun.get(PacoSakoUUID + '/meta').onWithCancel(function(meta) { - for (const gameId in meta) { /* use of 'in' here is deliberate */ - /* 'gameId' may include extra GUN fields like '_' */ - if (gameId.match(/^[0-9a-f]{16}$/)) { - if (!Gun.obj.is(meta[gameId])) { - updateSelectMeta(null, gameId); - if (gameId in cancellers) { - cancellers[gameId](); - delete cancellers[gameId]; - } - } else if (!(gameId in cancellers)) { - cancellers[gameId] = gun.get(meta[gameId]).onWithCancel(function(d) { - updateSelectMeta(d, gameId); - }); - } - } - } - }, { change: true }); + /* cancel and resubscribe to get all the cached data again */ + if (cancelMeta) { + /* cancel, but don't stop polling the server */ + /* this is only a very brief interruption */ + cancelMeta(true); + } - cancelAll = function() { - cancelMeta(); - cancelMeta = function(){}; - for (const gameId in cancellers) { - cancellers[gameId](); - delete cancellers[gameId]; - } - cancelAll = function(){}; - }; - - return cancelAll; + cancelMeta = IO.onMetaUpdate(function(data, gameId) { + updateSelectMeta(data, gameId); + }); }; })(); @@ -1047,31 +979,6 @@ $(function (){ }); }, 15000); - window.setInterval(function(){ - const peers = gun._.opt.peers; - let n_open = 0; - let n_total = 0; - for (const peerId in peers) { - ++n_total; - try { - const peer = peers[peerId]; - if (peer.constructor === RTCPeerConnection) { - if (peer.connectionState === 'connected') { - ++n_open; - } - } else if (peer.wire && peer.wire.constructor === WebSocket) { - if (peer.wire.readyState === peer.wire.OPEN) { - ++n_open; - } - } - } catch (err) {} - } - const newText = 'Synchronizing with ' + n_open + (n_open === 1 ? ' peer' : ' peers') + ' (' + n_total + ' total).'; - if (newText !== $('#cb_peers').text()) { - $('#cb_peers').text(newText); - } - }, 1000); - window.onpopstate = function(event){ const foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/); if (foundId) { @@ -1091,32 +998,16 @@ $(function (){ /* Low-level commands to be run from the JS console */ window.Admin = { - convertFromV2: function() { - gun.get(PacoSakoUUID).get('games').map().once(function(d,key){ - if (d && d.board) { - debug('converting ' + key); - gun.get(PacoSakoUUID + '/game/' + key).get('board').put(d.board); - } - }); - - gun.get(PacoSakoUUID).get('meta').map().once(function(d,key){ - if (d) { - debug('converting metadata for ' + key); - gun.get(PacoSakoUUID + '/meta').get(key).put(d); - } - }); - }, - getCurrentGame: function() { return currentGame; }, getVisibleGame: function() { return visibleGame; }, - setCurrentGame: setCurrentGame, - setVisibleGame: setVisibleGame, + setCurrentGame, + setVisibleGame, refresh: function() { setCurrentGame(currentGame); }, - renderBoard: renderBoard, - putState: putState, - putMeta: putMeta, - gun: gun, - PacoSakoUUID: PacoSakoUUID, + renderBoard, + putState, + putMeta, + PacoSakoUUID, + IO, }; /* diff --git a/package-lock.json b/package-lock.json index e75921e..a7274bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1083,42 +1083,6 @@ "@hapi/hoek": "^8.3.0" } }, - "@peculiar/asn1-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-1.0.5.tgz", - "integrity": "sha512-rzzorGYnQNmVHleLvC8gJSbbdNYtg+EB9s075dHvwpxs10teXHYnRmTWhCVuWjbSVSofwdm7IYPtMTWTbcNUWA==", - "dev": true, - "optional": true, - "requires": { - "asn1js": "^2.0.26", - "tslib": "^1.11.1" - } - }, - "@peculiar/json-schema": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.10.tgz", - "integrity": "sha512-kbpnG9CkF1y6wwGkW7YtSA+yYK4X5uk4rAwsd1hxiaYE3Hkw2EsGlbGh/COkMLyFf+Fe830BoFiMSB3QnC/ItA==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^1.11.1" - } - }, - "@peculiar/webcrypto": { - "version": "1.0.26", - "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.0.26.tgz", - "integrity": "sha512-7Ws4t+eUiLPSL9q8OCcbevd9S8qjqBDAXJrbnjzbuWu3nI8NgBvc7qZ97DZO8y8qxEoh5cGqK2p+p+UhxLw9Kg==", - "dev": true, - "optional": true, - "requires": { - "@peculiar/asn1-schema": "^1.0.5", - "@peculiar/json-schema": "^1.1.10", - "asn1js": "^2.0.26", - "pvtsutils": "^1.0.10", - "tslib": "^1.11.1", - "webcrypto-core": "^1.0.18" - } - }, "@rollup/plugin-node-resolve": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz", @@ -1316,28 +1280,6 @@ "source-map": "^0.6.1" } }, - "@unimodules/core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@unimodules/core/-/core-5.1.0.tgz", - "integrity": "sha512-gaamGkJ4PVwusWEfsZyPo4uhrVWPDE0BmHc/lTYfkZCv2oIAswC7gG/ULRdtZpYdwnYqFIZng+WQxwuVrJUNDw==", - "dev": true, - "optional": true, - "requires": { - "compare-versions": "^3.4.0" - } - }, - "@unimodules/react-native-adapter": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@unimodules/react-native-adapter/-/react-native-adapter-5.2.0.tgz", - "integrity": "sha512-S3HMEeQbV6xs7ORRcxXFGMk38DAnxqNcZG9T8JkX/KGY9ILUUqTS/e68+d849B6beEeglNMcOxyjwlqjykN+FA==", - "dev": true, - "optional": true, - "requires": { - "invariant": "^2.2.4", - "lodash": "^4.5.0", - "prop-types": "^15.6.1" - } - }, "@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -1531,13 +1473,6 @@ "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, - "addressparser": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-0.3.2.tgz", - "integrity": "sha1-WYc/Nej89sc2HBAjkmHXbhU0i7I=", - "dev": true, - "optional": true - }, "ajv": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", @@ -1661,13 +1596,6 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "asmcrypto.js": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-0.22.0.tgz", - "integrity": "sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA==", - "dev": true, - "optional": true - }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -1679,16 +1607,6 @@ "minimalistic-assert": "^1.0.0" } }, - "asn1js": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.0.26.tgz", - "integrity": "sha512-yG89F0j9B4B0MKIcFyWWxnpZPLaNTjCj4tkE3fjbAoo0qmpGw0PYYqSbX/4ebnd9Icn8ZgK4K1fvDyEtW1JYtQ==", - "dev": true, - "optional": true, - "requires": { - "pvutils": "^1.0.17" - } - }, "assert": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", @@ -1734,26 +1652,6 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, - "b64-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/b64-lite/-/b64-lite-1.4.0.tgz", - "integrity": "sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w==", - "dev": true, - "optional": true, - "requires": { - "base-64": "^0.1.0" - } - }, - "b64u-lite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/b64u-lite/-/b64u-lite-1.1.0.tgz", - "integrity": "sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A==", - "dev": true, - "optional": true, - "requires": { - "b64-lite": "^1.4.0" - } - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -2564,13 +2462,6 @@ } } }, - "base-64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", - "integrity": "sha1-eAqZyE59YAJgNhURxId2E78k9rs=", - "dev": true, - "optional": true - }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", @@ -2751,16 +2642,6 @@ "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", "dev": true }, - "buffer": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.5.0.tgz", - "integrity": "sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -2773,16 +2654,6 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "dev": true, - "optional": true, - "requires": { - "node-gyp-build": "~3.7.0" - } - }, "builtin-modules": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", @@ -3126,13 +2997,6 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true, - "optional": true - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -4029,36 +3893,6 @@ "minimalistic-crypto-utils": "^1.0.0" } }, - "emailjs": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/emailjs/-/emailjs-2.2.0.tgz", - "integrity": "sha1-ulsj5KSwpFEPZS6HOxVOlAe2ygM=", - "dev": true, - "optional": true, - "requires": { - "addressparser": "^0.3.2", - "emailjs-mime-codec": "^2.0.7" - } - }, - "emailjs-base64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/emailjs-base64/-/emailjs-base64-1.1.4.tgz", - "integrity": "sha512-4h0xp1jgVTnIQBHxSJWXWanNnmuc5o+k4aHEpcLXSToN8asjB5qbXAexs7+PEsUKcEyBteNYsSvXUndYT2CGGA==", - "dev": true, - "optional": true - }, - "emailjs-mime-codec": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/emailjs-mime-codec/-/emailjs-mime-codec-2.0.9.tgz", - "integrity": "sha512-7qJo4pFGcKlWh/kCeNjmcgj34YoJWY0ekZXEHYtluWg4MVBnXqGM4CRMtZQkfYwitOhUgaKN5EQktJddi/YIDQ==", - "dev": true, - "optional": true, - "requires": { - "emailjs-base64": "^1.1.4", - "ramda": "^0.26.1", - "text-encoding": "^0.7.0" - } - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -4281,16 +4115,6 @@ "homedir-polyfill": "^1.0.1" } }, - "expo-random": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/expo-random/-/expo-random-8.1.0.tgz", - "integrity": "sha512-9n2gg83Hpg3ErkKu+a3FFOGmaPIxaHn6RuzjW24xFckdfmnrAKtbs1aU1aAcmoL1kXPvDeufRSEV/3lW93u6ug==", - "dev": true, - "optional": true, - "requires": { - "base64-js": "^1.3.0" - } - }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -5245,21 +5069,6 @@ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, - "gun": { - "version": "0.2020.301", - "resolved": "https://registry.npmjs.org/gun/-/gun-0.2020.301.tgz", - "integrity": "sha512-Jb5VNKgBt2my+XHB/K65gTOMbBICgglc4kVaMmQ/lD7ZyxLvwUSXissfcLHAwBxUWPU3P4GqTi+fQmsK068FQw==", - "dev": true, - "requires": { - "buffer": "^5.4.3", - "bufferutil": "^4.0.1", - "emailjs": "^2.2.0", - "isomorphic-webcrypto": "^2.3.2", - "text-encoding": "^0.7.0", - "utf-8-validate": "^5.0.2", - "ws": "^7.2.1" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -5904,24 +5713,21 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isomorphic-webcrypto": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/isomorphic-webcrypto/-/isomorphic-webcrypto-2.3.6.tgz", - "integrity": "sha512-d1prB3b0UMWOao5DK3+O2Dr5ZJCakzB5Q+2kCWNkNuM9ln7VB8TSw2SwUjbnErzg7cgsYja+VPQaeBtXEojpew==", + "jbox": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jbox/-/jbox-1.2.0.tgz", + "integrity": "sha512-TCReU5oT+cQz8YFoSumFuJ1czrY4VXxgSg8WJ/1klxFtAs4wXDhBvvmltXFA9qJgQQUhd8CErwArHRP3R11j/A==", "dev": true, - "optional": true, "requires": { - "@peculiar/webcrypto": "^1.0.22", - "@unimodules/core": "*", - "@unimodules/react-native-adapter": "*", - "asmcrypto.js": "^0.22.0", - "b64-lite": "^1.3.1", - "b64u-lite": "^1.0.1", - "expo-random": "*", - "msrcrypto": "^1.5.6", - "react-native-securerandom": "^0.1.1", - "str2buf": "^1.3.0", - "webcrypto-shim": "^0.1.4" + "jquery": "^3.5.0" + }, + "dependencies": { + "jquery": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz", + "integrity": "sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ==", + "dev": true + } } }, "jest-worker": { @@ -6396,13 +6202,6 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "msrcrypto": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/msrcrypto/-/msrcrypto-1.5.8.tgz", - "integrity": "sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q==", - "dev": true, - "optional": true - }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", @@ -6451,13 +6250,6 @@ "tslib": "^1.10.0" } }, - "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", - "dev": true, - "optional": true - }, "node-libs-browser": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", @@ -8898,18 +8690,6 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "optional": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -8969,23 +8749,6 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "pvtsutils": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.10.tgz", - "integrity": "sha512-8ZKQcxnZKTn+fpDh7wL4yKax5fdl3UJzT8Jv49djZpB/dzPxacyN1Sez90b6YLdOmvIr9vaySJ5gw4aUA1EdSw==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^1.10.0" - } - }, - "pvutils": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.0.17.tgz", - "integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ==", - "dev": true, - "optional": true - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -9014,13 +8777,6 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, - "ramda": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", - "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", - "dev": true, - "optional": true - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -9040,23 +8796,6 @@ "safe-buffer": "^5.1.0" } }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "optional": true - }, - "react-native-securerandom": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz", - "integrity": "sha1-8TBiOkEsM4sK+t7bwgTFy7i/IHA=", - "dev": true, - "optional": true, - "requires": { - "base64-js": "*" - } - }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -9752,13 +9491,6 @@ } } }, - "str2buf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/str2buf/-/str2buf-1.3.0.tgz", - "integrity": "sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==", - "dev": true, - "optional": true - }, "stream-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", @@ -10144,13 +9876,6 @@ } } }, - "text-encoding": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", - "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", - "dev": true, - "optional": true - }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -10436,16 +10161,6 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "dev": true, - "optional": true, - "requires": { - "node-gyp-build": "~3.7.0" - } - }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -10522,24 +10237,6 @@ "neo-async": "^2.5.0" } }, - "webcrypto-core": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.0.18.tgz", - "integrity": "sha512-wHRMXYxtDUWsTXNyRdaYlbcbq1OJF9pQov5THqvn5OBvixpCjnjU2spvEscxqRY8bLlpHk2S7RtROIMyNoEyFg==", - "dev": true, - "optional": true, - "requires": { - "pvtsutils": "^1.0.10", - "tslib": "^1.11.1" - } - }, - "webcrypto-shim": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/webcrypto-shim/-/webcrypto-shim-0.1.5.tgz", - "integrity": "sha512-mE+E00gulvbLjHaAwl0kph60oOLQRsKyivEFgV9DMM/3Y05F1vZvGq12hAcNzHRnYxyEOABBT/XMtwGSg5xA7A==", - "dev": true, - "optional": true - }, "webpack": { "version": "4.42.1", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", @@ -10964,12 +10661,6 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "ws": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz", - "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==", - "dev": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index a7681a5..0ed8768 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paco_sako", - "version": "0.2.0", + "version": "0.3.0", "description": "Online version of the Paco Ŝako chess variation", "browser": "index.js", "scripts": { @@ -19,8 +19,8 @@ "copy-webpack-plugin": "^5.1.1", "deep-equal": "git+https://jessemcdonald.info/gogs/nybble/node-deep-equal", "extract-loader": "^5.0.1", - "gun": "^0.2020.301", "html-webpack-plugin": "^4.0.4", + "jbox": "^1.2.0", "jquery-ui-touch-punch": "^0.2.3", "lodash": "^4.17.15", "mini-css-extract-plugin": "^0.9.0",