5m4ck3r commited on
Commit
d8322b8
·
verified ·
1 Parent(s): ba758fd

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +711 -115
index.html CHANGED
@@ -9,6 +9,7 @@
9
  <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
10
  <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
  <script src="https://unpkg.com/feather-icons"></script>
 
12
  <script>
13
  tailwind.config = {
14
  theme: {
@@ -23,6 +24,12 @@
23
  }
24
  </script>
25
  <style>
 
 
 
 
 
 
26
  .paper-container {
27
  width: 210mm;
28
  min-height: 297mm;
@@ -113,10 +120,56 @@
113
  color: white;
114
  border-color: #2563eb;
115
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  </style>
117
  </head>
118
  <body class="bg-gray-100 min-h-screen">
119
- <!-- Initial Setup Modal -->
120
  <div id="setupModal" class="popup-overlay">
121
  <div class="popup-content" data-aos="zoom-in">
122
  <h2 class="text-2xl font-bold text-center mb-6">Create Your Exam Paper</h2>
@@ -140,6 +193,14 @@
140
  <label class="block text-sm font-medium text-gray-700 mb-1">Total Time (Hour)</label>
141
  <input type="number" id="totalTime" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter total time">
142
  </div>
 
 
 
 
 
 
 
 
143
  <div class="flex justify-end pt-4">
144
  <button onclick="saveSetup()" class="bg-primary text-white px-6 py-2 rounded-md hover:bg-blue-700 transition">Create Paper</button>
145
  </div>
@@ -147,7 +208,6 @@
147
  </div>
148
  </div>
149
 
150
- <!-- Add Question Modal -->
151
  <div id="addQuestionModal" class="popup-overlay hidden">
152
  <div class="popup-content" data-aos="zoom-in">
153
  <h2 class="text-2xl font-bold text-center mb-6">Add Question</h2>
@@ -207,7 +267,6 @@
207
  </div>
208
  </div>
209
 
210
- <!-- Question Details Modal -->
211
  <div id="questionDetailsModal" class="popup-overlay hidden">
212
  <div class="popup-content" data-aos="zoom-in">
213
  <h2 id="detailsModalTitle" class="text-2xl font-bold text-center mb-6">Question Details</h2>
@@ -234,7 +293,6 @@
234
  </div>
235
  </div>
236
 
237
- <!-- Subquestion Add Modal -->
238
  <div id="subquestionModal" class="popup-overlay hidden">
239
  <div class="popup-content" data-aos="zoom-in">
240
  <h2 class="text-2xl font-bold text-center mb-6">Add Subquestion</h2>
@@ -249,50 +307,52 @@
249
  </div>
250
  </div>
251
 
252
- <!-- Main Content -->
253
  <div class="container mx-auto py-8">
254
- <!-- Header with controls -->
255
  <div class="flex justify-between items-center mb-6 px-4">
256
- <div>
257
- <div class="flex items-center space-x-4 mt-2 text-sm text-gray-600">
258
- <span id="timeDisplay">Time: --:--</span>
259
- <span id="totalMarksDisplay">Total Marks: 0</span>
260
- </div>
261
- </div>
262
  <div class="flex items-center space-x-3">
263
  <button onclick="showAddQuestionModal()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700 flex items-center">
264
  <i data-feather="plus" class="mr-2"></i> Add Question
265
  </button>
 
 
 
266
  <button id="downloadPdf" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 flex items-center">
267
  <i data-feather="download" class="mr-2"></i> Export PDF
268
  </button>
 
 
 
 
 
 
269
  </div>
270
  </div>
271
 
272
- <!-- Paper Container -->
273
  <div class="paper-container" id="paperContent">
274
  <!-- School and exam details -->
275
  <div class="text-center mb-8">
276
- <h2 id="schoolNameDisplay" class="text-xl font-bold uppercase"></h2>
277
- <h3 id="examNameDisplay" class="text-lg font-semibold mt-2"></h3>
 
 
 
 
 
 
278
  <div class="flex justify-between items-center mt-6 text-sm">
279
  <div>Time: <span id="paperTimeDisplay"></span></div>
280
  <div>Total Marks: <span id="paperTotalMarksDisplay">0</span></div>
281
  </div>
282
  <div class="border-b border-gray-300 mt-4"></div>
283
  </div>
284
-
285
- <!-- Questions will be added here -->
286
  <div id="questionsContainer"></div>
287
  </div>
288
 
289
- <!-- Page counter -->
290
  <div class="text-center mt-4 text-gray-600">
291
  Page <span id="currentPage">1</span> of <span id="totalPages">1</span>
292
  </div>
293
  </div>
294
 
295
- <!-- Text size controls -->
296
  <div class="text-size-control flex items-center space-x-2">
297
  <button onclick="decreaseTextSize()" class="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center hover:bg-gray-200">
298
  <i data-feather="minus"></i>
@@ -307,31 +367,93 @@
307
  let currentQuestionType = '';
308
  let questions = [];
309
  let textSize = 16;
 
 
 
310
  let paperData = {
311
  schoolName: '',
312
  examName: '',
313
  language: 'english',
314
  totalTime: 0,
 
315
  date: new Date()
316
  };
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  document.addEventListener('DOMContentLoaded', function() {
319
  AOS.init();
320
  feather.replace();
321
-
322
- // Check if we have saved data
323
  const savedData = localStorage.getItem('examPaperData');
324
  if (savedData) {
325
- paperData = JSON.parse(savedData);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  updatePaperHeader();
 
 
327
  document.getElementById('setupModal').classList.add('hidden');
328
  }
329
-
330
- // Update date and time displays
331
  updateDateTime();
332
- setInterval(updateDateTime, 60000); // Update every minute
333
  });
334
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  function getReadableQuestionTypeLabel(type) {
336
  const map = {
337
  'mcq': 'Multiple Choice',
@@ -367,8 +489,6 @@
367
  function updateDateTime() {
368
  const now = new Date();
369
  const timeStr = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
370
-
371
- document.getElementById('timeDisplay').textContent = `Time: ${timeStr}`;
372
  }
373
 
374
  function saveSetup() {
@@ -376,27 +496,35 @@
376
  const examName = document.getElementById('examName').value;
377
  const language = document.getElementById('language').value;
378
  const totalTime = document.getElementById('totalTime').value;
379
-
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  if (!schoolName || !examName || !totalTime) {
381
  alert('Please fill all required fields');
382
  return;
383
  }
384
-
385
  paperData = {
386
  schoolName,
387
  examName,
388
  language,
389
  totalTime: parseInt(totalTime),
 
 
390
  date: new Date()
391
  };
392
-
393
- // Save to localStorage
394
  localStorage.setItem('examPaperData', JSON.stringify(paperData));
395
-
396
- // Update the display
397
  updatePaperHeader();
398
-
399
- // Hide the setup modal
400
  document.getElementById('setupModal').classList.add('hidden');
401
  }
402
 
@@ -404,6 +532,17 @@
404
  document.getElementById('schoolNameDisplay').textContent = paperData.schoolName;
405
  document.getElementById('examNameDisplay').textContent = paperData.examName;
406
  document.getElementById('paperTimeDisplay').textContent = `${paperData.totalTime} Hours`;
 
 
 
 
 
 
 
 
 
 
 
407
  updateTotalMarks();
408
  }
409
 
@@ -547,14 +686,14 @@
547
  <div class="mb-4">
548
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Upload Image' : 'Question'}</label>
549
  ${currentQuestionType === 'imagefirst' ?
550
- `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md">` :
551
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="3" placeholder="Enter your question"></textarea>`}
552
  </div>
553
  <div class="mb-4">
554
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Question' : 'Upload Image'}</label>
555
  ${currentQuestionType === 'imagefirst' ?
556
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="3" placeholder="Enter your question"></textarea>` :
557
- `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md">`}
558
  </div>
559
  <div class="flex items-center">
560
  <input type="checkbox" id="imageAnswerOnPaper" class="mr-2">
@@ -642,7 +781,7 @@
642
  let questionData = {
643
  type: currentQuestionType,
644
  marks: marks,
645
- id: editId || Date.now()
646
  };
647
 
648
  // Collect data based on question type
@@ -670,11 +809,10 @@
670
  case 'imagefirst':
671
  case 'questionfirst':
672
  questionData.question = document.getElementById('questionText').value;
673
- questionData.hasImage = true;
674
- questionData.imagePlaceholder = true;
675
  questionData.answerOnPaper = document.getElementById('imageAnswerOnPaper').checked;
676
  break;
677
-
678
  case 'subquestion':
679
  const titleEl = document.getElementById('mainQuestionTitle');
680
  // Prefer the id we stored earlier
@@ -740,18 +878,125 @@
740
  }
741
 
742
  function openSubquestionModal() {
743
- // show the subquestion editor modal
744
  document.getElementById('questionDetailsModal').classList.add('hidden');
745
  document.getElementById('subquestionModal').classList.remove('hidden');
746
 
747
- // populate subquestionContainer from preview (#subquestionsUI) so user can edit
748
- const details = document.getElementById('subquestionsUI');
749
  const container = document.getElementById('subquestionContainer');
750
  container.innerHTML = '';
751
 
752
- if (details && details.children.length) {
753
- Array.from(details.children).forEach(child => {
754
- container.appendChild(child.cloneNode(true));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755
  });
756
  } else {
757
  container.innerHTML = `
@@ -1097,14 +1342,34 @@
1097
  }
1098
 
1099
  function renderQuestions() {
 
 
 
 
1100
  const container = document.getElementById('questionsContainer');
1101
  container.innerHTML = '';
1102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1103
  questions.forEach((q, index) => {
1104
  const questionElement = document.createElement('div');
1105
  questionElement.className = 'question-item';
1106
  questionElement.setAttribute('data-id', q.id);
1107
 
 
1108
  let questionHTML = `
1109
  <div class="flex justify-between items-start mb-2">
1110
  <div class="font-semibold">
@@ -1139,30 +1404,20 @@
1139
  </div>
1140
  `;
1141
  if (q.answerOnPaper) {
1142
- questionHTML += `
1143
- <div class="mt-3 flex space-x-6">
1144
- <div>i) □</div>
1145
- <div>ii) □</div>
1146
- <div>iii) □</div>
1147
- <div>iv) □</div>
1148
- </div>
1149
- `;
1150
  }
1151
  break;
1152
-
1153
  case 'normal':
1154
  if (q.answerOnPaper) {
1155
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1156
  }
1157
  break;
1158
-
1159
  case 'truefalse':
1160
  break;
1161
-
1162
  case 'imagefirst':
1163
  questionHTML += `
