/** * BreakPilot Studio - Cloze (Lückentext) Module * * Lückentext-Funktionalität mit Übersetzung: * - Generierung von Lückentexten aus Arbeitsblättern * - Mehrsprachige Übersetzungen (TR, AR, RU, UK, PL, EN) * - Interaktives Übungsmodul mit Auswertung * - Druckfunktion (mit/ohne Lösungen) * * Refactored: 2026-01-19 */ import { t } from './i18n.js'; import { setStatus } from './api-helpers.js'; // State let currentClozeData = null; let clozeAnswers = {}; // DOM References let clozePreview = null; let clozeBadge = null; let clozeLanguageSelect = null; let clozeModal = null; let clozeModalBody = null; let clozeModalClose = null; let btnClozeGenerate = null; let btnClozeShow = null; let btnClozePrint = null; // Callbacks let getEingangFilesCallback = null; let getCurrentIndexCallback = null; /** * Initialisiert das Cloze-Modul * @param {Object} options - Konfiguration */ export function initClozeModule(options = {}) { getEingangFilesCallback = options.getEingangFiles || (() => []); getCurrentIndexCallback = options.getCurrentIndex || (() => 0); // DOM References clozePreview = document.getElementById('cloze-preview') || options.previewEl; clozeBadge = document.getElementById('cloze-badge') || options.badgeEl; clozeLanguageSelect = document.getElementById('cloze-language') || options.languageSelectEl; clozeModal = document.getElementById('cloze-modal') || options.modalEl; clozeModalBody = document.getElementById('cloze-modal-body') || options.modalBodyEl; clozeModalClose = document.getElementById('cloze-modal-close') || options.modalCloseEl; btnClozeGenerate = document.getElementById('btn-cloze-generate') || options.generateBtn; btnClozeShow = document.getElementById('btn-cloze-show') || options.showBtn; btnClozePrint = document.getElementById('btn-cloze-print') || options.printBtn; // Event-Listener if (btnClozeGenerate) { btnClozeGenerate.addEventListener('click', generateClozeTexts); } if (btnClozeShow) { btnClozeShow.addEventListener('click', openClozeModal); } if (btnClozePrint) { btnClozePrint.addEventListener('click', openClozePrintDialog); } if (clozeModalClose) { clozeModalClose.addEventListener('click', closeClozeModal); } if (clozeModal) { clozeModal.addEventListener('click', (ev) => { if (ev.target === clozeModal) { closeClozeModal(); } }); } // Event für Datei-Wechsel window.addEventListener('fileSelected', () => { loadClozePreviewForCurrent(); }); } /** * Generiert Lückentexte für alle Arbeitsblätter */ export async function generateClozeTexts() { const targetLang = clozeLanguageSelect ? clozeLanguageSelect.value : 'tr'; try { setStatus(t('cloze_generating') || 'Generiere Lückentexte …', t('ai_working') || 'Bitte warten, KI arbeitet.', 'busy'); if (clozeBadge) clozeBadge.textContent = t('generating') || 'Generiert...'; const resp = await fetch('/api/generate-cloze?target_language=' + targetLang, { method: 'POST' }); if (!resp.ok) { throw new Error('HTTP ' + resp.status); } const result = await resp.json(); if (result.status === 'OK' && result.generated.length > 0) { setStatus(t('cloze_generated') || 'Lückentexte generiert', result.generated.length + ' ' + (t('files_created') || 'Dateien erstellt')); if (clozeBadge) clozeBadge.textContent = t('ready') || 'Fertig'; if (btnClozeShow) btnClozeShow.style.display = 'inline-block'; if (btnClozePrint) btnClozePrint.style.display = 'inline-block'; // Lade Vorschau für aktuelle Datei await loadClozePreviewForCurrent(); } else if (result.errors && result.errors.length > 0) { setStatus(t('cloze_error') || 'Fehler bei Lückentext-Generierung', result.errors[0].error, 'error'); if (clozeBadge) clozeBadge.textContent = t('error') || 'Fehler'; } else { setStatus(t('no_cloze_generated') || 'Keine Lückentexte generiert', t('analysis_missing') || 'Möglicherweise fehlen Analyse-Daten.', 'error'); if (clozeBadge) clozeBadge.textContent = t('ready') || 'Bereit'; } } catch (e) { console.error('Lückentext-Generierung fehlgeschlagen:', e); setStatus(t('cloze_error') || 'Fehler bei Lückentext-Generierung', String(e), 'error'); if (clozeBadge) clozeBadge.textContent = t('error') || 'Fehler'; } } /** * Lädt Cloze-Vorschau für die aktuelle Datei */ export async function loadClozePreviewForCurrent() { const eingangFiles = getEingangFilesCallback(); const currentIndex = getCurrentIndexCallback(); if (!eingangFiles.length) { if (clozePreview) clozePreview.innerHTML = '
' + (t('no_worksheets') || 'Keine Arbeitsblätter vorhanden.') + '
'; return; } const currentFile = eingangFiles[currentIndex]; if (!currentFile) return; try { const resp = await fetch('/api/cloze-data/' + encodeURIComponent(currentFile)); const result = await resp.json(); if (result.status === 'OK' && result.data) { currentClozeData = result.data; renderClozePreview(result.data); if (btnClozeShow) btnClozeShow.style.display = 'inline-block'; if (btnClozePrint) btnClozePrint.style.display = 'inline-block'; } else { if (clozePreview) clozePreview.innerHTML = '
' + (t('no_cloze_for_worksheet') || 'Noch keine Lückentexte für dieses Arbeitsblatt generiert.') + '
'; currentClozeData = null; if (btnClozeShow) btnClozeShow.style.display = 'none'; if (btnClozePrint) btnClozePrint.style.display = 'none'; } } catch (e) { console.error('Fehler beim Laden der Lückentext-Daten:', e); if (clozePreview) clozePreview.innerHTML = ''; } } /** * Rendert die Cloze-Vorschau * @param {Object} clozeData - Cloze-Daten */ function renderClozePreview(clozeData) { if (!clozePreview) return; if (!clozeData || !clozeData.cloze_items || clozeData.cloze_items.length === 0) { clozePreview.innerHTML = '
' + (t('no_cloze_texts') || 'Keine Lückentexte vorhanden.') + '
'; return; } const items = clozeData.cloze_items; const metadata = clozeData.metadata || {}; let html = ''; // Statistiken html += '
'; if (metadata.subject) { html += '
' + (t('subject') || 'Fach') + ': ' + escapeHtml(metadata.subject) + '
'; } if (metadata.grade_level) { html += '
' + (t('grade') || 'Stufe') + ': ' + escapeHtml(metadata.grade_level) + '
'; } html += '
' + (t('sentences') || 'Sätze') + ': ' + items.length + '
'; if (metadata.total_gaps) { html += '
' + (t('gaps') || 'Lücken') + ': ' + metadata.total_gaps + '
'; } html += '
'; // Zeige erste 2 Sätze als Vorschau const previewItems = items.slice(0, 2); previewItems.forEach((item, idx) => { html += '
'; html += '
' + (idx + 1) + '. ' + item.sentence_with_gaps.replace(/___/g, '___') + '
'; // Übersetzung anzeigen if (item.translation && item.translation.full_sentence) { html += '
'; html += '
' + (item.translation.language_name || t('translation') || 'Übersetzung') + ':
'; html += escapeHtml(item.translation.full_sentence); html += '
'; } html += '
'; }); if (items.length > 2) { html += '
+ ' + (items.length - 2) + ' ' + (t('more_sentences') || 'weitere Sätze') + '
'; } clozePreview.innerHTML = html; } /** * Öffnet das Cloze-Modal */ export function openClozeModal() { if (!currentClozeData || !currentClozeData.cloze_items) { alert(t('no_cloze_texts') || 'Keine Lückentexte vorhanden. Bitte zuerst generieren.'); return; } clozeAnswers = {}; // Reset Antworten renderClozeModal(currentClozeData); if (clozeModal) clozeModal.classList.remove('hidden'); } /** * Schließt das Cloze-Modal */ export function closeClozeModal() { if (clozeModal) clozeModal.classList.add('hidden'); } /** * Rendert den Modal-Inhalt * @param {Object} clozeData - Cloze-Daten */ function renderClozeModal(clozeData) { if (!clozeModalBody) return; const items = clozeData.cloze_items; const metadata = clozeData.metadata || {}; let html = ''; // Header html += '
'; if (metadata.source_title) { html += '
' + (t('worksheet') || 'Arbeitsblatt') + ': ' + escapeHtml(metadata.source_title) + '
'; } if (metadata.total_gaps) { html += '
' + (t('total_gaps') || 'Lücken gesamt') + ': ' + metadata.total_gaps + '
'; } html += '
'; html += '
' + (t('cloze_instruction') || 'Fülle die Lücken aus und klicke auf "Prüfen".') + '
'; // Alle Sätze mit Eingabefeldern items.forEach((item, idx) => { html += '
'; // Satz mit Eingabefeldern statt ___ let sentenceHtml = item.sentence_with_gaps; const gaps = item.gaps || []; // Ersetze ___ durch Eingabefelder let gapIndex = 0; sentenceHtml = sentenceHtml.replace(/___/g, () => { const gap = gaps[gapIndex] || { id: 'g' + gapIndex, word: '' }; const inputId = item.id + '_' + gap.id; gapIndex++; return ''; }); html += '
' + (idx + 1) + '. ' + sentenceHtml + '
'; // Übersetzung als Hilfe if (item.translation && item.translation.sentence_with_gaps) { html += '
'; html += '
' + (item.translation.language_name || t('translation') || 'Übersetzung') + ' (' + (t('with_gaps') || 'mit Lücken') + '):
'; html += escapeHtml(item.translation.sentence_with_gaps); html += '
'; } html += '
'; }); // Buttons html += '
'; html += ''; html += ''; html += '
'; clozeModalBody.innerHTML = html; // Event-Listener für Prüfen-Button const btnCheck = document.getElementById('btn-cloze-check'); if (btnCheck) { btnCheck.addEventListener('click', checkClozeAnswers); } // Event-Listener für Lösungen zeigen const btnShowAnswers = document.getElementById('btn-cloze-show-answers'); if (btnShowAnswers) { btnShowAnswers.addEventListener('click', showClozeAnswers); } // Enter-Taste zum Prüfen clozeModalBody.querySelectorAll('.cloze-gap-input').forEach(input => { input.addEventListener('keypress', (e) => { if (e.key === 'Enter') { checkClozeAnswers(); } }); }); } /** * Überprüft die Cloze-Antworten */ function checkClozeAnswers() { if (!clozeModalBody) return; let correct = 0; let total = 0; clozeModalBody.querySelectorAll('.cloze-gap-input').forEach(input => { const userAnswer = input.value.trim().toLowerCase(); const correctAnswer = input.getAttribute('data-answer').toLowerCase(); total++; // Entferne vorherige Klassen input.classList.remove('correct', 'incorrect'); if (userAnswer === correctAnswer) { input.classList.add('correct'); correct++; } else if (userAnswer !== '') { input.classList.add('incorrect'); } }); // Zeige Ergebnis let existingResult = clozeModalBody.querySelector('.cloze-result'); if (existingResult) existingResult.remove(); const percentage = Math.round(correct / total * 100); const resultHtml = '
' + '
' + correct + ' ' + (t('of') || 'von') + ' ' + total + ' ' + (t('correct_answers') || 'richtig') + '
' + '
' + percentage + '% ' + (t('percent_correct') || 'korrekt') + '
' + '
'; const resultDiv = document.createElement('div'); resultDiv.innerHTML = resultHtml; clozeModalBody.appendChild(resultDiv.firstChild); } /** * Zeigt alle Cloze-Lösungen */ function showClozeAnswers() { if (!clozeModalBody) return; clozeModalBody.querySelectorAll('.cloze-gap-input').forEach(input => { const correctAnswer = input.getAttribute('data-answer'); input.value = correctAnswer; input.classList.remove('incorrect'); input.classList.add('correct'); }); } /** * Öffnet den Druck-Dialog */ export function openClozePrintDialog() { if (!currentClozeData) { alert(t('no_cloze_texts') || 'Keine Lückentexte vorhanden.'); return; } const eingangFiles = getEingangFilesCallback(); const currentIndex = getCurrentIndexCallback(); const currentFile = eingangFiles[currentIndex]; const confirmMsg = (t('cloze_print_with_answers') || 'Mit Lösungen drucken?') + '\n\nOK = ' + (t('with_filled_gaps') || 'Mit ausgefüllten Lücken') + '\n' + (t('cancel') || 'Abbrechen') + ' = ' + (t('exercise_with_wordbank') || 'Übungsblatt mit Wortbank'); const choice = confirm(confirmMsg); const url = '/api/print-cloze/' + encodeURIComponent(currentFile) + '?show_answers=' + choice; window.open(url, '_blank'); } // === Getter und Setter === /** * Gibt die aktuellen Cloze-Daten zurück * @returns {Object|null} */ export function getClozeData() { return currentClozeData; } /** * Setzt die Cloze-Daten * @param {Object} data */ export function setClozeData(data) { currentClozeData = data; if (data) { renderClozePreview(data); } } /** * Gibt die aktuelle Zielsprache zurück * @returns {string} */ export function getTargetLanguage() { return clozeLanguageSelect ? clozeLanguageSelect.value : 'tr'; } /** * Helper: HTML-Escape */ function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }