Skip to content

Commit daa415a

Browse files
committed
Add exam styles and HTML structure for Intelligent Systems Finals exam
- Created exam-styles.css for styling the exam interface, including layout, buttons, and responsive design. - Developed exam.html to structure the exam page, including sections for the exam screen and results screen. - Implemented navigation and action buttons for user interaction. - Added detailed results display for post-exam analysis.
1 parent 0e5a826 commit daa415a

7 files changed

Lines changed: 2537 additions & 0 deletions

File tree

File renamed without changes.
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
(() => {
2+
// Exam State
3+
const examState = {
4+
userAnswers: {}, // { questionId: selectedOptionIndex }
5+
shuffledQuestions: [],
6+
isSubmitted: false
7+
};
8+
9+
// Initialize exam on page load
10+
function initExam() {
11+
// Shuffle only the options for each question, not the questions themselves
12+
examState.shuffledQuestions = examQuestions.map(q => {
13+
// Create array of option indices
14+
const optionIndices = q.options.map((_, index) => index);
15+
const shuffledIndices = shuffleArray(optionIndices);
16+
17+
// Create shuffled options with mapping to original index
18+
const shuffledOptions = shuffledIndices.map(originalIndex => ({
19+
text: q.options[originalIndex],
20+
originalIndex: originalIndex
21+
}));
22+
23+
return {
24+
...q,
25+
shuffledOptions: shuffledOptions,
26+
// Track which shuffled index corresponds to the correct answer
27+
correctShuffledIndex: shuffledIndices.indexOf(q.correct)
28+
};
29+
});
30+
31+
renderQuestions();
32+
attachEventListeners();
33+
}
34+
35+
// Render all questions
36+
function renderQuestions() {
37+
const container = document.getElementById('questionsContainer');
38+
if (!container) return;
39+
40+
container.innerHTML = '';
41+
42+
examState.shuffledQuestions.forEach((q, index) => {
43+
const questionCard = document.createElement('div');
44+
questionCard.className = 'question-card';
45+
questionCard.id = `question-${q.id}`;
46+
47+
const questionHeader = document.createElement('div');
48+
questionHeader.className = 'question-header';
49+
questionHeader.innerHTML = `
50+
<span class="question-number">Question ${index + 1} of ${examState.shuffledQuestions.length}</span>
51+
<span class="question-topic">${q.lesson} - ${q.topic}</span>
52+
`;
53+
54+
const questionText = document.createElement('div');
55+
questionText.className = 'question-text';
56+
questionText.textContent = q.question;
57+
58+
const optionsContainer = document.createElement('div');
59+
optionsContainer.className = 'options-container';
60+
61+
q.shuffledOptions.forEach((option, optionIndex) => {
62+
const optionDiv = document.createElement('div');
63+
optionDiv.className = 'option-item';
64+
65+
const input = document.createElement('input');
66+
input.type = 'radio';
67+
input.name = `question-${q.id}`;
68+
input.id = `q${q.id}-opt${optionIndex}`;
69+
input.value = optionIndex;
70+
input.dataset.questionId = q.id;
71+
72+
// Check if this option was previously selected
73+
if (examState.userAnswers[q.id] === optionIndex) {
74+
input.checked = true;
75+
}
76+
77+
const label = document.createElement('label');
78+
label.htmlFor = `q${q.id}-opt${optionIndex}`;
79+
label.textContent = option.text;
80+
81+
optionDiv.appendChild(input);
82+
optionDiv.appendChild(label);
83+
optionsContainer.appendChild(optionDiv);
84+
});
85+
86+
questionCard.appendChild(questionHeader);
87+
questionCard.appendChild(questionText);
88+
questionCard.appendChild(optionsContainer);
89+
container.appendChild(questionCard);
90+
});
91+
92+
updateProgress();
93+
}
94+
95+
// Attach event listeners
96+
function attachEventListeners() {
97+
// Listen for answer selections
98+
const container = document.getElementById('questionsContainer');
99+
if (container) {
100+
container.addEventListener('change', (e) => {
101+
if (e.target.type === 'radio') {
102+
const questionId = parseInt(e.target.dataset.questionId);
103+
const selectedOption = parseInt(e.target.value);
104+
examState.userAnswers[questionId] = selectedOption;
105+
updateProgress();
106+
107+
// Scroll smoothly to next question
108+
const currentQuestion = document.getElementById(`question-${questionId}`);
109+
const nextQuestion = currentQuestion?.nextElementSibling;
110+
if (nextQuestion) {
111+
setTimeout(() => {
112+
nextQuestion.scrollIntoView({ behavior: 'smooth', block: 'start' });
113+
}, 200);
114+
}
115+
}
116+
});
117+
}
118+
119+
// Submit buttons
120+
const submitBtn = document.getElementById('submitExamBtn');
121+
const submitBtn2 = document.getElementById('submitExamBtn2');
122+
if (submitBtn) submitBtn.addEventListener('click', handleSubmit);
123+
if (submitBtn2) submitBtn2.addEventListener('click', handleSubmit);
124+
125+
// Reset button
126+
const resetBtn = document.getElementById('resetBtn');
127+
if (resetBtn) resetBtn.addEventListener('click', handleReset);
128+
129+
// Scroll to top button
130+
const scrollTopBtn = document.getElementById('scrollTopBtn');
131+
if (scrollTopBtn) {
132+
scrollTopBtn.addEventListener('click', () => {
133+
window.scrollTo({ top: 0, behavior: 'smooth' });
134+
});
135+
}
136+
137+
// Retake button
138+
const retakeBtn = document.getElementById('retakeBtn');
139+
if (retakeBtn) retakeBtn.addEventListener('click', handleRetake);
140+
}
141+
142+
// Update progress display
143+
function updateProgress() {
144+
const answeredCount = Object.keys(examState.userAnswers).length;
145+
const totalCount = examState.shuffledQuestions.length;
146+
const progressText = document.getElementById('progressText');
147+
148+
if (progressText) {
149+
progressText.textContent = `${answeredCount} / ${totalCount} answered`;
150+
}
151+
}
152+
153+
// Handle exam submission
154+
function handleSubmit() {
155+
const answeredCount = Object.keys(examState.userAnswers).length;
156+
const totalCount = examState.shuffledQuestions.length;
157+
158+
if (answeredCount < totalCount) {
159+
const unanswered = totalCount - answeredCount;
160+
const confirmSubmit = confirm(
161+
`You have ${unanswered} unanswered question(s). Are you sure you want to submit?`
162+
);
163+
if (!confirmSubmit) return;
164+
}
165+
166+
examState.isSubmitted = true;
167+
calculateAndShowResults();
168+
}
169+
170+
// Calculate results and show results screen
171+
function calculateAndShowResults() {
172+
let correctCount = 0;
173+
let wrongCount = 0;
174+
let unansweredCount = 0;
175+
176+
const results = examState.shuffledQuestions.map(q => {
177+
const userAnswer = examState.userAnswers[q.id];
178+
const isCorrect = userAnswer === q.correctShuffledIndex;
179+
const isAnswered = userAnswer !== undefined;
180+
181+
if (isAnswered) {
182+
if (isCorrect) correctCount++;
183+
else wrongCount++;
184+
} else {
185+
unansweredCount++;
186+
}
187+
188+
return {
189+
question: q,
190+
userAnswer: userAnswer,
191+
isCorrect: isCorrect,
192+
isAnswered: isAnswered
193+
};
194+
});
195+
196+
// Update results display
197+
document.getElementById('scoreValue').textContent = correctCount;
198+
document.getElementById('correctCount').textContent = correctCount;
199+
document.getElementById('wrongCount').textContent = wrongCount;
200+
document.getElementById('unansweredCount').textContent = unansweredCount;
201+
202+
const percentage = ((correctCount / examState.shuffledQuestions.length) * 100).toFixed(1);
203+
document.getElementById('scorePercentage').textContent = `${percentage}%`;
204+
205+
// Add color class based on percentage
206+
const scoreCircle = document.querySelector('.score-circle');
207+
if (scoreCircle) {
208+
scoreCircle.className = 'score-circle';
209+
if (percentage >= 90) scoreCircle.classList.add('excellent');
210+
else if (percentage >= 75) scoreCircle.classList.add('good');
211+
else if (percentage >= 60) scoreCircle.classList.add('fair');
212+
else scoreCircle.classList.add('poor');
213+
}
214+
215+
// Render detailed results
216+
renderDetailedResults(results);
217+
218+
// Switch screens
219+
document.getElementById('examScreen').hidden = true;
220+
document.getElementById('resultsScreen').hidden = false;
221+
window.scrollTo({ top: 0, behavior: 'smooth' });
222+
}
223+
224+
// Render detailed results with all questions and answers
225+
function renderDetailedResults(results) {
226+
const container = document.getElementById('detailedResults');
227+
if (!container) return;
228+
229+
container.innerHTML = '';
230+
231+
results.forEach((result, index) => {
232+
const q = result.question;
233+
const resultCard = document.createElement('div');
234+
resultCard.className = 'result-card';
235+
236+
if (result.isCorrect) {
237+
resultCard.classList.add('correct');
238+
} else if (result.isAnswered) {
239+
resultCard.classList.add('wrong');
240+
} else {
241+
resultCard.classList.add('unanswered');
242+
}
243+
244+
// Question header
245+
const header = document.createElement('div');
246+
header.className = 'result-header';
247+
248+
const statusIcon = result.isCorrect ? '✓' : result.isAnswered ? '✗' : '—';
249+
const statusClass = result.isCorrect ? 'correct' : result.isAnswered ? 'wrong' : 'unanswered';
250+
const statusText = result.isCorrect ? 'Correct' : result.isAnswered ? 'Wrong' : 'Unanswered';
251+
252+
header.innerHTML = `
253+
<div class="result-status">
254+
<span class="status-icon ${statusClass}">${statusIcon}</span>
255+
<span class="status-text ${statusClass}">${statusText}</span>
256+
</div>
257+
<span class="result-number">Question ${index + 1}</span>
258+
`;
259+
260+
// Question content
261+
const questionDiv = document.createElement('div');
262+
questionDiv.className = 'result-question';
263+
questionDiv.innerHTML = `
264+
<div class="result-topic">${q.lesson} - ${q.topic}</div>
265+
<div class="result-question-text">${q.question}</div>
266+
`;
267+
268+
// Answer options
269+
const answersDiv = document.createElement('div');
270+
answersDiv.className = 'result-answers';
271+
272+
q.shuffledOptions.forEach((option, optionIndex) => {
273+
const answerDiv = document.createElement('div');
274+
answerDiv.className = 'result-answer-option';
275+
276+
const isUserAnswer = result.userAnswer === optionIndex;
277+
const isCorrectAnswer = optionIndex === q.correctShuffledIndex;
278+
279+
if (isCorrectAnswer) {
280+
answerDiv.classList.add('correct-answer');
281+
}
282+
if (isUserAnswer && !isCorrectAnswer) {
283+
answerDiv.classList.add('user-wrong-answer');
284+
}
285+
if (isUserAnswer && isCorrectAnswer) {
286+
answerDiv.classList.add('user-correct-answer');
287+
}
288+
289+
let prefix = '';
290+
if (isCorrectAnswer) {
291+
prefix = '<span class="answer-icon correct">✓</span>';
292+
} else if (isUserAnswer) {
293+
prefix = '<span class="answer-icon wrong">✗</span>';
294+
}
295+
296+
let label = '';
297+
if (isCorrectAnswer && isUserAnswer) {
298+
label = '<span class="answer-label correct">Your Answer (Correct)</span>';
299+
} else if (isCorrectAnswer) {
300+
label = '<span class="answer-label correct">Correct Answer</span>';
301+
} else if (isUserAnswer) {
302+
label = '<span class="answer-label wrong">Your Answer</span>';
303+
}
304+
305+
answerDiv.innerHTML = `
306+
${prefix}
307+
<span class="answer-text">${option.text}</span>
308+
${label}
309+
`;
310+
311+
answersDiv.appendChild(answerDiv);
312+
});
313+
314+
// Explanation
315+
const explanationDiv = document.createElement('div');
316+
explanationDiv.className = 'result-explanation';
317+
explanationDiv.innerHTML = `
318+
<strong>Explanation:</strong> ${q.explanation}
319+
`;
320+
321+
resultCard.appendChild(header);
322+
resultCard.appendChild(questionDiv);
323+
resultCard.appendChild(answersDiv);
324+
resultCard.appendChild(explanationDiv);
325+
container.appendChild(resultCard);
326+
});
327+
}
328+
329+
// Handle reset
330+
function handleReset() {
331+
const confirmReset = confirm('Are you sure you want to reset the exam? All your answers will be lost.');
332+
if (!confirmReset) return;
333+
334+
examState.userAnswers = {};
335+
renderQuestions();
336+
window.scrollTo({ top: 0, behavior: 'smooth' });
337+
}
338+
339+
// Handle retake
340+
function handleRetake() {
341+
examState.userAnswers = {};
342+
examState.isSubmitted = false;
343+
344+
// Re-shuffle options
345+
examState.shuffledQuestions = examQuestions.map(q => {
346+
const optionIndices = q.options.map((_, index) => index);
347+
const shuffledIndices = shuffleArray(optionIndices);
348+
349+
const shuffledOptions = shuffledIndices.map(originalIndex => ({
350+
text: q.options[originalIndex],
351+
originalIndex: originalIndex
352+
}));
353+
354+
return {
355+
...q,
356+
shuffledOptions: shuffledOptions,
357+
correctShuffledIndex: shuffledIndices.indexOf(q.correct)
358+
};
359+
});
360+
361+
renderQuestions();
362+
363+
document.getElementById('examScreen').hidden = false;
364+
document.getElementById('resultsScreen').hidden = true;
365+
window.scrollTo({ top: 0, behavior: 'smooth' });
366+
}
367+
368+
// Initialize when DOM is ready
369+
if (document.readyState === 'loading') {
370+
document.addEventListener('DOMContentLoaded', initExam);
371+
} else {
372+
initExam();
373+
}
374+
})();

0 commit comments

Comments
 (0)