1164
  <div class="mb-3">
1165
- <img src="http://static.photos/education/640x360/${index + 10}" class="image-preview">
1166
  </div>
1167
  <div class="mb-2">${q.question}</div>
1168
  `;
@@ -1170,35 +1425,25 @@
1170
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1171
  }
1172
  break;
1173
-
1174
  case 'questionfirst':
1175
  questionHTML += `
1176
  <div class="mb-3">
1177
- <img src="http://static.photos/education/640x360/${index + 20}" class="image-preview">
1178
  </div>
1179
  `;
1180
  if (q.answerOnPaper) {
1181
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1182
  }
1183
  break;
1184
-
1185
  case 'subquestion':
1186
- // Main title with spacing
1187
  if (q.subquestions && q.subquestions.length) {
1188
  questionHTML += `<div class="subquestions-container ml-6">`;
1189
-
1190
- // Roman numerals for numbering
1191
  const romanNumerals = ['i','ii','iii','iv','v','vi','vii','viii','ix','x'];
1192
-
1193
  q.subquestions.forEach((sq, sqIndex) => {
1194
  const label = romanNumerals[sqIndex] || (sqIndex + 1);
1195
-
1196
  questionHTML += `<div class="mb-3">`;
1197
- // layout: left = question content, right = marks
1198
  questionHTML += `<div class="flex justify-between">`;
1199
  questionHTML += `<div class="w-full">`;
1200
-
1201
- // render by subquestion type (mirror top-level rendering)
1202
  switch (sq.type) {
1203
  case 'mcq':
1204
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
@@ -1212,48 +1457,34 @@
1212
  questionHTML += `<div class="mt-3 flex space-x-6"><div>i) □</div><div>ii) □</div><div>iii) □</div><div>iv) □</div></div>`;
1213
  }
1214
  break;
1215
-
1216
  case 'normal':
1217
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1218
  if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1219
  break;
1220
-
1221
  case 'truefalse':
1222
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''} (True / False)</div>`;
1223
  break;
1224
-
1225
  case 'imagefirst':
1226
- // show image first then question (use stored imageData if available; else placeholder)
1227
- questionHTML += `<div class="mb-3">`;
1228
- questionHTML += `<img src="${sq.imageData || `http://static.photos/education/640x360/${index + 10}-${sqIndex}`}" class="image-preview">`;
1229
- questionHTML += `</div>`;
1230
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1231
  if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1232
  break;
1233
-
1234
  case 'questionfirst':
1235
- // question then image
1236
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1237
- questionHTML += `<div class="mb-3">`;
1238
- questionHTML += `<img src="${sq.imageData || `http://static.photos/education/640x360/${index + 20}-${sqIndex}`}" class="image-preview">`;
1239
- questionHTML += `</div>`;
1240
  if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1241
  break;
1242
-
1243
  case 'questionspace':
1244
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1245
  questionHTML += `<div class="mt-2" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1246
  break;
1247
-
1248
  case 'spacequestion':
1249
  questionHTML += `<div class="mb-2" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1250
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1251
  break;
1252
-
1253
  case 'blank':
1254
  questionHTML += `<div class="my-4" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1255
  break;
1256
-
1257
  case 'orquestion':
1258
  questionHTML += `<div class="font-medium">${label})</div>`;
1259
  questionHTML += `<div class="space-y-3">`;
@@ -1263,33 +1494,23 @@
1263
  });
1264
  questionHTML += `</div>`;
1265
  break;
1266
-
1267
  default:
1268
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1269
  }
1270
-
1271
- questionHTML += `</div>`; // close left side (w-full)
1272
-
1273
- // marks on the right
1274
  questionHTML += `<div class="ml-4"><span class="text-sm text-blue-800 px-2 py-1 rounded">${sq.marks || 1} marks</span></div>`;
1275
-
1276
- questionHTML += `</div>`; // close flex justify-between
1277
- questionHTML += `</div>`; // close mb-3
1278
  });
1279
-
1280
- questionHTML += `</div>`; // close subquestions-container
1281
  }
1282
  break;
1283
-
1284
  case 'questionspace':
1285
- questionHTML += `
1286
- <div class="mt-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>
1287
- `;
1288
  if (q.answerOnPaper) {
1289
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1290
  }
1291
  break;
1292
-
1293
  case 'spacequestion':
1294
  questionHTML += `
1295
  <div class="mb-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>
@@ -1299,20 +1520,13 @@
1299
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1300
  }
1301
  break;
1302
-
1303
  case 'blank':
