diff --git a/js/games/text-reader.js b/js/games/text-reader.js index 9bb5394..faff052 100644 --- a/js/games/text-reader.js +++ b/js/games/text-reader.js @@ -7,13 +7,15 @@ class TextReaderGame { this.onScoreUpdate = options.onScoreUpdate || (() => {}); this.onGameEnd = options.onGameEnd || (() => {}); - // État du lecteur + // Reader state this.currentTextIndex = 0; this.currentSentenceIndex = 0; this.isRunning = false; - // Données de lecture + // Reading data this.texts = this.extractTexts(this.content); + this.sentencesFromContent = this.extractSentences(this.content); + this.vocabulary = this.extractVocabulary(this.content); this.currentText = null; this.sentences = []; this.showingFullText = false; @@ -22,13 +24,15 @@ class TextReaderGame { } init() { - // Vérifier que nous avons des textes - if (!this.texts || this.texts.length === 0) { - logSh('Aucun texte disponible pour Text Reader', 'ERROR'); + // Check that we have texts, sentences, or vocabulary + if ((!this.texts || this.texts.length === 0) && + (!this.sentencesFromContent || this.sentencesFromContent.length === 0) && + (!this.vocabulary || this.vocabulary.length === 0)) { + logSh('No texts, sentences, or vocabulary available for Text Reader', 'ERROR'); this.showInitError(); return; } - + this.createReaderInterface(); this.setupEventListeners(); this.loadText(); @@ -38,8 +42,8 @@ class TextReaderGame { this.container.innerHTML = `

❌ Error loading

-

This content doesn't contain texts compatible with Text Reader.

-

The reader needs texts to display.

+

This content doesn't contain texts, sentences, or vocabulary compatible with Text Reader.

+

The reader needs content with ultra-modular format (original_language/user_language).

`; @@ -47,85 +51,208 @@ class TextReaderGame { extractTexts(content) { let texts = []; - + logSh('📖 Extracting texts from:', content?.name || 'content', 'INFO'); - - // Use raw module content if available + + // Priority 1: Use raw module content (simple format) if (content.rawContent) { logSh('📦 Using raw module content', 'INFO'); return this.extractTextsFromRaw(content.rawContent); } - - // Format with texts array + + // Priority 2: Ultra-modular format (texts array) - ONLY format supported if (content.texts && Array.isArray(content.texts)) { - logSh('📝 Texts format detected', 'INFO'); - texts = content.texts.filter(text => - text.content && text.content.trim() !== '' - ); - } - // Modern format with contentItems - else if (content.contentItems && Array.isArray(content.contentItems)) { - logSh('🆕 ContentItems format detected', 'INFO'); - texts = content.contentItems - .filter(item => item.type === 'text' && item.content) - .map(item => ({ - title: item.title || 'Text', - content: item.content + logSh('✨ Ultra-modular format detected (texts array)', 'INFO'); + texts = content.texts + .filter(text => text && text.original_language && text.user_language) + .map(text => ({ + id: text.id || `text_${Date.now()}_${Math.random()}`, + title: text.title || text.id || 'Text', + original: text.original_language, + translation: text.user_language })); + logSh(`✨ ${texts.length} texts extracted from ultra-modular format`, 'INFO'); } - + return this.finalizeTexts(texts); } extractTextsFromRaw(rawContent) { logSh('🔧 Extracting from raw content:', rawContent.name || 'Module', 'INFO'); let texts = []; - - // Simple format (texts array) + + // Ultra-modular format (texts array) - ONLY format supported if (rawContent.texts && Array.isArray(rawContent.texts)) { - texts = rawContent.texts.filter(text => - text.content && text.content.trim() !== '' - ); - logSh(`📝 ${texts.length} texts extracted from texts array`, 'INFO'); - } - // ContentItems format - else if (rawContent.contentItems && Array.isArray(rawContent.contentItems)) { - texts = rawContent.contentItems - .filter(item => item.type === 'text' && item.content) - .map(item => ({ - title: item.title || 'Text', - content: item.content + texts = rawContent.texts + .filter(text => text && text.original_language && text.user_language) + .map(text => ({ + id: text.id || `text_${Date.now()}_${Math.random()}`, + title: text.title || text.id || 'Text', + original: text.original_language, + translation: text.user_language })); - logSh(`🆕 ${texts.length} texts extracted from contentItems`, 'INFO'); + logSh(`✨ ${texts.length} texts extracted from ultra-modular format`, 'INFO'); } - + return this.finalizeTexts(texts); } finalizeTexts(texts) { // Validation and cleanup - texts = texts.filter(text => - text && - typeof text.content === 'string' && - text.content.trim() !== '' + texts = texts.filter(text => + text && + typeof text.original === 'string' && + text.original.trim() !== '' && + typeof text.translation === 'string' && + text.translation.trim() !== '' ); - + if (texts.length === 0) { logSh('❌ No valid texts found', 'ERROR'); // Demo texts as fallback texts = [ - { - title: "Demo Text", - content: "This is a demo text. It has multiple sentences. Each sentence will be displayed one by one. You can navigate using the buttons below." + { + id: "demo_text_1", + title: "Demo Text", + original: "This is a demo text. It has multiple sentences. Each sentence will be displayed one by one. You can navigate using the buttons below.", + translation: "Ceci est un texte de démonstration. Il contient plusieurs phrases. Chaque phrase sera affichée une par une. Vous pouvez naviguer avec les boutons ci-dessous." } ]; logSh('🚨 Using demo texts', 'WARN'); } - + logSh(`✅ Text Reader: ${texts.length} texts finalized`, 'INFO'); return texts; } + extractSentences(content) { + let sentences = []; + + logSh('📝 Extracting sentences from:', content?.name || 'content', 'INFO'); + + // Priority 1: Use raw module content + if (content.rawContent) { + logSh('📦 Using raw module content for sentences', 'INFO'); + return this.extractSentencesFromRaw(content.rawContent); + } + + // Priority 2: Ultra-modular format (sentences array) - ONLY format supported + if (content.sentences && Array.isArray(content.sentences)) { + logSh('✨ Ultra-modular format detected (sentences array)', 'INFO'); + sentences = content.sentences + .filter(sentence => sentence && sentence.original_language && sentence.user_language) + .map(sentence => ({ + id: sentence.id || `sentence_${Date.now()}_${Math.random()}`, + original: sentence.original_language, + translation: sentence.user_language + })); + logSh(`✨ ${sentences.length} sentences extracted from ultra-modular format`, 'INFO'); + } + + return this.finalizeSentences(sentences); + } + + extractSentencesFromRaw(rawContent) { + logSh('🔧 Extracting sentences from raw content:', rawContent.name || 'Module', 'INFO'); + let sentences = []; + + // Ultra-modular format (sentences array) - ONLY format supported + if (rawContent.sentences && Array.isArray(rawContent.sentences)) { + sentences = rawContent.sentences + .filter(sentence => sentence && sentence.original_language && sentence.user_language) + .map(sentence => ({ + id: sentence.id || `sentence_${Date.now()}_${Math.random()}`, + original: sentence.original_language, + translation: sentence.user_language + })); + logSh(`✨ ${sentences.length} sentences extracted from ultra-modular format`, 'INFO'); + } + + return this.finalizeSentences(sentences); + } + + finalizeSentences(sentences) { + // Validation and cleanup + sentences = sentences.filter(sentence => + sentence && + typeof sentence.original === 'string' && + sentence.original.trim() !== '' && + typeof sentence.translation === 'string' && + sentence.translation.trim() !== '' + ); + + logSh(`✅ Text Reader: ${sentences.length} sentences finalized`, 'INFO'); + return sentences; + } + + extractVocabulary(content) { + let vocabulary = []; + + logSh('🔍 Extracting vocabulary from:', content?.name || 'content', 'INFO'); + + // Priority 1: Use raw module content (simple format) + if (content.rawContent) { + logSh('📦 Using raw module content for vocabulary', 'INFO'); + return this.extractVocabularyFromRaw(content.rawContent); + } + + // Priority 2: Ultra-modular format (vocabulary object) - ONLY format supported + if (content.vocabulary && typeof content.vocabulary === 'object' && !Array.isArray(content.vocabulary)) { + logSh('✨ Ultra-modular format detected (vocabulary object)', 'INFO'); + vocabulary = Object.entries(content.vocabulary).map(([word, data]) => { + // Support ultra-modular format ONLY + if (typeof data === 'object' && data.user_language) { + return { + original: word, // Clé = original_language + translation: data.user_language, + type: data.type || 'unknown' + }; + } + return null; + }).filter(item => item !== null); + logSh(`✨ ${vocabulary.length} vocabulary words extracted from ultra-modular format`, 'INFO'); + } + + return this.finalizeVocabulary(vocabulary); + } + + extractVocabularyFromRaw(rawContent) { + logSh('🔧 Extracting vocabulary from raw content:', rawContent.name || 'Module', 'INFO'); + let vocabulary = []; + + // Ultra-modular format (vocabulary object) - ONLY format supported + if (rawContent.vocabulary && typeof rawContent.vocabulary === 'object' && !Array.isArray(rawContent.vocabulary)) { + vocabulary = Object.entries(rawContent.vocabulary).map(([word, data]) => { + // Support ultra-modular format ONLY + if (typeof data === 'object' && data.user_language) { + return { + original: word, // Clé = original_language + translation: data.user_language, + type: data.type || 'unknown' + }; + } + return null; + }).filter(item => item !== null); + logSh(`✨ ${vocabulary.length} vocabulary words extracted from ultra-modular format`, 'INFO'); + } + + return this.finalizeVocabulary(vocabulary); + } + + finalizeVocabulary(vocabulary) { + // Validation and cleanup + vocabulary = vocabulary.filter(word => + word && + typeof word.original === 'string' && + word.original.trim() !== '' && + typeof word.translation === 'string' && + word.translation.trim() !== '' + ); + + logSh(`✅ Text Reader: ${vocabulary.length} vocabulary words finalized`, 'INFO'); + return vocabulary; + } + createReaderInterface() { this.container.innerHTML = `
@@ -215,16 +342,50 @@ class TextReaderGame { } loadText() { - if (this.currentTextIndex >= this.texts.length) { - this.currentTextIndex = 0; + // Use texts if available, otherwise use individual sentences, then vocabulary + if (this.texts && this.texts.length > 0) { + if (this.currentTextIndex >= this.texts.length) { + this.currentTextIndex = 0; + } + + this.currentText = this.texts[this.currentTextIndex]; + this.sentences = this.splitIntoSentences(this.currentText.original); + this.currentSentenceIndex = 0; + this.showingFullText = false; + + this.populateTextSelector(); + } else if (this.sentencesFromContent && this.sentencesFromContent.length > 0) { + // Use individual sentences as "mini-texts" + this.texts = this.sentencesFromContent.map((sentence, index) => ({ + id: sentence.id || `sentence_text_${index}`, + title: `Sentence ${index + 1}`, + original: sentence.original, + translation: sentence.translation + })); + + this.currentText = this.texts[0]; + this.sentences = [this.currentText.original]; // Single sentence + this.currentSentenceIndex = 0; + this.showingFullText = false; + + this.populateTextSelector(); + } else if (this.vocabulary && this.vocabulary.length > 0) { + // Use vocabulary words as "mini-texts" + this.texts = this.vocabulary.map((word, index) => ({ + id: `vocab_text_${index}`, + title: `Word: ${word.original}`, + original: word.original, + translation: word.translation + })); + + this.currentText = this.texts[0]; + this.sentences = [this.currentText.original]; // Single word + this.currentSentenceIndex = 0; + this.showingFullText = false; + + this.populateTextSelector(); } - - this.currentText = this.texts[this.currentTextIndex]; - this.sentences = this.splitIntoSentences(this.currentText.content); - this.currentSentenceIndex = 0; - this.showingFullText = false; - - this.populateTextSelector(); + this.updateDisplay(); this.updateUI(); } @@ -248,7 +409,7 @@ class TextReaderGame { if (textIndex >= 0 && textIndex < this.texts.length) { this.currentTextIndex = textIndex; this.currentText = this.texts[this.currentTextIndex]; - this.sentences = this.splitIntoSentences(this.currentText.content); + this.sentences = this.splitIntoSentences(this.currentText.original); this.currentSentenceIndex = 0; // Always go back to sentence reading when changing text @@ -298,7 +459,14 @@ class TextReaderGame { document.getElementById('full-text-display').style.display = 'block'; document.getElementById('full-text-display').innerHTML = `
-

${this.currentText.content}

+
+

Original:

+

${this.currentText.original}

+
+
+

Translation:

+

${this.currentText.translation}

+
`;