372 lines
9.8 KiB
JavaScript
372 lines
9.8 KiB
JavaScript
/**
|
|
* Notenrechner - Berechnet Noten aus Punktzahlen
|
|
* David & Karo - EIS Projekt
|
|
*/
|
|
|
|
// Notenmaßstab definieren
|
|
const NOTENMASSTAB = {
|
|
'sehr gut': { min: 90, max: 100 },
|
|
'gut': { min: 75, max: 89 },
|
|
'befriedigend': { min: 60, max: 74 },
|
|
'ausreichend': { min: 50, max: 59 },
|
|
'nicht bestanden': { min: 0, max: 49 }
|
|
};
|
|
|
|
/**
|
|
* Berechnet die Note basierend auf der Punktzahl
|
|
* @param {number} punkte - Erreichte Punktzahl
|
|
* @param {number} maxPunkte - Maximale Punktzahl (default: 100)
|
|
* @returns {object} - Enthält Note, Prozentage und Details
|
|
*/
|
|
function berechneNote(punkte, maxPunkte = 100) {
|
|
// Eingabe validieren
|
|
punkte = parseFloat(punkte);
|
|
maxPunkte = parseFloat(maxPunkte);
|
|
|
|
if (isNaN(punkte) || isNaN(maxPunkte)) {
|
|
return {
|
|
note: 'Fehler',
|
|
prozent: 0,
|
|
valid: false,
|
|
nachricht: 'Bitte geben Sie Zahlen ein!'
|
|
};
|
|
}
|
|
|
|
if (maxPunkte <= 0) {
|
|
return {
|
|
note: 'Fehler',
|
|
prozent: 0,
|
|
valid: false,
|
|
nachricht: 'Maximale Punktzahl muss größer als 0 sein!'
|
|
};
|
|
}
|
|
|
|
if (punkte < 0 || punkte > maxPunkte) {
|
|
return {
|
|
note: 'Fehler',
|
|
prozent: 0,
|
|
valid: false,
|
|
nachricht: `Punkte müssen zwischen 0 und ${maxPunkte} liegen!`
|
|
};
|
|
}
|
|
|
|
// Prozentage berechnen
|
|
const prozent = Math.round((punkte / maxPunkte) * 100);
|
|
|
|
// Note basierend auf Prozentage bestimmen
|
|
let note = 'nicht bestanden';
|
|
|
|
for (const [noteText, range] of Object.entries(NOTENMASSTAB)) {
|
|
if (prozent >= range.min && prozent <= range.max) {
|
|
note = noteText;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
note: note,
|
|
prozent: prozent,
|
|
punkte: punkte,
|
|
maxPunkte: maxPunkte,
|
|
valid: true,
|
|
nachricht: `${punkte} von ${maxPunkte} Punkten (${prozent}%) = ${note}`
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Berechnet den Durchschnitt mehrerer Noten
|
|
* @param {array} noten - Array von Objekten mit Note-Informationen
|
|
* @returns {object} - Durchschnitt und Gesamtnote
|
|
*/
|
|
function berechneDurchschnitt(noten) {
|
|
if (!noten || noten.length === 0) {
|
|
return {
|
|
valid: false,
|
|
nachricht: 'Keine Noten zum Berechnen vorhanden!'
|
|
};
|
|
}
|
|
|
|
// Alle gültigen Prozentagen sammeln
|
|
const prozente = noten
|
|
.filter(n => n.valid)
|
|
.map(n => n.prozent);
|
|
|
|
if (prozente.length === 0) {
|
|
return {
|
|
valid: false,
|
|
nachricht: 'Keine gültigen Noten zum Berechnen vorhanden!'
|
|
};
|
|
}
|
|
|
|
// Durchschnitt berechnen
|
|
const durchschnittProzent = Math.round(
|
|
prozente.reduce((sum, p) => sum + p, 0) / prozente.length
|
|
);
|
|
|
|
// Note für Durchschnitt bestimmen
|
|
let durchschnittNote = 'nicht bestanden';
|
|
for (const [noteText, range] of Object.entries(NOTENMASSTAB)) {
|
|
if (durchschnittProzent >= range.min && durchschnittProzent <= range.max) {
|
|
durchschnittNote = noteText;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {
|
|
anzahl: noten.length,
|
|
gueltig: prozente.length,
|
|
durchschnittProzent: durchschnittProzent,
|
|
durchschnittNote: durchschnittNote,
|
|
valid: true,
|
|
nachricht: `Durchschnitt: ${durchschnittProzent}% = ${durchschnittNote}`
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Live-Konvertierung von Note zu Punkten
|
|
* @param {string} noteText - Die Note als Text
|
|
* @param {number} maxPunkte - Maximale Punktzahl
|
|
* @returns {object} - Min und Max Punkte für diese Note
|
|
*/
|
|
function noteZuPunkte(noteText, maxPunkte = 100) {
|
|
const range = NOTENMASSTAB[noteText.toLowerCase()];
|
|
|
|
if (!range) {
|
|
return {
|
|
valid: false,
|
|
nachricht: 'Unbekannte Note!'
|
|
};
|
|
}
|
|
|
|
const minPunkte = Math.round((range.min / 100) * maxPunkte);
|
|
const maxPunkteNote = Math.round((range.max / 100) * maxPunkte);
|
|
|
|
return {
|
|
note: noteText,
|
|
minPunkte: minPunkte,
|
|
maxPunkte: maxPunkteNote,
|
|
valid: true,
|
|
nachricht: `Note "${noteText}": ${minPunkte}-${maxPunkteNote} Punkte von ${maxPunkte}`
|
|
};
|
|
}
|
|
|
|
// DOM-Manipulation und Event-Listener
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const addBtn = document.getElementById('add-grade-btn');
|
|
const calculateBtn = document.getElementById('calculate-btn');
|
|
const resetBtn = document.getElementById('reset-btn');
|
|
const gradesContainer = document.getElementById('grades-container');
|
|
const resultDiv = document.getElementById('result');
|
|
const averageDiv = document.getElementById('average-result');
|
|
|
|
// Neue Note hinzufügen
|
|
if (addBtn) {
|
|
addBtn.addEventListener('click', function() {
|
|
addGradeInput();
|
|
});
|
|
}
|
|
|
|
// Berechnen-Button
|
|
if (calculateBtn) {
|
|
calculateBtn.addEventListener('click', function() {
|
|
calculateAllGrades();
|
|
});
|
|
}
|
|
|
|
// Zurücksetzen-Button
|
|
if (resetBtn) {
|
|
resetBtn.addEventListener('click', function() {
|
|
resetCalculator();
|
|
});
|
|
}
|
|
|
|
// Initiale Note hinzufügen
|
|
if (gradesContainer) {
|
|
addGradeInput();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Neue Note-Input-Zeile hinzufügen
|
|
*/
|
|
function addGradeInput() {
|
|
const container = document.getElementById('grades-container');
|
|
if (!container) return;
|
|
|
|
const gradeId = Date.now();
|
|
const gradeDiv = document.createElement('div');
|
|
gradeDiv.className = 'grade-input-group';
|
|
gradeDiv.id = `grade-${gradeId}`;
|
|
|
|
gradeDiv.innerHTML = `
|
|
<div class="grade-input-wrapper">
|
|
<input
|
|
type="number"
|
|
class="grade-points"
|
|
placeholder="Punkte"
|
|
min="0"
|
|
step="0.5"
|
|
>
|
|
<span class="grade-separator">/</span>
|
|
<input
|
|
type="number"
|
|
class="grade-max"
|
|
placeholder="Max"
|
|
value="100"
|
|
min="1"
|
|
step="1"
|
|
>
|
|
<div class="grade-result">
|
|
<span class="grade-note">-</span>
|
|
<span class="grade-percent">0%</span>
|
|
</div>
|
|
<button class="grade-remove-btn" onclick="removeGradeInput('${gradeId}')">✕</button>
|
|
</div>
|
|
`;
|
|
|
|
container.appendChild(gradeDiv);
|
|
|
|
// Live-Berechnung hinzufügen
|
|
const pointsInput = gradeDiv.querySelector('.grade-points');
|
|
const maxInput = gradeDiv.querySelector('.grade-max');
|
|
const noteSpan = gradeDiv.querySelector('.grade-note');
|
|
const percentSpan = gradeDiv.querySelector('.grade-percent');
|
|
|
|
function updateGrade() {
|
|
const points = parseFloat(pointsInput.value) || 0;
|
|
const max = parseFloat(maxInput.value) || 100;
|
|
|
|
if (max > 0 && points >= 0 && points <= max) {
|
|
const result = berechneNote(points, max);
|
|
if (result.valid) {
|
|
noteSpan.textContent = result.note;
|
|
percentSpan.textContent = result.prozent + '%';
|
|
noteSpan.style.color = getGradeColor(result.note);
|
|
}
|
|
} else {
|
|
noteSpan.textContent = '-';
|
|
percentSpan.textContent = '0%';
|
|
}
|
|
}
|
|
|
|
pointsInput.addEventListener('input', updateGrade);
|
|
maxInput.addEventListener('input', updateGrade);
|
|
pointsInput.focus();
|
|
}
|
|
|
|
/**
|
|
* Note-Input-Zeile entfernen
|
|
*/
|
|
function removeGradeInput(gradeId) {
|
|
const element = document.getElementById(`grade-${gradeId}`);
|
|
if (element) {
|
|
element.remove();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Alle Noten berechnen
|
|
*/
|
|
function calculateAllGrades() {
|
|
const container = document.getElementById('grades-container');
|
|
const resultDiv = document.getElementById('result');
|
|
const averageDiv = document.getElementById('average-result');
|
|
|
|
if (!container) return;
|
|
|
|
const gradeInputs = container.querySelectorAll('.grade-input-group');
|
|
const noten = [];
|
|
let hasErrors = false;
|
|
|
|
// Alle Noten sammeln
|
|
gradeInputs.forEach(input => {
|
|
const points = parseFloat(input.querySelector('.grade-points').value);
|
|
const max = parseFloat(input.querySelector('.grade-max').value);
|
|
|
|
if (isNaN(points) || isNaN(max)) {
|
|
hasErrors = true;
|
|
return;
|
|
}
|
|
|
|
const result = berechneNote(points, max);
|
|
if (result.valid) {
|
|
noten.push(result);
|
|
} else {
|
|
hasErrors = true;
|
|
}
|
|
});
|
|
|
|
if (hasErrors || noten.length === 0) {
|
|
resultDiv.innerHTML = `<p class="error-message">⚠️ Bitte füllen Sie alle Felder korrekt aus!</p>`;
|
|
return;
|
|
}
|
|
|
|
// Einzelnote-Ergebnisse anzeigen
|
|
let resultHTML = '<div class="result-list"><h4>📊 Einzelne Noten:</h4>';
|
|
noten.forEach((note, index) => {
|
|
const color = getGradeColor(note.note);
|
|
resultHTML += `
|
|
<div class="result-item">
|
|
<span class="result-number">#${index + 1}</span>
|
|
<span class="result-details">${note.punkte} / ${note.maxPunkte} Punkte (${note.prozent}%)</span>
|
|
<span class="result-grade" style="color: ${color};">${note.note}</span>
|
|
</div>
|
|
`;
|
|
});
|
|
resultHTML += '</div>';
|
|
resultDiv.innerHTML = resultHTML;
|
|
|
|
// Durchschnitt berechnen
|
|
if (noten.length > 1) {
|
|
const average = berechneDurchschnitt(noten);
|
|
if (average.valid) {
|
|
const color = getGradeColor(average.durchschnittNote);
|
|
let averageHTML = `
|
|
<div class="average-box">
|
|
<h4>📈 Durchschnitt:</h4>
|
|
<div class="average-display">
|
|
<div class="average-percent">${average.durchschnittProzent}%</div>
|
|
<div class="average-note" style="color: ${color};">${average.durchschnittNote}</div>
|
|
</div>
|
|
<p class="average-details">Von ${average.gueltig} Note(n) berechnet</p>
|
|
</div>
|
|
`;
|
|
averageDiv.innerHTML = averageHTML;
|
|
|
|
// Console und Alert
|
|
console.log(average.nachricht);
|
|
console.log(`Details: ${average.gueltig} von ${average.anzahl} Noten`);
|
|
}
|
|
} else {
|
|
averageDiv.innerHTML = '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Notenrechner zurücksetzen
|
|
*/
|
|
function resetCalculator() {
|
|
const container = document.getElementById('grades-container');
|
|
const resultDiv = document.getElementById('result');
|
|
const averageDiv = document.getElementById('average-result');
|
|
|
|
container.innerHTML = '';
|
|
resultDiv.innerHTML = '';
|
|
averageDiv.innerHTML = '';
|
|
|
|
addGradeInput();
|
|
}
|
|
|
|
/**
|
|
* Farbe basierend auf Note auswählen
|
|
*/
|
|
function getGradeColor(note) {
|
|
const colors = {
|
|
'sehr gut': '#ff1493',
|
|
'gut': '#ff69b4',
|
|
'befriedigend': '#ffa500',
|
|
'ausreichend': '#ff8c00',
|
|
'nicht bestanden': '#dc143c'
|
|
};
|
|
return colors[note.toLowerCase()] || '#6b3a5f';
|
|
}
|