1304
  questionHTML += `<div class="my-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1305
  break;
1306
-
1307
  case 'orquestion':
1308
  questionHTML += `<div class="space-y-3">`;
1309
  q.options.forEach((opt, optIndex) => {
1310
- questionHTML += `
1311
- <div class="flex items-start">
1312
- <div class="mr-2 font-medium">${optIndex + 1}.</div>
1313
- <div>${opt.text}</div>
1314
- </div>
1315
- `;
1316
  if (optIndex < q.options.length - 1) {
1317
  questionHTML += `<div class="text-center font-bold">OR</div>`;
1318
  }
@@ -1322,10 +1536,33 @@
1322
  }
1323
 
1324
  questionElement.innerHTML = questionHTML;
1325
- container.appendChild(questionElement);
 
 
 
 
 
 
 
 
 
 
 
 
1326
  });
1327
-
 
 
 
 
 
1328
  feather.replace();
 
 
 
 
 
 
1329
  }
1330
 
1331
  function getQuestionTypeLabel(type) {
@@ -1356,7 +1593,7 @@
1356
  }
1357
 
1358
  function editQuestion(id) {
1359
- const question = questions.find(q => q.id === id);
1360
  if (!question) return;
1361
 
1362
  currentQuestionType = question.type;
@@ -1423,14 +1660,14 @@
1423
  <div class="mb-4">
1424
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Upload Image' : 'Question'}</label>
1425
  ${currentQuestionType === 'imagefirst' ?
1426
- `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md">` :
1427
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>`}
1428
  </div>
1429
  <div class="mb-4">
1430
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Question' : 'Upload Image'}</label>
1431
  ${currentQuestionType === 'imagefirst' ?
1432
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>` :
1433
- `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md">`}
1434
  </div>
1435
  <div class="flex items-center"><input type="checkbox" id="imageAnswerOnPaper" ${question.answerOnPaper ? 'checked' : ''} class="mr-2"><label class="text-sm text-gray-700">Students answer on question paper</label></div>
