224 lines
6.9 KiB
JavaScript
224 lines
6.9 KiB
JavaScript
// Question Navigator Module
|
|
class QuestionNavigator {
|
|
constructor(containerId, options = {}) {
|
|
this.containerId = containerId;
|
|
this.questions = [];
|
|
this.currentIndex = 0;
|
|
this.visitedQuestions = new Set();
|
|
this.onQuestionSelect = options.onQuestionSelect || (() => {});
|
|
this.storagePrefix = options.storagePrefix || 'navigator';
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.setupEventListeners();
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Jump button
|
|
const jumpBtn = document.getElementById('jump-btn');
|
|
if (jumpBtn) {
|
|
jumpBtn.addEventListener('click', () => this.jumpToQuestion());
|
|
}
|
|
|
|
// Jump input
|
|
const jumpInput = document.getElementById('jump-to-question');
|
|
if (jumpInput) {
|
|
jumpInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') {
|
|
this.jumpToQuestion();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Reset button
|
|
const resetBtn = document.getElementById('reset-visited-btn');
|
|
if (resetBtn) {
|
|
resetBtn.addEventListener('click', () => this.resetVisitedQuestions());
|
|
}
|
|
}
|
|
|
|
setQuestions(questions) {
|
|
this.questions = questions;
|
|
this.createNavigator();
|
|
this.loadVisitedFromStorage();
|
|
this.updateNavigator();
|
|
}
|
|
|
|
createNavigator() {
|
|
const container = document.getElementById(this.containerId);
|
|
if (!container) return;
|
|
|
|
container.innerHTML = '';
|
|
|
|
this.questions.forEach((question, index) => {
|
|
const button = document.createElement('button');
|
|
button.className = 'question-nav-btn';
|
|
button.textContent = question.id;
|
|
button.setAttribute('data-index', index);
|
|
button.title = `Pregunta ${question.id}`;
|
|
|
|
button.addEventListener('click', () => {
|
|
this.selectQuestion(index);
|
|
});
|
|
|
|
container.appendChild(button);
|
|
});
|
|
}
|
|
|
|
selectQuestion(index) {
|
|
if (index >= 0 && index < this.questions.length) {
|
|
this.currentIndex = index;
|
|
this.visitedQuestions.add(index);
|
|
this.saveVisitedToStorage();
|
|
this.updateNavigator();
|
|
this.onQuestionSelect(index);
|
|
}
|
|
}
|
|
|
|
updateNavigator() {
|
|
const buttons = document.querySelectorAll(`#${this.containerId} .question-nav-btn`);
|
|
|
|
buttons.forEach((button, index) => {
|
|
button.classList.remove('current', 'visited');
|
|
|
|
if (index === this.currentIndex) {
|
|
button.classList.add('current');
|
|
this.visitedQuestions.add(index);
|
|
this.saveVisitedToStorage();
|
|
// Scroll to current button
|
|
button.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'nearest',
|
|
inline: 'center'
|
|
});
|
|
} else if (this.visitedQuestions.has(index)) {
|
|
button.classList.add('visited');
|
|
}
|
|
});
|
|
|
|
this.updateProgress();
|
|
this.updateJumpInputMax();
|
|
}
|
|
|
|
updateProgress() {
|
|
const progressEl = document.getElementById('navigator-progress');
|
|
if (progressEl) {
|
|
progressEl.textContent = `${this.visitedQuestions.size} de ${this.questions.length} visitadas`;
|
|
}
|
|
}
|
|
|
|
updateJumpInputMax() {
|
|
const jumpInput = document.getElementById('jump-to-question');
|
|
if (jumpInput && this.questions.length > 0) {
|
|
jumpInput.max = this.questions[this.questions.length - 1].id;
|
|
}
|
|
}
|
|
|
|
jumpToQuestion() {
|
|
const jumpInput = document.getElementById('jump-to-question');
|
|
if (!jumpInput) return;
|
|
|
|
const questionId = parseInt(jumpInput.value);
|
|
|
|
if (!questionId || questionId < 1) {
|
|
this.showNotification('Por favor ingresa un número de pregunta válido', 'warning');
|
|
return;
|
|
}
|
|
|
|
const questionIndex = this.questions.findIndex(q => q.id === questionId);
|
|
|
|
if (questionIndex === -1) {
|
|
this.showNotification(`La pregunta ${questionId} no está en el rango actual`, 'warning');
|
|
return;
|
|
}
|
|
|
|
this.selectQuestion(questionIndex);
|
|
jumpInput.value = '';
|
|
|
|
this.playSound('click');
|
|
this.showNotification(`Navegando a pregunta ${questionId}`, 'success', 2000);
|
|
}
|
|
|
|
resetVisitedQuestions() {
|
|
if (this.visitedQuestions.size === 0) {
|
|
this.showNotification('No hay preguntas visitadas para limpiar', 'info', 3000);
|
|
return;
|
|
}
|
|
|
|
const confirmReset = confirm(
|
|
`¿Estás seguro de que quieres limpiar el progreso de ${this.visitedQuestions.size} preguntas visitadas?`
|
|
);
|
|
|
|
if (confirmReset) {
|
|
// Clear visited questions but keep current question
|
|
this.visitedQuestions.clear();
|
|
this.visitedQuestions.add(this.currentIndex);
|
|
|
|
this.saveVisitedToStorage();
|
|
this.updateNavigator();
|
|
|
|
this.playSound('complete');
|
|
this.showNotification('Progreso de preguntas visitadas limpiado', 'success', 3000);
|
|
|
|
// Animation for reset button
|
|
const resetBtn = document.getElementById('reset-visited-btn');
|
|
if (resetBtn) {
|
|
resetBtn.style.transform = 'scale(0.9)';
|
|
setTimeout(() => {
|
|
resetBtn.style.transform = 'scale(1)';
|
|
}, 150);
|
|
}
|
|
}
|
|
}
|
|
|
|
saveVisitedToStorage() {
|
|
try {
|
|
const storageKey = `${this.storagePrefix}_visited`;
|
|
localStorage.setItem(storageKey, JSON.stringify(Array.from(this.visitedQuestions)));
|
|
} catch (error) {
|
|
console.warn('Could not save visited questions to localStorage:', error);
|
|
}
|
|
}
|
|
|
|
loadVisitedFromStorage() {
|
|
try {
|
|
const storageKey = `${this.storagePrefix}_visited`;
|
|
const saved = localStorage.getItem(storageKey);
|
|
if (saved) {
|
|
const visitedArray = JSON.parse(saved);
|
|
this.visitedQuestions = new Set(visitedArray);
|
|
} else {
|
|
this.visitedQuestions = new Set();
|
|
}
|
|
} catch (error) {
|
|
console.warn('Could not load visited questions from localStorage:', error);
|
|
this.visitedQuestions = new Set();
|
|
}
|
|
}
|
|
|
|
setCurrentIndex(index) {
|
|
this.currentIndex = index;
|
|
this.updateNavigator();
|
|
}
|
|
|
|
// Utility methods
|
|
showNotification(message, type = 'info', duration = 3000) {
|
|
if (window.Utils) {
|
|
Utils.showNotification(message, type, duration);
|
|
} else {
|
|
alert(message);
|
|
}
|
|
}
|
|
|
|
playSound(type) {
|
|
if (window.Utils) {
|
|
Utils.playSound(type);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Export for global use
|
|
window.QuestionNavigator = QuestionNavigator;
|