<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Math Mock Test</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
</head>
<style>
/* Original styles for input and result pages */
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: "Inter", sans-serif;
background-color: #f0f2f5;
margin: 0;
padding: 0;
overflow-x: hidden;
}
textarea::-webkit-scrollbar {
width: 8px;
}
textarea::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
textarea::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
textarea::-webkit-scrollbar-thumb:hover {
background: #555;
border-radius: 10px;
}
.page-container {
width: 100%;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
}
.page-card {
background-color: white;
padding: 2rem;
border-radius: 0.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 768px;
}
.chart-container {
position: relative;
width: 100%;
max-width: 800px;
margin: 0 auto;
padding-bottom: 2rem;
}
.chart-container canvas {
width: 100% !important;
height: auto !important;
}
.horizontal-chart-wrapper {
overflow-x: auto;
width: 100%;
padding-bottom: 10px;
min-height: 200px;
}
#question-feedback-modal {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.75);
display: none;
align-items: center;
justify-content: center;
padding: 1rem;
z-index: 2000;
}
#question-feedback-modal .modal-content {
background-color: white;
padding: 1.5rem;
border-radius: 0.5rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
width: 100%;
max-width: 600px;
max-height: 90vh;
overflow-y: auto;
position: relative;
}
#question-feedback-modal .modal-close-btn {
position: absolute;
top: 10px;
right: 10px;
padding: 8px;
background: none;
border: none;
cursor: pointer;
color: #4a5568;
font-size: 1.5rem;
}
.time-breakdown-list {
list-style: none;
padding: 0;
margin: 0;
max-height: calc(10 * 2.5rem);
overflow-y: auto;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
padding: 0.5rem;
}
.time-breakdown-list li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0.75rem;
border-bottom: 1px solid #f0f2f5;
white-space: nowrap;
}
.time-breakdown-list li:last-child {
border-bottom: none;
}
#question-palette-result {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
gap: 0.5rem;
padding: 1rem;
background-color: #f8fafc;
border-radius: 0.5rem;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.06);
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}
#question-palette-result .question-palette-btn {
width: 48px;
height: 48px;
border-radius: 9999px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 0.875rem;
cursor: pointer;
transition: all 0.2s ease-in-out;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
#question-palette-result .question-palette-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
}
#pdf-content-wrapper {
position: absolute;
left: -9999px;
top: -9999px;
width: 794px;
padding: 20px;
background-color: white;
color: #333;
}
#pdf-content-wrapper .question-feedback-item {
margin-bottom: 1.5rem;
padding: 1rem;
border: 1px solid #e2e8f0;
border-radius: 0.5rem;
background-color: #f8fafc;
}
#pdf-content-wrapper .question-feedback-item .option-item {
padding: 0.5rem;
border-radius: 0.25rem;
margin-bottom: 0.25rem;
border: 1px solid transparent;
}
#pdf-content-wrapper .question-feedback-item .option-item.correct {
background-color: #d1fae5;
border-color: #34d399;
}
#pdf-content-wrapper .question-feedback-item .option-item.incorrect {
background-color: #fee2e2;
border-color: #ef4444;
}
#pdf-content-wrapper .chart-container.pdf-chart {
width: 100%;
height: auto;
max-width: 500px;
margin: 1rem auto;
}
/* New mock test interface styles from provided code */
#mocktest-page, #solution-page { /* Apply styles to both mocktest and solution pages */
font-family: "Roboto", sans-serif;
background-color: #e0e0e0;
width: 100vw;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: flex-start;
}
.hidden {
display: none !important;
}
#mocktest-page .container, #solution-page .container {
width: 1800px;
max-width: 1800px;
background-color: #ffffff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
min-height: 100vh;
}
#mocktest-page .header, #solution-page .header {
background-color: #f0f0f0;
border-bottom: 1px solid #cccccc;
color: #333;
padding: 0.5rem 1rem; /* Adjusted padding */
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
@media (min-width: 768px) {
#mocktest-page .header, #solution-page .header {
flex-direction: row;
}
}
#mocktest-page .header-left, #mocktest-page .header-right,
#solution-page .header-left, #solution-page .header-right {
display: flex;
align-items: center;
}
#mocktest-page .header-right, #solution-page .header-right {
margin-top: 0.5rem;
justify-content: flex-end;
width: 100%;
}
@media (min-width: 768px) {
#mocktest-page .header-right, #solution-page .header-right {
margin-top: 0;
width: auto;
}
}
#mocktest-page .time-left-box, #solution-page .time-left-box {
background-color: #ffeb3b;
border: 1px solid #ccc;
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-weight: bold;
color: #e53e3e;
margin-left: 1rem;
white-space: nowrap;
}
#mocktest-page .instructions-warning, #solution-page .instructions-warning {
background-color: #ffffff;
border-bottom: 1px solid #e2e8f0;
color: #333;
padding: 0.5rem 1rem; /* Adjusted padding */
display: flex;
flex-direction: column;
align-items: flex-start;
}
@media (min-width: 768px) {
#mocktest-page .instructions-warning, #solution-page .instructions-warning {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
}
#mocktest-page .warning-message, #solution-page .warning-message {
color: #e53e3e;
font-weight: bold;
text-align: center;
margin-top: 0.5rem;
}
@media (min-width: 768px) {
#mocktest-page .warning-message, #solution-page .warning-message {
margin-top: 0;
}
}
#mocktest-page .main-content, #solution-page .main-content {
flex-grow: 1;
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
#mocktest-page .main-content, #solution-page .main-content {
flex-direction: row;
}
}
#mocktest-page .left-panel, #solution-page .left-panel {
background-color: #ffffff;
padding: 1rem;
width: 100%;
flex-shrink: 0;
}
@media (min-width: 768px) {
#mocktest-page .left-panel, #solution-page .left-panel {
width: 360px;
}
}
/* Section Tabs (PART-A to PART-D) */
#mocktest-page .part-tabs, #solution-page .part-tabs {
display: flex;
margin-bottom: 1rem;
width: 100%;
justify-content: center;
}
@media (min-width: 768px) {
#mocktest-page .part-tabs, #solution-page .part-tabs {
justify-content: flex-start;
}
}
#mocktest-page .part-tab-button, #solution-page .part-tab-button {
padding: 0.2rem 0.75rem; /* Adjusted padding */
font-weight: bold;
font-size: 0.875rem;
border: 1px solid #333;
color: white; /* flex-grow: 1; */
flex-wrap: nowrap;
text-align: center;
border-radius: 4px; /* No default border-radius */
}
#mocktest-page .part-tab-button:first-child, #solution-page .part-tab-button:first-child {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
#mocktest-page .part-tab-button:last-child, #solution-page .part-tab-button:last-child {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
#mocktest-page .part-tab-button.active, #solution-page .part-tab-button.active {
background-color: #008000; /* Green for active tab */
border-color: #38a169;
}
#mocktest-page .part-tab-button:not(.active), #solution-page .part-tab-button:not(.active) {
background-color: blue;
border-color: blue;
}
/* Question Grid - Updated CSS for Numbers Layout */
#mocktest-page .question-grid, #solution-page .question-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); /* Adjusted for new button size */
gap: 1.2rem; /* margin-bottom: 1rem; */
width: 100%;
margin-left: auto;
margin-right: auto;
height: 260px;
overflow: auto;
padding:4px
}
#mocktest-page .question-grid-item, #solution-page .question-grid-item {
position: relative;
width: 40px; /* New width */
height: 18px; /* New height */
border-radius: 5px; /* New border-radius */
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 0.875rem;
color: white;
cursor: pointer;
box-sizing: border-box;
transition: background-color 0.2s, box-shadow 0.2s;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
margin-bottom:20px;
}
/* Colors for question grid items */
#mocktest-page .question-grid-item.not-visited, #mocktest-page .question-grid-item.not-answered {
background-color: blue; /* Deep Blue */
}
#mocktest-page .question-grid-item.answered {
background-color: green; /* Green */
}
#mocktest-page .question-grid-item.marked {
background-color: #e53e3e; /* Red */
}
#mocktest-page .question-grid-item.answered-marked {
background-color: #FFEB3B; /* Yellow */
color: #333; /* Black text for yellow background */
}
/* Solution Page specific colors for question grid items */
#solution-page .question-grid-item.correct {
background-color: green; /* Green for correct answers */
}
#solution-page .question-grid-item.incorrect {
background-color: red; /* Red for incorrect answers */
}
#solution-page .question-grid-item.unattempted {
background-color: blue; /* Blue for unattempted questions */
}
#mocktest-page .question-grid-item .triangle, #solution-page .question-grid-item .triangle {
position: absolute;
bottom: -8px; /* Position under the number */
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 8px solid black; /* Black upward triangle */
display: none; /* Hidden by default, shown based on logic */
}
/* The triangle will now be shown if the question is current OR marked */
#mocktest-page .question-grid-item.current .triangle, #mocktest-page .question-grid-item.marked .triangle, #mocktest-page .question-grid-item.answered-marked .triangle,
#solution-page .question-grid-item.current .triangle { /* Only current for solution page */
display: block;
}
#mocktest-page .analysis-summary p, #solution-page .analysis-summary p {
color: #333;
font-weight: normal;
}
#mocktest-page .analysis-summary span, #solution-page .analysis-summary span {
font-weight: bold;
color: red;
}
#mocktest-page .right-panel, #solution-page .right-panel {
flex-grow: 1;
background-color: #ffffff;
padding: 1rem;
}
#mocktest-page .top-action-buttons, #solution-page .top-action-buttons {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
#mocktest-page .top-action-buttons button, #solution-page .top-action-buttons button {
background-color: #007bff;
color: white;
font-weight: bold;
padding: 0.3rem 1rem; /* Adjusted padding */
border-radius: 8px;
border: none;
cursor: pointer;
transition: background-color 0.2s;
white-space: nowrap;
}
#mocktest-page .top-action-buttons button:hover, #solution-page .top-action-buttons button:hover {
background-color: #0056b3;
}
#mocktest-page .top-action-buttons button#mark-review-btn.marked-active,
#solution-page .top-action-buttons button#mark-review-btn.marked-active {
background-color: #a0aec0;
}
#mocktest-page .question-status-language, #solution-page .question-status-language {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
margin-bottom: 1rem;
}
@media (min-width: 768px) {
#mocktest-page .question-status-language, #solution-page .question-status-language {
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
}
#mocktest-page .question-status-language p, #solution-page .question-status-language p {
color: #333;
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
@media (min-width: 768px) {
#mocktest-page .question-status-language p, #solution-page .question-status-language p {
margin-bottom: 0;
}
}
#mocktest-page .question-status-language span, #solution-page .question-status-language span {
font-weight: bold;
color: #48bb78;
}
#mocktest-page .language-select-container, #solution-page .language-select-container {
display: flex;
align-items: center;
margin-top: 0.5rem;
}
@media (min-width: 768px) {
#mocktest-page .language-select-container, #solution-page .language-select-container {
margin-top: 0;
}
}
#mocktest-page .language-select-container select, #solution-page .language-select-container select {
background-color: #edf2f7;
border: 1px solid #e2e8f0;
color: #333;
padding: 0.25rem 0.5rem;
border-radius: 4px;
}
/* Main Question Display Panel - NEW STYLES */
.question-panel-container {
border: 1px solid #e2e8f0; /* Light gray solid border */
border-radius: 4px;
background-color: #ffffff;
margin-bottom: 1rem;
padding: 0; /* Remove padding from container, add to cells */
}
.question-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
color: red;
font-size: 14px;
padding: 5px 10px;
border-bottom: 1px solid #ccc;
}
.question-panel-header .question-label {
margin: 0;
color: #333; /* Make it dark like the original */
font-weight: bold;
}
.question-panel-header .last-minutes-text {
color: #e53e3e; /* Red color for last minutes */
font-weight: bold;
}
.question-table {
width: 100%;
border-collapse: collapse;
position: relative;
}
.question-table td {
border: 1px solid #ccc;
padding: 10px;
vertical-align: top;
position: relative;
}
.question-table td::before {
content: "10018161613181840400181816161318"; /* Watermark text */
color: #ccc;
font-size: 8px;
position: absolute;
top: 0;
left: 0;
opacity: 0.3;
z-index: -1;
pointer-events: none; /* Prevents interference with clicks */
}
.question-table .language-cell {
text-align: right;
padding-right: 10px;
}
.question-table .language-cell select {
width: 100px; /* Smaller width to match screenshot */
padding: 5px;
border: 1px solid #ccc;
box-sizing: border-box;
background-color: #edf2f7; /* Match original select background */
color: #333;
border-radius: 4px;
}
.question-table .question-cell {
font-weight: 400;
color: #333;
font-size: 20px; /* Dark text */
}
.question-table .radio-cell {
border-right: 1px solid #ccc;
width: 20px;
text-align: center;
vertical-align: middle;
}
.question-table .text-cell {
padding-left: 10px;
color: #333; /* Dark text */
}
.question-table input[type="radio"] {
margin: 0;
accent-color: #007BFF; /* Dark blue filled radio button */
transform: scale(1.2); /* Make radio button slightly larger */
}
/* Solution Page specific option highlighting */
.solution-option-correct {
background-color: #d1fae5; /* Light green */
border: 1px solid #34d399; /* Green border */
padding: 0.5rem;
border-radius: 0.25rem;
margin-bottom: 0.25rem;
}
.solution-option-incorrect {
background-color: #fee2e2; /* Light red */
border: 1px solid #ef4444; /* Red border */
padding: 0.5rem;
border-radius: 0.25rem;
margin-bottom: 0.25rem;
}
.solution-option-normal {
padding: 0.5rem;
border-radius: 0.25rem;
margin-bottom: 0.25rem;
border: 1px solid transparent;
}
.solution-text {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #e2e8f0;
font-size: 0.95rem;
color: #333;
}
/* KaTeX specific styling for better integration */
#mocktest-page .katex, #solution-page .katex {
font-size: 1em;
line-height: normal;
}
#mocktest-page .katex-display, #solution-page .katex-display {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
/* Modal for Symbols Legend */
#mocktest-page .modal, #solution-page .modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
align-items: center;
justify-content: center;
}
#mocktest-page .modal-content, #solution-page .modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 90%;
max-width: 600px;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
position: relative;
}
#mocktest-page .close-button, #solution-page .close-button {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
position: absolute;
top: 10px;
right: 20px;
cursor: pointer;
}
#mocktest-page .close-button:hover, #mocktest-page .close-button:focus,
#solution-page .close-button:hover, #solution-page .close-button:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
#mocktest-page .modal-table, #solution-page .modal-table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
#mocktest-page .modal-table th, #mocktest-page .modal-table td,
#solution-page .modal-table th, #solution-page .modal-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
#mocktest-page .modal-table th, #solution-page .modal-table th {
background-color: #f2f2f2;
font-weight: bold;
}
#mocktest-page .modal-table .symbol-cell, #solution-page .modal-table .symbol-cell {
width: 80px;
text-align: center;
font-weight: bold;
}
#mocktest-page .modal-table .symbol-cell .question-grid-item-legend,
#solution-page .modal-table .symbol-cell .question-grid-item-legend {
display: inline-flex;
width: 25px; /* Smaller size for legend */
height: 25px;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
border: 1px solid white;
box-sizing: border-box;
margin: 0 auto;
border-radius: 0; /* Sharp corners */
}
#mocktest-page .modal-table .symbol-cell .question-grid-item-legend.blue,
#solution-page .modal-table .symbol-cell .question-grid-item-legend.blue {
background-color: #007BFF;
}
#mocktest-page .modal-table .symbol-cell .question-grid-item-legend.green,
#solution-page .modal-table .symbol-cell .question-grid-item-legend.green {
background-color: #48bb78;
}
#mocktest-page .modal-table .symbol-cell .question-grid-item-legend.red,
#solution-page .modal-table .symbol-cell .question-grid-item-legend.red {
background-color: #e53e3e;
}
#mocktest-page .modal-table .symbol-cell .question-grid-item-legend.yellow,
#solution-page .modal-table .symbol-cell .question-grid-item-legend.yellow {
background-color: #FFEB3B;
color: #333;
}
#mocktest-page .modal-table .symbol-cell .triangle-legend,
#solution-page .modal-table .symbol-cell .triangle-legend {
display: inline-block;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 6px solid black; /* Upward triangle */
vertical-align: middle;
margin-right: 5px;
}
/* Zoom buttons from provided code */
.zoom-button {
background-color: blue;
color: #ffffff;
padding: 0.3rem 0.5rem;
border: 1px solid #a0aec0;
border-radius: 10px;
font-size: 0.8rem;
margin: 0 0.25rem;
font-weight: 600;
}
</style>
<body class="">
<!-- Page 1: Input Page -->
<div id="input-page" class="page-container">
<div class="page-card">
<h1 class="text-2xl font-bold text-center mb-6 text-gray-800">
<span class="text-green-600">Powered by</span> Joharul Hasan
</h1>
<p class="text-center text-gray-600 mb-8">Paste your MCQ questions below and generate an interactive quiz </p>
<div class="mb-4">
<label for="topic-input" class="block text-gray-700 text-lg font-medium mb-3">Topic</label>
<input type="text" id="topic-input" class="w-full p-4 border-2 border-blue-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent text-gray-800" placeholder="e.g., Algebra, Geometry, Calculus">
</div>
<div class="mb-6">
<label for="question-input-part-a" class="block text-gray-700 text-lg font-medium mb-3">Enter MCQ Questions for Part A</label>
<textarea id="question-input-part-a" rows="7" class="w-full p-4 border-2 border-blue-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent text-gray-800 resize-y" placeholder="Paste questions for Part A here."></textarea>
</div>
<div class="mb-6">
<label for="question-input-part-b" class="block text-gray-700 text-lg font-medium mb-3">Enter MCQ Questions for Part B</label>
<textarea id="question-input-part-b" rows="7" class="w-full p-4 border-2 border-blue-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent text-gray-800 resize-y" placeholder="Paste questions for Part B here."></textarea>
</div>
<div class="mb-6">
<label for="question-input-part-c" class="block text-gray-700 text-lg font-medium mb-3">Enter MCQ Questions for Part C</label>
<textarea id="question-input-part-c" rows="7" class="w-full p-4 border-2 border-blue-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent text-gray-800 resize-y" placeholder="Paste questions for Part C here."></textarea>
</div>
<div class="mb-6">
<label for="question-input-part-d" class="block text-gray-700 text-lg font-medium mb-3">Enter MCQ Questions for Part D</label>
<textarea id="question-input-part-d" rows="7" class="w-full p-4 border-2 border-blue-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent text-gray-800 resize-y" placeholder="Paste questions for Part D here."></textarea>
</div>
<button id="start-mocktest-btn" class="w-full bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold py-3 px-6 rounded-lg shadow-lg hover:from-blue-600 hover:to-purple-700 transition duration-300 transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75">
Start Mock Test
</button>
</div>
</div>
<!-- Page 2: Mock Test Page -->
<div id="mocktest-page" class="hidden">
<div class="container">
<header class="header">
<div class="header-left flex items-center justify-between w-full px-4">
<!-- Left section -->
<div class="flex items-center">
<img src="https://placehold.co/40x40/000000/FFFFFF?text=SSC" alt="SSC Logo" class="h-10 w-10 mr-4 rounded-full border border-gray-400">
<span class="text-gray-700 font-semibold text-sm mr-4">SSC-Mock Test</span>
<button class="zoom-button" id="zoom-in-btn">Zoom (+)</button>
<button class="zoom-button" id="zoom-out-btn">Zoom (-)</button>
</div>
<!-- Center section (Roll No) -->
<div class="absolute left-1/2 transform -translate-x-1/2 text-center">
<span class="text-gray-700 font-semibold text-lg block"> SSC CGL Tier | 2025 - Free Test </span>
<span class="text-gray-700 font-semibold text-xs block"> Roll No: 100181161242163 (Candidate Name) </span>
</div>
<!-- Right placeholder (optional to maintain spacing) -->
<div class="w-40"></div>
</div>
<div class="header-right flex items-center space-x-4 mr-4">
<!-- Time Box -->
<div class="text-right">
<p class="text-gray-600 text-xs">Time Left</p>
<div class="time-left-box">
<span id="total-time-left-panel">00:00:00</span>
</div>
</div>
<!-- Avatars -->
<div class="flex space-x-2 pr-10">
<img class="inline-block h-14 w-14 ring-2 ring-white user-avatar" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAkjktNk_waKZ6A064JikKQRYLxoKPNIUR_g&s" alt="User 1">
<img class="inline-block h-14 w-14 ring-2 ring-white user-avatar" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAkjktNk_waKZ6A064JikKQRYLxoKPNIUR_g&s" alt="User 2">
</div>
</div>
</header>
<div class="instructions-warning1">
<div class="flex mb-2 md:mb-0">
<button id="symbols-tab" class="part-tab-button active rounded-t-md" style="border-radius: 4px 4px 0 0;">SYMBOLS</button>
<button id="instructions-tab" class="part-tab-button rounded-t-md" style="border-radius: 4px 4px 0 0;">INSTRUCTIONS</button>
</div>
</div>
<div class="flex mb-2 md:mb-0 ml-2">
<button class="px-2 py-2 bg-white text-orange-600 font-semibold rounded-tl-md rounded-tr-md underline focus:outline-none"> SYMBOLS </button>
<button class="px-2 py-2 bg-white text-orange-600 font-semibold rounded-tl-md rounded-tr-md underline focus:outline-none ml-1"> INSTRUCTIONS </button>
</div>
<div class="main-content flex-grow">
<div class="left-panel p-4 flex flex-col items-center md:items-start">
<!-- Updated Part Tabs -->
<div class="part-tabs space-x-2">
<button class="part-tab-button active whitespace-normal" data-part="A">PART-A</button>
<button class="part-tab-button" data-part="B">PART-B</button>
<button class="part-tab-button" data-part="C">PART-C</button>
<button class="part-tab-button" data-part="D">PART-D</button>
</div>
<p id="part-title" class="text-gray-700 mb-4 font-semibold text-center w-full"></p>
<div id="question-grid" class="question-grid"></div>
<div class="analysis-summary w-full text-center md:text-left mt-2">
<h3 class="text-lg font-semibold text-black text-center bg-gray-300 px-4 py-2 shadow-sm my-2"> <span id="current-part-analysis-title"></span> Analysis </h3>
<p class="text-sm font-medium"> Answered: <span id="answered-count" class="bg-yellow-200 text-red-400 px-1 py-1 inline-block"> 0 </span> </p>
<p class="text-sm font-medium"> Not Answered: <span id="not-answered-count" class="bg-yellow-200 text-red-600 px-1 py-1 inline-block"> 0 </span> </p>
<button id="submit-test-btn" class="mt-4 w-full mx-4 bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition duration-300">SUBMIT TEST</button>
</div>
</div>
<div class="right-panel p-4">
<div class="flex flex-col sm:flex-row justify-end items-center mb-4 space-y-2 sm:space-y-0">
<div class="flex top-action-buttons">
<button class="actionBtn" id="prev-btn">Previous</button>
<button class="actionBtn" id="mark-review-btn">Mark for Review</button>
<button class="actionBtn" id="save-next-btn">Save & Next</button>
</div>
<p class="text-gray-700 text-lg mb-2 sm:mb-0 flex flex-col sm:flex-row justify-end items-center ml-24 font-bold"> Total Questions Answered: <span id="total-answered" class="ml-2 px-1 py-1 bg-yellow-200 text-red-600 rounded"> 0 </span> </p>
</div>
<!-- Updated Question Panel -->
<div id="question-panel-container" class="question-panel-container">
<div class="question-panel-header">
<span class="question-label font-semibold text-lg">Question : <span id="question-number">1</span></span>
<span class="last-minutes-text text-xl font-bold">Last <span class="text-blue-800" id="last-minutes-display">60</span> Minutes</span>
</div>
<table class="question-table">
<tr>
<td class="language-cell" colspan="2">
Select Language:
<select id="language-select">
<option value="english">English</option>
</select>
</td>
</tr>
<tr>
<td id="question-text-cell" class="question-cell" colspan="2">
<!-- Question text will be inserted here -->
</td>
</tr>
<!-- Options will be dynamically inserted here -->
<tbody id="options-table-body">
</tbody>
</table>
<div id="solution-section" class="solution-text p-4 hidden">
<h4 class="font-bold text-lg mb-2">Solution:</h4>
<p id="solution-content"></p>
</div>
</div>
</div>
</div>
<!-- Symbols Legend Modal -->
<div id="symbols-modal" class="modal">
<div class="modal-content">
<span class="close-button">×</span>
<h2 class="text-xl font-bold mb-4 text-gray-800">The different symbols used in the next pages are shown below. Please go through them and understand their meaning before you start the test.</h2>
<table class="modal-table">
<thead>
<tr>
<th>Symbol</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="symbol-cell">
<div class="question-grid-item-legend"></div>
</td>
<td>Option Not chosen</td>
</tr>
<tr>
<td class="symbol-cell">
<div class="question-grid-item-legend">
<input type="radio" checked disabled style="margin:0; accent-color: #007BFF;">
</div>
</td>
<td>Option chosen as correct (By clicking on it again you can delete your option and choose another option if desired.)</td>
</tr>
<tr>
<td class="symbol-cell">
<div class="question-grid-item-legend blue">12</div>
</td>
<td>Question number shown in blue color indicates that you have not yet attempted the question.</td>
</tr>
<tr>
<td class="symbol-cell">
<div class="question-grid-item-legend green">13</div>
</td>
<td>Question number shown in green color indicates that you have answered the question. </td>
</tr>
<tr>
<td class="symbol-cell"><span class="triangle-legend"></span> <div class="question-grid-item-legend red">14</div> </td>
<td>You have not yet answered the question, but marked it for coming back for review later, if time permits.</td>
</tr>
<tr>
<td class="symbol-cell"><span class="triangle-legend"></span> <div class="question-grid-item-legend yellow">15</div> </td>
<td>You have answered the question, but marked it for review later, if time permits. </td>
</tr>
<tr>
<td class="symbol-cell">
<button class="top-action-buttons button" style="background-color: #007BFF; color: white; border-radius: 4px; padding: 0.25rem 0.5rem; font-weight: bold; font-size: 0.8rem;">Save & Next</button>
</td>
<td>Clicking on this will take you to the next question.</td>
</tr>
<tr>
<td class="symbol-cell">
<button class="top-action-buttons button" style="background-color: #007BFF; color: white; border-radius: 4px; padding: 0.25rem 0.5rem; font-weight: bold; font-size: 0.8rem;">Previous</button>
</td>
<td>Clicking on this will take you to the previous question.</td>
</tr>
<tr>
<td class="symbol-cell">
<button class="top-action-buttons button" style="background-color: #007BFF; color: white; border-radius: 4px; padding: 0.25rem 0.5rem; font-weight: bold; font-size: 0.8rem;">Mark for Review</button>
</td>
<td>By clicking on this button, you can mark the question for review later. Please note that if you answer the question and mark for review, the question will be treated as answered and evaluated even if you do not review it.</td>
</tr>
<tr>
<td class="symbol-cell">
<button class="top-action-buttons button" style="background-color: #a0aec0; color: #333; border-radius: 4px; padding: 0.25rem 0.5rem; font-weight: bold; font-size: 0.8rem;">Unmark Review</button>
</td>
<td>By clicking on this button, you can unmark the question for review</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Page 4: Solution Page -->
<div id="solution-page" class="hidden">
<div class="container">
<header class="header">
<div class="header-left flex items-center justify-between w-full px-4">
<!-- Left section -->
<div class="flex items-center">
<img src="https://placehold.co/40x40/000000/FFFFFF?text=SSC" alt="SSC Logo" class="h-10 w-10 mr-4 rounded-full border border-gray-400">
<span class="text-gray-700 font-semibold text-sm mr-4">SSC-Mock Test - Solutions</span>
<button class="zoom-button" id="solution-zoom-in-btn">Zoom (+)</button>
<button class="zoom-button" id="solution-zoom-out-btn">Zoom (-)</button>
</div>
<!-- Center section (Roll No) -->
<div class="absolute left-1/2 transform -translate-x-1/2 text-center">
<span class="text-gray-700 font-semibold text-lg block"> SSC CGL Tier | 2025 - Free Test </span>
<span class="text-gray-700 font-semibold text-xs block"> Roll No: 100181161242163 (Candidate Name) </span>
</div>
<!-- Right placeholder (optional to maintain spacing) -->
<div class="w-40"></div>
</div>
<div class="header-right flex items-center space-x-4 mr-4">
<div class="text-right">
<p class="text-gray-600 text-xs">Test Completed</p>
<div class="time-left-box">
<span id="solution-completion-time"></span>
</div>
</div>
<!-- Avatars -->
<div class="flex space-x-2 pr-10">
<img class="inline-block h-14 w-14 ring-2 ring-white user-avatar" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAkjktNk_waKZ6A064JikKQRYLxoKPNIUR_g&s" alt="User 1">
<img class="inline-block h-14 w-14 ring-2 ring-white user-avatar" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSAkjktNk_waKZ6A064JikKQRYLxoKPNIUR_g&s" alt="User 2">
</div>
</div>
</header>
<div class="instructions-warning1">
<div class="flex mb-2 md:mb-0">
<button id="solution-symbols-tab" class="part-tab-button active rounded-t-md" style="border-radius: 4px 4px 0 0;">SYMBOLS</button>
<button id="solution-instructions-tab" class="part-tab-button rounded-t-md" style="border-radius: 4px 4px 0 0;">INSTRUCTIONS</button>
</div>
</div>
<div class="flex mb-2 md:mb-0 ml-2">
<button class="px-2 py-2 bg-white text-orange-600 font-semibold rounded-tl-md rounded-tr-md underline focus:outline-none"> SYMBOLS </button>
<button class="px-2 py-2 bg-white text-orange-600 font-semibold rounded-tl-md rounded-tr-md underline focus:outline-none ml-1"> INSTRUCTIONS </button>
</div>
<div class="main-content flex-grow">
<div class="left-panel p-4 flex flex-col items-center md:items-start">
<!-- Updated Part Tabs -->
<div class="part-tabs space-x-2">
<button class="part-tab-button active whitespace-normal" data-part="A">PART-A</button>
<button class="part-tab-button" data-part="B">PART-B</button>
<button class="part-tab-button" data-part="C">PART-C</button>
<button class="part-tab-button" data-part="D">PART-D</button>
</div>
<p id="solution-part-title" class="text-gray-700 mb-4 font-semibold text-center w-full"></p>
<div id="solution-question-grid" class="question-grid"></div>
<div class="analysis-summary w-full text-center md:text-left mt-2">
<h3 class="text-lg font-semibold text-black text-center bg-gray-300 px-4 py-2 shadow-sm my-2"> <span id="solution-current-part-analysis-title"></span> Analysis </h3>
<p class="text-sm font-medium"> Answered: <span id="solution-answered-count" class="bg-yellow-200 text-red-400 px-1 py-1 inline-block"> 0 </span> </p>
<p class="text-sm font-medium"> Not Answered: <span id="solution-not-answered-count" class="bg-yellow-200 text-red-600 px-1 py-1 inline-block"> 0 </span> </p>
<button id="back-to-result-btn" class="mt-4 w-full mx-4 bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition duration-300">Back to Results</button>
</div>
</div>
<div class="right-panel p-4">
<div class="flex flex-col sm:flex-row justify-end items-center mb-4 space-y-2 sm:space-y-0">
<div class="flex top-action-buttons">
<button class="actionBtn" id="solution-prev-btn">Previous</button>
<button class="actionBtn" id="solution-next-btn">Next</button>
</div>
<p class="text-gray-700 text-lg mb-2 sm:mb-0 flex flex-col sm:flex-row justify-end items-center ml-24 font-bold"> Total Questions Answered: <span id="solution-total-answered" class="ml-2 px-1 py-1 bg-yellow-200 text-red-600 rounded"> 0 </span> </p>
</div>
<!-- Question Panel for Solutions -->
<div id="solution-question-panel-container" class="question-panel-container">
<div class="question-panel-header">
<span class="question-label font-semibold text-lg">Question : <span id="solution-question-number">1</span></span>
<span class="last-minutes-text text-xl font-bold">Review Mode</span>
</div>
<table class="question-table">
<tr>
<td class="language-cell" colspan="2">
Select Language:
<select id="solution-language-select" disabled>
<option value="english">English</option>
</select>
</td>
</tr>
<tr>
<td id="solution-question-text-cell" class="question-cell" colspan="2">
<!-- Question text will be inserted here -->
</td>
</tr>
<!-- Options will be dynamically inserted here -->
<tbody id="solution-options-table-body">
</tbody>
</table>
<div id="solution-section-content" class="solution-text p-4">
<h4 class="font-bold text-lg mb-2">Solution:</h4>
<p id="solution-dummy-content"></p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Page 3: Result Page -->
<div id="result-page" class="hidden page-container"></div>
<!-- Custom Alert Modal -->
<div id="custom-alert-modal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-75 flex items-center justify-center p-4 z-50">
<div class="bg-white p-6 rounded-lg shadow-xl text-center w-full max-w-sm">
<p id="custom-alert-message" class="text-lg font-semibold text-gray-800 mb-6"></p>
<div class="flex justify-center space-x-4">
<button id="custom-alert-ok-btn" class="bg-blue-500 text-white font-semibold py-2 px-6 rounded-lg shadow hover:bg-blue-600 transition duration-200">OK</button>
<button id="custom-alert-cancel-btn" class="bg-gray-300 text-gray-800 font-semibold py-2 px-6 rounded-lg shadow hover:bg-gray-400 transition duration-200 hidden">Cancel</button>
</div>
</div>
</div>
<!-- Question Feedback Modal -->
<div id="question-feedback-modal" class="hidden">
<div class="modal-content">
<button class="modal-close-btn" onclick="document.getElementById('question-feedback-modal').style.display='none'">×</button>
<h3 id="feedback-question-title" class="text-xl font-bold text-gray-800 mb-4"></h3>
<p id="feedback-question-text" class="text-lg text-gray-700 mb-2"></p>
<div id="feedback-status-tag" class="text-sm font-semibold px-2 py-1 rounded-full mb-4 inline-block"></div>
<div id="feedback-time-taken" class="flex items-center text-sm ml-2 mb-4">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span id="feedback-time-value" class="font-medium"></span>
</div>
<div id="feedback-options-container" class="space-y-2 mb-4"></div>
<div class="flex justify-between mt-4">
<button id="feedback-prev-btn" class="bg-gray-300 text-gray-800 font-semibold py-2 px-4 rounded-lg hover:bg-gray-400 transition">Previous</button>
<button id="feedback-next-btn" class="bg-blue-600 text-white font-semibold py-2 px-4 rounded-lg hover:bg-blue-700 transition">Next</button>
</div>
</div>
</div>
<!-- Hidden container for PDF content generation -->
<div id="pdf-content-wrapper"></div>
<script>
// --- Global Variables ---
let questions = []; // This will hold questions for the currently active part
let currentQuestionIndex = 0;
let totalTestStartTime;
let totalTestEndTime;
let totalTimerInterval;
let currentQuestionTimerInterval; // Timer for current question's time tracking
let filteredQuestionIndices = [];
let currentFeedbackIndex = 0;
let testCompletionDateTime;
let currentTopic = '';
let isSymbolsModalOpen = false;
const allQuestionsByPart = { 'A': [], 'B': [], 'C': [], 'D': [] };
const partNames = { 'A': 'General English', 'B': 'General Awareness', 'C': 'General Intelligence', 'D': 'General Quantitative Aptitude' };
const partOrder = ['A', 'B', 'C', 'D'];
let currentPart = 'A'; // Default active part
let currentQuestionTextFontSize = 20; // Initial font size for question text
let currentOptionTextFontSize = 16; // Initial font size for option text
let inSolutionMode = false; // Flag to indicate if we are in solution mode
// --- DOM Elements ---
const inputPage = document.getElementById('input-page');
const mocktestPage = document.getElementById('mocktest-page');
const solutionPage = document.getElementById('solution-page'); // New solution page
const resultPage = document.getElementById('result-page');
const questionInputPartA = document.getElementById('question-input-part-a');
const questionInputPartB = document.getElementById('question-input-part-b');
const questionInputPartC = document.getElementById('question-input-part-c');
const questionInputPartD = document.getElementById('question-input-part-d');
const topicInput = document.getElementById('topic-input');
const startMocktestBtn = document.getElementById('start-mocktest-btn');
const questionPanelContainer = document.getElementById('question-panel-container');
const questionPanelHeader = questionPanelContainer.querySelector('.question-panel-header');
const questionNumberSpan = questionPanelHeader.querySelector('#question-number');
const questionTextCell = document.getElementById('question-text-cell');
const optionsTableBody = document.getElementById('options-table-body');
const languageSelect = document.getElementById('language-select');
const questionGridEl = document.getElementById('question-grid');
const answeredCountEl = document.getElementById('answered-count');
const notAnsweredCountEl = document.getElementById('not-answered-count');
const totalAnsweredEl = document.getElementById('total-answered');
const prevBtn = document.getElementById('prev-btn');
const markReviewBtn = document.getElementById('mark-review-btn');
const saveNextBtn = document.getElementById('save-next-btn');
const submitTestBtn = document.getElementById('submit-test-btn');
const timeLeftEl = document.getElementById('total-time-left-panel');
const lastMinutesDisplay = document.getElementById('last-minutes-display'); // For countdown
const symbolsTab = document.getElementById('symbols-tab');
const instructionsTab = document.getElementById('instructions-tab');
const symbolsModal = document.getElementById('symbols-modal');
const closeModalBtn = symbolsModal.querySelector('.close-button');
const feedbackQuestionTitle = document.getElementById('feedback-question-title');
const feedbackQuestionText = document.getElementById('feedback-question-text');
const feedbackStatusTag = document.getElementById('feedback-status-tag');
const feedbackTimeValue = document.getElementById('feedback-time-value');
const feedbackOptionsContainer = document.getElementById('feedback-options-container');
const feedbackPrevBtn = document.getElementById('feedback-prev-btn');
const feedbackNextBtn = document.getElementById('feedback-next-btn');
const questionFeedbackModal = document.getElementById('question-feedback-modal');
const pdfContentWrapper = document.getElementById('pdf-content-wrapper');
const partTabs = document.querySelectorAll('#mocktest-page .part-tab-button');
const partTitleEl = document.getElementById('part-title');
const currentPartAnalysisTitle = document.getElementById('current-part-analysis-title'); // For part-wise analysis title
// Zoom buttons
const zoomInBtn = document.getElementById('zoom-in-btn');
const zoomOutBtn = document.getElementById('zoom-out-btn');
// Solution Page Elements
const solutionQuestionGridEl = document.getElementById('solution-question-grid');
const solutionPartTabs = document.querySelectorAll('#solution-page .part-tab-button');
const solutionPartTitleEl = document.getElementById('solution-part-title');
const solutionQuestionNumberSpan = document.getElementById('solution-question-number');
const solutionQuestionTextCell = document.getElementById('solution-question-text-cell');
const solutionOptionsTableBody = document.getElementById('solution-options-table-body');
const solutionDummyContent = document.getElementById('solution-dummy-content');
const solutionPrevBtn = document.getElementById('solution-prev-btn');
const solutionNextBtn = document.getElementById('solution-next-btn');
const backToResultBtn = document.getElementById('back-to-result-btn');
const solutionCompletionTimeEl = document.getElementById('solution-completion-time');
const solutionZoomInBtn = document.getElementById('solution-zoom-in-btn');
const solutionZoomOutBtn = document.getElementById('solution-zoom-out-btn');
const solutionAnsweredCountEl = document.getElementById('solution-answered-count');
const solutionNotAnsweredCountEl = document.getElementById('solution-not-answered-count');
const solutionTotalAnsweredEl = document.getElementById('solution-total-answered');
const solutionCurrentPartAnalysisTitle = document.getElementById('solution-current-part-analysis-title');
// --- Constants ---
const CORRECT_MARKS = 2;
const WRONG_MARKS = -0.5;
const TOTAL_TEST_DURATION_MINUTES = 60;
const ESTIMATED_EXAM_QUESTIONS = 25;
const REATTEMPT_DIFFICULT_TIME_LIMIT_MINUTES = 25;
// --- Fetch API Functions ---
async function fetchQuestions() {
try {
const response = await fetch('https://sscapi.trixofly.com/api/questions/');
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
console.log("API Data:", data);
if (data && data.length > 0) {
currentTopic = data[0].subject;
const partInputMap = { 'A': questionInputPartA, 'B': questionInputPartB, 'C': questionInputPartC, 'D': questionInputPartD };
partOrder.forEach((partKey, index) => {
if (data[index] && data[index].data && partInputMap[partKey]) {
let formattedQuestions = '';
data[index].data.forEach(question => {
formattedQuestions += `${question.serial}. ${question.question}\n`;
formattedQuestions += `(a) ${question.options.a}\n`;
formattedQuestions += `(b) ${question.options.b}\n`;
formattedQuestions += `(c) ${question.options.c}\n`;
formattedQuestions += `(d) ${question.options.d}\n`;
formattedQuestions += `Answer: (${question.answer}) ${question.options[question.answer]}\n\n`;
});
partInputMap[partKey].value = formattedQuestions;
} else if (partInputMap[partKey]) {
partInputMap[partKey].value = '';
}
});
}
} catch (error) {
console.error('Fetch error:', error);
}
}
// --- Utility Functions ---
function showAlert(message, title = "Alert", onConfirm = null) {
const modal = document.getElementById('custom-alert-modal');
const msgElement = document.getElementById('custom-alert-message');
const okBtn = document.getElementById('custom-alert-ok-btn');
const cancelBtn = document.getElementById('custom-alert-cancel-btn');
msgElement.textContent = message;
modal.classList.remove('hidden');
if (onConfirm) {
okBtn.textContent = 'Yes Submit';
cancelBtn.textContent = 'Close';
cancelBtn.classList.remove('hidden');
okBtn.onclick = () => {
modal.classList.add('hidden');
onConfirm(true);
};
cancelBtn.onclick = () => {
modal.classList.add('hidden');
onConfirm(false);
};
} else {
okBtn.textContent = 'OK';
cancelBtn.classList.add('hidden');
okBtn.onclick = () => modal.classList.add('hidden');
}
}
/**
* Parses raw text input into a structured array of question objects.
*/
function parseQuestions(rawText) {
const lines = rawText.split('\n').map(line => line.trim()).filter(line => line.length > 0);
const parsed = [];
let currentQuestion = null;
let optionsCounter = 0;
lines.forEach(line => {
const questionMatch = line.match(/^(\d+)\.\s*(.+)/);
if (questionMatch) {
if (currentQuestion) {
parsed.push(currentQuestion);
}
currentQuestion = {
originalNumber: parseInt(questionMatch[1]),
question: questionMatch[2].trim(),
options: [],
answer: '',
userAnswer: null,
status: 'not-visited',
timeTaken: 0, // Initialize timeTaken for each question
solution: `This is a dummy solution for Question ${questionMatch[1]}. The actual solution would involve detailed steps and explanations. For example, if it's an algebra question, the solution might show the derivation of the formula or the step-by-step calculation. If it's a geometry question, it might include diagrams and proofs. This dummy text serves as a placeholder to demonstrate the solution display functionality.`,
};
optionsCounter = 0;
} else if (currentQuestion && optionsCounter < 4) {
const optionMatch = line.match(/^\(([a-d])\)\s*(.*)/);
if (optionMatch) {
currentQuestion.options.push(line);
optionsCounter++;
} else if (currentQuestion.options.length === 0 && !currentQuestion.answer) {
// If it's a continuation of the question text before options
currentQuestion.question += ' ' + line;
}
} else if (line.startsWith('Answer:') && currentQuestion) {
const match = line.match(/\(([a-d])\)/);
if (match && match[1]) {
currentQuestion.answer = match[1];
}
}
});
if (currentQuestion) {
parsed.push(currentQuestion);
}
return parsed.filter(q => q.options.length === 4 && q.answer);
}
function formatMillisecondsToWords(totalMilliseconds) {
const totalSeconds = Math.round(totalMilliseconds / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
let result = '';
if (minutes > 0) {
result += `${minutes} minute${minutes > 1 ? 's' : ''}`;
}
if (seconds > 0) {
if (result) result += ' ';
result += `${seconds} sec`;
} else if (totalSeconds === 0) {
result = '0 sec';
}
return result;
}
function formatHoursMinutesSeconds(totalMilliseconds) {
const totalSeconds = Math.round(totalMilliseconds / 1000);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
const pad = (num) => String(num).padStart(2, '0');
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
}
function formatDate(date) {
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
return `${day}/${month}/${year}`;
}
function formatTime(date) {
let hours = date.getHours();
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const ampm = hours >= 12 ? 'PM' : 'AM';
hours = hours % 12 || 12;
return `${String(hours).padStart(2, '0')}:${minutes}:${seconds} ${ampm}`;
}
function getQuestionTimeDisplayColorClass(totalMilliseconds, question) {
const seconds = Math.round(totalMilliseconds / 1000);
if (question.userAnswer === null) return 'text-gray-500';
if (seconds <= 60) return 'text-green-600';
else if (seconds <= 90) return 'text-yellow-600';
else return 'text-red-600';
}
function getQuestionPaletteColorClasses(question) {
const selectedAnswer = question.userAnswer;
const correctAnswer = question.answer;
const timeTakenMilliseconds = question.timeTaken || 0;
const timeTakenSeconds = Math.round(timeTakenMilliseconds / 1000);
const isAttempted = selectedAnswer !== null;
const isCorrect = isAttempted && selectedAnswer.includes(`(${correctAnswer})`);
if (!isAttempted) {
return { bgColor: 'bg-gray-300', textColor: 'text-gray-800' };
} else if (isCorrect) {
if (timeTakenSeconds > 120) return { bgColor: 'bg-orange-500', textColor: 'text-white' };
else if (timeTakenSeconds > 90) return { bgColor: 'bg-yellow-500', textColor: 'text-yellow-900' };
else return { bgColor: 'bg-green-500', textColor: 'text-white' };
} else {
return { bgColor: 'bg-red-500', textColor: 'text-white' };
}
}
function getChartColor(totalMilliseconds, question) {
const seconds = Math.round(totalMilliseconds / 1000);
if (question.userAnswer === null) return '#9CA3AF';
if (seconds <= 60) return '#10B981';
else if (seconds <= 90) return '#F59E0B';
else return '#EF4444';
}
function getChartBorderColor(totalMilliseconds, question) {
const seconds = Math.round(totalMilliseconds / 1000);
if (question.userAnswer === null) return '#6B7280';
if (seconds <= 60) return '#059669';
else if (seconds <= 90) return '#D97706';
else return '#DC2626';
}
function saveCurrentQuestionTime() {
if (currentQuestionIndex !== null && questions[currentQuestionIndex]) {
const question = questions[currentQuestionIndex];
if (questionStartTimes[currentQuestionIndex] !== undefined) {
const sessionDuration = Date.now() - questionStartTimes[currentQuestionIndex];
question.timeTaken += sessionDuration;
questionStartTimes[currentQuestionIndex] = undefined; // Reset start time for next session
}
}
}
function startQuestionTimer() {
if (currentQuestionIndex !== null && questions[currentQuestionIndex]) {
if (questionStartTimes[currentQuestionIndex] === undefined || questionStartTimes[currentQuestionIndex] === null) {
questionStartTimes[currentQuestionIndex] = Date.now();
}
}
if (currentQuestionTimerInterval) clearInterval(currentQuestionTimerInterval);
// No need for a per-question display timer in the current UI
}
function stopQuestionTimer() {
if (currentQuestionTimerInterval) clearInterval(currentQuestionTimerInterval);
}
function startTotalTestTimer() {
totalTimerInterval = setInterval(() => {
if (isSymbolsModalOpen) return;
const elapsed = Date.now() - totalTestStartTime;
const totalTestDurationMilliseconds = TOTAL_TEST_DURATION_MINUTES * 60 * 1000;
const timeLeft = Math.max(0, totalTestDurationMilliseconds - elapsed);
// Update total time left display (MM:SS)
const minutes = Math.floor(timeLeft / (1000 * 60));
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
timeLeftEl.textContent = `${minutes.toString().padStart(2, '0')} : ${seconds.toString().padStart(2, '0')}`;
// Update "Last X Minutes" display
const remainingMinutes = Math.ceil(timeLeft / (1000 * 60));
lastMinutesDisplay.textContent = remainingMinutes;
if (timeLeft <= 0) {
showAlert("Time's up! The test has been submitted automatically.", "Time's Up!", () => {
submitTest();
});
}
}, 1000);
}
function stopTotalTestTimer() {
if (totalTimerInterval) {
clearInterval(totalTimerInterval);
totalTestEndTime = Date.now();
testCompletionDateTime = new Date();
}
}
function renderMath(element) {
// Ensure KaTeX renders algebraic identities correctly
renderMathInElement(element, {
delimiters: [
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: true },
{ left: '$$', right: '$$', display: true }, // For block equations
{ left: '$', right: '$', display: false }, // For inline equations
],
throwOnError: false
});
}
function displayQuestion(index = currentQuestionIndex, mode = 'mocktest') {
const targetQuestionGridEl = mode === 'mocktest' ? questionGridEl : solutionQuestionGridEl;
const targetQuestionNumberSpan = mode === 'mocktest' ? questionNumberSpan : solutionQuestionNumberSpan;
const targetQuestionTextCell = mode === 'mocktest' ? questionTextCell : solutionQuestionTextCell;
const targetOptionsTableBody = mode === 'mocktest' ? optionsTableBody : solutionOptionsTableBody;
const targetMarkReviewBtn = markReviewBtn; // Only on mocktest page
const targetSolutionSection = document.getElementById('solution-section'); // For mocktest page
const targetSolutionContent = document.getElementById('solution-content'); // For mocktest page
if (!questions || !Array.isArray(questions) || questions.length === 0) {
targetQuestionNumberSpan.textContent = 'N/A';
targetQuestionTextCell.innerHTML = 'No questions available for this section.';
targetOptionsTableBody.innerHTML = '';
updateQuestionGrid(mode);
updateAnalysisCounts(mode);
return;
}
if (mode === 'mocktest') {
saveCurrentQuestionTime();
}
if (index < 0 || index >= questions.length) {
showAlert("Question index out of bounds.");
return;
}
currentQuestionIndex = index;
const question = questions[currentQuestionIndex];
if (mode === 'mocktest' && question.status === 'not-visited') {
question.status = 'not-answered';
}
targetQuestionNumberSpan.textContent = question.originalNumber;
targetQuestionTextCell.innerHTML = question.question;
targetOptionsTableBody.innerHTML = '';
// Apply font sizes
targetQuestionTextCell.style.fontSize = `${currentQuestionTextFontSize}px`;
question.options.forEach((optionText, idx) => {
const optionId = `q${question.originalNumber}-option${idx}-${mode}`;
const isChecked = question.userAnswer === optionText;
const tr = document.createElement('tr');
const radioCell = document.createElement('td');
radioCell.className = 'radio-cell';
const input = document.createElement('input');
input.type = 'radio';
input.name = `question-${question.originalNumber}`;
input.id = optionId;
input.value = optionText;
input.checked = isChecked;
const textCell = document.createElement('td');
textCell.className = 'text-cell';
textCell.innerHTML = optionText;
textCell.style.fontSize = `${currentOptionTextFontSize}px`; // Apply font size
if (mode === 'mocktest') {
tr.className = 'option-row';
input.addEventListener('change', (event) => {
questions[currentQuestionIndex].userAnswer = event.target.value;
const currentStatus = questions[currentQuestionIndex].status;
if (currentStatus === 'not-answered' || currentStatus === 'not-visited') {
questions[currentQuestionIndex].status = 'answered';
} else if (currentStatus === 'marked') {
questions[currentQuestionIndex].status = 'answered-marked';
}
updateQuestionGrid(mode);
updateAnalysisCounts(mode);
});
input.addEventListener('dblclick', (event) => {
if (questions[currentQuestionIndex].userAnswer === optionText) {
questions[currentQuestionIndex].userAnswer = null;
input.checked = false;
const currentStatus = questions[currentQuestionIndex].status;
if (currentStatus === 'answered') {
questions[currentQuestionIndex].status = 'not-answered';
} else if (currentStatus === 'answered-marked') {
questions[currentQuestionIndex].status = 'marked';
}
updateQuestionGrid(mode);
updateAnalysisCounts(mode);
}
});
} else { // Solution mode
input.disabled = true; // Disable radio buttons in solution mode
const isCorrectOption = optionText.includes(`(${question.answer})`);
const isSelectedOption = question.userAnswer === optionText;
tr.classList.add('option-row'); // Add this for consistency
if (isCorrectOption) {
tr.classList.add('solution-option-correct');
} else if (isSelectedOption && !isCorrectOption) {
tr.classList.add('solution-option-incorrect');
} else {
tr.classList.add('solution-option-normal');
}
}
radioCell.appendChild(input);
tr.appendChild(radioCell);
tr.appendChild(textCell);
targetOptionsTableBody.appendChild(tr);
});
if (mode === 'mocktest') {
targetMarkReviewBtn.classList.remove('marked-active');
if (question.status === 'marked' || question.status === 'answered-marked') {
targetMarkReviewBtn.classList.add('marked-active');
}
targetSolutionSection.classList.add('hidden'); // Hide solution section in mocktest mode
} else { // Solution mode
// Display dummy solution
document.getElementById('solution-dummy-content').innerHTML = question.solution;
document.getElementById('solution-section-content').classList.remove('hidden'); // Show solution section
}
if (mode === 'mocktest') {
startQuestionTimer();
}
updateQuestionGrid(mode);
renderMath(targetQuestionTextCell);
renderMath(targetOptionsTableBody);
if (mode === 'solution') {
renderMath(document.getElementById('solution-dummy-content'));
}
}
function initializeQuestionGrid(mode = 'mocktest') {
const targetQuestionGridEl = mode === 'mocktest' ? questionGridEl : solutionQuestionGridEl;
targetQuestionGridEl.innerHTML = '';
const currentPartQuestions = allQuestionsByPart[currentPart];
for (let i = 0; i < currentPartQuestions.length; i++) {
const question = currentPartQuestions[i];
const buttonContainer = document.createElement('div');
buttonContainer.className = 'question-grid-item';
buttonContainer.dataset.index = i;
const triangle = document.createElement('div');
triangle.className = 'triangle';
buttonContainer.appendChild(triangle);
const buttonText = document.createElement('span');
buttonText.textContent = question.originalNumber;
buttonContainer.appendChild(buttonText);
buttonContainer.addEventListener('click', () => {
displayQuestion(parseInt(buttonContainer.dataset.index), mode);
});
targetQuestionGridEl.appendChild(buttonContainer);
}
updateQuestionGrid(mode);
}
function updateQuestionGrid(mode = 'mocktest') {
const targetQuestionGridEl = mode === 'mocktest' ? questionGridEl : solutionQuestionGridEl;
if (!targetQuestionGridEl) return;
const buttons = targetQuestionGridEl.children;
const currentPartQuestions = allQuestionsByPart[currentPart];
for (let i = 0; i < currentPartQuestions.length; i++) {
const question = currentPartQuestions[i];
const buttonContainer = buttons[i];
if (buttonContainer) {
const triangle = buttonContainer.querySelector('.triangle');
buttonContainer.classList.remove('not-visited', 'not-answered', 'answered', 'marked', 'answered-marked', 'current', 'correct', 'incorrect', 'unattempted');
if (mode === 'mocktest') {
buttonContainer.classList.add(question.status);
if (triangle) {
if (i === currentQuestionIndex || question.status === 'marked' || question.status === 'answered-marked') {
triangle.style.display = 'block';
} else {
triangle.style.display = 'none';
}
}
if (i === currentQuestionIndex) {
buttonContainer.classList.add('current');
}
} else { // Solution mode
const selectedAnswer = question.userAnswer;
const correctAnswerLetter = question.answer;
const isCorrect = selectedAnswer !== null && selectedAnswer.includes(`(${correctAnswerLetter})`);
if (selectedAnswer === null) {
buttonContainer.classList.add('unattempted');
} else if (isCorrect) {
buttonContainer.classList.add('correct');
} else {
buttonContainer.classList.add('incorrect');
}
// In solution mode, triangle only indicates the currently viewed question
if (triangle) {
if (i === currentQuestionIndex) {
triangle.style.display = 'block';
} else {
triangle.style.display = 'none';
}
}
if (i === currentQuestionIndex) {
buttonContainer.classList.add('current');
}
}
}
}
updateAnalysisCounts(mode);
}
function updateAnalysisCounts(mode = 'mocktest') {
const currentPartQuestions = allQuestionsByPart[currentPart];
let answered = 0;
let notAnswered = 0;
currentPartQuestions.forEach(q => {
if (q.userAnswer !== null) {
answered++;
} else {
notAnswered++;
}
});
if (mode === 'mocktest') {
answeredCountEl.textContent = answered;
notAnsweredCountEl.textContent = notAnswered;
totalAnsweredEl.textContent = answered; // This is the total for the current part
currentPartAnalysisTitle.textContent = `${partNames[currentPart]}`; // Update part analysis title
} else { // Solution mode
solutionAnsweredCountEl.textContent = answered;
solutionNotAnsweredCountEl.textContent = notAnswered;
solutionTotalAnsweredEl.textContent = answered;
solutionCurrentPartAnalysisTitle.textContent = `${partNames[currentPart]}`;
}
}
function goToNextQuestionWithSave() {
saveCurrentQuestionTime();
const question = questions[currentQuestionIndex];
if (question.userAnswer !== null) {
if (question.status === 'marked') {
question.status = 'answered-marked';
} else if (question.status === 'not-visited' || question.status === 'not-answered') {
question.status = 'answered';
}
} else if (question.status === 'not-visited') {
question.status = 'not-answered';
}
updateQuestionGrid();
if (currentQuestionIndex < questions.length - 1) {
displayQuestion(currentQuestionIndex + 1);
} else {
const currentPartIndex = partOrder.indexOf(currentPart);
let nextPartFound = false;
for (let i = currentPartIndex + 1; i < partOrder.length; i++) {
const nextPartKey = partOrder[i];
if (allQuestionsByPart[nextPartKey] && allQuestionsByPart[nextPartKey].length > 0) {
currentPart = nextPartKey;
questions = allQuestionsByPart[currentPart];
currentQuestionIndex = 0;
partTabs.forEach(tab => tab.classList.remove('active'));
document.querySelector(`#mocktest-page .part-tab-button[data-part="${currentPart}"]`).classList.add('active');
partTitleEl.textContent = partNames[currentPart];
initializeQuestionGrid();
displayQuestion(0);
nextPartFound = true;
break;
}
}
if (!nextPartFound) {
showAlert("You have reached the last question. Are you want to submit test?", "Test Complete", (confirmed) => {
if (confirmed) {
submitTest();
}
});
}
}
}
function goToPreviousQuestion() {
saveCurrentQuestionTime();
if (currentQuestionIndex > 0) {
displayQuestion(currentQuestionIndex - 1);
} else {
showAlert("You are at the first question in this section.");
}
}
function markForReviewToggle() {
const question = questions[currentQuestionIndex];
if (!question) return;
if (question.status === 'marked') {
question.status = 'not-answered';
markReviewBtn.classList.remove('marked-active');
} else if (question.status === 'answered-marked') {
question.status = 'answered';
markReviewBtn.classList.remove('marked-active');
} else if (question.userAnswer === null) {
question.status = 'marked';
markReviewBtn.classList.add('marked-active');
} else {
question.status = 'answered-marked';
markReviewBtn.classList.add('marked-active');
}
updateQuestionGrid();
}
function showQuestionFeedback(displayIndex, fromFilter = false) {
let questionIndexToShow;
if (fromFilter) {
questionIndexToShow = filteredQuestionIndices[displayIndex];
currentFeedbackIndex = displayIndex;
} else {
questionIndexToShow = displayIndex;
filteredQuestionIndices = [questionIndexToShow];
currentFeedbackIndex = 0;
}
const question = questions[questionIndexToShow];
const selectedAnswer = question.userAnswer;
const correctAnswer = question.answer;
const timeTaken = question.timeTaken || 0;
feedbackQuestionTitle.textContent = `Question ${question.originalNumber} Feedback`;
feedbackQuestionText.innerHTML = question.question;
if (selectedAnswer !== null) {
feedbackStatusTag.textContent = selectedAnswer.includes(correctAnswer) ? "Correct" : "Incorrect";
feedbackStatusTag.className = `text-sm font-semibold px-2 py-1 rounded-full inline-block ${selectedAnswer.includes(correctAnswer) ? 'bg-green-200 text-green-700' : 'bg-red-200 text-red-700'} mb-4`;
} else {
feedbackStatusTag.textContent = "Unattempted";
feedbackStatusTag.className = "text-sm font-semibold px-2 py-1 rounded-full inline-block bg-yellow-200 text-yellow-700 mb-4";
}
feedbackTimeValue.textContent = formatMillisecondsToWords(timeTaken);
feedbackTimeValue.className = `font-medium ${getQuestionTimeDisplayColorClass(timeTaken, question)}`;
feedbackOptionsContainer.innerHTML = '';
question.options.forEach((optionText) => {
const isCorrectOption = optionText.includes(`(${question.answer})`);
const isSelectedOption = selectedAnswer === optionText;
let colorClass = 'text-gray-800';
let borderColorClass = 'border-gray-200';
if (isCorrectOption) {
colorClass = 'bg-green-100 text-green-700 font-semibold';
borderColorClass = 'border-green-500';
} else if (isSelectedOption && !isCorrectOption) {
colorClass = 'bg-red-100 text-red-700 font-semibold';
borderColorClass = 'border-red-500';
}
const optionElement = document.createElement('p');
optionElement.className = `p-2 border rounded-md ${colorClass} ${borderColorClass}`;
optionElement.innerHTML = optionText;
feedbackOptionsContainer.appendChild(optionElement);
});
feedbackPrevBtn.disabled = currentFeedbackIndex === 0;
feedbackNextBtn.disabled = currentFeedbackIndex === filteredQuestionIndices.length - 1;
questionFeedbackModal.style.display = 'flex';
renderMath(feedbackQuestionText);
renderMath(feedbackOptionsContainer);
}
function navigateFeedbackPrev() {
if (currentFeedbackIndex > 0) {
showQuestionFeedback(currentFeedbackIndex - 1, true);
}
}
function navigateFeedbackNext() {
if (currentFeedbackIndex < filteredQuestionIndices.length - 1) {
showQuestionFeedback(currentFeedbackIndex + 1, true);
}
}
function showFeedbackFiltered(type) {
filteredQuestionIndices = [];
questions.forEach((question, index) => {
const selectedAnswer = question.userAnswer;
const correctAnswerLetter = question.answer;
const isCorrect = selectedAnswer !== null && selectedAnswer.includes(`(${correctAnswerLetter})`);
if (type === 'correct' && isCorrect) {
filteredQuestionIndices.push(index);
} else if (type === 'incorrect' && selectedAnswer !== null && !isCorrect) {
filteredQuestionIndices.push(index);
} else if (type === 'unattempted' && selectedAnswer === null) {
filteredQuestionIndices.push(index);
} else if (type === 'attempted' && selectedAnswer !== null) {
filteredQuestionIndices.push(index);
}
});
if (filteredQuestionIndices.length > 0) {
showQuestionFeedback(0, true);
} else {
showAlert(`No ${type} questions found.`);
}
}
function renderQuestionStatusChart(correctCount, incorrectCount, unattemptedCount, forPdf = false) {
const ctx = document.getElementById(forPdf ? 'pdfQuestionStatusChart' : 'questionStatusChart').getContext('2d');
const chartInstanceName = forPdf ? 'questionStatusChartInstancePdf' : 'questionStatusChartInstance';
if (window[chartInstanceName]) window[chartInstanceName].destroy();
window[chartInstanceName] = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Correct', 'Incorrect', 'Unattempted'],
datasets: [{
data: [correctCount, incorrectCount, unattemptedCount],
backgroundColor: ['#10B981', '#EF4444', '#F59E0B'],
hoverOffset: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
aspectRatio: forPdf ? 1.5 : 1.2,
plugins: {
legend: {
position: forPdf ? 'bottom' : 'top',
labels: {
font: { size: forPdf ? 10 : 12 }
}
}
}
}
});
}
function renderTimePerQuestionChart(questionTimeDurations, forPdf = false) {
const ctx = document.getElementById(forPdf ? 'pdfTimePerQuestionChart' : 'timePerQuestionChartCanvas').getContext('2d');
const chartInstanceName = forPdf ? 'timePerQuestionChartInstancePdf' : 'timePerQuestionChartInstance';
if (window[chartInstanceName]) window[chartInstanceName].destroy();
window[chartInstanceName] = new Chart(ctx, {
type: 'bar',
data: {
labels: questions.map((_, i) => `Q${i + 1}`),
datasets: [{
label: 'Time Taken (seconds)',
data: questionTimeDurations.map(ms => Math.round(ms / 1000)),
backgroundColor: questions.map(q => getChartColor(q.timeTaken, q)),
borderColor: questions.map(q => getChartBorderColor(q.timeTaken, q)),
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
scales: {
x: {
beginAtZero: true,
title: {
display: true,
text: 'Time (seconds)',
font: { size: forPdf ? 10 : 12 }
},
ticks: {
font: { size: forPdf ? 8 : 10 }
}
},
y: {
title: {
display: true,
text: 'Question',
font: { size: forPdf ? 10 : 12 }
},
ticks: {
font: { size: forPdf ? 8 : 10 }
}
}
},
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: function (context) {
return `Time: ${context.parsed.x} seconds`;
}
}
}
}
}
});
}
function renderQuestionResultPalette() {
const palette = document.getElementById('question-palette-result');
palette.innerHTML = '';
questions.forEach((question, index) => {
const { bgColor, textColor } = getQuestionPaletteColorClasses(question);
const button = document.createElement('button');
button.className = `question-palette-btn ${bgColor} ${textColor}`;
button.textContent = question.originalNumber;
button.onclick = () => showQuestionFeedback(index);
palette.appendChild(button);
});
}
function takeScreenshot() {
const element = document.getElementById('result-page');
html2canvas(element, { scale: 2 }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = imgData;
link.download = 'mock_test_result.png';
link.click();
});
}
function downloadPdf() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
const displayTopic = currentTopic || 'General';
doc.setFontSize(16);
doc.text('Mock Test Result', 105, 20, { align: 'center' });
doc.setFontSize(12);
doc.text(`Topic: ${displayTopic}`, 20, 30);
doc.text(`Completed On: ${formatDate(testCompletionDateTime)} at ${formatTime(testCompletionDateTime)}`, 20, 40);
let correctCount = 0;
let incorrectCount = 0;
let score = 0;
// Aggregate all questions from all parts for final scoring and PDF
const allQuestionsFlat = [];
partOrder.forEach(partKey => {
allQuestionsByPart[partKey].forEach(q => allQuestionsFlat.push(q));
});
allQuestionsFlat.forEach((question) => {
const selectedAnswer = question.userAnswer;
if (selectedAnswer) {
if (selectedAnswer.includes(`(${question.answer})`)) {
correctCount++;
score += CORRECT_MARKS;
} else {
incorrectCount++;
score += WRONG_MARKS;
}
}
});
const totalQuestions = allQuestionsFlat.length;
const attemptedQuestions = correctCount + incorrectCount;
const unattemptedQuestions = totalQuestions - attemptedQuestions;
doc.text(`Total Score: ${score.toFixed(2)} / ${(totalQuestions * CORRECT_MARKS).toFixed(2)}`, 20, 50);
doc.text(`Accuracy: ${((correctCount / (attemptedQuestions || 1)) * 100).toFixed(2)}%`, 20, 60);
doc.text(`Correct: ${correctCount}`, 20, 70);
doc.text(`Incorrect: ${incorrectCount}`, 110, 70);
doc.text(`Unattempted: ${unattemptedQuestions}`, 20, 80);
const totalTimeTakenMilliseconds = totalTestEndTime - totalTestStartTime;
const avgTimePerQuestionMilliseconds = totalQuestions > 0 ? totalTimeTakenMilliseconds / totalQuestions : 0;
const estimatedExamTimeMilliseconds = avgTimePerQuestionMilliseconds * ESTIMATED_EXAM_QUESTIONS;
const numQuestionsAttemptedIn25Min = avgTimePerQuestionMilliseconds > 0 ? REATTEMPT_DIFFICULT_TIME_LIMIT_MINUTES * 60 * 1000 / avgTimePerQuestionMilliseconds : 0;
doc.text(`Total Time Taken: ${formatHoursMinutesSeconds(totalTimeTakenMilliseconds)}`, 20, 90);
doc.text(`Avg Time per Question: ${formatMillisecondsToWords(avgTimePerQuestionMilliseconds)}`, 20, 100);
doc.text(`Est. Exam Time (${ESTIMATED_EXAM_QUESTIONS} Qs): ${formatMillisecondsToWords(estimatedExamTimeMilliseconds)}`, 20, 110);
doc.text(`Est. Questions in ${REATTEMPT_DIFFICULT_TIME_LIMIT_MINUTES} min: ${numQuestionsAttemptedIn25Min.toFixed(0)}`, 20, 120);
pdfContentWrapper.innerHTML = `
<div class="chart-container pdf-chart">
<canvas id="pdfQuestionStatusChart"></canvas>
</div>
<div class="chart-container pdf-chart">
<canvas id="pdfTimePerQuestionChart"></canvas>
</div>
${allQuestionsFlat.map((question) => { // Use allQuestionsFlat here
const selectedAnswer = question.userAnswer;
const correctAnswerLetter = question.answer;
const isCorrect = selectedAnswer !== null && selectedAnswer.includes(`(${correctAnswerLetter})`);
return `
<div class="question-feedback-item">
<h3 class="font-semibold text-lg">Question ${question.originalNumber}</h3>
<p class="text-gray-700 mb-2">${question.question}</p>
<p class="text-sm font-medium mb-1">Status: <span class="${selectedAnswer ? (isCorrect ? 'text-green-600' : 'text-red-600') : 'text-yellow-600'}">${selectedAnswer ? (isCorrect ? 'Correct' : 'Incorrect') : 'Unattempted'}</span></p>
<p class="text-sm font-medium mb-2">Time Taken: <span class="${getQuestionTimeDisplayColorClass(question.timeTaken || 0, question)}">${formatMillisecondsToWords(question.timeTaken || 0)}</span></p>
<div class="space-y-1">
${question.options.map((opt) => {
const optLetterMatch = opt.match(/^\(([a-d])\)/);
const optLetter = optLetterMatch ? optLetterMatch[1] : '';
const isCurrentOptionCorrect = optLetter === correctAnswerLetter;
const isCurrentOptionSelected = selectedAnswer === opt;
let className = 'option-item';
if (isCurrentOptionCorrect) className += ' correct';
else if (isCurrentOptionSelected && !isCurrentOptionCorrect) className += ' incorrect';
return `<p class="${className}">${opt}</p>`;
}).join('')}
</div>
</div>
`;
}).join('')}
`;
renderMath(pdfContentWrapper); // Render math in PDF content wrapper
renderQuestionStatusChart(correctCount, incorrectCount, unattemptedQuestions, true);
renderTimePerQuestionChart(allQuestionsFlat.map(q => q.timeTaken || 0), true); // Pass timeTaken values
html2canvas(pdfContentWrapper, { scale: 2 }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const imgProps = doc.getImageProperties(imgData);
const pdfWidth = doc.internal.pageSize.getWidth();
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
let yPosition = 130;
doc.addImage(imgData, 'PNG', 0, yPosition, pdfWidth, pdfHeight);
// This part needs careful handling for multi-page PDF.
// The current approach adds the entire image multiple times.
// A better approach would be to calculate page breaks and add content page by page.
// For now, keeping the original logic for multi-page image addition.
for (let i = 0; i < Math.ceil(pdfHeight / doc.internal.pageSize.getHeight()); i++) {
if (yPosition + pdfHeight > (i + 1) * doc.internal.pageSize.getHeight()) {
doc.addPage();
doc.addImage(imgData, 'PNG', 0, yPosition - (i + 1) * doc.internal.pageSize.getHeight(), pdfWidth, pdfHeight);
}
}
doc.save('mock_test_result.pdf');
});
}
function reattemptDifficultQuestions() {
const difficultQuestions = [];
partOrder.forEach(partKey => {
allQuestionsByPart[partKey].forEach((question) => {
const selectedAnswer = question.userAnswer;
const correctAnswerLetter = question.answer;
const isCorrect = selectedAnswer !== null && selectedAnswer.includes(`(${correctAnswerLetter})`);
const timeTaken = question.timeTaken || 0;
if (!isCorrect || timeTaken > 90 * 1000) {
difficultQuestions.push({ ...question, userAnswer: null, status: 'not-visited', timeTaken: 0 }); // Reset for reattempt
}
});
});
if (difficultQuestions.length === 0) {
showAlert('No difficult questions to reattempt.');
return;
}
// For reattempt, we'll put all difficult questions into Part A for simplicity.
// A more complex implementation would distribute them back into their original parts
// or create a new "Difficult Questions" part.
allQuestionsByPart['A'] = difficultQuestions;
partOrder.forEach(partKey => {
if (partKey !== 'A') {
allQuestionsByPart[partKey] = []; // Clear other parts
}
});
currentPart = 'A';
questions = allQuestionsByPart[currentPart];
currentQuestionIndex = 0;
totalTestStartTime = Date.now();
totalTestEndTime = undefined;
testCompletionDateTime = undefined;
resultPage.classList.add('hidden');
mocktestPage.classList.remove('hidden');
solutionPage.classList.add('hidden'); // Hide solution page if visible
partTabs.forEach(tab => tab.classList.remove('active'));
document.querySelector(`#mocktest-page .part-tab-button[data-part="A"]`).classList.add('active');
partTitleEl.textContent = partNames['A'];
updatePartTabVisibility();
initializeQuestionGrid();
displayQuestion(0);
startTotalTestTimer();
}
function resetApp() {
questions = [];
partOrder.forEach(partKey => {
allQuestionsByPart[partKey] = [];
});
currentQuestionIndex = 0;
totalTestStartTime = undefined;
totalTestEndTime = undefined;
testCompletionDateTime = undefined;
currentTopic = '';
currentPart = 'A';
inSolutionMode = false; // Reset solution mode flag
if (totalTimerInterval) clearInterval(totalTimerInterval);
if (currentQuestionTimerInterval) clearInterval(currentQuestionTimerInterval);
resultPage.classList.add('hidden');
mocktestPage.classList.add('hidden');
solutionPage.classList.add('hidden'); // Hide solution page
inputPage.classList.remove('hidden');
questionInputPartA.value = '';
questionInputPartB.value = '';
questionInputPartC.value = '';
questionInputPartD.value = '';
topicInput.value = '';
partTabs.forEach(tab => tab.classList.remove('active'));
document.querySelector(`#mocktest-page .part-tab-button[data-part="A"]`).classList.add('active');
partTitleEl.textContent = partNames['A'];
updatePartTabVisibility();
// Reset font sizes
currentQuestionTextFontSize = 20;
currentOptionTextFontSize = 16;
questionTextCell.style.fontSize = `${currentQuestionTextFontSize}px`;
// Options font size will be set in displayQuestion
}
function submitTest() {
saveCurrentQuestionTime();
stopTotalTestTimer();
stopQuestionTimer();
let correctCount = 0;
let incorrectCount = 0;
let score = 0;
const allQuestionsFlat = [];
partOrder.forEach(partKey => {
allQuestionsByPart[partKey].forEach(q => allQuestionsFlat.push(q));
});
allQuestionsFlat.forEach((question) => {
const selectedAnswer = question.userAnswer;
if (selectedAnswer) {
if (selectedAnswer.includes(`(${question.answer})`)) {
correctCount++;
score += CORRECT_MARKS;
} else {
incorrectCount++;
score += WRONG_MARKS;
}
}
});
const totalQuestions = allQuestionsFlat.length;
const attemptedQuestions = correctCount + incorrectCount;
const unattemptedQuestions = totalQuestions - attemptedQuestions;
const accuracy = attemptedQuestions > 0 ? (correctCount / attemptedQuestions) * 100 : 0;
const totalTimeTakenMilliseconds = totalTestEndTime - totalTestStartTime;
// Sum up timeTaken from all questions
let sumOfQuestionTimesMilliseconds = allQuestionsFlat.reduce((sum, q) => sum + (q.timeTaken || 0), 0);
const avgTimePerQuestionMilliseconds = totalQuestions > 0 ? sumOfQuestionTimesMilliseconds / totalQuestions : 0;
const estimatedExamTimeMilliseconds = avgTimePerQuestionMilliseconds * ESTIMATED_EXAM_QUESTIONS;
const numQuestionsAttemptedIn25Min = avgTimePerQuestionMilliseconds > 0 ? REATTEMPT_DIFFICULT_TIME_LIMIT_MINUTES * 60 * 1000 / avgTimePerQuestionMilliseconds : 0;
mocktestPage.classList.add('hidden');
inputPage.classList.add('hidden');
solutionPage.classList.add('hidden'); // Hide solution page
resultPage.classList.remove('hidden');
const completionDate = testCompletionDateTime ? formatDate(testCompletionDateTime) : 'N/A';
const completionTime = testCompletionDateTime ? formatTime(testCompletionDateTime) : 'N/A';
const displayTopic = currentTopic || 'General';
resultPage.innerHTML = `
<div class="page-card">
<h2 class="text-3xl font-bold text-center mb-2 text-gray-800">Mock Test Result</h2>
<p class="text-center text-gray-600 mb-4">Topic: <span class="font-semibold">${displayTopic}</span></p>
<p class="text-center text-gray-600 mb-6">Completed On: <span class="font-semibold">${completionDate}</span> at <span class="font-semibold">${completionTime}</span></p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="bg-blue-100 p-6 rounded-lg text-center">
<p class="text-2xl font-bold text-blue-700">${score.toFixed(2)} / ${(totalQuestions * CORRECT_MARKS).toFixed(2)}</p>
<p class="text-gray-600">Total Score</p>
<div class="w-full bg-gray-200 rounded-full h-2.5 mt-2">
<div class="bg-blue-600 h-2.5 rounded-full" style="width: ${((score / (totalQuestions * CORRECT_MARKS)) * 100) || 0}%"></div>
</div>
</div>
<div class="bg-green-100 p-6 rounded-lg text-center">
<p class="text-2xl font-bold text-green-700">${accuracy.toFixed(2)}%</p>
<p class="text-gray-600">Accuracy</p>
<div class="w-full bg-gray-200 rounded-full h-2.5 mt-2">
<div class="bg-green-600 h-2.5 rounded-full" style="width: ${accuracy}%"></div>
</div>
</div>
</div>
<div class="bg-gray-50 p-6 rounded-lg mb-8">
<h3 class="text-xl font-semibold text-gray-800 mb-4">Question Breakdown</h3>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-4 text-center">
<div class="cursor-pointer hover:bg-gray-100 p-2 rounded-lg" onclick="showFeedbackFiltered('correct')">
<p class="text-3xl font-bold text-green-600">${correctCount}</p>
<p class="text-gray-600">Correct</p>
</div>
<div class="cursor-pointer hover:bg-gray-100 p-2 rounded-lg" onclick="showFeedbackFiltered('incorrect')">
<p class="text-3xl font-bold text-red-600">${incorrectCount}</p>
<p class="text-gray-600">Incorrect</p>
</div>
<div class="cursor-pointer hover:bg-gray-100 p-2 rounded-lg" onclick="showFeedbackFiltered('unattempted')">
<p class="text-3xl font-bold text-yellow-600">${unattemptedQuestions}</p>
<p class="text-gray-600">Unattempted</p>
</div>
<div class="cursor-pointer hover:bg-gray-100 p-2 rounded-lg" onclick="showFeedbackFiltered('attempted')">
<p class="text-3xl font-bold text-indigo-600">${attemptedQuestions}</p>
<p class="text-gray-600">Attempted</p>
</div>
</div>
<div class="chart-container mt-8">
<canvas id="questionStatusChart"></canvas>
</div>
</div>
<div class="bg-white p-6 rounded-lg mb-8">
<h3 class="text-xl font-semibold text-gray-800 mb-4">Question Status Palette</h3>
<div id="question-palette-result"></div>
</div>
<div class="bg-gray-50 p-6 rounded-lg mb-8">
<h3 class="text-xl font-semibold text-gray-800 mb-4">Time Breakdown</h3>
<div class="horizontal-chart-wrapper">
<div class="chart-container">
<canvas id="timePerQuestionChartCanvas"></canvas>
</div>
</div>
<ul class="time-breakdown-list mt-4">
${allQuestionsFlat.map((question) => `
<li>
<span>Question ${question.originalNumber}</span>
<span class="${getQuestionTimeDisplayColorClass(question.timeTaken || 0, question)}">${formatMillisecondsToWords(question.timeTaken || 0)}</span>
</li>
`).join('')}
</ul>
</div>
<div class="bg-gray-50 p-6 rounded-lg mb-8">
<h3 class="text-xl font-semibold text-gray-800 mb-4">Performance Summary</h3>
<p class="text-gray-700 mb-2">Total Time Taken: <span class="font-semibold">${formatHoursMinutesSeconds(totalTimeTakenMilliseconds)}</span></p>
<p class="text-gray-700 mb-2">Average Time per Question: <span class="font-semibold">${formatMillisecondsToWords(avgTimePerQuestionMilliseconds)}</span></p>
<p class="text-gray-700 mb-2">Estimated Exam Completion Time (${ESTIMATED_EXAM_QUESTIONS} Questions): <span class="font-semibold">${formatMillisecondsToWords(estimatedExamTimeMilliseconds)}</span></p>
<p class="text-gray-700">Estimated Questions Attempted in ${REATTEMPT_DIFFICULT_TIME_LIMIT_MINUTES} Minutes: <span class="font-semibold">${numQuestionsAttemptedIn25Min.toFixed(0)}</span></p>
</div>
<div class="flex flex-col sm:flex-row justify-center gap-4 mt-8">
<button id="view-solution-btn" class="bg-purple-600 text-white font-semibold py-3 px-6 rounded-lg shadow-md hover:bg-purple-700 transition duration-300 transform hover:scale-105"> View Solution </button>
<button onclick="takeScreenshot()" class="bg-blue-600 text-white font-semibold py-3 px-6 rounded-lg shadow-md hover:bg-blue-700 transition duration-300 transform hover:scale-105"> Download Screenshot </button>
<button onclick="downloadPdf()" class="bg-green-600 text-white font-semibold py-3 px-6 rounded-lg shadow-md hover:bg-green-700 transition duration-300 transform hover:scale-105"> Download PDF Report </button>
<button onclick="resetApp()" class="bg-gray-600 text-white font-semibold py-3 px-6 rounded-lg shadow-md hover:bg-gray-700 transition duration-300 transform hover:scale-105"> Start New Test </button>
</div>
</div>
`;
renderQuestionStatusChart(correctCount, incorrectCount, unattemptedQuestions);
renderTimePerQuestionChart(allQuestionsFlat.map(q => q.timeTaken || 0)); // Pass timeTaken values
}
// Function to update visibility of Part tabs based on question availability
function updatePartTabVisibility() {
const targetPartTabs = inSolutionMode ? solutionPartTabs : partTabs;
targetPartTabs.forEach(tab => {
const partKey = tab.dataset.part;
if (allQuestionsByPart[partKey] && allQuestionsByPart[partKey].length > 0) {
tab.style.display = 'flex'; // Show the tab
} else {
tab.style.display = 'none'; // Hide the tab
}
});
}
// --- Solution Page Functions ---
function showSolutionPage() {
inSolutionMode = true;
resultPage.classList.add('hidden');
mocktestPage.classList.add('hidden');
solutionPage.classList.remove('hidden');
// Set completion time in solution header
const completionTime = testCompletionDateTime ? formatTime(testCompletionDateTime) : 'N/A';
solutionCompletionTimeEl.textContent = completionTime;
// Reset currentPart and questions for solution page to start from Part A
currentPart = 'A';
questions = allQuestionsByPart[currentPart];
currentQuestionIndex = 0;
solutionPartTabs.forEach(tab => tab.classList.remove('active'));
document.querySelector(`#solution-page .part-tab-button[data-part="${currentPart}"]`).classList.add('active');
solutionPartTitleEl.textContent = partNames[currentPart];
updatePartTabVisibility(); // Update visibility for solution page tabs
initializeQuestionGrid('solution'); // Initialize grid for solution mode
displayQuestion(0, 'solution'); // Display first question in solution mode
}
function navigateSolutionPrev() {
if (currentQuestionIndex > 0) {
displayQuestion(currentQuestionIndex - 1, 'solution');
} else {
showAlert("You are at the first question in this section.");
}
}
function navigateSolutionNext() {
if (currentQuestionIndex < questions.length - 1) {
displayQuestion(currentQuestionIndex + 1, 'solution');
} else {
const currentPartIndex = partOrder.indexOf(currentPart);
let nextPartFound = false;
for (let i = currentPartIndex + 1; i < partOrder.length; i++) {
const nextPartKey = partOrder[i];
if (allQuestionsByPart[nextPartKey] && allQuestionsByPart[nextPartKey].length > 0) {
currentPart = nextPartKey;
questions = allQuestionsByPart[currentPart];
currentQuestionIndex = 0;
solutionPartTabs.forEach(tab => tab.classList.remove('active'));
document.querySelector(`#solution-page .part-tab-button[data-part="${currentPart}"]`).classList.add('active');
solutionPartTitleEl.textContent = partNames[currentPart];
initializeQuestionGrid('solution');
displayQuestion(0, 'solution');
nextPartFound = true;
break;
}
}
if (!nextPartFound) {
showAlert("You have reached the last question in the solution. Click 'Back to Results' to go back.");
}
}
}
// --- Event Listeners ---
startMocktestBtn.addEventListener('click', async () => {
try {
currentTopic = topicInput.value.trim();
allQuestionsByPart['A'] = parseQuestions(questionInputPartA.value);
allQuestionsByPart['B'] = parseQuestions(questionInputPartB.value);
allQuestionsByPart['C'] = parseQuestions(questionInputPartC.value);
allQuestionsByPart['D'] = parseQuestions(questionInputPartD.value);
let firstPartWithQuestions = partOrder.find(partKey => allQuestionsByPart[partKey].length > 0);
if (!firstPartWithQuestions) {
showAlert("Please enter questions in at least one part to start the mock test.");
return;
}
currentPart = firstPartWithQuestions;
questions = allQuestionsByPart[currentPart];
// Initialize timeTaken for all questions across all parts
partOrder.forEach(partKey => {
allQuestionsByPart[partKey].forEach(q => {
q.userAnswer = null;
q.status = 'not-visited';
q.timeTaken = 0; // Ensure timeTaken is reset
});
});
// This array will now hold the start time for the *current session* on a question.
// The accumulated time is stored in question.timeTaken.
questionStartTimes = new Array(questions.length).fill(undefined);
currentQuestionIndex = 0;
totalTestStartTime = Date.now();
totalTestEndTime = undefined;
testCompletionDateTime = undefined;
inSolutionMode = false; // Ensure not in solution mode
inputPage.classList.add('hidden');
mocktestPage.classList.remove('hidden');
solutionPage.classList.add('hidden'); // Ensure solution page is hidden
partTabs.forEach(tab => tab.classList.remove('active'));
document.querySelector(`#mocktest-page .part-tab-button[data-part="${currentPart}"]`).classList.add('active');
partTitleEl.textContent = partNames[currentPart];
updatePartTabVisibility();
initializeQuestionGrid();
displayQuestion(0);
startTotalTestTimer();
} catch (error) {
console.error("Error starting test:", error);
showAlert("Failed to start test: " + error.message);
}
});
prevBtn.addEventListener('click', goToPreviousQuestion);
saveNextBtn.addEventListener('click', goToNextQuestionWithSave);
markReviewBtn.addEventListener('click', markForReviewToggle);
submitTestBtn.addEventListener('click', () => {
showAlert("Are you sure you want to submit the test?", "Confirm Submission", (confirmed) => {
if (confirmed) {
submitTest();
}
});
});
symbolsTab.addEventListener('click', () => {
symbolsTab.classList.add('active');
instructionsTab.classList.remove('active');
symbolsModal.style.display = 'flex';
isSymbolsModalOpen = true;
});
closeModalBtn.addEventListener('click', () => {
symbolsModal.style.display = 'none';
isSymbolsModalOpen = false;
});
window.addEventListener('click', (event) => {
if (event.target == symbolsModal) {
symbolsModal.style.display = 'none';
isSymbolsModalOpen = false;
}
});
instructionsTab.addEventListener('click', () => {
instructionsTab.classList.add('active');
symbolsTab.classList.remove('active');
showAlert("Instructions", "This is where the test instructions would be displayed. For this mock, please refer to the Symbols tab for color meanings.");
});
feedbackPrevBtn.addEventListener('click', navigateFeedbackPrev);
feedbackNextBtn.addEventListener('click', navigateFeedbackNext);
partTabs.forEach(tab => {
tab.addEventListener('click', () => {
saveCurrentQuestionTime(); // Save time for the question before switching parts
partTabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
currentPart = tab.dataset.part;
questions = allQuestionsByPart[currentPart];
if (questions.length === 0) {
showAlert(`No questions available for Part ${currentPart}.`);
}
currentQuestionIndex = 0; // Reset index for new part
partTitleEl.textContent = partNames[currentPart];
initializeQuestionGrid();
displayQuestion(0);
updateAnalysisCounts();
});
});
// Solution Page Part Tabs
solutionPartTabs.forEach(tab => {
tab.addEventListener('click', () => {
solutionPartTabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
currentPart = tab.dataset.part;
questions = allQuestionsByPart[currentPart];
if (questions.length === 0) {
showAlert(`No questions available for Part ${currentPart}.`);
}
currentQuestionIndex = 0;
solutionPartTitleEl.textContent = partNames[currentPart];
initializeQuestionGrid('solution');
displayQuestion(0, 'solution');
updateAnalysisCounts('solution');
});
});
// Zoom functionality
zoomInBtn.addEventListener('click', () => {
currentQuestionTextFontSize += 1;
currentOptionTextFontSize += 1;
displayQuestion(currentQuestionIndex, 'mocktest'); // Re-render with new font size
});
zoomOutBtn.addEventListener('click', () => {
if (currentQuestionTextFontSize > 10) { // Set a minimum font size
currentQuestionTextFontSize -= 1;
}
if (currentOptionTextFontSize > 8) { // Set a minimum font size
currentOptionTextFontSize -= 1;
}
displayQuestion(currentQuestionIndex, 'mocktest'); // Re-render with new font size
});
solutionZoomInBtn.addEventListener('click', () => {
currentQuestionTextFontSize += 1;
currentOptionTextFontSize += 1;
displayQuestion(currentQuestionIndex, 'solution'); // Re-render with new font size
});
solutionZoomOutBtn.addEventListener('click', () => {
if (currentQuestionTextFontSize > 10) {
currentQuestionTextFontSize -= 1;
}
if (currentOptionTextFontSize > 8) {
currentOptionTextFontSize -= 1;
}
displayQuestion(currentQuestionIndex, 'solution'); // Re-render with new font size
});
// View Solution button listener (added after result page HTML is rendered)
document.addEventListener('click', (event) => {
if (event.target && event.target.id === 'view-solution-btn') {
showSolutionPage();
}
});
// Solution page navigation buttons
solutionPrevBtn.addEventListener('click', navigateSolutionPrev);
solutionNextBtn.addEventListener('click', navigateSolutionNext);
backToResultBtn.addEventListener('click', () => {
solutionPage.classList.add('hidden');
resultPage.classList.remove('hidden');
inSolutionMode = false; // Exit solution mode
});
// Initial setup on page load
document.addEventListener('DOMContentLoaded', async () => {
await fetchQuestions(); // Populate input fields from API
updatePartTabVisibility(); // Set initial visibility of part tabs
// After fetching, try to set the initial questions for Part A
// This is crucial for the very first load to ensure 'questions' array is populated
if (allQuestionsByPart['A'].length > 0) {
currentPart = 'A';
questions = allQuestionsByPart[currentPart];
partTitleEl.textContent = partNames[currentPart];
initializeQuestionGrid();
displayQuestion(0);
} else {
// If Part A has no questions, find the first available part and set it
let firstAvailablePart = partOrder.find(partKey => allQuestionsByPart[partKey].length > 0);
if (firstAvailablePart) {
currentPart = firstAvailablePart;
questions = allQuestionsByPart[currentPart];
partTitleEl.textContent = partNames[currentPart];
partTabs.forEach(tab => tab.classList.remove('active'));
document.querySelector(`#mocktest-page .part-tab-button[data-part="${currentPart}"]`).classList.add('active');
initializeQuestionGrid();
displayQuestion(0);
} else {
// No questions in any part
questionNumberSpan.textContent = 'N/A';
questionTextCell.innerHTML = 'No questions available for any section. Please paste questions on the input page.';
optionsTableBody.innerHTML = '';
updateQuestionGrid();
updateAnalysisCounts();
}
}
// Initial render of math on the body for any static content
renderMath(document.body);
});
</script>
</body>
</html>
0 Comments