Merge branch 'develop' into laser_pointer

This commit is contained in:
Laurent Mazet 2020-08-30 21:24:02 +02:00
commit 344bae18a2
10 changed files with 1009 additions and 41 deletions

1
.gitignore vendored
View File

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

View File

@ -157,3 +157,16 @@ A full example is available in (doc/trivabble-apache-host.conf.sample)[doc/triva
: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.
## 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:
```sh
cd dict-dists-extractor
make
```
Then, set `ENABLE_CHECK_SPELLING` to `true` in `public/config.js`.

View File

@ -0,0 +1,597 @@
# -*- Makefile -*-
ROOT_DICT = ../public/dict
OBJ_DIR = obj
# 0MB < dictionary size < 10MB
SMALL = br ca cy da de en es fr ga hr hy is lv nl no pt sv
# 10MB < Dictionary size < 50MB
LARGE = bg el eo it pl ro ru sk sl uk
# 50MB < dictionary size < 500MB
VERY_LARGE = ar cs et fi
LANGS = $(SMALL) $(LARGE)
DICT_RULE_PRELUDE = sortuniq() { cat > "$$1"; sort -u -o "$$1" "$$1"; }; \
echo Building $@ from $<
# he and hu: can not be generated
# af eu la ms tr xk: no aspell dictionary
ASPDICT = https://ftp.gnu.org/gnu/aspell/dict/0index.html
LOWS = $(addsuffix .low,$(LANGS))
DICTS = $(addsuffix .dict,$(LANGS))
DEPEND = Makefile
#MAKEFLAGS = -s
.PHONY: all required count clean low
all: required
make $(addprefix check-,$(LANGS))
$(ROOT_DICT):
mkdir $(ROOT_DICT)
$(OBJ_DIR):
mkdir $(OBJ_DIR)
count:
wc -l $(ROOT_DICT)/*
low: $(LOWS)
required: ${OBJ_DIR} ${OBJ_DIR}/src.mk ${ROOT_DICT}
$(OBJ_DIR)/src.mk:
echo Creation language table
wget $(ASPDICT) -q -O - | \
awk 'BEGIN { base="'`dirname $(ASPDICT)`'" } \
/<tr><td><a/ { $$0 = gensub(/.*"(.+)".*"(.+)\/(.+)".*/, "\\1 \\2 \\3", "g"); \
printf "${OBJ_DIR}/%s.dir: $(OBJ_DIR)/%s\n\n", $$1, $$3; \
printf "${OBJ_DIR}/%s:\n", $$3; \
printf "\techo Downloading %s dictionary source\n", $$1; \
printf "\twget -q %s/%s/%s -O $$@\n\n", base, $$2, $$3 } \
/<\/table>/ { exit }' > $@
include $(wildcard ${OBJ_DIR}/src.mk)
%.dir:
echo Installing $@ dictionary
cd "$$(dirname "$@")" && \
tar -xjf "$$(basename "$<")" && \
cd "$$(basename "$(<:.tar.bz2=)")" && \
./configure && \
make && \
cd .. && \
mv "$$(basename "$<" .tar.bz2)" "$$(basename "$@")"
%.low: %.dir
@echo "Building list of word $@"
export DICT_NAME="$$(basename "$<" ".dir")"; \
aspell --dict-dir="$$(realpath $<)" -d "$$DICT_NAME" dump master "$$DICT_NAME" | \
aspell --dict-dir="$$(realpath $<)" -l "$$DICT_NAME" expand | \
tr -s '[:space:]' '\n' > "$@" && \
LC_ALL=C sort -S28G -u -o "$@" "$@"
$(OBJ_DIR)/no.low:
make $(OBJ_DIR)/nb.low
mv $(OBJ_DIR)/nb.low $@
$(OBJ_DIR)/pt.low:
make $(OBJ_DIR)/pt_PT.low
mv $(OBJ_DIR)/pt_PT.low $@
$(ROOT_DICT)/ar.dict: $(OBJ_DIR)/ar.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[ﺏِ ]/ب/g' \
-e 's/[ﻍُ]/غ/g' \
-e 's/[ﻙ ]/ل/g' \
-e 's/[ﻡَ]/م/g' \
-e 's/[ﻥَ]/ن/g' \
-e 's/[ﻱَﻳّ]/ي/g' | \
sortuniq "$@"
# ا ل ن ش ا ش ي بِ ي ي ب ا ل ن ش ا ش ي بِ ي ي ب ن ش ا ش ي بِ ي ي ف ا ل ن ش ا ش ي بِ ي ي ف ب ا ل ن ش ا ش ي بِ ي ي ف ب ن ش ا ش ي بِ ي ي ف ك ا ل ن ش ا ش ي بِ ي ي ف ك ن ش ا ش ي بِ ي ي ف ل ل ن ش ا ش ي بِ ي ي ف ل ن ش ا ش ي بِ ي ي ف ن ش ا ش ي بِ ي ي ك ا ل ن ش ا ش ي بِ ي ي ك ن ش ا ش ي بِ ي ي ل ل ن ش ا ش ي بِ ي ي ل ن ش ا ش ي بِ ي ي ن ش ا ش ي بِ ي ي و ا ل ن ش ا ش ي بِ ي ي و ب ا ل ن ش ا ش ي بِ ي ي و ب ن ش ا ش ي بِ ي ي و ك ا ل ن ش ا ش ي بِ ي ي و ك ن ش ا ش ي بِ ي ي و ل ل ن ش ا ش ي بِ ي ي و ل ن ش ا ش ي بِ ي ي و ن ش ا ش ي بِ ي ي
$(ROOT_DICT)/bg.dict: $(OBJ_DIR)/bg.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
grep -v "^[a-zA-Z]" | \
tr '[абвгдежзийклмнопрстуфхцчшщъьюя]' '[АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯ]' | \
sortuniq "$@"
$(ROOT_DICT)/br.dict: $(OBJ_DIR)/br.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[-]" | \
grep -v "[^c]'" | \
grep -v "'[^h]" | \
sed -e 's/[àâ]/a/g' \
-e 's/[éêè]/e/g' \
-e 's/[î]/i/g' \
-e 's/[ñ]/n/g' \
-e 's/[ô]/o/g' \
-e 's/[ûüù]/u/g' | \
tr '[abcdefghijklmnopqrstuvwxyz]' '[ABCDEFGHIJKLMNOPQRSTUVWXYZ]' | \
sortuniq "$@"
$(ROOT_DICT)/ca.dict: $(OBJ_DIR)/ca.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
sed -e 's/[áàãÁÀ]/a/g' \
-e 's/[éèÉÈ]/e/g' \
-e 's/[çÇ]/c/g' \
-e 's/[íïÍ]/i/g' \
-e 's/[ñ]/n/g' \
-e 's/[óöòÓÒ]/o/g' \
-e 's/[úüùÚ]/u/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/cs.dict: $(OBJ_DIR)/cs.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[äâ]/a/g' \
-e 's/[çćĆ]/c/g' \
-e 's/[ęë]/e/g' \
-e 's/[î]/i/g' \
-e 's/[ľĽłĺ]/l/g' \
-e 's/[ń]/n/g' \
-e 's/[őöŐÖô]/o/g' \
-e 's/[śŚ]/s/g' \
-e 's/[űüŰÜ]/u/g' \
-e 's/[ź]/z/g' | \
sed -e 's/á/Á/g' -e 's/č/Č/g' -e 's/ď/Ď/g' -e 's/é/É/g' -e 's/ě/Ě/g' \
-e 's/í/Í/g' -e 's/ň/Ň/g' -e 's/ó/Ó/g' -e 's/ř/Ř/g' -e 's/š/Š/g' \
-e 's/ť/Ť/g' -e 's/ú/Ú/g' -e 's/ů/Ů/g' -e 's/ý/Ý/g' -e 's/ž/Ž/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/cy.dict: $(OBJ_DIR)/cy.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
grep -vi "ph" | \
sed -e 's/[âä]/a/g' \
-e 's/[êë]/e/g' \
-e 's/[îï]/i/g' \
-e 's/[ôöò]/o/g' \
-e 's/[ûüù]/u/g' \
-e 's/[ŵ]/w/g' \
-e 's/[ŷ]/y/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/da.dict: $(OBJ_DIR)/da.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
sed -e 's/[áä]/a/g' \
-e 's/[ð]/d/g' \
-e 's/[éëè]/e/g' \
-e 's/[íì]/i/g' \
-e 's/[óöô]/o/g' \
-e 's/[ü]/u/g' | \
tr '[a-zæøå]' '[A-ZÆØÅ]' | \
sortuniq "$@"
$(ROOT_DICT)/de.dict: $(OBJ_DIR)/de.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
sed -e 's/[âà]/a/g' \
-e 's/[éê]/e/g' \
-e 's/[ñ]/n/g' \
-e 's/[œ]/oe/g' \
-e 's/[ß]/ss/g' | \
tr '[a-zäöü]' '[A-ZÄÖÜ]' | \
sortuniq "$@"
$(ROOT_DICT)/el.dict: $(OBJ_DIR)/el.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[άΆ]/α/g' \
-e 's/[ϐ]/β/g' \
-e 's/[έΈ]/ε/g' \
-e 's/[ήΉ]/η/g' \
-e 's/[ίϊΐΊ]/ι/g' \
-e 's/[όΌ]/ο/g' \
-e 's/[ς]/σ/g' \
-e 's/[ΰϋύΎ]/υ/g' \
-e 's/[ϕ]/φ/g' \
-e 's/[ώΏ]/ω/g' | \
tr '[αβγδεζηθικλμνξοπρστυφχψω]' '[ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ]' | \
sortuniq "$@"
$(ROOT_DICT)/en.dict: $(OBJ_DIR)/en.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
sed -e 's/[áâàäåÅ]/a/g' \
-e 's/[ç]/c/g' \
-e 's/[éêèë]/e/g' \
-e 's/[íîï]/i/g' \
-e 's/[ñ]/n/g' \
-e 's/[óôöøiÖ]/o/g' \
-e 's/[úûüùiÜ]/u/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/eo.dict: $(OBJ_DIR)/eo.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'qQwWxXyY-]" | \
tr '[a-zĉĝĥĵŝŭ]' '[A-ZĈĜĤĴŜŬ]' | \
sortuniq "$@"
$(ROOT_DICT)/es.dict: $(OBJ_DIR)/es.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
sed -e 's/[áÁ]/a/g' \
-e 's/[é]/e/g' \
-e 's/[í]/i/g' \
-e 's/[ó]/o/g' \
-e 's/[úü]/u/g' | \
tr '[a-zñ]' '[A-ZÑ]' | \
sortuniq "$@"
$(ROOT_DICT)/et.dict: $(OBJ_DIR)/et.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'qwxQWX-]" | \
tr '[a-zäöõüšž]' '[A-ZÄÖÕÜŠŽ]' | \
sortuniq "$@"
$(ROOT_DICT)/fi.dict: $(OBJ_DIR)/fi.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.':-]" | \
tr '[a-zäåöšž]' '[A-ZÄÅÖŠŽ]' | \
sortuniq "$@"
$(ROOT_DICT)/fr.dict: $(OBJ_DIR)/fr.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
sed -e 's/[àâä]/a/g' \
-e 's/[ç]/c/g' \
-e 's/[Ééèêë]/e/g' \
-e 's/[ïî]/i/g' \
-e 's/[ôö]/o/g' \
-e 's/[úùûü]/u/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/ga.dict: $(OBJ_DIR)/ga.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.':-]" | \
sed -e 's/[áâãÁ]/a/g' \
-e 's/[ç]/c/g' \
-e 's/[éèÉ]/e/g' \
-e 's/[íÍ]/i/g' \
-e 's/[óöòÓ]/o/g' \
-e 's/[úüÚ]/u/g' | \
tr '[a-zñ]' '[A-ZÑ]' | \
sortuniq "$@"
$(ROOT_DICT)/he.dict: $(OBJ_DIR)/he.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sortuniq "$@"
$(ROOT_DICT)/hr.dict: $(OBJ_DIR)/hr.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
tr '[a-zćčšž]' '[A-ZĆČŠŽ]' | \
sed 's/đ/Đ/g' | \
sortuniq "$@"
$(ROOT_DICT)/hu.dict: $(OBJ_DIR)/hu.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
tr '[a-záéóöőúüű]' '[A-ZÁÉÓÖŐÚÜŰ]' | \
sortuniq "$@"
#grep -v "['ԁԃԂԃ-]"
$(ROOT_DICT)/hy.dict: $(OBJ_DIR)/hy.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
sed -e 's/ա/Ա/g' -e 's/բ/Բ/g' -e 's/գ/Գ/g' -e 's/դ/Դ/g' -e 's/ե/Ե/g' \
-e 's/զ/Զ/g' -e 's/է/Է/g' -e 's/ը/Ը/g' -e 's/թ/Թ/g' -e 's/ժ/Ժ/g' \
-e 's/ի/Ի/g' -e 's/լ/Լ/g' -e 's/խ/Խ/g' -e 's/ծ/Ծ/g' -e 's/կ/Կ/g' \
-e 's/հ/Հ/g' -e 's/ձ/Ձ/g' -e 's/ղ/Ղ/g' -e 's/ճ/Ճ/g' -e 's/մ/Մ/g' \
-e 's/յ/Յ/g' -e 's/ն/Ն/g' -e 's/շ/Շ/g' -e 's/ո/Ո/g' -e 's/չ/Չ/g' \
-e 's/պ/Պ/g' -e 's/ջ/Ջ/g' -e 's/ռ/Ռ/g' -e 's/ս/Ս/g' -e 's/վ/Վ/g' \
-e 's/տ/Տ/g' -e 's/ր/Ր/g' -e 's/ց/Ց/g' -e 's/ւ/Ւ/g' -e 's/փ/Փ/g' \
-e 's/ք/Ք/g' -e 's/օ/Օ/g' -e 's/ֆ/Ֆ/g' -e 's/ու/ՈՒ/g' \
-e "s/'//g" | \
sortuniq "$@"
$(ROOT_DICT)/it.dict: $(OBJ_DIR)/it.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
sed -e 's/[àâ]/a/g' \
-e 's/[ç]/c/g' \
-e 's/[éè]/e/g' \
-e 's/[ì]/i/g' \
-e 's/[òiô]/o/g' \
-e 's/[ù]/u/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/is.dict: $(OBJ_DIR)/is.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
tr '[a-záæðéíóöúýþ]' '[A-ZÁÆÐÉÍÓÖÚÝÞ]' | sortuniq "$@"
$(ROOT_DICT)/lv.dict: $(OBJ_DIR)/lv.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'-]" | \
tr '[a-zāčēģīķļņšūž]' '[A-ZĀČĒĢĪĶĻŅŠŪŽ]' | sortuniq "$@"
$(ROOT_DICT)/nl.dict: $(OBJ_DIR)/nl.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[1-9+/' .-]" | \
sed -e 's/[áäâàÅ]/a/g' \
-e 's/[ç]/c/g' \
-e 's/[éëèê]/e/g' \
-e 's/[íïî]/i/g' \
-e 's/[ñ]/n/g' \
-e 's/[óöô]/o/g' \
-e 's/[úüûùÜ]/u/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/no.dict: $(OBJ_DIR)/no.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[.'\"-]" | \
sed -e 's/[áäÄ]/a/g' \
-e 's/[ç]/c/g' \
-e 's/[ð]/d/g' \
-e 's/[éëèêÉ]/e/g' \
-e 's/[íì]/i/g' \
-e 's/[óöôò]/o/g' \
-e 's/[ü]/u/g' | \
tr '[a-zæøå]' '[A-ZÆØÅ]' | \
sortuniq "$@"
$(ROOT_DICT)/pl.dict: $(OBJ_DIR)/pl.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[ä]/a/g' \
-e 's/[ç]/c/g' \
-e 's/[é]/e/g' \
-e 's/[i]/i/g' \
-e 's/[ö]/o/g' \
-e 's/[šŠ]/s/g' \
-e 's/[û]/u/g' | \
tr '[a-złąęóśżćńź]' '[A-ZŁĄĘÓŚŻĆŃŹ]' | \
sortuniq "$@"
$(ROOT_DICT)/pt.dict: $(OBJ_DIR)/pt.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[áãâàÁÂ]/a/g' \
-e 's/[éêèÉ]/e/g' \
-e 's/[íîÍ]/i/g' \
-e 's/[óôõÓ]/o/g' \
-e 's/[úüÚ]/u/g' | \
tr '[a-zç]' '[A-ZÇ]' | \
sortuniq "$@"
$(ROOT_DICT)/ro.dict: $(OBJ_DIR)/ro.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[áâăÂĂ]/a/g' \
-e 's/[éè]/e/g' \
-e 's/[íîÎ]/i/g' \
-e 's/[șȘ]/s/g' \
-e 's/[țȚ]/t/g' \
-e 's/[ü]/u/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/ru.dict: $(OBJ_DIR)/ru.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/а/А/g' -e 's/б/Б/g' -e 's/в/В/g' -e 's/г/Г/g' -e 's/д/Д/g' \
-e 's/е/Е/g' -e 's/ё/Ё/g' -e 's/ж/Ж/g' -e 's/з/З/g' -e 's/и/И/g' \
-e 's/й/Й/g' -e 's/к/К/g' -e 's/л/Л/g' -e 's/м/М/g' -e 's/н/Н/g' \
-e 's/о/О/g' -e 's/п/П/g' -e 's/р/Р/g' -e 's/с/С/g' -e 's/т/Т/g' \
-e 's/у/У/g' -e 's/ф/Ф/g' -e 's/х/Х/g' -e 's/ц/Ц/g' -e 's/ч/Ч/g' \
-e 's/ш/Ш/g' -e 's/щ/Щ/g' -e 's/ъ/Ъ/g' -e 's/ы/Ы/g' -e 's/ь/Ь/g' \
-e 's/э/Э/g' -e 's/ю/Ю/g' -e 's/я/Я/g' | \
sortuniq "$@"
$(ROOT_DICT)/sk.dict: $(OBJ_DIR)/sk.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[ë]/e/g' | \
sed -e 's/á/Á/g' -e 's/ä/Ä/g' -e 's/č/Č/g' -e 's/ď/Ď/g' -e 's/é/É/g' \
-e 's/ě/Ě/g' -e 's/í/Í/g' -e 's/ľ/Ľ/g' -e 's/ĺ/Ĺ/g' -e 's/ň/Ň/g' \
-e 's/ô/Ô/g' -e 's/ó/Ó/g' -e 's/ö/Ö/g' -e 's/ŕ/Ŕ/g' -e 's/ř/Ř/g' \
-e 's/š/Š/g' -e 's/ť/Ť/g' -e 's/ú/Ú/g' -e 's/ü/Ü/g' -e 's/ý/Ý/g' \
-e 's/ž/Ž/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/sl.dict: $(OBJ_DIR)/sl.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[áâȁ]/a/g' \
-e 's/[ćĆ]/c/g' \
-e 's/[éȇȅ]/e/g' \
-e 's/[đĐ]/d/g' \
-e 's/[íȋȉ]/i/g' \
-e 's/[óȏȍö]/o/g' \
-e 's/[ŕȓ]/r/g' \
-e 's/[úȗȕü]/u/g' | \
sed -e 's/č/Č/g' -e 's/š/Š/g' -e 's/ž/Ž/g' | \
tr '[a-z]' '[A-Z]' | \
sortuniq "$@"
$(ROOT_DICT)/sv.dict: $(OBJ_DIR)/sv.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "['-]" | \
sed -e 's/[æ]/ae/g' \
-e 's/[ç]/c/g' \
-e 's/[é]/e/g' \
-e 's/[ü]/u/g' | \
tr '[a-zåäö]' '[A-ZÅÄÖ]' | \
sortuniq "$@"
$(ROOT_DICT)/uk.dict: $(OBJ_DIR)/uk.low $(DEPEND)
@$(DICT_RULE_PRELUDE); \
cat "$<" | \
grep -v '^.$$' | \
grep -v "[-]" | \
tr '[абвгґдеєжзиіїйклмнопрстуфхцчшщьюя]' '[АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ]' | \
sortuniq "$@"
CHECKER = cat $(1) | \
grep -v "^[$(2)]*$$" | egrep -v "$(3)" && exit 1 || echo "$< is correct"
check-ar:$(ROOT_DICT)/ar.dict
$(call CHECKER,$<,ابتثجحخدذرزسشصضطظعغفقكلمنهويءأإؤئآةى,~)
check-bg:$(ROOT_DICT)/bg.dict
$(call CHECKER,$<,АОЕИТНПРСВМБДКЛГЪЖЗУЧЯЙХЦШЮФЩЬ,~)
check-br:$(ROOT_DICT)/br.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPRSTUVWXYZ,C'H[A-Z]*(C'H)*)
check-ca:$(ROOT_DICT)/ca.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZ,L·L[A-Z]*(L·L)*)
check-cs:$(ROOT_DICT)/cs.dict
$(call CHECKER,$<,AÁBCČDĎEÉĚFGHIÍJKLMNŇOÓPQRŘSŠTŤUÚŮVWXYÝZŽ,~)
check-da:$(ROOT_DICT)/da.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ,~)
check-de:$(ROOT_DICT)/de.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ,~)
check-el:$(ROOT_DICT)/el.dict
$(call CHECKER,$<,ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ,~)
check-eo:$(ROOT_DICT)/eo.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPRSTUVZĈĜĤĴŜŬ,~)
check-es:$(ROOT_DICT)/es.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÑ,~)
check-et:$(ROOT_DICT)/et.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPRSTUVYZÄÖÕÜŠŽ,~)
check-fi:$(ROOT_DICT)/fi.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÄÅÖŠŽ,~)
check-ga:$(ROOT_DICT)/ga.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÑ,~)
check-he:$(ROOT_DICT)/he.dict
$(call CHECKER,$<,אבגדהוזחטיכלמנסעפצקרשת,~)
check-hr:$(ROOT_DICT)/hr.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZĆČĐŠŽ,~)
check-hu:$(ROOT_DICT)/hu.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÁÉÓÖŐÚÜŰ,~)
check-hy:$(ROOT_DICT)/hy.dict
$(call CHECKER,$<,ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖՈՒև,~)
check-is:$(ROOT_DICT)/is.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÁÆÐÉÍÓÖÚÝÞ,~)
check-lv:$(ROOT_DICT)/lv.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZĀČĒĢĪĶĻŅŠŪŽ,~)
check-no:$(ROOT_DICT)/no.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ,~)
check-pl:$(ROOT_DICT)/pl.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZŁĄĘÓŚŻĆŃŹ,~)
check-pt:$(ROOT_DICT)/pt.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÇ,~)
check-ru:$(ROOT_DICT)/ru.dict
$(call CHECKER,$<,АБВГДЕЁЖЗИЙКЛлМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ,~)
check-sk:$(ROOT_DICT)/sk.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÁÄČĎÉĚÍĽĹŇÔÓÖŔŠŤÚÜÝŽ,~)
check-sl:$(ROOT_DICT)/sl.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZČŠŽ,~)
check-sv:$(ROOT_DICT)/sv.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ,~)
check-uk:$(ROOT_DICT)/uk.dict
$(call CHECKER,$<,ОАИНВЕІТКРСДЛМУПЗЯЬБГЧХЄЇЙЖЦШЮҐФЩ',~)
check-%:$(ROOT_DICT)/%.dict
$(call CHECKER,$<,ABCDEFGHIJKLMNOPQRSTUVWXYZ,~)
clean:
rm -rf "$(OBJ_DIR)"

View File

@ -166,8 +166,35 @@ msgstr "Vous avez changé la langue du plateau en {0}"
msgid "{0} changed the language of the board to {1}"
msgstr "{0} a changé la langue du plateau en {1}"
msgid "Configuration"
msgstr "Options de configuration"
msgid "Check spelling for new words"
msgstr "Vérifier l'orthographe des mots nouveaux"
msgid "Incorrect spelling for {0} from {1}"
msgstr "Orthographe incorrecte pour {0} parmi {1}"
msgid "All words are correct: {0}"
msgstr "Tous les mots sont corrects : {0}"
msgid "No new word found"
msgstr "Aucun nouveau mot trouvé"
msgid "Incorrect dictionary file."
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 ?"
msgid "Spell checking is based on:"
msgstr "La vérification orthographique est basée sur :"
msgid "Settings"
msgstr "Paramètres"
msgid "Configuration window"
msgstr "Fenêtre de configuration"

View File

@ -172,7 +172,34 @@ msgstr ""
msgid "{0} changed the language of the board to {1}"
msgstr ""
msgid "Configuration"
msgid "Check spelling for new words"
msgstr ""
msgid "Incorrect spelling for {0} from {1}"
msgstr ""
msgid "All words are correct: {0}"
msgstr ""
msgid "No new word found"
msgstr ""
msgid "Incorrect dictionary file."
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 ""
msgid "Spell checking is based on:"
msgstr ""
msgid "Settings"
msgstr ""
msgid "Configuration window"

View File

@ -17,6 +17,9 @@ window.TrivabbleConf = {
// to tweak only if your webserver is shared with other conflicting resources at / ( ex: 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,
// I don't like trailing commas, here is a nice message for you reading this file :-)
HAVE_FUN: true
};

View File

@ -44,37 +44,24 @@
</div>
</div>
<div class="modal" id="modal-config-dialog">
<div class="modal" id="modal-settings-dialog">
<div class="modal-content">
<h2>
<span data-l10n="text-content">Configuration window</span>
<span class="modal-button" id="modal-close-btn">&#215;</span>
</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>
</div>
<hr/>
<div id="prefs">
<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>
<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>
</div>
<div id="content">
<div id="content">
<table id="board">
<tr><td class="corner"></td></tr>
</table>
@ -84,6 +71,7 @@
</div>
<div id="panel">
<button id="btn-settings" data-l10n="text-content">Settings</button>
<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>
<table id="participants">
@ -94,7 +82,8 @@
<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="modal-config-btn" class="text-content" data-l10n="text-content">Configuration</button>
<button id="check-spelling" class="minibutton" data-l10n="text-content" style="display:none">Check spelling for 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>
</body>
</html>

View File

@ -156,6 +156,11 @@ button:hover {
cursor:pointer
}
button[disabled=disabled], button:disabled {
background:#AFA;
cursor:not-allowed;
}
#rack {
background:#EEC;
font-size:0;
@ -192,8 +197,12 @@ button:hover {
cursor:pointer
}
.highlight {
background-color: #E3E;
.tile.tile-spelling-ok {
background-color: #3E3;
}
.tile.tile-spelling-nok {
background-color: #E33;
}
.tile.tile-highlight {
@ -203,6 +212,10 @@ button:hover {
border: 1px solid black;
}
.highlight {
background-color: #E3E;
}
@keyframes highlight {
0% {
background:red;
@ -347,7 +360,8 @@ td.blink {
width:165px;
height:160px;
background:url(bag.svg) no-repeat;
background-size:100% 100%
background-size:100% 100%;
flex:1;
}
#bag:hover {
@ -502,12 +516,41 @@ td.blink {
border-radius: 0.25rem;
background-color: lightgray;
}
.modal-button:hover {
.modal-button:hover, #btn-settings:hover {
background-color: darkgray;
}
.show-modal {
opacity: 1;
visibility: visible;
transform: scale(1.0);
transition: visibility 0s linear 0s, opacity 0.25s 0s, transform 0.25s;
}
.alert {
z-index: 2;
}
#btn-settings {
background:lightgray;
color:black;
font-size:small;
border-radius:3px;
border:none;
margin:0 0 0 auto;
min-height: 2em;
}
#btn-settings:before {
content: "⚙️ "
}
#info-spell-checking {
font-size:small;
color:gray;
}
#info-spell-checking a {
color:black;
}

View File

@ -42,6 +42,7 @@
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("FLASH_LIGHT_DURATIONS", [800, 1600, 3200]);
setConf("FLASH_LIGHT_COLOR", "#E3E");
@ -62,6 +63,7 @@
let scoreOf;
let bag;
let boardLangSelect;
const downloadedDictionaries = {};
const playerLetters = [];
let currentPlayer = "";
@ -82,6 +84,7 @@
let remainingLetters = 0;
let needsRestart = false;
const gameInProgress = false;
let currentTilePlayed = {};
let eventSource = null;
let webSocket = null;
@ -151,6 +154,61 @@
}
}
function getDictionary(code, callback, force) {
if (downloadedDictionaries[code]) {
if (downloadedDictionaries[code].length) {
if (callback) {
callback(downloadedDictionaries[code]);
return;
}
}
// FIXME we silently never call the callback here
return;
}
if (!force && localStorage.spellCheckerEnabled !== "true") {
myConfirm(
_("Spell checking requires Trivabble to download a dictionary. Do you confirm?"),
function () {
localStorage.spellCheckerEnabled = "true";
getDictionary(code, callback, true);
}
);
return;
}
// Avoid redownloading the dictionary if a download is on its way
downloadedDictionaries[code] = [];
const file = new XMLHttpRequest();
file.open("GET", "dict/" + code + ".dict", true);
file.onreadystatechange = function () {
if (file.readyState === 4) {
if (file.status === 200) {
try {
const dictionary = file.responseText.split("\n");
downloadedDictionaries[code] = dictionary;
callback(dictionary);
return;
} catch (e) {
delete downloadedDictionaries[code];
myAlert(_("Incorrect dictionary file."));
}
} else {
delete downloadedDictionaries[code];
myAlert(_("Can't load dictionary file."));
}
}
};
file.send();
}
function spellCheckerSettingChecked() {
localStorage.spellCheckerEnabled = document.getElementById("enable-spell-checker").checked.toString();
initSpellChecker();
}
function removeElem(elem) {
elem.parentNode.removeChild(elem);
}
@ -211,6 +269,18 @@
movingTile.style.width = "";
movingTile.style.height = "";
/* Keep track of letter */
if (moveCMD.from === "rack") {
if (moveCMD.to === "board") {
currentTilePlayed[moveCMD.indexTo] = "-";
}
} else {/* moveCMD.from === "board" */
if (moveCMD.to === "board") {
currentTilePlayed[moveCMD.indexTo] = currentTilePlayed[moveCMD.indexFrom];
}
delete currentTilePlayed[moveCMD.indexFrom];
}
mouseUp(document, dragTileEnd, true);
mouseMove(document, dragTileMove, true);
@ -683,8 +753,6 @@
? format(_("You changed the language of the board to {0}"), newLang)
: format(_("{0} changed the language of the board to {1}"), msg.sender, newLang)
);
return;
}
case "highlightTile": {
@ -711,6 +779,7 @@
tablePlayers[currentPlayer].classList.remove("current-player");
}
currentTilePlayed = {};
currentPlayer = player;
refreshCurrentPlayer();
}
@ -872,6 +941,9 @@
break;
case "setCell":
setCell(data.indexTo, data.letter, Object.prototype.hasOwnProperty.call(data, "player") && data.player !== localStorage.trivabblePlayerName);
if ((data.letter !== "") && (currentTilePlayed[data.indexTo] === "-")) {
currentTilePlayed[data.indexTo] = data.letter;
}
break;
case "setRackCell":
setRackCell(data.indexTo, data.letter);
@ -1443,6 +1515,181 @@
}
function getLetterFromBoard(row, col) {
if ((row < 0) || (row >= 15) || (col < 0) || (col >= 15)) {
return "";
}
try {
const index = (row * 15) + col;
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
const tile = tilePlaceholder.getElementsByClassName("tile")[0];
return tile.firstChild.textContent;
} catch (e) {
return "";
}
}
function searchWordInLine(row, col, incRow, incCol) {
const initLetter = getLetterFromBoard(row, col);
/* Look for first letter in line */
while (getLetterFromBoard(row - incRow, col - incCol) !== "") {
row -= incRow;
col -= incCol;
}
/* Look for last letter in line */
const word = {letters: "", row: row, col: col, incRow: incRow, incCol: incCol};
let letter;
while ((letter = getLetterFromBoard(row, col)) !== "") {
word.letters += letter;
row += incRow;
col += incCol;
}
return (word.letters === initLetter) ? null : word;
}
function isNewWord(newWord, words) {
if (newWord === null) {
return false;
}
for (const k of Object.keys(words)) {
if ((words[k].letters === newWord.letters) &&
(words[k].row === newWord.row) &&
(words[k].col === newWord.col) &&
(words[k].incRow === newWord.incRow) &&
(words[k].incCol === newWord.incCol)) {
return false;
}
}
return true;
}
function searchNewWords() {
const words = [];
for (const i of Object.keys(currentTilePlayed)) {
/* Get board position */
const row = Math.floor(i / 15);
const col = i % 15;
/* Look for word in column */
const newWordInCol = searchWordInLine(row, col, 1, 0);
if (isNewWord(newWordInCol, words)) {
words.push(newWordInCol);
}
/* Look for word in row */
const newWordInRow = searchWordInLine(row, col, 0, 1);
if (isNewWord(newWordInRow, words)) {
words.push(newWordInRow);
}
/* Should we look for word in diag? */
/* If nothing found, it's a one letter word */
if ((newWordInCol === null) && (newWordInRow === null)) {
words.push({letters: getLetterFromBoard(row, col), row: row, col: col, incRow: 0, incCol: 0});
}
}
return words;
}
function modifyTileClass(word, value, action) {
/* action: true for add, false for remove */
if (action) {
/* only for new words */
for (let l = 0; l < word.letters.length; l++) {
const index = ((word.row + (l * word.incRow)) * 15) + word.col + (l * word.incCol);
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
const tile = tilePlaceholder.getElementsByClassName("tile")[0];
tile.classList.add(value);
}
} else {
/* tiles on board */
for (let index = 0; index < 15 * 15; index++) {
const tilePlaceholder = boardCells[index].getElementsByClassName("tile-placeholder")[0];
const tile = tilePlaceholder.getElementsByClassName("tile")[0];
if (typeof tile !== "undefined") {
tile.classList.remove(value);
}
}
/* tiles on rack */
for (const tilePlaceholder of rack.getElementsByClassName("tile-placeholder")) {
const tile = tilePlaceholder.getElementsByClassName("tile")[0];
if (typeof tile !== "undefined") {
tile.classList.remove(value);
}
}
/* tile on the move */
if (movingTile !== null) {
movingTile.classList.remove(value);
}
}
}
function checkSpellingClicked() {
if (localStorage.spellCheckerEnabled === "false") {
return;
}
getDictionary(localStorage.trivabbleBoardLang, checkSpelling);
}
function checkSpelling(dictionary) {
const newWords = searchNewWords();
if (newWords.length === 0) {
myAlert(_("No new word found"));
return;
}
/* Check if words are in dictionary */
let words = [];
let badWords = [];
for (const k of Object.keys(newWords)) {
const currentWord = newWords[k].letters.replace(/ /gu, "?");
if (words.indexOf(currentWord) === -1) {
words.push(currentWord);
}
modifyTileClass(newWords[k], "tile-spelling-ok", true);
const pattern = "^" + newWords[k].letters.replace(/ /gu, ".") + "$";
const find = dictionary.find(function (word) {
return word.match(pattern);
});
if (!find) {
if (badWords.indexOf(currentWord) === -1) {
badWords.push(currentWord);
}
modifyTileClass(newWords[k], "tile-spelling-nok", true);
}
}
setTimeout(
function () {
modifyTileClass(null, "tile-spelling-ok", false);
modifyTileClass(null, "tile-spelling-nok", false);
}, 5000);
words = words.join(", ").trim();
badWords = badWords.join(", ").trim();
/* Advice on erroneous spelling */
if (badWords.length > 0) {
const msg = chatMessage("", format(_("Incorrect spelling for {0} from {1}"), badWords, words));
msg.classList.add("error");
} else {
chatMessage("", format(_("All words are correct: {0}"), words));
}
}
function highlightTile(e) {
let id = this.id;
if (id === "") {
@ -1583,6 +1830,24 @@
}
}
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;
}
}
function translateDuration(x, values) {
if (x < 0) {
return null;
@ -1707,9 +1972,9 @@
location.reload();
}
function showConfig() {
const modal = document.querySelector(".modal");
modal.classList.toggle("show-modal");
function showSettings() {
const modal = document.querySelector(".modal");
modal.classList.toggle("show-modal");
}
function initGlobals() {
@ -1734,10 +1999,11 @@
document.getElementById("join-game").onclick = joinGame;
document.getElementById("clear-rack").onclick = clearRack;
document.getElementById("show-rack").onclick = showRack;
document.getElementById("check-spelling").onclick = checkSpellingClicked;
helpClear.onclick = clearGame;
document.getElementById("modal-config-btn").onclick = showConfig;
document.getElementById("modal-close-btn").onclick = showConfig;
document.getElementById("btn-settings").onclick = showSettings;
document.getElementById("btn-settings-close").onclick = showSettings;
}
function initGame() {
@ -1839,7 +2105,7 @@
document.getElementById("board-lang").value = localStorage.trivabbleBoardLang;
}
startGame(localStorage.trivabbleGameNumber, localStorage.trivabbleBoardLang);
startGame(localStorage.trivabbleGameNumber);
}
function initLang() {
@ -1861,6 +2127,7 @@
initChat();
initGame();
initSound();
initSpellChecker();
initLaserPointer();
};

View File

@ -980,7 +980,8 @@ function handleRequest(request, response) {
".ogg": "audio/ogg",
".js": "application/javascript; charset=utf-8",
".css": "text/css; charset=utf-8",
".svg": "image/svg+xml"
".svg": "image/svg+xml",
".dict": "text/plain"
};
for (const i in mimes) {