Merge branch 'develop' into timer

This commit is contained in:
Laurent Mazet 2020-09-28 22:28:45 +02:00
commit 8ff7a80b33
3 changed files with 261 additions and 108 deletions

View File

@ -135,9 +135,11 @@ We will assume www-data is the UNIX user of your Web server. Adapt if necessary.
exec sudo -u www-data DEBUG_LOG=true nodejs trivabble-server.js
```
8. Configure your web server.
8. Configure your web server. In the next two sections, we describe the configuration for Apache 2 and Nginx.
On Apache 2, the relevant rules are:
#### Apache 2
On Apache 2, the relevant rules are:
RewriteEngine on
RewriteRule "^/?:trivabble/ws/(.*)" ws://localhost:3000/ws/$1 [P]
@ -153,16 +155,47 @@ a2enmod proxy_wstunnel
a2enmod rewrite
```
A full example is available in (doc/trivabble-apache-host.conf.sample)[doc/trivabble-apache-host.conf.sample].
:warning: Be careful to allow enough concurrent processes / threads / workers on your web server.
Each player requires one long-standing connection (XHR, SSE or WebSocket), plus small requests when not using WebSockets.
Long-standing connections may cause issues with Apache 2, and using HTTP 2 may lead to "interesting" behavior.
### Nginx
Here is an example of an Nginx configuration for Trivabble:
```
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name trivabble.example.org;
location /:trivabble/ws/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location /:trivabble {
proxy_pass http://127.0.0.1:3000;
proxy_buffering off;
}
location /trivabble {
alias /var/www/html/trivabble-public;
}
# SSL configuration here
}
```
## Enable the spell checker
This is an experimental feature. The process of building the dictionaries takes a lot of disk space.
Install `aspell` (to have the `word-list-compress` command) and run:
Install `wget` and `aspell` (to have the `word-list-compress` command), and run:
```sh
cd dict-dists-extractor

View File

@ -51,8 +51,8 @@
<span class="modal-button" id="btn-settings-close">&#215;</span>
</h2>
<div id="prefs">
<p><label><input type="checkbox" id="tiles-sound" checked="checked" /><span data-l10n="text-content">Sound of the tiles</span></label></p>
<p><label><input type="checkbox" id="msg-sound" checked="checked" /><span data-l10n="text-content">Sound of messages</span></label></p>
<p><label><input type="checkbox" id="tiles-sound" /><span data-l10n="text-content">Sound of the tiles</span></label></p>
<p><label><input type="checkbox" id="msg-sound" /><span data-l10n="text-content">Sound of messages</span></label></p>
</div>
<div id="prefs">
<p id="enable-spell-checker-outer" style="display:none"><label><input type="checkbox" id="enable-spell-checker" /> <span data-l10n="text-content">Enable spell checker</span></label></p>

View File

@ -34,19 +34,126 @@
function setConf(parameterName, defaultValue) {
if (typeof Conf[parameterName] === "undefined") {
Conf[parameterName] = defaultValue;
} else if (typeof Conf[parameterName] !== typeof defaultValue) {
myAlert("Head's up - configuration " + parameterName + " does not have the right type. It should be a " + (typeof defaultValue) + ", it is a " + (typeof Conf[parameterName]));
throw new Error("Wrong type for configuration " + parameterName + ", expected " + (typeof defaultValue) + ", got " + (typeof Conf[parameterName]));
}
}
setConf("POLLING_DELAY", 2000);
setConf("ENABLE_WEBSOCKETS", true);
setConf("ENABLE_EVENT_SOURCE", true);
setConf("MAX_WEBSOCKET_ERRORS", 1);
setConf("APP_PATH", "");
setConf("ENABLE_SPELL_CHECKER", false);
setConf("DOUBLE_TAP_DURATIONS", [650, 1100, 1800, 3000, 5000]);
setConf("POLLING_DELAY", 2000);
setConf("ENABLE_WEBSOCKETS", true);
setConf("ENABLE_EVENT_SOURCE", true);
setConf("MAX_WEBSOCKET_ERRORS", 1);
setConf("APP_PATH", "");
setConf("ENABLE_MSG_SOUND", true);
setConf("ENABLE_TILE_SOUND", true);
setConf("ENABLE_SPELL_CHECKER", false);
setConf("DOUBLE_TAP_DURATIONS", [650, 1100, 1800, 3000, 5000]);
setConf("FLASH_LIGHT_DURATIONS", [800, 1600, 3200]);
setConf("FLASH_LIGHT_COLOR", "#E3E");
setConf("ENABLE_TIMER", true);
setConf("FLASH_LIGHT_COLOR", "#E3E");
setConf("ENABLE_TIMER", true);
function isSetting(key) {
return Object.prototype.hasOwnProperty.call(Conf, key) ||
Object.prototype.hasOwnProperty.call(localStorage, key);
}
function getSetting(key) {
let type;
let value;
/* get default value from configuration */
if (Object.prototype.hasOwnProperty.call(Conf, key)) {
value = Conf[key];
type = typeof value;
}
/* try to retrieve value from localstorage */
if (Object.prototype.hasOwnProperty.call(localStorage, key)) {
value = localStorage.getItem(key);
/* get type from localStorage if no default is set */
if (typeof type === "undefined") {
if (Object.prototype.hasOwnProperty.call(localStorage, key + "_type")) {
type = localStorage.getItem(key + "_type");
} else {
type = "string";
}
}
/* cast from string to type */
if (type === "boolean") {
value = (value === "true");
} else if (type === "number") {
value = Number(value);
} else if (type === "object") {
value = JSON.parse(value);
} else if (type === "string") {
value = String(value);
} else {
console.error("Unsupported type");
}
}
return value;
}
function setSetting(key, value) {
let type;
/* try to retrieve type from configuration */
if (Object.prototype.hasOwnProperty.call(Conf, key)) {
type = typeof Conf[key];
}
/* try to retrieve type from localstorage */
if (typeof type === "undefined") {
if (Object.prototype.hasOwnProperty.call(localStorage, key)) {
type = localStorage.getItem(key + "_type");
}
}
/* if not set type is defined from value */
if (typeof type === "undefined") {
type = typeof value;
}
/* storage value in localstorage */
if (type === typeof value) {
if (type === "object") {
value = JSON.stringify(value);
}
if ((type === "boolean") ||
(type === "number") ||
(type === "object") ||
(type === "string")) {
localStorage.setItem(key, value);
/* store type into localstorage if no default value in configuration */
if (!Object.prototype.hasOwnProperty.call(Conf, key)) {
localStorage.setItem(key + "_type", type);
}
} else {
console.error("Unsupported type");
}
} else {
console.error("incoherent type");
}
}
function migrateSetting(key, type) {
if (Object.prototype.hasOwnProperty.call(localStorage, key)) {
localStorage.setItem(key + "_type", type);
}
}
migrateSetting("doubleTapDuration", "number");
migrateSetting("flashLightDuration", "number");
migrateSetting("spellCheckerEnabled", "boolean");
migrateSetting("trivabbleGameNumber", "number");
migrateSetting("trivabbleBoardLang", "string");
migrateSetting("trivabbleGameNumber", "number");
migrateSetting("trivabbleLang", "string");
migrateSetting("trivabblePlayerName", "string");
const _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {
return s;
@ -168,11 +275,11 @@
return;
}
if (!force && localStorage.spellCheckerEnabled !== "true") {
if (!force && !getSetting("spellCheckerEnabled")) {
myConfirm(
_("Spell checking requires Trivabble to download a dictionary. Do you confirm?"),
function () {
localStorage.spellCheckerEnabled = "true";
setSetting("spellCheckerEnabled", true);
getDictionary(code, callback, true);
}
);
@ -206,7 +313,7 @@
}
function spellCheckerSettingChecked() {
localStorage.spellCheckerEnabled = document.getElementById("enable-spell-checker").checked.toString();
setSetting("spellCheckerEnabled", document.getElementById("enable-spell-checker").checked);
initSpellChecker();
}
@ -567,7 +674,7 @@
tile.lastChild.textContent = scoreOf[letter] || "";
if (highlight) {
tile.classList.add("tile-highlight");
if (tilesSound.checked) {
if (getSetting("ENABLE_TILE_SOUND")) {
audioNotification.play();
}
} else {
@ -634,14 +741,16 @@
function set(key, value) {
switch (key) {
case "playerName":
name.textContent = localStorage.trivabblePlayerName = value;
name.textContent = value;
setSetting("trivabblePlayerName", value);
break;
case "gameNumber":
document.getElementById("number").textContent = localStorage.trivabbleGameNumber = value;
document.getElementById("number").textContent = value;
setSetting("trivabbleGameNumber", value);
break;
case "boardLang":
localStorage.trivabbleBoardLang = value;
document.getElementById("board-lang").value = value;
setSetting("trivabbleBoardLang", value);
break;
}
}
@ -657,7 +766,7 @@
myAlert(
format(
_("You are about to leave the current game. To recover it, please note its number: {0}"),
localStorage.trivabbleGameNumber
getSetting("trivabbleGameNumber")
),
f
);
@ -737,8 +846,8 @@
chatMessages.scrollTop = chatMessages.scrollHeight;
if (sender && sender !== localStorage.trivabblePlayerName) {
if (msgSound.checked) {
if (sender && sender !== getSetting("trivabblePlayerName")) {
if (getSetting("ENABLE_MSG_SOUND")) {
audioChat.play();
}
@ -777,7 +886,7 @@
case "changeBoardLang": {
const newLang = boardLangSelect.querySelector("[value=" + msg.specialMsg.newBoardLang + "]").textContent;
infoMessage(
(msg.sender === localStorage.trivabblePlayerName)
(msg.sender === getSetting("trivabblePlayerName"))
? format(_("You changed the language of the board to {0}"), newLang)
: format(_("{0} changed the language of the board to {1}"), msg.sender, newLang)
);
@ -965,13 +1074,13 @@
}
if (data.to === "board") {
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== getSetting("trivabblePlayerName"));
} else if (data.to === "rack") {
setRackCell(data.indexTo, data.letter);
}
break;
case "setCell":
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== getSetting("trivabblePlayerName"));
if ((data.letter !== "") && (currentTilePlayed[data.indexTo] === "-")) {
currentTilePlayed[data.indexTo] = data.letter;
}
@ -1033,7 +1142,7 @@
}
if (data.gameNumber) {
set("gameNumber", data.gameNumber);
set("gameNumber", Number(data.gameNumber));
}
if (data.availableBoardLangs) {
@ -1101,7 +1210,7 @@
startConnection();
retryPollingTimeout = 0;
},
delay || Conf.POLLING_DELAY
delay || getSetting("POLLING_DELAY")
);
} else {
retriedImmediately = true;
@ -1178,7 +1287,7 @@
} else {
webSocketErrors++;
if (webSocketErrors > Conf.MAX_WEBSOCKET_ERRORS) {
if (webSocketErrors > getSetting("MAX_WEBSOCKET_ERRORS")) {
blacklistWebsockets = true;
}
}
@ -1205,7 +1314,7 @@
}
function pollServerWithEventSource() {
if (canConnect() && Conf.ENABLE_EVENT_SOURCE && window.EventSource) {
if (canConnect() && getSetting("ENABLE_EVENT_SOURCE") && window.EventSource) {
closeConnections();
pollingServer = true;
@ -1218,7 +1327,7 @@
}
function pollServerWithWebSocket() {
if (canConnect() && Conf.ENABLE_WEBSOCKETS && !blacklistWebsockets && window.WebSocket) {
if (canConnect() && getSetting("ENABLE_WEBSOCKETS") && !blacklistWebsockets && window.WebSocket) {
closeConnections();
pollingServer = true;
@ -1320,7 +1429,7 @@
xhrRequest(cmdsWithContext(cmds), function (xhr) {
if (xhr.readyState === 4) {
if (xhr.status === 0 || xhr.status >= 300) {
setTimeout(sendCmds.bind(null, cmds), Conf.POLLING_DELAY);
setTimeout(sendCmds.bind(null, cmds), getSetting("POLLING_DELAY"));
return;
}
@ -1337,9 +1446,9 @@
function cmdsWithContext(cmds) {
return {
gameNumber: localStorage.trivabbleGameNumber || "",
playerName: localStorage.trivabblePlayerName,
boardLang: localStorage.trivabbleBoardLang,
gameNumber: getSetting("trivabbleGameNumber") || "",
playerName: getSetting("trivabblePlayerName"),
boardLang: getSetting("trivabbleBoardLang"),
version: VERSION,
cmds: cmds
};
@ -1360,7 +1469,7 @@
if (!pollingReady) {
startConnection();
setTimeout(sendCmds, Conf.POLLING_DELAY, cmds);
setTimeout(sendCmds, getSetting("POLLING_DELAY"), cmds);
return;
}
@ -1371,13 +1480,13 @@
checkGameInProgress(
function () {
myPrompt(
_("To join a game, please give the number which is displayed on your adversary(ies)' screen.\nIf you do not know it, ask them.\n\nWarning: your adversary must not take your number, (s)he must keep his/her own. If you whish to recover your current game, please not the following number: {0}.").replace("{0}", localStorage.trivabbleGameNumber),
_("To join a game, please give the number which is displayed on your adversary(ies)' screen.\nIf you do not know it, ask them.\n\nWarning: your adversary must not take your number, (s)he must keep his/her own. If you whish to recover your current game, please not the following number: {0}.").replace("{0}", getSetting("trivabbleGameNumber")),
function (n) {
n = parseInt(n);
if (isNaN(n)) {
myAlert(_("It seems your did not give a correct number, or you clicked on “Cancel”. As a result, the current game continues, if any. To join a game, click on “Join a game” again."));
} else {
localStorage.trivabbleGameNumber = n;
setSetting("trivabbleGameNumber", n);
location.reload();
}
}
@ -1406,33 +1515,31 @@
_("To change your name, enter a new one. You can keep using your current name by cancelling. Please note that if you change your name and you have games in progress, you will not be able to keep playing them anymore unless you get back to your current name."),
function (newName) {
if (newName && newName.trim()) {
localStorage.trivabblePlayerName = newName.trim();
name.textContent = localStorage.trivabblePlayerName;
setSetting("trivabblePlayerName", newName.trim());
name.textContent = getSetting("trivabblePlayerName");
}
},
localStorage.trivabblePlayerName
getSetting("trivabblePlayerNamer")
);
}
function startGame(number) {
if (number) {
localStorage.trivabbleGameNumber = number;
setSetting("trivabbleGameNumber", number);
}
startConnection();
}
let audioTileLoaded = false;
let audioMsgLoaded = false;
let tilesSound;
let msgSound;
function loadAudio() {
if (!audioTileLoaded && tilesSound.checked) {
if (!audioTileLoaded && getSetting("ENABLE_TILE_SOUND")) {
audioTileLoaded = true;
audioNotification.load();
}
if (!audioMsgLoaded && msgSound.checked) {
if (!audioMsgLoaded && getSetting("ENABLE_MSG_SOUND")) {
audioMsgLoaded = true;
audioChat.load();
}
@ -1487,7 +1594,7 @@
sendCmds([{cmd: "changeBoard", lang: code}]);
},
function () {
boardLangSelect.value = localStorage.trivabbleBoardLang;
boardLangSelect.value = getSetting("trivabbleBoardLang");
}
);
}
@ -1671,11 +1778,11 @@
}
function checkSpellingClicked() {
if (localStorage.spellCheckerEnabled === "false") {
if (getSetting("spellCheckerEnabled") === "false") {
return;
}
getDictionary(localStorage.trivabbleBoardLang, checkSpelling);
getDictionary(getSetting("trivabbleBoardLang"), checkSpelling);
}
function checkSpelling(dictionary) {
@ -1764,7 +1871,7 @@
return;
}
if ((laserTouch.last !== cell) || (Date.now() - laserTouch.date > localStorage.doubleTapDuration)) {
if ((laserTouch.last !== cell) || (Date.now() - laserTouch.date > getSetting("doubleTapDuration"))) {
laserTouch.last = cell;
laserTouch.date = Date.now();
return;
@ -1778,7 +1885,7 @@
placeholder.classList.add("laser-highlighting");
placeholder.classList.add("laser-highlight");
const sixthFlashLightDuration = Number(localStorage.flashLightDuration) / 6;
const sixthFlashLightDuration = getSetting("flashLightDuration") / 6;
function on() {
placeholder.classList.add("laser-highlight");
@ -1857,39 +1964,47 @@
audioChat.appendChild(audioSourceOGG);
audioChat.appendChild(audioSourceMP3);
const tilesSound = document.getElementById("tiles-sound");
if (tilesSound) {
tilesSound = document.getElementById("tiles-sound");
msgSound = document.getElementById("msg-sound");
/* migration of old settings for tiles sound */
const oldSetting = getSetting("trivabbleTilesSound");
if (oldSetting) {
setSetting("ENABLE_TILE_SOUND", oldSetting === "true");
delete localStorage.trivabbleTilesSound;
}
tilesSound.onclick = function () {
localStorage.trivabbleTileSound = tilesSound.checked;
};
msgSound.onclick = function () {
localStorage.trivabbleMsgSound = msgSound.checked;
};
if (Object.prototype.hasOwnProperty.call(localStorage, "trivabbleMsgSound")) {
msgSound.checked = localStorage.trivabbleMsgSound === "true";
} else {
localStorage.trivabbleMsgSound = msgSound.checked;
tilesSound.checked = getSetting("ENABLE_TILE_SOUND");
tilesSound.onclick = function () {
setSetting("ENABLE_TILE_SOUND", this.checked);
};
}
if (Object.prototype.hasOwnProperty.call(localStorage, "trivabbleTileSound")) {
tilesSound.checked = localStorage.trivabbleTileSound === "true";
} else {
localStorage.trivabbleTilesSound = tilesSound.checked;
const msgSound = document.getElementById("msg-sound");
if (msgSound) {
/* migration of old settings for message sound */
const oldSetting = getSetting("trivabbleMsgSound");
if (oldSetting) {
setSetting("ENABLE_MSG_SOUND", oldSetting === "true");
delete localStorage.trivabbleMsgSound;
}
msgSound.checked = getSetting("ENABLE_MSG_SOUND");
msgSound.onclick = function () {
setSetting("ENABLE_MSG_SOUND", this.checked);
};
}
}
function initSpellChecker() {
if (Conf.ENABLE_SPELL_CHECKER) {
if (getSetting("ENABLE_SPELL_CHECKER")) {
document.getElementById("enable-spell-checker-outer").style.display = "";
}
document.getElementById("enable-spell-checker").onclick = spellCheckerSettingChecked;
if (!Conf.ENABLE_SPELL_CHECKER || localStorage.spellCheckerEnabled === "false") {
if (!getSetting("ENABLE_SPELL_CHECKER") || !getSetting("spellCheckerEnabled")) {
document.getElementById("check-spelling").style.display = "none";
document.getElementById("enable-spell-checker").checked = false;
document.getElementById("info-spell-checking").style.display = "none";
@ -1921,46 +2036,51 @@
document.body.addEventListener("dblclick", laserDblClick);
document.body.addEventListener("touchstart", laserTouch);
if (!Object.prototype.hasOwnProperty.call(localStorage, "doubleTapDuration")) {
localStorage.doubleTapDuration = Conf.DOUBLE_TAP_DURATIONS[Math.floor(Conf.DOUBLE_TAP_DURATIONS.length / 2)];
if (!isSetting("doubleTapDuration")) {
setSetting("doubleTapDuration", getSetting("DOUBLE_TAP_DURATIONS")[Math.floor(getSetting("DOUBLE_TAP_DURATIONS").length / 2)]);
}
const doubleTapDuration = document.getElementById("double-tap-duration");
if (doubleTapDuration) {
doubleTapDuration.max = Conf.DOUBLE_TAP_DURATIONS.length - 1;
doubleTapDuration.value = translateDuration(localStorage.doubleTapDuration, Conf.DOUBLE_TAP_DURATIONS);
doubleTapDuration.max = getSetting("DOUBLE_TAP_DURATIONS").length - 1;
doubleTapDuration.value = translateDuration(getSetting("doubleTapDuration"), getSetting("DOUBLE_TAP_DURATIONS"));
doubleTapDuration.onchange = function () {
localStorage.doubleTapDuration = translateDuration(
setSetting("doubleTapDuration", translateDuration(
document.getElementById("double-tap-duration").value,
Conf.DOUBLE_TAP_DURATIONS
);
getSetting("DOUBLE_TAP_DURATIONS")
));
};
}
if (!Object.prototype.hasOwnProperty.call(localStorage, "flashLightDuration")) {
localStorage.flashLightDuration = Conf.FLASH_LIGHT_DURATIONS[Math.floor(Conf.FLASH_LIGHT_DURATIONS.length / 2)];
if (!isSetting("flashLightDuration")) {
setSetting("flashLightDuration", getSetting("FLASH_LIGHT_DURATIONS")[Math.floor(getSetting("FLASH_LIGHT_DURATIONS").length / 2)]);
}
const flashLightDuration = document.getElementById("flash-light-duration");
if (flashLightDuration) {
flashLightDuration.max = Conf.FLASH_LIGHT_DURATIONS.length - 1;
flashLightDuration.value = translateDuration(localStorage.flashLightDuration, Conf.FLASH_LIGHT_DURATIONS);
flashLightDuration.max = getSetting("FLASH_LIGHT_DURATIONS").length - 1;
flashLightDuration.value = translateDuration(getSetting("flashLightDuration"), getSetting("FLASH_LIGHT_DURATIONS"));
flashLightDuration.onchange = function () {
localStorage.flashLightDuration = translateDuration(
setSetting("flashLightDuration", translateDuration(
document.getElementById("flash-light-duration").value,
Conf.FLASH_LIGHT_DURATIONS
);
getSetting("FLASH_LIGHT_DURATIONS")
));
};
}
if (!Object.prototype.hasOwnProperty.call(localStorage, "flashLightColor")) {
localStorage.flashLightColor = Conf.FLASH_LIGHT_COLOR;
}
const flashLightColor = document.getElementById("flash-light-color");
if (flashLightColor) {
flashLightColor.value = localStorage.flashLightColor;
/* migration of old settings for flash light color */
const oldSetting = getSetting("flashLightColor");
if (oldSetting) {
setSetting("FLASH_LIGHT_COLOR", oldSetting);
delete localStorage.flashLightColor;
}
flashLightColor.value = getSetting("FLASH_LIGHT_COLOR");
flashLightColor.onchange = function () {
localStorage.flashLightColor = document.getElementById("flash-light-color").value;
setSetting("FLASH_LIGHT_COLOR", document.getElementById("flash-light-color").value);
const style = document.createElement("style");
style.textContent = ".laser-highlight {background-color: " + localStorage.flashLightColor + ";}";
style.textContent = ".laser-highlight {background-color: " + getSetting("FLASH_LIGHT_COLOR") + ";}";
document.body.appendChild(style);
};
flashLightColor.onchange();
@ -1989,7 +2109,7 @@
localStorage.timerGlobalDate = timerDate();
}
if (!Object.prototype.hasOwnProperty.call(localStorage, "timerEnable")) {
localStorage.timerEnable = Conf.ENABLE_TIMER;
localStorage.timerEnable = getSetting("ENABLE_TIMER");
}
document.getElementById("enable-timer").onclick = function () {
setTimerState(document.getElementById("enable-timer").checked);
@ -2017,14 +2137,14 @@
}
function repromptName(f) {
if (localStorage.trivabblePlayerName && localStorage.trivabblePlayerName.trim()) {
if (getSetting("trivabblePlayerName") && getSetting("trivabblePlayerName").trim()) {
f();
} else {
myPrompt(
_("It seems your did not give your name. You need to do it for the game to run properly."),
function (name) {
if (name && name.trim()) {
localStorage.trivabblePlayerName = name.trim();
setSetting("trivabblePlayerName", name.trim());
}
repromptName(f);
@ -2073,11 +2193,11 @@
);
});
boardLangSelect.value = localStorage.trivabbleBoardLang;
boardLangSelect.value = getSetting("trivabbleBoardLang");
}
function langSelectionChange(e) {
localStorage.trivabbleLang = e.target.value;
setSetting("trivabbleLang", e.target.value);
location.reload();
}
@ -2116,12 +2236,12 @@
}
function initGame() {
if (!localStorage.trivabblePlayerName) {
if (!getSetting("trivabblePlayerName")) {
myPrompt(
_("Hello! To begin, enter your name. Your adversaries will see this name when you play with them."),
function (name) {
if (name && name.trim()) {
localStorage.trivabblePlayerName = name;
setSetting("trivabblePlayerName", name);
}
repromptName(initGame);
}
@ -2130,7 +2250,7 @@
return;
}
name.textContent = localStorage.trivabblePlayerName;
name.textContent = getSetting("trivabblePlayerName");
const letters = "ABCDEFGHIJKLMNO";
@ -2205,19 +2325,19 @@
board.lastChild.appendChild(board.rows[0].cells[0].cloneNode(false));
if (localStorage.trivabbleGameNumber) {
document.getElementById("number").textContent = localStorage.trivabbleGameNumber;
if (getSetting("trivabbleGameNumber")) {
document.getElementById("number").textContent = getSetting("trivabbleGameNumber");
}
if (localStorage.trivabbleBoardLang) {
document.getElementById("board-lang").value = localStorage.trivabbleBoardLang;
if (getSetting("trivabbleBoardLang")) {
document.getElementById("board-lang").value = getSetting("trivabbleBoardLang");
}
startGame(localStorage.trivabbleGameNumber);
startGame(getSetting("trivabbleGameNumber"));
}
function initLang() {
const lang = libD.lang = localStorage.trivabbleLang || libD.lang;
const lang = libD.lang = getSetting("trivabbleLang") || libD.lang;
const langSel = document.getElementById("select-lang");
langSel.value = lang;