Merge branch 'develop' into automatic_scoring

This commit is contained in:
Laurent Mazet 2020-10-18 19:21:36 +02:00
commit faf78ca932
26 changed files with 1247 additions and 364 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
/server/games.backup.json
/public/config.js
/public/dict/*.dict
/public/dict/
/public/l10n
/.vscode

View File

@ -10,7 +10,7 @@ endif
.PHONY: all help lang start-dev-server
all: lang
all: lang emptydictlist
help:
@echo make extract-lang-dists: extract the distributions per language from Wikipedia
@ -26,6 +26,11 @@ extract-lang-dists:
lang: public/l10n/js/fr.js
emptydictlist: public/dict/list.js
public/dict/list.js:
mkdir -p public/dict/ && touch public/dict/list.js
eslint:
-${ESLINT} **/*.js

View File

@ -137,7 +137,22 @@ We will assume www-data is the UNIX user of your Web server. Adapt if necessary.
8. Configure your web server.
On Apache 2, the relevant rules are:
#### Content-Security Policy Header
Trivabble loads 5 static javascript files, 2 static css files and a sound from its origin, and connects to the same server by SSE or websockets.
Here is the recommended HTTP CSP header for Trivabble:
```
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; connect-src 'self'; media-src 'self'
```
We advise you to set this up to improve the security of your setup.
In the next two sections, we describe the configuration for Apache 2 and Nginx.
#### Apache 2
On Apache 2, the relevant rules are:
RewriteEngine on
RewriteRule "^/?:trivabble/ws/(.*)" ws://localhost:3000/ws/$1 [P]
@ -153,16 +168,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

1
dict-dists-extractor/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/obj

View File

