Remove Text Reader game and enhance Story Reader
- Enhanced Story Reader with text-to-story conversion methods - Added support for simple texts and sentences in Story Reader - Removed Text Reader game file (js/games/text-reader.js) - Updated all configuration files to remove Text Reader references - Modified game compatibility system to use Story Reader instead - Updated test fixtures to reflect game changes - Cleaned up debug/test HTML files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e50dd624a0
commit
30a2028da6
@ -9,7 +9,6 @@ class ContentGameCompatibility {
|
||||
'memory-match': 50,
|
||||
'quiz-game': 30,
|
||||
'fill-the-blank': 30,
|
||||
'text-reader': 40,
|
||||
'adventure-reader': 50,
|
||||
'chinese-study': 35,
|
||||
'story-builder': 35,
|
||||
@ -119,7 +118,6 @@ class ContentGameCompatibility {
|
||||
case 'fill-the-blank':
|
||||
return this.calculateFillBlankCompat(capabilities);
|
||||
|
||||
case 'text-reader':
|
||||
case 'story-reader':
|
||||
return this.calculateTextReaderCompat(capabilities);
|
||||
|
||||
@ -467,7 +465,6 @@ class ContentGameCompatibility {
|
||||
'memory-match': ['4+ paires de vocabulaire', 'Idéalement avec images/audio'],
|
||||
'quiz-game': ['Vocabulaire OU phrases OU exercices', 'Très flexible'],
|
||||
'fill-the-blank': ['Phrases avec exercices à trous OU phrases simples', 'Contenu éducatif'],
|
||||
'text-reader': ['3+ phrases OU dialogues', 'Contenu narratif'],
|
||||
'adventure-reader': ['Dialogues + contenu narratif riche', 'Histoire cohérente'],
|
||||
'chinese-study': ['Vocabulaire et phrases chinoises', 'Audio recommandé'],
|
||||
'story-builder': ['Dialogues OU 5+ phrases', 'Vocabulaire varié'],
|
||||
@ -484,7 +481,6 @@ class ContentGameCompatibility {
|
||||
'memory-match': 'Jeu de mémoire optimisé pour paires vocabulaire-traduction',
|
||||
'quiz-game': 'Jeu polyvalent compatible avec la plupart des contenus',
|
||||
'fill-the-blank': 'Exercices à trous nécessitant phrases structurées',
|
||||
'text-reader': 'Lecture guidée nécessitant textes ou dialogues',
|
||||
'adventure-reader': 'Aventure narrative nécessitant contenu riche',
|
||||
'chinese-study': 'Optimisé pour apprentissage du chinois',
|
||||
'story-builder': 'Construction narrative nécessitant éléments variés',
|
||||
|
||||
@ -875,7 +875,6 @@ class ContentScanner {
|
||||
'memory-match': this.calculateMemoryMatchCompat(capabilities),
|
||||
'quiz-game': this.calculateQuizGameCompat(capabilities),
|
||||
'fill-the-blank': this.calculateFillBlankCompat(capabilities),
|
||||
'text-reader': this.calculateTextReaderCompat(capabilities),
|
||||
'adventure-reader': this.calculateAdventureCompat(capabilities),
|
||||
'sentence-builder': this.calculateSentenceBuilderCompat(capabilities),
|
||||
'pronunciation-game': this.calculatePronunciationCompat(capabilities),
|
||||
@ -986,14 +985,6 @@ class ContentScanner {
|
||||
return { compatible: score >= 30, score, reason: 'Nécessite phrases à trous' };
|
||||
}
|
||||
|
||||
calculateTextReaderCompat(capabilities) {
|
||||
let score = 0;
|
||||
if (capabilities.hasSentences) score += 40;
|
||||
if (capabilities.hasDialogues) score += 50;
|
||||
if (capabilities.hasAudioFiles) score += 10;
|
||||
|
||||
return { compatible: score >= 40, score, reason: 'Nécessite textes à lire' };
|
||||
}
|
||||
|
||||
calculateAdventureCompat(capabilities) {
|
||||
let score = 0;
|
||||
|
||||
@ -318,7 +318,6 @@ const GameLoader = {
|
||||
'memory-match': 'MemoryMatch',
|
||||
'quiz-game': 'QuizGame',
|
||||
'fill-the-blank': 'FillTheBlank',
|
||||
'text-reader': 'TextReader',
|
||||
'adventure-reader': 'AdventureReader',
|
||||
'chinese-study': 'ChineseStudy',
|
||||
'story-reader': 'StoryReader',
|
||||
@ -364,7 +363,6 @@ const GameLoader = {
|
||||
'memory-match': 'Memory Match',
|
||||
'quiz-game': 'Quiz Game',
|
||||
'fill-the-blank': 'Fill the Blank',
|
||||
'text-reader': 'Text Reader',
|
||||
'adventure-reader': 'Adventure Reader'
|
||||
};
|
||||
|
||||
|
||||
@ -74,12 +74,6 @@ const AppNavigation = {
|
||||
icon: '📝',
|
||||
description: 'Complete sentences by filling in the blanks!'
|
||||
},
|
||||
'text-reader': {
|
||||
enabled: true,
|
||||
name: 'Text Reader',
|
||||
icon: '📖',
|
||||
description: 'Read texts sentence by sentence'
|
||||
},
|
||||
'adventure-reader': {
|
||||
enabled: true,
|
||||
name: 'Adventure Reader',
|
||||
|
||||
@ -74,7 +74,7 @@ class AdventureReaderGame {
|
||||
<li><strong>📖 sentences:</strong> Individual phrases for reading practice</li>
|
||||
</ul>
|
||||
<p>Add adventure content to enable this game mode.</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back to Games</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ class ChineseStudyGame {
|
||||
<h3>❌ Error loading</h3>
|
||||
<p>This content doesn't have Chinese vocabulary for the Chinese Study Game.</p>
|
||||
<p>The game needs vocabulary with Chinese characters, translations, and optional pinyin.</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back</button>
|
||||
</div>
|
||||
`;
|
||||
this.addStyles();
|
||||
@ -214,7 +214,7 @@ class ChineseStudyGame {
|
||||
</div>
|
||||
|
||||
<div class="game-controls">
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back to Games</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back to Games</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -643,7 +643,7 @@ class ChineseStudyGame {
|
||||
<div class="final-actions">
|
||||
<button onclick="chineseStudyInstance.restart()" class="restart-btn">🔄 Study Again</button>
|
||||
<button onclick="chineseStudyInstance.backToMenu()" class="menu-btn">📚 Back to Modes</button>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back to Games</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back to Games</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -42,7 +42,7 @@ class FillTheBlankGame {
|
||||
<h3>❌ Loading Error</h3>
|
||||
<p>This content does not contain vocabulary compatible with Fill the Blank.</p>
|
||||
<p>The game requires words with their translations in ultra-modular format.</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back to Games</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ class MemoryMatchGame {
|
||||
<h3>❌ Error loading</h3>
|
||||
<p>This content doesn't have enough vocabulary for Memory Match.</p>
|
||||
<p>The game needs at least ${this.totalPairs} vocabulary pairs.</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -15,16 +15,18 @@ class QuizGame {
|
||||
this.correctAnswers = 0;
|
||||
this.currentQuestionData = null;
|
||||
this.hasAnswered = false;
|
||||
this.quizDirection = 'original_to_translation'; // 'original_to_translation' or 'translation_to_original'
|
||||
|
||||
// Extract vocabulary
|
||||
// Extract vocabulary and additional words from texts/stories
|
||||
this.vocabulary = this.extractVocabulary(this.content);
|
||||
this.allWords = this.extractAllWords(this.content);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Check if we have enough vocabulary
|
||||
if (!this.vocabulary || this.vocabulary.length < 4) {
|
||||
if (!this.vocabulary || this.vocabulary.length < 6) {
|
||||
logSh('Not enough vocabulary for Quiz Game', 'ERROR');
|
||||
this.showInitError();
|
||||
return;
|
||||
@ -42,8 +44,8 @@ class QuizGame {
|
||||
<div class="game-error">
|
||||
<h3>❌ Error loading</h3>
|
||||
<p>This content doesn't have enough vocabulary for Quiz Game.</p>
|
||||
<p>The game needs at least 4 vocabulary items.</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<p>The game needs at least 6 vocabulary items.</p>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back to Games</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -171,6 +173,76 @@ class QuizGame {
|
||||
return vocabulary;
|
||||
}
|
||||
|
||||
extractAllWords(content) {
|
||||
let allWords = [];
|
||||
|
||||
// Add vocabulary words first
|
||||
allWords = [...this.vocabulary];
|
||||
|
||||
// Extract from stories/texts
|
||||
if (content.rawContent?.story?.chapters) {
|
||||
content.rawContent.story.chapters.forEach(chapter => {
|
||||
if (chapter.sentences) {
|
||||
chapter.sentences.forEach(sentence => {
|
||||
if (sentence.words && Array.isArray(sentence.words)) {
|
||||
sentence.words.forEach(wordObj => {
|
||||
if (wordObj.word && wordObj.translation) {
|
||||
allWords.push({
|
||||
original: wordObj.word,
|
||||
translation: wordObj.translation,
|
||||
type: wordObj.type || 'word',
|
||||
pronunciation: wordObj.pronunciation
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Extract from additional stories (like WTA1B1)
|
||||
if (content.rawContent?.additionalStories) {
|
||||
content.rawContent.additionalStories.forEach(story => {
|
||||
if (story.chapters) {
|
||||
story.chapters.forEach(chapter => {
|
||||
if (chapter.sentences) {
|
||||
chapter.sentences.forEach(sentence => {
|
||||
if (sentence.words && Array.isArray(sentence.words)) {
|
||||
sentence.words.forEach(wordObj => {
|
||||
if (wordObj.word && wordObj.translation) {
|
||||
allWords.push({
|
||||
original: wordObj.word,
|
||||
translation: wordObj.translation,
|
||||
type: wordObj.type || 'word',
|
||||
pronunciation: wordObj.pronunciation
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove duplicates based on original word
|
||||
const uniqueWords = [];
|
||||
const seenWords = new Set();
|
||||
|
||||
allWords.forEach(word => {
|
||||
const key = word.original.toLowerCase();
|
||||
if (!seenWords.has(key)) {
|
||||
seenWords.add(key);
|
||||
uniqueWords.push(word);
|
||||
}
|
||||
});
|
||||
|
||||
logSh(`📚 Extracted ${uniqueWords.length} total words for quiz options`, 'INFO');
|
||||
return uniqueWords;
|
||||
}
|
||||
|
||||
shuffleArray(array) {
|
||||
const shuffled = [...array];
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
@ -272,20 +344,37 @@ class QuizGame {
|
||||
// Get current vocabulary item
|
||||
const correctAnswer = this.vocabulary[this.currentQuestion];
|
||||
|
||||
// Generate 3 wrong answers from other vocabulary items
|
||||
const wrongAnswers = this.vocabulary
|
||||
// Randomly choose quiz direction
|
||||
this.quizDirection = Math.random() < 0.5 ? 'original_to_translation' : 'translation_to_original';
|
||||
|
||||
let questionText, correctAnswerText, sourceForWrongAnswers;
|
||||
|
||||
if (this.quizDirection === 'original_to_translation') {
|
||||
questionText = correctAnswer.original;
|
||||
correctAnswerText = correctAnswer.translation;
|
||||
sourceForWrongAnswers = 'translation';
|
||||
} else {
|
||||
questionText = correctAnswer.translation;
|
||||
correctAnswerText = correctAnswer.original;
|
||||
sourceForWrongAnswers = 'original';
|
||||
}
|
||||
|
||||
// Generate 5 wrong answers from allWords (which includes story words)
|
||||
const availableWords = this.allWords.length >= 6 ? this.allWords : this.vocabulary;
|
||||
const wrongAnswers = availableWords
|
||||
.filter(item => item !== correctAnswer)
|
||||
.sort(() => Math.random() - 0.5)
|
||||
.slice(0, 3)
|
||||
.map(item => item.translation);
|
||||
.slice(0, 5)
|
||||
.map(item => sourceForWrongAnswers === 'translation' ? item.translation : item.original);
|
||||
|
||||
// Combine and shuffle all options
|
||||
const allOptions = [correctAnswer.translation, ...wrongAnswers].sort(() => Math.random() - 0.5);
|
||||
// Combine and shuffle all options (1 correct + 5 wrong = 6 total)
|
||||
const allOptions = [correctAnswerText, ...wrongAnswers].sort(() => Math.random() - 0.5);
|
||||
|
||||
this.currentQuestionData = {
|
||||
question: correctAnswer.original,
|
||||
correctAnswer: correctAnswer.translation,
|
||||
options: allOptions
|
||||
question: questionText,
|
||||
correctAnswer: correctAnswerText,
|
||||
options: allOptions,
|
||||
direction: this.quizDirection
|
||||
};
|
||||
|
||||
this.renderQuestion();
|
||||
@ -295,9 +384,13 @@ class QuizGame {
|
||||
renderQuestion() {
|
||||
const { question, options } = this.currentQuestionData;
|
||||
|
||||
// Update question text
|
||||
// Update question text with direction indicator
|
||||
const direction = this.currentQuestionData.direction;
|
||||
const directionText = direction === 'original_to_translation' ?
|
||||
'What is the translation of' : 'What is the original word for';
|
||||
|
||||
document.getElementById('question-text').innerHTML = `
|
||||
What is the translation of "<strong>${question}</strong>"?
|
||||
${directionText} "<strong>${question}</strong>"?
|
||||
`;
|
||||
|
||||
// Clear and generate options
|
||||
|
||||
@ -50,7 +50,7 @@ class StoryBuilderGame {
|
||||
<h3>❌ Error loading</h3>
|
||||
<p>This content doesn't have enough vocabulary for Story Builder.</p>
|
||||
<p>The game needs at least 6 vocabulary words with types (noun, verb, adjective, etc.).</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -109,6 +109,34 @@ class StoryReader {
|
||||
});
|
||||
}
|
||||
|
||||
// NEW: Check for simple texts and convert them to stories
|
||||
const texts = this.content.rawContent?.texts || this.content.texts;
|
||||
if (texts && Array.isArray(texts)) {
|
||||
texts.forEach((text, index) => {
|
||||
if (text && (text.title || text.original_language)) {
|
||||
const convertedStory = this.convertTextToStory(text, index);
|
||||
this.availableStories.push({
|
||||
id: `text_${index}`,
|
||||
title: text.title || `Text ${index + 1}`,
|
||||
data: convertedStory,
|
||||
source: 'text'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NEW: Check for sentences and create a story from them
|
||||
const sentences = this.content.rawContent?.sentences || this.content.sentences;
|
||||
if (sentences && Array.isArray(sentences) && sentences.length > 0 && this.availableStories.length === 0) {
|
||||
const sentencesStory = this.convertSentencesToStory(sentences);
|
||||
this.availableStories.push({
|
||||
id: 'sentences',
|
||||
title: 'Reading Practice',
|
||||
data: sentencesStory,
|
||||
source: 'sentences'
|
||||
});
|
||||
}
|
||||
|
||||
logSh(`📚 Discovered ${this.availableStories.length} stories:`, this.availableStories.map(s => s.title), 'INFO');
|
||||
}
|
||||
|
||||
@ -141,7 +169,7 @@ class StoryReader {
|
||||
<div class="story-error">
|
||||
<h3>❌ Error</h3>
|
||||
<p>${message}</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -1018,6 +1046,104 @@ class StoryReader {
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Convert simple text to story format
|
||||
convertTextToStory(text, index) {
|
||||
// Split text into sentences for easier reading
|
||||
const sentences = this.splitTextIntoSentences(text.original_language, text.user_language);
|
||||
|
||||
return {
|
||||
title: text.title || `Text ${index + 1}`,
|
||||
totalSentences: sentences.length,
|
||||
chapters: [{
|
||||
title: "Reading Text",
|
||||
sentences: sentences
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
// NEW: Convert array of sentences to story format
|
||||
convertSentencesToStory(sentences) {
|
||||
const storyTitle = this.content.name || "Reading Practice";
|
||||
|
||||
const convertedSentences = sentences.map((sentence, index) => ({
|
||||
id: index + 1,
|
||||
original: sentence.original_language || sentence.english || sentence.original || '',
|
||||
translation: sentence.user_language || sentence.chinese || sentence.french || sentence.translation || '',
|
||||
words: this.breakSentenceIntoWords(
|
||||
sentence.original_language || sentence.english || sentence.original || '',
|
||||
sentence.user_language || sentence.chinese || sentence.french || sentence.translation || ''
|
||||
)
|
||||
}));
|
||||
|
||||
return {
|
||||
title: storyTitle,
|
||||
totalSentences: convertedSentences.length,
|
||||
chapters: [{
|
||||
title: "Reading Sentences",
|
||||
sentences: convertedSentences
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
// NEW: Split long text into manageable sentences
|
||||
splitTextIntoSentences(originalText, translationText) {
|
||||
// Split by sentence endings
|
||||
const originalSentences = originalText.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
||||
const translationSentences = translationText.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
||||
|
||||
const sentences = [];
|
||||
const maxSentences = Math.max(originalSentences.length, translationSentences.length);
|
||||
|
||||
for (let i = 0; i < maxSentences; i++) {
|
||||
const original = (originalSentences[i] || '').trim();
|
||||
const translation = (translationSentences[i] || '').trim();
|
||||
|
||||
if (original || translation) {
|
||||
sentences.push({
|
||||
id: i + 1,
|
||||
original: original + (original && !original.match(/[.!?]$/) ? '.' : ''),
|
||||
translation: translation + (translation && !translation.match(/[.!?]$/) ? '.' : ''),
|
||||
words: this.breakSentenceIntoWords(original, translation)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sentences;
|
||||
}
|
||||
|
||||
// NEW: Break sentence into word-by-word format for Story Reader
|
||||
breakSentenceIntoWords(original, translation) {
|
||||
if (!original) return [];
|
||||
|
||||
const words = original.split(/\s+/).filter(word => word.trim().length > 0);
|
||||
const translationWords = translation ? translation.split(/\s+/).filter(word => word.trim().length > 0) : [];
|
||||
|
||||
return words.map((word, index) => {
|
||||
// Clean punctuation for word lookup
|
||||
const cleanWord = word.replace(/[.,!?;:"'()[\]{}\-–—]/g, '').toLowerCase();
|
||||
|
||||
// Try to find in vocabulary
|
||||
let wordTranslation = translationWords[index] || '';
|
||||
let wordType = 'word';
|
||||
let pronunciation = '';
|
||||
|
||||
// Look up in content vocabulary if available
|
||||
if (this.vocabulary && this.vocabulary[cleanWord]) {
|
||||
const vocabEntry = this.vocabulary[cleanWord];
|
||||
wordTranslation = vocabEntry.user_language || vocabEntry.translation || wordTranslation;
|
||||
wordType = vocabEntry.type || 'word';
|
||||
pronunciation = vocabEntry.pronunciation || '';
|
||||
}
|
||||
|
||||
return {
|
||||
word: word,
|
||||
translation: wordTranslation,
|
||||
type: wordType,
|
||||
pronunciation: pronunciation
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// TTS Methods
|
||||
playSentenceTTS() {
|
||||
const sentenceData = this.getCurrentSentenceData();
|
||||
@ -1163,7 +1289,7 @@ class StoryReader {
|
||||
<p><strong>Words read:</strong> ${this.wordsRead}</p>
|
||||
<p><strong>Total sentences:</strong> ${this.totalSentences}</p>
|
||||
<button onclick="this.restart()" class="nav-btn primary">Read Again</button>
|
||||
<button onclick="AppNavigation.goBack()" class="nav-btn">Back to Menu</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="nav-btn">Back to Menu</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
@ -1,535 +0,0 @@
|
||||
// === MODULE TEXT READER ===
|
||||
|
||||
class TextReaderGame {
|
||||
constructor(options) {
|
||||
this.container = options.container;
|
||||
this.content = options.content;
|
||||
this.onScoreUpdate = options.onScoreUpdate || (() => {});
|
||||
this.onGameEnd = options.onGameEnd || (() => {});
|
||||
|
||||
// Reader state
|
||||
this.currentTextIndex = 0;
|
||||
this.currentSentenceIndex = 0;
|
||||
this.isRunning = false;
|
||||
|
||||
// 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;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// 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();
|
||||
}
|
||||
|
||||
showInitError() {
|
||||
this.container.innerHTML = `
|
||||
<div class="game-error">
|
||||
<h3>❌ Error loading</h3>
|
||||
<p>This content doesn't contain texts, sentences, or vocabulary compatible with Text Reader.</p>
|
||||
<p>The reader needs content with ultra-modular format (original_language/user_language).</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
extractTexts(content) {
|
||||
let texts = [];
|
||||
|
||||
logSh('📖 Extracting texts from:', content?.name || 'content', 'INFO');
|
||||
|
||||
// Priority 1: Use raw module content (simple format)
|
||||
if (content.rawContent) {
|
||||
logSh('📦 Using raw module content', 'INFO');
|
||||
return this.extractTextsFromRaw(content.rawContent);
|
||||
}
|
||||
|
||||
// Priority 2: Ultra-modular format (texts array) - ONLY format supported
|
||||
if (content.texts && Array.isArray(content.texts)) {
|
||||
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 = [];
|
||||
|
||||
// Ultra-modular format (texts array) - ONLY format supported
|
||||
if (rawContent.texts && Array.isArray(rawContent.texts)) {
|
||||
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 ultra-modular format`, 'INFO');
|
||||
}
|
||||
|
||||
return this.finalizeTexts(texts);
|
||||
}
|
||||
|
||||
finalizeTexts(texts) {
|
||||
// Validation and cleanup
|
||||
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 = [
|
||||
{
|
||||
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 = `
|
||||
<div class="text-reader-wrapper">
|
||||
<!-- Text Selection -->
|
||||
<div class="text-selection">
|
||||
<label for="text-selector" class="text-selector-label">Choose a text:</label>
|
||||
<select id="text-selector" class="text-selector">
|
||||
<!-- Options will be generated here -->
|
||||
</select>
|
||||
<div class="text-progress">
|
||||
<span id="sentence-counter">1 / 1</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reading Area -->
|
||||
<div class="reading-area" id="reading-area">
|
||||
<div class="sentence-display" id="sentence-display">
|
||||
<!-- Current sentence will appear here -->
|
||||
</div>
|
||||
|
||||
<div class="full-text-display" id="full-text-display" style="display: none;">
|
||||
<!-- Full text will appear here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Controls -->
|
||||
<div class="reader-controls">
|
||||
<button class="control-btn secondary" id="prev-sentence-btn" disabled>← Previous</button>
|
||||
<button class="control-btn primary" id="next-sentence-btn">Next →</button>
|
||||
<button class="control-btn secondary" id="show-full-btn">📄 Full Text</button>
|
||||
</div>
|
||||
|
||||
<!-- Full Text Navigation -->
|
||||
<div class="full-text-navigation" id="full-text-navigation" style="display: none;">
|
||||
<button class="control-btn secondary" id="back-to-reading-btn">📖 Back to Reading</button>
|
||||
</div>
|
||||
|
||||
<!-- Feedback Area -->
|
||||
<div class="feedback-area" id="feedback-area">
|
||||
<div class="instruction">
|
||||
Use Next/Previous buttons to navigate through sentences
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
document.getElementById('next-sentence-btn').addEventListener('click', () => this.nextSentence());
|
||||
document.getElementById('prev-sentence-btn').addEventListener('click', () => this.prevSentence());
|
||||
document.getElementById('show-full-btn').addEventListener('click', () => this.showFullText());
|
||||
|
||||
document.getElementById('back-to-reading-btn').addEventListener('click', () => this.backToReading());
|
||||
document.getElementById('text-selector').addEventListener('change', (e) => this.selectText(parseInt(e.target.value)));
|
||||
|
||||
// Keyboard navigation
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (!this.isRunning) return;
|
||||
|
||||
if (this.showingFullText) {
|
||||
if (e.key === 'Escape') this.backToReading();
|
||||
} else {
|
||||
if (e.key === 'ArrowLeft') this.prevSentence();
|
||||
else if (e.key === 'ArrowRight') this.nextSentence();
|
||||
else if (e.key === 'Enter' || e.key === ' ') this.showFullText();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
logSh('📖 Text Reader: Starting', 'INFO');
|
||||
this.isRunning = true;
|
||||
}
|
||||
|
||||
restart() {
|
||||
logSh('🔄 Text Reader: Restarting', 'INFO');
|
||||
this.reset();
|
||||
this.start();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.currentTextIndex = 0;
|
||||
this.currentSentenceIndex = 0;
|
||||
this.isRunning = false;
|
||||
this.showingFullText = false;
|
||||
this.loadText();
|
||||
}
|
||||
|
||||
loadText() {
|
||||
// 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.updateDisplay();
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
populateTextSelector() {
|
||||
const selector = document.getElementById('text-selector');
|
||||
selector.innerHTML = '';
|
||||
|
||||
this.texts.forEach((text, index) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = index;
|
||||
option.textContent = text.title || `Text ${index + 1}`;
|
||||
if (index === this.currentTextIndex) {
|
||||
option.selected = true;
|
||||
}
|
||||
selector.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
selectText(textIndex) {
|
||||
if (textIndex >= 0 && textIndex < this.texts.length) {
|
||||
this.currentTextIndex = textIndex;
|
||||
this.currentText = this.texts[this.currentTextIndex];
|
||||
this.sentences = this.splitIntoSentences(this.currentText.original);
|
||||
this.currentSentenceIndex = 0;
|
||||
|
||||
// Always go back to sentence reading when changing text
|
||||
if (this.showingFullText) {
|
||||
this.backToReading();
|
||||
} else {
|
||||
this.updateDisplay();
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
this.showFeedback(`Switched to: ${this.currentText.title}`, 'info');
|
||||
}
|
||||
}
|
||||
|
||||
splitIntoSentences(text) {
|
||||
// Split by periods, exclamation marks, and question marks
|
||||
// Keep the punctuation with the sentence
|
||||
const sentences = text.split(/(?<=[.!?])\s+/)
|
||||
.filter(sentence => sentence.trim() !== '')
|
||||
.map(sentence => sentence.trim());
|
||||
|
||||
return sentences.length > 0 ? sentences : [text];
|
||||
}
|
||||
|
||||
nextSentence() {
|
||||
if (this.currentSentenceIndex < this.sentences.length - 1) {
|
||||
this.currentSentenceIndex++;
|
||||
this.updateDisplay();
|
||||
this.updateUI();
|
||||
} else {
|
||||
// End of sentences, show full text automatically
|
||||
this.showFullText();
|
||||
}
|
||||
}
|
||||
|
||||
prevSentence() {
|
||||
if (this.currentSentenceIndex > 0) {
|
||||
this.currentSentenceIndex--;
|
||||
this.updateDisplay();
|
||||
this.updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
showFullText() {
|
||||
this.showingFullText = true;
|
||||
document.getElementById('sentence-display').style.display = 'none';
|
||||
document.getElementById('full-text-display').style.display = 'block';
|
||||
document.getElementById('full-text-display').innerHTML = `
|
||||
<div class="full-text-content">
|
||||
<div class="text-original">
|
||||
<h4>Original:</h4>
|
||||
<p>${this.currentText.original}</p>
|
||||
</div>
|
||||
<div class="text-translation">
|
||||
<h4>Translation:</h4>
|
||||
<p>${this.currentText.translation}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Show full text navigation controls
|
||||
document.querySelector('.reader-controls').style.display = 'none';
|
||||
document.getElementById('full-text-navigation').style.display = 'flex';
|
||||
|
||||
this.showFeedback('Full text displayed. Use dropdown to change text.', 'info');
|
||||
}
|
||||
|
||||
backToReading() {
|
||||
this.showingFullText = false;
|
||||
document.getElementById('sentence-display').style.display = 'block';
|
||||
document.getElementById('full-text-display').style.display = 'none';
|
||||
|
||||
// Show sentence navigation controls
|
||||
document.querySelector('.reader-controls').style.display = 'flex';
|
||||
document.getElementById('full-text-navigation').style.display = 'none';
|
||||
|
||||
this.updateDisplay();
|
||||
this.updateUI();
|
||||
this.showFeedback('Back to sentence-by-sentence reading.', 'info');
|
||||
}
|
||||
|
||||
// Text navigation methods removed - using dropdown instead
|
||||
|
||||
updateDisplay() {
|
||||
if (this.showingFullText) return;
|
||||
|
||||
const sentenceDisplay = document.getElementById('sentence-display');
|
||||
const currentSentence = this.sentences[this.currentSentenceIndex];
|
||||
|
||||
sentenceDisplay.innerHTML = `
|
||||
<div class="current-sentence">
|
||||
${currentSentence}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
// Update counters
|
||||
document.getElementById('sentence-counter').textContent = `${this.currentSentenceIndex + 1} / ${this.sentences.length}`;
|
||||
|
||||
// Update button states
|
||||
document.getElementById('prev-sentence-btn').disabled = this.currentSentenceIndex === 0;
|
||||
document.getElementById('next-sentence-btn').disabled = false;
|
||||
document.getElementById('next-sentence-btn').textContent =
|
||||
this.currentSentenceIndex === this.sentences.length - 1 ? 'Full Text →' : 'Next →';
|
||||
}
|
||||
|
||||
// updateTextNavigation method removed - using dropdown instead
|
||||
|
||||
showFeedback(message, type = 'info') {
|
||||
const feedbackArea = document.getElementById('feedback-area');
|
||||
feedbackArea.innerHTML = `<div class="instruction ${type}">${message}</div>`;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.isRunning = false;
|
||||
this.container.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Module registration
|
||||
window.GameModules = window.GameModules || {};
|
||||
window.GameModules.TextReader = TextReaderGame;
|
||||
@ -10,7 +10,7 @@ class WhackAMoleHardGame {
|
||||
// Game state
|
||||
this.score = 0;
|
||||
this.errors = 0;
|
||||
this.maxErrors = 5;
|
||||
this.maxErrors = 3;
|
||||
this.gameTime = 60; // 60 seconds
|
||||
this.timeLeft = this.gameTime;
|
||||
this.isRunning = false;
|
||||
@ -59,7 +59,7 @@ class WhackAMoleHardGame {
|
||||
<h3>❌ Loading Error</h3>
|
||||
<p>This content does not contain vocabulary compatible with Whack-a-Mole.</p>
|
||||
<p>The game requires words with their translations.</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ class WhackAMoleGame {
|
||||
// Game state
|
||||
this.score = 0;
|
||||
this.errors = 0;
|
||||
this.maxErrors = 5;
|
||||
this.maxErrors = 3;
|
||||
this.gameTime = 60; // 60 secondes
|
||||
this.timeLeft = this.gameTime;
|
||||
this.isRunning = false;
|
||||
@ -58,7 +58,7 @@ class WhackAMoleGame {
|
||||
<h3>❌ Loading Error</h3>
|
||||
<p>This content does not contain vocabulary compatible with Whack-a-Mole.</p>
|
||||
<p>The game requires words with their translations.</p>
|
||||
<button onclick="AppNavigation.goBack()" class="back-btn">← Back</button>
|
||||
<button onclick="AppNavigation.navigateTo('games')" class="back-btn">← Back</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -454,7 +454,7 @@ class WordStormGame {
|
||||
<h2>🌪️ Word Storm</h2>
|
||||
<p>❌ No vocabulary found in this content.</p>
|
||||
<p>This game requires content with vocabulary words.</p>
|
||||
<button class="back-btn" onclick="window.history.back()">← Back to Games</button>
|
||||
<button class="back-btn" onclick="AppNavigation.navigateTo('games')">← Back to Games</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -1,96 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test Chinese Beginner + Grammar Discovery</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
|
||||
#container { width: 100%; height: 85vh; border: 2px solid #ddd; border-radius: 10px; }
|
||||
.controls { margin-bottom: 20px; }
|
||||
button { padding: 10px 20px; margin: 5px; border: none; border-radius: 5px; background: #667eea; color: white; cursor: pointer; }
|
||||
button:hover { background: #5a67d8; }
|
||||
.info { background: #f0fff4; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🌸 Test: Chinese Beginner Story + Grammar Discovery</h1>
|
||||
|
||||
<div class="info">
|
||||
<h3>🎯 Test du nouveau module "Le Jardin Magique"</h3>
|
||||
<p><strong>Objectif :</strong> Vérifier que le contenu chinois → français fonctionne avec Grammar Discovery</p>
|
||||
<p><strong>Attendu :</strong> Sélecteur de concept avec "Structure de Phrase de Base"</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button onclick="startGame()">🚀 Start Grammar Discovery</button>
|
||||
<button onclick="restartGame()">🔄 Restart</button>
|
||||
</div>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
// Mock dependencies
|
||||
window.logSh = (msg, level) => console.log(`[${level}] ${msg}`);
|
||||
window.Utils = { storage: { get: () => [], set: () => {} } };
|
||||
window.GameModules = {};
|
||||
window.ContentModules = {};
|
||||
|
||||
// Mock SettingsManager for TTS
|
||||
window.SettingsManager = {
|
||||
speak: (text, options = {}) => {
|
||||
console.log(`🔊 TTS: "${text}" (${options.lang || 'auto'} at ${options.rate || 1.0}x)`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script src="js/content/chinese-beginner-story.js"></script>
|
||||
<script src="js/games/grammar-discovery.js"></script>
|
||||
|
||||
<script>
|
||||
let currentGame = null;
|
||||
|
||||
function startGame() {
|
||||
try {
|
||||
const container = document.getElementById('container');
|
||||
const content = window.ContentModules.ChineseBeginnerStory;
|
||||
|
||||
if (!content) {
|
||||
console.error('❌ Chinese Beginner Story content not found');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Content loaded:', content.name);
|
||||
console.log('📚 Grammar topics:', Object.keys(content.grammar || {}));
|
||||
console.log('🔤 Vocabulary count:', Object.keys(content.vocabulary || {}).length);
|
||||
console.log('📖 Story chapters:', content.story?.chapters?.length || 0);
|
||||
|
||||
currentGame = new window.GameModules.GrammarDiscovery({
|
||||
container: container,
|
||||
content: content,
|
||||
onScoreUpdate: score => console.log('📊 Score updated:', score),
|
||||
onGameEnd: score => console.log('🏁 Game ended with score:', score)
|
||||
});
|
||||
|
||||
console.log('🎮 Grammar Discovery started with Chinese Beginner Story!');
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting game:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
if (currentGame && currentGame.restart) {
|
||||
currentGame.restart();
|
||||
console.log('🔄 Game restarted');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-start when page loads
|
||||
window.addEventListener('load', () => {
|
||||
console.log('🔧 Testing Chinese Beginner Story with Grammar Discovery...');
|
||||
console.log('📚 Expected: Concept selector with "Structure de Phrase de Base"');
|
||||
setTimeout(startGame, 500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,92 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Grammar Discovery Test</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
|
||||
#container { width: 100%; height: 80vh; border: 2px solid #ddd; border-radius: 10px; }
|
||||
.controls { margin-bottom: 20px; }
|
||||
button { padding: 10px 20px; margin: 5px; border: none; border-radius: 5px; background: #667eea; color: white; cursor: pointer; }
|
||||
button:hover { background: #5a67d8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎓 Grammar Discovery Game Test</h1>
|
||||
|
||||
<div class="controls">
|
||||
<button onclick="startGame()">🚀 Start Game</button>
|
||||
<button onclick="restartGame()">🔄 Restart</button>
|
||||
</div>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
// Mock dependencies
|
||||
window.logSh = (msg, level) => console.log(`[${level}] ${msg}`);
|
||||
window.Utils = { storage: { get: () => [], set: () => {} } };
|
||||
window.GameModules = {};
|
||||
window.ContentModules = {};
|
||||
|
||||
// Mock SettingsManager for TTS
|
||||
window.SettingsManager = {
|
||||
speak: (text, options = {}) => {
|
||||
console.log(`🔊 TTS: "${text}" (${options.lang || 'auto'} at ${options.rate || 1.0}x)`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script src="js/content/grammar-lesson-le.js"></script>
|
||||
<script src="js/games/grammar-discovery.js"></script>
|
||||
|
||||
<script>
|
||||
let currentGame = null;
|
||||
|
||||
function startGame() {
|
||||
try {
|
||||
const container = document.getElementById('container');
|
||||
const content = window.ContentModules.GrammarLessonLe;
|
||||
|
||||
if (!content) {
|
||||
console.error('❌ Grammar lesson content not found');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Content loaded:', content.name);
|
||||
console.log('📚 Grammar topics:', Object.keys(content.grammar || {}));
|
||||
|
||||
currentGame = new window.GameModules.GrammarDiscovery({
|
||||
container: container,
|
||||
content: content,
|
||||
onScoreUpdate: score => console.log('📊 Score updated:', score),
|
||||
onGameEnd: score => console.log('🏁 Game ended with score:', score)
|
||||
});
|
||||
|
||||
console.log('🎮 Grammar Discovery game started!');
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting game:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
if (currentGame && currentGame.restart) {
|
||||
currentGame.restart();
|
||||
console.log('🔄 Game restarted');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-start when page loads
|
||||
window.addEventListener('load', () => {
|
||||
console.log('🔧 Testing NEW CONCEPT SELECTOR Grammar Discovery...');
|
||||
console.log('🎯 Features:');
|
||||
console.log('- Dropdown to select specific grammar concept');
|
||||
console.log('- Preview with statistics for selected concept');
|
||||
console.log('- Laser focus on chosen concept only');
|
||||
console.log('- 8-step rotation system for focused learning');
|
||||
console.log('✅ Concept selector system ready!');
|
||||
setTimeout(startGame, 500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,106 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test Letter Discovery Game</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
|
||||
#container { width: 100%; height: 85vh; border: 2px solid #ddd; border-radius: 10px; }
|
||||
.controls { margin-bottom: 20px; }
|
||||
button { padding: 10px 20px; margin: 5px; border: none; border-radius: 5px; background: #667eea; color: white; cursor: pointer; }
|
||||
button:hover { background: #5a67d8; }
|
||||
.info { background: #f0fff4; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🔤 Test: Letter Discovery Game</h1>
|
||||
|
||||
<div class="info">
|
||||
<h3>🎯 Testing Letter Discovery with French Beginner Story</h3>
|
||||
<p><strong>Flow:</strong> Letter discovery → Word exploration → Practice</p>
|
||||
<p><strong>Expected:</strong> Letters A, B, C, F, G, J, M, P, R, T, V with their words</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button onclick="startGame()">🚀 Start Letter Discovery</button>
|
||||
<button onclick="restartGame()">🔄 Restart</button>
|
||||
</div>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
// Mock dependencies
|
||||
window.logSh = (msg, level) => console.log(`[${level}] ${msg}`);
|
||||
window.Utils = { storage: { get: () => [], set: () => {} } };
|
||||
window.GameModules = {};
|
||||
window.ContentModules = {};
|
||||
|
||||
// Mock SettingsManager for TTS
|
||||
window.SettingsManager = {
|
||||
speak: (text, options = {}) => {
|
||||
console.log(`🔊 TTS: "${text}" (${options.lang || 'auto'} at ${options.rate || 1.0}x)`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script src="js/content/french-beginner-story.js"></script>
|
||||
<script src="js/games/letter-discovery.js"></script>
|
||||
|
||||
<script>
|
||||
let currentGame = null;
|
||||
|
||||
function startGame() {
|
||||
try {
|
||||
const container = document.getElementById('container');
|
||||
const content = window.ContentModules.FrenchBeginnerStory;
|
||||
|
||||
if (!content) {
|
||||
console.error('❌ French Beginner Story content not found');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Content loaded:', content.name);
|
||||
console.log('🔤 Letters available:', Object.keys(content.letters || {}));
|
||||
console.log('📚 Total letters:', Object.keys(content.letters || {}).length);
|
||||
|
||||
// Count words per letter
|
||||
if (content.letters) {
|
||||
Object.keys(content.letters).forEach(letter => {
|
||||
console.log(`📝 Letter ${letter}: ${content.letters[letter].length} words`);
|
||||
});
|
||||
}
|
||||
|
||||
currentGame = new window.GameModules.LetterDiscovery({
|
||||
container: container,
|
||||
content: content,
|
||||
onScoreUpdate: score => console.log('📊 Score updated:', score),
|
||||
onGameEnd: score => console.log('🏁 Game ended with score:', score)
|
||||
});
|
||||
|
||||
if (currentGame.start) {
|
||||
currentGame.start();
|
||||
}
|
||||
|
||||
console.log('🎮 Letter Discovery started successfully!');
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting game:', error);
|
||||
console.error('Stack trace:', error.stack);
|
||||
}
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
if (currentGame && currentGame.restart) {
|
||||
currentGame.restart();
|
||||
console.log('🔄 Game restarted');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-start when page loads
|
||||
window.addEventListener('load', () => {
|
||||
console.log('🔧 Testing Letter Discovery...');
|
||||
setTimeout(startGame, 500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,108 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test River Run Game</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
|
||||
#container { width: 100%; height: 85vh; border: 2px solid #ddd; border-radius: 10px; }
|
||||
.controls { margin-bottom: 20px; }
|
||||
button { padding: 10px 20px; margin: 5px; border: none; border-radius: 5px; background: #4682B4; color: white; cursor: pointer; }
|
||||
button:hover { background: #5F9EA0; }
|
||||
.info { background: #E6F7FF; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #4682B4; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🌊 Test: River Run Game</h1>
|
||||
|
||||
<div class="info">
|
||||
<h3>🎯 Testing River Run with French Beginner Story</h3>
|
||||
<p><strong>Gameplay:</strong></p>
|
||||
<ul>
|
||||
<li>🛶 Click anywhere to move your boat</li>
|
||||
<li>🎯 Target word appears at top - find and catch it!</li>
|
||||
<li>❌ Avoid all other floating words (obstacles)</li>
|
||||
<li>⚡ Collect power-ups for bonuses</li>
|
||||
<li>📈 Speed increases as you progress</li>
|
||||
</ul>
|
||||
<p><strong>Expected:</strong> Endless river runner with French vocabulary</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button onclick="startGame()">🚀 Start River Run</button>
|
||||
<button onclick="restartGame()">🔄 Restart</button>
|
||||
</div>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
// Mock dependencies
|
||||
window.logSh = (msg, level) => console.log(`[${level}] ${msg}`);
|
||||
window.Utils = { storage: { get: () => [], set: () => {} } };
|
||||
window.GameModules = {};
|
||||
window.ContentModules = {};
|
||||
|
||||
// Mock SettingsManager for TTS
|
||||
window.SettingsManager = {
|
||||
speak: (text, options = {}) => {
|
||||
console.log(`🔊 TTS: "${text}" (${options.lang || 'auto'} at ${options.rate || 1.0}x)`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script src="js/content/french-beginner-story.js"></script>
|
||||
<script src="js/games/river-run.js"></script>
|
||||
|
||||
<script>
|
||||
let currentGame = null;
|
||||
|
||||
function startGame() {
|
||||
try {
|
||||
const container = document.getElementById('container');
|
||||
const content = window.ContentModules.FrenchBeginnerStory;
|
||||
|
||||
if (!content) {
|
||||
console.error('❌ French Beginner Story content not found');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Content loaded:', content.name);
|
||||
console.log('🔤 Vocabulary available:', Object.keys(content.vocabulary || {}).length);
|
||||
|
||||
// Log some vocabulary examples
|
||||
if (content.vocabulary) {
|
||||
const words = Object.keys(content.vocabulary).slice(0, 5);
|
||||
console.log('📝 Sample words:', words.join(', '));
|
||||
}
|
||||
|
||||
currentGame = new window.GameModules.RiverRun({
|
||||
container: container,
|
||||
content: content,
|
||||
onScoreUpdate: score => console.log('📊 Score updated:', score),
|
||||
onGameEnd: score => console.log('🏁 Game ended with score:', score)
|
||||
});
|
||||
|
||||
console.log('🌊 River Run started! Click to begin sailing!');
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting game:', error);
|
||||
console.error('Stack trace:', error.stack);
|
||||
}
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
if (currentGame && currentGame.restart) {
|
||||
currentGame.restart();
|
||||
console.log('🔄 Game restarted');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-load when page loads
|
||||
window.addEventListener('load', () => {
|
||||
console.log('🔧 Testing River Run...');
|
||||
console.log('💡 Instructions: Click anywhere on the river to move your boat!');
|
||||
setTimeout(startGame, 500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,98 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Quick Word Discovery Test</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
|
||||
#container { width: 100%; height: 85vh; border: 2px solid #ddd; border-radius: 10px; }
|
||||
.controls { margin-bottom: 20px; }
|
||||
button { padding: 10px 20px; margin: 5px; border: none; border-radius: 5px; background: #667eea; color: white; cursor: pointer; }
|
||||
button:hover { background: #5a67d8; }
|
||||
.info { background: #f0fff4; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🔍 Test: Word Discovery with Shuffle</h1>
|
||||
|
||||
<div class="info">
|
||||
<h3>🎯 Testing Word Discovery Game</h3>
|
||||
<p><strong>Objective:</strong> Verify that Word Discovery loads and shuffles work</p>
|
||||
<p><strong>Expected:</strong> Game starts with discovery phase, then shuffled practice</p>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button onclick="startGame()">🚀 Start Word Discovery</button>
|
||||
<button onclick="restartGame()">🔄 Restart</button>
|
||||
</div>
|
||||
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
// Mock dependencies
|
||||
window.logSh = (msg, level) => console.log(`[${level}] ${msg}`);
|
||||
window.Utils = { storage: { get: () => [], set: () => {} } };
|
||||
window.GameModules = {};
|
||||
window.ContentModules = {};
|
||||
|
||||
// Mock SettingsManager for TTS
|
||||
window.SettingsManager = {
|
||||
speak: (text, options = {}) => {
|
||||
console.log(`🔊 TTS: "${text}" (${options.lang || 'auto'} at ${options.rate || 1.0}x)`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script src="js/content/example-with-images.js"></script>
|
||||
<script src="js/games/word-discovery.js"></script>
|
||||
|
||||
<script>
|
||||
let currentGame = null;
|
||||
|
||||
function startGame() {
|
||||
try {
|
||||
const container = document.getElementById('container');
|
||||
const content = window.ContentModules.ExampleWithImages;
|
||||
|
||||
if (!content) {
|
||||
console.error('❌ Example With Images content not found');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Content loaded:', content.name);
|
||||
console.log('🔤 Vocabulary count:', Object.keys(content.vocabulary || {}).length);
|
||||
|
||||
currentGame = new window.GameModules.WordDiscovery({
|
||||
container: container,
|
||||
content: content,
|
||||
onScoreUpdate: score => console.log('📊 Score updated:', score),
|
||||
onGameEnd: score => console.log('🏁 Game ended with score:', score)
|
||||
});
|
||||
|
||||
if (currentGame.start) {
|
||||
currentGame.start();
|
||||
}
|
||||
|
||||
console.log('🎮 Word Discovery started with shuffle functionality!');
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting game:', error);
|
||||
console.error('Stack trace:', error.stack);
|
||||
}
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
if (currentGame && currentGame.restart) {
|
||||
currentGame.restart();
|
||||
console.log('🔄 Game restarted');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-start when page loads
|
||||
window.addEventListener('load', () => {
|
||||
console.log('🔧 Testing Word Discovery with shuffle...');
|
||||
setTimeout(startGame, 500);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
159
test-wta1b1.html
159
test-wta1b1.html
@ -1,159 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test WTA1B1 Content Module</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 20px; font-family: Arial, sans-serif; }
|
||||
.info { background: #E6F7FF; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #1890FF; }
|
||||
.test-results { background: #F6FFED; padding: 15px; border-radius: 8px; margin: 20px 0; border-left: 4px solid #52C41A; }
|
||||
.error { background: #FFF2F0; padding: 15px; border-radius: 8px; margin: 20px 0; border-left: 4px solid #FF4D4F; }
|
||||
.vocab-sample, .grammar-sample { background: #FAFAFA; padding: 10px; border-radius: 5px; margin: 10px 0; font-family: monospace; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🧪 Test: WTA1B1 Content Module</h1>
|
||||
|
||||
<div class="info">
|
||||
<h3>🎯 Testing WTA1B1 Integration</h3>
|
||||
<p><strong>Expected:</strong> English letters U, V, T & pets vocabulary with Chinese translation</p>
|
||||
<p><strong>Features:</strong> Grammar lessons, vocabulary, story content, corrections</p>
|
||||
</div>
|
||||
|
||||
<div id="test-results"></div>
|
||||
|
||||
<script>
|
||||
// Load WTA1B1 content
|
||||
const script = document.createElement('script');
|
||||
script.src = 'js/content/WTA1B1.js';
|
||||
script.onload = function() {
|
||||
runTests();
|
||||
};
|
||||
script.onerror = function() {
|
||||
showError('Failed to load WTA1B1.js');
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
|
||||
function runTests() {
|
||||
const resultsDiv = document.getElementById('test-results');
|
||||
|
||||
try {
|
||||
// Check if module is loaded
|
||||
const content = window.ContentModules?.WTA1B1;
|
||||
if (!content) {
|
||||
throw new Error('WTA1B1 module not found in window.ContentModules');
|
||||
}
|
||||
|
||||
let results = '<div class="test-results">';
|
||||
results += '<h3>✅ WTA1B1 Module Loaded Successfully</h3>';
|
||||
|
||||
// Basic info
|
||||
results += `<p><strong>ID:</strong> ${content.id || 'Not set'}</p>`;
|
||||
results += `<p><strong>Name:</strong> ${content.name}</p>`;
|
||||
results += `<p><strong>Description:</strong> ${content.description}</p>`;
|
||||
results += `<p><strong>Language:</strong> ${content.language} → ${content.userLanguage}</p>`;
|
||||
results += `<p><strong>Difficulty:</strong> ${content.difficulty}</p>`;
|
||||
|
||||
// Test vocabulary
|
||||
if (content.vocabulary) {
|
||||
const vocabKeys = Object.keys(content.vocabulary);
|
||||
results += `<p><strong>Vocabulary:</strong> ${vocabKeys.length} words</p>`;
|
||||
|
||||
// Sample vocabulary
|
||||
const sampleWords = vocabKeys.slice(0, 5);
|
||||
results += '<div class="vocab-sample">';
|
||||
results += '<strong>Sample vocabulary:</strong><br>';
|
||||
sampleWords.forEach(word => {
|
||||
const data = content.vocabulary[word];
|
||||
results += `${word} → ${data.user_language || data.translation} (${data.pronunciation || 'no pronunciation'})<br>`;
|
||||
});
|
||||
results += '</div>';
|
||||
} else {
|
||||
results += '<p><strong>Vocabulary:</strong> ❌ Not found</p>';
|
||||
}
|
||||
|
||||
// Test grammar
|
||||
if (content.grammar) {
|
||||
const grammarTopics = Object.keys(content.grammar);
|
||||
results += `<p><strong>Grammar Topics:</strong> ${grammarTopics.length}</p>`;
|
||||
|
||||
results += '<div class="grammar-sample">';
|
||||
results += '<strong>Grammar topics:</strong><br>';
|
||||
grammarTopics.forEach(topic => {
|
||||
const grammarData = content.grammar[topic];
|
||||
results += `• ${grammarData.title}<br>`;
|
||||
});
|
||||
results += '</div>';
|
||||
} else {
|
||||
results += '<p><strong>Grammar:</strong> ❌ Not found</p>';
|
||||
}
|
||||
|
||||
// Test story
|
||||
if (content.story && content.story.chapters) {
|
||||
const totalChapters = content.story.chapters.length;
|
||||
const totalSentences = content.story.chapters.reduce((sum, chapter) =>
|
||||
sum + (chapter.sentences ? chapter.sentences.length : 0), 0);
|
||||
results += `<p><strong>Story:</strong> ${totalChapters} chapters, ${totalSentences} sentences</p>`;
|
||||
} else {
|
||||
results += '<p><strong>Story:</strong> ❌ Not found</p>';
|
||||
}
|
||||
|
||||
// Test exercises
|
||||
if (content.fillInBlanks) {
|
||||
results += `<p><strong>Fill-in-blanks:</strong> ${content.fillInBlanks.length} exercises</p>`;
|
||||
}
|
||||
|
||||
if (content.corrections) {
|
||||
results += `<p><strong>Corrections:</strong> ${content.corrections.length} exercises</p>`;
|
||||
}
|
||||
|
||||
// Content compatibility test
|
||||
results += '<h4>🎮 Game Compatibility Test</h4>';
|
||||
|
||||
// Test for River Run
|
||||
if (content.vocabulary) {
|
||||
results += '✅ River Run: Compatible (has vocabulary)<br>';
|
||||
} else {
|
||||
results += '❌ River Run: Not compatible (no vocabulary)<br>';
|
||||
}
|
||||
|
||||
// Test for Word Discovery
|
||||
if (content.vocabulary) {
|
||||
results += '✅ Word Discovery: Compatible (has vocabulary)<br>';
|
||||
} else {
|
||||
results += '❌ Word Discovery: Not compatible (no vocabulary)<br>';
|
||||
}
|
||||
|
||||
// Test for Grammar Discovery
|
||||
if (content.grammar) {
|
||||
results += '✅ Grammar Discovery: Compatible (has grammar)<br>';
|
||||
} else {
|
||||
results += '❌ Grammar Discovery: Not compatible (no grammar)<br>';
|
||||
}
|
||||
|
||||
// Test for Letter Discovery
|
||||
if (content.letters) {
|
||||
results += '✅ Letter Discovery: Compatible (has letters structure)<br>';
|
||||
} else {
|
||||
results += '⚠️ Letter Discovery: Partial compatibility (will auto-generate from vocabulary)<br>';
|
||||
}
|
||||
|
||||
results += '</div>';
|
||||
resultsDiv.innerHTML = results;
|
||||
|
||||
console.log('🎯 WTA1B1 Content Analysis:');
|
||||
console.log('Full content object:', content);
|
||||
|
||||
} catch (error) {
|
||||
showError('Test failed: ' + error.message);
|
||||
console.error('WTA1B1 test error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
const resultsDiv = document.getElementById('test-results');
|
||||
resultsDiv.innerHTML = `<div class="error"><h3>❌ Error</h3><p>${message}</p></div>`;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
2
tests/fixtures/content-samples.js
vendored
2
tests/fixtures/content-samples.js
vendored
@ -70,7 +70,7 @@ export const gameCompatibilityTestData = [
|
||||
vocabulary: { "test": "test" },
|
||||
texts: [{ title: "Test", content: "Test content" }]
|
||||
},
|
||||
expectedGames: ["whack-a-mole", "memory-match", "quiz-game", "text-reader"]
|
||||
expectedGames: ["whack-a-mole", "memory-match", "quiz-game", "story-reader"]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user