Class_generator/js/core/navigation.js
StillHammer 30a2028da6 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>
2025-09-20 11:22:56 +08:00

828 lines
32 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// === NAVIGATION SYSTEM ===
const AppNavigation = {
currentPage: 'home',
navigationHistory: ['home'],
gamesConfig: null,
contentScanner: new ContentScanner(),
scannedContent: null,
compatibilityChecker: null,
init() {
this.loadGamesConfig();
this.initContentScanner();
this.initCompatibilityChecker();
this.setupEventListeners();
this.handleInitialRoute();
},
async loadGamesConfig() {
// Direct use of default config (no fetch)
logSh('📁 Using default configuration', 'INFO');
this.gamesConfig = this.getDefaultConfig();
},
async initContentScanner() {
try {
logSh('🔍 Initializing content scanner...', 'INFO');
this.scannedContent = await this.contentScanner.scanAllContent();
logSh(`${this.scannedContent.found.length} content modules detected automatically`, 'INFO');
} catch (error) {
logSh('Content scan error:', error, 'ERROR');
}
},
initCompatibilityChecker() {
if (window.ContentGameCompatibility) {
this.compatibilityChecker = new ContentGameCompatibility();
logSh('🎯 Content-Game compatibility checker initialized', 'INFO');
} else {
logSh('⚠️ ContentGameCompatibility not found, compatibility checks disabled', 'WARN');
}
},
getDefaultConfig() {
return {
games: {
'whack-a-mole': {
enabled: true,
name: 'Whack-a-Mole',
icon: '🔨',
description: 'Hit the right answers!'
},
'whack-a-mole-hard': {
enabled: true,
name: 'Whack-a-Mole Hard',
icon: '💥',
description: '3 moles at once, 5x3 grid, harder!'
},
'memory-match': {
enabled: true,
name: 'Memory Match',
icon: '🧠',
description: 'Find matching English-French pairs!'
},
'quiz-game': {
enabled: true,
name: 'Quiz Game',
icon: '❓',
description: 'Answer vocabulary questions!'
},
'fill-the-blank': {
enabled: true,
name: 'Fill the Blank',
icon: '📝',
description: 'Complete sentences by filling in the blanks!'
},
'adventure-reader': {
enabled: true,
name: 'Adventure Reader',
icon: '⚔️',
description: 'Zelda-style adventure with vocabulary!'
},
'story-reader': {
enabled: true,
name: 'Story Reader',
icon: '📚',
description: 'Read long stories with sentence chunking and word-by-word translation'
},
'word-storm': {
enabled: true,
name: 'Word Storm',
icon: '🌪️',
description: 'Catch falling words before they hit the ground!'
},
'word-discovery': {
enabled: true,
name: 'Word Discovery',
icon: '🔍',
description: 'Learn new words with images and interactive practice!'
},
'letter-discovery': {
enabled: true,
name: 'Letter Discovery',
icon: '🔤',
description: 'Discover letters first, then explore words that start with each letter!'
},
'river-run': {
enabled: true,
name: 'River Run',
icon: '🌊',
description: 'Navigate the river and catch your target words while avoiding obstacles!'
},
'grammar-discovery': {
enabled: true,
name: 'Grammar Discovery',
icon: '📚',
description: 'Discover and learn grammar patterns through interactive examples!'
}
},
content: {
'sbs-level-8': {
enabled: true,
name: 'SBS Level 8',
icon: '📚',
description: 'SBS textbook vocabulary'
},
'animals': {
enabled: false,
name: 'Animals',
icon: '🐱',
description: 'Animal vocabulary'
},
'colors': {
enabled: false,
name: 'Colors & Numbers',
icon: '🌈',
description: 'Colors and numbers'
},
'story-prototype-1000words': {
enabled: true,
name: 'The Magical Library (1000 words)',
icon: '✨',
description: '1000-word adventure story with sentence-by-sentence chunking'
},
'story-test': {
enabled: true,
name: 'Story Test - Short Adventure',
icon: '📖',
description: 'Simple test story for Story Reader (8 sentences)'
},
'story-complete-1000words': {
enabled: true,
name: 'The Secret Garden Adventure',
icon: '🌸',
description: 'Complete 1000-word story with full pronunciation and translation'
},
'chinese-long-story': {
enabled: true,
name: 'The Dragon\'s Pearl (Chinese)',
icon: '🐉',
description: 'Chinese story with English translation and pinyin pronunciation'
},
'chinese-beginner-story': {
enabled: true,
name: 'Le Jardin Magique (Chinese → French)',
icon: '🌸',
description: 'Simple Chinese story for beginners with French translation'
},
'story-prototype-optimized': {
enabled: true,
name: 'The Magical Library (Optimized)',
icon: '⚡',
description: 'Story with smart vocabulary matching and game compatibility'
}
}
};
},
setupEventListeners() {
// URL navigation
window.addEventListener('popstate', () => {
this.handleInitialRoute();
});
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.goBack();
}
});
// Scroll pour masquer/afficher la breadcrumb
this.setupScrollBehavior();
},
async handleInitialRoute() {
const params = Utils.getUrlParams();
if (params.page === 'play' && params.game && params.content) {
this.showGamePage(params.game, params.content);
} else if (params.page === 'games' && params.content) {
await this.showGamesPage(params.content);
} else if (params.page === 'levels') {
this.showLevelsPage();
} else {
this.showHomePage();
}
},
setupScrollBehavior() {
let lastScrollY = window.scrollY;
let breadcrumb = null;
const handleScroll = () => {
if (!breadcrumb) {
breadcrumb = document.querySelector('.breadcrumb');
if (!breadcrumb) return;
}
const currentScrollY = window.scrollY;
// Si on scroll vers le bas et qu'on a scrollé plus de 50px
if (currentScrollY > lastScrollY && currentScrollY > 50) {
breadcrumb.classList.add('hidden');
breadcrumb.classList.remove('visible');
}
// Si on scroll vers le haut ou qu'on est près du top
else if (currentScrollY < lastScrollY || currentScrollY <= 50) {
breadcrumb.classList.remove('hidden');
breadcrumb.classList.add('visible');
}
lastScrollY = currentScrollY;
};
// Throttle scroll event pour les performances
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
handleScroll();
ticking = false;
});
ticking = true;
}
});
},
// Navigate to a page
navigateTo(page, game = null, content = null) {
logSh(`🧭 Navigating to: ${page} ${game ? `(game: ${game})` : ''} ${content ? `(content: ${content})` : ''}`, 'INFO');
const params = { page };
if (game) params.game = game;
if (content) params.content = content;
Utils.setUrlParams(params);
// Update history
if (this.currentPage !== page) {
this.navigationHistory.push(page);
}
this.currentPage = page;
// Display appropriate page
switch(page) {
case 'games':
if (!content) {
logSh(`⚠️ Pas de contenu spécifié pour la page games, retour aux levels`, 'WARN');
this.showLevelsPage();
} else {
// Utiliser l'async pour les jeux pour le système de compatibilité
this.showGamesPage(content).catch(error => {
logSh(`❌ Erreur lors de l'affichage des jeux: ${error.message}`, 'ERROR');
// Retour aux levels en cas d'erreur
this.showLevelsPage();
});
}
break;
case 'levels':
this.showLevelsPage();
break;
case 'play':
this.showGamePage(game, content);
break;
case 'settings':
this.showSettingsPage();
break;
default:
this.showHomePage();
}
this.updateBreadcrumb();
},
// Go back
goBack() {
logSh(`⬅️ Going back from: ${this.currentPage}`, 'INFO');
if (this.navigationHistory.length > 1) {
this.navigationHistory.pop(); // Remove current page
const previousPage = this.navigationHistory[this.navigationHistory.length - 1];
logSh(`📍 Going back to: ${previousPage}`, 'DEBUG');
const params = Utils.getUrlParams();
if (previousPage === 'levels') {
this.navigateTo('levels', params.game);
} else if (previousPage === 'games') {
// Récupérer le content depuis les paramètres URL ou retourner aux levels
const urlContent = params.content;
if (urlContent) {
this.navigateTo('games', null, urlContent);
} else {
logSh(`⚠️ Pas de content pour revenir aux jeux, retour aux levels`, 'WARN');
this.navigateTo('levels');
}
} else {
this.navigateTo('home');
}
}
},
// Display home page
showHomePage() {
logSh('🏠 Displaying home page', 'INFO');
this.hideAllPages();
document.getElementById('home-page').classList.add('active');
this.currentPage = 'home';
this.updateBreadcrumb();
},
showSettingsPage() {
logSh('⚙️ Displaying settings page', 'INFO');
this.hideAllPages();
document.getElementById('settings-page').classList.add('active');
this.currentPage = 'settings';
this.updateBreadcrumb();
// Initialize settings if SettingsManager is available
if (window.SettingsManager) {
// Ensure SettingsManager is initialized for this page
setTimeout(() => {
window.SettingsManager.init();
}, 100);
}
},
// Display games selection page
async showGamesPage(contentType) {
logSh(`🎮 Displaying games selection page for content: ${contentType}`, 'INFO');
this.hideAllPages();
document.getElementById('games-page').classList.add('active');
this.currentPage = 'games';
this.selectedContent = contentType;
// Update description first
const contentInfo = this.gamesConfig?.content[contentType];
if (contentInfo) {
document.getElementById('game-description').textContent =
`Select a game to play with "${contentInfo.name}"`;
} else {
document.getElementById('game-description').textContent =
`Select a game to play with this content`;
}
this.updateBreadcrumb();
// Render games grid (async)
await this.renderGamesGrid(contentType);
},
// Display levels selection page (now the first step)
showLevelsPage() {
logSh('📚 Displaying levels selection page', 'INFO');
this.hideAllPages();
document.getElementById('levels-page').classList.add('active');
this.renderLevelsGrid();
this.currentPage = 'levels';
this.updateBreadcrumb();
},
// Display game page
async showGamePage(gameType, contentType) {
this.hideAllPages();
document.getElementById('game-page').classList.add('active');
this.currentPage = 'play';
Utils.showLoading();
try {
await GameLoader.loadGame(gameType, contentType);
this.updateBreadcrumb();
} catch (error) {
logSh('Game loading error:', error, 'ERROR');
Utils.showToast('Error loading game', 'error');
this.goBack();
} finally {
Utils.hideLoading();
}
},
// Hide all pages
hideAllPages() {
document.querySelectorAll('.page').forEach(page => {
page.classList.remove('active');
});
},
// Render games grid
async renderGamesGrid(contentType) {
logSh(`🎲 Generating games grid for content: ${contentType}...`, 'DEBUG');
const grid = document.getElementById('games-grid');
grid.innerHTML = '<div class="loading-content">🔍 Analyzing game compatibility...</div>';
if (!this.gamesConfig) {
logSh('❌ No games configuration available', 'ERROR');
return;
}
// DEBUG: Log détaillé du contenu
logSh(`🔍 DEBUG: Recherche contenu avec ID: "${contentType}"`, 'DEBUG');
logSh(`🔍 DEBUG: Contenu scanné disponible: ${this.scannedContent?.found?.length || 0} items`, 'DEBUG');
if (this.scannedContent?.found) {
this.scannedContent.found.forEach(content => {
logSh(`🔍 DEBUG: Contenu trouvé - ID: "${content.id}", Name: "${content.name}"`, 'DEBUG');
});
}
// Récupérer les informations du contenu
let contentInfo = null;
if (this.scannedContent && this.scannedContent.found) {
// Recherche directe par ID
contentInfo = this.scannedContent.found.find(content => content.id === contentType);
// Si pas trouvé, essayer de chercher par nom de fichier ou nom de module
if (!contentInfo) {
contentInfo = this.scannedContent.found.find(content =>
content.filename.replace('.js', '') === contentType ||
content.filename.replace('.js', '').toLowerCase() === contentType ||
content.id.toLowerCase() === contentType.toLowerCase()
);
}
}
if (!contentInfo) {
logSh(`⚠️ Content info not found for ${contentType}`, 'WARN');
logSh(`🔍 DEBUG: IDs disponibles: ${this.scannedContent?.found?.map(c => c.id).join(', ')}`, 'DEBUG');
logSh(`🔍 DEBUG: Noms de fichiers: ${this.scannedContent?.found?.map(c => c.filename).join(', ')}`, 'DEBUG');
} else {
logSh(`✅ DEBUG: Contenu trouvé: ${contentInfo.name} (ID: ${contentInfo.id})`, 'DEBUG');
}
const enabledGames = Object.entries(this.gamesConfig.games).filter(([key, game]) => game.enabled);
logSh(`🎯 ${enabledGames.length} enabled games found`, 'INFO');
// Clear loading
grid.innerHTML = '';
// Analyser la compatibilité et séparer les jeux
const compatibleGames = [];
const incompatibleGames = [];
enabledGames.forEach(([key, game]) => {
let compatibility = null;
if (contentInfo && this.compatibilityChecker) {
// Récupérer le module JavaScript réel pour le test de compatibilité
const moduleName = this.getModuleName(contentType);
const actualContentModule = window.ContentModules?.[moduleName];
if (actualContentModule) {
compatibility = this.compatibilityChecker.checkCompatibility(actualContentModule, key);
logSh(`🎯 ${game.name} compatibility: ${compatibility.compatible ? '✅' : '❌'} (score: ${compatibility.score}%) - ${compatibility.reason}`, 'DEBUG');
} else {
logSh(`⚠️ Module JavaScript non trouvé: ${moduleName}`, 'WARN');
// Pas de compatibilité = compatible par défaut (comportement de fallback)
compatibility = { compatible: true, score: 50, reason: "Module not loaded - default compatibility" };
}
}
const gameData = { key, game, compatibility };
if (!compatibility || compatibility.compatible) {
compatibleGames.push(gameData);
} else {
incompatibleGames.push(gameData);
}
});
// Afficher d'abord les jeux compatibles
if (compatibleGames.length > 0) {
const compatibleSection = document.createElement('div');
compatibleSection.className = 'compatible-games-section';
if (incompatibleGames.length > 0) {
compatibleSection.innerHTML = '<h3 class="section-title">🎯 Jeux recommandés</h3>';
}
compatibleGames
.sort((a, b) => (b.compatibility?.score || 50) - (a.compatibility?.score || 50))
.forEach(({ key, game, compatibility }) => {
const card = this.createGameCard(key, game, contentType, compatibility);
compatibleSection.appendChild(card);
});
grid.appendChild(compatibleSection);
}
// Puis afficher les jeux incompatibles (avec avertissement)
if (incompatibleGames.length > 0) {
const incompatibleSection = document.createElement('div');
incompatibleSection.className = 'incompatible-games-section';
incompatibleSection.innerHTML = '<h3 class="section-title">⚠️ Jeux avec limitations</h3>';
incompatibleGames.forEach(({ key, game, compatibility }) => {
const card = this.createGameCard(key, game, contentType, compatibility);
incompatibleSection.appendChild(card);
});
grid.appendChild(incompatibleSection);
}
// Message si aucun contenu
if (compatibleGames.length === 0 && incompatibleGames.length === 0) {
grid.innerHTML = '<div class="no-games">Aucun jeu disponible</div>';
}
},
// Create a game card
createGameCard(gameKey, gameInfo, contentType, compatibility = null) {
const card = document.createElement('div');
// Classes CSS selon la compatibilité
let cardClass = 'game-card';
let compatibilityBadge = '';
let compatibilityInfo = '';
let clickable = true;
if (compatibility) {
if (compatibility.compatible) {
cardClass += ' compatible';
if (compatibility.score >= 80) {
compatibilityBadge = '<div class="compatibility-badge excellent">🎯 Excellent</div>';
} else if (compatibility.score >= 60) {
compatibilityBadge = '<div class="compatibility-badge good">✅ Recommandé</div>';
} else {
compatibilityBadge = '<div class="compatibility-badge compatible">👍 Compatible</div>';
}
} else {
cardClass += ' incompatible';
compatibilityBadge = '<div class="compatibility-badge incompatible">⚠️ Limité</div>';
clickable = false; // Désactiver le clic pour les jeux incompatibles
}
// Informations détaillées de compatibilité
compatibilityInfo = `
<div class="compatibility-info">
<div class="compatibility-score">Score: ${compatibility.score}%</div>
<div class="compatibility-reason">${compatibility.reason}</div>
</div>
`;
} else {
// Pas d'analyse de compatibilité
compatibilityBadge = '<div class="compatibility-badge unknown">❓ Non analysé</div>';
}
card.className = cardClass;
card.innerHTML = `
<div class="card-header">
<div class="icon">${gameInfo.icon}</div>
${compatibilityBadge}
</div>
<div class="title">${gameInfo.name}</div>
<div class="description">${gameInfo.description}</div>
${compatibilityInfo}
${!clickable ? '<div class="incompatible-warning">Ce jeu nécessite plus de contenu pour fonctionner correctement</div>' : ''}
`;
if (clickable) {
card.addEventListener('click', () => {
logSh(`🎮 Clicked on game card: ${gameInfo.name} (${gameKey}) with content: ${contentType}`, 'INFO');
Utils.animateElement(card, 'pulse');
this.navigateTo('play', gameKey, contentType);
});
} else {
// Pour les jeux incompatibles, montrer les suggestions d'amélioration
card.addEventListener('click', () => {
this.showCompatibilityHelp(gameKey, gameInfo, compatibility);
});
}
return card;
},
// Afficher l'aide de compatibilité pour les jeux incompatibles
showCompatibilityHelp(gameKey, gameInfo, compatibility) {
if (!this.compatibilityChecker || !compatibility) return;
const modal = document.createElement('div');
modal.className = 'compatibility-help-modal';
// Récupérer les suggestions d'amélioration
const contentInfo = this.scannedContent?.found?.find(content => content.id === this.selectedContent);
const suggestions = contentInfo ?
this.compatibilityChecker.getImprovementSuggestions(contentInfo, gameKey) :
['Enrichir le contenu du module'];
modal.innerHTML = `
<div class="modal-content">
<div class="modal-header">
<h3>🎮 ${gameInfo.name} - Améliorer la compatibilité</h3>
<button class="close-btn">×</button>
</div>
<div class="modal-body">
<p><strong>Raison:</strong> ${compatibility.reason}</p>
<p><strong>Score actuel:</strong> ${compatibility.score}% (minimum requis: ${compatibility.details?.minimumScore || 'N/A'}%)</p>
<h4>💡 Suggestions d'amélioration:</h4>
<ul>
${suggestions.map(suggestion => `<li>${suggestion}</li>`).join('')}
</ul>
<div class="modal-actions">
<button class="try-anyway-btn">Essayer quand même</button>
<button class="close-modal-btn">Fermer</button>
</div>
</div>
</div>
`;
document.body.appendChild(modal);
// Event listeners
modal.querySelector('.close-btn').addEventListener('click', () => modal.remove());
modal.querySelector('.close-modal-btn').addEventListener('click', () => modal.remove());
modal.querySelector('.try-anyway-btn').addEventListener('click', () => {
modal.remove();
logSh(`🎮 Forcing game launch: ${gameInfo.name} despite compatibility issues`, 'WARN');
this.navigateTo('play', gameKey, this.selectedContent);
});
// Fermer avec ESC
const handleEscape = (e) => {
if (e.key === 'Escape') {
modal.remove();
document.removeEventListener('keydown', handleEscape);
}
};
document.addEventListener('keydown', handleEscape);
},
// Render levels grid
async renderLevelsGrid() {
const grid = document.getElementById('levels-grid');
grid.innerHTML = '<div class="loading-content">🔍 Searching for available content...</div>';
try {
// Get all available content automatically
const availableContent = await this.contentScanner.getAvailableContent();
if (availableContent.length === 0) {
grid.innerHTML = '<div class="no-content">No content found</div>';
return;
}
// Clear loading
grid.innerHTML = '';
logSh(`📋 Displaying ${availableContent.length} content modules`, 'INFO');
// Create cards for each found content
availableContent.forEach(content => {
const card = this.createLevelCard(content.id, content);
grid.appendChild(card);
});
} catch (error) {
logSh('Levels render error:', error, 'ERROR');
grid.innerHTML = '<div class="error-content">❌ Error loading content</div>';
}
},
// Convertir contentType vers nom de module JavaScript
getModuleName(contentType) {
const mapping = {
'sbs-level-7-8-new': 'SBSLevel78New',
'basic-chinese': 'BasicChinese',
'english-class-demo': 'EnglishClassDemo',
'chinese-long-story': 'ChineseLongStory',
'chinese-beginner-story': 'ChineseBeginnerStory',
'test-minimal': 'TestMinimal',
'test-rich': 'TestRich'
};
return mapping[contentType] || this.toPascalCase(contentType);
},
// Convertir kebab-case vers PascalCase
toPascalCase(str) {
return str.split('-').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join('');
},
// Create a level card
createLevelCard(contentKey, contentInfo) {
const card = document.createElement('div');
card.className = 'level-card';
// Calculate stats to display
const stats = [];
if (contentInfo.stats) {
if (contentInfo.stats.vocabularyCount > 0) {
stats.push(`📚 ${contentInfo.stats.vocabularyCount} words`);
}
if (contentInfo.stats.sentenceCount > 0) {
stats.push(`💬 ${contentInfo.stats.sentenceCount} sentences`);
}
if (contentInfo.stats.dialogueCount > 0) {
stats.push(`🎭 ${contentInfo.stats.dialogueCount} dialogues`);
}
}
card.innerHTML = `
<div class="card-header">
<div class="icon">${contentInfo.icon}</div>
</div>
<div class="title">${contentInfo.name}</div>
<div class="description">${contentInfo.description}</div>
<div class="content-stats">
<span class="difficulty-badge difficulty-${contentInfo.difficulty}">${contentInfo.difficulty}</span>
<span class="items-count">${contentInfo.metadata.totalItems} items</span>
<span class="time-estimate">~${contentInfo.metadata.estimatedTime}min</span>
</div>
${stats.length > 0 ? `<div class="detailed-stats">${stats.join(' • ')}</div>` : ''}
`;
card.addEventListener('click', () => {
logSh(`📚 Clicked on content card: ${contentInfo.name} (${contentKey})`, 'INFO');
Utils.animateElement(card, 'pulse');
this.navigateTo('games', null, contentKey);
});
return card;
},
// Update breadcrumb
updateBreadcrumb() {
logSh(`🍞 Updating breadcrumb for page: ${this.currentPage}`, 'DEBUG');
const breadcrumb = document.getElementById('breadcrumb');
breadcrumb.innerHTML = '';
const params = Utils.getUrlParams();
// Add back button if not on home
if (this.currentPage !== 'home' && this.navigationHistory.length > 1) {
const backButton = document.createElement('button');
backButton.className = 'back-button';
backButton.innerHTML = '← Back';
backButton.onclick = () => this.goBack();
breadcrumb.appendChild(backButton);
}
// Home
const homeItem = this.createBreadcrumbItem('🏠 Home', 'home',
this.currentPage === 'home');
breadcrumb.appendChild(homeItem);
// Levels
if (['levels', 'games', 'play'].includes(this.currentPage)) {
const levelsItem = this.createBreadcrumbItem('📚 Levels', 'levels',
this.currentPage === 'levels');
breadcrumb.appendChild(levelsItem);
}
// Games (pour un contenu spécifique)
if (['games', 'play'].includes(this.currentPage) && params.content) {
// Récupérer le nom du contenu depuis les résultats du scan
let contentName = params.content;
if (this.scannedContent?.found) {
const foundContent = this.scannedContent.found.find(c => c.id === params.content);
if (foundContent) {
contentName = foundContent.name;
}
}
const gamesItem = this.createBreadcrumbItem(`🎮 ${contentName}`, 'games',
this.currentPage === 'games', params.content);
breadcrumb.appendChild(gamesItem);
}
// Current game
if (this.currentPage === 'play' && params.content) {
const contentInfo = this.gamesConfig?.content[params.content];
const playText = contentInfo ? `🎯 ${contentInfo.name}` : 'Game';
const playItem = this.createBreadcrumbItem(playText, 'play', true);
breadcrumb.appendChild(playItem);
}
},
// Create a breadcrumb element
createBreadcrumbItem(text, page, isActive, content = null) {
const item = document.createElement('button');
item.className = `breadcrumb-item ${isActive ? 'active' : ''}`;
item.textContent = text;
item.dataset.page = page;
if (!isActive) {
item.addEventListener('click', () => {
logSh(`🍞 Clicked on breadcrumb: ${text}${page}`, 'INFO');
if (page === 'home') {
this.navigateTo('home');
} else if (page === 'games' && content) {
this.navigateTo('games', null, content);
} else if (page === 'levels') {
this.navigateTo('levels');
}
});
}
return item;
}
};
// Global functions for HTML
window.navigateTo = (page, game, content) => AppNavigation.navigateTo(page, game, content);
window.goBack = () => AppNavigation.goBack();
// Export
window.AppNavigation = AppNavigation;