@ -24,13 +24,14 @@ LOWS = $(addsuffix .low,$(LANGS))
DICTS = $(addsuffix .dict,$(LANGS))
DEPEND = Makefile
#DEPEND = Makefile
#MAKEFLAGS = -s
.PHONY: all required count clean low
.PHONY: all required count clean low list
all: required
make $(addprefix check-,$(LANGS))
make list
$(ROOT_DICT):
mkdir $(ROOT_DICT)
@ -45,6 +46,15 @@ low: $(LOWS)
required: ${OBJ_DIR} ${OBJ_DIR}/src.mk ${ROOT_DICT}
list: $(ROOT_DICT)/list.js
$(ROOT_DICT)/list.js: $(wildcard $(ROOT_DICT)/*.dict)
ls -s ${ROOT_DICT}/*.dict| \
awk 'BEGIN { printf "window.DictionaryList = {\n" } \
{ $$0 = gensub(/(.+) .*\/(.+)\.dict/, "\\1 \\2", "g"); \
printf "\"%s\": %d,\n", $$2, $$1 } \
END { printf "\"none\": 0\n};" }' > $@
$(OBJ_DIR)/src.mk:
echo Creation language table
wget $(ASPDICT) -q -O - | \

View File

@ -164,8 +164,8 @@ msgstr "Son ar c'hemennadenoù"
msgid "Number of tiles in the bag:"
msgstr "Niver a bezhioù er sac'h:"
msgid "Click on it to take one."
msgstr "Klikit warnañ evit kemer unan"
msgid "Click on the bag to take onei tile."
msgstr ""
msgid "Put back all the tiles in the bag"
msgstr "Adlakaat an holl pezhioù er sac'h"

View File

@ -1,121 +1,121 @@
#TranslationFunction trivabble
msgid "Afrikaans"
msgstr "afrikaans"
msgstr "Afrikaans"
msgid "Arabic"
msgstr "arabe"
msgstr "Arabe"
msgid "Bulgarian"
msgstr "bulgare"
msgstr "Bulgare"
msgid "Breton"
msgstr "breton"
msgstr "Breton"
msgid "Catalan"
msgstr "catalan"
msgstr "Catalan"
msgid "Czech"
msgstr "tchèque"
msgstr "Tchèque"
msgid "Welsh"
msgstr "gallois"
msgstr "Gallois"
msgid "Danish"
msgstr "danois"
msgstr "Danois"
msgid "German"
msgstr "allemand"
msgstr "Allemand"
msgid "Greek"
msgstr "grec"
msgstr "Grec"
msgid "English"
msgstr "anglais"
msgstr "Anglais"
msgid "Esperanto"
msgstr "espéranto"
msgstr "Espéranto"
msgid "Spanish"
msgstr "espagnol"
msgstr "Espagnol"
msgid "Estonian"
msgstr "estonien"
msgstr "Estonien"
msgid "Basque"
msgstr "basque"
msgstr "Basque"
msgid "Finnish"
msgstr "finlandais"
msgstr "Finlandais"
msgid "French"
msgstr "français"
msgstr "Français"
msgid "Irish"
msgstr "irlandais"
msgstr "Irlandais"
msgid "Hebrew"
msgstr "hébreu"
msgstr "Hébreu"
msgid "Croatian"
msgstr "croate"
msgstr "Croate"
msgid "Hungarian"
msgstr "hongrois"
msgstr "Hongrois"
msgid "Armenian"
msgstr "arménien"
msgstr "Arménien"
msgid "Icelandic"
msgstr "islandais"
msgstr "Islandais"
msgid "Italian"
msgstr "italien"
msgstr "Italien"
msgid "Latin"
msgstr "latin"
msgstr "Latin"
msgid "Lithuanian"
msgstr "lituanien"
msgstr "Lituanien"
msgid "Latvian"
msgstr "letton"
msgstr "Letton"
msgid "Malay"
msgstr "malais"
msgstr "Malais"
msgid "Dutch"
msgstr "néerlandais"
msgstr "Néerlandais"
msgid "Norwegian"
msgstr "norvégien"
msgstr "Norvégien"
msgid "Polish"
msgstr "polonais"
msgstr "Polonais"
msgid "Portuguese"
msgstr "portugais"
msgstr "Portugais"
msgid "Romanian"
msgstr "roumain"
msgstr "Roumain"
msgid "Russian"
msgstr "russe"
msgstr "Russe"
msgid "Slovak"
msgstr "slovaque"
msgstr "Slovaque"
msgid "Slovenian"
msgstr "slovène"
msgstr "Slovène"
msgid "Swedish"
msgstr "suédois"
msgstr "Suédois"
msgid "Turkish"
msgstr "turc"
msgstr "Turc"
msgid "Ukrainian"
msgstr "ukrainien"
msgstr "Ukrainien"
msgid "Klingon"
msgstr "klingon"
msgstr "Klingon"

View File

@ -85,8 +85,8 @@ msgstr "Bruit des messages"
msgid "Number of tiles in the bag:"
msgstr "Nombre de pièces dans le sac :"
msgid "Click on it to take one."
msgstr "Cliquez dessus pour en prendre une."
msgid "Click on the bag to take one tile."
msgstr "Cliquez sur le sac pour prendre une pièce."
msgid "Put back all the tiles in the bag"
msgstr "Ranger toutes les pièces dans le sac"
@ -136,6 +136,12 @@ msgstr "Tour"
msgid "Who's turn? Click on the Turn button!"
msgstr "À qui le tour ? Cliquez sur le bouton Tour !"
msgid "Click on (+) to increase someone's score."
msgstr "Cliquez sur (+) pour augmenter le score de quelquun."
msgid "Show a cell to everyone by double-clicking on it."
msgstr "Montrez une case à tout le monde en double-cliquant dessus."
msgid "Show my rack to other players"
msgstr "Montrer mon jeu aux autres"
@ -158,7 +164,7 @@ msgid "Are you sure you want to change board to '{0}'? This will put all the ti
msgstr "Êtes-vous sûr·e de vouloir de changer la langue du plateau à '{0}' ? Cela remettra toutes les lettres du jeu dans le sac et commencera une nouvelle partie."
msgid "Board language:"
msgstr "Langue du plateau :"
msgstr "Langue du plateau :"
msgid "You changed the language of the board to {0}"
msgstr "Vous avez changé la langue du plateau en {0}"
@ -184,9 +190,6 @@ msgstr "Dictionnaire incorrect."
msgid "Can't load dictionary file."
msgstr "Impossible de charger le dictionnaire"
msgid "Enable spell checker"
msgstr "Activer le vérificateur orthographique"
msgid "Spell checking requires Trivabble to download a dictionary. Do you confirm?"
msgstr "La vérification orthographique nécessite que Trivabble télécharge un dictionnaire. Confirmez-vous ?"
@ -205,8 +208,8 @@ msgstr "Fenêtre de configuration"
msgid "Look at:"
msgstr "Regardez ici :"
msgid "Double tap duration"
msgstr "Durée de la double tape"
msgid "Double tap duration:"
msgstr "Durée de la double tape :"
msgid "(slow)"
msgstr "(lent)"
@ -214,8 +217,17 @@ msgstr "(lent)"
msgid "(fast)"
msgstr "(rapide)"
msgid "Flash light duration"
msgstr "Durée du flash laser"
msgid "Flash light duration:"
msgstr "Durée du flash laser :"
msgid "Flash light color"
msgstr "Couleur du flash laser"
msgid "Tip!"
msgstr "Astuce !"
msgid "Next tip"
msgstr "Astuce suivante"
msgid "Disable the spell checker"
msgstr "Désactiver la vérification orthographique"

View File

@ -156,8 +156,8 @@ msgstr "Sunet de la mesaje"
msgid "Number of tiles in the bag:"
msgstr "Număr de jetoane în sac:"
msgid "Click on it to take one."
msgstr "Apasă pe el ca să iei unul."
msgid "Click on the bag to take one tile."
msgstr ""
msgid "Put back all the tiles in the bag"
msgstr "Pune toate jetoanele înapoi în sac"

View File

@ -87,7 +87,7 @@ msgstr ""
msgid "Number of tiles in the bag:"
msgstr ""
msgid "Click on it to take one."
msgid "Click on the bag to take one tile."
msgstr ""
msgid "Put back all the tiles in the bag"
@ -190,9 +190,6 @@ msgstr ""
msgid "Can't load dictionary file."
msgstr ""
msgid "Enable spell checker"
msgstr ""
msgid "Spell checking requires Trivabble to download a dictionary. Do you confirm?"
msgstr ""
@ -210,7 +207,7 @@ msgid "Configuration window"
msgid "Look at:"
msgstr ""
msgid "Double tap duration"
msgid "Double tap duration:"
msgstr ""
msgid "(slow)"
@ -219,8 +216,23 @@ msgstr ""
msgid "(fast)"
msgstr ""
msgid "Flash light duration"
msgid "Flash light duration:"
msgstr ""
msgid "Flash light color"
msgstr ""
msgid "Click on (+) to increase someone's score."
msgstr ""
msgid "Show a cell to everyone by double-clicking on it."
msgstr ""
msgid "Tip!"
msgstr ""
msgid "Next tip"
msgstr ""
msgid "Disable the spell checker"
msgstr ""

View File

@ -15,7 +15,7 @@ $1 == "-" { sub(/- /, "") }
gsub(/[×,]/, "")
for (i = 1; i<= NF; i+=2) {
lettre = $(i)
if (lettre == "blank") {
if ((lettre == "blank") || (lettre ~ /[Jj]oker/)) {
lettre = " "
}
nombre = $(i+1)

View File

@ -22,6 +22,7 @@
divAlert.style.display = "none";
if (divAlertCallback) {
divAlertCallback(alertInput.value);
divAlertCallback = null;
}
}
@ -29,6 +30,7 @@
divAlert.style.display = "none";
if (divAlertCallback) {
divAlertCallback(null);
divAlertCallback = null;
}
}
@ -36,12 +38,15 @@
divAlert.style.display = "none";
if (divAlertCallbackYes) {
divAlertCallbackYes();
divAlertCallbackYes = null;
}
}
function confirmNo() {
divAlert.style.display = "none";
if (divAlertCallbackNo) {
divAlertCallbackNo();
divAlertCallbackNo = null;
}
}
@ -112,6 +117,12 @@
divAlertOuter.appendChild(divAlertButton);
divAlert.appendChild(divAlertOuter);
document.body.appendChild(divAlert);
document.body.addEventListener("keydown", function (e) {
if (e.key === "Escape") {
promptCancel();
confirmNo();
}
});
}
global.myAlert = function (msg, callback) {

View File

@ -1,23 +1,202 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" height="319mm" width="331mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1172.8346 1130.315" xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs id="defs4">
<linearGradient id="linearGradient5620" y2="482.82" gradientUnits="userSpaceOnUse" x2="1120.7" gradientTransform="matrix(1.21 0 0 1.21 -122.4 -73.147)" y1="482.82" x1="56.525">
<stop id="stop5616" offset="0"/>
<stop id="stop5618" stop-color="#33333f" stop-opacity=".94118" offset="1"/>
</linearGradient>
</defs>
<metadata id="metadata7">
<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/>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1" transform="translate(0 77.953)">
<path id="path3348" d="m510.26 107.75-273.12 245.46-127.91 449.43 490.91 207.46 456.36-183.26-159.04-487.46-221.26-224.71 190.18-148.75c-88.433-9.4796-177.32-14.756-266.25-15.804-96.89-1.1424-193.84 2.7337-290.33 11.607l200.46 146.03z" fill-rule="evenodd" stroke="#000" stroke-width="1.21px" fill="url(#linearGradient5620)"/>
<path id="path4164" stroke-linejoin="round" d="m684.88 431.89c-6.34-58.38-23.22-115.59-49.58-168.06-29.85-59.42-71.88-112.69-122.72-155.55 22.722 2.9086 45.556 4.9435 68.435 6.0986 34.007 1.717 68.112 1.489 102.09-0.68242" stroke="#b47f58" stroke-linecap="round" stroke-width="12.1" fill="none"/>
</g>
<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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
height="319mm"
width="331mm"
version="1.1"
viewBox="0 0 1172.8346 1130.315"
sodipodi:docname="bag.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1355"
id="namedview11"
showgrid="false"
inkscape:zoom="3.3994373"
inkscape:cx="132.01055"
inkscape:cy="886.12477"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g1005" />
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient990">
<stop
style="stop-color:#e6e6e6;stop-opacity:1"
offset="0"
id="stop986" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop988" />
</linearGradient>
<linearGradient
id="linearGradient5620"
y2="482.82"
gradientUnits="userSpaceOnUse"
x2="1120.7"
gradientTransform="matrix(1.21 0 0 1.21 -122.4 -73.147)"
y1="482.82"
x1="56.525">
<stop
id="stop5616"
offset="0" />
<stop
id="stop5618"
stop-color="#33333f"
stop-opacity=".94118"
offset="1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient953"
id="linearGradient955"
x1="-135.19894"
y1="53.679901"
x2="-135.27045"
y2="97.79319"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.6041101,0,0,1.6040558,56.429896,-19.958597)" />
<linearGradient
inkscape:collect="always"
id="linearGradient953">
<stop
style="stop-color:#dcdcdc;stop-opacity:1"
offset="0"
id="stop949" />
<stop
style="stop-color:#f0f0f0;stop-opacity:1"
offset="1"
id="stop951" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient990"
id="linearGradient984"
x1="19.518953"
y1="456.18195"
x2="75.192528"
y2="456.18195"
gradientUnits="userSpaceOnUse" />
</defs>
<metadata
id="metadata7">
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="g1005"
transform="matrix(1.4450193,-0.42637031,0.42637031,1.4450193,-580.17097,642.33303)">
<rect
style="fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:#b4b4b4;stroke-width:1.87486361;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="rect30310-3"
width="53.798576"
height="107.53391"
x="20.456453"
y="402.41501"
transform="matrix(0.56850715,-0.82267832,0.82284526,0.5682655,0,0)"
ry="0" />
<rect
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#f5f5c8;fill-opacity:1;fill-rule:nonzero;stroke:#646400;stroke-width:1.68281;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="rect30310"
width="47.172165"
height="47.172165"
x="404.79202"
y="-73.046112"
transform="rotate(34.895179)" />
<path
d="m 375.58291,176.93724 6.67114,4.65302 -0.62629,0.89793 -2.79945,-1.95258 -4.87407,6.98807 -1.07223,-0.74788 4.87405,-6.98806 -2.79944,-1.95257 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path865" />
<path
d="m 388.64898,192.98983 q 0.26228,0.35567 0.31922,0.9607 0.0623,0.60872 -0.0745,1.50267 l -0.42031,2.91028 -1.14619,-0.79945 0.40216,-2.72666 q 0.16175,-1.06492 -0.0274,-1.58162 -0.18393,-0.51303 -0.81777,-0.95512 l -1.16204,-0.81049 -2.32466,3.33292 -1.06696,-0.74418 5.50035,-7.886 2.40859,1.67995 q 1.35219,0.94313 1.62352,1.97249 0.27132,1.02938 -0.52444,2.17029 -0.51945,0.74475 -1.21069,0.99283 -0.68594,0.25175 -1.47882,-0.0186 z m -0.36275,-5.17596 -1.95257,2.79945 1.34162,0.93577 q 0.77117,0.53788 1.40887,0.45661 0.64667,-0.0828 1.12928,-0.77481 0.48263,-0.69194 0.32963,-1.31685 -0.14403,-0.62652 -0.9152,-1.16441 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path862" />
<path
d="m 401.80985,195.77451 1.06696,0.74418 -5.50036,7.886 -1.06697,-0.74418 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path859" />
<path
d="m 364.54235,199.2558 2.48963,-9.98593 1.11449,0.77735 -2.13253,8.38202 7.13456,-4.89318 1.10922,0.77366 -8.5058,5.78975 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path868" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.1889px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="423.34247"
y="-44.575214"
id="text30292"
transform="rotate(34.895179)"><tspan
sodipodi:role="line"
id="tspan30290"
x="423.34247"
y="-44.575214"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.1889px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.04247;stroke-miterlimit:4;stroke-dasharray:none">A</tspan></text>
<path
d="m 389.87343,211.89098 -2.0152,2.88924 1.71136,1.19366 q 0.86096,0.6005 1.51979,0.53397 0.6678,-0.0681 1.17988,-0.80233 0.51578,-0.73948 0.34165,-1.37913 -0.16516,-0.64125 -1.02611,-1.24176 z m 2.26204,-3.24314 -1.65784,2.37689 1.5793,1.10155 q 0.78174,0.54524 1.36467,0.52 0.59189,-0.0268 1.01187,-0.62901 0.41632,-0.59686 0.23704,-1.16158 -0.174,-0.56105 -0.95574,-1.1063 z m -0.4554,-1.62099 2.72549,1.90099 q 1.22014,0.85103 1.52671,1.8186 0.30658,0.96759 -0.34551,1.90251 -0.50472,0.72362 -1.14117,0.91568 -0.63646,0.19206 -1.36511,-0.15912 0.66912,0.71795 0.72647,1.5588 0.0662,0.83926 -0.49368,1.64213 -0.73681,1.0564 -1.85673,1.13109 -1.11991,0.0747 -2.4457,-0.85001 l -2.83113,-1.97467 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path856" />
<path
d="m 356.50627,207.03039 -2.0152,2.88924 1.71137,1.19366 q 0.86096,0.6005 1.51979,0.53397 0.66779,-0.0681 1.17988,-0.80233 0.51578,-0.73948 0.34165,-1.37913 -0.16516,-0.64125 -1.02612,-1.24176 z m 2.26204,-3.24314 -1.65784,2.37689 1.57931,1.10155 q 0.78173,0.54525 1.36466,0.52 0.5919,-0.0268 1.01188,-0.62901 0.4163,-0.59686 0.23703,-1.16158 -0.17399,-0.56105 -0.95573,-1.1063 z m -0.45539,-1.62099 2.72549,1.901 q 1.22014,0.85102 1.52671,1.81861 0.30658,0.96758 -0.34551,1.90249 -0.50472,0.72362 -1.14118,0.91568 -0.63645,0.19206 -1.3651,-0.15912 0.66912,0.71795 0.72647,1.55881 0.0662,0.83925 -0.49368,1.64212 -0.73682,1.05639 -1.85673,1.13109 -1.11992,0.0747 -2.4457,-0.85001 l -2.83114,-1.97467 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path871" />
<path
d="m 370.41237,210.97107 1.06695,0.74419 -4.87405,6.98805 3.83999,2.67834 -0.62629,0.89795 -4.90697,-3.42253 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path874" />
<path
d="m 383.47589,219.82112 4.98619,3.47779 -0.6263,0.89795 -3.91922,-2.73361 -1.62837,2.33464 3.75549,2.61939 -0.6263,0.89793 -3.75549,-2.61938 -1.9931,2.85754 4.01431,2.79992 -0.62631,0.89793 -5.08125,-3.5441 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04247;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="path877" />
<path
style="fill:none;stroke:#000000;stroke-width:0.0182944px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 389.96115,186.00939 -0.007,0.34731"
id="path30411"
inkscape:connector-curvature="0" />
</g>
<g
id="layer1"
transform="translate(0 77.953)">
<path
id="path3348"
d="m510.26 107.75-273.12 245.46-127.91 449.43 490.91 207.46 456.36-183.26-159.04-487.46-221.26-224.71 190.18-148.75c-88.433-9.4796-177.32-14.756-266.25-15.804-96.89-1.1424-193.84 2.7337-290.33 11.607l200.46 146.03z"
fill-rule="evenodd"
stroke="#000"
stroke-width="1.21px"
fill="url(#linearGradient5620)" />
<path
id="path4164"
stroke-linejoin="round"
d="m684.88 431.89c-6.34-58.38-23.22-115.59-49.58-168.06-29.85-59.42-71.88-112.69-122.72-155.55 22.722 2.9086 45.556 4.9435 68.435 6.0986 34.007 1.717 68.112 1.489 102.09-0.68242"
stroke="#b47f58"
stroke-linecap="round"
stroke-width="12.1"
fill="none" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -14,11 +14,24 @@ window.TrivabbleConf = {
// Max consecutive tries before blacklisting WebSockets for the current session
MAX_WEBSOCKET_ERRORS: 1,
// to tweak only if your webserver is shared with other conflicting resources at / ( ex: yunohost integration )
// To tweak only if your webserver is shared with other conflicting resources at / (e.g. Yunohost integration)
APP_PATH: "",
// Whether the spell checker is enabled (dictionaries must be downloaded on the server before enabling this option)
ENABLE_SPELL_CHECKER: false,
// The color of the flash light when double clicking on a cell
FLASH_LIGHT_COLOR: "#EE6633",
// The list of durations of the flash light available in the settings box
FLASH_LIGHT_DURATIONS: [800, 1600, 3200],
// The defaut flash light duration. If not set, the value at the middle of the previous array is used.
FLASH_LIGHT_DURATION: 1600,
// The list of durations used to detect a double tap available in the settings box
DOUBLE_TAP_DURATIONS: [650, 1100, 1800, 3000, 5000],
// The defaut double tap duration. If not set, the value at the middle of the previous array is used.
DOUBLE_TAP_DURATION: 1800,
// I don't like trailing commas, here is a nice message for you reading this file :-)
HAVE_FUN: true

View File

@ -9,13 +9,20 @@
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="format-detection" content="telephone=no" />
<script src="config.js"></script>
<script src="dict/list.js"></script>
<script src="l10n.js"></script>
<script src="alert.js"></script>
<script src="touch2click.js"></script>
<script src="trivabble.js"></script>
<link rel="icon" type="image/svg+xml" href="logo/path.svg" />
<link rel="icon" type="image/png" href="logo/16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="logo/32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="logo/86.png" sizes="86x86" />
<link rel="icon" type="image/png" href="logo/180.png" sizes="180x180" />
<link rel="icon" type="image/png" href="logo/192.png" sizes="192x192" />
</head>
<body>
<div id="menu">
<div id="menu" class="panel">
<button data-l10n="text-content" id="join-game">Join adversaries</button>
<p id="p-name"> <span data-l10n="text-content">You are:</span> <span id="name" data-l10n="text-content">(name to give)</span><br /><button class="minibutton" id="change-name" data-l10n="text-content">Change</button></p>
<button id="clear-game" data-l10n="text-content">Put back all the tiles&#10;in the bag</button>
@ -24,42 +31,72 @@
<textarea id="chat-ta" placeholder="Write a message to your adversaries here" data-l10n="placeholder"></textarea>
<button id="chat-btn" class="minibutton" data-l10n="text-content">Send message</button>
<div id="lang-selection">
<label>
<span data-l10n="text-content">Interface language: </span>
<p>
<label for="select-lang" data-l10n="text-content">Interface language: </label>
<select id="select-lang">
<option value="en">English</option>
<option value="fr">Français</option>
<option value="br">Brezhoneg</option>
<option value="ro">Română</option>
</select>
</label>
</div>
<div id="board-lang-selection" style="display:none">
<label>
<span data-l10n="text-content">Board language: </span>
</p>
<p id="board-lang-selection" hidden="true">
<label for="board-lang" data-l10n="text-content">Board language: </label>
<select id="board-lang">
<option value="fr">French</option>
</select>
</label>
</p>
</div>
</div>
<div class="modal" id="modal-settings-dialog">
<section class="modal" id="modal-settings-dialog">
<div class="modal-content">
<h2>
<span data-l10n="text-content">Settings</span>
<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 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>
<p><label><span data-l10n="text-content">(fast)</span><input type="range" min="0" max="0" id="double-tap-duration"/><span data-l10n="text-content">(slow)</span><br/><span data-l10n="text-content">Double tap duration</span></label></p>
<p><label><span data-l10n="text-content">(fast)</span><input type="range" min="0" max="0" id="flash-light-duration"/><span data-l10n="text-content">(slow)</span><br/><span data-l10n="text-content">Flash light duration</span></label></p>
<p><label><input type="text" size="6" id="flash-light-color"/> <span data-l10n="text-content">Flash light color</span></label></p>
</div>
<div class="modal-header">
<h1 data-l10n="text-content">Settings</h1>
<button class="modal-button" id="btn-settings-close">&#215;</button>
</div>
<div>
<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>
<p id="disable-spell-checker-p" hide="true">
<label>
<input type="checkbox" id="disable-spell-checker" />
<span data-l10n="text-content">Disable the spell checker</span>
</label>
</p>
</div>
<div>
<p>
<label for="double-tap-duration" data-l10n="text-content">Double tap duration:</label>
<span class="span-range">
<span data-l10n="text-content">(fast)</span>
<input type="range" min="0" max="0" id="double-tap-duration" />
<span data-l10n="text-content">(slow)</span><br/>
</span>
</p>
</div>
<div>
<p>
<label for="flash-light-duration" data-l10n="text-content">Flash light duration:</label>
<span class="span-range">
<span data-l10n="text-content">(fast)</span>
<input type="range" min="0" max="0" id="flash-light-duration" />
<span data-l10n="text-content">(slow)</span>
</span>
</p>
</div>
<div>
<p id="flash-light-color-p">
<label>
<input type="color" size="6" id="flash-light-color" />
<span data-l10n="text-content">Flash light color</span>
</label>
</p>
</div>
</div>
</div>
</section>
<div id="content">
<table id="board">
@ -70,21 +107,36 @@
</div>
</div>
<div id="panel">
<button id="btn-settings" data-l10n="text-content">Settings</button>
<div id="panel" class="panel">
<div id="btn-settings-wrapper">
<button id="btn-settings" data-l10n="text-content">Settings</button>
</div>
<div id="bag"></div>
<p><span data-l10n="text-content">Number of tiles in the bag:</span><br /><span id="remaining-letters">102</span><br /><span id="help-bag" data-l10n="text-content">Click on it to take one.</span><a href="#" id="help-clear" style="display:none"></a></p>
<p><span data-l10n="text-content">Number of tiles in the bag:</span><br /><span id="remaining-letters">102</span></p>
<table id="participants">
<tr><th data-l10n="text-content">Participant</th><th data-l10n="text-content">Rack</th><th data-l10n="text-content">Score</th><th data-l10n="text-content">Turn</th></tr>
<tr id="participants-placeholder"><td colspan="4" data-l10n="text-content">Waiting for other participants…</td></tr>
</table>
<p id="help-scores" data-l10n="text-content">Click on someone's score&#10;to change it.</p>
<p id="help-turn" data-l10n="text-content">Who's turn? Click on the Turn button!</p>
<button id="clear-rack" class="minibutton" data-l10n="text-content">Put back all the tiles of&#10;your rack in the bag</button>
<button id="show-rack" class="minibutton" data-l10n="text-content">Show my rack to other players</button>
<button id="check-spelling" class="minibutton" data-l10n="text-content" style="display:none">Check spelling for new words</button>
<button id="score-words" class="minibutton" data-l10n="text-content">Score new words</button>
<p id="info-spell-checking" style="display:none;"><span data-l0n="text-content">Spell checking is based on:</span> <a rel="noopener noreferrer" target="_blank" href="http://aspell.net">Aspell</a></p>
<div id="panel-buttons">
<button id="clear-rack" class="minibutton" data-l10n="text-content">Put back all the tiles of&#10;your rack in the bag</button>
<button id="show-rack" class="minibutton" data-l10n="text-content">Show my rack to other players</button>
<button id="check-spelling" class="minibutton" data-l10n="text-content" hidden="true">Check spelling for new words</button>
<button id="score-words" class="minibutton" data-l10n="text-content">Score new words</button>
</div>
<div id="help-box">
<p id="help-box-title" data-l10n="text-content">Tip!</p>
<div id="help-box-inner">
<div id="help-messages">
<p data-l10n="text-content" hidden="true">Click on the bag to take one tile.</p>
<p data-l10n="text-content" hidden="true">Click on someone's score&#10;to change it.</p>
<p data-l10n="text-content" hidden="true">Who's turn? Click on the Turn button!</p>
<p data-l10n="text-content" hidden="true">Click on (+) to increase someone's score.</p>
<p data-l10n="text-content" hidden="true">Show a cell to everyone by double-clicking on it.</p>
</div>
<button id="next-help-msg" class="minibutton" data-l10n="title" title="Next tip"></button>
</div>
</div>
<p id="info-spell-checking" hidden="true"><span data-l10n="text-content">Spell checking is based on:</span> <a rel="noopener noreferrer" target="_blank" href="http://aspell.net">Aspell</a></p>
</div>
</body>
</html>

BIN
public/logo/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

BIN
public/logo/180.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
public/logo/192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
public/logo/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/logo/86.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

212
public/logo/logo.svg Normal file
View File

@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="66.145836mm"
height="66.078644mm"
viewBox="0 0 66.145836 66.078644"
version="1.1"
id="svg33266"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
sodipodi:docname="logo.svg">
<defs
id="defs33260" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4826594"
inkscape:cx="216.83666"
inkscape:cy="216.09552"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:document-rotation="0"
inkscape:window-width="2560"
inkscape:window-height="1355"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<sodipodi:guide
position="64.454693,27.711101"
orientation="0,1"
id="guide33349"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata33263">
<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="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-27.05171,-93.927608)">
<g
id="g30409"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,308.75047,-7.1664876)">
<g
transform="matrix(0.87918095,0,0,0.87918095,-40.911317,12.434453)"
id="g30385">
<rect
style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ccccaa;stroke-width:3.89294624;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="rect30310-1"
width="48.301006"
height="48.242859"
x="-173.92935"
y="68.081581" />
<rect
style="opacity:1;fill:#eeee88;fill-opacity:1;fill-rule:nonzero;stroke:#aaaa55;stroke-width:1.75621903;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
id="rect30310"
width="49.229862"
height="49.229862"
x="-174.99074"
y="67.010086" />
</g>
<g
transform="translate(-98.735177,20.673278)"
style="fill:#000000"
id="g30330" />
</g>
<g
id="g892"
transform="translate(-3.8920143,1.8276076)">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-91.001099"
y="63.331829"
id="text30272"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30270"
x="-91.001099"
y="63.331829"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716">T</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-77.750885"
y="63.331829"
id="text30276"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
x="-77.750885"
y="63.331829"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716"
id="tspan30278">R</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-61.445587"
y="63.331829"
id="text30284"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30282"
x="-61.445587"
y="63.331829"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716">I</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-91.318954"
y="77.142586"
id="text30288"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30286"
x="-91.318954"
y="77.142586"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716">V</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-77.862938"
y="77.188324"
id="text30292"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30290"
x="-77.862938"
y="77.188324"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716">A</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-63.890118"
y="77.188324"
id="text30296"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30294"
x="-63.890118"
y="77.188324"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716">B</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-91.5522"
y="91.044815"
id="text30300"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30298"
x="-91.5522"
y="91.044815"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716">B</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-77.39415"
y="91.044815"
id="text30304"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30302"
x="-77.39415"
y="91.044815"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716"
rotate="0 0">L</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;line-height:1;font-family:LilyUPC;-inkscape-font-specification:'LilyUPC, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.31716;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
x="-63.451061"
y="91.044815"
id="text30308"
transform="matrix(1.4391919,0.00245906,-0.00245906,1.4391919,166.60077,22.343531)"><tspan
sodipodi:role="line"
id="tspan30306"
x="-63.451061"
y="91.044815"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.6683px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke-width:1.31716">E</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.0366029px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 91.191608,121.7174 -0.580184,0.38267"
id="path30411"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

1
public/logo/path.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -1,3 +1,7 @@
:root {
--flash-light-color: #ee6633;
}
#board {
border-collapse:collapse;
}
@ -47,7 +51,7 @@ html, #board, [draggable], .tile {
color: green;
}
#clear-game, #help-scores, #clear-rack, #help-turn {
#clear-game, #clear-rack {
white-space:pre-wrap;
}
@ -57,7 +61,7 @@ html, #board, [draggable], .tile {
-webkit-user-drag: element;
}
#help-clear, #help-bag, #help-scores, #help-turn {
#help-message {
font-size:small
}
@ -75,6 +79,9 @@ html, #board, [draggable], .tile {
width: 100%;
top: 0px;
left: 0px;
display: -webkit-flex;
-webkit-justify-content: center;
-webkit-flex-direction: column;
display: flex;
justify-content: center;
flex-direction: column;
@ -141,22 +148,29 @@ body {
display:-webkit-flex;
display:flex;
-webkit-flex-direction:column;
flex-direction:column
flex-direction:column;
}
button {
border-radius: 2px;
}
.panel button {
margin: 0.25em 0;
min-height:3em;
background:#AFA;
border:1px solid gray;
background: #CFC;
border: 1px solid #8C8;
cursor: pointer;
transition: 0.2s;
}
button:hover {
background:#FD7;
cursor:pointer
.panel button:hover {
background: #FD7;
border: 1px solid #CA5;
}
button[disabled=disabled], button:disabled {
.panel button[disabled=disabled], .panel button:disabled {
background:#AFA;
cursor:not-allowed;
}
@ -223,15 +237,16 @@ button[disabled=disabled], button:disabled {
}
}
.laser-highlighting .tile {
.flash-light-highlighting .tile {
transition:none;
}
.laser-highlight {
background-color: #E3E;
.flash-light-highlight {
background-color: #ee6633;
background-color: var(--flash-light-color);
}
.laser-highlight .tile:not(.tile-highlight) {
.flash-light-highlight .tile:not(.tile-highlight) {
background-color: inherit;
}
@ -334,9 +349,9 @@ td.blink {
}
#panel {
-webkit-justify-content:center;
justify-content:center;
text-align:center
/* -webkit-justify-content:center; does not look good */
justify-content:space-between;
text-align:center;
}
#bag, #rack-outer {
@ -350,8 +365,8 @@ td.blink {
font-size:small
}
.minibutton {
padding:0;
.panel .minibutton {
padding:0.25ex 0.5ex;
min-height:0;
font-size:small
}
@ -369,7 +384,6 @@ td.blink {
height:160px;
background:url(bag.svg) no-repeat;
background-size:100% 100%;
flex:1;
}
#bag:hover {
@ -407,12 +421,13 @@ td.blink {
min-width:2.5em;
}
#participants .score-cell > span:hover, .turn-cell button:hover {
#participants .score-cell > span:hover, #participants .turn-cell button:hover {
cursor:pointer;
background:#FFA
}
#participants .score-cell button {
min-height:4ex; /* for old webkit */
min-height: auto;
font-size:x-small;
padding:0 1ex;
@ -436,7 +451,7 @@ td.blink {
font-style:italic;
}
.turn-cell button {
#participants .turn-cell button {
padding:0 2px;
background:white;
min-height:1em;
@ -445,7 +460,7 @@ td.blink {
display:flex;
}
.turn-cell img {
#participants .turn-cell img {
width:1em;
vertical-align: middle;
}
@ -469,22 +484,41 @@ td.blink {
}
}
#prefs {
margin:1ex 0 0 0;
}
#prefs p {
#modal-settings-dialog p {
margin:0;
padding:0;
font-size:small
}
#lang-selection, #board-lang-selection {
font-size:small;
}
#prefs input, #prefs label {
vertical-align:middle
#modal-settings-dialog label span, #modal-settings-dialog label input {
display: inline-block;
vertical-align: middle;
}
#lang-selection {
display:table;
}
#lang-selection p {
display:table-row;
}
#lang-selection p:first-child select {
margin-bottom:0.5ex;
}
#lang-selection select, #lang-selection label {
display:table-cell;
vertical-align:middle;
font-size:small;
}
#lang-selection select {
width:100%;
}
#lang-selection label {
padding-right:0.5ex;
}
.modal {
@ -496,10 +530,21 @@ td.blink {
background-color: rgba(0, 0, 0, 0.5);
opacity: 0;
visibility: hidden;
-webkit-transition: visibility 0s linear 0.25s, opacity 0.25s 0s;
transform: scale(1.1);
transition: visibility 0s linear 0.25s, opacity 0.25s 0s, transform 0.25s;
z-index: 1;
}
.modal-header {
display:flex;
flex-direction:row;
}
.modal-header h1 {
flex:1;
}
.modal-content {
position: absolute;
top: 50%;
@ -510,11 +555,19 @@ td.blink {
width: 24rem;
border-radius: 0.3rem;
}
.modal-content h2 {
#btn-settings-close {
font-weight: bold;
text-align: center;
border: 0;
}
.modal-content h1 {
margin: 0px;
font-weight: bold;
font-size: large;
}
.modal-button {
float: right;
width: 1.5rem;
@ -532,24 +585,48 @@ td.blink {
.show-modal {
opacity: 1;
visibility: visible;
-webkit-transition: visibility 0s linear 0s, opacity 0.25s 0s;
transform: scale(1.0);
transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s;
}
.modal-content > div:not(:last-child) {
margin-bottom: 1ex;
}
.span-range {
display:block;
text-align:center;
}
.span-range > span {
font-size:smaller;
}
.span-range > span, .span-range > input {
display:inline-block;
vertical-align: middle;
}
.alert {
z-index: 2;
}
#btn-settings {
#panel #btn-settings {
background:lightgray;
color:black;
font-size:small;
border-radius:3px;
border:none;
margin:0 0 0 auto;
margin:0;
min-height: 2em;
}
#panel #btn-settings-wrapper {
text-align:right;
}
#btn-settings:before {
content: "⚙️ "
}
@ -562,3 +639,92 @@ td.blink {
#info-spell-checking a {
color:black;
}
#help-messages {
box-sizing:border-box;
overflow:auto;
width: auto;
margin-bottom:1px;
}
#help-box {
margin-left:1ex;
margin-top:1ex;
display:-webkit-flex;
-webkit-flex-direction:column;
-webkit-justify-content:center;
-webkit-align-items:center;
align-items:center;
display:flex;
flex-direction:column;
justify-content:center;
}
#help-box-inner {
display:-webkit-flex;
-webkit-flex-direction:row;
display:flex;
flex-direction:row;
min-height:2em;
border-top:1px solid gray;
border-bottom:1px solid gray;
-webkit-align-items:center;
align-items:center;
}
button#next-help-msg {
border:0;
background:none;
color:#333;
font-size:1.5rem;
margin-right:0.5ex;
background:#F1F1F1;
border-radius:100rem;
height:1.5em;
width:1.5em;
font-weight:bold;
transition:500ms;
padding:0;
}
button#next-help-msg:hover {
background:#CCC;
}
#help-box-title {
margin:0;
padding:0 0 0.25ex 0;
text-align:right;
width:100%;
color:gray;
font-size:small;
font-style:italic;
}
#help-messages p {
margin:0;
font-size:0.9;
color:#222;
padding:0 0.5ex;
max-width:17em;
}
#help-box-inner:before {
content:"i";
display:inline-block;
height:1.2em;
width:1.2em;
font-style:italic;
font-weight:bold;
font-family:serif;
color:#AAA;
border:2px solid;
border-radius:100rem;
}
#panel-buttons {
display:-webkit-flex;
-webkit-flex-direction: column;
display:flex;
flex-direction:column;
}

