qp / index.html
5m4ck3r's picture
Update index.html
d8322b8 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exam Paper Creator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://js.puter.com/v2/"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#2563eb',
secondary: '#64748b',
accent: '#f59e0b',
}
}
}
}
</script>
<style>
#paperContent {
transition: transform 0.2s ease;
transform-origin: top center;
}
.paper-container {
width: 210mm;
min-height: 297mm;
padding: 20mm;
margin: 10mm auto;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
background: white;
}
.question-item {
position: relative;
margin-bottom: 20px;
padding: 15px;
border-radius: 8px;
border-left: 4px solid #2563eb;
}
.question-item:hover {
background-color: #f8fafc;
}
.option-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 10px;
}
.subquestions-container {
margin-left: 30px;
border-left: 2px dashed #cbd5e1;
padding-left: 15px;
}
.image-preview {
max-width: 100%;
max-height: 200px;
border-radius: 6px;
margin: 10px 0;
}
.popup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
}
#questionModal.popup-overlay {
z-index: 3000;
}
#subquestionTypeModal.popup-overlay {
z-index: 5000;
}
.popup-content {
background: white;
border-radius: 12px;
padding: 24px;
max-width: 600px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
}
.question-type-btn {
transition: all 0.2s ease;
}
.question-type-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
}
.text-size-control {
position: fixed;
bottom: 20px;
right: 20px;
background: white;
border-radius: 50px;
padding: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 100;
}
#questionsContainer .question-item {
position: relative;
z-index: 1;
}
.popup-overlay {
z-index: 9999;
}
.question-type-btn.selected {
background-color: #2563eb;
color: white;
border-color: #2563eb;
}
.paper-container {
width: 210mm;
min-height: 297mm;
padding: 20mm;
margin: 10mm auto;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
background: white;
box-sizing: border-box;
}
.question-item {
break-inside: avoid;
page-break-inside: avoid;
}
#questionsContainer {
break-after: always;
page-break-after: always;
}
.paper-container + .paper-container {
margin-top: 20mm;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.image-preview {
max-width: 100%;
max-height: 200px;
border-radius: 6px;
margin: 10px 0;
}
#paperContent img {
width: auto;
height: auto;
max-width: 100%;
max-height: 200px;
}
#logoContainer img {
max-height: 64px;
height: auto;
width: auto;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div id="setupModal" class="popup-overlay">
<div class="popup-content" data-aos="zoom-in">
<h2 class="text-2xl font-bold text-center mb-6">Create Your Exam Paper</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">School Name</label>
<input type="text" id="schoolName" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter school name">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Exam Name</label>
<input type="text" id="examName" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter exam name">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Language</label>
<select id="language" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary">
<option value="english">English</option>
<option value="hindi">Hindi</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Total Time (Hour)</label>
<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">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">School Logo (Optional)</label>
<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">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Important Notes (Optional)</label>
<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>
</div>
<div class="flex justify-end pt-4">
<button onclick="saveSetup()" class="bg-primary text-white px-6 py-2 rounded-md hover:bg-blue-700 transition">Create Paper</button>
</div>
</div>
</div>
</div>
<div id="addQuestionModal" class="popup-overlay hidden">
<div class="popup-content" data-aos="zoom-in">
<h2 class="text-2xl font-bold text-center mb-6">Add Question</h2>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Marks for this question</label>
<input type="number" id="questionMarks" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter marks">
</div>
<h3 class="text-lg font-semibold mb-4">Select Question Type</h3>
<div class="grid grid-cols-2 gap-4">
<button onclick="selectQuestionType('mcq', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="check-square" class="mx-auto mb-2"></i>
<p>Multiple Choice</p>
</button>
<button onclick="selectQuestionType('normal', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="edit" class="mx-auto mb-2"></i>
<p>Normal Question</p>
</button>
<button onclick="selectQuestionType('truefalse', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="check-circle" class="mx-auto mb-2"></i>
<p>True/False</p>
</button>
<button onclick="selectQuestionType('imagefirst', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="image" class="mx-auto mb-2"></i>
<p>Image with Question</p>
</button>
<button onclick="selectQuestionType('questionfirst', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="type" class="mx-auto mb-2"></i>
<p>Question with Image</p>
</button>
<button onclick="selectQuestionType('subquestion', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="layers" class="mx-auto mb-2"></i>
<p>Question with Subquestions</p>
</button>
<button onclick="selectQuestionType('questionspace', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="align-left" class="mx-auto mb-2"></i>
<p>Question with Space</p>
</button>
<button onclick="selectQuestionType('spacequestion', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="align-right" class="mx-auto mb-2"></i>
<p>Space with Question</p>
</button>
<button onclick="selectQuestionType('blank', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="square" class="mx-auto mb-2"></i>
<p>Blank Space</p>
</button>
<button onclick="selectQuestionType('orquestion', this)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="list" class="mx-auto mb-2"></i>
<p>Or Question</p>
</button>
</div>
<div class="flex justify-end space-x-3 mt-6 pt-4 border-t">
<button onclick="closeAddQuestionModal()" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">Cancel</button>
<button id="nextToDetailsBtn" onclick="showQuestionDetails()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700 hidden">Next</button>
</div>
</div>
</div>
<div id="questionDetailsModal" class="popup-overlay hidden">
<div class="popup-content" data-aos="zoom-in">
<h2 id="detailsModalTitle" class="text-2xl font-bold text-center mb-6">Question Details</h2>
<!-- Dynamic content based on question type -->
<div id="questionDetailsContent"></div>
<div class="flex justify-between mt-6 pt-4 border-t">
<button onclick="backToTypeSelection()" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">
<i data-feather="arrow-left" class="inline mr-2"></i> Back
</button>
<div class="space-x-3">
<button onclick="showUpcomingAlert('scan')" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">
<i data-feather="camera" class="inline mr-2"></i> Scan
</button>
<button onclick="showUpcomingAlert('ai')" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">
<i data-feather="cpu" class="inline mr-2"></i> AI Rewrite
</button>
<button onclick="addQuestionToPaper()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700">
Add Question
</button>
</div>
</div>
</div>
</div>
<div id="subquestionModal" class="popup-overlay hidden">
<div class="popup-content" data-aos="zoom-in">
<h2 class="text-2xl font-bold text-center mb-6">Add Subquestion</h2>
<div id="subquestionContainer" class="space-y-4 mb-4"></div>
<div class="flex justify-between mt-6 pt-4 border-t">
<button onclick="addSubquestionType()" class="px-4 py-2 border border-primary text-primary rounded-md hover:bg-blue-50">
<i data-feather="plus" class="inline mr-2"></i> Add Another
</button>
<button onclick="finishSubquestions()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700">Done</button>
</div>
</div>
</div>
<div class="container mx-auto py-8">
<div class="flex justify-between items-center mb-6 px-4">
<div class="flex items-center space-x-3">
<button onclick="showAddQuestionModal()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700 flex items-center">
<i data-feather="plus" class="mr-2"></i> Add Question
</button>
<button onclick="confirmNewPaper()" class="bg-gray-600 text-white px-4 py-2 rounded-md hover:bg-gray-700 flex items-center">
<i data-feather="file-plus" class="mr-2"></i> New Paper
</button>
<button id="downloadPdf" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 flex items-center">
<i data-feather="download" class="mr-2"></i> Export PDF
</button>
<button onclick="useofAI()" id="useAI" class="bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 flex items-center">
<i data-feather="book" class="mr-2"></i> AI
</button>
<button onclick="editPaperDetails()" class="bg-amber-600 text-white px-4 py-2 rounded-md hover:bg-amber-700 flex items-center">
<i data-feather="edit" class="mr-2"></i> Edit Paper
</button>
</div>
</div>
<div class="paper-container" id="paperContent">
<!-- School and exam details -->
<div class="text-center mb-8">
<div class="flex items-start justify-between">
<div id="logoContainer" class="mr-4"></div>
<div class="text-center flex-1">
<h2 id="schoolNameDisplay" class="text-xl font-bold uppercase"></h2>
<h3 id="examNameDisplay" class="text-lg font-semibold mt-2"></h3>
</div>
</div>
<div id="importantNotesSection" class="mt-4 text-sm italic text-gray-700 text-left"></div>
<div class="flex justify-between items-center mt-6 text-sm">
<div>Time: <span id="paperTimeDisplay"></span></div>
<div>Total Marks: <span id="paperTotalMarksDisplay">0</span></div>
</div>
<div class="border-b border-gray-300 mt-4"></div>
</div>
<div id="questionsContainer"></div>
</div>
<div class="text-center mt-4 text-gray-600">
Page <span id="currentPage">1</span> of <span id="totalPages">1</span>
</div>
</div>
<div class="text-size-control flex items-center space-x-2">
<button onclick="decreaseTextSize()" class="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center hover:bg-gray-200">
<i data-feather="minus"></i>
</button>
<span class="text-sm">Text Size</span>
<button onclick="increaseTextSize()" class="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center hover:bg-gray-200">
<i data-feather="plus"></i>
</button>
</div>
<script>
let currentQuestionType = '';
let questions = [];
let textSize = 16;
let paperScale = 1.0;
let baseFontSize = 16;
let currentScale = 1.0;
let paperData = {
schoolName: '',
examName: '',
language: 'english',
totalTime: 0,
importantNotes: '', // ← added
date: new Date()
};
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('questionDetailsModal').addEventListener('change', function(e) {
if (e.target.id === 'imageUpload') {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
document.getElementById('imageDataHidden').value = event.target.result; // store Base64
};
reader.readAsDataURL(file);
} else {
document.getElementById('imageDataHidden').value = '';
}
}
});
});
document.addEventListener('DOMContentLoaded', function() {
AOS.init();
feather.replace();
const savedData = localStorage.getItem('examPaperData');
if (savedData) {
try {
paperData = JSON.parse(savedData);
// Ensure questions is always an array
if (Array.isArray(paperData.questions)) {
questions = paperData.questions;
} else {
console.warn('Invalid questions format in localStorage. Resetting to empty array.');
questions = [];
paperData.questions = questions;
}
} catch (e) {
console.error('Failed to parse saved data', e);
questions = [];
paperData = { schoolName: '', examName: '', language: 'english', totalTime: 0, date: new Date(), questions: [] };
}
updatePaperHeader();
renderLogo();
renderQuestions();
document.getElementById('setupModal').classList.add('hidden');
}
updateDateTime();
setInterval(updateDateTime, 60000);
});
function confirmNewPaper() {
if (confirm('Are you sure you want to start a new question paper? All current data will be lost.')) {
// Clear data
questions = [];
paperData = {
schoolName: '',
examName: '',
language: 'english',
totalTime: 0,
date: new Date()
};
// Clear localStorage
localStorage.removeItem('examPaperData');
// Reset UI
document.getElementById('questionsContainer').innerHTML = '';
document.getElementById('paperTotalMarksDisplay').textContent = '0';
document.getElementById('setupModal').classList.remove('hidden');
}
}
function renderLogo() {
const logoContainer = document.getElementById('logoContainer');
if (paperData.logo) {
logoContainer.innerHTML = `<img src="${paperData.logo}" alt="School Logo" class="h-16 object-contain">`;
} else {
logoContainer.innerHTML = '';
}
}
function getReadableQuestionTypeLabel(type) {
const map = {
'mcq': 'Multiple Choice',
'normal': 'Normal Question',
'truefalse': 'True/False',
'imagefirst': 'Image with Question',
'questionfirst': 'Question with Image',
'questionspace': 'Question with Space',
'spacequestion': 'Space with Question',
'blank': 'Blank Space',
'orquestion': 'Or Question',
'subquestion': 'Subquestion'
};
return map[type] || '';
}
function removeSubquestion(btn) {
const item = btn.closest('.subquestion-item');
if (!item) return;
const id = item.getAttribute('data-id');
// remove from editor container
const container = document.getElementById('subquestionContainer');
const modalItem = container ? container.querySelector(`.subquestion-item[data-id="${id}"]`) : null;
if (modalItem) modalItem.remove();
// remove from preview/details container
const detailsContainer = document.getElementById('subquestionsUI');
const detailsItem = detailsContainer ? detailsContainer.querySelector(`.subquestion-item[data-id="${id}"]`) : null;
if (detailsItem) detailsItem.remove();
}
function updateDateTime() {
const now = new Date();
const timeStr = now.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
}
function saveSetup() {
const schoolName = document.getElementById('schoolName').value;
const examName = document.getElementById('examName').value;
const language = document.getElementById('language').value;
const totalTime = document.getElementById('totalTime').value;
const importantNotes = document.getElementById('importantNotes').value;
const logoFile = document.getElementById('schoolLogo').files[0];
let logoData = null;
if (logoFile) {
const reader = new FileReader();
reader.onload = (e) => {
paperData.logo = e.target.result;
localStorage.setItem('examPaperData', JSON.stringify(paperData));
renderLogo();
};
reader.readAsDataURL(logoFile);
} else {
paperData.logo = null;
}
if (!schoolName || !examName || !totalTime) {
alert('Please fill all required fields');
return;
}
paperData = {
schoolName,
examName,
language,
totalTime: parseInt(totalTime),
importantNotes,
logo: paperData.logo, // preserve existing logo if no new one uploaded
date: new Date()
};
localStorage.setItem('examPaperData', JSON.stringify(paperData));
updatePaperHeader();
document.getElementById('setupModal').classList.add('hidden');
}
function updatePaperHeader() {
document.getElementById('schoolNameDisplay').textContent = paperData.schoolName;
document.getElementById('examNameDisplay').textContent = paperData.examName;
document.getElementById('paperTimeDisplay').textContent = `${paperData.totalTime} Hours`;
const notesSection = document.getElementById('importantNotesSection');
if (paperData.importantNotes && paperData.importantNotes.trim()) {
const formattedNotes = paperData.importantNotes.replace(/\n/g, '<br>');
notesSection.innerHTML = `<strong>Notes:</strong> ${formattedNotes}`;
notesSection.classList.remove('hidden');
} else {
notesSection.innerHTML = '';
notesSection.classList.add('hidden');
}
updateTotalMarks();
}
function showAddQuestionModal() {
document.getElementById('addQuestionModal').classList.remove('hidden');
}
function closeAddQuestionModal() {
document.getElementById('addQuestionModal').classList.add('hidden');
document.getElementById('nextToDetailsBtn').classList.add('hidden');
currentQuestionType = '';
}
function selectQuestionType(type, button) {
currentQuestionType = type;
document.querySelectorAll('.question-type-btn').forEach(btn => {
btn.classList.remove('selected');
});
button.classList.add('selected');
document.getElementById('nextToDetailsBtn').classList.remove('hidden');
}
function showQuestionDetails() {
if (!currentQuestionType) {
alert('Please select a question type');
return;
}
const marks = document.getElementById('questionMarks').value;
if (!marks || marks <= 0) {
alert('Please enter valid marks for this question');
return;
}
document.getElementById('addQuestionModal').classList.add('hidden');
// Set modal title based on type
const typeTitles = {
'mcq': 'Multiple Choice Question',
'normal': 'Normal Question',
'truefalse': 'True/False Question',
'imagefirst': 'Image with Question',
'questionfirst': 'Question with Image',
'subquestion': 'Question with Subquestions',
'questionspace': 'Question with Space',
'spacequestion': 'Space with Question',
'blank': 'Blank Space',
'orquestion': 'Or Question'
};
document.getElementById('detailsModalTitle').textContent = typeTitles[currentQuestionType];
// Generate content based on question type
let contentHTML = '';
switch(currentQuestionType) {
case 'subquestion':
// Create parent object (only if not editing)
const newQuestion = {
type: 'subquestion',
marks: parseInt(document.getElementById('questionMarks').value),
id: Date.now(),
mainTitle: '',
subquestions: []
};
questions.push(newQuestion);
// store the parent id on the details modal so we can find it later
document.getElementById('questionDetailsModal').setAttribute('data-new-subquestion-id', newQuestion.id);
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Main Question Title</label>
<input type="text" id="mainQuestionTitle" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter main question title">
</div>
<div id="subquestionsUI" class="space-y-3"></div>
<div class="text-center py-4">
<button onclick="openSubquestionModal()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700">
<i data-feather="plus" class="inline mr-2"></i> Add Subquestions
</button>
</div>
`;
break;
case 'mcq':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="mcqQuestion" 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>
</div>
<div class="option-grid">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option i)</label>
<input type="text" id="option1" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Option 1">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option ii)</label>
<input type="text" id="option2" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Option 2">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option iii)</label>
<input type="text" id="option3" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Option 3">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option iv)</label>
<input type="text" id="option4" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Option 4">
</div>
</div>
<div class="mt-4 flex items-center">
<input type="checkbox" id="mcqAnswerOnPaper" class="mr-2">
<label for="mcqAnswerOnPaper" class="text-sm text-gray-700">Students answer on question paper</label>
</div>
`;
break;
case 'normal':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="normalQuestion" 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>
</div>
<div class="flex items-center">
<input type="checkbox" id="normalAnswerOnPaper" class="mr-2">
<label for="normalAnswerOnPaper" class="text-sm text-gray-700">Students answer on question paper</label>
</div>
`;
break;
case 'truefalse':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="tfQuestion" 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>
</div>
`;
break;
case 'imagefirst':
case 'questionfirst':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Upload Image' : 'Question'}</label>
${currentQuestionType === 'imagefirst' ?
`<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="">`:
`<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>`}
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Question' : 'Upload Image'}</label>
${currentQuestionType === 'imagefirst' ?
`<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>` :
`<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="">`}
</div>
<div class="flex items-center">
<input type="checkbox" id="imageAnswerOnPaper" class="mr-2">
<label for="imageAnswerOnPaper" class="text-sm text-gray-700">Students answer on question paper</label>
</div>
`;
break;
case 'subquestion':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Main Question Title</label>
<input type="text" id="mainQuestionTitle" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" placeholder="Enter main question title">
</div>
<div class="text-center py-4">
<button onclick="openSubquestionModal()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700">
<i data-feather="plus" class="inline mr-2"></i> Add Subquestions
</button>
</div>
`;
break;
case 'questionspace':
case 'spacequestion':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="spaceQuestion" 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>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" id="spaceHeight" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" value="5" min="1">
</div>
<div class="flex items-center">
<input type="checkbox" id="spaceAnswerOnPaper" class="mr-2">
<label for="spaceAnswerOnPaper" class="text-sm text-gray-700">Students answer on question paper</label>
</div>
`;
break;
case 'blank':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" id="blankHeight" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" value="5" min="1">
</div>
`;
break;
case 'orquestion':
contentHTML = `
<div id="orQuestionsContainer" class="space-y-4">
<div class="or-question-item">
<label class="block text-sm font-medium text-gray-700 mb-1">Option 1</label>
<textarea class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="2" placeholder="Enter question option"></textarea>
</div>
<div class="or-question-item">
<label class="block text-sm font-medium text-gray-700 mb-1">Option 2</label>
<textarea class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="2" placeholder="Enter question option"></textarea>
</div>
</div>
<div class="mt-4">
<button onclick="addOrOption()" class="text-primary hover:underline flex items-center">
<i data-feather="plus" class="inline mr-1 w-4 h-4"></i> Add another option
</button>
</div>
`;
break;
}
document.getElementById('questionDetailsContent').innerHTML = contentHTML;
feather.replace();
document.getElementById('questionDetailsModal').classList.remove('hidden');
}
function backToTypeSelection() {
document.getElementById('questionDetailsModal').classList.add('hidden');
document.getElementById('addQuestionModal').classList.remove('hidden');
}
function addQuestionToPaper() {
const marks = parseInt(document.getElementById('questionMarks').value);
const editId = document.getElementById('questionDetailsModal').getAttribute('data-edit-id');
let questionData = {
type: currentQuestionType,
marks: marks,
id: editId ? parseInt(editId) : Date.now()
};
// Collect data based on question type
switch(currentQuestionType) {
case 'mcq':
questionData.question = document.getElementById('mcqQuestion').value;
questionData.options = [
document.getElementById('option1').value,
document.getElementById('option2').value,
document.getElementById('option3').value,
document.getElementById('option4').value
];
questionData.answerOnPaper = document.getElementById('mcqAnswerOnPaper').checked;
break;
case 'normal':
questionData.question = document.getElementById('normalQuestion').value;
questionData.answerOnPaper = document.getElementById('normalAnswerOnPaper').checked;
break;
case 'truefalse':
questionData.question = document.getElementById('tfQuestion').value;
break;
case 'imagefirst':
case 'questionfirst':
questionData.question = document.getElementById('questionText').value;
questionData.imageData = document.getElementById('imageDataHidden').value || null;
questionData.answerOnPaper = document.getElementById('imageAnswerOnPaper').checked;
break;
case 'subquestion':
const titleEl = document.getElementById('mainQuestionTitle');
// Prefer the id we stored earlier
const newId = document.getElementById('questionDetailsModal').getAttribute('data-new-subquestion-id');
let mainQuestion = null;
if (newId) mainQuestion = questions.find(q => q.id == newId);
if (!mainQuestion) {
// fallback
mainQuestion = questions.find(q => q.type === 'subquestion');
}
if (mainQuestion) {
mainQuestion.mainTitle = titleEl ? titleEl.value : '';
questionData = mainQuestion; // reuse existing object
}
break;
case 'questionspace':
case 'spacequestion':
questionData.question = document.getElementById('spaceQuestion').value;
questionData.spaceHeight = parseInt(document.getElementById('spaceHeight').value);
questionData.answerOnPaper = document.getElementById('spaceAnswerOnPaper').checked;
break;
case 'blank':
questionData.spaceHeight = parseInt(document.getElementById('blankHeight').value);
break;
case 'orquestion':
const orOptions = [];
document.querySelectorAll('.or-question-item textarea').forEach((ta, index) => {
orOptions.push({
id: index + 1,
text: ta.value
});
});
questionData.options = orOptions;
break;
}
if (editId) {
// Update existing question
const index = questions.findIndex(q => q.id === parseInt(editId));
if (index !== -1) {
questions[index] = { ...questions[index], ...questionData };
}
} else {
// Only push new questions if it's NOT subquestion
if (currentQuestionType !== 'subquestion') {
questions.push(questionData);
}
}
renderQuestions();
updateTotalMarks();
// Close the modal
document.getElementById('questionDetailsModal').classList.add('hidden');
document.getElementById('questionDetailsModal').removeAttribute('data-edit-id');
// Reset for next question
currentQuestionType = '';
document.getElementById('questionMarks').value = '';
}
function openSubquestionModal() {
document.getElementById('questionDetailsModal').classList.add('hidden');
document.getElementById('subquestionModal').classList.remove('hidden');
const container = document.getElementById('subquestionContainer');
container.innerHTML = '';
// Get the parent question being edited
const editId = document.getElementById('questionDetailsModal').getAttribute('data-edit-id');
const newId = document.getElementById('questionDetailsModal').getAttribute('data-new-subquestion-id');
const parentId = editId ? parseInt(editId) : (newId ? parseInt(newId) : null);
let parentQuestion = null;
if (parentId !== null) {
parentQuestion = questions.find(q => q.id === parentId && q.type === 'subquestion');
}
if (parentQuestion && parentQuestion.subquestions && parentQuestion.subquestions.length > 0) {
// Rebuild editor UI from actual subquestion data
parentQuestion.subquestions.forEach(sq => {
const subquestionId = Date.now() + Math.floor(Math.random() * 1000);
const item = document.createElement('div');
item.className = 'subquestion-item p-4 border border-gray-200 rounded-md mb-3';
item.setAttribute('data-id', subquestionId);
item.setAttribute('data-type', sq.type);
const readable = getReadableQuestionTypeLabel(sq.type);
let html = `
<div class="flex justify-between items-start mb-3">
<h4 class="font-semibold">${readable}</h4>
<button onclick="removeSubquestion(this)" class="text-red-600 hover:text-red-800">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</div>
`;
// Reconstruct input fields with saved values
switch (sq.type) {
case 'mcq':
html += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
</div>
<div class="grid grid-cols-2 gap-3 mb-3">
<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>
<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>
<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>
<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>
</div>
`;
break;
case 'normal':
case 'truefalse':
html += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
</div>
`;
break;
case 'imagefirst':
case 'questionfirst':
html += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
</div>
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Image</label>
<input type="file" class="w-full px-3 py-1 border border-gray-300 rounded-md">
${sq.imageData ? `<img src="${sq.imageData}" class="image-preview mt-2" style="max-height:100px;">` : ''}
</div>
`;
break;
case 'questionspace':
case 'spacequestion':
html += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2">${sq.question || ''}</textarea>
</div>
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.spaceHeight || 5}" min="1">
</div>
`;
break;
case 'blank':
html += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${sq.spaceHeight || 5}" min="1">
</div>
`;
break;
case 'orquestion':
html += `
<div class="space-y-3 mb-3">
${(sq.options || []).map((opt, i) => `
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option ${i + 1}</label>
<textarea class="w-full px-3 py-1 border border-gray-300 rounded-md" rows="1">${opt.text || ''}</textarea>
</div>
`).join('')}
</div>
<button onclick="addOrOptionToSubquestion(this)" class="text-primary text-sm hover:underline">+ Add another option</button>
`;
break;
}
html += `
<div class="mt-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Marks</label>
<input type="number" class="w-20 px-3 py-1 border border-gray-300 rounded-md" value="${sq.marks || 1}" min="1">
</div>
`;
item.innerHTML = html;
container.appendChild(item);
});
} else {
container.innerHTML = `
<div class="text-center py-6 border-2 border-dashed border-gray-300 rounded-lg">
<p class="text-gray-500 mb-3">No subquestions added yet</p>
<button onclick="addSubquestionType()" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700">
<i data-feather="plus" class="inline mr-2"></i> Add Subquestion
</button>
</div>
`;
}
feather.replace();
}
function addSubquestionType() {
// Create modal for selecting subquestion type
const modal = document.createElement('div');
modal.className = 'popup-overlay';
modal.id = 'subquestionTypeModal';
modal.innerHTML = `
<div class="popup-content">
<h2 class="text-2xl font-bold text-center mb-6">Select Subquestion Type</h2>
<div class="grid grid-cols-2 gap-4">
<button onclick="createSubquestion('mcq', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="check-square" class="mx-auto mb-2"></i>
<p>Multiple Choice</p>
</button>
<button onclick="createSubquestion('normal', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="edit" class="mx-auto mb-2"></i>
<p>Normal Question</p>
</button>
<button onclick="createSubquestion('truefalse', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="check-circle" class="mx-auto mb-2"></i>
<p>True/False</p>
</button>
<button onclick="createSubquestion('imagefirst', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="image" class="mx-auto mb-2"></i>
<p>Image with Question</p>
</button>
<button onclick="createSubquestion('questionfirst', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="type" class="mx-auto mb-2"></i>
<p>Question with Image</p>
</button>
<button onclick="createSubquestion('questionspace', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="align-left" class="mx-auto mb-2"></i>
<p>Question with Space</p>
</button>
<button onclick="createSubquestion('spacequestion', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="align-right" class="mx-auto mb-2"></i>
<p>Space with Question</p>
</button>
<button onclick="createSubquestion('blank', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="square" class="mx-auto mb-2"></i>
<p>Blank Space</p>
</button>
<button onclick="createSubquestion('orquestion', true)" class="question-type-btn p-4 border border-gray-200 rounded-md text-center hover:border-primary hover:bg-blue-50">
<i data-feather="list" class="mx-auto mb-2"></i>
<p>Or Question</p>
</button>
</div>
<div class="flex justify-end mt-6 pt-4 border-t">
<button onclick="document.getElementById('subquestionTypeModal').remove()" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">Cancel</button>
</div>
</div>
`;
document.body.appendChild(modal);
modal.classList.remove('hidden');
document.getElementById('subquestionModal').style.display = 'none';
feather.replace();
}
function createSubquestion(type, vie=false) {
// remove the type selection modal
if (vie) {
document.getElementById('subquestionModal').style.removeProperty("display");
}
const modal = document.getElementById('subquestionTypeModal');
if (modal) modal.remove();
const container = document.getElementById('subquestionContainer');
// Remove empty state if it exists
if (container && container.innerHTML.includes('No subquestions')) {
container.innerHTML = '';
}
const subquestionId = Date.now();
const newSubquestion = document.createElement('div');
newSubquestion.className = 'subquestion-item p-4 border border-gray-200 rounded-md mb-3';
newSubquestion.setAttribute('data-id', subquestionId);
newSubquestion.setAttribute('data-type', type);
// Header + delete button (delete calls removeSubquestion to sync preview + editor)
const readable = getReadableQuestionTypeLabel(type);
let contentHTML = `
<div class="flex justify-between items-start mb-3">
<h4 class="font-semibold">${readable}</h4>
<button onclick="removeSubquestion(this)" class="text-red-600 hover:text-red-800">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</div>
`;
// Fill based on type
switch(type) {
case 'mcq':
contentHTML += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2" placeholder="Enter question"></textarea>
</div>
<div class="grid grid-cols-2 gap-3 mb-3">
<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" placeholder="Option 1"></div>
<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" placeholder="Option 2"></div>
<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" placeholder="Option 3"></div>
<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" placeholder="Option 4"></div>
</div>
`;
break;
case 'normal':
case 'truefalse':
contentHTML += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2" placeholder="Enter question"></textarea>
</div>
`;
break;
case 'imagefirst':
case 'questionfirst':
contentHTML += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2" placeholder="Enter question"></textarea>
</div>
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Image</label>
<input type="file" class="w-full px-3 py-1 border border-gray-300 rounded-md">
</div>
`;
break;
case 'questionspace':
case 'spacequestion':
// note: space height input comes BEFORE marks input so finishSubquestions can pick correctly
contentHTML += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea class="w-full px-3 py-2 border border-gray-300 rounded-md" rows="2" placeholder="Enter question"></textarea>
</div>
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="5" min="1">
</div>
`;
break;
case 'blank':
contentHTML += `
<div class="mb-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="5" min="1">
</div>
`;
break;
case 'orquestion':
contentHTML += `
<div class="space-y-3 mb-3">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option 1</label>
<textarea class="w-full px-3 py-1 border border-gray-300 rounded-md" rows="1" placeholder="Enter option"></textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option 2</label>
<textarea class="w-full px-3 py-1 border border-gray-300 rounded-md" rows="1" placeholder="Enter option"></textarea>
</div>
</div>
<button onclick="addOrOptionToSubquestion(this)" class="text-primary text-sm hover:underline">+ Add another option</button>
`;
break;
}
// Marks field - note: this should be the LAST number input to identify marks reliably
contentHTML += `
<div class="mt-3">
<label class="block text-sm font-medium text-gray-700 mb-1">Marks</label>
<input type="number" class="w-20 px-3 py-1 border border-gray-300 rounded-md" value="1" min="1">
</div>
`;
newSubquestion.innerHTML = contentHTML;
// Append to modal subquestion container (editor)
container.appendChild(newSubquestion);
// Clone to preview area in details modal (so the user sees a live preview)
const detailsContainer = document.getElementById("subquestionsUI");
if (detailsContainer) {
detailsContainer.appendChild(newSubquestion.cloneNode(true));
}
feather.replace();
}
function addOrOptionToSubquestion(button) {
const container = button.parentElement.querySelector('.space-y-3');
const optionCount = container.children.length + 1;
const newOption = document.createElement('div');
newOption.innerHTML = `
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Option ${optionCount}</label>
<textarea class="w-full px-3 py-1 border border-gray-300 rounded-md" rows="1" placeholder="Enter option"></textarea>
<button onclick="this.parentElement.remove()" class="text-red-600 text-xs hover:underline mt-1">Remove</button>
</div>
`;
container.appendChild(newOption);
}
function finishSubquestions() {
const subquestions = [];
document.querySelectorAll('#subquestionContainer .subquestion-item').forEach((item) => {
const type = item.getAttribute('data-type');
const numberInputs = item.querySelectorAll('input[type="number"]');
// last numeric input = marks, the earlier numeric input (if any) is space height
let marks = 1;
let spaceHeight;
if (numberInputs.length) {
marks = parseInt(numberInputs[numberInputs.length - 1].value) || 1;
if (numberInputs.length > 1) {
spaceHeight = parseInt(numberInputs[0].value) || 5;
}
}
let questionData = {
type: type,
marks: marks
};
switch (type) {
case 'mcq':
questionData.question = item.querySelector('textarea') ? item.querySelector('textarea').value : '';
questionData.options = Array.from(item.querySelectorAll('input[type="text"]')).map(opt => opt.value);
break;
case 'normal':
case 'truefalse':
questionData.question = item.querySelector('textarea') ? item.querySelector('textarea').value : '';
break;
case 'imagefirst':
case 'questionfirst':
questionData.question = item.querySelector('textarea') ? item.querySelector('textarea').value : '';
const fileInput = item.querySelector('input[type="file"]');
if (fileInput && fileInput.files && fileInput.files[0]) {
const file = fileInput.files[0];
const reader = new FileReader();
reader.onload = (e) => {
questionData.imageData = e.target.result; // store base64 data
};
reader.readAsDataURL(file);
} else {
questionData.imageData = null; // fallback if no file
}
break;
case 'questionspace':
case 'spacequestion':
questionData.question = item.querySelector('textarea') ? item.querySelector('textarea').value : '';
questionData.spaceHeight = spaceHeight || 5;
break;
case 'blank':
questionData.type = 'blank';
questionData.spaceHeight = spaceHeight || 5;
break;
case 'orquestion':
questionData.options = Array.from(item.querySelectorAll('.space-y-3 textarea')).map((ta, idx) => ({
id: idx + 1,
text: ta.value
}));
break;
}
subquestions.push(questionData);
});
// Attach to the parent question using the id we stored on the details modal
const mainId = document.getElementById('questionDetailsModal').getAttribute('data-new-subquestion-id');
let mainQuestion = null;
if (mainId) {
mainQuestion = questions.find(q => q.id == mainId);
}
// fallback: find the most recent subquestion parent
if (!mainQuestion) {
mainQuestion = [...questions].reverse().find(q => q.type === 'subquestion');
}
if (mainQuestion) {
mainQuestion.subquestions = subquestions;
}
// close the subquestion editor and return to details
document.getElementById('subquestionModal').classList.add('hidden');
document.getElementById('questionDetailsModal').classList.remove('hidden');
// refresh preview in details (optional)
const detailsContainer = document.getElementById('subquestionsUI');
if (detailsContainer) {
detailsContainer.innerHTML = '';
subquestions.forEach((sq, idx) => {
// make a simple preview node for each subquestion
const preview = document.createElement('div');
preview.className = 'subquestion-item p-3 border border-gray-100 rounded mb-2';
preview.innerHTML = `<div class="font-medium">${getReadableQuestionTypeLabel(sq.type)}: ${sq.question || ''}</div>`;
detailsContainer.appendChild(preview);
});
}
}
function addOrOption() {
const container = document.getElementById('orQuestionsContainer');
const count = container.children.length + 1;
const newOption = document.createElement('div');
newOption.className = 'or-question-item';
newOption.innerHTML = `
<label class="block text-sm font-medium text-gray-700 mb-1">Option ${count}</label>
<textarea class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="2" placeholder="Enter question option"></textarea>
<button onclick="this.parentElement.remove()" class="mt-1 text-red-600 text-sm hover:underline flex items-center">
<i data-feather="trash-2" class="inline mr-1 w-4 h-4"></i> Remove
</button>
`;
container.appendChild(newOption);
feather.replace();
}
function renderQuestions() {
if (!Array.isArray(questions)) {
console.error('questions is not an array in renderQuestions. Resetting.');
questions = [];
}
const container = document.getElementById('questionsContainer');
container.innerHTML = '';
// Create a temporary container to measure height
const tempContainer = document.createElement('div');
tempContainer.style.width = '210mm';
tempContainer.style.padding = '20mm';
tempContainer.style.boxSizing = 'border-box';
tempContainer.style.position = 'absolute';
tempContainer.style.left = '-9999px';
tempContainer.style.top = '0';
document.body.appendChild(tempContainer);
let currentPage = document.createElement('div');
currentPage.className = 'paper-page';
let currentPageHeight = 0;
const pageHeight = 297 * 4; // ~297mm in pixels (approx 4px per mm)
questions.forEach((q, index) => {
const questionElement = document.createElement('div');
questionElement.className = 'question-item';
questionElement.setAttribute('data-id', q.id);
// Build question HTML (same as before)
let questionHTML = `
<div class="flex justify-between items-start mb-2">
<div class="font-semibold">
Q${index + 1}. ${
q.type === 'subquestion'
? (q.mainTitle || '')
: ['imagefirst','spacequestion','orquestion'].includes(q.type)
? ''
: q.question
}${q.type === 'truefalse' ? ' (True / False)' : ''}
</div>
<div class="flex space-x-2">
<span class="text-sm text-blue-800 px-2 py-1 rounded">${q.marks || ''} marks</span>
<button onclick="editQuestion(${q.id})" class="text-blue-600 hover:text-blue-800">
<i data-feather="edit" class="w-4 h-4"></i>
</button>
<button onclick="deleteQuestion(${q.id})" class="text-red-600 hover:text-red-800">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</div>
</div>
`;
switch(q.type) {
case 'mcq':
questionHTML += `
<div class="option-grid mt-3">
<div>i) ${q.options[0]}</div>
<div>ii) ${q.options[1]}</div>
<div>iii) ${q.options[2]}</div>
<div>iv) ${q.options[3]}</div>
</div>
`;
if (q.answerOnPaper) {
questionHTML += `<div class="mt-3 flex space-x-6"><div>i) □</div><div>ii) □</div><div>iii) □</div><div>iv) □</div></div>`;
}
break;
case 'normal':
if (q.answerOnPaper) {
questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
}
break;
case 'truefalse':
break;
case 'imagefirst':
questionHTML += `
<div class="mb-3">
${q.imageData ? `<img src="${q.imageData}" class="image-preview">` : '<div class="text-gray-500 italic">No image uploaded</div>'}
</div>
<div class="mb-2">${q.question}</div>
`;
if (q.answerOnPaper) {
questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
}
break;
case 'questionfirst':
questionHTML += `
<div class="mb-3">
${q.imageData ? `<img src="${q.imageData}" class="image-preview">` : '<div class="text-gray-500 italic">No image uploaded</div>'}
</div>
`;
if (q.answerOnPaper) {
questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
}
break;
case 'subquestion':
if (q.subquestions && q.subquestions.length) {
questionHTML += `<div class="subquestions-container ml-6">`;
const romanNumerals = ['i','ii','iii','iv','v','vi','vii','viii','ix','x'];
q.subquestions.forEach((sq, sqIndex) => {
const label = romanNumerals[sqIndex] || (sqIndex + 1);
questionHTML += `<div class="mb-3">`;
questionHTML += `<div class="flex justify-between">`;
questionHTML += `<div class="w-full">`;
switch (sq.type) {
case 'mcq':
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
questionHTML += `<div class="option-grid mt-2">`;
questionHTML += `<div>i) ${sq.options && sq.options[0] ? sq.options[0] : ''}</div>`;
questionHTML += `<div>ii) ${sq.options && sq.options[1] ? sq.options[1] : ''}</div>`;
questionHTML += `<div>iii) ${sq.options && sq.options[2] ? sq.options[2] : ''}</div>`;
questionHTML += `<div>iv) ${sq.options && sq.options[3] ? sq.options[3] : ''}</div>`;
questionHTML += `</div>`;
if (sq.answerOnPaper) {
questionHTML += `<div class="mt-3 flex space-x-6"><div>i) □</div><div>ii) □</div><div>iii) □</div><div>iv) □</div></div>`;
}
break;
case 'normal':
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
break;
case 'truefalse':
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''} (True / False)</div>`;
break;
case 'imagefirst':
questionHTML += `<div class="mb-3">${sq.imageData ? `<img src="${sq.imageData}" class="image-preview">` : '<span class="text-gray-500">No image</span>'}</div>`;
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
break;
case 'questionfirst':
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
questionHTML += `<div class="mb-3">${sq.imageData ? `<img src="${sq.imageData}" class="image-preview">` : '<span class="text-gray-500">No image</span>'}</div>`;
if (sq.answerOnPaper) questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
break;
case 'questionspace':
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
questionHTML += `<div class="mt-2" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
break;
case 'spacequestion':
questionHTML += `<div class="mb-2" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
break;
case 'blank':
questionHTML += `<div class="my-4" style="height: ${(sq.spaceHeight || 5) * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
break;
case 'orquestion':
questionHTML += `<div class="font-medium">${label})</div>`;
questionHTML += `<div class="space-y-3">`;
(sq.options || []).forEach((opt, optIndex) => {
questionHTML += `<div class="flex items-start"><div class="mr-2 font-medium">${optIndex + 1}.</div><div>${opt.text || ''}</div></div>`;
if (optIndex < (sq.options || []).length - 1) questionHTML += `<div class="text-center font-bold">OR</div>`;
});
questionHTML += `</div>`;
break;
default:
questionHTML += `<div class="font-medium">${label}) ${sq.question || ''}</div>`;
}
questionHTML += `</div>`;
questionHTML += `<div class="ml-4"><span class="text-sm text-blue-800 px-2 py-1 rounded">${sq.marks || 1} marks</span></div>`;
questionHTML += `</div>`;
questionHTML += `</div>`;
});
questionHTML += `</div>`;
}
break;
case 'questionspace':
questionHTML += `<div class="mt-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
if (q.answerOnPaper) {
questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
}
break;
case 'spacequestion':
questionHTML += `
<div class="mb-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>
<div class="mb-2">${q.question}</div>
`;
if (q.answerOnPaper) {
questionHTML += `<div class="mt-2 border-t border-gray-300 pt-2">Answer: _________________________</div>`;
}
break;
case 'blank':
questionHTML += `<div class="my-4" style="height: ${q.spaceHeight * 24}px; border-bottom: 1px dashed #ccc;"></div>`;
break;
case 'orquestion':
questionHTML += `<div class="space-y-3">`;
q.options.forEach((opt, optIndex) => {
questionHTML += `<div class="flex items-start"><div class="mr-2 font-medium">${optIndex + 1}.</div><div>${opt.text}</div></div>`;
if (optIndex < q.options.length - 1) {
questionHTML += `<div class="text-center font-bold">OR</div>`;
}
});
questionHTML += `</div>`;
break;
}
questionElement.innerHTML = questionHTML;
tempContainer.appendChild(questionElement.cloneNode(true));
const questionHeight = questionElement.offsetHeight + 30; // + margin
if (currentPageHeight + questionHeight > pageHeight && currentPage.children.length > 0) {
// Start new page
container.appendChild(currentPage);
currentPage = document.createElement('div');
currentPage.className = 'paper-page';
currentPageHeight = 0;
}
currentPage.appendChild(questionElement);
currentPageHeight += questionHeight;
});
if (currentPage.children.length > 0) {
container.appendChild(currentPage);
}
document.body.removeChild(tempContainer);
feather.replace();
// Auto-save
if (paperData.schoolName) {
paperData.questions = questions;
localStorage.setItem('examPaperData', JSON.stringify(paperData));
}
}
function getQuestionTypeLabel(type) {
const labels = {
'mcq': '',
'normal': '',
'truefalse': '',
'imagefirst': '',
'questionfirst': '',
'subquestion': '',
'questionspace': '',
'spacequestion': '',
'blank': '',
'orquestion': ''
};
return labels[type] || '';
}
function updateTotalMarks() {
const total = questions.reduce((sum, q) => {
if (q.type === 'subquestion' && q.subquestions) {
return sum + q.subquestions.reduce((subSum, sq) => subSum + sq.marks, 0);
}
return sum + (q.marks || 0);
}, 0);
document.getElementById('paperTotalMarksDisplay').textContent = total;
}
function editQuestion(id) {
const question = questions.find(q => q.id === Number(id));
if (!question) return;
currentQuestionType = question.type;
document.getElementById('questionMarks').value = question.marks;
// Set modal title
const typeTitles = {
'mcq': 'Multiple Choice Question',
'normal': 'Normal Question',
'truefalse': 'True/False Question',
'imagefirst': 'Image with Question',
'questionfirst': 'Question with Image',
'subquestion': 'Question with Subquestions',
'questionspace': 'Question with Space',
'spacequestion': 'Space with Question',
'blank': 'Blank Space',
'orquestion': 'Or Question'
};
document.getElementById('detailsModalTitle').textContent = `Edit ${typeTitles[currentQuestionType]}`;
// Generate content with pre-filled data
let contentHTML = '';
switch(currentQuestionType) {
case 'mcq':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="mcqQuestion" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-primary focus:border-primary" rows="3">${question.question || ''}</textarea>
</div>
<div class="option-grid">
<div><label class="block text-sm font-medium text-gray-700 mb-1">Option i)</label><input type="text" id="option1" class="w-full px-4 py-2 border border-gray-300 rounded-md" value="${question.options[0] || ''}"></div>
<div><label class="block text-sm font-medium text-gray-700 mb-1">Option ii)</label><input type="text" id="option2" class="w-full px-4 py-2 border border-gray-300 rounded-md" value="${question.options[1] || ''}"></div>
<div><label class="block text-sm font-medium text-gray-700 mb-1">Option iii)</label><input type="text" id="option3" class="w-full px-4 py-2 border border-gray-300 rounded-md" value="${question.options[2] || ''}"></div>
<div><label class="block text-sm font-medium text-gray-700 mb-1">Option iv)</label><input type="text" id="option4" class="w-full px-4 py-2 border border-gray-300 rounded-md" value="${question.options[3] || ''}"></div>
</div>
<div class="mt-4 flex items-center"><input type="checkbox" id="mcqAnswerOnPaper" ${question.answerOnPaper ? 'checked' : ''} class="mr-2"><label class="text-sm text-gray-700">Students answer on question paper</label></div>
`;
break;
case 'normal':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="normalQuestion" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>
</div>
<div class="flex items-center"><input type="checkbox" id="normalAnswerOnPaper" ${question.answerOnPaper ? 'checked' : ''} class="mr-2"><label class="text-sm text-gray-700">Students answer on question paper</label></div>
`;
break;
case 'truefalse':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="tfQuestion" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>
</div>
`;
break;
case 'imagefirst':
case 'questionfirst':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Upload Image' : 'Question'}</label>
${currentQuestionType === 'imagefirst' ?
`<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 || ''}">` :
`<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>`}
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">${currentQuestionType === 'imagefirst' ? 'Question' : 'Upload Image'}</label>
${currentQuestionType === 'imagefirst' ?
`<textarea id="questionText" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>` :
`<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 || ''}">`}
</div>
<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>
`;
break;
case 'subquestion':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Main Question Title</label>
<input type="text" id="mainQuestionTitle" class="w-full px-4 py-2 border border-gray-300 rounded-md" value="${question.mainTitle || ''}">
</div>
<div class="text-center py-4">
<button onclick="openSubquestionModal()" class="bg-primary text-white px-4 py-2 rounded-md">
<i data-feather="edit" class="inline mr-2"></i> Edit Subquestions
</button>
</div>
`;
break;
case 'questionspace':
case 'spacequestion':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Question</label>
<textarea id="spaceQuestion" class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="3">${question.question || ''}</textarea>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" id="spaceHeight" class="w-full px-4 py-2 border border-gray-300 rounded-md" value="${question.spaceHeight || 5}">
</div>
<div class="flex items-center"><input type="checkbox" id="spaceAnswerOnPaper" ${question.answerOnPaper ? 'checked' : ''} class="mr-2"><label class="text-sm text-gray-700">Students answer on question paper</label></div>
`;
break;
case 'blank':
contentHTML = `
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Space Height (lines)</label>
<input type="number" id="blankHeight" class="w-full px-4 py-2 border border-gray-300 rounded-md" value="${question.spaceHeight || 5}">
</div>
`;
break;
case 'orquestion':
contentHTML = `
<div id="orQuestionsContainer" class="space-y-4">
${question.options.map((opt, index) => `
<div class="or-question-item">
<label class="block text-sm font-medium text-gray-700 mb-1">Option ${index + 1}</label>
<textarea class="w-full px-4 py-2 border border-gray-300 rounded-md" rows="2">${opt.text || ''}</textarea>
</div>
`).join('')}
</div>
<div class="mt-4">
<button onclick="addOrOption()" class="text-primary hover:underline flex items-center">
<i data-feather="plus" class="inline mr-1 w-4 h-4"></i> Add another option
</button>
</div>
`;
break;
}
document.getElementById('questionDetailsContent').innerHTML = contentHTML;
feather.replace();
// Store the question ID for updating
document.getElementById('questionDetailsModal').setAttribute('data-edit-id', id);
document.getElementById('questionDetailsModal').classList.remove('hidden');
}
function deleteQuestion(id) {
if (confirm('Are you sure you want to delete this question?')) {
if (!Array.isArray(questions)) {
console.error('questions is not an array!', questions);
questions = []; // reset
}
questions = questions.filter(q => q.id !== id);
renderQuestions();
updateTotalMarks();
}
}
function increaseTextSize() {
textSize = Math.min(24, textSize + 1);
document.getElementById('paperContent').style.fontSize = `${textSize}px`;
}
function decreaseTextSize() {
textSize = Math.max(12, textSize - 1);
document.getElementById('paperContent').style.fontSize = `${textSize}px`;
}
function showUpcomingAlert(feature) {
alert(`Feature "${feature}" is upcoming and will be available soon!`);
}
</script>
<script>feather.replace();</script>
<script>AOS.init();</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>
<script>
document.getElementById("downloadPdf").addEventListener("click", async () => {
document.querySelectorAll('#paperContent svg').forEach(el => el.style.display = 'none');
feather.replace();
const examName = paperData.examName || 'Exam Paper';
const now = new Date();
const dateStr = String(now.getDate()).padStart(2, '0');
const monthStr = String(now.getMonth() + 1).padStart(2, '0');
const year = now.getFullYear();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const fileName = `${examName} - ${dateStr}-${monthStr}-${year} ${hours}-${minutes}.pdf`;
const { jsPDF } = window.jspdf;
const pdf = new jsPDF('p', 'pt', 'a4'); // Use mm units for easier calculations
const element = document.getElementById("paperContent");
await pdf.html(element, {
callback: function (pdf) {
pdf.save(fileName);
},
x: 5,
y: 5,
html2canvas: { scale: 0.7 }
});
document.querySelectorAll('#paperContent svg').forEach(el => el.style.display = '');
});
</script>
<script>
function useofAI() {
const existing = document.getElementById('aiModal');
if (existing) existing.remove();
const modal = document.createElement('div');
modal.id = 'aiModal';
modal.className = 'popup-overlay';
modal.style.zIndex = '6000';
modal.style.opacity = '1';
modal.style.transform = 'none !important';
modal.innerHTML = `
<div class="popup-content" data-aos="zoom-in" style="opacity: 1; z-index: 99999; font-size: 1rem;">
<h2 class="text-2xl font-bold text-center mb-6">AI Assistant</h2>
<div class="grid grid-cols-2 gap-4">
<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">
<i data-feather="camera" class="mx-auto mb-2"></i>
<p>Scan Question Paper</p>
</button>
<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">
<i data-feather="file-text" class="mx-auto mb-2"></i>
<p>Scan Paragraph & Generate Questions</p>
</button>
<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">
<i data-feather="hash" class="mx-auto mb-2"></i>
<p>Create Questions on Topic</p>
</button>
<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">
<i data-feather="search" class="mx-auto mb-2"></i>
<p>Search Question Papers Online</p>
</button>
<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">
<i data-feather="file" class="mx-auto mb-2"></i>
<p>Generate from PDF</p>
</button>
</div>
<div class="flex justify-end mt-6 pt-4 border-t">
<button onclick="closeAiModal()" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50 text-base">Close</button>
</div>
</div>
`;
document.body.appendChild(modal);
feather.replace();
}
function aiOptionSelected(option) {
const modal = document.getElementById('aiModal');
const content = modal.querySelector('.popup-content');
if (option === 'scan-paper' || option === 'scan-paragraph') {
// Trigger camera capture
navigator.mediaDevices.getUserMedia({ video: true })
.then(stream => {
const video = document.createElement('video');
video.style.display = 'none';
video.autoplay = true;
video.srcObject = stream;
document.body.appendChild(video);
video.onloadedmetadata = () => {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const base64 = canvas.toDataURL('image/png');
// Stop camera
stream.getTracks().forEach(track => track.stop());
document.body.removeChild(video);
// Alert result
alert(option === 'scan-paper'
? 'Received scanned question paper!'
: 'Received paragraph image for question generation!');
closeAiModal();
};
})
.catch(err => {
alert('Camera access denied or not supported.');
console.error(err);
});
} else {
// Show input form for other options
let formHTML = '';
let placeholder = '';
let inputType = 'text';
let buttonText = 'Save';
if (option === 'topic') {
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)">`;
buttonText = 'Generate';
} else if (option === 'search') {
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)">`;
buttonText = 'Search';
} else if (option === 'pdf') {
formHTML = `<input type="file" id="aiPdfInput" accept=".pdf" class="w-full px-4 py-2 border border-gray-300 rounded-md">`;
inputType = 'file';
buttonText = 'Upload';
}
content.innerHTML = `
<h2 class="text-2xl font-bold text-center mb-6">
${option === 'topic' ? 'Enter Topic' : option === 'search' ? 'Search Papers' : 'Upload PDF'}
</h2>
<div class="mb-6">${formHTML}</div>
<div class="flex justify-between">
<button onclick="closeAiModal()" class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50">Close</button>
<button onclick="handleAiSave('${option}')" class="bg-primary text-white px-4 py-2 rounded-md hover:bg-blue-700">${buttonText}</button>
</div>
`;
feather.replace();
}
}
function handleAiSave(option) {
if (option === 'pdf') {
const fileInput = document.getElementById('aiPdfInput');
if (fileInput.files.length === 0) {
alert('Please select a PDF file.');
return;
}
alert('PDF received! Processing...');
} else {
const input = document.getElementById('aiInput');
if (!input.value.trim()) {
alert('Please enter a valid input.');
return;
}
alert(`"${input.value}" received! Processing...`);
}
closeAiModal();
}
function closeAiModal() {
const modal = document.getElementById('aiModal');
if (modal) modal.remove();
}
function editPaperDetails() {
document.getElementById('schoolName').value = paperData.schoolName || '';
document.getElementById('examName').value = paperData.examName || '';
document.getElementById('language').value = paperData.language || 'english';
document.getElementById('totalTime').value = paperData.totalTime || '';
document.getElementById('importantNotes').value = paperData.importantNotes || '';
document.getElementById('setupModal').classList.remove('hidden');
}
function multipleChoiceQuestion(question, options){
}
function ocr(image){
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"
}
function generateId() {
return Date.now() + Math.floor(Math.random() * 1000);
}
function processPDF(pdfFile){
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"
}
function normalQuestion(question, marks = 1) {
const q = {
id: generateId(),
type: 'normal',
question: question,
marks: marks,
answerOnPaper: false
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function addTrueFalse(question, marks = 1) {
const q = {
id: generateId(),
type: 'truefalse',
question: question,
marks: marks
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function ImageWithQuestion(image, question, marks = 1) {
const q = {
id: generateId(),
type: 'imagefirst',
question: question,
marks: marks,
imageData: image, // base64 string
answerOnPaper: false
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function questionWithImage(question, image, marks = 1) {
const q = {
id: generateId(),
type: 'questionfirst',
question: question,
marks: marks,
imageData: image, // base64 string
answerOnPaper: false
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function questionWithSubquestions(questionTitle, subQuestionList, marks = 1) {
const subquestions = [];
if (Array.isArray(subQuestionList)) {
subQuestionList.forEach(item => {
const entry = item;
if (typeof entry === 'object' && entry !== null) {
const key = Object.keys(entry)[0];
const value = entry[key];
if (typeof key === 'string') {
const idNum = parseInt(key);
if (!isNaN(idNum)) {
let sq = { marks: 1 };
if (typeof value === 'string') {
// Normal or True/False
if (value.toLowerCase().includes('true') || value.toLowerCase().includes('false')) {
sq = { type: 'truefalse', question: value, marks: 1 };
} else {
sq = { type: 'normal', question: value, marks: 1 };
}
} else if (Array.isArray(value)) {
if (value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string' && value[0].startsWith('data:image')) {
// [base64, "With Question"] → imagefirst
sq = { type: 'imagefirst', imageData: value[0], question: value[1], marks: 1 };
} else if (value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'string' && value[1].startsWith('data:image')) {
// ["question", base64] → questionfirst
sq = { type: 'questionfirst', question: value[0], imageData: value[1], marks: 1 };
} else if (value.length === 2 && typeof value[0] === 'string' && typeof value[1] === 'number') {
// ["Question with space", 10]
sq = { type: 'questionspace', question: value[0], spaceHeight: value[1], marks: 1 };
} else if (value.length === 2 && typeof value[0] === 'number' && typeof value[1] === 'string') {
// [10, "Space with question"]
sq = { type: 'spacequestion', spaceHeight: value[0], question: value[1], marks: 1 };
} else if (Array.isArray(value) && value.every(v => typeof v === 'string')) {
// Or question
sq = { type: 'orquestion', options: value.map((text, i) => ({ id: i + 1, text })), marks: 1 };
}
} else if (typeof value === 'number') {
// Blank space
sq = { type: 'blank', spaceHeight: value, marks: 1 };
}
subquestions.push(sq);
}
}
}
});
}
const q = {
id: generateId(),
type: 'subquestion',
mainTitle: questionTitle,
marks: marks,
subquestions: subquestions
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function questionWithSpace(question, spaceSize, marks = 1) {
const q = {
id: generateId(),
type: 'questionspace',
question: question,
spaceHeight: spaceSize,
marks: marks,
answerOnPaper: false
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function spaceWithQuestion(spaceSize, question, marks = 1) {
const q = {
id: generateId(),
type: 'spacequestion',
question: question,
spaceHeight: spaceSize,
marks: marks,
answerOnPaper: false
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function addSpace(spaceSize, marks = 1) {
const q = {
id: generateId(),
type: 'blank',
spaceHeight: spaceSize,
marks: marks
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function orQuestion(questionList, marks = 1) {
if (!Array.isArray(questionList)) return;
const options = questionList.map((text, i) => ({ id: i + 1, text }));
const q = {
id: generateId(),
type: 'orquestion',
options: options,
marks: marks
};
questions.push(q);
renderQuestions();
updateTotalMarks();
}
function linkToBase64(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous'; // Handle CORS
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const base64 = canvas.toDataURL('image/png');
resolve(base64);
};
img.onerror = () => reject(new Error('Failed to load image'));
img.src = url;
});
}
</script>
</body>
</html>