Merge pull request 'add prevention of ctrl+c ctrl + v' (#8) from prohibit-copy into main
Reviewed-on: #8
This commit is contained in:
commit
57ae0ccda5
165
js/play.js
165
js/play.js
@ -12,7 +12,7 @@
|
|||||||
"Der mutige Browser",
|
"Der mutige Browser",
|
||||||
"Eine schlaue Funktion",
|
"Eine schlaue Funktion",
|
||||||
"Der vergessliche Server",
|
"Der vergessliche Server",
|
||||||
"Die kreative Gruppe"
|
"Die kreative Gruppe",
|
||||||
],
|
],
|
||||||
actions: [
|
actions: [
|
||||||
"sortiert leise",
|
"sortiert leise",
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"rendert ploetzlich",
|
"rendert ploetzlich",
|
||||||
"zaehlt konzentriert",
|
"zaehlt konzentriert",
|
||||||
"testet neugierig",
|
"testet neugierig",
|
||||||
"kompiliert langsam"
|
"kompiliert langsam",
|
||||||
],
|
],
|
||||||
objects: [
|
objects: [
|
||||||
"sieben blaue Buttons",
|
"sieben blaue Buttons",
|
||||||
@ -32,7 +32,7 @@
|
|||||||
"acht schnelle Requests",
|
"acht schnelle Requests",
|
||||||
"zwei leuchtende Karten",
|
"zwei leuchtende Karten",
|
||||||
"fuenf stille Fehlermeldungen",
|
"fuenf stille Fehlermeldungen",
|
||||||
"sechs winzige Icons"
|
"sechs winzige Icons",
|
||||||
],
|
],
|
||||||
places: [
|
places: [
|
||||||
"im hellen Dashboard",
|
"im hellen Dashboard",
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"vor dem ersten Kaffee",
|
"vor dem ersten Kaffee",
|
||||||
"waehrend der Lernphase",
|
"waehrend der Lernphase",
|
||||||
"hinter dem lokalen Server",
|
"hinter dem lokalen Server",
|
||||||
"mitten im Semesterprojekt"
|
"mitten im Semesterprojekt",
|
||||||
],
|
],
|
||||||
endings: [
|
endings: [
|
||||||
"Danach lacht der Code, weil alles endlich funktioniert.",
|
"Danach lacht der Code, weil alles endlich funktioniert.",
|
||||||
@ -50,8 +50,8 @@
|
|||||||
"Kurz darauf blinkt die Konsole und behauptet, sie sei unschuldig.",
|
"Kurz darauf blinkt die Konsole und behauptet, sie sei unschuldig.",
|
||||||
"Spaeter landet der Score im Ranking und wartet auf Applaus.",
|
"Spaeter landet der Score im Ranking und wartet auf Applaus.",
|
||||||
"Dabei bleibt die Seite ruhig, obwohl der Timer dramatisch tickt.",
|
"Dabei bleibt die Seite ruhig, obwohl der Timer dramatisch tickt.",
|
||||||
"Zum Schluss gewinnt, wer die Woerter sauber in Reihenfolge bringt."
|
"Zum Schluss gewinnt, wer die Woerter sauber in Reihenfolge bringt.",
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
let timerInterval;
|
let timerInterval;
|
||||||
@ -87,21 +87,28 @@
|
|||||||
let generatedText = "";
|
let generatedText = "";
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const firstSentence = [
|
const firstSentence =
|
||||||
|
[
|
||||||
getRandomItem(TEXT_PARTS.subjects),
|
getRandomItem(TEXT_PARTS.subjects),
|
||||||
getRandomItem(TEXT_PARTS.actions),
|
getRandomItem(TEXT_PARTS.actions),
|
||||||
getRandomItem(TEXT_PARTS.objects),
|
getRandomItem(TEXT_PARTS.objects),
|
||||||
getRandomItem(TEXT_PARTS.places)
|
getRandomItem(TEXT_PARTS.places),
|
||||||
].join(" ") + ".";
|
].join(" ") + ".";
|
||||||
|
|
||||||
const secondSentence = [
|
const secondSentence =
|
||||||
|
[
|
||||||
getRandomItem(TEXT_PARTS.subjects),
|
getRandomItem(TEXT_PARTS.subjects),
|
||||||
getRandomItem(TEXT_PARTS.actions),
|
getRandomItem(TEXT_PARTS.actions),
|
||||||
getRandomItem(TEXT_PARTS.objects),
|
getRandomItem(TEXT_PARTS.objects),
|
||||||
getRandomItem(TEXT_PARTS.places)
|
getRandomItem(TEXT_PARTS.places),
|
||||||
].join(" ") + ".";
|
].join(" ") + ".";
|
||||||
|
|
||||||
generatedText = firstSentence + " " + secondSentence + " " + getRandomItem(TEXT_PARTS.endings);
|
generatedText =
|
||||||
|
firstSentence +
|
||||||
|
" " +
|
||||||
|
secondSentence +
|
||||||
|
" " +
|
||||||
|
getRandomItem(TEXT_PARTS.endings);
|
||||||
} while (generatedText === lastGeneratedText);
|
} while (generatedText === lastGeneratedText);
|
||||||
|
|
||||||
lastGeneratedText = generatedText;
|
lastGeneratedText = generatedText;
|
||||||
@ -112,8 +119,8 @@
|
|||||||
if (!phaseStart || !phaseMemorize) return;
|
if (!phaseStart || !phaseMemorize) return;
|
||||||
|
|
||||||
// Startansicht ausblenden und den neu generierten Text fuer die Lernphase anzeigen.
|
// Startansicht ausblenden und den neu generierten Text fuer die Lernphase anzeigen.
|
||||||
phaseStart.classList.add('d-none');
|
phaseStart.classList.add("d-none");
|
||||||
phaseMemorize.classList.remove('d-none');
|
phaseMemorize.classList.remove("d-none");
|
||||||
|
|
||||||
if (gameStatus) {
|
if (gameStatus) {
|
||||||
gameStatus.textContent = "Lernphase";
|
gameStatus.textContent = "Lernphase";
|
||||||
@ -145,8 +152,8 @@
|
|||||||
if (!phaseMemorize || !phaseInput) return;
|
if (!phaseMemorize || !phaseInput) return;
|
||||||
|
|
||||||
// Text verschwindet, Eingabefeld erscheint: ab hier zaehlt nur noch das Gedaechtnis.
|
// Text verschwindet, Eingabefeld erscheint: ab hier zaehlt nur noch das Gedaechtnis.
|
||||||
phaseMemorize.classList.add('d-none');
|
phaseMemorize.classList.add("d-none");
|
||||||
phaseInput.classList.remove('d-none');
|
phaseInput.classList.remove("d-none");
|
||||||
|
|
||||||
if (gameStatus) {
|
if (gameStatus) {
|
||||||
gameStatus.textContent = "Eingabe";
|
gameStatus.textContent = "Eingabe";
|
||||||
@ -168,15 +175,19 @@
|
|||||||
|
|
||||||
// Behält die sichtbaren Woerter separat, damit Satzzeichen in der Ergebnisanzeige erhalten bleiben.
|
// Behält die sichtbaren Woerter separat, damit Satzzeichen in der Ergebnisanzeige erhalten bleiben.
|
||||||
function getWords(text) {
|
function getWords(text) {
|
||||||
return text.split(/\s+/).filter(word => word.length > 0);
|
return text.split(/\s+/).filter((word) => word.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateScore(original, input) {
|
function calculateScore(original, input) {
|
||||||
if (!original || !input) return 0;
|
if (!original || !input) return 0;
|
||||||
|
|
||||||
// Score-Regel: gleiche Woerter an gleicher Position, Satzzeichen und Grossschreibung ignoriert.
|
// Score-Regel: gleiche Woerter an gleicher Position, Satzzeichen und Grossschreibung ignoriert.
|
||||||
const cleanOriginal = getWords(original).map(normalizeWord).filter(word => word.length > 0);
|
const cleanOriginal = getWords(original)
|
||||||
const cleanInput = getWords(input).map(normalizeWord).filter(word => word.length > 0);
|
.map(normalizeWord)
|
||||||
|
.filter((word) => word.length > 0);
|
||||||
|
const cleanInput = getWords(input)
|
||||||
|
.map(normalizeWord)
|
||||||
|
.filter((word) => word.length > 0);
|
||||||
|
|
||||||
let correctWords = 0;
|
let correctWords = 0;
|
||||||
const limit = Math.min(cleanOriginal.length, cleanInput.length);
|
const limit = Math.min(cleanOriginal.length, cleanInput.length);
|
||||||
@ -193,7 +204,8 @@
|
|||||||
// Baut ein einzelnes farbiges Wort-Label fuer den Ergebnisvergleich.
|
// Baut ein einzelnes farbiges Wort-Label fuer den Ergebnisvergleich.
|
||||||
function createWordBadge(word, isCorrect) {
|
function createWordBadge(word, isCorrect) {
|
||||||
const badge = document.createElement("span");
|
const badge = document.createElement("span");
|
||||||
badge.className = "badge me-1 mb-1 " + (isCorrect ? "text-bg-success" : "text-bg-danger");
|
badge.className =
|
||||||
|
"badge me-1 mb-1 " + (isCorrect ? "text-bg-success" : "text-bg-danger");
|
||||||
badge.textContent = word;
|
badge.textContent = word;
|
||||||
return badge;
|
return badge;
|
||||||
}
|
}
|
||||||
@ -210,13 +222,15 @@
|
|||||||
|
|
||||||
// Original: rot, wenn das eingegebene Wort an dieser Position fehlt oder falsch ist.
|
// Original: rot, wenn das eingegebene Wort an dieser Position fehlt oder falsch ist.
|
||||||
originalWords.forEach((word, index) => {
|
originalWords.forEach((word, index) => {
|
||||||
const isCorrect = normalizeWord(word) === normalizeWord(inputWords[index] || "");
|
const isCorrect =
|
||||||
|
normalizeWord(word) === normalizeWord(inputWords[index] || "");
|
||||||
resultOriginal.appendChild(createWordBadge(word, isCorrect));
|
resultOriginal.appendChild(createWordBadge(word, isCorrect));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Eingabe: rot, wenn das Wort nicht zum Originalwort an derselben Position passt.
|
// Eingabe: rot, wenn das Wort nicht zum Originalwort an derselben Position passt.
|
||||||
inputWords.forEach((word, index) => {
|
inputWords.forEach((word, index) => {
|
||||||
const isCorrect = normalizeWord(word) === normalizeWord(originalWords[index] || "");
|
const isCorrect =
|
||||||
|
normalizeWord(word) === normalizeWord(originalWords[index] || "");
|
||||||
resultInput.appendChild(createWordBadge(word, isCorrect));
|
resultInput.appendChild(createWordBadge(word, isCorrect));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -250,7 +264,7 @@
|
|||||||
if (!auth || !auth.username || !auth.password) {
|
if (!auth || !auth.username || !auth.password) {
|
||||||
showScoreSaveFeedback(
|
showScoreSaveFeedback(
|
||||||
"Score wurde nur lokal berechnet. Bitte einloggen, damit er im Leaderboard gespeichert wird.",
|
"Score wurde nur lokal berechnet. Bitte einloggen, damit er im Leaderboard gespeichert wird.",
|
||||||
"warning"
|
"warning",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -258,7 +272,10 @@
|
|||||||
// Auth-Daten kommen aus login.js; der ScoreService setzt daraus die Backend-Header.
|
// Auth-Daten kommen aus login.js; der ScoreService setzt daraus die Backend-Header.
|
||||||
const scoreService = getScoreService();
|
const scoreService = getScoreService();
|
||||||
if (!scoreService) {
|
if (!scoreService) {
|
||||||
showScoreSaveFeedback("Score-Service konnte nicht geladen werden.", "danger");
|
showScoreSaveFeedback(
|
||||||
|
"Score-Service konnte nicht geladen werden.",
|
||||||
|
"danger",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,21 +287,33 @@
|
|||||||
scoreData.score,
|
scoreData.score,
|
||||||
scoreData.time,
|
scoreData.time,
|
||||||
scoreData.text,
|
scoreData.text,
|
||||||
scoreData.userWrittenText
|
scoreData.userWrittenText,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
const place = result.body && result.body.place ? " Platz " + result.body.place + "." : "";
|
const place =
|
||||||
showScoreSaveFeedback("Score erfolgreich gespeichert." + place, "success");
|
result.body && result.body.place
|
||||||
|
? " Platz " + result.body.place + "."
|
||||||
|
: "";
|
||||||
|
showScoreSaveFeedback(
|
||||||
|
"Score erfolgreich gespeichert." + place,
|
||||||
|
"success",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.status === 401) {
|
if (result.status === 401) {
|
||||||
showScoreSaveFeedback("Score konnte nicht gespeichert werden: Login ist nicht gültig.", "danger");
|
showScoreSaveFeedback(
|
||||||
|
"Score konnte nicht gespeichert werden: Login ist nicht gültig.",
|
||||||
|
"danger",
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
showScoreSaveFeedback("Score konnte nicht gespeichert werden (Status " + result.status + ").", "danger");
|
showScoreSaveFeedback(
|
||||||
|
"Score konnte nicht gespeichert werden (Status " + result.status + ").",
|
||||||
|
"danger",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitScore() {
|
async function submitScore() {
|
||||||
@ -304,8 +333,8 @@
|
|||||||
const score = calculateScore(currentGameText, userInput);
|
const score = calculateScore(currentGameText, userInput);
|
||||||
|
|
||||||
// Ergebnis sofort anzeigen; das Speichern im Backend passiert danach asynchron.
|
// Ergebnis sofort anzeigen; das Speichern im Backend passiert danach asynchron.
|
||||||
if (phaseInput) phaseInput.classList.add('d-none');
|
if (phaseInput) phaseInput.classList.add("d-none");
|
||||||
if (phaseResult) phaseResult.classList.remove('d-none');
|
if (phaseResult) phaseResult.classList.remove("d-none");
|
||||||
|
|
||||||
if (gameStatus) {
|
if (gameStatus) {
|
||||||
gameStatus.textContent = "Abgeschlossen";
|
gameStatus.textContent = "Abgeschlossen";
|
||||||
@ -322,7 +351,7 @@
|
|||||||
score: score,
|
score: score,
|
||||||
time: MEMORIZE_TIME_SECONDS,
|
time: MEMORIZE_TIME_SECONDS,
|
||||||
text: currentGameText,
|
text: currentGameText,
|
||||||
userWrittenText: userInput
|
userWrittenText: userInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Score bereit zum Senden:", scoreData);
|
console.log("Score bereit zum Senden:", scoreData);
|
||||||
@ -331,7 +360,10 @@
|
|||||||
await saveScore(scoreData);
|
await saveScore(scoreData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Fehler beim Speichern des Scores:", error);
|
console.error("Fehler beim Speichern des Scores:", error);
|
||||||
showScoreSaveFeedback("Score konnte wegen eines technischen Fehlers nicht gespeichert werden.", "danger");
|
showScoreSaveFeedback(
|
||||||
|
"Score konnte wegen eines technischen Fehlers nicht gespeichert werden.",
|
||||||
|
"danger",
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
if (btnSubmitScore) {
|
if (btnSubmitScore) {
|
||||||
btnSubmitScore.disabled = false;
|
btnSubmitScore.disabled = false;
|
||||||
@ -344,36 +376,61 @@
|
|||||||
clearInterval(timerInterval);
|
clearInterval(timerInterval);
|
||||||
|
|
||||||
// Die Navigation laedt play.html per fetch; deshalb werden die Elemente erst hier gesucht.
|
// Die Navigation laedt play.html per fetch; deshalb werden die Elemente erst hier gesucht.
|
||||||
phaseStart = document.getElementById('phaseStart');
|
phaseStart = document.getElementById("phaseStart");
|
||||||
phaseMemorize = document.getElementById('phaseMemorize');
|
phaseMemorize = document.getElementById("phaseMemorize");
|
||||||
phaseInput = document.getElementById('phaseInput');
|
phaseInput = document.getElementById("phaseInput");
|
||||||
phaseResult = document.getElementById('phaseResult');
|
phaseResult = document.getElementById("phaseResult");
|
||||||
|
|
||||||
targetTextDisplay = document.getElementById('targetTextDisplay');
|
targetTextDisplay = document.getElementById("targetTextDisplay");
|
||||||
timerDisplay = document.getElementById('timerDisplay');
|
timerDisplay = document.getElementById("timerDisplay");
|
||||||
userTextInput = document.getElementById('userTextInput');
|
userTextInput = document.getElementById("userTextInput");
|
||||||
resultScore = document.getElementById('resultScore');
|
resultScore = document.getElementById("resultScore");
|
||||||
resultOriginal = document.getElementById('resultOriginal');
|
resultOriginal = document.getElementById("resultOriginal");
|
||||||
resultInput = document.getElementById('resultInput');
|
resultInput = document.getElementById("resultInput");
|
||||||
gameStatus = document.getElementById('gameStatus');
|
gameStatus = document.getElementById("gameStatus");
|
||||||
scoreSaveFeedback = document.getElementById('scoreSaveFeedback');
|
scoreSaveFeedback = document.getElementById("scoreSaveFeedback");
|
||||||
|
|
||||||
const btnStart = document.getElementById('btnStartGame');
|
const btnStart = document.getElementById("btnStartGame");
|
||||||
btnSubmitScore = document.getElementById('btnSubmitScore');
|
btnSubmitScore = document.getElementById("btnSubmitScore");
|
||||||
const btnRestart = document.getElementById('btnRestart');
|
const btnRestart = document.getElementById("btnRestart");
|
||||||
const btnLeaderboard = document.getElementById('btnLeaderboard');
|
const btnLeaderboard = document.getElementById("btnLeaderboard");
|
||||||
|
|
||||||
if (btnStart) btnStart.addEventListener('click', startGame);
|
if (btnStart) btnStart.addEventListener("click", startGame);
|
||||||
if (btnSubmitScore) btnSubmitScore.addEventListener('click', submitScore);
|
if (btnSubmitScore) btnSubmitScore.addEventListener("click", submitScore);
|
||||||
if (btnRestart) btnRestart.addEventListener('click', () => window.loadPage("play", "nav-play"));
|
if (btnRestart)
|
||||||
if (btnLeaderboard) btnLeaderboard.addEventListener('click', () => {
|
btnRestart.addEventListener("click", () =>
|
||||||
const navLink = document.getElementById('nav-leaderboard');
|
window.loadPage("play", "nav-play"),
|
||||||
|
);
|
||||||
|
if (btnLeaderboard)
|
||||||
|
btnLeaderboard.addEventListener("click", () => {
|
||||||
|
const navLink = document.getElementById("nav-leaderboard");
|
||||||
if (navLink) {
|
if (navLink) {
|
||||||
navLink.click(); // Nutzt die bestehende Navigation inklusive Active-State.
|
navLink.click(); // Nutzt die bestehende Navigation inklusive Active-State.
|
||||||
} else {
|
} else {
|
||||||
console.warn("Sidebar Link #nav-leaderboard nicht gefunden.");
|
console.warn("Sidebar Link #nav-leaderboard nicht gefunden.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
|
if (userTextInput) {
|
||||||
|
userTextInput.addEventListener("keydown", (e) => {
|
||||||
|
const key = e.key.toLowerCase();
|
||||||
|
if (
|
||||||
|
(e.ctrlKey || e.metaKey) &&
|
||||||
|
(key === "c" || key === "v" || key === "x" || key === "a")
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
alert("Yeah, cheating is not sooo nice...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
userTextInput.addEventListener("paste", (e) => e.preventDefault());
|
||||||
|
userTextInput.addEventListener("copy", (e) => e.preventDefault());
|
||||||
|
userTextInput.addEventListener("cut", (e) => e.preventDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetTextDisplay) {
|
||||||
|
targetTextDisplay.addEventListener("copy", (e) => e.preventDefault());
|
||||||
|
targetTextDisplay.style.userSelect = "none";
|
||||||
|
}
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user