1436
  `;
@@ -1503,6 +1740,10 @@
1503
 
1504
  function deleteQuestion(id) {
1505
  if (confirm('Are you sure you want to delete this question?')) {
 
 
 
 
1506
  questions = questions.filter(q => q.id !== id);
1507
  renderQuestions();
1508
  updateTotalMarks();
@@ -1532,6 +1773,14 @@
1532
  document.getElementById("downloadPdf").addEventListener("click", async () => {
1533
  document.querySelectorAll('#paperContent svg').forEach(el => el.style.display = 'none');
1534
  feather.replace();
 
 
 
 
 
 
 
 
1535
  const { jsPDF } = window.jspdf;
1536
 
1537
  const pdf = new jsPDF('p', 'pt', 'a4'); // Use mm units for easier calculations
@@ -1540,7 +1789,7 @@
1540
 
1541
  await pdf.html(element, {
1542
  callback: function (pdf) {
1543
- pdf.save("section.pdf");
1544
  },
1545
  x: 5,
1546
  y: 5,
@@ -1550,5 +1799,352 @@
1550
  document.querySelectorAll('#paperContent svg').forEach(el => el.style.display = '');
1551
  });
1552
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1553
  </body>
1554
  </html>
 
9
  <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
10
  <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
  <script src="https://unpkg.com/feather-icons"></script>
12
+ <script src="https://js.puter.com/v2/"></script>
13
  <script>
14
  tailwind.config = {
15
  theme: {
 
24
  }
25
  </script>
26
  <style>
27
+
28
+ #paperContent {
29
+ transition: transform 0.2s ease;
30
+ transform-origin: top center;
31
+ }
32
+
33
  .paper-container {
34
  width: 210mm;
35
  min-height: 297mm;
 
120
  color: white;
121
  border-color: #2563eb;
122
  }
123
+
124
+ .paper-container {
125
+ width: 210mm;
126
+ min-height: 297mm;
127
+ padding: 20mm;
128
+ margin: 10mm auto;
129
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
130
+ background: white;
131
+ box-sizing: border-box;
132
+ }
133
+
134
+ .question-item {
135
+ break-inside: avoid;
136
+ page-break-inside: avoid;
137
+ }
138
+
139
+ #questionsContainer {
140
+ break-after: always;
141
+ page-break-after: always;
142
+ }
143
+
144
+ .paper-container + .paper-container {
145
+ margin-top: 20mm;
146
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
147
+ }
148
+
149
+ .image-preview {
150
+ max-width: 100%;
151
+ max-height: 200px;
152
+ border-radius: 6px;
153
+ margin: 10px 0;
154
+ }
155
+
156
+ #paperContent img {
157
+ width: auto;
158
+ height: auto;
159
+ max-width: 100%;
160
+ max-height: 200px;
161
+ }
162
+
163
+ #logoContainer img {
164
+ max-height: 64px;
165
+ height: auto;
166
+ width: auto;
167
+ }
168
+
169
  </style>
170
  </head>
171
  <body class="bg-gray-100 min-h-screen">
172
+
173
  <div id="setupModal" class="popup-overlay">
174
  <div class="popup-content" data-aos="zoom-in">
175
  <h2 class="text-2xl font-bold text-center mb-6">Create Your Exam Paper</h2>
 
193
  <label class="block text-sm font-medium text-gray-700 mb-1">Total Time (Hour)</label>
194
  <input type="number" id="totalTime" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter total time">
195
  </div>
196
+ <div>
197
+ <label class="block text-sm font-medium text-gray-700 mb-1">School Logo (Optional)</label>
198
+ <input type="file" id="schoolLogo" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary">
199
+ </div>
200
+ <div>
201
+ <label class="block text-sm font-medium text-gray-700 mb-1">Important Notes (Optional)</label>
202
+ <textarea id="importantNotes" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="3" placeholder="e.g., Use of calculator not allowed, Show all steps, etc."></textarea>
203
+ </div>
204
  <div class="flex justify-end pt-4">
205
  <button onclick="saveSetup()" class="bg-primary text-white px-6 py-2 rounded-md hover:bg-blue-700 transition">Create Paper</button>
206
  </div>
 
208
  </div>
209
  </div>
210
 
 
211
  <div id="addQuestionModal" class="popup-overlay hidden">
212
  <div class="popup-content" data-aos="zoom-in">
213
  <h2 class="text-2xl font-bold text-center mb-6">Add Question</h2>
 
267
  </div>
268
  </div>
269
 
 
270
  <div id="questionDetailsModal" class="popup-overlay hidden">
271
  <div class="popup-content" data-aos="zoom-in">
272
  <h2 id="detailsModalTitle" class="text-2xl font-bold text-center mb-6">Question Details</h2>
 
293
  </div>
294
  </div>
295
 
 
296
  <div id="subquestionModal" class="popup-overlay hidden">
297
  <div class="popup-content" data-aos="zoom-in">
298
  <h2 class="text-2xl font-bold text-center mb-6">Add Subquestion</h2>
 
307
  </div>
308
  </div>
309
 
 
310
  <div class="container mx-auto py-8">
 
311
  <div class="flex justify-between items-center mb-6 px-4">
 
 
 
 
 
 
312
  <div class="flex items-center space-x-3">
313
  <button onclick="showAddQuestionModal()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700 flex items-center">
314
  <i data-feather="plus" class="mr-2"></i> Add Question
315
  </button>
316
+ <button onclick="confirmNewPaper()" class="bg-gray-600 text-white px-4 py-2 rounded-md hover:bg-gray-700 flex items-center">
317
+ <i data-feather="file-plus" class="mr-2"></i> New Paper
318
+ </button>
319
  <button id="downloadPdf" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 flex items-center">
320
  <i data-feather="download" class="mr-2"></i> Export PDF
321
  </button>
322
+ <button onclick="useofAI()" id="useAI" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 flex items-center">
323
+ <i data-feather="book" class="mr-2"></i> AI
324
+ </button>
325
+ <button onclick="editPaperDetails()" class="bg-amber-600 text-white px-4 py-2 rounded-md hover:bg-amber-700 flex items-center">
326
+ <i data-feather="edit" class="mr-2"></i> Edit Paper
327
+ </button>
328
  </div>
329
  </div>
330
 
 
331
  <div class="paper-container" id="paperContent">
332
  <!-- School and exam details -->
333
  <div class="text-center mb-8">
334
+ <div class="flex items-start justify-between">
335
+ <div id="logoContainer" class="mr-4"></div>
336
+ <div class="text-center flex-1">
337
+ <h2 id="schoolNameDisplay" class="text-xl font-bold uppercase"></h2>
338
+ <h3 id="examNameDisplay" class="text-lg font-semibold mt-2"></h3>
339
+ </div>
340
+ </div>
341
+ <div id="importantNotesSection" class="mt-4 text-sm italic text-gray-700 text-left"></div>
342
  <div class="flex justify-between items-center mt-6 text-sm">
343
  <div>Time: <span id="paperTimeDisplay"></span></div>
344
  <div>Total Marks: <span id="paperTotalMarksDisplay">0</span></div>
345
  </div>
346
  <div class="border-b border-gray-300 mt-4"></div>
347
  </div>
 
 
348
  <div id="questionsContainer"></div>
349
  </div>
350
 
 
351
  <div class="text-center mt-4 text-gray-600">
352
  Page <span id="currentPage">1</span> of <span id="totalPages">1</span>
353
  </div>
354
  </div>
355
 
 
356
  <div class="text-size-control flex items-center space-x-2">
357
  <button onclick="decreaseTextSize()" class="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center hover:bg-gray-200">
358
  <i data-feather="minus"></i>
 
367
  let currentQuestionType = '';
368
  let questions = [];
369
  let textSize = 16;
370
+ let paperScale = 1.0;
371
+ let baseFontSize = 16;
372
+ let currentScale = 1.0;
373
  let paperData = {
374
  schoolName: '',
375
  examName: '',
376
  language: 'english',
377
  totalTime: 0,
378
+ importantNotes: '', // ← added
379
  date: new Date()
380
  };
381
 
382
+ document.addEventListener('DOMContentLoaded', () => {
383
+ document.getElementById('questionDetailsModal').addEventListener('change', function(e) {
384
+ if (e.target.id === 'imageUpload') {
385
+ const file = e.target.files[0];
386
+ if (file) {
387
+ const reader = new FileReader();
388
+ reader.onload = (event) => {
389
+ document.getElementById('imageDataHidden').value = event.target.result; // store Base64
390
+ };
391
+ reader.readAsDataURL(file);
392
+ } else {
393
+ document.getElementById('imageDataHidden').value = '';
394
+ }
395
+ }
396
+ });
397
+ });
398
+
399
  document.addEventListener('DOMContentLoaded', function() {
400
  AOS.init();
401
  feather.replace();
 
 
402
  const savedData = localStorage.getItem('examPaperData');
403
  if (savedData) {
404
+ try {
405
+ paperData = JSON.parse(savedData);
406
+ // Ensure questions is always an array
407
+ if (Array.isArray(paperData.questions)) {
408
+ questions = paperData.questions;
409
+ } else {
410
+ console.warn('Invalid questions format in localStorage. Resetting to empty array.');
411
+ questions = [];
412
+ paperData.questions = questions;
413
+ }
414
+ } catch (e) {
415
+ console.error('Failed to parse saved data', e);
416
+ questions = [];
417
+ paperData = { schoolName: '', examName: '', language: 'english', totalTime: 0, date: new Date(), questions: [] };
418
+ }
419
  updatePaperHeader();
420
+ renderLogo();
421
+ renderQuestions();
422
  document.getElementById('setupModal').classList.add('hidden');
423
  }
 
 
424
  updateDateTime();
425
+ setInterval(updateDateTime, 60000);
426
  });
427
 
428
+ function confirmNewPaper() {
429
+ if (confirm('Are you sure you want to start a new question paper? All current data will be lost.')) {
430
+ // Clear data
431
+ questions = [];
432
+ paperData = {
433
+ schoolName: '',
434
+ examName: '',
435
+ language: 'english',
436
+ totalTime: 0,
437
+ date: new Date()
438
+ };
439
+ // Clear localStorage
440
+ localStorage.removeItem('examPaperData');
441
+ // Reset UI
442
+ document.getElementById('questionsContainer').innerHTML = '';
443
+ document.getElementById('paperTotalMarksDisplay').textContent = '0';
444
+ document.getElementById('setupModal').classList.remove('hidden');
445
+ }
446
+ }
447
+
448
+ function renderLogo() {
449
+ const logoContainer = document.getElementById('logoContainer');
450
+ if (paperData.logo) {
451
+ logoContainer.innerHTML = `<img src="${paperData.logo}" alt="School Logo" class="h-16 object-contain">`;
452
+ } else {
453
+ logoContainer.innerHTML = '';
454
+ }
455
+ }
456
+
457
  function getReadableQuestionTypeLabel(type) {
458
  const map = {
459
  'mcq': 'Multiple Choice',
 
489
  function updateDateTime() {
490
  const now = new Date();
491
  const timeStr = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
 
 
492
  }
493
 
494
  function saveSetup() {
 
496
  const examName = document.getElementById('examName').value;
497
  const language = document.getElementById('language').value;
498
  const totalTime = document.getElementById('totalTime').value;
499
+ const importantNotes = document.getElementById('importantNotes').value;
500
+ const logoFile = document.getElementById('schoolLogo').files[0];
501
+ let logoData = null;
502
+ if (logoFile) {
503
+ const reader = new FileReader();
504
+ reader.onload = (e) => {
505
+ paperData.logo = e.target.result;
506
+ localStorage.setItem('examPaperData', JSON.stringify(paperData));
507
+ renderLogo();
508
+ };
509
+ reader.readAsDataURL(logoFile);
510
+ } else {
511
+ paperData.logo = null;
512
+ }
513
  if (!schoolName || !examName || !totalTime) {
514
  alert('Please fill all required fields');
515
  return;
516
  }
 
517
  paperData = {
518
  schoolName,
519
  examName,
520
  language,
521
  totalTime: parseInt(totalTime),
522
+ importantNotes,
523
+ logo: paperData.logo, // preserve existing logo if no new one uploaded
524
  date: new Date()
525
  };
 
 
526
  localStorage.setItem('examPaperData', JSON.stringify(paperData));
 
 
527
  updatePaperHeader();
 
 
528
  document.getElementById('setupModal').classList.add('hidden');
529
  }
530
 
 
532
  document.getElementById('schoolNameDisplay').textContent = paperData.schoolName;
533
  document.getElementById('examNameDisplay').textContent = paperData.examName;
534
  document.getElementById('paperTimeDisplay').textContent = `${paperData.totalTime} Hours`;
535
+
536
+ const notesSection = document.getElementById('importantNotesSection');
537
+ if (paperData.importantNotes && paperData.importantNotes.trim()) {
538
+ const formattedNotes = paperData.importantNotes.replace(/\n/g, '<br>');
539
+ notesSection.innerHTML = `<strong>Notes:</strong> ${formattedNotes}`;
540
+ notesSection.classList.remove('hidden');
541
+ } else {
542
+ notesSection.innerHTML = '';
543
+ notesSection.classList.add('hidden');
544
+ }
545
+
546
  updateTotalMarks();
547
  }
548
 
 
686
  <div class="mb-4">
687
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Upload Image' : 'Question'}</label>
688
  ${currentQuestionType === 'imagefirst' ?
689
+ `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md"><input type="hidden" id="imageDataHidden" value="">`:
690
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="3" placeholder="Enter your question"></textarea>`}
691
  </div>
692
  <div class="mb-4">
693
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Question' : 'Upload Image'}</label>
694
  ${currentQuestionType === 'imagefirst' ?
695
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="3" placeholder="Enter your question"></textarea>` :
696
+ `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md"><input type="hidden" id="imageDataHidden" value="">`}
697
  </div>
698
  <div class="flex items-center">
699
  <input type="checkbox" id="imageAnswerOnPaper" class="mr-2">
 
