Compare commits

..

No commits in common. "master" and "v0.7.4" have entirely different histories.

11 changed files with 5808 additions and 17828 deletions

View File

@ -1,21 +0,0 @@
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"jquery": true,
"es6": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"no-console": "off",
"semi": "warn",
}
};

View File

@ -179,7 +179,6 @@ button#settings, button#cb_choose_game {
#cb_message { #cb_message {
height: 1.2em; height: 1.2em;
margin-bottom: 0.1rem; margin-bottom: 0.1rem;
flex: 1 1 auto;
} }
#cb_explain_check { #cb_explain_check {
@ -207,33 +206,6 @@ button#settings, button#cb_choose_game {
color: grey; color: grey;
} }
#cb_times {
display: flex;
flex-flow: row nowrap;
white-space: nowrap;
justify-content: stretch;
align-items: flex-end;
width: 100%;
padding-left: 3rem;
padding-right: 3rem;
}
#cb_times.cb-hide-times {
visibility: hidden;
}
#cb_light_time, #cb_dark_time {
width: 100%;
}
#cb_light_time {
text-align: left;
}
#cb_dark_time {
text-align: right;
}
#cb_names { #cb_names {
position: relative; position: relative;
display: flex; display: flex;
@ -271,16 +243,6 @@ button#settings, button#cb_choose_game {
height: 3rem; height: 3rem;
} }
#cb_names.cb-light-turn::before {
filter: drop-shadow(0 0 5px blue);
-webkit-filter: drop-shadow(0 0 5px blue);
}
#cb_names.cb-light-won::before {
filter: drop-shadow(0 0 5px darkviolet);
-webkit-filter: drop-shadow(0 0 5px darkviolet);
}
#cb_names::after { #cb_names::after {
content: ''; content: '';
display: block; display: block;
@ -294,16 +256,6 @@ button#settings, button#cb_choose_game {
height: 3rem; height: 3rem;
} }
#cb_names.cb-dark-turn::after {
filter: drop-shadow(0 0 5px blue);
-webkit-filter: drop-shadow(0 0 5px blue);
}
#cb_names.cb-dark-won::after {
filter: drop-shadow(0 0 5px darkviolet);
-webkit-filter: drop-shadow(0 0 5px darkviolet);
}
#cb_names .cb-names-vs { #cb_names .cb-names-vs {
padding-left: 0.25em; padding-left: 0.25em;
padding-right: 0.25em; padding-right: 0.25em;
@ -526,43 +478,6 @@ button#settings, button#cb_choose_game {
z-index: 1; z-index: 1;
} }
.cb-private .cb-hide-if-private {
display: none !important;
}
.cb-show-if-private {
display: none !important;
}
.cb-private .cb-show-if-private {
display: initial !important;
}
div.hbox {
display: flex;
flex-flow: row nowrap;
justify-content: stretch;
align-items: stretch;
}
.private-link {
flex: 0 1 auto;
margin: 0.1em;
color: black;
}
.private-link:visited {
color: black;
}
.private-link:hover {
color: blue;
}
.jBox-wrapper.jBox-hasTitle > .jBox-container {
padding: 4px; /* same as border-radius */
}
.jBox-title h2 { .jBox-title h2 {
margin: 0; margin: 0;
} }
@ -609,13 +524,10 @@ div.hbox {
} }
.game-tiles { .game-tiles {
width: calc(100vw - 6rem); max-width: 80vw;
max-width: calc(100vw - 6rem); max-height: 80vh;
max-height: calc(100vh - 12rem); overflow-y: auto;
display: grid; text-align: center;
grid-template-columns: repeat(auto-fill, minmax(15em, auto));
align-items: center;
justify-items: center;
} }
.game-tiles > * { .game-tiles > * {
@ -624,14 +536,14 @@ div.hbox {
.game-tile { .game-tile {
position: relative; position: relative;
display: flex; display: inline-flex;
flex-flow: column nowrap; flex-flow: column nowrap;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 14em; width: 14em;
height: 14em; height: 14em;
box-shadow: 0 0 0 1px black, 5px 5px 3px #00000080; box-shadow: 0 0 0 1px black, 5px 5px 3px #00000080;
margin: 0.5em; margin: 1em;
padding: 0.5em; padding: 0.5em;
border-radius: 6pt; border-radius: 6pt;
background-color: #FFFFF0; background-color: #FFFFF0;
@ -664,7 +576,6 @@ div.hbox {
font-size: 3em; font-size: 3em;
text-shadow: 2px 2px 4px #000000c0; text-shadow: 2px 2px 4px #000000c0;
font-weight: bold; font-weight: bold;
text-align: center;
} }
.new-game-text::before { .new-game-text::before {
@ -787,19 +698,19 @@ div.hbox {
margin-top: 0.5em; margin-top: 0.5em;
} }
.game-tiles { .game-tile {
font-size: calc(12in * 0.014); font-size: calc(12in * 0.015);
} }
@media only screen and (min-width: 6in) and (max-width: 12in) { @media only screen and (min-width: 6in) and (max-width: 12in) {
.game-tiles { .game-tile {
font-size: 1.4vw; font-size: 1.5vw;
} }
} }
@media only screen and (max-width: 6in) { @media only screen and (max-width: 6in) {
.game-tiles { .game-tile {
font-size: calc(6in * 0.014); font-size: calc(6in * 0.015);
} }
} }

View File

@ -1,4 +1,3 @@
<!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -18,10 +17,10 @@
<div id="board_ui"> <div id="board_ui">
<div id="header"> <div id="header">
<button id="settings" title="Settings" type="button"><span class="fas fa-cog"></span></button> <button id="settings" title="Settings" type="button"><span class="fas fa-cog"></span></button>
<button id="cb_choose_game" title="Choose Game" type="button" class="cb-hide-if-private"><span class="fas fa-list"></span></button> <button id="cb_choose_game" title="Choose Game" type="button"><span class="fas fa-list"></span></button>
<h1>Paco Ŝako<span class="cb-show-if-private" style="display: none"> - Private Mode</span></h1> <h1>Paco Ŝako</h1>
<div class="checkbox-container"><input id="cb_reverse" title="Reverse Board" type="checkbox" autocomplete="off" class="image-checkbox fas fa-sync"></div> <div class="checkbox-container"><input id="cb_reverse" title="Reverse Board" type="checkbox" autocomplete="off" class="image-checkbox fas fa-sync"></div>
<div class="checkbox-container cb-hide-if-private"><input id="cb_notify" title="Notify" type="checkbox" autocomplete="off" class="image-checkbox fas fa-bell-slash"></div> <div class="checkbox-container"><input id="cb_notify" title="Notify" type="checkbox" autocomplete="off" class="image-checkbox fas fa-bell-slash"></div>
<button id="help" title="Help" type="button"><span class="fas fa-question-circle"></span></button> <button id="help" title="Help" type="button"><span class="fas fa-question-circle"></span></button>
</div> </div>
@ -150,10 +149,7 @@
</table> </table>
</div> </div>
<div id="cb_status"> <div id="cb_status">
<div class="hbox">
<div id="cb_message"></div> <div id="cb_message"></div>
<div class="cb-hide-if-private"><a class="private-link" target="_blank" title="Open Private Copy"><span class="fas fa-copy"></span></a></div>
</div>
<div id="cb_explain_check"></div> <div id="cb_explain_check"></div>
<div id="cb_scrollable"> <div id="cb_scrollable">
<div id="cb_history"> <div id="cb_history">
@ -161,17 +157,13 @@
</div> </div>
</div> </div>
</div> </div>
<div id="cb_names" class="cb-hide-if-private"> <div id="cb_names">
<div id="cb_names_text"> <div id="cb_names_text">
<input id="cb_light_name" autocomplete="off" placeholder="Light"> <input id="cb_light_name" autocomplete="off" placeholder="Light">
<span class="cb-names-vs">vs.</span> <span class="cb-names-vs">vs.</span>
<input id="cb_dark_name" autocomplete="off" placeholder="Dark"> <input id="cb_dark_name" autocomplete="off" placeholder="Dark">
</div> </div>
</div> </div>
<div id="cb_times" class="cb-hide-if-private">
<div id="cb_light_time">0:00:00</div>
<div id="cb_dark_time">0:00:00</div>
</div>
<div id="cb_navigate"> <div id="cb_navigate">
<button id="cb_undo" title="Undo" type="button" disabled="true"><span class="fas fa-undo"></span></button> <button id="cb_undo" title="Undo" type="button" disabled="true"><span class="fas fa-undo"></span></button>
<button id="cb_redo" title="Redo" type="button" disabled="true"><span class="fas fa-redo"></span></button> <button id="cb_redo" title="Redo" type="button" disabled="true"><span class="fas fa-redo"></span></button>
@ -180,8 +172,8 @@
<div class="nav-spacer"></div> <div class="nav-spacer"></div>
<button id="cb_nav_first" title="View First Turn" type="button" disabled="true"><span class="fas fa-fast-backward"></span></button> <button id="cb_nav_first" title="View First Turn" type="button" disabled="true"><span class="fas fa-fast-backward"></span></button>
<button id="cb_nav_prev_turn" title="View Prior Turn" type="button" disabled="true"><span class="fas fa-backward"></span></button> <button id="cb_nav_prev_turn" title="View Prior Turn" type="button" disabled="true"><span class="fas fa-backward"></span></button>
<button id="cb_nav_prev_state" title="View Prior Move" type="button" disabled="true"><span class="fas fa-play fa-flip-horizontal"></span></button> <button id="cb_nav_prev_state" title="View Prior State" type="button" disabled="true"><span class="fas fa-play fa-flip-horizontal"></span></button>
<button id="cb_nav_next_state" title="View Next Move" type="button" disabled="true"><span class="fas fa-play"></span></button> <button id="cb_nav_next_state" title="View Next State" type="button" disabled="true"><span class="fas fa-play"></span></button>
<button id="cb_nav_next_turn" title="View Next Turn" type="button" disabled="true"><span class="fas fa-forward"></span></button> <button id="cb_nav_next_turn" title="View Next Turn" type="button" disabled="true"><span class="fas fa-forward"></span></button>
<button id="cb_nav_last" title="View Current Move" type="button" disabled="true"><span class="fas fa-fast-forward"></span></button> <button id="cb_nav_last" title="View Current Move" type="button" disabled="true"><span class="fas fa-fast-forward"></span></button>
</div> </div>

View File

@ -1,6 +1,5 @@
import './css/chess.css'; import './css/chess.css';
import './css/theme/pacosako.css'; import './css/theme/pacosako.css';
import './css/theme/traditional.css';
import '@fortawesome/fontawesome-free/css/fontawesome.css'; import '@fortawesome/fontawesome-free/css/fontawesome.css';
import '@fortawesome/fontawesome-free/css/solid.css'; import '@fortawesome/fontawesome-free/css/solid.css';

View File

@ -90,9 +90,6 @@ export class Iterator {
} }
flatMap(f) { flatMap(f) {
if (typeof f === 'undefined') {
f = identity;
}
const next = this.next; const next = this.next;
let innerNext; let innerNext;
@ -111,8 +108,7 @@ export class Iterator {
return z; return z;
} }
const mapped = f(z.value); const iter = y.value.__proto__[Symbol.iterator].call(y.value);
const iter = mapped.__proto__[Symbol.iterator].call(mapped);
innerNext = iter.next.bind(iter); innerNext = iter.next.bind(iter);
} }
}); });
@ -198,6 +194,6 @@ export class Iterator {
strictlyIncludes(x) { strictlyIncludes(x) {
return this.some(function matches(y) { return y === x; }); return this.some(function matches(y) { return y === x; });
} }
} };
export default Iterator; export default Iterator;

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
import {Iterator} from './iterator.js'; import {Iterator} from './iterator.js';
import {Buffer} from 'buffer';
/* Game states */ /* Game states */
const PLAYING = 'playing'; const PLAYING = 'playing';
@ -272,7 +271,7 @@ class Board {
function testFunction(getPiece, match) { function testFunction(getPiece, match) {
if (match === undefined) { if (match === undefined) {
return function(/*here*/) { return true; }; return function(here) { return true; }
} else if (match === false) { } else if (match === false) {
return function(here) { return getPiece(here) === EMPTY; }; return function(here) { return getPiece(here) === EMPTY; };
} else if (match === true) { } else if (match === true) {
@ -287,7 +286,7 @@ class Board {
const test = function(here) { return testSide(here) && testOther(here); }; const test = function(here) { return testSide(here) && testOther(here); };
if (this._phantom && this._phantom.side === side) { if (this._phantom && this._phantom.side === side) {
const getPhantom = (/*here*/) => this._phantom.type; const getPhantom = (here) => this._phantom.type;
const testPhantom = testFunction(getPhantom, type); const testPhantom = testFunction(getPhantom, type);
if (testPhantom(PHANTOM)) { if (testPhantom(PHANTOM)) {
yield this._phantom.from; yield this._phantom.from;
@ -316,8 +315,8 @@ const KNIGHT_DIR =
[-1, -2], [ 1, -2]]; [-1, -2], [ 1, -2]];
const NBSP = '\u00a0'; /* non-breaking space */ const NBSP = '\u00a0'; /* non-breaking space */
const SHY = '\u00ad'; /* soft hyphen */ const SHY = '\u00ad' /* soft hyphen */
/* const ZWSP = '\u200b'; */ /* zero-width space */ const ZWSP = '\u200b'; /* zero-width space */
function addHistory(game) { function addHistory(game) {
const prior = game._undo; const prior = game._undo;
@ -573,30 +572,7 @@ class Game {
return this._version; return this._version;
} }
toJSON(style) { toJSON() {
function shrinkMove(move) {
if (move.resign) {
return { side: move.side, resign: true };
} else if (move.phantom) {
return { side: move.side, phantom: true, to: move.to };
} else {
return { side: move.side, from: move.from, to: move.to };
}
}
if (style === 'minify') {
/* Just the fields that are used by the constructor to replay the game */
/* Omits metadata and extra annotation fields */
const state = { past: [], future: [], version: this._version };
for (const move of this._moves) {
state.past.push(shrinkMove(move));
}
for (const move of this._redo) {
state.future.push(shrinkMove(move));
}
return JSON.stringify(state);
}
return JSON.stringify({ return JSON.stringify({
past: this._moves, past: this._moves,
future: this._redo, future: this._redo,
@ -772,6 +748,8 @@ class Game {
/* if a piece has few moves (king, pawn, knight) then just enumerate them */ /* if a piece has few moves (king, pawn, knight) then just enumerate them */
/* if movement is more extensive (bishop, rook, queen) then we can do better */ /* if movement is more extensive (bishop, rook, queen) then we can do better */
if (type === BISHOP || type === ROOK || type === QUEEN) { if (type === BISHOP || type === ROOK || type === QUEEN) {
const ortho = (type === ROOK || type === QUEEN);
const diag = (type === BISHOP || type === QUEEN);
const fromIndex = squareIndex(fromSquare); const fromIndex = squareIndex(fromSquare);
const toIndex = squareIndex(to); const toIndex = squareIndex(to);
const rowsDiff = (toIndex >>> 3) - (fromIndex >>> 3); const rowsDiff = (toIndex >>> 3) - (fromIndex >>> 3);
@ -855,7 +833,7 @@ class Game {
try { try {
sim.move(from, king); sim.move(from, king);
check = diffHistory(this, sim) || true; check = diffHistory(this, sim) || true;
} catch(err) {/*ignore*/} } catch(err) {}
} }
return recordCheck(this, check); return recordCheck(this, check);
} }
@ -911,7 +889,7 @@ class Game {
try { try {
game2.move(PHANTOM, king); game2.move(PHANTOM, king);
check = diffHistory(this, game2) || true; check = diffHistory(this, game2) || true;
} catch(err) {/*ignore*/} } catch(err) {}
} }
return recordCheck(this, check); return recordCheck(this, check);
} }

View File

@ -23,7 +23,7 @@ const meta = {
}; };
const stateListeners = {}; const stateListeners = {};
let stateNextId = 1; const stateNextId = 1;
/* One-time request, no caching or polling */ /* One-time request, no caching or polling */
function getGameState(gameId, retries) { function getGameState(gameId, retries) {
@ -37,7 +37,7 @@ function getGameState(gameId, retries) {
url: `${API_BASE}/game/${gameId}`, url: `${API_BASE}/game/${gameId}`,
cache: false, cache: false,
timeout: SHORT_TIMEOUT, timeout: SHORT_TIMEOUT,
}).done((data/*, textStatus, jqXHR*/) => { }).done((data, textStatus, jqXHR) => {
resolve(data); resolve(data);
}).fail((jqXHR, textStatus, errorThrown) => { }).fail((jqXHR, textStatus, errorThrown) => {
if ((!jqXHR.status || jqXHR.status < 400 || jqXHR.status > 499) && retries > 0) { if ((!jqXHR.status || jqXHR.status < 400 || jqXHR.status > 499) && retries > 0) {
@ -63,7 +63,7 @@ function getGameMeta(gameId, retries) {
url: `${API_BASE}/meta/${gameId}`, url: `${API_BASE}/meta/${gameId}`,
cache: false, cache: false,
timeout: SHORT_TIMEOUT, timeout: SHORT_TIMEOUT,
}).done((data/*, textStatus, jqXHR*/) => { }).done((data, textStatus, jqXHR) => {
resolve(data); resolve(data);
}).fail((jqXHR, textStatus, errorThrown) => { }).fail((jqXHR, textStatus, errorThrown) => {
if ((!jqXHR.status || jqXHR.status < 400 || jqXHR.status > 499) && retries > 0) { if ((!jqXHR.status || jqXHR.status < 400 || jqXHR.status > 499) && retries > 0) {
@ -106,7 +106,7 @@ function onGameUpdate(gameId, callback) {
stopGamePoll(gameId); stopGamePoll(gameId);
} }
} }
}; }
} }
function getCachedGame(gameId) { function getCachedGame(gameId) {
@ -150,7 +150,7 @@ function onMetaUpdate(callback) {
stopMetaPoll(); stopMetaPoll();
} }
} }
}; }
} }
function sendUpdate(gameId, data, retries) { function sendUpdate(gameId, data, retries) {
@ -306,7 +306,7 @@ function startGamePoll(gameId, afterTime) {
setConnectionState(game, 'polling'); setConnectionState(game, 'polling');
startGamePoll(gameId, afterTime); startGamePoll(gameId, afterTime);
} }
}).fail((/*jqXHR, textStatus, errorThrown*/) => { }).fail((jqXHR, textStatus, errorThrown) => {
if (game.currentRequest === thisRequest) { if (game.currentRequest === thisRequest) {
setConnectionState(game, 'failed'); setConnectionState(game, 'failed');
setTimeout(() => { setTimeout(() => {
@ -370,9 +370,9 @@ function startMetaPoll(afterTime) {
const thisRequest = meta.currentRequest = $.ajax({ const thisRequest = meta.currentRequest = $.ajax({
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
url: `${API_BASE}/games${(afterTime === undefined) ? '' : `/poll/${afterTime}`}`, url: `${API_BASE}/games${!afterTime ? '' : `/poll/${afterTime}`}`,
cache: false, cache: false,
timeout: (afterTime === undefined) ? SHORT_TIMEOUT : LONG_TIMEOUT, timeout: afterTime ? LONG_TIMEOUT : SHORT_TIMEOUT,
}).done((data, textStatus, jqXHR) => { }).done((data, textStatus, jqXHR) => {
if (meta.currentRequest === thisRequest) { if (meta.currentRequest === thisRequest) {
meta.currentRequest = null; meta.currentRequest = null;
@ -383,7 +383,7 @@ function startMetaPoll(afterTime) {
setConnectionState('polling'); setConnectionState('polling');
startMetaPoll(afterTime); startMetaPoll(afterTime);
} }
}).fail((/*jqXHR, textStatus, errorThrown*/) => { }).fail((jqXHR, textStatus, errorThrown) => {
if (meta.currentRequest === thisRequest) { if (meta.currentRequest === thisRequest) {
setConnectionState('failed'); setConnectionState('failed');
setTimeout(() => { setTimeout(() => {

View File

@ -8,7 +8,7 @@ import deepEqual from 'deep-equal';
import 'webpack-jquery-ui/draggable'; import 'webpack-jquery-ui/draggable';
import 'webpack-jquery-ui/droppable'; import 'webpack-jquery-ui/droppable';
import 'webpack-jquery-ui/selectmenu'; import 'webpack-jquery-ui/selectmenu';
import 'webpack-jquery-ui/clip-effect'; import 'webpack-jquery-ui/fold-effect';
import 'webpack-jquery-ui/css'; import 'webpack-jquery-ui/css';
import 'jquery-ui-touch-punch'; import 'jquery-ui-touch-punch';
@ -23,10 +23,6 @@ import {Workbox, messageSW} from 'workbox-window';
import {ResizeSensor, ElementQueries} from 'css-element-queries'; import {ResizeSensor, ElementQueries} from 'css-element-queries';
ElementQueries.listen(); ElementQueries.listen();
import {Buffer} from 'buffer';
import pako from 'pako';
import {sprintf} from 'sprintf-js';
/* "Waterdrop" by Porphyr (freesound.org/people/Porphyr) / CC BY 3.0 (creativecommons.org/licenses/by/3.0) */ /* "Waterdrop" by Porphyr (freesound.org/people/Porphyr) / CC BY 3.0 (creativecommons.org/licenses/by/3.0) */
import Waterdrop from '../mp3/191678__porphyr__waterdrop.mp3'; import Waterdrop from '../mp3/191678__porphyr__waterdrop.mp3';
@ -101,7 +97,7 @@ $(function (){
square.hide().appendTo('body').droppable({ square.hide().appendTo('body').droppable({
accept: '.cb-piece', accept: '.cb-piece',
disabled: true, disabled: true,
deactivate: function(/*ev, ui*/) { deactivate: function(ev, ui){
$(this).droppable('disable'); $(this).droppable('disable');
}, },
drop: squareDropDestination, drop: squareDropDestination,
@ -109,7 +105,7 @@ $(function (){
} }
return square; return square;
} else if (where.match(/^[a-h][1-8]$/)) { } else if (where.match(/^[a-h][1-8]$/)) {
return $('#cb_' + where).first(); return $('#cb_' + where).first()
} else { } else {
return null; return null;
} }
@ -143,6 +139,7 @@ $(function (){
function pieceStartMove(piece, event) { function pieceStartMove(piece, event) {
const side = piece.data('side'); const side = piece.data('side');
const type = piece.data('type');
const from = piece.data('location'); const from = piece.data('location');
const legals = currentGame.legalMoves(side, from); const legals = currentGame.legalMoves(side, from);
for (const there of legals) { for (const there of legals) {
@ -151,7 +148,7 @@ $(function (){
} }
const square = cbSquare(there); const square = cbSquare(there);
square.addClass('cb-legal'); square.addClass('cb-legal')
if (event === 'drag') { if (event === 'drag') {
square.droppable('enable'); square.droppable('enable');
} else if (event === 'click') { } else if (event === 'click') {
@ -180,7 +177,7 @@ $(function (){
putState(); putState();
} }
function squareClickDestination(/*ev, ui*/) { function squareClickDestination(ev, ui) {
let selected = $('#cb_board .cb-selected'); let selected = $('#cb_board .cb-selected');
if (selected.length !== 1) { if (selected.length !== 1) {
renderBoard(); renderBoard();
@ -199,11 +196,11 @@ $(function (){
} }
} }
function squareClickUnselect(/*ev, ui*/) { function squareClickUnselect(ev, ui) {
renderBoard(); renderBoard();
} }
function squareClickSelect(/*ev, ui*/) { function squareClickSelect(ev, ui) {
renderBoard(); renderBoard();
const clicked = $(this).children('.cb-piece.ui-draggable').not('.ui-draggable-disabled'); const clicked = $(this).children('.cb-piece.ui-draggable').not('.ui-draggable-disabled');
clicked.addClass('cb-selected'); clicked.addClass('cb-selected');
@ -217,7 +214,7 @@ $(function (){
} }
} }
function pieceStartDrag(/*ev, ui*/) { function pieceStartDrag(ev, ui) {
const dragged = $(this); const dragged = $(this);
$('#cb_board .cb-selected').removeClass('cb-selected'); $('#cb_board .cb-selected').removeClass('cb-selected');
$('#cb_board .cb-legal').removeClass('cb-legal'); $('#cb_board .cb-legal').removeClass('cb-legal');
@ -226,7 +223,7 @@ $(function (){
pieceStartMove(dragged, 'drag'); pieceStartMove(dragged, 'drag');
} }
function pieceStopDrag(/*ev, ui*/) { function pieceStopDrag(ev, ui) {
const dragged = $(this); const dragged = $(this);
dragged.attr('style', dragged.data('saved-style')); dragged.attr('style', dragged.data('saved-style'));
dragged.removeData('saved-style'); dragged.removeData('saved-style');
@ -255,81 +252,6 @@ $(function (){
return piece; return piece;
} }
function updatePlayerTimes() {
let lightTime = 0;
let darkTime = 0;
let moveStartTime = undefined;
for (const move of visibleGame.moves) {
if (!move.meta || !Number.isInteger(move.meta.timestamp)) {
lightTime = darkTime = undefined;
break;
}
if (move.replaced) {
/* not the final move in the chain */
continue;
}
/* start counting when the dark player finishes their first move */
if (moveStartTime === undefined) {
if (move.side === PS.DARK) {
moveStartTime = move.meta.timestamp;
} else {
continue;
}
}
const moveTime = move.meta.timestamp - moveStartTime;
if (moveTime > 0) {
if (move.side === PS.LIGHT) {
lightTime += moveTime;
} else {
darkTime += moveTime;
}
}
moveStartTime = move.meta.timestamp;
}
if (moveStartTime === undefined) {
$('#cb_light_time, #cb_dark_time').text('0:00:00');
$('#cb_times').addClass('cb-hide-times');
return;
}
if (visibleGame.status === PS.PLAYING) {
if (!visibleGame.canRedo) {
const currentMoveTime = +new Date() - moveStartTime;
if (currentMoveTime > 0) {
if (visibleGame.player === PS.LIGHT) {
lightTime += currentMoveTime;
} else {
darkTime += currentMoveTime;
}
}
}
}
$('#cb_times').removeClass('cb-hide-times');
function formatTime(milliseconds) {
let seconds = milliseconds / 1000;
const hours = seconds / 3600;
seconds %= 3600;
const minutes = seconds / 60;
seconds %= 60;
return sprintf('%d:%02d:%02d', hours, minutes, seconds);
}
$('#cb_light_time').text(formatTime(lightTime));
$('#cb_dark_time').text(formatTime(darkTime));
}
setInterval(() => { updatePlayerTimes(); }, 250);
function renderBoard(animate) { function renderBoard(animate) {
$('#cb_board').removeData('dragging_from'); $('#cb_board').removeData('dragging_from');
$('#cb_board .cb-piece').remove(); $('#cb_board .cb-piece').remove();
@ -340,8 +262,6 @@ $(function (){
$('#cb_board .cb-end').removeClass('cb-end'); $('#cb_board .cb-end').removeClass('cb-end');
$('#cb_board .cb-legal').removeClass('cb-legal'); $('#cb_board .cb-legal').removeClass('cb-legal');
$('#cb_explain_check').text(''); $('#cb_explain_check').text('');
$('#cb_names').removeClass('cb-light-turn').removeClass('cb-dark-turn');
$('#cb_names').removeClass('cb-light-won').removeClass('cb-dark-won');
$('#cb_phantom').remove(); $('#cb_phantom').remove();
const game = visibleGame; const game = visibleGame;
@ -420,7 +340,7 @@ $(function (){
pieceStartMove(piece, 'click'); pieceStartMove(piece, 'click');
} }
} else if (liveView && playing) { } else if (liveView && playing) {
const pieces = $('#cb_board .' + clss); const pieces = $('#cb_board .' + clss)
pieces.parent().on('click.select', squareClickSelect); pieces.parent().on('click.select', squareClickSelect);
pieces.draggable('enable'); pieces.draggable('enable');
} }
@ -467,20 +387,6 @@ $(function (){
} else { } else {
$('#cb_board').removeClass('cb-live').addClass('cb-archive'); $('#cb_board').removeClass('cb-live').addClass('cb-archive');
} }
if (game.status === PS.PLAYING) {
if (game.player === PS.LIGHT) {
$('#cb_names').addClass('cb-light-turn');
} else {
$('#cb_names').addClass('cb-dark-turn');
}
} else if (game.winner === PS.LIGHT) {
$('#cb_names').addClass('cb-light-won');
} else if (game.winner === PS.DARK) {
$('#cb_names').addClass('cb-dark-won');
}
updatePlayerTimes();
} }
function applyTheme(theme) { function applyTheme(theme) {
@ -503,14 +409,6 @@ $(function (){
function setCurrentGame(game, animate) { function setCurrentGame(game, animate) {
currentGame = game; currentGame = game;
setVisibleGame(game, animate); setVisibleGame(game, animate);
const state = game.toJSON('minify');
const deflated = pako.deflate(Buffer.from(state));
const encoded = Buffer.from(deflated).toString('base64');
if ($('#page').hasClass('cb-private')) {
history.replaceState(null, document.title, `#/private/${encoded}`);
} else {
$('.private-link').attr('href', `#/private/${encoded}`);
}
} }
function randomId(){ function randomId(){
@ -569,6 +467,7 @@ $(function (){
signal_idle: function() {}, signal_idle: function() {},
add(gameId, data, modified) { add(gameId, data, modified) {
const wasEmpty = this.isEmpty();
data = Object.assign({}, data, { modified }); data = Object.assign({}, data, { modified });
this[this.tail] = { gameId, data }; this[this.tail] = { gameId, data };
@ -678,6 +577,7 @@ $(function (){
function putState() { function putState() {
const boardElem = $('#cb_board'); const boardElem = $('#cb_board');
const gameId = boardElem.data('gameId');
notifyLocalMove(currentGame, boardElem.data('gameId')); notifyLocalMove(currentGame, boardElem.data('gameId'));
putMeta({ board: JSON.parse(currentGame.toJSON()) }); putMeta({ board: JSON.parse(currentGame.toJSON()) });
} }
@ -686,12 +586,7 @@ $(function (){
function player(side) { function player(side) {
return (side === PS.LIGHT ? 'light' : 'dark'); return (side === PS.LIGHT ? 'light' : 'dark');
} }
const gameId = $('#cb_board').data('gameId'); const gameId = $('#cb_board').data('gameId');
if (gameId === 'private') {
return;
}
const lightName = $('#cb_light_name').val(); const lightName = $('#cb_light_name').val();
const darkName = $('#cb_dark_name').val(); const darkName = $('#cb_dark_name').val();
const turns = currentGame.turns; const turns = currentGame.turns;
@ -754,10 +649,7 @@ $(function (){
$('#cb_light_name').val(''); $('#cb_light_name').val('');
$('#cb_dark_name').val(''); $('#cb_dark_name').val('');
if (newId === 'private') { cancelGameCallback = IO.onGameUpdate(newId, function(data, gameId) {
cancelGameCallback = function() {};
} else {
cancelGameCallback = IO.onGameUpdate(newId, function(data/*, gameId*/) {
updateQueue.idle.then(() => { updateQueue.idle.then(() => {
if (data.modified > $('#cb_board').data('modified')) { if (data.modified > $('#cb_board').data('modified')) {
try { try {
@ -780,8 +672,6 @@ $(function (){
$('#cb_dark_name').val(String(d.darkName || '')); $('#cb_dark_name').val(String(d.darkName || ''));
$('#cb_board').data('modified', data.modified); $('#cb_board').data('modified', data.modified);
updateSelectGameMeta(data, newId);
} }
}); });
}); });
@ -793,17 +683,16 @@ $(function (){
requestNotify(); requestNotify();
} }
/* Ensure that the selected game is in the list (for new games). */
if ($('#game_tile_' + newId).length < 1) {
updateSelectGameMeta({}, newId);
}
}
const reverseList = $('#cb_reverse').data('gameList'); const reverseList = $('#cb_reverse').data('gameList');
const doReverse = reverseList.includes('*') || reverseList.includes(newId); const doReverse = reverseList.includes('*') || reverseList.includes(newId);
$('#cb_reverse').prop('checked', doReverse); $('#cb_reverse').prop('checked', doReverse);
arrangeBoard(doReverse); arrangeBoard(doReverse);
/* Ensure that the selected game is in the list (for new games). */
if ($('#game_tile_' + newId).length < 1) {
updateSelectGameMeta({}, newId);
}
/* This is in case the old tile no longer qualifies to be in the list. */ /* This is in case the old tile no longer qualifies to be in the list. */
const oldGameTile = $('#game_tile_' + gameId); const oldGameTile = $('#game_tile_' + gameId);
if (oldGameTile.length >= 1) { if (oldGameTile.length >= 1) {
@ -834,7 +723,7 @@ $(function (){
const notifyAudio = new Audio(Waterdrop); const notifyAudio = new Audio(Waterdrop);
function playNotifySound(){ function playNotifySound(){
try { notifyAudio.play(); } catch (err) {/*ignore*/} try { notifyAudio.play(); } catch (err) {}
} }
function notify(body) { function notify(body) {
@ -867,7 +756,7 @@ $(function (){
notification.close(); notification.close();
} }
} }
} catch (err) {/*ignore*/} } catch (err) {}
} }
function arrangeBoard(reversed) { function arrangeBoard(reversed) {
@ -929,11 +818,11 @@ $(function (){
} }
} }
function reloadForUpdate(/*event*/) { function reloadForUpdate(event) {
window.location.reload(); window.location.reload();
} }
wb.addEventListener('installed', (/*event*/) => { wb.addEventListener('installed', (event) => {
try { try {
if (Notification.permission === 'denied') { if (Notification.permission === 'denied') {
disableNotify(); disableNotify();
@ -950,7 +839,6 @@ $(function (){
wb.addEventListener('externalactivated', reloadForUpdate); wb.addEventListener('externalactivated', reloadForUpdate);
const registration = await wb.register(); const registration = await wb.register();
await registration.ready;
/* Check for updates every 4h without reloading the page. */ /* Check for updates every 4h without reloading the page. */
setInterval(() => { wb.update(); }, 4*3600*1000); setInterval(() => { wb.update(); }, 4*3600*1000);
@ -973,7 +861,7 @@ $(function (){
let gameList = undefined; let gameList = undefined;
const gameId = $('#cb_board').data('gameId'); const gameId = $('#cb_board').data('gameId');
if (value === 'on') { if (value === 'on') {
gameList = ['*']; gameList = ['*']
} else if (value === null || value === 'off') { } else if (value === null || value === 'off') {
gameList = []; gameList = [];
} else { } else {
@ -1032,7 +920,7 @@ $(function (){
applyTheme(value); applyTheme(value);
} }
} }
}; }
$(window).on('storage', function(event){ $(window).on('storage', function(event){
fromStorage(event.originalEvent.key, event.originalEvent.newValue); fromStorage(event.originalEvent.key, event.originalEvent.newValue);
@ -1047,7 +935,7 @@ $(function (){
$('.cb-square').droppable({ $('.cb-square').droppable({
accept: '.cb-piece', accept: '.cb-piece',
disabled: true, disabled: true,
deactivate: function(/*ev, ui*/){ deactivate: function(ev, ui){
$(this).droppable('disable'); $(this).droppable('disable');
}, },
drop: squareDropDestination, drop: squareDropDestination,
@ -1238,7 +1126,6 @@ $(function (){
var selectBoxIntervalID = undefined; var selectBoxIntervalID = undefined;
var selectBox = new jBox('Modal', { var selectBox = new jBox('Modal', {
title: '<h2>Recent Games</h2>',
content: gameSelectContent, content: gameSelectContent,
blockScroll: false, blockScroll: false,
blockScrollAdjust: false, blockScrollAdjust: false,
@ -1308,6 +1195,11 @@ $(function (){
} }
function updateSelectGameMeta(data, gameId) { function updateSelectGameMeta(data, gameId) {
data = Object.assign({}, data, {
gameId: gameId || data.gameId,
timestamp: data.timestamp || +new Date(),
});
if (typeof gameId !== 'string' || !gameId.match(/^[0-9a-f]{16}$/)) { if (typeof gameId !== 'string' || !gameId.match(/^[0-9a-f]{16}$/)) {
debug('invalid game ID', gameId); debug('invalid game ID', gameId);
return; return;
@ -1317,11 +1209,10 @@ $(function (){
const oldTile = $('#game_tile_' + gameId).first(); const oldTile = $('#game_tile_' + gameId).first();
if (!data.lightName && !data.darkName && !data.moves && gameId !== currentGameId) { if (!data.lightName && !data.darkName && !data.moves && gameId !== currentGameId) {
if (oldTile.length >= 1) {
oldTile.removeAttr('id'); oldTile.removeAttr('id');
if (oldTile.length >= 1) {
oldTile.hide({ oldTile.hide({
effect: "clip", effect: "fold",
direction: "horizontal",
complete() { complete() {
oldTile.remove(); oldTile.remove();
}, },
@ -1330,13 +1221,6 @@ $(function (){
return; return;
} }
if (oldTile.length && data.modified && oldTile.data('gameMeta').modified >= data.modified) {
return;
}
data = Object.assign({ gameId, timestamp: +new Date(), modified: 0 }, data);
delete data.board;
const tile = oldTile.length ? oldTile : const tile = oldTile.length ? oldTile :
$(`<div class="game-tile existing-game-tile"></div>`).hide(); $(`<div class="game-tile existing-game-tile"></div>`).hide();
tile.attr('id', 'game_tile_' + gameId).data('gameMeta', data).empty(); tile.attr('id', 'game_tile_' + gameId).data('gameMeta', data).empty();
@ -1386,14 +1270,13 @@ $(function (){
$(list).appendTo(gameTiles); $(list).appendTo(gameTiles);
updateTileAges(); updateTileAges();
tile.show({ tile.show("fold");
effect: "clip",
direction: "horizontal",
});
selectBox.setContent(gameSelectContent); selectBox.setContent(gameSelectContent);
} }
IO.onMetaUpdate(updateSelectGameMeta);
const lastNotifyState = {}; const lastNotifyState = {};
function notifyForGame(meta, gameId) { function notifyForGame(meta, gameId) {
@ -1462,27 +1345,15 @@ $(function (){
} }
} }
window.onhashchange = function(/*event*/){ IO.onMetaUpdate(notifyForGame);
window.onpopstate = function(event){
const foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/); const foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/);
if (foundId) { if (foundId) {
switchGameId(foundId[1]); switchGameId(foundId[1]);
} }
}; };
const foundPrivate = location.hash.match(/^#\/private((\/(.*)?)?)$/);
if (foundPrivate) {
switchGameId('private');
$('#page').addClass('cb-private');
let state;
try {
const decoded = Buffer.from(foundPrivate[1].slice(1), 'base64');
const inflated = pako.inflate(decoded);
state = JSON.parse(Buffer.from(inflated).toString());
} catch(err) {/*ignore*/}
if (state) {
setCurrentGame(new PS.Game(JSON.stringify(state)));
}
} else {
const foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/); const foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/);
if (foundId) { if (foundId) {
switchGameId(foundId[1]); switchGameId(foundId[1]);
@ -1490,13 +1361,6 @@ $(function (){
switchGameId(randomId()); switchGameId(randomId());
} }
IO.onMetaUpdate(async (meta, gameId) => {
await updateQueue.idle;
updateSelectGameMeta(meta, gameId);
notifyForGame(meta, gameId);
});
}
function adjustBoardSize() { function adjustBoardSize() {
let container = $('#cb_container'); let container = $('#cb_container');
let outerWidth = container.width(); let outerWidth = container.width();
@ -1527,7 +1391,6 @@ $(function (){
$('#header').appendTo('#board_ui'); $('#header').appendTo('#board_ui');
$('#cb_status').appendTo('#board_ui'); $('#cb_status').appendTo('#board_ui');
$('#cb_names').appendTo('#board_ui'); $('#cb_names').appendTo('#board_ui');
$('#cb_times').appendTo('#board_ui');
$('#cb_navigate').appendTo('#board_ui'); $('#cb_navigate').appendTo('#board_ui');
$('#board_ui').appendTo('#page'); $('#board_ui').appendTo('#page');
$('#page').removeClass('vertical-layout').addClass('horizontal-layout'); $('#page').removeClass('vertical-layout').addClass('horizontal-layout');
@ -1536,7 +1399,6 @@ $(function (){
$('#cb_container').appendTo('#board_ui'); $('#cb_container').appendTo('#board_ui');
$('#cb_status').appendTo('#board_ui'); $('#cb_status').appendTo('#board_ui');
$('#cb_names').appendTo('#board_ui'); $('#cb_names').appendTo('#board_ui');
$('#cb_times').appendTo('#board_ui');
$('#cb_navigate').appendTo('#board_ui'); $('#cb_navigate').appendTo('#board_ui');
$('#board_ui').appendTo('#page'); $('#board_ui').appendTo('#page');
$('#page').removeClass('horizontal-layout').addClass('vertical-layout'); $('#page').removeClass('horizontal-layout').addClass('vertical-layout');

23049
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "paco-sako", "name": "paco-sako",
"version": "0.8.7", "version": "0.7.4",
"description": "Online version of the Paco Ŝako chess variation", "description": "Online version of the Paco Ŝako chess variation",
"keywords": [ "keywords": [
"game", "game",
@ -32,34 +32,29 @@
}, },
"devDependencies": { "devDependencies": {
"@fortawesome/fontawesome-free": "^5.13.0", "@fortawesome/fontawesome-free": "^5.13.0",
"buffer": "^6.0.3", "clean-webpack-plugin": "^3.0.0",
"clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^5.1.1",
"copy-webpack-plugin": "^6.4.1",
"css-element-queries": "^1.2.3", "css-element-queries": "^1.2.3",
"deep-equal": "git+https://jessemcdonald.info/gogs/nybble/node-deep-equal", "deep-equal": "git+https://jessemcdonald.info/gogs/nybble/node-deep-equal",
"extract-loader": "^5.0.1", "extract-loader": "^5.0.1",
"html-webpack-plugin": "^4.5.2", "html-webpack-plugin": "^4.3.0",
"jbox": "^1.2.0", "jbox": "^1.2.0",
"jquery-ui-touch-punch": "^0.2.3", "jquery-ui-touch-punch": "^0.2.3",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"mini-css-extract-plugin": "^1.6.2", "mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^6.0.1", "optimize-css-assets-webpack-plugin": "^5.0.3",
"pako": "^2.0.4", "svgo": "^1.3.2",
"sprintf-js": "^1.0.3", "svgo-loader": "^2.2.1",
"svgo": "^2.7.0", "webpack": "^4.43.0",
"svgo-loader": "^3.0.0", "webpack-cli": "^3.3.11",
"webpack": "^4.46.0",
"webpack-cli": "^4.8.0",
"webpack-jquery-ui": "^2.0.1", "webpack-jquery-ui": "^2.0.1",
"workbox-precaching": "^6.3.0", "workbox-precaching": "^5.1.3",
"workbox-routing": "^6.3.0", "workbox-routing": "^5.1.3",
"workbox-strategies": "^6.3.0", "workbox-strategies": "^5.1.3",
"workbox-webpack-plugin": "^6.3.0", "workbox-webpack-plugin": "^5.1.3",
"workbox-window": "^6.3.0" "workbox-window": "^5.1.3"
},
"dependencies": {
"paco-sako-server": "git+https://jessemcdonald.info/gogs/nybble/paco_sako_server"
}, },
"dependencies": {},
"optionalDependencies": { "optionalDependencies": {
"paco-sako-server": "git+https://jessemcdonald.info/gogs/nybble/paco_sako_server" "paco-sako-server": "git+https://jessemcdonald.info/gogs/nybble/paco_sako_server"
}, },

View File

@ -52,14 +52,7 @@ module.exports = {
loader: "svgo-loader", loader: "svgo-loader",
options: { options: {
plugins: [ plugins: [
{ { cleanupIDs: false }
name: "preset-default",
params: {
overrides: {
cleanupIDs: false,
}
}
},
] ]
} }
} }
@ -95,11 +88,9 @@ module.exports = {
"window.jQuery": "jquery'", "window.jQuery": "jquery'",
"window.$": "jquery" "window.$": "jquery"
}), }),
new CopyPlugin({ new CopyPlugin([
patterns: [
{ from: '.htaccess' }, { from: '.htaccess' },
], ]),
}),
new InjectManifest({ new InjectManifest({
swSrc: './sw.js', swSrc: './sw.js',
dontCacheBustURLsMatching: /\.[0-9a-f]{16,}\.\w{2,4}$/, dontCacheBustURLsMatching: /\.[0-9a-f]{16,}\.\w{2,4}$/,