initial commit
This commit is contained in:
223
static/js/modules/question-navigator.js
Normal file
223
static/js/modules/question-navigator.js
Normal file
@@ -0,0 +1,223 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user