781
  let questionData = {
782
  type: currentQuestionType,
783
  marks: marks,
784
+ id: editId ? parseInt(editId) : Date.now()
785
  };
786
 
787
  // Collect data based on question type
 
809
  case 'imagefirst':
810
  case 'questionfirst':
811
  questionData.question = document.getElementById('questionText').value;
812
+ questionData.imageData = document.getElementById('imageDataHidden').value || null;
 
813
  questionData.answerOnPaper = document.getElementById('imageAnswerOnPaper').checked;
814
  break;
815
+
816
  case 'subquestion':
817
  const titleEl = document.getElementById('mainQuestionTitle');
818
  // Prefer the id we stored earlier
 
878
  }
879
 
880
  function openSubquestionModal() {
 
881
  document.getElementById('questionDetailsModal').classList.add('hidden');
882
  document.getElementById('subquestionModal').classList.remove('hidden');
883
 
 
 
884
  const container = document.getElementById('subquestionContainer');
885
  container.innerHTML = '';
886
 
887
+ // Get the parent question being edited
888
+ const editId = document.getElementById('questionDetailsModal').getAttribute('data-edit-id');
889
+ const newId = document.getElementById('questionDetailsModal').getAttribute('data-new-subquestion-id');
890
+ const parentId = editId ? parseInt(editId) : (newId ? parseInt(newId) : null);
891
+
892
+ let parentQuestion = null;
893
+ if (parentId !== null) {
894
+ parentQuestion = questions.find(q => q.id === parentId && q.type === 'subquestion');
895
+ }
896
+
897
+ if (parentQuestion && parentQuestion.subquestions && parentQuestion.subquestions.length > 0) {
898
+ // Rebuild editor UI from actual subquestion data
899
+ parentQuestion.subquestions.forEach(sq => {
900
+ const subquestionId = Date.now() + Math.floor(Math.random() * 1000);
901
+ const item = document.createElement('div');
902
+ item.className = 'subquestion-item p-4 border border-gray-200 rounded-md mb-3';
903
+ item.setAttribute('data-id', subquestionId);
904
+ item.setAttribute('data-type', sq.type);
905
+
906
+ const readable = getReadableQuestionTypeLabel(sq.type);
907
+ let html = `
908
+ <div class="flex justify-between items-start mb-3">
909
+ <h4 class="font-semibold">${readable}</h4>
910
+ <button onclick="removeSubquestion(this)" class="text-red-600 hover:text-red-800">
911
+ <i data-feather="trash-2" class="w-4 h-4"></i>
912
+ </button>
913
+ </div>
914
+ `;
915
+
916
+ // Reconstruct input fields with saved values
917
+ switch (sq.type) {
918
+ case 'mcq':
919
+ html += `
920
+ <div class="mb-3">
921
+ <label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
922
+ <textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
923
+ </div>
924
+ <div class="grid grid-cols-2 gap-3 mb-3">
925
+ <div><label class="text-sm text-gray-600 block mb-1">i)</label><input type="text" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.options?.[0] || ''}"></div>
926
+ <div><label class="text-sm text-gray-600 block mb-1">ii)</label><input type="text" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.options?.[1] || ''}"></div>
927
+ <div><label class="text-sm text-gray-600 block mb-1">iii)</label><input type="text" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.options?.[2] || ''}"></div>
928
+ <div><label class="text-sm text-gray-600 block mb-1">iv)</label><input type="text" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.options?.[3] || ''}"></div>
929
+ </div>
930
+ `;
931
+ break;
932
+ case 'normal':
933
+ case 'truefalse':
934
+ html += `
935
+ <div class="mb-3">
936
+ <label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
937
+ <textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
938
+ </div>
939
+ `;
940
+ break;
941
+ case 'imagefirst':
942
+ case 'questionfirst':
943
+ html += `
944
+ <div class="mb-3">
945
+ <label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
946
+ <textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
947
+ </div>
948
+ <div class="mb-3">
949
+ <label class="block text-sm font-medium text-gray-700 mb-1">Image</label>
950
+ <input type="file" class="w-full px-3 py-1 border border-gray-300 rounded-md">
951
+ ${sq.imageData ? `<img src="${sq.imageData}" class="image-preview mt-2" style="max-height:100px;">` : ''}
952
+ </div>
953
+ `;
954
+ break;
955
+ case 'questionspace':
956
+ case 'spacequestion':
957
+ html += `
958
+ <div class="mb-3">
959
+ <label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
960
+ <textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
961
+ </div>
962
+ <div class="mb-3">
963
+ <label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
964
+ <input type="number" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.spaceHeight || 5}" min="1">
965
+ </div>
966
+ `;
967
+ break;
968
+ case 'blank':
969
+ html += `
970
+ <div class="mb-3">
971
+ <label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
972
+ <input type="number" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.spaceHeight || 5}" min="1">
973
+ </div>
974
+ `;
975
+ break;
976
+ case 'orquestion':
977
+ html += `
978
+ <div class="space-y-3 mb-3">
979
+ ${(sq.options || []).map((opt, i) => `
980
+ <div>
981
+ <label class="block text-sm font-medium text-gray-700 mb-1">Option ${i + 1}</label>
982
+ <textarea class="w-full px-3 py-1 border border-gray-300 rounded-md" rows="1">${opt.text || ''}</textarea>
983
+ </div>
984
+ `).join('')}
985
+ </div>
986
+ <button onclick="addOrOptionToSubquestion(this)" class="text-primary text-sm hover:underline">+ Add another option</button>
987
+ `;
988
+ break;
989
+ }
990
+
991
+ html += `
992
+ <div class="mt-3">
993
+ <label class="block text-sm font-medium text-gray-700 mb-1">Marks</label>
994
+ <input type="number" class="w-20 px-3 py-1 border border-gray-300 rounded-md" value="${sq.marks || 1}" min="1">
995
+ </div>
996
+ `;
997
+
998
+ item.innerHTML = html;
999
+ container.appendChild(item);
1000
  });
1001
  } else {
1002
  container.innerHTML = `
 
1342
  }
1343
 
1344
  function renderQuestions() {
1345
+ if (!Array.isArray(questions)) {
1346
+ console.error('questions is not an array in renderQuestions. Resetting.');
1347
+ questions = [];
1348
+ }
1349
  const container = document.getElementById('questionsContainer');
1350
  container.innerHTML = '';
1351
 
1352
+ // Create a temporary container to measure height
1353
+ const tempContainer = document.createElement('div');
1354
+ tempContainer.style.width = '210mm';
1355
+ tempContainer.style.padding = '20mm';
1356
+ tempContainer.style.boxSizing = 'border-box';
1357
+ tempContainer.style.position = 'absolute';
1358
+ tempContainer.style.left = '-9999px';
1359
+ tempContainer.style.top = '0';
1360
+ document.body.appendChild(tempContainer);
1361
+
1362
+ let currentPage = document.createElement('div');
1363
+ currentPage.className = 'paper-page';
1364
+ let currentPageHeight = 0;
1365
+ const pageHeight = 297 * 4; // ~297mm in pixels (approx 4px per mm)
1366
+
1367
  questions.forEach((q, index) => {
1368
  const questionElement = document.createElement('div');
1369
  questionElement.className = 'question-item';
1370
  questionElement.setAttribute('data-id', q.id);
1371
 
1372
+ // Build question HTML (same as before)
1373
  let questionHTML = `
1374
  <div class="flex justify-between items-start mb-2">
1375
  <div class="font-semibold">
 
1404
  </div>
1405
  `;
1406
  if (q.answerOnPaper) {
1407
+ questionHTML += `<div class="mt-3 flex space-x-6"><div>i) □</div><div>ii) □</div><div>iii) □</div><div>iv) □</div></div>`;
 
 
 
 
 
 
 
1408
  }
1409
  break;
 
1410
  case 'normal':
1411
  if (q.answerOnPaper) {
1412
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1413
  }
1414
  break;
 
1415
  case 'truefalse':
1416
  break;
 
1417
  case 'imagefirst':
1418
  questionHTML += `
