Compare commits

...

35 Commits

Author SHA1 Message Date
Jesse D. McDonald 21623b24f0 0.8.7 2021-09-26 18:21:16 -05:00
Jesse D. McDonald d2fe0f47c3 update dependencies to latest versions 2021-09-26 18:21:01 -05:00
Jesse D. McDonald f5bd481975 0.8.6 2021-09-26 18:21:01 -05:00
Jesse D. McDonald efe995e247 add <!DOCTYPE html> to index.html to avoid quirks mode 2021-09-26 18:21:01 -05:00
Jesse D. McDonald b553eb434a distinguish between 0 and undefined for afterTime when polling metadata 2021-09-26 18:21:01 -05:00
Jesse D. McDonald 4f72d75642 0.8.5 2020-06-24 21:45:23 -05:00
Jesse D. McDonald a829e7224c minor layout tweaks 2020-06-24 21:45:15 -05:00
Jesse D. McDonald 23202e7a7b 0.8.4 2020-06-22 14:26:52 -05:00
Jesse D. McDonald 06c605344f fix missing CSS import for traditional chess icon theme 2020-06-22 14:26:45 -05:00
Jesse D. McDonald 015447dc57 0.8.3 2020-06-21 23:04:48 -05:00
Jesse D. McDonald 59af2e0a29 fix for missing data when showing tiles for old games 2020-06-21 23:04:33 -05:00
Jesse D. McDonald 8de753aac5 use horizontal clip effect instead of fold 2020-06-21 23:02:51 -05:00
Jesse D. McDonald 0febac8b3c use CSS grid for game selection list 2020-06-21 23:01:34 -05:00
Jesse D. McDonald 2c1892303e 0.8.2 2020-05-21 16:14:59 -05:00
Jesse D. McDonald 0df3a7c647 display cumulative waiting time since each player's first move 2020-05-21 16:14:55 -05:00
Jesse D. McDonald 3bae819cb2 0.8.1 2020-05-20 13:39:38 -05:00
Jesse D. McDonald d020e94ac0 highlight the piece beside the name of the current player or winner 2020-05-20 13:39:29 -05:00
Jesse D. McDonald f2fad336a3 0.8.0 2020-05-14 23:48:24 -05:00
Jesse D. McDonald 6495c49022 add "private copy" feature for client-only exploration of moves 2020-05-14 23:46:14 -05:00
Jesse D. McDonald 6dd6c2e3d6 fix issues reported by eslint 2020-05-12 18:26:05 -05:00
Jesse D. McDonald 04638e650d 0.7.4 2020-05-11 13:04:04 -05:00
Jesse D. McDonald d1439b3f09 fix (maybe) for page not reloading after updating service worker 2020-05-11 13:04:00 -05:00
Jesse D. McDonald 994880f454 0.7.3 2020-05-10 20:20:37 -05:00
Jesse D. McDonald 9795c83583 fix for race between polling and server response after update 2020-05-10 20:20:31 -05:00
Jesse D. McDonald f4318b2464 0.7.2 2020-05-10 17:56:05 -05:00
Jesse D. McDonald fd3a8db71d fix for too much padding in side-panel header 2020-05-10 17:56:00 -05:00
Jesse D. McDonald 994da62aac add -moz-appearance alongside -webkit-appearance (formality) 2020-05-10 17:39:48 -05:00
Jesse D. McDonald 07069e25a8 0.7.1 2020-05-10 17:29:10 -05:00
Jesse D. McDonald 0f3313902c add background & gradient border to side panel 2020-05-10 17:29:06 -05:00
Jesse D. McDonald 5f94e931a0 0.7.0 2020-05-10 15:42:41 -05:00
Jesse D. McDonald cb99962074 completely redesign the user interface 2020-05-10 15:42:23 -05:00
Jesse D. McDonald 8b06a83361 use 11 KiB .mp3 for audio rather than 188 KiB .wav 2020-05-10 11:07:18 -05:00
Jesse D. McDonald 91a7884a4d mark completed feature in roadmap 2020-05-09 13:06:17 -05:00
Jesse D. McDonald 555864b1cf 0.6.1 2020-05-09 13:05:38 -05:00
Jesse D. McDonald a1a9eea758 fix for not notifying on next move after switching game 2020-05-09 13:05:32 -05:00
17 changed files with 18571 additions and 6540 deletions

21
.eslintrc.js Normal file
View File

@ -0,0 +1,21 @@
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

@ -1,7 +1,7 @@
# Road Map # Road Map
- [x] Block castling through check (middle square needs to be clear of check). - [x] Block castling through check (middle square needs to be clear of check).
- [ ] Notifications for games other than the currently selected game. - [x] Notifications for games other than the currently selected game.
- [ ] Protocol and UI for offering and accepting a draw. - [ ] Protocol and UI for offering and accepting a draw.
- [ ] In-browser local, private games. - [ ] In-browser local, private games.
- [ ] Password-protected user accounts. - [ ] Password-protected user accounts.

View File

