Drew4564's picture
add a red border on exam page like when taking an exam like a square where the exam is inside and the exam should be one question then move tio other not alow of numbers in one page every answer next page
a64558d verified
// App State
const state = {
isLoggedIn: false,
currentStudent: null,
exams: [
{
id: 1,
subject: "Mathematics",
date: "2023-06-15",
duration: 30, // minutes
questions: [
{
id: 1,
type: "multiple",
text: "What is the derivative of x²?",
options: ["x", "2x", "x²", "2x²"],
answer: 1
},
{
id: 2,
type: "multiple",
text: "What is the value of π (pi) to two decimal places?",
options: ["3.14", "3.16", "3.12", "3.18"],
answer: 0
},
{
id: 3,
type: "short",
text: "Solve for x: 2x + 5 = 15",
answer: "5"
},
{
id: 4,
type: "multiple",
text: "Which of these is a prime number?",
options: ["4", "9", "11", "15"],
answer: 2
},
{
id: 5,
type: "short",
text: "What is the area of a circle with radius 3? (Use π ≈ 3.14)",
answer: "28.26"
}
]
},
{
id: 2,
subject: "Physics",
date: "2023-06-20",
duration: 45,
questions: [
{
id: 1,
type: "multiple",
text: "What is the SI unit of force?",
options: ["Joule", "Watt", "Newton", "Pascal"],
answer: 2
},
{
id: 2,
type: "short",
text: "What is the acceleration due to gravity on Earth (in m/s²)?",
answer: "9.8"
}
]
}
],
currentExam: null,
examStartTime: null,
examTimer: null,
suspiciousActivities: 0,
submissions: []
};
// DOM Elements
const loginScreen = document.getElementById('login-screen');
const appScreens = document.getElementById('app-screens');
const loginForm = document.getElementById('login-form');
const forgotPasswordBtn = document.getElementById('forgot-password');
const forgotModal = document.getElementById('forgot-modal');
const resetConfirmation = document.getElementById('reset-confirmation');
const studentName = document.getElementById('student-name');
const studentAvatar = document.getElementById('student-avatar');
const statusBadge = document.getElementById('status-badge');
const examList = document.getElementById('exam-list');
const examScreen = document.getElementById('exam-screen');
const examQuestions = document.getElementById('exam-questions');
const examTitle = document.getElementById('exam-title');
const examTimer = document.getElementById('exam-timer');
const examProgress = document.getElementById('exam-progress');
const submitExamBtn = document.getElementById('submit-exam');
const submissionList = document.getElementById('submission-list');
const logoutBtn = document.getElementById('logout-btn');
const navButtons = document.querySelectorAll('.nav-btn');
// Initialize the app
function init() {
checkAuth();
setupEventListeners();
renderExamList();
loadSubmissions();
}
// Check if user is logged in
function checkAuth() {
const student = localStorage.getItem('examGuardStudent');
if (student) {
state.isLoggedIn = true;
state.currentStudent = JSON.parse(student);
showApp();
} else {
showLogin();
}
}
// Show login screen
function showLogin() {
loginScreen.classList.remove('hidden');
appScreens.classList.add('hidden');
state.isLoggedIn = false;
}
// Show main app
function showApp() {
loginScreen.classList.add('hidden');
appScreens.classList.remove('hidden');
// Update student info
studentName.textContent = state.currentStudent?.name || 'Student';
studentAvatar.src = `https://static.photos/people/120x120/${state.currentStudent?.id || 1}`;
// Show dashboard by default
showScreen('dashboard');
updateStatusBadge();
}
// Show a specific screen
function showScreen(screenId) {
// Hide all screens
document.querySelectorAll('[id$="-screen"]').forEach(screen => {
screen.classList.add('hidden');
});
// Show requested screen
document.getElementById(`${screenId}-screen`)?.classList.remove('hidden');
// Update active nav button
navButtons.forEach(btn => {
if (btn.dataset.screen === screenId) {
btn.classList.add('text-indigo-600');
btn.classList.remove('text-gray-500');
} else {
btn.classList.remove('text-indigo-600');
btn.classList.add('text-gray-500');
}
});
}
// Update status badge
function updateStatusBadge() {
const pendingExams = state.exams.filter(exam =>
!state.submissions.some(sub => sub.examId === exam.id)
);
if (pendingExams.length > 0) {
statusBadge.textContent = `${pendingExams.length} pending exam${pendingExams.length > 1 ? 's' : ''}`;
statusBadge.className = 'text-xs px-2 py-1 bg-amber-100 text-amber-800 rounded-full';
} else {
statusBadge.textContent = 'No active exams';
statusBadge.className = 'text-xs px-2 py-1 bg-emerald-100 text-emerald-800 rounded-full';
}
}
// Render exam list
function renderExamList() {
examList.innerHTML = '';
state.exams.forEach(exam => {
const isSubmitted = state.submissions.some(sub => sub.examId === exam.id);
const examCard = document.createElement('div');
examCard.className = 'bg-white rounded-xl shadow-sm p-4';
examCard.innerHTML = `
<div class="flex justify-between items-start">
<div>
<h3 class="font-medium text-gray-800">${exam.subject}</h3>
<p class="text-sm text-gray-500">${new Date(exam.date).toLocaleDateString()}${exam.duration} min</p>
</div>
${isSubmitted ?
'<span class="text-xs px-2 py-1 bg-green-100 text-green-800 rounded-full">Submitted</span>' :
`<button data-exam-id="${exam.id}" class="take-exam-btn text-sm bg-indigo-600 text-white px-3 py-1 rounded-lg hover:bg-indigo-700">
Take Exam
</button>`
}
</div>
`;
examList.appendChild(examCard);
});
// Add event listeners to take exam buttons
document.querySelectorAll('.take-exam-btn').forEach(btn => {
btn.addEventListener('click', () => {
const examId = parseInt(btn.dataset.examId);
startExam(examId);
});
});
}
// Start an exam
function startExam(examId) {
const exam = state.exams.find(e => e.id === examId);
if (!exam) return;
state.currentExam = exam;
state.currentQuestionIndex = 0;
state.examStartTime = new Date();
state.suspiciousActivities = 0;
// Setup exam UI
examTitle.textContent = exam.subject;
examQuestions.innerHTML = '';
// Render first question
renderQuestion(0);
const questionCard = document.createElement('div');
questionCard.className = 'question-card bg-white rounded-xl shadow-sm p-4';
if (q.type === 'multiple') {
questionCard.innerHTML = `
<h4 class="font-medium mb-3">${index + 1}. ${q.text}</h4>
<div class="space-y-2">
${q.options.map((opt, i) => `
<label class="flex items-center space-x-3 cursor-pointer">
<input type="radio" name="q${q.id}" value="${i}" class="w-4 h-4 text-indigo-600">
<span>${opt}</span>
</label>
`).join('')}
</div>
`;
} else {
questionCard.innerHTML = `
<h4 class="font-medium mb-3">${index + 1}. ${q.text}</h4>
<input type="text" name="q${q.id}"
class="w-full px-4 py-2 border border-gray-300 rounded-lg">
`;
}
// Add event listeners for navigation
document.getElementById('prev-question').addEventListener('click', () => {
if (state.currentQuestionIndex > 0) {
state.currentQuestionIndex--;
renderQuestion(state.currentQuestionIndex);
}
});
document.getElementById('next-question').addEventListener('click', () => {
if (state.currentQuestionIndex < state.currentExam.questions.length - 1) {
state.currentQuestionIndex++;
renderQuestion(state.currentQuestionIndex);
} else {
submitExam();
}
});
// Render a single question
function renderQuestion(index) {
const question = state.currentExam.questions[index];
examQuestions.innerHTML = '';
const questionCard = document.createElement('div');
questionCard.className = 'question-card';
if (question.type === 'multiple') {
questionCard.innerHTML = `
<h4 class="font-medium mb-3">${index + 1}. ${question.text}</h4>
<div class="space-y-2">
${question.options.map((opt, i) => `
<label class="flex items-center space-x-3 cursor-pointer">
<input type="radio" name="q${question.id}" value="${i}" class="w-4 h-4 text-indigo-600">
<span>${opt}</span>
</label>
`).join('')}
</div>
`;
} else {
questionCard.innerHTML = `
<h4 class="font-medium mb-3">${index + 1}. ${question.text}</h4>
<input type="text" name="q${question.id}"
class="w-full px-4 py-2 border border-gray-300 rounded-lg">
`;
}
examQuestions.appendChild(questionCard);
// Update navigation buttons
const prevBtn = document.getElementById('prev-question');
const nextBtn = document.getElementById('next-question');
prevBtn.classList.toggle('hidden', index === 0);
nextBtn.textContent = index === state.currentExam.questions.length - 1 ? 'Submit Exam' : 'Next Question';
}
// Start timer
const durationInSeconds = exam.duration * 60;
let remainingTime = durationInSeconds;
updateTimerDisplay(remainingTime);
state.examTimer = setInterval(() => {
remainingTime--;
updateTimerDisplay(remainingTime);
if (remainingTime <= 0) {
clearInterval(state.examTimer);
submitExam();
}
}, 1000);
// Setup anti-cheat
setupAntiCheat();
// Show exam screen
showScreen('exam');
}
// Update timer display
function updateTimerDisplay(seconds) {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
examTimer.textContent = `${mins}:${secs < 10 ? '0' + secs : secs}`;
if (seconds < 60) {
examTimer.classList.add('timer-urgent');
} else {
examTimer.classList.remove('timer-urgent');
}
// Update progress bar
const examDuration = state.currentExam.duration * 60;
const percent = (seconds / examDuration) * 100;
examProgress.style.width = `${percent}%`;
}
// Setup anti-cheat measures
function setupAntiCheat() {
// Add exam layout class
document.getElementById('app').classList.add('exam-layout');
// Detect tab switching
document.addEventListener('visibilitychange', () => {
if (document.visibilityState !== 'visible') {
handleSuspiciousActivity('Tab switched');
}
});
// Disable right click
document.addEventListener('contextmenu', (e) => {
e.preventDefault();
handleSuspiciousActivity('Right click attempt');
});
// Disable text selection
document.addEventListener('selectstart', (e) => {
e.preventDefault();
handleSuspiciousActivity('Text selection attempt');
});
// Disable copy/paste
document.addEventListener('copy', (e) => {
e.preventDefault();
handleSuspiciousActivity('Copy attempt');
});
document.addEventListener('paste', (e) => {
e.preventDefault();
handleSuspiciousActivity('Paste attempt');
});
// Try to access camera
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
// Camera access granted
console.log('Camera access granted for monitoring');
})
.catch(err => {
console.log('Camera access denied or not available');
});
}
}
// Handle suspicious activity
function handleSuspiciousActivity(reason) {
state.suspiciousActivities++;
// Show warning
const warning = document.createElement('div');
warning.className = 'warning-flash fixed top-4 left-1/2 transform -translate-x-1/2 bg-white shadow-md rounded-lg px-4 py-2 z-50';
warning.textContent = `⚠️ Suspicious activity detected: ${reason}`;
document.body.appendChild(warning);
setTimeout(() => {
warning.remove();
}, 3000);
}
// Submit exam
function submitExam() {
// Remove exam layout class
document.getElementById('app').classList.remove('exam-layout');
if (!state.currentExam) return;
clearInterval(state.examTimer);
// Create submission
const submission = {
examId: state.currentExam.id,
subject: state.currentExam.subject,
submittedAt: new Date().toISOString(),
suspiciousCount: state.suspiciousActivities
};
// Save to state and localStorage
state.submissions.push(submission);
localStorage.setItem('examGuardSubmissions', JSON.stringify(state.submissions));
// Update UI
updateStatusBadge();
renderExamList();
loadSubmissions();
// Show pending screen
showScreen('pending');
}
// Load submissions from localStorage
function loadSubmissions() {
const savedSubmissions = localStorage.getItem('examGuardSubmissions');
if (savedSubmissions) {
state.submissions = JSON.parse(savedSubmissions);
}
renderSubmissions();
}
// Render submissions list
function renderSubmissions() {
submissionList.innerHTML = '';
if (state.submissions.length === 0) {
submissionList.innerHTML = '<p class="text-gray-500 text-center py-4">No submissions yet</p>';
return;
}
state.submissions.forEach(sub => {
const subCard = document.createElement('div');
subCard.className = 'bg-white rounded-xl shadow-sm p-4';
subCard.innerHTML = `
<div class="flex justify-between items-start">
<div>
<h3 class="font-medium text-gray-800">${sub.subject}</h3>
<p class="text-sm text-gray-500">Submitted: ${new Date(sub.submittedAt).toLocaleString()}</p>
</div>
<div class="text-right">
<span class="block text-xs px-2 py-1 bg-blue-100 text-blue-800 rounded-full mb-1">Pending Review</span>
${sub.suspiciousCount > 0 ?
`<span class="text-xs px-2 py-1 bg-red-100 text-red-800 rounded-full">
${sub.suspiciousCount} suspicious event${sub.suspiciousCount > 1 ? 's' : ''}
</span>` :
`<span class="text-xs px-2 py-1 bg-green-100 text-green-800 rounded-full">
No issues detected
</span>`
}
</div>
</div>
`;
submissionList.appendChild(subCard);
});
}
// Setup event listeners
function setupEventListeners() {
// Login form
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
const studentId = document.getElementById('student-id').value;
const password = document.getElementById('password').value;
// Mock authentication
state.currentStudent = {
id: studentId,
name: `Student ${studentId}`,
email: `student${studentId}@example.com`
};
// Save to localStorage
localStorage.setItem('examGuardStudent', JSON.stringify(state.currentStudent));
// Show app
showApp();
});
// Forgot password
forgotPasswordBtn.addEventListener('click', () => {
forgotModal.classList.remove('hidden');
});
// Cancel reset
document.getElementById('cancel-reset').addEventListener('click', () => {
forgotModal.classList.add('hidden');
});
// Submit reset
document.getElementById('submit-reset').addEventListener('click', () => {
const email = document.getElementById('reset-email').value;
if (email) {
forgotModal.classList.add('hidden');
resetConfirmation.classList.remove('hidden');
}
});
// Close confirmation
document.getElementById('close-confirmation').addEventListener('click', () => {
resetConfirmation.classList.add('hidden');
});
// Navigation buttons
navButtons.forEach(btn => {
btn.addEventListener('click', () => {
showScreen(btn.dataset.screen);
});
});
// Submit exam button
submitExamBtn.addEventListener('click', submitExam);
// Logout button
logoutBtn.addEventListener('click', () => {
localStorage.removeItem('examGuardStudent');
showLogin();
});
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', init);