1419
  <div class="mb-3">
1420
+ ${q.imageData ? `<img src="${q.imageData}" class="image-preview">` : '<div class="text-gray-500 italic">No image uploaded</div>'}
1421
  </div>
1422
  <div class="mb-2">${q.question}</div>
1423
  `;
 
1425
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1426
  }
1427
  break;
 
1428
  case 'questionfirst':
1429
  questionHTML += `
1430
  <div class="mb-3">
1431
+ ${q.imageData ? `<img src="${q.imageData}" class="image-preview">` : '<div class="text-gray-500 italic">No image uploaded</div>'}
1432
  </div>
1433
  `;
1434
  if (q.answerOnPaper) {
1435
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1436
  }
1437
  break;
 
1438
  case 'subquestion':
 
1439
  if (q.subquestions && q.subquestions.length) {
1440
  questionHTML += `<div class="subquestions-container ml-6">`;
 
 
1441
  const romanNumerals = ['i','ii','iii','iv','v','vi','vii','viii','ix','x'];
 
1442
  q.subquestions.forEach((sq, sqIndex) => {
1443
  const label = romanNumerals[sqIndex] || (sqIndex + 1);
 
1444
  questionHTML += `<div class="mb-3">`;
 
1445
  questionHTML += `<div class="flex justify-between">`;
1446
  questionHTML += `<div class="w-full">`;
 
 
1447
  switch (sq.type) {
1448
  case 'mcq':
1449
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
 
1457
  questionHTML += `<div class="mt-3 flex space-x-6"><div>i) □</div><div>ii) □</div><div>iii) □</div><div>iv) □</div></div>`;
1458
  }
1459
  break;
 
1460
  case 'normal':
1461
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1462
  if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1463
  break;
 
1464
  case 'truefalse':
1465
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''} (True / False)</div>`;
1466
  break;
 
1467
  case 'imagefirst':
1468
+ questionHTML += `<div class="mb-3">${sq.imageData ? `<img src="${sq.imageData}" class="image-preview">` : '<span class="text-gray-500">No image</span>'}</div>`;
 
 
 
1469
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1470
  if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1471
  break;
 
1472
  case 'questionfirst':
 
1473
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1474
+ questionHTML += `<div class="mb-3">${sq.imageData ? `<img src="${sq.imageData}" class="image-preview">` : '<span class="text-gray-500">No image</span>'}</div>`;
 
 
1475
  if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1476
  break;
 
1477
  case 'questionspace':
1478
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1479
  questionHTML += `<div class="mt-2" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1480
  break;
 
1481
  case 'spacequestion':
1482
  questionHTML += `<div class="mb-2" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1483
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1484
  break;
 
1485
  case 'blank':
1486
  questionHTML += `<div class="my-4" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1487
  break;
 
1488
  case 'orquestion':
1489
  questionHTML += `<div class="font-medium">${label})</div>`;
1490
  questionHTML += `<div class="space-y-3">`;
 
1494
  });
1495
  questionHTML += `</div>`;
1496
  break;
 
1497
  default:
1498
  questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
1499
  }
1500
+ questionHTML += `</div>`;
 
 
 
1501
  questionHTML += `<div class="ml-4"><span class="text-sm text-blue-800 px-2 py-1 rounded">${sq.marks || 1} marks</span></div>`;
1502
+ questionHTML += `</div>`;
1503
+ questionHTML += `</div>`;
 
1504
  });
1505
+ questionHTML += `</div>`;
 
1506
  }
1507
  break;
 
1508
  case 'questionspace':
1509
+ questionHTML += `<div class="mt-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
 
 
1510
  if (q.answerOnPaper) {
1511
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1512
  }
1513
  break;
 
1514
  case 'spacequestion':
1515
  questionHTML += `
1516
  <div class="mb-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>
 
1520
  questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
1521
  }
1522
  break;
 
1523
  case 'blank':
1524
  questionHTML += `<div class="my-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
1525
  break;
 
1526
  case 'orquestion':
1527
  questionHTML += `<div class="space-y-3">`;
1528
  q.options.forEach((opt, optIndex) => {
1529
+ questionHTML += `<div class="flex items-start"><div class="mr-2 font-medium">${optIndex + 1}.</div><div>${opt.text}</div></div>`;
 
 
 
 
 
1530
  if (optIndex < q.options.length - 1) {
1531
  questionHTML += `<div class="text-center font-bold">OR</div>`;
1532
  }
 
1536
  }
1537
 
1538
  questionElement.innerHTML = questionHTML;
1539
+ tempContainer.appendChild(questionElement.cloneNode(true));
1540
+ const questionHeight = questionElement.offsetHeight + 30; // + margin
1541
+
1542
+ if (currentPageHeight + questionHeight > pageHeight && currentPage.children.length > 0) {
1543
+ // Start new page
1544
+ container.appendChild(currentPage);
1545
+ currentPage = document.createElement('div');
1546
+ currentPage.className = 'paper-page';
1547
+ currentPageHeight = 0;
1548
+ }
1549
+
1550
+ currentPage.appendChild(questionElement);
1551
+ currentPageHeight += questionHeight;
1552
  });
1553
+
1554
+ if (currentPage.children.length > 0) {
1555
+ container.appendChild(currentPage);
1556
+ }
1557
+
1558
+ document.body.removeChild(tempContainer);
1559
  feather.replace();
1560
+
1561
+ // Auto-save
1562
+ if (paperData.schoolName) {
1563
+ paperData.questions = questions;
1564
+ localStorage.setItem('examPaperData', JSON.stringify(paperData));
1565
+ }
1566
  }
1567
 
1568
  function getQuestionTypeLabel(type) {
 
1593
  }
1594
 