@ -2,92 +2,375 @@
box-sizing: border-box; box-sizing: border-box;
} }
html, body, #page {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: white;
}
body { body {
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
#page { body > #page {
position: fixed;
top: 0;
left: 0;
width: 100%; width: 100%;
height: 100vh;
display: flex;
flex-flow: column nowrap;
}
h1 {
font-family: 'Vollkorn', serif;
font-size: 2em;
line-height: 1.4em;
height: 1.4em;
margin-top: 8px;
margin-left: 8px;
margin-bottom: 0;
}
a.plain-link {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
line-height: inherit;
color: inherit;
background-color: inherit;
text-decoration: inherit;
}
.media-button-svg {
width: 12pt;
height: 12pt;
}
button:disabled .silhouette {
fill: #c0c0c0;
}
#content {
display: flex; display: flex;
flex-flow: row nowrap; flex-flow: row nowrap;
} }
#cb_outer2 { #board_ui {
flex: 1 1 auto; display: flex;
flex-flow: column nowrap;
justify-content: stretch;
align-items: center;
width: 100%; width: 100%;
max-width: calc(100vh - (8px + 2.8em + 8px)); height: 100%;
margin-left: 8px; background-color: #FFFFF0;
} }
#cb_outer { #page.horizontal-layout #board_ui {
border-left: 10px solid #C4B28F;
border-image:
linear-gradient(
to right,
#C4B28F,
#F5DEB3 15%,
#C4B28F 45%,
#FFFFF0 80%,
#C4B28F) 1 100%;
}
#header {
display: flex;
flex-flow: row nowrap;
flex: 0 0 auto;
width: 100%; width: 100%;
max-width: calc(100vw - (8px + 24px)); height: 2.5rem;
font-size: 2rem;
line-height: 2.4rem;
margin: 0;
background-image: linear-gradient(#FFFFF0, #F5DEB3);
border-bottom: 1px solid black;
align-items: center;
}
#header > h1 {
font-family: 'Vollkorn', serif;
text-align: center;
font-size: 1.2rem;
line-height: 2.4rem;
margin: 0;
padding: 0;
padding-top: 0.1rem;
height: 2.5rem;
white-space: nowrap;
flex: 1 0 auto;
}
#header > button {
flex: 0 0 auto;
width: 2.5rem;
height: 2.5rem;
border: none;
background: none;
padding: 0;
margin: 0;
}
#header > button:active {
background-image: linear-gradient(#F5DEB3, #FFFFF0);
}
input.image-checkbox {
display: inline-block;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
padding: 0;
margin: 0;
}
input.image-checkbox:checked {
color: #00f;
}
input.image-checkbox:focus {
outline: blue solid medium;
outline: -webkit-focus-ring-color solid medium;
outline-offset: 3px;
}
#header > .checkbox-container {
flex: 0 0 auto;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
width: 2.5rem;
height: 2.5rem;
border: none;
background: none;
}
button#settings, button#cb_choose_game {
border-right: 1px solid #DCC8A1;
}
#header > .checkbox-container, button#help {
border-left: 1px solid #DCC8A1;
} }
#cb_container { #cb_container {
position: relative;
width: 100%; width: 100%;
padding-top: 100%; flex: 1 1 auto;
position: relative;
} }
#cb_inner { #page.horizontal-layout #cb_container {
position: absolute; flex: 0 0.5 auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
} }
#cb_board { #cb_board {
position: relative; position: absolute;
width: 100%;
height: 100%;
border-collapse: collapse; border-collapse: collapse;
border-style: hidden; border-style: hidden;
padding-top: 100%;
box-shadow: 0 0 0 1px black, 2px 2px 4px #00000080; box-shadow: 0 0 0 1px black, 2px 2px 4px #00000080;
border-radius: 6pt; border-radius: 6pt;
overflow: hidden; overflow: hidden;
background-color: #FAEED2; background-color: #FAEED2;
} }
#cb_status {
display: flex;
position: relative;
flex-flow: column nowrap;
width: 100%;
padding-top: 0.25rem;
padding-left: 0.25rem;
padding-right: 0.25rem;
padding-bottom: 0.5rem;
}
#page.horizontal-layout #cb_status {
flex: 1 1 auto;
}
#cb_status::after {
content: '';
display: block;
position: absolute;
width: 100%;
height: 1.5em;
left: 0;
bottom: 0.5rem;
background: linear-gradient(rgba(255,255,240,0),rgba(255,255,240,1));
pointer-events: none;
}
#cb_message {
height: 1.2em;
margin-bottom: 0.1rem;
flex: 1 1 auto;
}
#cb_explain_check {
color: red;
height: 2.4em;
margin-bottom: 0.1em;
}
#cb_scrollable {
flex: 0 0 auto;
overflow: auto;
height: 4.8em;
padding-bottom: 1.2em;
}
#page.horizontal-layout #cb_scrollable {
flex: 1 1 auto;
}
#cb_history {
color: black;
}
#cb_history_future {
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 {
position: relative;
display: flex;
flex-flow: row nowrap;
white-space: nowrap;
justify-content: stretch;
align-items: flex-end;
font-size: 1.5rem;
width: 100%;
height: 3rem;
padding-left: 2rem;
padding-right: 2rem;
}
#cb_names_text {
display: flex;
flex-flow: row nowrap;
white-space: nowrap;
justify-content: stretch;
align-items: baseline;
width: 100%;
z-index: 1;
}
#cb_names::before {
content: '';
display: block;
background-image: url(../svg/pacosako-theme.svg#light_king_left);
background-size: contain;
background-repeat: no-repeat;
position: absolute;
bottom: 0;
left: 0.5rem;
width: 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 {
content: '';
display: block;
background-image: url(../svg/pacosako-theme.svg#dark_king_right);
background-size: contain;
background-repeat: no-repeat;
position: absolute;
bottom: 0;
right: 0.5rem;
width: 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 {
padding-left: 0.25em;
padding-right: 0.25em;
}
#cb_names input {
flex: 1 1 auto;
width: 4em;
background-color: inherit;
border: none;
margin: 2px;
font-size: 1.5rem;
}
#cb_names input:focus {
outline: 1px solid blue;
outline-offset: 2px;
}
#cb_dark_name {
text-align: right;
}
#cb_navigate {
display: flex;
flex-flow: row nowrap;
align-items: stretch;
justify-content: stretch;
border-top: 1px solid black;
background-image: linear-gradient(#FFFFF0, #F5DEB3);
white-space: nowrap;
width: 100%;
flex: 0 1 auto;
font-size: 1.25rem;
}
#cb_navigate .nav-spacer {
flex: 1 1 auto;
min-width: 1rem;
width: 100%;
background: none;
background-color: #DCC8A140;
}
#cb_navigate button {
padding: 0;
margin: 0;
border: none;
width: 2rem;
min-width: 2rem;
min-height: 2rem;
background: none;
font-size: inherit;
}
#cb_navigate button:active {
background-image: linear-gradient(#F5DEB3, #FFFFF0);
}
#cb_navigate > .nav-spacer, #cb_navigate > button {
border-left: 1px solid #DCC8A1;
}
#cb_navigate > .nav-spacer:first-child, #cb_navigate > button:first-child {
border-left: none;
}
#cb_board tr { #cb_board tr {
position: relative; position: relative;
} }
@ -97,163 +380,14 @@ button:disabled .silhouette {
position: relative; position: relative;
} }
#cb_light_name, #cb_dark_name {
width: 8em;
}
#cb_control_container {
display: flex;
flex-flow: column nowrap;
flex: 0 1000 auto;
margin-left: 1em;
width: 100%;
min-width: 24em;
max-height: calc(100vh - (8px + 2.8em));
padding-right: calc(3.25em + 10px);
}
#cb_control_form {
flex: 0 1 auto;
display: flex;
flex-flow: column nowrap;
margin-right: auto;
border-radius: 6pt;
padding: 1.125em;
background-color: #FFFFF0;
box-shadow: 0 0 0 1px black, 2px 2px 4px #00000080;
}
#cb_controls, #cb_theme, #cb_names, #cb_navigate {
white-space: nowrap;
}
#cb_theme, #cb_names, #cb_navigate {
margin-top: 1.125em;
}
#cb_names input {
background-color: inherit;
border: none;
border-bottom: 1px solid black;
margin: 2px;
}
#cb_names input:focus {
outline: 1px solid blue;
outline-offset: 2px;
}
#cb_scrollable {
flex: 1 1000 auto;
padding-bottom: 0.5em;
overflow: auto;
}
#cb_message {
margin-top: 0.5em;
margin-bottom: 1em;
}
#cb_theme {
display: flex;
flex-flow: row nowrap;
align-items: baseline;
}
.cb-spacer {
flex: 1 1 auto;
}
#cb_choose_game {
margin-left: 0.2em;
}
#cb_theme label {
flex: 0 0 auto;
padding-right: 0.2em;
}
#cb_undo {
margin-left: auto;
margin-right: 0.1em;
}
#cb_redo {
margin-left: 0.1em;
margin-right: auto;
}
#cb_resign {
margin-left: 0.1em;
margin-right: 0;
}
#cb_navigate {
display: flex;
flex-flow: row nowrap;
justify-items: center;
justify-content: center;
padding-left: 1em;
}
#cb_navigate button {
margin-left: 0.1em;
margin-right: 0.1em;
}
#cb_navigate button:first-child {
margin-left: 0;
}
#cb_navigate button:last-child {
margin-right: 0;
}
#cb_history {
max-width: 7.5in;
margin-bottom: 0.5em;
}
#cb_history_future {
color: grey;
}
#cb_explain_check {
color: red;
margin-top: 0.5em;
}
.cb-hbox {
display: flex;
flex-flow: row nowrap;
align-items: center;
}
.cb-vbox {
display: flex;
flex-flow: column nowrap;
align-items: center;
}
.cb-align-start {
align-items: start;
}
.cb-align-stretch {
align-items: stretch;
}
.cb-square { .cb-square {
position: relative; position: relative;
width: calc((100% - 16pt) / 8);
height: calc((100% - 16pt) / 8);
border: 1px solid black; border: 1px solid black;
padding: 0; padding: 0;
} }
.cb-horiz-label { .cb-horiz-label {
position: relative; position: relative;
width: calc((100% - 16pt) / 8);
height: 8pt; height: 8pt;
border: 1px solid black; border: 1px solid black;
padding: 0; padding: 0;
@ -261,7 +395,7 @@ button:disabled .silhouette {
.cb-horiz-label div { .cb-horiz-label div {
position: absolute; position: absolute;
top: calc(50% - (1em / 2)); top: calc(50% - 3pt);
left: 0; left: 0;
bottom: 0; bottom: 0;
right: 0; right: 0;
@ -273,7 +407,6 @@ button:disabled .silhouette {
.cb-vert-label { .cb-vert-label {
position: relative; position: relative;
width: 8pt; width: 8pt;
height: calc((100% - 16pt) / 8);
border: 1px solid black; border: 1px solid black;
padding: 0; padding: 0;
} }
@ -393,13 +526,62 @@ button:disabled .silhouette {
z-index: 1; z-index: 1;
} }
.badges { .cb-private .cb-hide-if-private {
position: fixed; display: none !important;
bottom: 5px; }
right: 5px;
margin: 0; .cb-show-if-private {
display: none !important;
}
.cb-private .cb-show-if-private {
display: initial !important;
}
div.hbox {
display: flex; display: flex;
flex-flow: column nowrap; 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 {
margin: 0;
}
.rules {
display: block;
max-width: 80vw;
}
.badges {
position: static;
bottom: auto;
right: auto;
margin-top: 0;
margin-bottom: 0;
margin-left: auto;
margin-right: 5px;
display: flex;
flex-flow: row nowrap;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
} }
@ -409,23 +591,31 @@ button:disabled .silhouette {
} }
.badge img { .badge img {
width: 3em; width: 3rem;
height: 3em; height: 3rem;
opacity: 0.5;
margin: 0; margin: 0;
} }
.badge.game-link-badge img { #cb_theme {
width: 1.8em; display: flex;
height: 1.8em; flex-flow: row nowrap;
margin: 0.6em; align-items: baseline;
margin-top: 1rem;
}
#cb_theme label {
flex: 0 0 auto;
padding-right: 0.2rem;
} }
.game-tiles { .game-tiles {
max-width: 80vw; width: calc(100vw - 6rem);
max-height: 80vh; max-width: calc(100vw - 6rem);
overflow-y: auto; max-height: calc(100vh - 12rem);
text-align: center; display: grid;
grid-template-columns: repeat(auto-fill, minmax(15em, auto));
align-items: center;
justify-items: center;
} }
.game-tiles > * { .game-tiles > * {
@ -434,14 +624,14 @@ button:disabled .silhouette {
.game-tile { .game-tile {
position: relative; position: relative;
display: inline-flex; display: 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: 1em; margin: 0.5em;
padding: 0.5em; padding: 0.5em;
border-radius: 6pt; border-radius: 6pt;
background-color: #FFFFF0; background-color: #FFFFF0;
@ -474,6 +664,7 @@ button:disabled .silhouette {
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 {
@ -596,77 +787,46 @@ button:disabled .silhouette {
margin-top: 0.5em; margin-top: 0.5em;
} }
.game-tile { .game-tiles {
font-size: calc(12in * 0.015); font-size: calc(12in * 0.014);
} }
@media only screen and (min-width: 6in) and (max-width: 12in) { @media only screen and (min-width: 6in) and (max-width: 12in) {
.game-tile { .game-tiles {
font-size: 1.5vw; font-size: 1.4vw;
} }
} }
@media only screen and (max-width: 6in) { @media only screen and (max-width: 6in) {
.game-tile { .game-tiles {
font-size: calc(6in * 0.015); font-size: calc(6in * 0.014);
} }
} }
@media only screen and (max-aspect-ratio: 3/2) { .cb-dk-piece.cb-king { content: url(../svg/pacosako-theme.svg#dark_king_right); }
#content { .cb-dk-piece.cb-queen { content: url(../svg/pacosako-theme.svg#dark_queen_right); }
flex-flow: column nowrap; .cb-dk-piece.cb-bishop { content: url(../svg/pacosako-theme.svg#dark_bishop_right); }
} .cb-dk-piece.cb-knight { content: url(../svg/pacosako-theme.svg#dark_knight_right); }
.cb-dk-piece.cb-rook { content: url(../svg/pacosako-theme.svg#dark_rook_right); }
.cb-dk-piece.cb-pawn { content: url(../svg/pacosako-theme.svg#dark_pawn_right); }
.cb-lt-piece.cb-king { content: url(../svg/pacosako-theme.svg#light_king_left); }
.cb-lt-piece.cb-queen { content: url(../svg/pacosako-theme.svg#light_queen_left); }
.cb-lt-piece.cb-bishop { content: url(../svg/pacosako-theme.svg#light_bishop_left); }
.cb-lt-piece.cb-knight { content: url(../svg/pacosako-theme.svg#light_knight_left); }
.cb-lt-piece.cb-rook { content: url(../svg/pacosako-theme.svg#light_rook_left); }
.cb-lt-piece.cb-pawn { content: url(../svg/pacosako-theme.svg#light_pawn_left); }
#cb_outer2 { .cb-reversed .cb-dk-piece.cb-king { content: url(../svg/pacosako-theme.svg#dark_king_left); }
width: auto; .cb-reversed .cb-dk-piece.cb-queen { content: url(../svg/pacosako-theme.svg#dark_queen_left); }
} .cb-reversed .cb-dk-piece.cb-bishop { content: url(../svg/pacosako-theme.svg#dark_bishop_left); }
.cb-reversed .cb-dk-piece.cb-knight { content: url(../svg/pacosako-theme.svg#dark_knight_left); }
#cb_control_container { .cb-reversed .cb-dk-piece.cb-rook { content: url(../svg/pacosako-theme.svg#dark_rook_left); }
margin-top: 1em; .cb-reversed .cb-dk-piece.cb-pawn { content: url(../svg/pacosako-theme.svg#dark_pawn_left); }
margin-left: 8px; .cb-reversed .cb-lt-piece.cb-king { content: url(../svg/pacosako-theme.svg#light_king_right); }
max-height: none; .cb-reversed .cb-lt-piece.cb-queen { content: url(../svg/pacosako-theme.svg#light_queen_right); }
overflow: visible; .cb-reversed .cb-lt-piece.cb-bishop { content: url(../svg/pacosako-theme.svg#light_bishop_right); }
width: 100%; .cb-reversed .cb-lt-piece.cb-knight { content: url(../svg/pacosako-theme.svg#light_knight_right); }
max-width: calc(100vw - (8px + 24px)); .cb-reversed .cb-lt-piece.cb-rook { content: url(../svg/pacosako-theme.svg#light_rook_right); }
padding-right: 0; .cb-reversed .cb-lt-piece.cb-pawn { content: url(../svg/pacosako-theme.svg#light_pawn_right); }
}
.badges {
position: static;
bottom: auto;
right: auto;
margin-left: auto;
margin-right: 5px;
display: flex;
flex-flow: row nowrap;
justify-content: flex-end;
}
}
.cb-dk-piece.cb-king { content: url(../svg/traditional-theme.svg#dark_king_right); }
.cb-dk-piece.cb-queen { content: url(../svg/traditional-theme.svg#dark_queen_right); }
.cb-dk-piece.cb-bishop { content: url(../svg/traditional-theme.svg#dark_bishop_right); }
.cb-dk-piece.cb-knight { content: url(../svg/traditional-theme.svg#dark_knight_right); }
.cb-dk-piece.cb-rook { content: url(../svg/traditional-theme.svg#dark_rook_right); }
.cb-dk-piece.cb-pawn { content: url(../svg/traditional-theme.svg#dark_pawn_right); }
.cb-lt-piece.cb-king { content: url(../svg/traditional-theme.svg#light_king_left); }
.cb-lt-piece.cb-queen { content: url(../svg/traditional-theme.svg#light_queen_left); }
.cb-lt-piece.cb-bishop { content: url(../svg/traditional-theme.svg#light_bishop_left); }
.cb-lt-piece.cb-knight { content: url(../svg/traditional-theme.svg#light_knight_left); }
.cb-lt-piece.cb-rook { content: url(../svg/traditional-theme.svg#light_rook_left); }
.cb-lt-piece.cb-pawn { content: url(../svg/traditional-theme.svg#light_pawn_left); }
.cb-reversed .cb-dk-piece.cb-king { content: url(../svg/traditional-theme.svg#dark_king_left); }
.cb-reversed .cb-dk-piece.cb-queen { content: url(../svg/traditional-theme.svg#dark_queen_left); }
.cb-reversed .cb-dk-piece.cb-bishop { content: url(../svg/traditional-theme.svg#dark_bishop_left); }
.cb-reversed .cb-dk-piece.cb-knight { content: url(../svg/traditional-theme.svg#dark_knight_left); }
.cb-reversed .cb-dk-piece.cb-rook { content: url(../svg/traditional-theme.svg#dark_rook_left); }
.cb-reversed .cb-dk-piece.cb-pawn { content: url(../svg/traditional-theme.svg#dark_pawn_left); }
.cb-reversed .cb-lt-piece.cb-king { content: url(../svg/traditional-theme.svg#light_king_right); }
.cb-reversed .cb-lt-piece.cb-queen { content: url(../svg/traditional-theme.svg#light_queen_right); }
.cb-reversed .cb-lt-piece.cb-bishop { content: url(../svg/traditional-theme.svg#light_bishop_right); }
.cb-reversed .cb-lt-piece.cb-knight { content: url(../svg/traditional-theme.svg#light_knight_right); }
.cb-reversed .cb-lt-piece.cb-rook { content: url(../svg/traditional-theme.svg#light_rook_right); }
.cb-reversed .cb-lt-piece.cb-pawn { content: url(../svg/traditional-theme.svg#light_pawn_right); }
/* vim: set expandtab sw=3 ts=8: */ /* vim: set expandtab sw=3 ts=8: */

25
css/theme/traditional.css Normal file
View File

@ -0,0 +1,25 @@
.cb-theme-traditional .cb-dk-piece.cb-king { content: url(../../svg/traditional-theme.svg#dark_king_right); }
.cb-theme-traditional .cb-dk-piece.cb-queen { content: url(../../svg/traditional-theme.svg#dark_queen_right); }
.cb-theme-traditional .cb-dk-piece.cb-bishop { content: url(../../svg/traditional-theme.svg#dark_bishop_right); }
.cb-theme-traditional .cb-dk-piece.cb-knight { content: url(../../svg/traditional-theme.svg#dark_knight_right); }
.cb-theme-traditional .cb-dk-piece.cb-rook { content: url(../../svg/traditional-theme.svg#dark_rook_right); }
.cb-theme-traditional .cb-dk-piece.cb-pawn { content: url(../../svg/traditional-theme.svg#dark_pawn_right); }
.cb-theme-traditional .cb-lt-piece.cb-king { content: url(../../svg/traditional-theme.svg#light_king_left); }
.cb-theme-traditional .cb-lt-piece.cb-queen { content: url(../../svg/traditional-theme.svg#light_queen_left); }
.cb-theme-traditional .cb-lt-piece.cb-bishop { content: url(../../svg/traditional-theme.svg#light_bishop_left); }
.cb-theme-traditional .cb-lt-piece.cb-knight { content: url(../../svg/traditional-theme.svg#light_knight_left); }
.cb-theme-traditional .cb-lt-piece.cb-rook { content: url(../../svg/traditional-theme.svg#light_rook_left); }
.cb-theme-traditional .cb-lt-piece.cb-pawn { content: url(../../svg/traditional-theme.svg#light_pawn_left); }
.cb-theme-traditional.cb-reversed .cb-dk-piece.cb-king { content: url(../../svg/traditional-theme.svg#dark_king_left); }
.cb-theme-traditional.cb-reversed .cb-dk-piece.cb-queen { content: url(../../svg/traditional-theme.svg#dark_queen_left); }
.cb-theme-traditional.cb-reversed .cb-dk-piece.cb-bishop { content: url(../../svg/traditional-theme.svg#dark_bishop_left); }
.cb-theme-traditional.cb-reversed .cb-dk-piece.cb-knight { content: url(../../svg/traditional-theme.svg#dark_knight_left); }
.cb-theme-traditional.cb-reversed .cb-dk-piece.cb-rook { content: url(../../svg/traditional-theme.svg#dark_rook_left); }
.cb-theme-traditional.cb-reversed .cb-dk-piece.cb-pawn { content: url(../../svg/traditional-theme.svg#dark_pawn_left); }
.cb-theme-traditional.cb-reversed .cb-lt-piece.cb-king { content: url(../../svg/traditional-theme.svg#light_king_right); }
.cb-theme-traditional.cb-reversed .cb-lt-piece.cb-queen { content: url(../../svg/traditional-theme.svg#light_queen_right); }
.cb-theme-traditional.cb-reversed .cb-lt-piece.cb-bishop { content: url(../../svg/traditional-theme.svg#light_bishop_right); }
.cb-theme-traditional.cb-reversed .cb-lt-piece.cb-knight { content: url(../../svg/traditional-theme.svg#light_knight_right); }
.cb-theme-traditional.cb-reversed .cb-lt-piece.cb-rook { content: url(../../svg/traditional-theme.svg#light_rook_right); }
.cb-theme-traditional.cb-reversed .cb-lt-piece.cb-pawn { content: url(../../svg/traditional-theme.svg#light_pawn_right); }

View File

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -14,246 +15,215 @@
</head> </head>
<body> <body>
<div id="page"> <div id="page">
<h1><a href="." class="plain-link">Paco Ŝako</a></h1> <div id="board_ui">
<div id="header">
<div id="content"> <button id="settings" title="Settings" type="button"><span class="fas fa-cog"></span></button>
<div id="cb_outer2"> <button id="cb_choose_game" title="Choose Game" type="button" class="cb-hide-if-private"><span class="fas fa-list"></span></button>
<div id="cb_outer"> <h1>Paco Ŝako<span class="cb-show-if-private" style="display: none"> - Private Mode</span></h1>
<div id="cb_container"> <div class="checkbox-container"><input id="cb_reverse" title="Reverse Board" type="checkbox" autocomplete="off" class="image-checkbox fas fa-sync"></div>
<div id="cb_inner"> <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>
<table id="cb_board" class="noselect"> <button id="help" title="Help" type="button"><span class="fas fa-question-circle"></span></button>
<tr id="cb_row9">
<td id="cb_L9" class="cb-corner"></td>
<td id="cb_a9" class="cb-horiz-label"><div>A</div></td>
<td id="cb_b9" class="cb-horiz-label"><div>B</div></td>
<td id="cb_c9" class="cb-horiz-label"><div>C</div></td>
<td id="cb_d9" class="cb-horiz-label"><div>D</div></td>
<td id="cb_e9" class="cb-horiz-label"><div>E</div></td>
<td id="cb_f9" class="cb-horiz-label"><div>F</div></td>
<td id="cb_g9" class="cb-horiz-label"><div>G</div></td>
<td id="cb_h9" class="cb-horiz-label"><div>H</div></td>
<td id="cb_R9" class="cb-corner"></td>
</tr>
<tr id="cb_row8">
<td id="cb_L8" class="cb-vert-label"><div>8</div></td>
<td id="cb_a8" class="cb-square cb-lt-bg"></td>
<td id="cb_b8" class="cb-square cb-dk-bg"></td>
<td id="cb_c8" class="cb-square cb-lt-bg"></td>
<td id="cb_d8" class="cb-square cb-dk-bg"></td>
<td id="cb_e8" class="cb-square cb-lt-bg"></td>
<td id="cb_f8" class="cb-square cb-dk-bg"></td>
<td id="cb_g8" class="cb-square cb-lt-bg"></td>
<td id="cb_h8" class="cb-square cb-dk-bg"></td>
<td id="cb_R8" class="cb-vert-label"><div>8</div></td>
</tr>
<tr id="cb_row7">
<td id="cb_L7" class="cb-vert-label"><div>7</div></td>
<td id="cb_a7" class="cb-square cb-dk-bg"></td>
<td id="cb_b7" class="cb-square cb-lt-bg"></td>
<td id="cb_c7" class="cb-square cb-dk-bg"></td>
<td id="cb_d7" class="cb-square cb-lt-bg"></td>
<td id="cb_e7" class="cb-square cb-dk-bg"></td>
<td id="cb_f7" class="cb-square cb-lt-bg"></td>
<td id="cb_g7" class="cb-square cb-dk-bg"></td>
<td id="cb_h7" class="cb-square cb-lt-bg"></td>
<td id="cb_R7" class="cb-vert-label"><div>7</div></td>
</tr>
<tr id="cb_row6">
<td id="cb_L6" class="cb-vert-label"><div>6</div></td>
<td id="cb_a6" class="cb-square cb-lt-bg"></td>
<td id="cb_b6" class="cb-square cb-dk-bg"></td>
<td id="cb_c6" class="cb-square cb-lt-bg"></td>
<td id="cb_d6" class="cb-square cb-dk-bg"></td>
<td id="cb_e6" class="cb-square cb-lt-bg"></td>
<td id="cb_f6" class="cb-square cb-dk-bg"></td>
<td id="cb_g6" class="cb-square cb-lt-bg"></td>
<td id="cb_h6" class="cb-square cb-dk-bg"></td>
<td id="cb_R6" class="cb-vert-label"><div>6</div></td>
</tr>
<tr id="cb_row5">
<td id="cb_L5" class="cb-vert-label"><div>5</div></td>
<td id="cb_a5" class="cb-square cb-dk-bg"></td>
<td id="cb_b5" class="cb-square cb-lt-bg"></td>
<td id="cb_c5" class="cb-square cb-dk-bg"></td>
<td id="cb_d5" class="cb-square cb-lt-bg"></td>
<td id="cb_e5" class="cb-square cb-dk-bg"></td>
<td id="cb_f5" class="cb-square cb-lt-bg"></td>
<td id="cb_g5" class="cb-square cb-dk-bg"></td>
<td id="cb_h5" class="cb-square cb-lt-bg"></td>
<td id="cb_R5" class="cb-vert-label"><div>5</div></td>
</tr>
<tr id="cb_row4">
<td id="cb_L4" class="cb-vert-label"><div>4</div></td>
<td id="cb_a4" class="cb-square cb-lt-bg"></td>
<td id="cb_b4" class="cb-square cb-dk-bg"></td>
<td id="cb_c4" class="cb-square cb-lt-bg"></td>
<td id="cb_d4" class="cb-square cb-dk-bg"></td>
<td id="cb_e4" class="cb-square cb-lt-bg"></td>
<td id="cb_f4" class="cb-square cb-dk-bg"></td>
<td id="cb_g4" class="cb-square cb-lt-bg"></td>
<td id="cb_h4" class="cb-square cb-dk-bg"></td>
<td id="cb_R4" class="cb-vert-label"><div>4</div></td>
</tr>
<tr id="cb_row3">
<td id="cb_L3" class="cb-vert-label"><div>3</div></td>
<td id="cb_a3" class="cb-square cb-dk-bg"></td>
<td id="cb_b3" class="cb-square cb-lt-bg"></td>
<td id="cb_c3" class="cb-square cb-dk-bg"></td>
<td id="cb_d3" class="cb-square cb-lt-bg"></td>
<td id="cb_e3" class="cb-square cb-dk-bg"></td>
<td id="cb_f3" class="cb-square cb-lt-bg"></td>
<td id="cb_g3" class="cb-square cb-dk-bg"></td>
<td id="cb_h3" class="cb-square cb-lt-bg"></td>
<td id="cb_R3" class="cb-vert-label"><div>3</div></td>
</tr>
<tr id="cb_row2">
<td id="cb_L2" class="cb-vert-label"><div>2</div></td>
<td id="cb_a2" class="cb-square cb-lt-bg"></td>
<td id="cb_b2" class="cb-square cb-dk-bg"></td>
<td id="cb_c2" class="cb-square cb-lt-bg"></td>
<td id="cb_d2" class="cb-square cb-dk-bg"></td>
<td id="cb_e2" class="cb-square cb-lt-bg"></td>
<td id="cb_f2" class="cb-square cb-dk-bg"></td>
<td id="cb_g2" class="cb-square cb-lt-bg"></td>
<td id="cb_h2" class="cb-square cb-dk-bg"></td>
<td id="cb_R2" class="cb-vert-label"><div>2</div></td>
</tr>
<tr id="cb_row1">
<td id="cb_L1" class="cb-vert-label"><div>1</div></td>
<td id="cb_a1" class="cb-square cb-dk-bg"></td>
<td id="cb_b1" class="cb-square cb-lt-bg"></td>
<td id="cb_c1" class="cb-square cb-dk-bg"></td>
<td id="cb_d1" class="cb-square cb-lt-bg"></td>
<td id="cb_e1" class="cb-square cb-dk-bg"></td>
<td id="cb_f1" class="cb-square cb-lt-bg"></td>
<td id="cb_g1" class="cb-square cb-dk-bg"></td>
<td id="cb_h1" class="cb-square cb-lt-bg"></td>
<td id="cb_R1" class="cb-vert-label"><div>1</div></td>
</tr>
<tr id="cb_row0">
<td id="cb_L0" class="cb-corner"></td>
<td id="cb_a0" class="cb-horiz-label"><div>A</div></td>
<td id="cb_b0" class="cb-horiz-label"><div>B</div></td>
<td id="cb_c0" class="cb-horiz-label"><div>C</div></td>
<td id="cb_d0" class="cb-horiz-label"><div>D</div></td>
<td id="cb_e0" class="cb-horiz-label"><div>E</div></td>
<td id="cb_f0" class="cb-horiz-label"><div>F</div></td>
<td id="cb_g0" class="cb-horiz-label"><div>G</div></td>
<td id="cb_h0" class="cb-horiz-label"><div>H</div></td>
<td id="cb_R0" class="cb-corner"></td>
</tr>
</table>
</div>
</div>
</div>
</div> </div>
<div id="cb_control_container"> <div id="cb_container">
<form id="cb_control_form" autocomplete="off" onsubmit="return false;"> <table id="cb_board" class="noselect cb-theme-pacosako" style="visibility: hidden">
<div id="cb_controls"> <tr id="cb_row9">
<div class="cb-hbox"> <td id="cb_L9" class="cb-corner"></td>
<div class="cb-vbox cb-align-start"> <td id="cb_a9" class="cb-horiz-label"><div>A</div></td>
<div><input id="cb_notify" type="checkbox"><label for="cb_notify">Notify</label></div> <td id="cb_b9" class="cb-horiz-label"><div>B</div></td>
<div><input id="cb_sound" type="checkbox"><label for="cb_sound">Sound</label></div> <td id="cb_c9" class="cb-horiz-label"><div>C</div></td>
<div><input id="cb_reverse" type="checkbox"><label for="cb_reverse">Reverse</label></div> <td id="cb_d9" class="cb-horiz-label"><div>D</div></td>
</div> <td id="cb_e9" class="cb-horiz-label"><div>E</div></td>
<div class="cb-spacer"></div> <td id="cb_f9" class="cb-horiz-label"><div>F</div></td>
<div class="cb-vbox cb-align-stretch"> <td id="cb_g9" class="cb-horiz-label"><div>G</div></td>
<div class="cb-hbox"> <td id="cb_h9" class="cb-horiz-label"><div>H</div></td>
<button id="cb_undo" type="button" disabled="true">Undo</button> <td id="cb_R9" class="cb-corner"></td>
<button id="cb_redo" type="button" disabled="true">Redo</button> </tr>
<button id="cb_resign" type="button" disabled="true">Resign</button> <tr id="cb_row8">
</div> <td id="cb_L8" class="cb-vert-label"><div>8</div></td>
<div id="cb_navigate"> <td id="cb_a8" class="cb-square cb-lt-bg"></td>
<button id="cb_nav_first" title="View First Turn" type="button" disabled="true"> <td id="cb_b8" class="cb-square cb-dk-bg"></td>
<svg viewBox="0 0 144 144" class="media-button-svg silhouette"> <td id="cb_c8" class="cb-square cb-lt-bg"></td>
<path d="M 6 24 H 24 V 120 H 6"/> <td id="cb_d8" class="cb-square cb-dk-bg"></td>
<path d="M 18 72 L 90 24 V 120"/> <td id="cb_e8" class="cb-square cb-lt-bg"></td>
<path d="M 66 72 L 138 24 V 120"/> <td id="cb_f8" class="cb-square cb-dk-bg"></td>
</svg> <td id="cb_g8" class="cb-square cb-lt-bg"></td>
</button> <td id="cb_h8" class="cb-square cb-dk-bg"></td>
<button id="cb_nav_prev_turn" title="View Prior Turn" type="button" disabled="true"> <td id="cb_R8" class="cb-vert-label"><div>8</div></td>
<svg viewBox="0 0 144 144" class="media-button-svg silhouette"> </tr>
<path d="M 12 72 L 84 24 V 120"/> <tr id="cb_row7">
<path d="M 60 72 L 132 24 V 120"/> <td id="cb_L7" class="cb-vert-label"><div>7</div></td>
</svg> <td id="cb_a7" class="cb-square cb-dk-bg"></td>
</button> <td id="cb_b7" class="cb-square cb-lt-bg"></td>
<button id="cb_nav_prev_state" title="View Prior State" type="button" disabled="true"> <td id="cb_c7" class="cb-square cb-dk-bg"></td>
<svg viewBox="0 0 144 144" class="media-button-svg silhouette"> <td id="cb_d7" class="cb-square cb-lt-bg"></td>
<path d="M 36 72 L 108 24 V 120"/> <td id="cb_e7" class="cb-square cb-dk-bg"></td>
</svg> <td id="cb_f7" class="cb-square cb-lt-bg"></td>
</button> <td id="cb_g7" class="cb-square cb-dk-bg"></td>
<button id="cb_nav_next_state" title="View Next State" type="button" disabled="true"> <td id="cb_h7" class="cb-square cb-lt-bg"></td>
<svg viewBox="0 0 144 144" class="media-button-svg silhouette"> <td id="cb_R7" class="cb-vert-label"><div>7</div></td>
<path d="M 108 72 L 36 24 V 120"/> </tr>
</svg> <tr id="cb_row6">
</button> <td id="cb_L6" class="cb-vert-label"><div>6</div></td>
<button id="cb_nav_next_turn" title="View Next Turn" type="button" disabled="true"> <td id="cb_a6" class="cb-square cb-lt-bg"></td>
<svg viewBox="0 0 144 144" class="media-button-svg silhouette"> <td id="cb_b6" class="cb-square cb-dk-bg"></td>
<path d="M 84 72 L 12 24 V 120"/> <td id="cb_c6" class="cb-square cb-lt-bg"></td>
<path d="M 132 72 L 60 24 V 120"/> <td id="cb_d6" class="cb-square cb-dk-bg"></td>
</svg> <td id="cb_e6" class="cb-square cb-lt-bg"></td>
</button> <td id="cb_f6" class="cb-square cb-dk-bg"></td>
<button id="cb_nav_last" title="View Current Move" type="button" disabled="true"> <td id="cb_g6" class="cb-square cb-lt-bg"></td>
<svg viewBox="0 0 144 144" class="media-button-svg silhouette"> <td id="cb_h6" class="cb-square cb-dk-bg"></td>
<path d="M 78 72 L 6 24 V 120"/> <td id="cb_R6" class="cb-vert-label"><div>6</div></td>
<path d="M 126 72 L 54 24 V 120"/> </tr>
<path d="M 138 24 H 120 V 120 H 138"/> <tr id="cb_row5">
</svg> <td id="cb_L5" class="cb-vert-label"><div>5</div></td>
</button> <td id="cb_a5" class="cb-square cb-dk-bg"></td>
</div> <td id="cb_b5" class="cb-square cb-lt-bg"></td>
</div> <td id="cb_c5" class="cb-square cb-dk-bg"></td>
</div> <td id="cb_d5" class="cb-square cb-lt-bg"></td>
</div> <td id="cb_e5" class="cb-square cb-dk-bg"></td>
<div id="cb_theme"> <td id="cb_f5" class="cb-square cb-lt-bg"></td>
<label for="cb_select_theme">Theme:</label> <td id="cb_g5" class="cb-square cb-dk-bg"></td>
<select id="cb_select_theme"> <td id="cb_h5" class="cb-square cb-lt-bg"></td>
<option value="traditional">Traditional</option> <td id="cb_R5" class="cb-vert-label"><div>5</div></td>
<option value="pacosako">Paco Ŝako</option> </tr>
</select> <tr id="cb_row4">
<div class="cb-spacer"></div> <td id="cb_L4" class="cb-vert-label"><div>4</div></td>
<button id="cb_choose_game">Choose Game…</button> <td id="cb_a4" class="cb-square cb-lt-bg"></td>
</div> <td id="cb_b4" class="cb-square cb-dk-bg"></td>
<div id="cb_names"> <td id="cb_c4" class="cb-square cb-lt-bg"></td>
<label for="cb_light_name">Players:</label> <td id="cb_d4" class="cb-square cb-dk-bg"></td>
<input id="cb_light_name" placeholder="Light"> vs. <input id="cb_dark_name" placeholder="Dark"> <td id="cb_e4" class="cb-square cb-lt-bg"></td>
</div> <td id="cb_f4" class="cb-square cb-dk-bg"></td>
</form> <td id="cb_g4" class="cb-square cb-lt-bg"></td>
<div id="cb_message"></div> <td id="cb_h4" class="cb-square cb-dk-bg"></td>
<td id="cb_R4" class="cb-vert-label"><div>4</div></td>
</tr>
<tr id="cb_row3">
<td id="cb_L3" class="cb-vert-label"><div>3</div></td>
<td id="cb_a3" class="cb-square cb-dk-bg"></td>
<td id="cb_b3" class="cb-square cb-lt-bg"></td>
<td id="cb_c3" class="cb-square cb-dk-bg"></td>
<td id="cb_d3" class="cb-square cb-lt-bg"></td>
<td id="cb_e3" class="cb-square cb-dk-bg"></td>
<td id="cb_f3" class="cb-square cb-lt-bg"></td>
<td id="cb_g3" class="cb-square cb-dk-bg"></td>
<td id="cb_h3" class="cb-square cb-lt-bg"></td>
<td id="cb_R3" class="cb-vert-label"><div>3</div></td>
</tr>
<tr id="cb_row2">
<td id="cb_L2" class="cb-vert-label"><div>2</div></td>
<td id="cb_a2" class="cb-square cb-lt-bg"></td>
<td id="cb_b2" class="cb-square cb-dk-bg"></td>
<td id="cb_c2" class="cb-square cb-lt-bg"></td>
<td id="cb_d2" class="cb-square cb-dk-bg"></td>
<td id="cb_e2" class="cb-square cb-lt-bg"></td>
<td id="cb_f2" class="cb-square cb-dk-bg"></td>
<td id="cb_g2" class="cb-square cb-lt-bg"></td>
<td id="cb_h2" class="cb-square cb-dk-bg"></td>
<td id="cb_R2" class="cb-vert-label"><div>2</div></td>
</tr>
<tr id="cb_row1">
<td id="cb_L1" class="cb-vert-label"><div>1</div></td>
<td id="cb_a1" class="cb-square cb-dk-bg"></td>
<td id="cb_b1" class="cb-square cb-lt-bg"></td>
<td id="cb_c1" class="cb-square cb-dk-bg"></td>
<td id="cb_d1" class="cb-square cb-lt-bg"></td>
<td id="cb_e1" class="cb-square cb-dk-bg"></td>
<td id="cb_f1" class="cb-square cb-lt-bg"></td>
<td id="cb_g1" class="cb-square cb-dk-bg"></td>
<td id="cb_h1" class="cb-square cb-lt-bg"></td>
<td id="cb_R1" class="cb-vert-label"><div>1</div></td>
</tr>
<tr id="cb_row0">
<td id="cb_L0" class="cb-corner"></td>
<td id="cb_a0" class="cb-horiz-label"><div>A</div></td>
<td id="cb_b0" class="cb-horiz-label"><div>B</div></td>
<td id="cb_c0" class="cb-horiz-label"><div>C</div></td>
<td id="cb_d0" class="cb-horiz-label"><div>D</div></td>
<td id="cb_e0" class="cb-horiz-label"><div>E</div></td>
<td id="cb_f0" class="cb-horiz-label"><div>F</div></td>
<td id="cb_g0" class="cb-horiz-label"><div>G</div></td>
<td id="cb_h0" class="cb-horiz-label"><div>H</div></td>
<td id="cb_R0" class="cb-corner"></td>
</tr>
</table>
</div>
<div id="cb_status">
<div class="hbox">
<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_scrollable"> <div id="cb_scrollable">
<div id="cb_history"> <div id="cb_history">
<span id="cb_history_past"></span><span id="cb_history_future"></span> <span id="cb_history_past"></span><span id="cb_history_future"></span>
<div id="cb_explain_check"></div>
</div> </div>
<details>
<summary>Rule Reference</summary>
<p>The basic movement for each piece is the same as traditional chess.</p>
<p>Pieces are never removed from the board. Instead, the capturing piece shares the square with the captured piece, and the two pieces become a joined pair.</p>
<p>When either piece moves from a square containing two pieces, the other piece moves with it.
Each player can only move a joined pair according to the rules for their own piece. Joined pieces cannot capture other pieces.</p>
<p>A free (non-joined) piece may move into a square occupied by pieces of both colors.
When it does, the other piece of the same color becomes a free piece and must move to a new location following the normal movement rules.
This process may be repeated multiple times in the same turn.</p>
<p>Ŝako (checkmate) occurs when an opposing piece joins with the king.
<strong>Important:</strong> The king is not permitted to join with (capture) other pieces.</p>
<p>Due to joined movement, it is possible for a pawn to be moved backward to, or past, its starting row.
Pawns may move forward two spaces from either of the first two rows on their own side of the board.</p>
<p>When a pawn is captured <i>en passant</i> while joined with another piece, the captured pawn moves back one square to become joined with the capturing pawn
and the joined piece from the capturing side moves to a new location from its original position as with any other capture.</p>
<p>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.</p>
</details>
</div> </div>
</div> </div>
</div> <div id="cb_names" class="cb-hide-if-private">
<div id="cb_names_text">
<input id="cb_light_name" autocomplete="off" placeholder="Light">
<span class="cb-names-vs">vs.</span>
<input id="cb_dark_name" autocomplete="off" placeholder="Dark">
</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">
<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>
<div class="nav-spacer"></div>
<button id="cb_resign" title="Resign" type="button" disabled="true"><span class="fas fa-flag"></span></button>
<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_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_next_state" title="View Next Move" 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_last" title="View Current Move" type="button" disabled="true"><span class="fas fa-fast-forward"></span></button>
</div>
</div> <!-- board_ui -->
</div>
<div id="rules_content" style="display: none">
<div id="rules" class="rules">
<p>The basic movement for each piece is the same as traditional chess.</p>
<p>Pieces are never removed from the board. Instead, the capturing piece shares the square with the captured piece, and the two pieces become a joined pair.</p>
<p>When either piece moves from a square containing two pieces, the other piece moves with it.
Each player can only move a joined pair according to the rules for their own piece. Joined pieces cannot capture other pieces.</p>
<p>A free (non-joined) piece may move into a square occupied by pieces of both colors.
When it does, the other piece of the same color becomes a free piece and must move to a new location following the normal movement rules.
This process may be repeated multiple times in the same turn.</p>
<p>Ŝako (checkmate) occurs when an opposing piece joins with the king.
<strong>Important:</strong> The king is not permitted to join with (capture) other pieces.</p>
<p>Due to joined movement, it is possible for a pawn to be moved backward to, or past, its starting row.
Pawns may move forward two spaces from either of the first two rows on their own side of the board.</p>
<p>When a pawn is captured <i>en passant</i> while joined with another piece, the captured pawn moves back one square to become joined with the capturing pawn
and the joined piece from the capturing side moves to a new location from its original position as with any other capture.</p>
<p>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.</p>
</div>
<div class="badges"> <div class="badges">
<!-- "external-link-alt" by Font Awesome (fontawesome.com/icons/external-link-alt?style=solid) / CC BY 4.0 (creativecommons.org/licenses/by/4.0) --> <!-- "external-link-alt" by Font Awesome (fontawesome.com/icons/external-link-alt?style=solid) / CC BY 4.0 (creativecommons.org/licenses/by/4.0) -->
<div class="badge game-link-badge"><a id="game_link" href="#" rel="noopener noreferer" target="_blank"><img src="<%=require('./svg/external-link-alt-solid.svg')%>" alt="This Game" title="Link to This Game"></a></div>
<div class="badge official-badge"><a href="https://pacosako.com/" rel="noopener noreferer" target="_blank"><img src="<%=require('./svg/pacosako-logo.svg')%>" alt="Official Site" title="Official Site"></a></div> <div class="badge official-badge"><a href="https://pacosako.com/" rel="noopener noreferer" target="_blank"><img src="<%=require('./svg/pacosako-logo.svg')%>" alt="Official Site" title="Official Site"></a></div>
<div class="badge jitsi-badge"><a id="jitsi_link" href="https://meet.jit.si/PacoSako" rel="noopener noreferer" target="_blank"><img src="<%=require('./svg/jitsi-logo-blue.svg')%>" alt="Jitsi Meet" title="Jitsi Meet"></a></div>
<div class="badge gogs-badge"><a href="https://jessemcdonald.info/gogs/nybble/paco_sako" rel="noopener noreferer" target="_blank"><img src="<%=require('./png/gogs.png')%>" alt="Source Code" title="Source Code"></a></div> <div class="badge gogs-badge"><a href="https://jessemcdonald.info/gogs/nybble/paco_sako" rel="noopener noreferer" target="_blank"><img src="<%=require('./png/gogs.png')%>" alt="Source Code" title="Source Code"></a></div>
</div> </div>
</div> </div>
<div id="settings_content" style="display: none">
<div id="settings">
<div><input id="cb_sound" type="checkbox"><label for="cb_sound">Sound</label></div>
<div id="cb_theme">
<label for="cb_select_theme">Theme:</label>
<select id="cb_select_theme">
<option value="pacosako">Paco Ŝako</option>
<option value="traditional">Traditional</option>
</select>
</div>
</div>
</div>
</body> </body>
</html> </html>
<!-- vim:set noexpandtab sw=2 ts=2: --> <!-- vim:set noexpandtab sw=2 ts=2: -->

View File

@ -1,4 +1,8 @@
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/solid.css';
import './js/pacosako_ui.js'; import './js/pacosako_ui.js';

View File

@ -90,6 +90,9 @@ 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;
@ -108,7 +111,8 @@ export class Iterator {
return z; return z;
} }
const iter = y.value.__proto__[Symbol.iterator].call(y.value); const mapped = f(z.value);
const iter = mapped.__proto__[Symbol.iterator].call(mapped);
innerNext = iter.next.bind(iter); innerNext = iter.next.bind(iter);
} }
}); });
@ -194,6 +198,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,6 +1,7 @@
'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';
@ -271,7 +272,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) {
@ -286,7 +287,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;
@ -315,8 +316,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;
@ -572,7 +573,30 @@ class Game {
return this._version; return this._version;
} }
toJSON() { toJSON(style) {
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,
@ -748,8 +772,6 @@ 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);
@ -833,7 +855,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) {} } catch(err) {/*ignore*/}
} }
return recordCheck(this, check); return recordCheck(this, check);
} }
@ -889,7 +911,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) {} } catch(err) {/*ignore*/}
} }
return recordCheck(this, check); return recordCheck(this, check);
} }

View File

@ -23,7 +23,7 @@ const meta = {
}; };
const stateListeners = {}; const stateListeners = {};
const stateNextId = 1; let 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 ? '' : `/poll/${afterTime}`}`, url: `${API_BASE}/games${(afterTime === undefined) ? '' : `/poll/${afterTime}`}`,
cache: false, cache: false,
timeout: afterTime ? LONG_TIMEOUT : SHORT_TIMEOUT, timeout: (afterTime === undefined) ? SHORT_TIMEOUT : LONG_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/fold-effect'; import 'webpack-jquery-ui/clip-effect';
import 'webpack-jquery-ui/css'; import 'webpack-jquery-ui/css';
import 'jquery-ui-touch-punch'; import 'jquery-ui-touch-punch';
@ -20,8 +20,15 @@ import 'jbox/dist/jBox.all.css';
import {Workbox, messageSW} from 'workbox-window'; import {Workbox, messageSW} from 'workbox-window';
import {ResizeSensor, ElementQueries} from 'css-element-queries';
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 '../wav/191678__porphyr__waterdrop.wav'; import Waterdrop from '../mp3/191678__porphyr__waterdrop.mp3';
$(function (){ $(function (){
function debug() { function debug() {
@ -94,7 +101,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,
@ -102,7 +109,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;
} }
@ -136,7 +143,6 @@ $(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) {
@ -145,7 +151,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') {
@ -174,7 +180,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();
@ -193,11 +199,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');
@ -211,7 +217,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');
@ -220,7 +226,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');
@ -249,6 +255,81 @@ $(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();
@ -259,6 +340,8 @@ $(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;
@ -337,7 +420,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');
} }
@ -384,6 +467,20 @@ $(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) {
@ -406,6 +503,14 @@ $(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(){
@ -460,9 +565,10 @@ $(function (){
head: 0, /* next item to be sent */ head: 0, /* next item to be sent */
tail: 0, /* ID to assign to the next update added to the queue */ tail: 0, /* ID to assign to the next update added to the queue */
sending: false, sending: false,
idle: Promise.resolve(true), /* resolves to true if updates succeeded */
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 };
@ -471,6 +577,9 @@ $(function (){
if (!this.sending) { if (!this.sending) {
openNoticeBox('Saving...'); openNoticeBox('Saving...');
this.sending = true; this.sending = true;
this.idle = new Promise((resolve) => {
this.signal_idle = resolve;
});
this.sendNext(); this.sendNext();
} }
}, },
@ -540,6 +649,7 @@ $(function (){
if (queue.isEmpty()) { if (queue.isEmpty()) {
queue.sending = false; queue.sending = false;
queue.signal_idle(true);
/* close the Saving... notice*/ /* close the Saving... notice*/
noticeBox.close({ ignoreDelay: true }); noticeBox.close({ ignoreDelay: true });
} else { } else {
@ -556,6 +666,7 @@ $(function (){
} }
queue.sending = false; queue.sending = false;
queue.signal_idle(false);
/* force a reset back to the latest server data */ /* force a reset back to the latest server data */
if (update.gameId === $('#cb_board').data('gameId')) { if (update.gameId === $('#cb_board').data('gameId')) {
@ -567,7 +678,6 @@ $(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()) });
} }
@ -576,7 +686,12 @@ $(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;
@ -604,6 +719,16 @@ $(function (){
updateQueue.add(gameId, meta, $('#cb_board').data('modified')); updateQueue.add(gameId, meta, $('#cb_board').data('modified'));
} }
function setNotifyChecked(doNotify) {
const cbNotify = $('#cb_notify');
cbNotify.prop('checked', doNotify);
if (doNotify) {
cbNotify.removeClass('fa-bell-slash').addClass('fa-bell');
} else {
cbNotify.removeClass('fa-bell').addClass('fa-bell-slash');
}
}
function switchGameId(newId, force) { function switchGameId(newId, force) {
const boardElem = $('#cb_board'); const boardElem = $('#cb_board');
const gameId = boardElem.data('gameId'); const gameId = boardElem.data('gameId');
@ -629,36 +754,49 @@ $(function (){
$('#cb_light_name').val(''); $('#cb_light_name').val('');
$('#cb_dark_name').val(''); $('#cb_dark_name').val('');
cancelGameCallback = IO.onGameUpdate(newId, function(data, gameId) { if (newId === 'private') {
if (data.modified > $('#cb_board').data('modified')) { cancelGameCallback = function() {};
try { } else {
const newGame = new PS.Game(JSON.stringify(data.board)); cancelGameCallback = IO.onGameUpdate(newId, function(data/*, gameId*/) {
const newState = JSON.parse(newGame.toJSON()); updateQueue.idle.then(() => {
const oldState = JSON.parse(currentGame.toJSON()); if (data.modified > $('#cb_board').data('modified')) {
try {
const newGame = new PS.Game(JSON.stringify(data.board));
const newState = JSON.parse(newGame.toJSON());
const oldState = JSON.parse(currentGame.toJSON());
if (!deepEqual(newState, oldState)) { if (!deepEqual(newState, oldState)) {
debug('got board', newGame.moves); debug('got board', newGame.moves);
setCurrentGame(newGame, newGame.moves.length > currentGame.moves.length); setCurrentGame(newGame, newGame.moves.length > currentGame.moves.length);
}
} catch (err) {
debug('Error parsing board data', err);
}
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);
updateSelectGameMeta(data, newId);
} }
} catch (err) { });
debug('Error parsing board data', err); });
}
const d = data || {}; const notifyList = $('#cb_notify').data('gameList');
$('#cb_board').data('lightName', shortenName(String(d.lightName || 'Light'))); const doNotify = notifyList.includes('*') || notifyList.includes(newId);
$('#cb_board').data('darkName', shortenName(String(d.darkName || 'Dark'))); setNotifyChecked(doNotify);
$('#cb_light_name').val(String(d.lightName || '')); if (doNotify) {
$('#cb_dark_name').val(String(d.darkName || '')); requestNotify();
$('#cb_board').data('modified', data.modified);
} }
});
const notifyList = $('#cb_notify').data('gameList'); /* Ensure that the selected game is in the list (for new games). */
const doNotify = notifyList.includes('*') || notifyList.includes(newId); if ($('#game_tile_' + newId).length < 1) {
$('#cb_notify').prop('checked', doNotify); updateSelectGameMeta({}, newId);
if (doNotify) { }
requestNotify();
} }
const reverseList = $('#cb_reverse').data('gameList'); const reverseList = $('#cb_reverse').data('gameList');
@ -666,14 +804,6 @@ $(function (){
$('#cb_reverse').prop('checked', doReverse); $('#cb_reverse').prop('checked', doReverse);
arrangeBoard(doReverse); arrangeBoard(doReverse);
$('#jitsi_link').attr('href', 'https://meet.jit.si/PacoSaco_' + newId);
$('#game_link').attr('href', location.href);
/* 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) {
@ -685,7 +815,7 @@ $(function (){
} }
function disableNotify(){ function disableNotify(){
$('#cb_notify')[0].checked = false; setNotifyChecked(false);
$('#cb_notify').attr('disabled', true); $('#cb_notify').attr('disabled', true);
} }
@ -704,7 +834,7 @@ $(function (){
const notifyAudio = new Audio(Waterdrop); const notifyAudio = new Audio(Waterdrop);
function playNotifySound(){ function playNotifySound(){
try { notifyAudio.play(); } catch (err) {} try { notifyAudio.play(); } catch (err) {/*ignore*/}
} }
function notify(body) { function notify(body) {
@ -737,7 +867,7 @@ $(function (){
notification.close(); notification.close();
} }
} }
} catch (err) {} } catch (err) {/*ignore*/}
} }
function arrangeBoard(reversed) { function arrangeBoard(reversed) {
@ -767,33 +897,43 @@ $(function (){
Promise.resolve().then(async () => { Promise.resolve().then(async () => {
const wb = new Workbox('sw.js'); const wb = new Workbox('sw.js');
let latest_sw = null;
function showSkipWaitingPrompt(event) { function showSkipWaitingPrompt(event) {
if (confirmBox) { latest_sw = event.sw;
confirmBox.close({ ignoreDelay: true });
if (!confirmBox) {
confirmBox = new jBox('Confirm', {
attach: null,
content: "A new version is available. Update the page?",
confirmButton: `<span class="update-confirm-button">Update</span>`,
cancelButton: 'Not now',
closeOnConfirm: false,
async confirm() {
/* The SW should signal us to reload, but do it after 20s regardless. */
setTimeout(() => { window.location.reload(); }, 20000);
messageSW(latest_sw, {type: 'SKIP_WAITING'});
$('.update-confirm-button').text('Updating…');
},
onClose() {
if (confirmBox === this) {
confirmBox = null;
}
},
onCloseComplete() {
this.destroy();
},
});
confirmBox.open();
} }
confirmBox = new jBox('Confirm', {
attach: null,
content: "A new version is available. Update the page?",
confirmButton: `<span class="update-confirm-button">Update</span>`,
cancelButton: 'Not now',
closeOnConfirm: false,
confirm() {
/* The SW should signal us to reload, but do it after 20s regardless. */
setTimeout(() => { window.location.reload(); }, 20000);
messageSW(event.sw, {type: 'SKIP_WAITING'});
$('.update-confirm-button').text('Updating…');
},
onCloseComplete() {
this.destroy();
},
});
confirmBox.open();
} }
wb.addEventListener('installed', (event) => { function reloadForUpdate(/*event*/) {
window.location.reload();
}
wb.addEventListener('installed', (/*event*/) => {
try { try {
if (Notification.permission === 'denied') { if (Notification.permission === 'denied') {
disableNotify(); disableNotify();
@ -803,23 +943,19 @@ $(function (){
} }
}); });
wb.addEventListener('controlling', (event) => {
if (event.isUpdate) {
window.location.reload();
}
});
wb.addEventListener('waiting', showSkipWaitingPrompt); wb.addEventListener('waiting', showSkipWaitingPrompt);
wb.addEventListener('externalwaiting', showSkipWaitingPrompt); wb.addEventListener('externalwaiting', showSkipWaitingPrompt);
const registration = await wb.register(); wb.addEventListener('controlling', reloadForUpdate);
wb.addEventListener('externalactivated', reloadForUpdate);
if ('update' in registration) { const registration = await wb.register();
/* Check for updates every 4h without reloading the page. */ await registration.ready;
setInterval(() => { registration.update(); }, 4*3600*1000);
} else { /* Check for updates every 4h without reloading the page. */
console.log('service worker update method not supported, disabling update checks'); setInterval(() => { wb.update(); }, 4*3600*1000);
}
window.Admin.workbox = wb;
}).catch((err) => { }).catch((err) => {
console.error('failed to register the service worker', err); console.error('failed to register the service worker', err);
disableNotify(); disableNotify();
@ -837,8 +973,8 @@ $(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 === 'off') { } else if (value === null || value === 'off') {
gameList = []; gameList = [];
} else { } else {
try { try {
@ -869,6 +1005,7 @@ $(function (){
debug('from localStorage', { key, value }); debug('from localStorage', { key, value });
if (key === LS_KEY_NOTIFY) { if (key === LS_KEY_NOTIFY) {
updatePerGameFlag(key, value, '#cb_notify', (enabled) => { updatePerGameFlag(key, value, '#cb_notify', (enabled) => {
setNotifyChecked(enabled);
if (enabled) { if (enabled) {
requestNotify(); requestNotify();
} }
@ -882,17 +1019,20 @@ $(function (){
arrangeBoard(enabled); arrangeBoard(enabled);
}); });
} else if (key === LS_KEY_THEME) { } else if (key === LS_KEY_THEME) {
if (value === null) {
value = 'pacosako';
}
const cb_theme = $('#cb_select_theme'); const cb_theme = $('#cb_select_theme');
if (value !== cb_theme.val()) { if (value !== cb_theme.val()) {
cb_theme.val(value); cb_theme.val(value);
if (!cb_theme.val()) { if (!cb_theme.val()) {
value = 'traditional'; value = 'pacosako';
cb_theme.val(value); cb_theme.val(value);
} }
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);
@ -900,16 +1040,14 @@ $(function (){
for (const key of [LS_KEY_NOTIFY, LS_KEY_SOUND, LS_KEY_REVERSE, LS_KEY_THEME]) { for (const key of [LS_KEY_NOTIFY, LS_KEY_SOUND, LS_KEY_REVERSE, LS_KEY_THEME]) {
const value = window.localStorage.getItem(key); const value = window.localStorage.getItem(key);
if (value !== null) { fromStorage(key, value);
fromStorage(key, value);
}
} }
} }
$('.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,
@ -940,6 +1078,7 @@ $(function (){
$('#cb_notify').on('change', function(){ $('#cb_notify').on('change', function(){
perGameFlagChanged(LS_KEY_NOTIFY, this); perGameFlagChanged(LS_KEY_NOTIFY, this);
setNotifyChecked(this.checked);
if (this.checked) { if (this.checked) {
requestNotify(); requestNotify();
} }
@ -1056,6 +1195,36 @@ $(function (){
} }
}); });
let helpBox = null;
$('#help').on('click', function() {
helpBox = helpBox || new jBox('Modal', {
title: '<h2>Rule Reference</h2>',
content: $('#rules_content'),
blockScroll: false,
blockScrollAdjust: false,
isolateScroll: false,
footer: `<div id="help_badges"></div>`,
closeButton: 'title',
onCreated() {
$('.badges').appendTo('#help_badges');
},
});
helpBox.open();
});
let settingBox = null;
$('#settings').on('click', function() {
settingBox = settingBox || new jBox('Modal', {
title: '<h2>Settings</h2>',
content: $('#settings_content'),
blockScroll: false,
blockScrollAdjust: false,
isolateScroll: false,
closeButton: 'title',
});
settingBox.open();
});
$('#cb_light_name, #cb_dark_name').on('input', function() { putMeta(); }); $('#cb_light_name, #cb_dark_name').on('input', function() { putMeta(); });
let gameSelectContent = $(`<div style="display: none"></div>`).appendTo('body'); let gameSelectContent = $(`<div style="display: none"></div>`).appendTo('body');
@ -1069,6 +1238,7 @@ $(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,
@ -1138,11 +1308,6 @@ $(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;
@ -1152,10 +1317,11 @@ $(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) {
oldTile.removeAttr('id');
if (oldTile.length >= 1) { if (oldTile.length >= 1) {
oldTile.removeAttr('id');
oldTile.hide({ oldTile.hide({
effect: "fold", effect: "clip",
direction: "horizontal",
complete() { complete() {
oldTile.remove(); oldTile.remove();
}, },
@ -1164,6 +1330,13 @@ $(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();
@ -1213,13 +1386,14 @@ $(function (){
$(list).appendTo(gameTiles); $(list).appendTo(gameTiles);
updateTileAges(); updateTileAges();
tile.show("fold"); tile.show({
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) {
@ -1276,28 +1450,104 @@ $(function (){
function notifyLocalMove(game, gameId) { function notifyLocalMove(game, gameId) {
if (!game) { if (!game) {
delete lastNotifyState[gameId]; delete lastNotifyState[gameId];
/* Preload the last-known state if we already have data for this game */
const localMeta = IO.getCachedMeta();
if (gameId in localMeta) {
notifyForGame(localMeta[gameId], gameId);
}
} else { } else {
lastNotifyState[gameId] = lastNotifyState[gameId] || {}; lastNotifyState[gameId] = lastNotifyState[gameId] || {};
lastNotifyState[gameId].moves = game.moves; lastNotifyState[gameId].moves = game.moves;
} }
} }
IO.onMetaUpdate(notifyForGame); window.onhashchange = function(/*event*/){
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 foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/); const foundPrivate = location.hash.match(/^#\/private((\/(.*)?)?)$/);
if (foundId) { if (foundPrivate) {
switchGameId(foundId[1]); 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 { } else {
switchGameId(randomId()); const foundId = location.hash.match(/^#\/([0-9a-f]{16}\b)/);
if (foundId) {
switchGameId(foundId[1]);
} else {
switchGameId(randomId());
}
IO.onMetaUpdate(async (meta, gameId) => {
await updateQueue.idle;
updateSelectGameMeta(meta, gameId);
notifyForGame(meta, gameId);
});
} }
function adjustBoardSize() {
let container = $('#cb_container');
let outerWidth = container.width();
let outerHeight = container.height();
let size = ((outerWidth < outerHeight) ? outerWidth : outerHeight) - 24;
$('#cb_board').css({
width: size + 'px',
height: size + 'px',
top: ((outerHeight - size) / 2) + 'px',
left: ((outerWidth - size) / 2) + 'px',
visibility: '',
});
}
new ResizeSensor($('#cb_container'), adjustBoardSize);
adjustBoardSize();
(function() {
const body = $('body').first();
let horizontal = false;
function updateLayout() {
const oldHorizontal = horizontal;
horizontal = body.width() >= (1.5 * body.height());
if (horizontal !== oldHorizontal) {
if (horizontal) {
$('#cb_container').prependTo('#page');
$('#header').appendTo('#board_ui');
$('#cb_status').appendTo('#board_ui');
$('#cb_names').appendTo('#board_ui');
$('#cb_times').appendTo('#board_ui');
$('#cb_navigate').appendTo('#board_ui');
$('#board_ui').appendTo('#page');
$('#page').removeClass('vertical-layout').addClass('horizontal-layout');
} else {
$('#header').appendTo('#board_ui');
$('#cb_container').appendTo('#board_ui');
$('#cb_status').appendTo('#board_ui');
$('#cb_names').appendTo('#board_ui');
$('#cb_times').appendTo('#board_ui');
$('#cb_navigate').appendTo('#board_ui');
$('#board_ui').appendTo('#page');
$('#page').removeClass('horizontal-layout').addClass('vertical-layout');
}
}
}
new ResizeSensor(body, updateLayout);
updateLayout();
})();
/* Low-level commands to be run from the JS console */ /* Low-level commands to be run from the JS console */
window.Admin = { window.Admin = {
getGameId() { return $('#cb_board').data('gameId'); }, getGameId() { return $('#cb_board').data('gameId'); },
@ -1314,25 +1564,6 @@ $(function (){
$, $,
}; };
/*
* Safari uses the container's *height* instead of its *width* as
* required by W3C standards for relative margin/padding values.
* This breaks the CSS that should be ensuring a 1:1 aspect ratio.
*/
function fixSafariPadding() {
let squares = $('#cb_board .cb-square');
for (let square of squares) {
square = $(square);
const width = square.width();
square.css('height', width + 'px');
}
}
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
$(window).on('resize', fixSafariPadding);
fixSafariPadding();
}
(function() { (function() {
const match = location.href.match('^https://jessemcdonald.info/~nybble/paco_sako/(.*)'); const match = location.href.match('^https://jessemcdonald.info/~nybble/paco_sako/(.*)');
if (match) { if (match) {

Binary file not shown.

23127
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.6.0", "version": "0.8.7",
"description": "Online version of the Paco Ŝako chess variation", "description": "Online version of the Paco Ŝako chess variation",
"keywords": [ "keywords": [
"game", "game",
@ -31,28 +31,35 @@
"url": "https://jessemcdonald.info/gogs/nybble/paco_sako.git" "url": "https://jessemcdonald.info/gogs/nybble/paco_sako.git"
}, },
"devDependencies": { "devDependencies": {
"clean-webpack-plugin": "^3.0.0", "@fortawesome/fontawesome-free": "^5.13.0",
"copy-webpack-plugin": "^5.1.1", "buffer": "^6.0.3",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^6.4.1",
"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.3.0", "html-webpack-plugin": "^4.5.2",
"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": "^0.9.0", "mini-css-extract-plugin": "^1.6.2",
"optimize-css-assets-webpack-plugin": "^5.0.3", "optimize-css-assets-webpack-plugin": "^6.0.1",
"svgo": "^1.3.2", "pako": "^2.0.4",
"svgo-loader": "^2.2.1", "sprintf-js": "^1.0.3",
"webpack": "^4.43.0", "svgo": "^2.7.0",
"webpack-cli": "^3.3.11", "svgo-loader": "^3.0.0",
"webpack": "^4.46.0",
"webpack-cli": "^4.8.0",
"webpack-jquery-ui": "^2.0.1", "webpack-jquery-ui": "^2.0.1",
"workbox-precaching": "^5.1.3", "workbox-precaching": "^6.3.0",
"workbox-routing": "^5.1.3", "workbox-routing": "^6.3.0",
"workbox-strategies": "^5.1.3", "workbox-strategies": "^6.3.0",
"workbox-webpack-plugin": "^5.1.3", "workbox-webpack-plugin": "^6.3.0",
"workbox-window": "^5.1.3" "workbox-window": "^6.3.0"
},
"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

@ -1 +0,0 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="external-link-alt" class="svg-inline--fa fa-external-link-alt fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M432,320H400a16,16,0,0,0-16,16V448H64V128H208a16,16,0,0,0,16-16V80a16,16,0,0,0-16-16H48A48,48,0,0,0,0,112V464a48,48,0,0,0,48,48H400a48,48,0,0,0,48-48V336A16,16,0,0,0,432,320ZM488,0h-128c-21.37,0-32.05,25.91-17,41l35.73,35.73L135,320.37a24,24,0,0,0,0,34L157.67,377a24,24,0,0,0,34,0L435.28,133.32,471,169c15,15,41,4.5,41-17V24A24,24,0,0,0,488,0Z"></path></svg>

Before

Width:  |  Height:  |  Size: 597 B

View File

@ -1,160 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="160"
height="160"
viewBox="0 0 150 150.00001"
id="svg3526"
version="1.1"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:export-filename="/Users/ystamcheva/Dropbox/Designs/appLogo.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
sodipodi:docname="jitsi-logo-blue.svg">
<defs
id="defs3528">
<filter
style="color-interpolation-filters:sRGB;"
inkscape:label="Blur"
id="filter921">
<feGaussianBlur
stdDeviation="4 4"
result="blur"
id="feGaussianBlur919" />
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="89.185304"
inkscape:cy="80.953412"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="999"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
units="px"
inkscape:snap-bbox="true"
inkscape:snap-page="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:pagecheckerboard="true" />
<metadata
id="metadata3531">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-902.36211)"
style="opacity:1">
<path
style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.91489375;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal;filter:url(#filter921)"
d="M 142.5,977.36212 A 67.499998,67.499998 0 0 1 74.999998,1044.8621 67.499998,67.499998 0 0 1 7.5,977.36212 a 67.499998,67.499998 0 0 1 67.499998,-67.5 67.499998,67.499998 0 0 1 67.500002,67.5 z"
id="path1006-7"
inkscape:connector-curvature="0"
transform="matrix(0.97916666,0,0,0.97916666,1.562503,20.361715)" />
<path
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.87500012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
d="M 141.09375,977.36212 A 66.09375,66.09375 0 0 1 75.000003,1043.4558 66.09375,66.09375 0 0 1 8.9062529,977.36212 66.09375,66.09375 0 0 1 75.000003,911.26837 66.09375,66.09375 0 0 1 141.09375,977.36212 Z"
id="path1006"
inkscape:connector-curvature="0" />
<g
id="g1247"
transform="matrix(0.93034217,0,0,0.93034217,4.9699418,67.566622)"
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333">
<path
inkscape:connector-curvature="0"
id="path3618"
d=""
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path35"
d="m 66.150479,984.58608 c 3.546945,0 11.036812,-2.11769 15.990996,-3.51965 0.720489,-0.20282 1.392044,-0.39263 2.002303,-0.56287 0.09877,-0.0272 0.194448,-0.0519 0.284976,-0.0746 -0.27828,-0.27544 -0.741093,-0.6937 -1.495321,-1.32572 -3.300213,-2.23667 -6.416409,-3.27807 -9.805606,-3.27807 -0.73929,0 -1.507554,0.0453 -2.344712,0.14011 -2.869465,0.32644 -5.26015,1.01023 -6.408683,1.33796 -0.582186,0.16689 -0.757576,0.21672 -0.939018,0.21672 -0.346144,0 -0.643483,-0.20591 -0.759765,-0.52398 -0.116412,-0.31974 -0.01674,-0.67258 0.253555,-0.89897 l 0.01635,-0.0131 c 1.980412,-1.65899 5.670166,-4.74737 12.67005,-6.12731 1.214078,-0.23862 2.512891,-0.36006 3.859091,-0.36006 3.133581,0 6.378164,0.64722 9.641162,1.92195 4.444111,1.7351 7.480982,3.9625 9.923563,5.75233 1.31491,0.96438 2.45057,1.79755 3.43891,2.2556 0.25252,0.11744 0.38361,0.15053 0.44465,0.15916 0.0449,-0.0582 0.14243,-0.21891 0.28575,-0.64052 0.0384,-0.11165 0.31588,-1.29018 -1.67496,-7.99311 -1.15703,-3.89116 -2.552556,-7.86512 -3.279226,-9.62945 -1.405694,-3.41366 -1.060709,-5.83293 -0.62803,-7.2479 -1.190899,0.4377 -2.408198,1.11879 -3.688853,2.05948 l -0.07984,0.059 c -0.639877,0.4744 -1.29173,1.01126 -1.993675,1.63864 -4.13029,3.69813 -7.224723,5.62613 -9.201529,5.73095 l -0.05447,0.003 -0.05421,-0.005 c -1.741536,-0.14783 -4.380754,-2.68287 -5.460393,-3.96636 -0.224453,-0.15054 -0.480197,-0.21879 -0.817713,-0.21879 -0.338933,0 -0.718944,0.0712 -1.086593,0.14101 -0.267463,0.0507 -0.543168,0.10237 -0.816555,0.13006 -0.871412,0.0874 -2.092831,0.36919 -3.271626,0.64052 -0.568278,0.13058 -1.154714,0.26566 -1.690799,0.37628 -0.01017,0.002 -0.240292,0.0492 -0.651595,0.15762 -2.913377,0.86188 -5.108841,1.85073 -6.182685,2.78512 -0.01172,0.0102 -0.04636,0.0371 -0.101603,0.0777 -0.726284,0.51625 -2.472327,1.98916 -2.99914,4.4951 l -0.0055,0.0209 c -1.099856,4.61487 0.567635,10.33796 2.783445,13.60701 1.042166,1.53653 2.165202,2.49512 3.083359,2.62956 0.228573,0.0324 0.502861,0.0497 0.813978,0.0497 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path39"
d="m 82.429799,960.11108 c 0.0707,0.0139 0.15337,0.0209 0.24467,0.0209 1.90392,0 6.155901,-2.75679 6.942451,-4.01838 l 0.06258,-0.10045 0.08937,-0.0787 c 0.268236,-0.26476 1.042938,-1.65925 1.418958,-2.49396 -4.032164,-1.2097 -4.477077,-3.3928 -4.870352,-5.33252 -0.04572,-0.22523 -0.08589,-0.42804 -0.12182,-0.60936 -0.04597,-0.2372 -0.09413,-0.47813 -0.13547,-0.66434 -6.00678,1.99226 -7.842965,3.1104 -8.845083,3.94189 -0.518572,0.43075 -0.823894,1.65577 0.671555,5.04652 0.763886,1.7284 3.397825,4.06809 4.543138,4.28842 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path43"
d="m 51.013383,1000.8062 c -0.746759,-0.88008 -1.063414,-1.98921 -0.939147,-3.29974 0.528616,-6.36104 -0.19638,-7.8977 -0.532093,-8.26238 -0.081,-0.0882 -0.124782,-0.0882 -0.159422,-0.0882 -0.01301,0 -0.02511,7.7e-4 -0.03812,0.002 -0.623265,0.14899 -4.821418,2.98729 -5.748203,11.46142 -0.446973,4.0896 0.336228,9.3415 1.003663,12.5359 0.536342,-1.886 1.383287,-3.9635 3.023994,-6.1018 1.527514,-1.9888 3.193718,-3.5571 5.260665,-4.9318 -0.642581,-0.2672 -1.332293,-0.6791 -1.87134,-1.3155 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path47"
d="m 95.474451,928.32276 c -0.501702,3.30962 -1.318514,6.4535 -2.842938,9.77174 1.661697,-1.88125 2.63278,-3.92515 2.958062,-6.21925 0.135341,-0.95035 0.263342,-1.85937 -0.115124,-3.55249 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path51"
d="m 86.212271,943.73132 c 2.97918,-2.69331 4.792959,-7.14115 5.547315,-9.78307 -2.594662,2.49911 -5.747945,3.84003 -7.771624,4.70178 -0.696924,0.29721 -1.29804,0.55309 -1.658349,0.76312 -0.151695,0.0882 -0.304807,0.17693 -0.459851,0.26643 -1.2276,0.71019 -3.080783,1.78352 -3.35095,2.44503 l -0.0077,0.0215 -0.01004,0.0213 c -0.840764,1.76678 -1.314522,4.42364 -1.466347,5.94729 1.948991,-1.19013 3.958248,-1.98441 5.627284,-2.6436 1.549149,-0.61206 2.888653,-1.14132 3.550293,-1.73973 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path55"
d="m 89.217464,946.05955 c -0.01816,0.046 -0.0376,0.0895 -0.05859,0.13096 -0.49359,1.43492 0.186206,3.38675 1.445743,4.12295 0.335069,0.19599 0.660866,0.31575 0.9734,0.35696 0.04301,0.002 0.08576,0.003 0.128903,0.003 0.978809,0 1.777206,-0.71599 2.369437,-2.12722 0.372929,-1.07397 0.425983,-2.34754 0.14487,-3.49724 -0.2184,-0.89317 -0.783459,-1.95929 -1.143511,-2.49087 -0.67207,0.46127 -2.000242,1.47601 -2.912218,2.28136 -0.636915,0.61515 -0.867034,1.0025 -0.948032,1.21962 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path59"
d="m 97.64004,982.53162 c 1.082085,-0.29489 1.941393,-0.53634 2.57522,-0.72409 -1.305382,-0.90928 -2.816414,-2.04055 -4.870613,-3.63825 -5.034538,-3.91524 -10.43312,-5.90119 -16.043535,-5.90119 -3.07267,0 -5.749361,0.59133 -7.867818,1.30719 0.528101,-0.0328 1.065087,-0.0485 1.616625,-0.0485 0.655201,0 1.341823,0.0231 2.040034,0.0697 2.182586,0.14628 7.80575,1.139 12.891025,7.20244 1.024524,1.21936 2.125668,2.22869 5.257575,2.22869 1.117627,0 2.420431,-0.12453 3.980783,-0.38104 0.09645,-0.0269 0.242352,-0.0667 0.420704,-0.11487 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path63"
d="m 104.77707,956.11317 c -0.99864,-1.55082 -2.25329,-2.51418 -3.27214,-2.51418 -0.18853,0 -0.36855,0.0332 -0.53493,0.0985 -0.54587,0.21531 -1.41033,2.70966 -1.027486,4.52331 0.260506,1.23391 1.760336,5.40335 2.480436,7.40707 0.27584,0.76723 0.4095,1.14209 0.45419,1.28142 0.0354,0.10985 0.0863,0.26747 0.15066,0.46629 0.58168,1.77257 2.07468,6.32859 2.51908,10.0177 0.92395,-3.93455 1.44728,-8.6742 1.24228,-14.34655 -0.0931,-2.60111 -0.80793,-5.06378 -2.01209,-6.93357 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path67"
d="m 86.080664,982.55557 c -0.0197,-0.0258 -0.03863,-0.0511 -0.0573,-0.0753 -0.07907,0.0129 -0.208099,0.0411 -0.410145,0.0999 l -0.0846,0.0254 c -1.031607,0.37988 -2.924066,1.01834 -5.111545,1.75647 -2.625311,0.88468 -5.598697,1.88769 -8.088022,2.78087 -3.167319,1.18021 -4.194419,1.64586 -4.516482,1.80901 -0.05846,0.15685 -0.124009,0.35696 -0.197024,0.57768 -0.749077,2.27994 -2.4646,7.4985 -10.924006,11.1469 1.747203,1.7699 5.550278,4.9468 13.037055,4.9468 0.648376,0 1.320961,-0.024 1.99522,-0.073 5.652653,-0.4019 10.966115,-2.903 14.964283,-7.04037 3.635669,-3.76149 5.699397,-8.45284 5.731333,-12.97525 -0.884804,-0.0735 -2.384761,-0.29373 -3.7732,-0.95292 -0.0161,-0.008 -1.602847,-0.7729 -2.565559,-2.02626 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path71"
d="m 53.890317,990.77391 c -0.01571,1.33075 -0.191744,2.60703 -0.363142,3.85124 -0.0922,0.67168 -0.188525,1.36693 -0.25935,2.06566 -0.09607,1.40544 0.207583,1.89516 0.342409,2.03836 0.07353,0.081 0.194835,0.17333 0.455344,0.17333 0.20063,0 0.440149,-0.0556 0.693189,-0.1611 2.51199,-1.05285 4.897523,-2.88028 6.044254,-3.8945 -1.76008,-0.78488 -4.467033,-2.48018 -6.912704,-4.07299 z" />
<path
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333"
inkscape:connector-curvature="0"
id="path75"
d="m 95.375553,985.509 c -0.07108,0.93464 -0.22368,2.69768 -0.467577,4.04066 -0.295536,1.62628 -0.784876,3.00172 -1.069466,3.72015 0.508012,0.46976 1.014479,0.93077 1.342981,1.2218 1.321863,-0.8651 5.661019,-4.15901 8.654629,-11.63703 -0.95898,0.64774 -2.30583,1.21769 -4.051869,1.71682 -1.776691,0.50595 -3.503161,0.80213 -4.408698,0.9376 z" />
<path
d="m 113.82909,953.6111 c -1.81082,-3.78531 -4.33568,-5.69644 -6.07954,-6.80467 -1.46622,-0.93142 -3.15225,-1.60825 -5.02372,-2.01878 -0.20887,-1.30809 -0.56622,-2.64527 -1.05569,-3.9464 3.15586,-4.70424 3.91563,-9.99349 2.19019,-15.35151 -1.32521,-4.11947 -2.35412,-6.77569 -3.23905,-8.36102 -1.911774,-3.42899 -4.56065,-4.14858 -6.446413,-4.14858 -1.943196,0 -3.803719,0.8257 -5.108069,2.26771 -1.335383,1.47601 -1.959035,3.40761 -1.755701,5.43786 0.173716,1.73149 -0.280469,4.87692 -0.581928,6.00008 -0.0052,0.0151 -0.541108,1.51644 -4.528716,3.44908 -0.09606,0.0345 -0.3003,0.10443 -0.614637,0.20977 -2.293718,0.76814 -6.556258,2.19714 -9.395719,5.9518 -2.169451,2.62788 -2.702317,5.23194 -3.317984,8.24615 -0.368036,1.79704 -0.752812,4.14484 -0.65739,6.92095 -7.127627,1.58275 -12.26261,4.80815 -15.273726,9.59879 -3.116711,4.96037 -3.657432,11.10957 -1.605551,18.28497 l 0.01803,0.0533 c 0.0376,0.13058 0.07726,0.26064 0.118601,0.39019 -0.350007,-0.06 -0.703749,-0.11049 -1.066891,-0.15054 -0.317942,-0.0366 -0.6458,-0.0541 -0.970954,-0.0541 -3.558791,0 -7.345641,2.23693 -10.130116,5.98463 -2.115366,2.84564 -4.655943,7.87207 -4.74557,15.65802 -0.06735,5.7826 0.551023,11.1139 1.839276,15.8461 0.96078,3.526 2.032564,5.6921 2.443997,6.4503 l 8.729958,16.3644 3.999713,-18.1224 c 1.365002,-6.1728 2.712748,-8.7931 4.451837,-10.525 4.046715,2.4388 8.826282,3.7217 13.899968,3.7217 5.655614,0 11.552937,-1.6311 16.603314,-4.5923 3.280769,-1.9257 6.07722,-4.2913 8.322519,-7.0385 0.03039,0 0.06039,0 0.09066,0 h 0.239133 c 1.388438,-0.047 3.286563,-0.7336 5.206829,-1.884 1.5995,-0.9571 4.01092,-2.71815 6.43174,-5.67428 2.43963,-2.98176 4.43007,-6.60919 5.91407,-10.78236 1.73845,-4.88554 2.79658,-10.57425 3.1426,-16.90438 0.41889,-5.88793 -0.2676,-10.75686 -2.04507,-14.47211 z m -4.00796,14.09132 c -0.31098,5.76971 -1.2549,10.90199 -2.80263,15.25209 -1.25078,3.51578 -2.89766,6.53385 -4.89404,8.97425 -3.437747,4.19944 -6.797068,5.32543 -7.153514,5.3396 h -0.02833 c -0.32554,0 -0.51664,-0.1329 -1.428745,-0.7711 -0.254843,-0.17758 -0.639877,-0.44646 -0.957562,-0.65352 -1.907525,3.7101 -5.040848,6.91526 -9.099024,9.29556 -4.13338,2.424 -8.941019,3.7604 -13.534121,3.7604 -5.453826,0 -10.241634,-1.8001 -13.887219,-5.2144 -4.973371,3.1622 -7.962467,5.8005 -10.38586,16.7757 l -0.458692,2.0806 -1.002762,-1.8788 c -0.25304,-0.4625 -1.121233,-2.1754 -1.938946,-5.1807 -1.14042,-4.19 -1.688224,-8.9624 -1.627314,-14.1831 0.121563,-10.41898 5.714078,-15.64835 8.811602,-15.64835 0.101988,0 0.203205,0.005 0.299785,0.0165 2.936041,0.3285 4.854641,1.59705 8.337456,3.90069 0.360052,0.23862 0.739162,0.48934 1.138747,0.75178 1.247817,0.819 3.011631,1.81893 3.455643,1.90469 0.845014,-0.0491 1.672256,-1.94178 1.912677,-3.69748 0.01468,-0.10675 0.0291,-0.20578 0.04301,-0.29811 0.01893,-0.20758 0.02923,-0.39019 0.03477,-0.54008 -0.147317,-0.0514 -0.300429,-0.1119 -0.45676,-0.18067 l -0.03477,-0.0117 c -2.190699,-0.75422 -5.3915,-4.45879 -7.002459,-9.83638 -1.555716,-5.43554 -1.247947,-9.93129 0.914164,-13.37058 3.505351,-5.57951 11.114077,-6.89132 13.630573,-7.3222 0.786679,-0.15633 1.609414,-0.30275 2.446573,-0.435 0.390828,-0.0981 0.658033,-0.18015 0.830462,-0.25471 0.07469,-0.0519 0.107526,-0.10727 0.05653,-0.30945 l 0.06825,-0.0191 -0.06606,0.0148 c -0.03052,-0.13714 -0.07276,-0.29759 -0.121176,-0.48328 -0.06709,-0.25717 -0.142037,-0.54807 -0.219559,-0.89318 -0.779081,-3.49505 -0.381428,-6.42375 0.111132,-8.8286 0.553341,-2.70669 0.859951,-4.20523 2.138288,-5.70197 1.693632,-2.33119 4.73926,-3.35134 6.561409,-3.96237 0.508399,-0.17024 0.947646,-0.31704 1.201588,-0.43938 5.581569,-2.67707 7.358776,-5.49168 7.866917,-7.38105 0.402675,-1.49918 1.041007,-5.44108 0.763757,-8.19387 -0.03078,-0.31228 0.04198,-0.56931 0.218271,-0.76428 0.156461,-0.17268 0.378853,-0.27094 0.611418,-0.27094 0.52295,0 1.23919,0 3.91176,8.30308 1.409944,4.37779 0.358635,8.48207 -3.12199,12.20002 -0.04468,0.0483 -0.09014,0.0956 -0.133925,0.14101 -0.09504,0.0979 -0.237974,0.24583 -0.257419,0.29953 7.73e-4,0 -0.0062,0.13418 0.225998,0.51909 1.47124,2.42918 2.270668,5.45009 2.038746,7.6959 -0.05524,0.52308 -0.155301,1.02646 -0.241966,1.46944 -0.05151,0.25343 -0.113192,0.56364 -0.144999,0.80368 0.01429,0 0.0291,0 0.04468,0 0.397654,-0.0421 1.013064,-0.0901 1.754929,-0.0901 1.745657,0 4.245547,0.26747 6.245917,1.53898 1.20816,0.76646 2.73709,1.94925 3.86077,4.30156 1.32392,2.76503 1.81519,6.62579 1.46402,11.47554 z"
id="path87"
inkscape:connector-curvature="0"
style="fill:#0096d5;fill-opacity:1;stroke-width:1.07487333" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

4
sw.js
View File

@ -14,3 +14,7 @@ addEventListener('message', (event) => {
skipWaiting(); skipWaiting();
} }
}); });
self.addEventListener('activate', (event) => {
event.waitUntil(clients.claim());
});

View File

@ -23,7 +23,7 @@ module.exports = {
loaders: [MiniCssExtractPlugin.loader, "css-loader"] loaders: [MiniCssExtractPlugin.loader, "css-loader"]
}, },
{ {
test: { and: [ /\.(jpe?g|png|gif|svg)$/i, /jquery-ui/i ] }, test: { and: [ /\.(jpe?g|png|gif|svg|ttf|eot|woff2?)$/i, /jquery-ui/i ] },
loader: "file-loader", loader: "file-loader",
options: { options: {
name: '[name].[contenthash].[ext]', name: '[name].[contenthash].[ext]',
@ -31,7 +31,7 @@ module.exports = {
} }
}, },
{ {
test: { and: [ /\.(jpe?g|png|gif|mp3|wav)$/i, { not: [ /jquery-ui/i ] } ] }, test: { and: [ /\.(jpe?g|png|gif|mp3|wav|ttf|eot|woff2?)$/i, { not: [ /jquery-ui/i ] } ] },
loader: "file-loader", loader: "file-loader",
options: { options: {
name: '[path][name].[contenthash].[ext]', name: '[path][name].[contenthash].[ext]',
@ -52,7 +52,14 @@ module.exports = {
loader: "svgo-loader", loader: "svgo-loader",
options: { options: {
plugins: [ plugins: [
{ cleanupIDs: false } {
name: "preset-default",
params: {
overrides: {
cleanupIDs: false,
}
}
},
] ]
} }
} }
@ -88,12 +95,14 @@ module.exports = {
"window.jQuery": "jquery'", "window.jQuery": "jquery'",
"window.$": "jquery" "window.$": "jquery"
}), }),
new CopyPlugin([ new CopyPlugin({
{ from: '.htaccess' }, patterns: [
]), { from: '.htaccess' },
],
}),
new InjectManifest({ new InjectManifest({
swSrc: './sw.js', swSrc: './sw.js',
dontCacheBustURLsMatching: /\.[0-9a-f]{16,}\.(css|js|svg|png|jpg|wav)$/, dontCacheBustURLsMatching: /\.[0-9a-f]{16,}\.\w{2,4}$/,
exclude: [ /(^|[/\\])\.htaccess$/ ], exclude: [ /(^|[/\\])\.htaccess$/ ],
}), }),
], ],