add prevention of ctrl+c ctrl + v
This commit is contained in:
parent
ad64bae67b
commit
82f8e46cde
181
js/play.js
181
js/play.js
@ -1,4 +1,4 @@
|
||||
(function() {
|
||||
(function () {
|
||||
// --- Konfiguration ---
|
||||
const MEMORIZE_TIME_SECONDS = 15;
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"Der mutige Browser",
|
||||
"Eine schlaue Funktion",
|
||||
"Der vergessliche Server",
|
||||
"Die kreative Gruppe"
|
||||
"Die kreative Gruppe",
|
||||
],
|
||||
actions: [
|
||||
"sortiert leise",
|
||||
@ -22,7 +22,7 @@
|
||||
"rendert ploetzlich",
|
||||
"zaehlt konzentriert",
|
||||
"testet neugierig",
|
||||
"kompiliert langsam"
|
||||
"kompiliert langsam",
|
||||
],
|
||||
objects: [
|
||||
"sieben blaue Buttons",
|
||||
@ -32,7 +32,7 @@
|
||||
"acht schnelle Requests",
|
||||
"zwei leuchtende Karten",
|
||||
"fuenf stille Fehlermeldungen",
|
||||
"sechs winzige Icons"
|
||||
"sechs winzige Icons",
|
||||
],
|
||||
places: [
|
||||
"im hellen Dashboard",
|
||||
@ -42,7 +42,7 @@
|
||||
"vor dem ersten Kaffee",
|
||||
"waehrend der Lernphase",
|
||||
"hinter dem lokalen Server",
|
||||
"mitten im Semesterprojekt"
|
||||
"mitten im Semesterprojekt",
|
||||
],
|
||||
endings: [
|
||||
"Danach lacht der Code, weil alles endlich funktioniert.",
|
||||
@ -50,8 +50,8 @@
|
||||
"Kurz darauf blinkt die Konsole und behauptet, sie sei unschuldig.",
|
||||
"Spaeter landet der Score im Ranking und wartet auf Applaus.",
|
||||
"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;
|
||||
@ -87,21 +87,28 @@
|
||||
let generatedText = "";
|
||||
|
||||
do {
|
||||
const firstSentence = [
|
||||
const firstSentence =
|
||||
[
|
||||
getRandomItem(TEXT_PARTS.subjects),
|
||||
getRandomItem(TEXT_PARTS.actions),
|
||||
getRandomItem(TEXT_PARTS.objects),
|
||||
getRandomItem(TEXT_PARTS.places)
|
||||
getRandomItem(TEXT_PARTS.places),
|
||||
].join(" ") + ".";
|
||||
|
||||
const secondSentence = [
|
||||
const secondSentence =
|
||||
[
|
||||
getRandomItem(TEXT_PARTS.subjects),
|
||||
getRandomItem(TEXT_PARTS.actions),
|
||||
getRandomItem(TEXT_PARTS.objects),
|
||||
getRandomItem(TEXT_PARTS.places)
|
||||
getRandomItem(TEXT_PARTS.places),
|
||||
].join(" ") + ".";
|
||||
|
||||
generatedText = firstSentence + " " + secondSentence + " " + getRandomItem(TEXT_PARTS.endings);
|
||||
generatedText =
|
||||
firstSentence +
|
||||
" " +
|
||||
secondSentence +
|
||||
" " +
|
||||
getRandomItem(TEXT_PARTS.endings);
|
||||
} while (generatedText === lastGeneratedText);
|
||||
|
||||
lastGeneratedText = generatedText;
|
||||
@ -112,10 +119,10 @@
|
||||
if (!phaseStart || !phaseMemorize) return;
|
||||
|
||||
// Startansicht ausblenden und den neu generierten Text fuer die Lernphase anzeigen.
|
||||
phaseStart.classList.add('d-none');
|
||||
phaseMemorize.classList.remove('d-none');
|
||||
phaseStart.classList.add("d-none");
|
||||
phaseMemorize.classList.remove("d-none");
|
||||
|
||||
if(gameStatus) {
|
||||
if (gameStatus) {
|
||||
gameStatus.textContent = "Lernphase";
|
||||
gameStatus.className = "badge fs-6 px-3 py-2";
|
||||
gameStatus.style.backgroundColor = "#ffd166";
|
||||
@ -123,15 +130,15 @@
|
||||
}
|
||||
|
||||
currentGameText = generateGameText();
|
||||
if(targetTextDisplay) targetTextDisplay.textContent = currentGameText;
|
||||
if (targetTextDisplay) targetTextDisplay.textContent = currentGameText;
|
||||
|
||||
// Nach Ablauf des Timers wird automatisch zur Eingabe gewechselt.
|
||||
currentTime = MEMORIZE_TIME_SECONDS;
|
||||
if(timerDisplay) timerDisplay.textContent = currentTime;
|
||||
if (timerDisplay) timerDisplay.textContent = currentTime;
|
||||
|
||||
timerInterval = setInterval(() => {
|
||||
currentTime--;
|
||||
if(timerDisplay) timerDisplay.textContent = currentTime;
|
||||
if (timerDisplay) timerDisplay.textContent = currentTime;
|
||||
|
||||
if (currentTime <= 0) {
|
||||
endMemorizePhase();
|
||||
@ -145,17 +152,17 @@
|
||||
if (!phaseMemorize || !phaseInput) return;
|
||||
|
||||
// Text verschwindet, Eingabefeld erscheint: ab hier zaehlt nur noch das Gedaechtnis.
|
||||
phaseMemorize.classList.add('d-none');
|
||||
phaseInput.classList.remove('d-none');
|
||||
phaseMemorize.classList.add("d-none");
|
||||
phaseInput.classList.remove("d-none");
|
||||
|
||||
if(gameStatus) {
|
||||
if (gameStatus) {
|
||||
gameStatus.textContent = "Eingabe";
|
||||
gameStatus.className = "badge fs-6 px-3 py-2";
|
||||
gameStatus.style.backgroundColor = "#4a6fa5";
|
||||
gameStatus.style.color = "#fff";
|
||||
}
|
||||
|
||||
if(userTextInput) {
|
||||
if (userTextInput) {
|
||||
userTextInput.value = "";
|
||||
userTextInput.focus();
|
||||
}
|
||||
@ -168,15 +175,19 @@
|
||||
|
||||
// Behält die sichtbaren Woerter separat, damit Satzzeichen in der Ergebnisanzeige erhalten bleiben.
|
||||
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) {
|
||||
if (!original || !input) return 0;
|
||||
|
||||
// Score-Regel: gleiche Woerter an gleicher Position, Satzzeichen und Grossschreibung ignoriert.
|
||||
const cleanOriginal = getWords(original).map(normalizeWord).filter(word => word.length > 0);
|
||||
const cleanInput = getWords(input).map(normalizeWord).filter(word => word.length > 0);
|
||||
const cleanOriginal = getWords(original)
|
||||
.map(normalizeWord)
|
||||
.filter((word) => word.length > 0);
|
||||
const cleanInput = getWords(input)
|
||||
.map(normalizeWord)
|
||||
.filter((word) => word.length > 0);
|
||||
|
||||
let correctWords = 0;
|
||||
const limit = Math.min(cleanOriginal.length, cleanInput.length);
|
||||
@ -193,7 +204,8 @@
|
||||
// Baut ein einzelnes farbiges Wort-Label fuer den Ergebnisvergleich.
|
||||
function createWordBadge(word, isCorrect) {
|
||||
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;
|
||||
return badge;
|
||||
}
|
||||
@ -210,13 +222,15 @@
|
||||
|
||||
// Original: rot, wenn das eingegebene Wort an dieser Position fehlt oder falsch ist.
|
||||
originalWords.forEach((word, index) => {
|
||||
const isCorrect = normalizeWord(word) === normalizeWord(inputWords[index] || "");
|
||||
const isCorrect =
|
||||
normalizeWord(word) === normalizeWord(inputWords[index] || "");
|
||||
resultOriginal.appendChild(createWordBadge(word, isCorrect));
|
||||
});
|
||||
|
||||
// Eingabe: rot, wenn das Wort nicht zum Originalwort an derselben Position passt.
|
||||
inputWords.forEach((word, index) => {
|
||||
const isCorrect = normalizeWord(word) === normalizeWord(originalWords[index] || "");
|
||||
const isCorrect =
|
||||
normalizeWord(word) === normalizeWord(originalWords[index] || "");
|
||||
resultInput.appendChild(createWordBadge(word, isCorrect));
|
||||
});
|
||||
}
|
||||
@ -250,7 +264,7 @@
|
||||
if (!auth || !auth.username || !auth.password) {
|
||||
showScoreSaveFeedback(
|
||||
"Score wurde nur lokal berechnet. Bitte einloggen, damit er im Leaderboard gespeichert wird.",
|
||||
"warning"
|
||||
"warning",
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -258,7 +272,10 @@
|
||||
// Auth-Daten kommen aus login.js; der ScoreService setzt daraus die Backend-Header.
|
||||
const scoreService = getScoreService();
|
||||
if (!scoreService) {
|
||||
showScoreSaveFeedback("Score-Service konnte nicht geladen werden.", "danger");
|
||||
showScoreSaveFeedback(
|
||||
"Score-Service konnte nicht geladen werden.",
|
||||
"danger",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -270,21 +287,33 @@
|
||||
scoreData.score,
|
||||
scoreData.time,
|
||||
scoreData.text,
|
||||
scoreData.userWrittenText
|
||||
scoreData.userWrittenText,
|
||||
);
|
||||
|
||||
if (result.ok) {
|
||||
const place = result.body && result.body.place ? " Platz " + result.body.place + "." : "";
|
||||
showScoreSaveFeedback("Score erfolgreich gespeichert." + place, "success");
|
||||
const place =
|
||||
result.body && result.body.place
|
||||
? " Platz " + result.body.place + "."
|
||||
: "";
|
||||
showScoreSaveFeedback(
|
||||
"Score erfolgreich gespeichert." + place,
|
||||
"success",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
showScoreSaveFeedback("Score konnte nicht gespeichert werden (Status " + result.status + ").", "danger");
|
||||
showScoreSaveFeedback(
|
||||
"Score konnte nicht gespeichert werden (Status " + result.status + ").",
|
||||
"danger",
|
||||
);
|
||||
}
|
||||
|
||||
async function submitScore() {
|
||||
@ -304,10 +333,10 @@
|
||||
const score = calculateScore(currentGameText, userInput);
|
||||
|
||||
// Ergebnis sofort anzeigen; das Speichern im Backend passiert danach asynchron.
|
||||
if (phaseInput) phaseInput.classList.add('d-none');
|
||||
if (phaseResult) phaseResult.classList.remove('d-none');
|
||||
if (phaseInput) phaseInput.classList.add("d-none");
|
||||
if (phaseResult) phaseResult.classList.remove("d-none");
|
||||
|
||||
if(gameStatus) {
|
||||
if (gameStatus) {
|
||||
gameStatus.textContent = "Abgeschlossen";
|
||||
gameStatus.className = "badge fs-6 px-3 py-2";
|
||||
gameStatus.style.backgroundColor = "#28a745";
|
||||
@ -322,7 +351,7 @@
|
||||
score: score,
|
||||
time: MEMORIZE_TIME_SECONDS,
|
||||
text: currentGameText,
|
||||
userWrittenText: userInput
|
||||
userWrittenText: userInput,
|
||||
};
|
||||
|
||||
console.log("Score bereit zum Senden:", scoreData);
|
||||
@ -331,7 +360,10 @@
|
||||
await saveScore(scoreData);
|
||||
} catch (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 {
|
||||
if (btnSubmitScore) {
|
||||
btnSubmitScore.disabled = false;
|
||||
@ -344,36 +376,61 @@
|
||||
clearInterval(timerInterval);
|
||||
|
||||
// Die Navigation laedt play.html per fetch; deshalb werden die Elemente erst hier gesucht.
|
||||
phaseStart = document.getElementById('phaseStart');
|
||||
phaseMemorize = document.getElementById('phaseMemorize');
|
||||
phaseInput = document.getElementById('phaseInput');
|
||||
phaseResult = document.getElementById('phaseResult');
|
||||
phaseStart = document.getElementById("phaseStart");
|
||||
phaseMemorize = document.getElementById("phaseMemorize");
|
||||
phaseInput = document.getElementById("phaseInput");
|
||||
phaseResult = document.getElementById("phaseResult");
|
||||
|
||||
targetTextDisplay = document.getElementById('targetTextDisplay');
|
||||
timerDisplay = document.getElementById('timerDisplay');
|
||||
userTextInput = document.getElementById('userTextInput');
|
||||
resultScore = document.getElementById('resultScore');
|
||||
resultOriginal = document.getElementById('resultOriginal');
|
||||
resultInput = document.getElementById('resultInput');
|
||||
gameStatus = document.getElementById('gameStatus');
|
||||
scoreSaveFeedback = document.getElementById('scoreSaveFeedback');
|
||||
targetTextDisplay = document.getElementById("targetTextDisplay");
|
||||
timerDisplay = document.getElementById("timerDisplay");
|
||||
userTextInput = document.getElementById("userTextInput");
|
||||
resultScore = document.getElementById("resultScore");
|
||||
resultOriginal = document.getElementById("resultOriginal");
|
||||
resultInput = document.getElementById("resultInput");
|
||||
gameStatus = document.getElementById("gameStatus");
|
||||
scoreSaveFeedback = document.getElementById("scoreSaveFeedback");
|
||||
|
||||
const btnStart = document.getElementById('btnStartGame');
|
||||
btnSubmitScore = document.getElementById('btnSubmitScore');
|
||||
const btnRestart = document.getElementById('btnRestart');
|
||||
const btnLeaderboard = document.getElementById('btnLeaderboard');
|
||||
const btnStart = document.getElementById("btnStartGame");
|
||||
btnSubmitScore = document.getElementById("btnSubmitScore");
|
||||
const btnRestart = document.getElementById("btnRestart");
|
||||
const btnLeaderboard = document.getElementById("btnLeaderboard");
|
||||
|
||||
if (btnStart) btnStart.addEventListener('click', startGame);
|
||||
if (btnSubmitScore) btnSubmitScore.addEventListener('click', submitScore);
|
||||
if (btnRestart) btnRestart.addEventListener('click', () => window.loadPage("play", "nav-play"));
|
||||
if (btnLeaderboard) btnLeaderboard.addEventListener('click', () => {
|
||||
const navLink = document.getElementById('nav-leaderboard');
|
||||
if (btnStart) btnStart.addEventListener("click", startGame);
|
||||
if (btnSubmitScore) btnSubmitScore.addEventListener("click", submitScore);
|
||||
if (btnRestart)
|
||||
btnRestart.addEventListener("click", () =>
|
||||
window.loadPage("play", "nav-play"),
|
||||
);
|
||||
if (btnLeaderboard)
|
||||
btnLeaderboard.addEventListener("click", () => {
|
||||
const navLink = document.getElementById("nav-leaderboard");
|
||||
if (navLink) {
|
||||
navLink.click(); // Nutzt die bestehende Navigation inklusive Active-State.
|
||||
} else {
|
||||
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