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}
+
`;