add locale support
|
Before Width: | Height: | Size: 320 B After Width: | Height: | Size: 320 B |
|
Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B |
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 380 B |
|
Before Width: | Height: | Size: 569 B After Width: | Height: | Size: 569 B |
|
Before Width: | Height: | Size: 659 B After Width: | Height: | Size: 659 B |
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 381 B |
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"title": "Rollenspiel Würfel",
|
||||||
|
"reset-dices": "Würfel zurücksetzen",
|
||||||
|
"clear-history": "Verlauf löschen",
|
||||||
|
"export-data": "Daten exportieren",
|
||||||
|
"import-data": "Daten importieren",
|
||||||
|
"dark-mode": "Dunkler Modus",
|
||||||
|
"light-mode": "Heller Modus",
|
||||||
|
"update": "Update versuchen",
|
||||||
|
"add-selected": "Ausgewählte addieren",
|
||||||
|
"roll-selected": "Ausgewählte würfeln",
|
||||||
|
"new-dices": "Neue Würfel",
|
||||||
|
"roll": "Würfeln",
|
||||||
|
"custom": "Benutzerdefiniert",
|
||||||
|
"new-dice": "Neuer Würfel",
|
||||||
|
"regex": "(\\d+)?[w|W](\\d+)([\\+|\\-]\\d+)?(\\[(.+)\\])?",
|
||||||
|
"regex-placeholder": "1W6+1 + 2W4",
|
||||||
|
"D": "W"
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"title": "Role Playing Game Dices",
|
||||||
|
"reset-dices": "Reset Dices",
|
||||||
|
"clear-history": "Clear History",
|
||||||
|
"export-data": "Export Data",
|
||||||
|
"import-data": "Import Data",
|
||||||
|
"dark-mode": "Dark Mode",
|
||||||
|
"light-mode": "Light Mode",
|
||||||
|
"update": "Try to Update",
|
||||||
|
"add-selected": "Add Selected",
|
||||||
|
"roll-selected": "Roll Selected",
|
||||||
|
"new-dices": "New Dices",
|
||||||
|
"roll": "Roll",
|
||||||
|
"custom": "Custom",
|
||||||
|
"new-dice": "New Dice",
|
||||||
|
"regex": "(\\d+)?[d|D](\\d+)([\\+|\\-]\\d+)?(\\[(.+)\\])?",
|
||||||
|
"regex-placeholder": "1D6+1 + 2D4",
|
||||||
|
"D": "D"
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 194 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 202 B After Width: | Height: | Size: 202 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 686 B |
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 405 B After Width: | Height: | Size: 405 B |
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Role Playing Game Dices</title>
|
<title data-i18n="title">Role Playing Game Dices</title>
|
||||||
<meta name="application-name" content="Role Playing Game Dices">
|
<meta name="application-name" content="Role Playing Game Dices">
|
||||||
<meta name="application-version" content="v0.2.2">
|
<meta name="application-version" content="v0.2.2">
|
||||||
|
|
||||||
@@ -37,20 +37,45 @@
|
|||||||
<label id="sidebar-toggle-close" class="sidebar-toggle text-fresh float-right" for="sidebar-toggle-input"><img
|
<label id="sidebar-toggle-close" class="sidebar-toggle text-fresh float-right" for="sidebar-toggle-input"><img
|
||||||
src="./assets/close.svg"></label>
|
src="./assets/close.svg"></label>
|
||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li onclick="resetDices()"><img src="./assets/dices.svg"> Reset Dices</li>
|
<li onclick="resetDices()">
|
||||||
<li onclick="clearHistory()"><img src="./assets/history.svg"> Clear History</li>
|
<img src="./assets/dices.svg">
|
||||||
<li onclick="exportData()"><img src="./assets/export.svg"> Export Data</li>
|
<span data-i18n="reset-dices">Reset Dices</span>
|
||||||
<li><label><img src="./assets/import.svg"> Import Data
|
</li>
|
||||||
|
<li onclick="clearHistory()">
|
||||||
|
<img src="./assets/history.svg">
|
||||||
|
<span data-i18n="clear-history">Clear History</span>
|
||||||
|
</li>
|
||||||
|
<li onclick="exportData()">
|
||||||
|
<img src="./assets/export.svg">
|
||||||
|
<span data-i18n="export-data">Export Data</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<img src="./assets/import.svg">
|
||||||
|
<span data-i18n="import-data">Import Data</span>
|
||||||
<input id="importFile" type="file" accept="application/json" />
|
<input id="importFile" type="file" accept="application/json" />
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li onclick="toggleDarkMode()"><img id="dark-mode-icon" src="./assets/dark.svg"><span
|
<li onclick="toggleDarkMode()">
|
||||||
id="dark-mode-text">Dark Mode</span></li>
|
<img id="dark-mode-icon" src="./assets/dark.svg">
|
||||||
|
<span id="dark-mode-text">Dark Mode</span>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li onclick="clearAndRefresh()"><img id="dark-mode-icon" src="./assets/update.svg"><span>Try to Update</span></li>
|
<li onclick="setLocale('en')">
|
||||||
|
<span>English</span>
|
||||||
|
</li>
|
||||||
|
<li onclick="setLocale('de')">
|
||||||
|
<span>Deutsch</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="menu">
|
||||||
|
<li onclick="clearAndRefresh()">
|
||||||
|
<img id="dark-mode-icon" src="./assets/update.svg">
|
||||||
|
<span data-i18n="update">Try to Update</span>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<script>
|
<script>
|
||||||
Sidebar();
|
Sidebar();
|
||||||
@@ -62,10 +87,14 @@
|
|||||||
|
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button id="roll-add-button" onclick="addSelected()"><img src="./assets/add.svg"> Add
|
<button id="roll-add-button" onclick="addSelected()">
|
||||||
Selected</button>
|
<img src="./assets/add.svg">
|
||||||
<button id="roll-button" onclick="rollSelected()"><img src="./assets/roll.svg"> Roll
|
<span data-i18n="add-selected">Add Selected</span>
|
||||||
Selected</button>
|
</button>
|
||||||
|
<button id="roll-button" onclick="rollSelected()">
|
||||||
|
<img src="./assets/roll.svg">
|
||||||
|
<span data-i18n="roll-selected">Roll Selected</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,35 +102,47 @@
|
|||||||
<div class="history-container">
|
<div class="history-container">
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button id="history-undo-button" onclick="undo()"><img src="./assets/undo.svg"></button>
|
<button id="history-undo-button" onclick="undo()">
|
||||||
<button id="history-button" onclick="clearHistory()"><img src="./assets/history.svg"> Clear
|
<img src="./assets/undo.svg">
|
||||||
History</button>
|
</button>
|
||||||
<button id="history-redo-button" onclick="redo()"><img src="./assets/redo.svg"></button>
|
<button id="history-button" onclick="clearHistory()">
|
||||||
|
<img src="./assets/history.svg">
|
||||||
|
<span data-i18n="clear-history">Clear History</span>
|
||||||
|
</button>
|
||||||
|
<button id="history-redo-button" onclick="redo()">
|
||||||
|
<img src="./assets/redo.svg">
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="history" id="history"></div>
|
<div class="history" id="history"></div>
|
||||||
|
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<input type="text" id="inputText" placeholder="1D6+1 + 2W4">
|
<input type="text" id="inputText" placeholder="1D6+1 + 2D4" data-i18n-placeholder="regex-placeholder">
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button onclick="addDicesText()"><img src="./assets/plus.svg"> New Dices</button>
|
<button onclick="addDicesText()">
|
||||||
<button onclick="rollText()"><img src="./assets/roll.svg"> Roll</button>
|
<img src="./assets/plus.svg">
|
||||||
|
<span data-i18n="new-dices">New Dices</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="rollText()">
|
||||||
|
<img src="./assets/roll.svg">
|
||||||
|
<span data-i18n="roll">Roll</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<input min="1" value="1" type="number" id="inputCount">
|
<input min="1" value="1" type="number" id="inputCount">
|
||||||
D
|
D
|
||||||
<select value="6" id="inputSides" onchange="updateCustom()">
|
<select value="6" id="inputSides" onchange="updateCustom()">
|
||||||
<option>4</option>
|
<option value="4">4</option>
|
||||||
<option>6</option>
|
<option value="6">6</option>
|
||||||
<option>8</option>
|
<option value="8">8</option>
|
||||||
<option>10</option>
|
<option value="10">10</option>
|
||||||
<option>12</option>
|
<option value="12">12</option>
|
||||||
<option>20</option>
|
<option value="20">20</option>
|
||||||
<option>100</option>
|
<option value="100">100</option>
|
||||||
<option value="">custom</option>
|
<option value="" data-i18n="custom">custom</option>
|
||||||
</select>
|
</select>
|
||||||
<input class="hidden" min="2" value="2" type="number" id="inputCustom">
|
<input class="hidden" min="2" value="2" type="number" id="inputCustom">
|
||||||
+
|
+
|
||||||
@@ -110,8 +151,14 @@
|
|||||||
<input type="color" id="inputColor">
|
<input type="color" id="inputColor">
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button onclick="addDiceForm()"><img src="./assets/plus.svg"> New Dice</button>
|
<button onclick="addDiceForm()">
|
||||||
<button onclick="rollForm()"><img src="./assets/roll.svg"> Roll</button>
|
<img src="./assets/plus.svg">
|
||||||
|
<span data-i18n="new-dice">New Dice</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="rollForm()">
|
||||||
|
<img src="./assets/roll.svg">
|
||||||
|
<span data-i18n="roll">Roll</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class Dice {
|
|||||||
if (this.count > 1) {
|
if (this.count > 1) {
|
||||||
result += this.count;
|
result += this.count;
|
||||||
}
|
}
|
||||||
result += "D" + this.sides;
|
result += (localeData && localeData['D'] || "D") + this.sides;
|
||||||
if (this.addition) {
|
if (this.addition) {
|
||||||
result += (this.addition < 0 ? "-" : "+") + Math.abs(this.addition);
|
result += (this.addition < 0 ? "-" : "+") + Math.abs(this.addition);
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ class Dice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fromText(value) {
|
fromText(value) {
|
||||||
const result = value.match(dice_regex);
|
const result = value.match(localeData && localeData['regex'] || dice_regex);
|
||||||
if (result) {
|
if (result) {
|
||||||
if (result[1]) {
|
if (result[1]) {
|
||||||
this.count = +result[1];
|
this.count = +result[1];
|
||||||
@@ -35,6 +35,8 @@ class Dice {
|
|||||||
if (result[5]) {
|
if (result[5]) {
|
||||||
this.color = result[5];
|
this.color = result[5];
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw Error("Invalid Text: " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,14 +53,16 @@ class DiceHistoryEntry {
|
|||||||
const default_sides = [4, 6, 8, 10, 12, 20, 100];
|
const default_sides = [4, 6, 8, 10, 12, 20, 100];
|
||||||
const default_colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2", "#6c584c"];
|
const default_colors = ["#de324c", "#f4895f", "#f8e16f", "#95cf92", "#369acc", "#9656a2", "#6c584c"];
|
||||||
|
|
||||||
const dice_regex = /(\d+)?[D|d](\d+)([\+|\-]\d+)?(\[(.+)\])?/;
|
const dice_regex = new RegExp("(\\d+)?[D|d](\\d+)([\\+|\\-]\\d+)?(\\[(.+)\\])?");
|
||||||
|
|
||||||
let darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
let darkMode = localStorage.getItem('dark-mode') === 'false' ? false : localStorage.getItem('dark-mode') === 'true' || window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
|
||||||
|
let locale = localStorage.getItem('locale') || 'en';
|
||||||
|
let localeData = {};
|
||||||
|
|
||||||
let dices = [];
|
let dices = [];
|
||||||
let history = [];
|
let history = [];
|
||||||
let redoHistory = [];
|
let redoHistory = [];
|
||||||
|
|
||||||
let dicesContainer = document.getElementById("dices");
|
let dicesContainer = document.getElementById("dices");
|
||||||
let historyContainer = document.getElementById("history");
|
let historyContainer = document.getElementById("history");
|
||||||
|
|
||||||
@@ -264,6 +268,7 @@ function renderHistory() {
|
|||||||
historyContainer.appendChild(diceTime);
|
historyContainer.appendChild(diceTime);
|
||||||
|
|
||||||
if (entry.time) {
|
if (entry.time) {
|
||||||
|
entry.time.locale(locale);
|
||||||
diceTime.innerText = entry.time.fromNow();
|
diceTime.innerText = entry.time.fromNow();
|
||||||
diceTime.title = entry.time.format("LLLL");
|
diceTime.title = entry.time.format("LLLL");
|
||||||
}
|
}
|
||||||
@@ -468,13 +473,14 @@ function toggleDarkMode() {
|
|||||||
darkMode = false;
|
darkMode = false;
|
||||||
document.body.classList.remove("dark");
|
document.body.classList.remove("dark");
|
||||||
document.getElementById("dark-mode-icon").src = "./assets/dark.svg";
|
document.getElementById("dark-mode-icon").src = "./assets/dark.svg";
|
||||||
document.getElementById("dark-mode-text").innerText = "Dark Mode";
|
document.getElementById("dark-mode-text").innerText = localeData && localeData['dark-mode'] || "Dark Mode";
|
||||||
} else {
|
} else {
|
||||||
darkMode = true;
|
darkMode = true;
|
||||||
document.body.classList.add("dark");
|
document.body.classList.add("dark");
|
||||||
document.getElementById("dark-mode-icon").src = "./assets/light.svg";
|
document.getElementById("dark-mode-icon").src = "./assets/light.svg";
|
||||||
document.getElementById("dark-mode-text").innerText = "Light Mode";
|
document.getElementById("dark-mode-text").innerText = localeData && localeData['light-mode'] || "Light Mode";
|
||||||
}
|
}
|
||||||
|
localStorage.setItem('dark-mode', darkMode);
|
||||||
renderDices();
|
renderDices();
|
||||||
renderHistory();
|
renderHistory();
|
||||||
}
|
}
|
||||||
@@ -508,6 +514,17 @@ function redo() {
|
|||||||
renderHistory();
|
renderHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderDarkMode() {
|
||||||
|
if (darkMode) {
|
||||||
|
document.body.classList.add("dark");
|
||||||
|
document.getElementById("dark-mode-icon").src = "./assets/light.svg";
|
||||||
|
document.getElementById("dark-mode-text").innerText = localeData && localeData['light-mode'] || "Light Mode";
|
||||||
|
} else {
|
||||||
|
document.getElementById("dark-mode-icon").src = "./assets/dark.svg";
|
||||||
|
document.getElementById("dark-mode-text").innerText = localeData && localeData['dark-mode'] || "Dark Mode";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function clearAndRefresh() {
|
async function clearAndRefresh() {
|
||||||
if ('caches' in window) {
|
if ('caches' in window) {
|
||||||
const keyList = await caches.keys();
|
const keyList = await caches.keys();
|
||||||
@@ -516,6 +533,33 @@ async function clearAndRefresh() {
|
|||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setLocale(newLocale) {
|
||||||
|
locale = newLocale;
|
||||||
|
localStorage.setItem('locale', locale);
|
||||||
|
const response = await fetch('assets/i18n/' + locale + '.json');
|
||||||
|
localeData = await response.json();
|
||||||
|
if (localeData) {
|
||||||
|
document.querySelectorAll('[data-i18n]').forEach((el) => {
|
||||||
|
const key = el.getAttribute('data-i18n');
|
||||||
|
if (localeData[key]) {
|
||||||
|
el.innerHTML = localeData[key];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-i18n-placeholder]').forEach((el) => {
|
||||||
|
const key = el.getAttribute('data-i18n-placeholder');
|
||||||
|
if (localeData[key]) {
|
||||||
|
el.placeholder = localeData[key];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
moment.locale(locale);
|
||||||
|
renderDices();
|
||||||
|
renderHistory();
|
||||||
|
renderDarkMode();
|
||||||
|
updateCustom();
|
||||||
|
}
|
||||||
|
|
||||||
if (localStorage.getItem('dices')) {
|
if (localStorage.getItem('dices')) {
|
||||||
dices = JSON.parse(localStorage.getItem('dices')).map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color));
|
dices = JSON.parse(localStorage.getItem('dices')).map((dice) => new Dice(dice.sides, dice.addition, dice.count, dice.color));
|
||||||
} else {
|
} else {
|
||||||
@@ -542,6 +586,17 @@ document.getElementById("inputText").addEventListener("keyup", (event) => {
|
|||||||
|
|
||||||
|
|
||||||
document.addEventListener("keyup", (event) => {
|
document.addEventListener("keyup", (event) => {
|
||||||
|
let prevent = false;
|
||||||
|
document.querySelectorAll('input').forEach((input) => {
|
||||||
|
if (input === document.activeElement) {
|
||||||
|
prevent = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (prevent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isNaN(+event.key)) {
|
if (!isNaN(+event.key)) {
|
||||||
let number = +event.key;
|
let number = +event.key;
|
||||||
if (number == 0) {
|
if (number == 0) {
|
||||||
@@ -561,15 +616,7 @@ document.addEventListener("keyup", (event) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
renderDices();
|
setLocale(locale);
|
||||||
renderHistory();
|
|
||||||
updateCustom();
|
|
||||||
|
|
||||||
if (darkMode) {
|
|
||||||
document.body.classList.add("dark");
|
|
||||||
document.getElementById("dark-mode-icon").src = "./assets/light.svg";
|
|
||||||
document.getElementById("dark-mode-text").innerText = "Light Mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
window.addEventListener("load", function () {
|
window.addEventListener("load", function () {
|
||||||
|
|||||||