1595
  function editQuestion(id) {
1596
+ const question = questions.find(q => q.id === Number(id));
1597
  if (!question) return;
1598
 
1599
  currentQuestionType = question.type;
 
1660
  <div class="mb-4">
1661
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Upload Image' : 'Question'}</label>
1662
  ${currentQuestionType === 'imagefirst' ?
1663
+ `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md"><input type="hidden" id="imageDataHidden" value="${question.imageData || ''}">` :
1664
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>`}
1665
  </div>
1666
  <div class="mb-4">
1667
  <label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Question' : 'Upload Image'}</label>
1668
  ${currentQuestionType === 'imagefirst' ?
1669
  `<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>` :
1670
+ `<input type="file" id="imageUpload" accept="image/*" class="w-full px-4 py-2 border border-gray-300 rounded-md"><input type="hidden" id="imageDataHidden" value="${question.imageData || ''}">`}
1671
  </div>
1672
  <div class="flex items-center"><input type="checkbox" id="imageAnswerOnPaper" ${question.answerOnPaper ? 'checked' : ''} class="mr-2"><label class="text-sm text-gray-700">Students answer on question paper</label></div>
1673
  `;
 
1740
 
1741
  function deleteQuestion(id) {
1742
  if (confirm('Are you sure you want to delete this question?')) {
1743
+ if (!Array.isArray(questions)) {
1744
+ console.error('questions is not an array!', questions);
1745
+ questions = []; // reset
1746
+ }
1747
  questions = questions.filter(q => q.id !== id);
1748
  renderQuestions();
1749
  updateTotalMarks();
 
1773
  document.getElementById("downloadPdf").addEventListener("click", async () => {
1774
  document.querySelectorAll('#paperContent svg').forEach(el => el.style.display = 'none');
1775
  feather.replace();
1776
+ const examName = paperData.examName || 'Exam Paper';
1777
+ const now = new Date();
1778
+ const dateStr = String(now.getDate()).padStart(2, '0');
1779
+ const monthStr = String(now.getMonth() + 1).padStart(2, '0');
1780
+ const year = now.getFullYear();
1781
+ const hours = String(now.getHours()).padStart(2, '0');
1782
+ const minutes = String(now.getMinutes()).padStart(2, '0');
1783
+ const fileName = `${examName} - ${dateStr}-${monthStr}-${year} ${hours}-${minutes}.pdf`;
1784
  const { jsPDF } = window.jspdf;
1785
 
1786
  const pdf = new jsPDF('p', 'pt', 'a4'); // Use mm units for easier calculations
 
1789
 
1790
  await pdf.html(element, {
1791
  callback: function (pdf) {
1792
+ pdf.save(fileName);
1793
  },
1794
  x: 5,
1795
  y: 5,
 
1799
  document.querySelectorAll('#paperContent svg').forEach(el => el.style.display = '');
1800
  });
1801
  </script>
1802
+ <script>
1803
+
1804
+ function useofAI() {
1805
+ const existing = document.getElementById('aiModal');
1806
+ if (existing) existing.remove();
1807
+ const modal = document.createElement('div');
1808
+ modal.id = 'aiModal';
1809
+ modal.className = 'popup-overlay';
1810
+ modal.style.zIndex = '6000';
1811
+ modal.style.opacity = '1';
1812
+ modal.style.transform = 'none !important';
1813
+ modal.innerHTML = `
1814
+ <div class="popup-content" data-aos="zoom-in" style="opacity: 1; z-index: 99999; font-size: 1rem;">
1815
+ <h2 class="text-2xl font-bold text-center mb-6">AI Assistant</h2>
1816
+ <div class="grid grid-cols-2 gap-4">
1817
+ <button onclick="aiOptionSelected('scan-paper')" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50 text-base">
1818
+ <i data-feather="camera" class="mx-auto mb-2"></i>
1819
+ <p>Scan Question Paper</p>
1820
+ </button>
1821
+ <button onclick="aiOptionSelected('scan-paragraph')" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50 text-base">
1822
+ <i data-feather="file-text" class="mx-auto mb-2"></i>
1823
+ <p>Scan Paragraph & Generate Questions</p>
1824
+ </button>
1825
+ <button onclick="aiOptionSelected('topic')" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50 text-base">
1826
+ <i data-feather="hash" class="mx-auto mb-2"></i>
1827
+ <p>Create Questions on Topic</p>
1828
+ </button>
1829
+ <button onclick="aiOptionSelected('search')" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50 text-base">
1830
+ <i data-feather="search" class="mx-auto mb-2"></i>
1831
+ <p>Search Question Papers Online</p>
1832
+ </button>
1833
+ <button onclick="aiOptionSelected('pdf')" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50 text-base">
1834
+ <i data-feather="file" class="mx-auto mb-2"></i>
1835
+ <p>Generate from PDF</p>
1836
+ </button>
1837
+ </div>
1838
+ <div class="flex justify-end mt-6 pt-4 border-t">
1839
+ <button onclick="closeAiModal()" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50 text-base">Close</button>
1840
+ </div>
1841
+ </div>
1842
+ `;
1843
+ document.body.appendChild(modal);
1844
+ feather.replace();
1845
+ }
1846
+
1847
+ function aiOptionSelected(option) {
1848
+ const modal = document.getElementById('aiModal');
1849
+ const content = modal.querySelector('.popup-content');
1850
+
1851
+ if (option === 'scan-paper' || option === 'scan-paragraph') {
1852
+ // Trigger camera capture
1853
+ navigator.mediaDevices.getUserMedia({ video: true })
1854
+ .then(stream => {
1855
+ const video = document.createElement('video');
1856
+ video.style.display = 'none';
1857
+ video.autoplay = true;
1858
+ video.srcObject = stream;
1859
+ document.body.appendChild(video);
1860
+
1861
+ video.onloadedmetadata = () => {
1862
+ const canvas = document.createElement('canvas');
1863
+ canvas.width = video.videoWidth;
1864
+ canvas.height = video.videoHeight;
1865
+ const ctx = canvas.getContext('2d');
1866
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
1867
+ const base64 = canvas.toDataURL('image/png');
1868
+
1869
+ // Stop camera
1870
+ stream.getTracks().forEach(track => track.stop());
1871
+ document.body.removeChild(video);
1872
+
1873
+ // Alert result
1874
+ alert(option === 'scan-paper'
1875
+ ? 'Received scanned question paper!'
1876
+ : 'Received paragraph image for question generation!');
1877
+ closeAiModal();
1878
+ };
1879
+ })
1880
+ .catch(err => {
1881
+ alert('Camera access denied or not supported.');
1882
+ console.error(err);
1883
+ });
1884
+ } else {
1885
+ // Show input form for other options
1886
+ let formHTML = '';
1887
+ let placeholder = '';
1888
+ let inputType = 'text';
1889
+ let buttonText = 'Save';
1890
+
1891
+ if (option === 'topic') {
1892
+ formHTML = `<input type="text" id="aiInput" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter topic (e.g., Photosynthesis)">`;
1893
+ buttonText = 'Generate';
1894
+ } else if (option === 'search') {
1895
+ formHTML = `<input type="text" id="aiInput" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Search term (e.g., Class 10 Math)">`;
1896
+ buttonText = 'Search';
1897
+ } else if (option === 'pdf') {
1898
+ formHTML = `<input type="file" id="aiPdfInput" accept=".pdf" class="w-full px-4 py-2 border border-gray-300 rounded-md">`;
1899
+ inputType = 'file';
1900
+ buttonText = 'Upload';
1901
+ }
1902
+
1903
+ content.innerHTML = `
1904
+ <h2 class="text-2xl font-bold text-center mb-6">
1905
+ ${option === 'topic' ? 'Enter Topic' : option === 'search' ? 'Search Papers' : 'Upload PDF'}
1906
+ </h2>
1907
+ <div class="mb-6">${formHTML}</div>
1908
+ <div class="flex justify-between">
1909
+ <button onclick="closeAiModal()" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">Close</button>
1910
+ <button onclick="handleAiSave('${option}')" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700">${buttonText}</button>
1911
+ </div>
1912
+ `;
1913
+ feather.replace();
1914
+ }
1915
+ }
1916
+
1917
+ function handleAiSave(option) {
1918
+ if (option === 'pdf') {
1919
+ const fileInput = document.getElementById('aiPdfInput');
1920
+ if (fileInput.files.length === 0) {
1921
+ alert('Please select a PDF file.');
1922
+ return;
1923
+ }
1924
+ alert('PDF received! Processing...');
1925
+ } else {
1926
+ const input = document.getElementById('aiInput');
1927
+ if (!input.value.trim()) {
1928
+ alert('Please enter a valid input.');
1929
+ return;
1930
+ }
1931
+ alert(`"${input.value}" received! Processing...`);
1932
+ }
1933
+ closeAiModal();
1934
+ }
1935
+
1936
+ function closeAiModal() {
1937
+ const modal = document.getElementById('aiModal');
1938
+ if (modal) modal.remove();
1939
+ }
1940
+
1941
+ function editPaperDetails() {
1942
+ document.getElementById('schoolName').value = paperData.schoolName || '';
1943
+ document.getElementById('examName').value = paperData.examName || '';
1944
+ document.getElementById('language').value = paperData.language || 'english';
1945
+ document.getElementById('totalTime').value = paperData.totalTime || '';
1946
+ document.getElementById('importantNotes').value = paperData.importantNotes || '';
1947
+ document.getElementById('setupModal').classList.remove('hidden');
1948
+ }
1949
+
1950
+ function multipleChoiceQuestion(question, options){
1951
+ }
1952
+
1953
+ function ocr(image){
1954
+ return "Photosynthesis is the process by which green plants, algae, and some bacteria use sunlight, water, and carbon dioxide to create chemical energy in the form of glucose (sugar), releasing oxygen as a byproduct. This energy-producing process, which occurs within organelles called chloroplasts, is essential for most life on Earth, providing food and sustaining the atmospheric balance of oxygen and carbon dioxide"
1955
+ }
1956
+
1957
+ function generateId() {
1958
+ return Date.now() + Math.floor(Math.random() * 1000);
1959
+ }
1960
+
1961
+ function processPDF(pdfFile){
1962
+ return "1. What is Photosynthesis?\nA) A process of respiration\nB) A process of digestion\nC) A process by which plants make food\nD) A process of transpiration\n\n2. Which organelle is responsible for Photosynthesis?\nA) Mitochondria\nB) Chloroplast\nC) Nucleus\nD) Ribosome"
1963
+ }
1964
+
1965
+ function normalQuestion(question, marks = 1) {
1966
+ const q = {
1967
+ id: generateId(),
1968
+ type: 'normal',
1969
+ question: question,
1970
+ marks: marks,
1971
+ answerOnPaper: false
1972
+ };
1973
+ questions.push(q);
1974
+ renderQuestions();
1975
+ updateTotalMarks();
1976
+ }
1977
+
1978
+ function addTrueFalse(question, marks = 1) {
1979
+ const q = {
1980
+ id: generateId(),
1981
+ type: 'truefalse',
1982
+ question: question,
1983
+ marks: marks
1984
+ };
1985
+ questions.push(q);
1986
+ renderQuestions();
1987
+ updateTotalMarks();
1988
+ }
1989
+
1990
+ function ImageWithQuestion(image, question, marks = 1) {
1991
+ const q = {
1992
+ id: generateId(),
1993
+ type: 'imagefirst',
1994
+ question: question,
1995
+ marks: marks,
1996
+ imageData: image, // base64 string
1997
+ answerOnPaper: false
1998
+ };
1999
+ questions.push(q);
2000
+ renderQuestions();
2001
+ updateTotalMarks();
2002
+ }
2003
+
2004
+ function questionWithImage(question, image, marks = 1) {
2005
+ const q = {
2006
+ id: generateId(),
2007
+ type: 'questionfirst',
2008
+ question: question,
2009
+ marks: marks,
2010
+ imageData: image, // base64 string
2011
+ answerOnPaper: false
2012
+ };
2013
+ questions.push(q);
2014
+ renderQuestions();
2015
+ updateTotalMarks();
2016
+ }
2017
+
2018
+ function questionWithSubquestions(questionTitle, subQuestionList, marks = 1) {
2019
+ const subquestions = [];
2020
+ if (Array.isArray(subQuestionList)) {
2021
+ subQuestionList.forEach(item => {
2022
+ const entry = item;
2023
+ if (typeof entry === 'object' && entry !== null) {
2024
+ const key = Object.keys(entry)[0];
2025
+ const value = entry[key];
2026
+ if (typeof key === 'string') {
2027
+ const idNum = parseInt(key);
2028
+ if (!isNaN(idNum)) {
2029
+ let sq = { marks: 1 };
2030
+ if (typeof value === 'string') {
2031
+ // Normal or True/False
2032
+ if (value.toLowerCase().includes('true') || value.toLowerCase().includes('false')) {
2033
+ sq = { type: 'truefalse', question: value, marks: 1 };
2034
+ } else {
2035
+ sq = { type: 'normal', question: value, marks: 1 };
2036
+ }
2037
+ } else if (Array.isArray(value)) {
2038
+ if (value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string' && value[0].startsWith('data:image')) {
2039
+ // [base64, "With Question"] → imagefirst
2040
+ sq = { type: 'imagefirst', imageData: value[0], question: value[1], marks: 1 };
2041
+ } else if (value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string' && value[1].startsWith('data:image')) {
2042
+ // ["question", base64] → questionfirst
2043
+ sq = { type: 'questionfirst', question: value[0], imageData: value[1], marks: 1 };
2044
+ } else if (value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'number') {
2045
+ // ["Question with space", 10]
2046
+ sq = { type: 'questionspace', question: value[0], spaceHeight: value[1], marks: 1 };
2047
+ } else if (value.length === 2 && typeof value[0] === 'number' && typeof value[1] === 'string') {
2048
+ // [10, "Space with question"]
2049
+ sq = { type: 'spacequestion', spaceHeight: value[0], question: value[1], marks: 1 };
2050
+ } else if (Array.isArray(value) && value.every(v => typeof v === 'string')) {
2051
+ // Or question
2052
+ sq = { type: 'orquestion', options: value.map((text, i) => ({ id: i + 1, text })), marks: 1 };
2053
+ }
2054
+ } else if (typeof value === 'number') {
2055
+ // Blank space
2056
+ sq = { type: 'blank', spaceHeight: value, marks: 1 };
2057
+ }
2058
+ subquestions.push(sq);
2059
+ }
2060
+ }
2061
+ }
2062
+ });
2063
+ }
2064
+ const q = {
2065
+ id: generateId(),
2066
+ type: 'subquestion',
2067
+ mainTitle: questionTitle,
2068
+ marks: marks,
2069
+ subquestions: subquestions
2070
+ };
2071
+ questions.push(q);
2072
+ renderQuestions();
2073
+ updateTotalMarks();
2074
+ }
2075
+
2076
+ function questionWithSpace(question, spaceSize, marks = 1) {
2077
+ const q = {
2078
+ id: generateId(),
2079
+ type: 'questionspace',
2080
+ question: question,
2081
+ spaceHeight: spaceSize,
2082
+ marks: marks,
2083
+ answerOnPaper: false
2084
+ };
2085
+ questions.push(q);
2086
+ renderQuestions();
2087
+ updateTotalMarks();
2088
+ }
2089
+
2090
+ function spaceWithQuestion(spaceSize, question, marks = 1) {
2091
+ const q = {
2092
+ id: generateId(),
2093
+ type: 'spacequestion',
2094
+ question: question,
2095
+ spaceHeight: spaceSize,
2096
+ marks: marks,
2097
+ answerOnPaper: false
2098
+ };
2099
+ questions.push(q);
2100
+ renderQuestions();
2101
+ updateTotalMarks();
2102
+ }
2103
+
2104
+ function addSpace(spaceSize, marks = 1) {
2105
+ const q = {
2106
+ id: generateId(),
2107
+ type: 'blank',
2108
+ spaceHeight: spaceSize,
2109
+ marks: marks
2110
+ };
2111
+ questions.push(q);
2112
+ renderQuestions();
2113
+ updateTotalMarks();
2114
+ }
2115
+
2116
+ function orQuestion(questionList, marks = 1) {
2117
+ if (!Array.isArray(questionList)) return;
2118
+ const options = questionList.map((text, i) => ({ id: i + 1, text }));
2119
+ const q = {
2120
+ id: generateId(),
2121
+ type: 'orquestion',
2122
+ options: options,
2123
+ marks: marks
2124
+ };
2125
+ questions.push(q);
2126
+ renderQuestions();
2127
+ updateTotalMarks();
2128
+ }
2129
+
2130
+ function linkToBase64(url) {
2131
+ return new Promise((resolve, reject) => {
2132
+ const img = new Image();
2133
+ img.crossOrigin = 'anonymous'; // Handle CORS
2134
+ img.onload = () => {
2135
+ const canvas = document.createElement('canvas');
2136
+ canvas.width = img.width;
2137
+ canvas.height = img.height;
2138
+ const ctx = canvas.getContext('2d');
2139
+ ctx.drawImage(img, 0, 0);
2140
+ const base64 = canvas.toDataURL('image/png');
2141
+ resolve(base64);
2142
+ };
2143
+ img.onerror = () => reject(new Error('Failed to load image'));
2144
+ img.src = url;
2145
+ });
2146
+ }
2147
+
2148
+ </script>
2149
  </body>
2150
  </html>