View File

@ -31,21 +31,117 @@
const Conf = window.TrivabbleConf || {};
const DictionaryList = window.DictionaryList || {};
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]);
function middle(key) {
const setting = getSetting(key);
return setting[Math.floor(setting.length / 2)];
}
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("DOUBLE_TAP_DURATIONS", [650, 1100, 1800, 3000, 5000]);
setConf("DOUBLE_TAP_DURATION", middle("DOUBLE_TAP_DURATIONS"));
setConf("FLASH_LIGHT_DURATIONS", [800, 1600, 3200]);
setConf("FLASH_LIGHT_COLOR", "#E3E");
setConf("FLASH_LIGHT_DURATION", middle("FLASH_LIGHT_DURATIONS"));
setConf("FLASH_LIGHT_COLOR", "#ee6633");
function getSetting(key) {
let type;
let value;
if (Object.prototype.hasOwnProperty.call(Conf, key)) {
// get default value from configuration
value = Conf[key];
type = typeof value;
} else if (Object.prototype.hasOwnProperty.call(SettingsTypes, key)) {
type = SettingsTypes[key];
}
/* try to retrieve value from localstorage */
if (Object.prototype.hasOwnProperty.call(localStorage, "trivabble" + key)) {
value = localStorage.getItem("trivabble" + key);
/* get type from localStorage if no default is set */
if (typeof type === "undefined") {
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 unsetSetting(key) {
localStorage.removeItem("trivabble" + key);
}
function setSetting(key, value) {
if (getSetting(key) === value) {
return;
}
let type;
/* try to retrieve type from configuration */
if (Object.prototype.hasOwnProperty.call(SettingsTypes, key)) {
type = SettingsTypes[key];
} else if (Object.prototype.hasOwnProperty.call(Conf, key)) {
type = typeof Conf[key];
}
/* 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("trivabble" + key, value);
} else {
console.error("Unsupported type");
}
} else {
console.error("Incoherent or missing type. See the SettingsTypes object.");
}
}
const SettingsTypes = {
SpellCheckerEnabledOnce: "boolean",
DisableSpellChecker: "boolean",
GameNumber: "number",
BoardLang: "string",
Lang: "string",
PlayerName: "string"
};
const _ = (window.libD && libD.l10n) ? libD.l10n() : function (s) {
return s;
@ -72,8 +168,6 @@
let audioChat;
let chatMessages;
let chatTextarea;
let helpBag;
let helpClear;
let tablePlayers = {};
let participantPlaceholder;
@ -154,6 +248,20 @@
}
}
function checkDictionaryExistance() {
const code = document.getElementById("board-lang").value;
const availableLang = Object.prototype.hasOwnProperty.call(DictionaryList, code);
document.getElementById("disable-spell-checker-p").hidden = !availableLang;
if (availableLang && !document.getElementById("disable-spell-checker").checked) {
document.getElementById("check-spelling").hidden = false;
document.getElementById("info-spell-checking").hidden = false;
} else {
document.getElementById("check-spelling").hidden = true;
document.getElementById("info-spell-checking").hidden = true;
}
}
function getDictionary(code, callback, force) {
if (downloadedDictionaries[code]) {
if (downloadedDictionaries[code].length) {
@ -167,11 +275,11 @@
return;
}
if (!force && localStorage.spellCheckerEnabled !== "true") {
if (!force && !getSetting("SpellCheckerEnabledOnce")) {
myConfirm(
_("Spell checking requires Trivabble to download a dictionary. Do you confirm?"),
function () {
localStorage.spellCheckerEnabled = "true";
setSetting("SpellCheckerEnabledOnce", true);
getDictionary(code, callback, true);
}
);
@ -204,11 +312,6 @@
file.send();
}
function spellCheckerSettingChecked() {
localStorage.spellCheckerEnabled = document.getElementById("enable-spell-checker").checked.toString();
initSpellChecker();
}
function removeElem(elem) {
elem.parentNode.removeChild(elem);
}
@ -524,33 +627,32 @@
rackBCR = rack.getBoundingClientRect();
bagBCR = bag.getBoundingClientRect();
let from;
let from = null;
let index;
let p = movingTile.parentNode;
let oldP = movingTile;
let oldOldP = null;
let p = movingTile;
while (p) {
if (p === board) {
while (p && !from) {
index = boardCells.indexOf(p);
if (index !== -1) {
from = "board";
index = boardCells.indexOf(oldOldP);
break;
}
if (p === rack) {
index = playerLetters.indexOf(p);
if (index !== -1) {
from = "rack";
index = playerLetters.indexOf(oldP);
break;
}
oldOldP = oldP;
oldP = p;
p = p.parentNode;
}
if (!from) {
fatalError(new Error("Error: did not find the parent of the moving tile"));
return;
}
moveCMD = {
cmd: "moveLetter",
from: from,
@ -566,7 +668,7 @@
tile.lastChild.textContent = scoreOf[letter] || "";
if (highlight) {
tile.classList.add("tile-highlight");
if (tilesSound.checked) {
if (getSetting("ENABLE_TILE_SOUND")) {
audioNotification.play();
}
} else {
@ -581,7 +683,7 @@
}
function touchStartTilePreventDefault(e) {
laserTouch(e);
flashLightTouch(e);
return preventDefault(e);
}
@ -633,14 +735,17 @@
function set(key, value) {
switch (key) {
case "playerName":
name.textContent = localStorage.trivabblePlayerName = value;
name.textContent = value;
setSetting("PlayerName", value);
break;
case "gameNumber":
document.getElementById("number").textContent = localStorage.trivabbleGameNumber = value;
document.getElementById("number").textContent = value;
setSetting("GameNumber", value);
break;
case "boardLang":
localStorage.trivabbleBoardLang = value;
document.getElementById("board-lang").value = value;
setSetting("BoardLang", value);
checkDictionaryExistance(value);
break;
}
}
@ -656,7 +761,7 @@
myAlert(
format(
_("You are about to leave the current game. To recover it, please note its number: {0}"),
localStorage.trivabbleGameNumber
getSetting("GameNumber")
),
f
);
@ -714,6 +819,7 @@
}
function chatMessage(sender, content) {
chatMessages.style.width = chatTextarea.offsetWidth + "px"; // HACK to prevent the chat box from getting too big
const msgDom = document.createElement("div");
msgDom.className = "msg";
@ -736,8 +842,8 @@
chatMessages.scrollTop = chatMessages.scrollHeight;
if (sender && sender !== localStorage.trivabblePlayerName) {
if (msgSound.checked) {
if (sender && sender !== getSetting("PlayerName")) {
if (getSetting("ENABLE_MSG_SOUND")) {
audioChat.play();
}
@ -751,7 +857,7 @@
const content = document.createElement("div");
content.appendChild(document.createElement("span"));
content.lastChild.className = "info";
content.lastChild.textContent = info;
content.lastChild.textContent = info || "";
chatMessage("", content);
return content;
}
@ -776,7 +882,7 @@
case "changeBoardLang": {
const newLang = boardLangSelect.querySelector("[value=" + msg.specialMsg.newBoardLang + "]").textContent;
infoMessage(
(msg.sender === localStorage.trivabblePlayerName)
(msg.sender === getSetting("PlayerName"))
? format(_("You changed the language of the board to {0}"), newLang)
: format(_("{0} changed the language of the board to {1}"), msg.sender, newLang)
);
@ -963,13 +1069,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("PlayerName"));
} 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("PlayerName"));
if ((data.letter !== "") && (currentTilePlayed[data.indexTo] === "-")) {
currentTilePlayed[data.indexTo] = data.letter;
}
@ -1031,7 +1137,7 @@
}
if (data.gameNumber) {
set("gameNumber", data.gameNumber);
set("gameNumber", Number(data.gameNumber));
}
if (data.availableBoardLangs) {
@ -1054,13 +1160,6 @@
if (typeof data.remainingLetters === "number") {
remainingLetters = data.remainingLetters;
document.getElementById("remaining-letters").textContent = data.remainingLetters;
if (data.remainingLetters === 0) {
helpBag.style.display = "none";
helpClear.style.display = "";
} else {
helpBag.style.display = "";
helpClear.style.display = "none";
}
}
if (data.rack) {
@ -1099,7 +1198,7 @@
startConnection();
retryPollingTimeout = 0;
},
delay || Conf.POLLING_DELAY
delay || getSetting("POLLING_DELAY")
);
} else {
retriedImmediately = true;
@ -1176,7 +1275,7 @@
} else {
webSocketErrors++;
if (webSocketErrors > Conf.MAX_WEBSOCKET_ERRORS) {
if (webSocketErrors > getSetting("MAX_WEBSOCKET_ERRORS")) {
blacklistWebsockets = true;
}
}
@ -1203,7 +1302,7 @@
}
function pollServerWithEventSource() {
if (canConnect() && Conf.ENABLE_EVENT_SOURCE && window.EventSource) {
if (canConnect() && getSetting("ENABLE_EVENT_SOURCE") && window.EventSource) {
closeConnections();
pollingServer = true;
@ -1216,7 +1315,7 @@
}
function pollServerWithWebSocket() {
if (canConnect() && Conf.ENABLE_WEBSOCKETS && !blacklistWebsockets && window.WebSocket) {
if (canConnect() && getSetting("ENABLE_WEBSOCKETS") && !blacklistWebsockets && window.WebSocket) {
closeConnections();
pollingServer = true;
@ -1318,7 +1417,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;
}
@ -1335,9 +1434,9 @@
function cmdsWithContext(cmds) {
return {
gameNumber: localStorage.trivabbleGameNumber || "",
playerName: localStorage.trivabblePlayerName,
boardLang: localStorage.trivabbleBoardLang,
gameNumber: getSetting("GameNumber") || "",
playerName: getSetting("PlayerName"),
boardLang: getSetting("BoardLang"),
version: VERSION,
cmds: cmds
};
@ -1358,7 +1457,7 @@
if (!pollingReady) {
startConnection();
setTimeout(sendCmds, Conf.POLLING_DELAY, cmds);
setTimeout(sendCmds, getSetting("POLLING_DELAY"), cmds);
return;
}
@ -1369,13 +1468,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("GameNumber")),
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("GameNumber", n);
location.reload();
}
}
@ -1404,33 +1503,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("PlayerName", newName.trim());
name.textContent = getSetting("PlayerName");
}
},
localStorage.trivabblePlayerName
getSetting("PlayerNamer")
);
}
function startGame(number) {
if (number) {
localStorage.trivabbleGameNumber = number;
setSetting("GameNumber", 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();
}
@ -1484,7 +1581,7 @@
sendCmds([{cmd: "changeBoard", lang: code}]);
},
function () {
boardLangSelect.value = localStorage.trivabbleBoardLang;
boardLangSelect.value = getSetting("BoardLang");
}
);
}
@ -1493,15 +1590,21 @@
myConfirm(
_("Are you sure you want to put all your tiles back in the bag?"),
function () {
sendCmds([
{cmd: "moveLetter", from: "rack", indexFrom: 0, to: "bag", indexTo: -1},
{cmd: "moveLetter", from: "rack", indexFrom: 1, to: "bag", indexTo: -1},
{cmd: "moveLetter", from: "rack", indexFrom: 2, to: "bag", indexTo: -1},
{cmd: "moveLetter", from: "rack", indexFrom: 3, to: "bag", indexTo: -1},
{cmd: "moveLetter", from: "rack", indexFrom: 4, to: "bag", indexTo: -1},
{cmd: "moveLetter", from: "rack", indexFrom: 5, to: "bag", indexTo: -1},
{cmd: "moveLetter", from: "rack", indexFrom: 6, to: "bag", indexTo: -1}
]);
const cmds = [];
for (let i = 0; i < playerLetters.length; i++) {
if (playerLetters[i].getElementsByClassName("tile")[0]) {
cmds.push({
cmd: "moveLetter",
from: "rack",
indexFrom: i,
to: "bag",
indexTo: -1
});
}
}
sendCmds(cmds);
setRack("");
}
);
@ -1668,11 +1771,11 @@
}
function checkSpellingClicked() {
if (localStorage.spellCheckerEnabled === "false") {
if (getSetting("SpellCheckerEnabledOnce") === "false") {
return;
}
getDictionary(localStorage.trivabbleBoardLang, checkSpelling);
getDictionary(getSetting("BoardLang"), checkSpelling);
}
function checkSpelling(dictionary) {
@ -1719,6 +1822,11 @@
}
}
function getRowIndex(tr) {
// tr.rowIndex is -1 on WebKit-based browsers in Debian Jessie, hence this function
return [].indexOf.call(tr.parentNode.rows, tr);
}
function scoreWords() {
let totalScore = 0;
@ -1781,14 +1889,14 @@
);
}
function triggerLaser(cell) {
function triggerFlashLight(cell) {
if (!cell) {
return;
}
const letters = "ABCDEFGHIJKLMNO";
const col = cell.cellIndex - 1;
const row = cell.parentNode.rowIndex - 1;
const row = getRowIndex(cell.parentNode) - 1;
sendCmds([{
cmd: "msg",
@ -1804,7 +1912,7 @@
const tilePlaceholder = getParentWithClass(e.target, "tile-placeholder");
if (tilePlaceholder) {
const cell = tilePlaceholder.parentNode;
if (cell.cellIndex && cell.parentNode.rowIndex) {
if (cell.cellIndex && getRowIndex(cell.parentNode)) {
return cell;
}
}
@ -1812,39 +1920,39 @@
return null;
}
function laserDblClick(e) {
triggerLaser(getCellFromEvent(e));
function flashLightDblClick(e) {
triggerFlashLight(getCellFromEvent(e));
}
function laserTouch(e) {
function flashLightTouch(e) {
const cell = getCellFromEvent(e);
if (!cell) {
return;
}
if ((laserTouch.last !== cell) || (Date.now() - laserTouch.date > localStorage.doubleTapDuration)) {
laserTouch.last = cell;
laserTouch.date = Date.now();
if ((flashLightTouch.last !== cell) || (Date.now() - flashLightTouch.date > getSetting("DOUBLE_TAP_DURATION"))) {
flashLightTouch.last = cell;
flashLightTouch.date = Date.now();
return;
}
triggerLaser(cell);
triggerFlashLight(cell);
}
function localHighlightCell(id) {
const placeholder = boardCells[id].getElementsByClassName("tile-placeholder")[0];
placeholder.classList.add("laser-highlighting");
placeholder.classList.add("laser-highlight");
placeholder.classList.add("flash-light-highlighting");
placeholder.classList.add("flash-light-highlight");
const sixthFlashLightDuration = Number(localStorage.flashLightDuration) / 6;
const sixthFlashLightDuration = getSetting("FLASH_LIGHT_DURATION") / 6;
function on() {
placeholder.classList.add("laser-highlight");
placeholder.classList.add("flash-light-highlight");
}
function off() {
placeholder.classList.remove("laser-highlight");
placeholder.classList.remove("flash-light-highlight");
}
setTimeout(off, sixthFlashLightDuration);
@ -1855,7 +1963,7 @@
setTimeout(
function () {
placeholder.classList.remove("laser-highlighting");
placeholder.classList.remove("flash-light-highlighting");
},
sixthFlashLightDuration * 6
);
@ -1916,58 +2024,66 @@
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("TilesSound");
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("MsgSound");
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 toggleSpellChecker(e) {
const disabled = document.getElementById("disable-spell-checker").checked;
checkDictionaryExistance();
if (e) {
setSetting("DisableSpellChecker", disabled);
}
if (disabled) {
unsetSetting("SpellCheckerEnabledOnce");
}
}
function initSpellChecker() {
if (Conf.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") {
document.getElementById("check-spelling").style.display = "none";
document.getElementById("enable-spell-checker").checked = false;
document.getElementById("info-spell-checking").style.display = "none";
} else {
document.getElementById("check-spelling").style.display = "";
document.getElementById("info-spell-checking").style.display = "";
document.getElementById("enable-spell-checker").checked = true;
}
document.getElementById("disable-spell-checker").checked = getSetting("DisableSpellChecker");
toggleSpellChecker();
}
function translateDuration(x, values) {
if (x < 0) {
return null;
}
if (x < values.length) {
return values[Math.floor(x)];
}
for (let i = 0; i < values.length; i++) {
if (x <= values[i]) {
return i;
@ -1976,65 +2092,80 @@
return values.length - 1;
}
function initLaserPointer() {
document.body.addEventListener("dblclick", laserDblClick);
document.body.addEventListener("touchstart", laserTouch);
function initFlashLight() {
document.body.addEventListener("dblclick", flashLightDblClick);
document.body.addEventListener("touchstart", flashLightTouch);
if (!Object.prototype.hasOwnProperty.call(localStorage, "doubleTapDuration")) {
localStorage.doubleTapDuration = Conf.DOUBLE_TAP_DURATIONS[Math.floor(Conf.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("DOUBLE_TAP_DURATION"), getSetting("DOUBLE_TAP_DURATIONS"));
doubleTapDuration.onchange = function () {
localStorage.doubleTapDuration = translateDuration(
setSetting("DOUBLE_TAP_DURATION", 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)];
}
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("FLASH_LIGHT_DURATION"), getSetting("FLASH_LIGHT_DURATIONS"));
flashLightDuration.onchange = function () {
localStorage.flashLightDuration = translateDuration(
setSetting("FLASH_LIGHT_DURATION", 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;
// thx https://stackoverflow.com/a/26633844
if (!(window.CSS && CSS.supports && CSS.supports("color", "var(--v)")) && document.getElementById("flash-light-color-p")) {
document.getElementById("flash-light-color-p").hidden = true;
return;
}
const flashLightColor = document.getElementById("flash-light-color");
if (flashLightColor) {
flashLightColor.value = localStorage.flashLightColor;
flashLightColor.value = getSetting("FLASH_LIGHT_COLOR");
flashLightColor.onchange = function () {
localStorage.flashLightColor = document.getElementById("flash-light-color").value;
const style = document.createElement("style");
style.textContent = ".laser-highlight {background-color: " + localStorage.flashLightColor + ";}";
document.body.appendChild(style);
const color = document.getElementById("flash-light-color").value;
setSetting("FLASH_LIGHT_COLOR", color);
document.documentElement.style.setProperty("--flash-light-color", color);
};
flashLightColor.onchange();
}
}
function nextHelpMessage() {
if (typeof nextHelpMessage.index === "undefined") {
nextHelpMessage.index = -1;
}
const helpMessages = document.getElementById("help-messages");
if (helpMessages) {
const listOfMessages = helpMessages.getElementsByTagName("p");
if ((nextHelpMessage.index >= 0) && (nextHelpMessage.index < listOfMessages.length)) {
listOfMessages[nextHelpMessage.index].hidden = true;
}
nextHelpMessage.index++;
if (nextHelpMessage.index === listOfMessages.length) {
nextHelpMessage.index = 0;
}
listOfMessages[nextHelpMessage.index].hidden = false;
}
}
function repromptName(f) {
if (localStorage.trivabblePlayerName && localStorage.trivabblePlayerName.trim()) {
if (getSetting("PlayerName") && getSetting("PlayerName").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("PlayerName", name.trim());
}
repromptName(f);
@ -2062,7 +2193,7 @@
if (!boardLangSelect) {
const boardLangSelection = document.getElementById("board-lang-selection");
boardLangSelect = boardLangSelection.querySelector("select");
boardLangSelection.style.display = "";
boardLangSelection.hidden = false;
}
boardLangSelect.textContent = "";
@ -2083,17 +2214,22 @@
);
});
boardLangSelect.value = localStorage.trivabbleBoardLang;
boardLangSelect.value = getSetting("BoardLang");
}
function langSelectionChange(e) {
localStorage.trivabbleLang = e.target.value;
setSetting("Lang", e.target.value);
location.reload();
}
function showSettings() {
const modal = document.querySelector(".modal");
modal.classList.toggle("show-modal");
modal.classList.add("show-modal");
modal.querySelector("button").focus();
}
function hideSettings() {
document.querySelector(".modal").classList.remove("show-modal");
}
function initGlobals() {
@ -2104,8 +2240,6 @@
bag = document.getElementById("bag");
chatMessages = document.getElementById("chat-messages");
chatTextarea = document.getElementById("chat-ta");
helpBag = document.getElementById("help-bag");
helpClear = document.getElementById("help-clear");
participantPlaceholder = document.getElementById("participants-placeholder");
}
@ -2119,20 +2253,25 @@
document.getElementById("clear-rack").onclick = clearRack;
document.getElementById("show-rack").onclick = showRack;
document.getElementById("check-spelling").onclick = checkSpellingClicked;
document.getElementById("score-words").onclick = scoreWords;
helpClear.onclick = clearGame;
document.getElementById("btn-settings").onclick = showSettings;
document.getElementById("btn-settings-close").onclick = showSettings;
document.getElementById("btn-settings-close").onclick = hideSettings;
document.getElementById("next-help-msg").onclick = nextHelpMessage;
document.getElementById("disable-spell-checker").onclick = toggleSpellChecker;
document.getElementById("score-words").onclick = scoreWords;
window.addEventListener("keydown", function (e) {
if (e.key === "Escape") {
document.querySelector(".modal").classList.remove("show-modal");
}
});
}
function initGame() {
if (!localStorage.trivabblePlayerName) {
if (!getSetting("PlayerName")) {
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("PlayerName", name);
}
repromptName(initGame);
}
@ -2141,7 +2280,7 @@
return;
}
name.textContent = localStorage.trivabblePlayerName;
name.textContent = getSetting("PlayerName");
const letters = "ABCDEFGHIJKLMNO";
@ -2160,6 +2299,7 @@
};
let cell;
let row;
for (let i = 0; i < 7; i++) {
const span = document.createElement("span");
@ -2172,63 +2312,63 @@
board.rows[0].appendChild(document.createElement("th"));
board.rows[0].lastChild.textContent = i + 1;
board.appendChild(document.createElement("tr"));
board.lastChild.appendChild(document.createElement("th"));
board.lastChild.lastChild.textContent = letters[i];
row = board.insertRow(-1);
row.appendChild(document.createElement("th"));
row.lastChild.textContent = letters[i];
for (let j = 0; j < 15; j++) {
cell = document.createElement("td");
boardCells.push(cell);
board.lastChild.appendChild(cell);
row.appendChild(cell);
cell.appendChild(document.createElement("div"));
cell.lastChild.className = "tile-placeholder";
if (i === j && i === 7) {
specialCell("doubleWord", board.lastChild.lastChild);
cell = board.lastChild.lastChild.getElementsByClassName("special-cell-label")[0];
specialCell("doubleWord", row.lastChild);
cell = row.lastChild.getElementsByClassName("special-cell-label")[0];
cell.textContent = "★";
board.lastChild.lastChild.id = "center-cell";
row.lastChild.id = "center-cell";
} else if (i % 7 === 0 && j % 7 === 0) {
specialCell("tripleWord", board.lastChild.lastChild);
specialCell("tripleWord", row.lastChild);
} else if ((i === j || i + j === 14) && (i < 5 || i > 9)) {
specialCell("doubleWord", board.lastChild.lastChild);
specialCell("doubleWord", row.lastChild);
} else if ((i % 4 === 1) && (j % 4 === 1)) {
specialCell("tripleLetter", board.lastChild.lastChild);
specialCell("tripleLetter", row.lastChild);
} else if ((i < 8 && doubleLetter[i + "," + j]) || (i > 7 && doubleLetter[(14 - i) + "," + j]) || (i === 7 && (j === 3 || j === 11))) {
specialCell("doubleLetter", board.lastChild.lastChild);
specialCell("doubleLetter", row.lastChild);
}
}
board.lastChild.appendChild(document.createElement("th"));
board.lastChild.lastChild.textContent = letters[i];
row.appendChild(document.createElement("th"));
row.lastChild.textContent = letters[i];
}
board.rows[0].appendChild(board.rows[0].cells[0].cloneNode(false));
board.appendChild(document.createElement("tr"));
board.lastChild.appendChild(board.rows[0].cells[0].cloneNode(false));
row = board.insertRow(-1);
row.appendChild(board.rows[0].cells[0].cloneNode(false));
for (let i = 0; i < 15; i++) {
board.lastChild.appendChild(document.createElement("th"));
board.lastChild.lastChild.textContent = i + 1;
row.appendChild(document.createElement("th"));
row.lastChild.textContent = i + 1;
}
board.lastChild.appendChild(board.rows[0].cells[0].cloneNode(false));
row.appendChild(board.rows[0].cells[0].cloneNode(false));
if (localStorage.trivabbleGameNumber) {
document.getElementById("number").textContent = localStorage.trivabbleGameNumber;
if (getSetting("GameNumber")) {
document.getElementById("number").textContent = getSetting("GameNumber");
}
if (localStorage.trivabbleBoardLang) {
document.getElementById("board-lang").value = localStorage.trivabbleBoardLang;
if (getSetting("BoardLang")) {
document.getElementById("board-lang").value = getSetting("BoardLang");
}
startGame(localStorage.trivabbleGameNumber);
startGame(getSetting("GameNumber"));
}
function initLang() {
const lang = libD.lang = localStorage.trivabbleLang || libD.lang;
const lang = libD.lang = getSetting("Lang") || libD.lang;
const langSel = document.getElementById("select-lang");
langSel.value = lang;
@ -2247,7 +2387,8 @@
initGame();
initSound();
initSpellChecker();
initLaserPointer();
initFlashLight();
nextHelpMessage();
};
trivabble.l10nError = trivabble.run;

View File

@ -3,7 +3,7 @@
"name": "Ukrainian",
"bag": [
"Jokers", "Jokers",
" ", " ",
"О", "О", "О", "О", "О", "О", "О", "О", "О",
"А", "А", "А", "А", "А", "А", "А", "А",
"И", "И", "И", "И", "И", "И",
@ -41,7 +41,7 @@
],
"letterValues": {
"Jokers": 0,
" ": 0,
"О": 1,
"А": 1,
"И": 1,

View File

@ -29,6 +29,7 @@
const port = parseInt(process.env.TRIVABBLE_PORT || "3000");
const host = process.env.TRIVABBLE_HOST || "localhost";
const disableCSP = process.env.DISABLE_CSP || false;
const SAVE_TIMEOUT = 5000;
const KEEP_ALIVE = 30000;
@ -445,9 +446,12 @@ function countTiles(rack) {
return count;
}
function joinGame(gameNumber) {
function joinGame(gameNumber, message) {
if (!gameNumber) {
gameNumber = newGameId();
if (message) {
message.gameNumber = gameNumber;
}
}
const game = games[gameNumber] || (games[gameNumber] = new Game());
@ -718,7 +722,7 @@ function handleCommand(cmdNumber, message, response) {
function handleCommands(message, responseAndType) {
if (!message.cmds || !message.cmds.length) {
const {gameNumber, game} = joinGame(message.gameNumber);
const {gameNumber, game} = joinGame(message.gameNumber, message);
writeMessage(responseAndType,
JSON.stringify({
@ -998,6 +1002,24 @@ function handleRequest(request, response) {
}
response.setHeader("Content-Type", mime);
response.setHeader(
"Content-Security-Policy",
"default-src 'none'; " +
"script-src 'self'; " +
"style-src 'self'; " +
"img-src 'self'; " +
(disableCSP ? "" : "connect-src 'self' " + (
// uzbl (like Safari 9 / iPad 2) does not like insecure websockets on the
// same port with connect-src 'self'
// See https://github.com/w3c/webappsec-csp/issues/7
"ws://" + host + ":" + port + " " +
"ws://127.0.0.1:" + port + " " +
"ws://localhost:" + port + "; "
)) +
"media-src 'self'"
);
response.end(contents);
});
} else {