Files
CK-X/app/public/js/components/timer-service.js
2025-04-02 10:39:59 +05:30

218 lines
6.3 KiB
JavaScript

/**
* Timer Service
* Handles timer functionality for the exam
*/
let timerDuration = 120; // Default timer duration in minutes
let timerInterval = null;
let timerElement = null;
let onTimerEndCallback = null;
let isTimerRunning = false;
const timeThresholds = [30, 10, 0]; // Time thresholds in minutes
let timeThresholdCallbacks = {}; // Callbacks for time thresholds
let processedThresholds = new Set(); // Track which thresholds have been processed
/**
* Set timer duration
* @param {number} duration - Duration in minutes
*/
function setTimerDuration(duration) {
timerDuration = duration;
// Reset processed thresholds when timer duration changes
processedThresholds.clear();
}
/**
* Register callbacks for specific time thresholds
* @param {Object} callbacks - Object with threshold minutes as keys and callback functions as values
*/
function registerTimeThresholdCallbacks(callbacks) {
timeThresholdCallbacks = { ...timeThresholdCallbacks, ...callbacks };
}
/**
* Get remaining time
* @returns {number} Remaining time in minutes
*/
function getRemainingTime() {
return timerDuration;
}
/**
* Calculate remaining exam time based on exam info
* @param {Object} examInfo - The exam information object
* @returns {number} Remaining time in minutes
*/
function calculateRemainingTime(examInfo) {
// If exam hasn't started yet or no duration set, return default
if (!examInfo.info?.examDurationInMinutes) {
return getRemainingTime();
}
// Check if exam has ended
if (examInfo.info?.events?.examEndTime) {
return 0; // No time remaining if exam is finished
}
// If exam has started, calculate remaining time
if (examInfo.info?.events?.examStartTime) {
// Make sure we have a proper number value for startTime
let startTime = examInfo.info.events.examStartTime;
// Parse the timestamp to ensure it's a number (could be stored as string)
if (typeof startTime === 'string') {
startTime = parseInt(startTime, 10);
}
// Debug log to identify the issue
console.log('Start time (epoch):', startTime);
console.log('Current time (epoch):', Date.now());
console.log('Exam duration (minutes):', examInfo.info.examDurationInMinutes);
const durationMs = examInfo.info.examDurationInMinutes * 60 * 1000;
const elapsedMs = Date.now() - startTime;
const remainingMs = Math.max(0, durationMs - elapsedMs);
// Debug log for troubleshooting
console.log('Duration (ms):', durationMs);
console.log('Elapsed (ms):', elapsedMs);
console.log('Remaining (ms):', remainingMs);
console.log('Remaining (minutes):', Math.ceil(remainingMs / (60 * 1000)));
// Convert ms to minutes
return Math.ceil(remainingMs / (60 * 1000));
}
// Default: return full duration if exam hasn't started
return examInfo.info.examDurationInMinutes;
}
/**
* Initialize the timer
* @param {HTMLElement} element - DOM element to display the timer
* @param {number} minutes - Timer duration in minutes
* @param {Object} options - Optional configuration
* @param {Function} options.onTimerEnd - Callback function when timer ends
*/
function initTimer(element, minutes, options = {}) {
timerElement = element;
timerDuration = minutes;
onTimerEndCallback = options.onTimerEnd;
// Reset processed thresholds when initializing new timer
processedThresholds.clear();
updateTimerDisplay(minutes);
}
/**
* Check if a time threshold has been reached and trigger callback
* @param {number} remainingMinutes - Remaining time in minutes
* @param {number} remainingSeconds - Remaining time in seconds
*/
function checkTimeThresholds(remainingMinutes, remainingSeconds) {
// Check each threshold
timeThresholds.forEach(threshold => {
// If we just crossed this threshold and haven't processed it yet
if (remainingMinutes === threshold &&
remainingSeconds === 0 &&
!processedThresholds.has(threshold)) {
// Mark as processed
processedThresholds.add(threshold);
// Call the registered callback if exists
if (timeThresholdCallbacks[threshold]) {
timeThresholdCallbacks[threshold](threshold);
}
}
});
}
/**
* Start the timer
*/
function startTimer() {
if (isTimerRunning) return;
isTimerRunning = true;
let remainingSeconds = timerDuration * 60;
timerInterval = setInterval(() => {
remainingSeconds--;
if (remainingSeconds <= 0) {
stopTimer();
updateTimerDisplay(0);
if (onTimerEndCallback) {
onTimerEndCallback();
}
return;
}
const minutes = Math.floor(remainingSeconds / 60);
const seconds = remainingSeconds % 60;
// Check if we've hit any time thresholds
checkTimeThresholds(minutes, seconds);
updateTimerDisplay(minutes, seconds);
}, 1000);
}
/**
* Stop the timer
*/
function stopTimer() {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = null;
isTimerRunning = false;
}
}
/**
* Reset the timer
*/
function resetTimer() {
stopTimer();
timerDuration = 120; // Reset to default
processedThresholds.clear();
if (timerElement) {
updateTimerDisplay(timerDuration);
}
}
/**
* Update the timer display
* @param {number} minutes - Minutes remaining
* @param {number} seconds - Seconds remaining
*/
function updateTimerDisplay(minutes, seconds = 0) {
if (!timerElement) return;
const formattedMinutes = String(minutes).padStart(2, '0');
const formattedSeconds = String(seconds).padStart(2, '0');
timerElement.textContent = `${formattedMinutes}:${formattedSeconds}`;
// Add visual warning when less than 5 minutes remaining
if (minutes < 5) {
timerElement.classList.add('timer-warning');
} else {
timerElement.classList.remove('timer-warning');
}
}
// Export the timer service
export {
setTimerDuration,
getRemainingTime,
calculateRemainingTime,
initTimer,
startTimer,
stopTimer,
resetTimer,
registerTimeThresholdCallbacks
};