nnsohamnn commited on
Commit
7191d7b
·
verified ·
1 Parent(s): 3a3efed

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +380 -382
templates/index.html CHANGED
@@ -1,382 +1,380 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
- <title>SparkQuiz</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Montserrat:wght@700;900&display=swap" rel="stylesheet">
10
- <link rel="stylesheet" href="/static/css/style.css" />
11
- </head>
12
- <body>
13
- <div class="container">
14
- <header>
15
- <h1 class="spark-heading">
16
- <span class="spark-gradient">SparkQuiz</span>
17
- <span class="spark-underline"></span>
18
- </h1>
19
- </header>
20
- <section class="info-box" aria-live="polite">
21
- Instantly generate custom quizzes for any subject. Select your preferences below!
22
- </section>
23
- <form class="main-fields" id="quiz-form" autocomplete="off">
24
- <div class="row">
25
- <div class="input-group">
26
- <label for="topic">Topic <span class="field-desc">(e.g., "Quantum Physics")</span></label>
27
- <input type="text" id="topic" name="topic" placeholder="e.g. Quantum Physics" required />
28
- </div>
29
- <div class="input-group">
30
- <label for="difficulty">Difficulty <span class="field-desc">(easy, medium, hard)</span></label>
31
- <select id="difficulty" name="difficulty" required>
32
- <option value="">Select</option>
33
- <option value="easy">Easy</option>
34
- <option value="medium">Medium</option>
35
- <option value="hard">Hard</option>
36
- </select>
37
- </div>
38
- </div>
39
- <div class="row">
40
- <div class="question-types-container">
41
- <span style="font-weight:500; margin-bottom:8px;">Desired Question Types <span class="field-desc">(select one or more, e.g., "multiple choice", "short answer", "true/false")</span></span>
42
- <div class="question-types-row">
43
- <div class="question-type-group" id="question-type-group">
44
- <label class="checkbox-label">
45
- <input type="checkbox" name="type-mcq" id="type-mcq" checked />
46
- Multiple Choice
47
- </label>
48
- <div class="type-quantity" id="mcq-quantity-box">
49
- <label for="mcq-quantity" class="quantity-label">How many Multiple Choice?</label>
50
- <input type="number" id="mcq-quantity" name="mcq-quantity" min="1" max="10" value="1" class="quantity-input-custom" />
51
- </div>
52
- <label class="checkbox-label">
53
- <input type="checkbox" name="type-short" id="type-short" />
54
- Short Answer
55
- </label>
56
- <div class="type-quantity" id="short-quantity-box" style="display:none;">
57
- <label for="short-quantity" class="quantity-label">How many Short Answer?</label>
58
- <input type="number" id="short-quantity" name="short-quantity" min="1" max="10" value="1" class="quantity-input-custom" />
59
- </div>
60
- <label class="checkbox-label">
61
- <input type="checkbox" name="type-truefalse" id="type-truefalse" />
62
- True / False
63
- </label>
64
- <div class="type-quantity" id="truefalse-quantity-box" style="display:none;">
65
- <label for="truefalse-quantity" class="quantity-label">How many True/False?</label>
66
- <input type="number" id="truefalse-quantity" name="truefalse-quantity" min="1" max="10" value="1" class="quantity-input-custom" />
67
- </div>
68
- </div>
69
- </div>
70
- <div class="input-group custom-question-types">
71
- <label for="custom-types">Custom Question Types <span class="field-desc">(Optional, comma separated, e.g., "Fill-in-the-blank, Essay")</span></label>
72
- <input type="text" id="custom-types" name="custom-types" placeholder="e.g. Fill-in-the-blank, Essay" />
73
- </div>
74
- </div>
75
- </div>
76
- <details class="advanced-options">
77
- <summary>Advanced Options</summary>
78
- <div class="advanced-content">
79
- <div class="input-group">
80
- <label for="subtopics">Specific Sub-topics <span class="field-desc">(Optional, comma separated)</span></label>
81
- <input type="text" id="subtopics" name="subtopics" placeholder='e.g. Wave-particle duality, Quantum entanglement' />
82
- </div>
83
- <div class="input-group">
84
- <label for="context">Context Keywords <span class="field-desc">(Optional, comma separated)</span></label>
85
- <input type="text" id="context" name="context" placeholder='e.g. experiment, theory, equation' />
86
- </div>
87
- <div class="input-group">
88
- <label for="audience">Target Audience <span class="field-desc">(Optional)</span></label>
89
- <input type="text" id="audience" name="audience" placeholder='e.g. Undergraduate Physics Students' />
90
- </div>
91
- <div class="input-group">
92
- <label for="language">Language <span class="field-desc">(Optional, default: English)</span></label>
93
- <input type="text" id="language" name="language" placeholder='e.g. English, French' />
94
- </div>
95
- <div class="input-group">
96
- <label for="max-length">Maximum Length per Question <span class="field-desc">(Optional)</span></label>
97
- <input type="number" id="max-length" name="max-length" min="1" placeholder="e.g. 30" class="quantity-input-custom" />
98
- </div>
99
- <div class="input-group">
100
- <label>Include Explanations?</label>
101
- <div class="radio-group">
102
- <label class="radio-label">
103
- <input type="radio" name="include-explanations" value="yes" checked />
104
- Yes
105
- </label>
106
- <label class="radio-label">
107
- <input type="radio" name="include-explanations" value="no" />
108
- No
109
- </label>
110
- </div>
111
- </div>
112
- </div>
113
- </details>
114
- <div class="btn-center">
115
- <button type="submit" class="main-btn">Generate Quiz</button>
116
- </div>
117
- </form>
118
- <div class="loading" style="display:none;">
119
- <div class="spinner"></div>
120
- Generating your quiz...
121
- </div>
122
- <div class="error" style="display:none;"></div>
123
- <section class="about-box">
124
- <div class="about-title">About SparkQuiz</div>
125
- <div>
126
- <strong>SparkQuiz</strong> is your AI-powered quiz generator for any subject, audience, or language.<br><br>
127
- Powered by <b>Qwen3 30B A3B</b> via free OpenRouter (10 requests/min, 50/day).<br>
128
- If not working, please wait until the next day.<br><br>
129
- SparkQuiz crafts high-quality, accurate quizzes tailored to your needs.<br>
130
- Choose question types, difficulty, sub-topics, and more.<br>
131
- Explanations are included for deeper learning and understanding.<br><br>
132
- <a class="about-link" href="https://huggingface.co/Qwen/Qwen3-30B-A3B" target="_blank" rel="noopener">
133
- Check the model card: Qwen3 30B A3B
134
- </a>
135
- </div>
136
- </section>
137
- <footer>
138
- © 2025 SparkQuiz — Crafted with <span aria-label="love">❤️</span> for learning.
139
- </footer>
140
- </div>
141
- <script>
142
- document.addEventListener('DOMContentLoaded', function() {
143
- // Inline Markdown parser
144
- function parseMarkdown(text) {
145
- const lines = text.split('\n');
146
- let html = '';
147
- let inList = false;
148
-
149
- for (let line of lines) {
150
- line = line.trim();
151
-
152
- // Headings
153
- if (line.startsWith('# ')) {
154
- html += `<h1>${line.slice(2)}</h1>`;
155
- } else if (line.startsWith('## ')) {
156
- html += `<h2>${line.slice(3)}</h2>`;
157
- }
158
- // Bold text (e.g., **Explanation:**)
159
- else if (line.match(/\*\*.*\*\*/)) {
160
- const boldText = line.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
161
- html += `<p>${boldText}</p>`;
162
- }
163
- // Unordered lists (e.g., - A) Option)
164
- else if (line.startsWith('- ')) {
165
- if (!inList) {
166
- html += '<ul>';
167
- inList = true;
168
- }
169
- html += `<li>${line.slice(2)}</li>`;
170
- }
171
- // Close list if no longer in list item
172
- else if (inList && line !== '') {
173
- html += '</ul>';
174
- inList = false;
175
- html += `<p>${line}</p>`;
176
- }
177
- // Plain text or empty lines
178
- else if (line !== '') {
179
- html += `<p>${line}</p>`;
180
- }
181
- }
182
-
183
- // Close any open list
184
- if (inList) {
185
- html += '</ul>';
186
- }
187
-
188
- return html;
189
- }
190
-
191
- // Show/hide quantity boxes for each question type
192
- function toggleQuantityBox(checkboxId, boxId) {
193
- const checkbox = document.getElementById(checkboxId);
194
- const box = document.getElementById(boxId);
195
- if (checkbox && box) {
196
- checkbox.addEventListener('change', function() {
197
- box.style.display = this.checked ? 'block' : 'none';
198
- });
199
- }
200
- }
201
-
202
- // Initialize quantity box visibility for predefined types
203
- toggleQuantityBox('type-mcq', 'mcq-quantity-box');
204
- toggleQuantityBox('type-short', 'short-quantity-box');
205
- toggleQuantityBox('type-truefalse', 'truefalse-quantity-box');
206
-
207
- document.getElementById('mcq-quantity-box').style.display = document.getElementById('type-mcq').checked ? 'block' : 'none';
208
- document.getElementById('short-quantity-box').style.display = document.getElementById('type-short').checked ? 'block' : 'none';
209
- document.getElementById('truefalse-quantity-box').style.display = document.getElementById('type-truefalse').checked ? 'block' : 'none';
210
-
211
- // Handle custom question types
212
- const customTypesInput = document.getElementById('custom-types');
213
- const questionTypeGroup = document.getElementById('question-type-group');
214
-
215
- customTypesInput.addEventListener('input', function() {
216
- // Remove existing custom type elements
217
- const existingCustomElements = document.querySelectorAll('.custom-type');
218
- existingCustomElements.forEach(el => el.remove());
219
-
220
- // Get custom types from input
221
- const customTypes = customTypesInput.value.split(',').map(type => type.trim()).filter(type => type);
222
-
223
- // Check if the number of custom types exceeds 10
224
- if (customTypes.length > 10) {
225
- document.querySelector('.error').style.display = 'block';
226
- document.querySelector('.error').textContent = "Error: Maximum 10 custom question types allowed.";
227
- return; // Stop further processing
228
- } else {
229
- document.querySelector('.error').style.display = 'none';
230
- }
231
-
232
- // Create checkbox and quantity input for each custom type
233
- customTypes.forEach((type, index) => {
234
- const typeId = `type-custom-${index}`;
235
- const quantityBoxId = `custom-quantity-box-${index}`;
236
-
237
- // Create checkbox
238
- const checkboxLabel = document.createElement('label');
239
- checkboxLabel.className = 'checkbox-label custom-type';
240
- checkboxLabel.innerHTML = `
241
- <input type="checkbox" name="type-custom-${index}" id="${typeId}" />
242
- ${type}
243
- `;
244
- questionTypeGroup.appendChild(checkboxLabel);
245
-
246
- // Create quantity box
247
- const quantityDiv = document.createElement('div');
248
- quantityDiv.className = 'type-quantity custom-type';
249
- quantityDiv.id = quantityBoxId;
250
- quantityDiv.style.display = 'none';
251
- quantityDiv.innerHTML = `
252
- <label for="custom-quantity-${index}" class="quantity-label">How many ${type}?</label>
253
- <input type="number" id="custom-quantity-${index}" name="custom-quantity-${index}" min="1" max="10" value="1" class="quantity-input-custom" />
254
- `;
255
- questionTypeGroup.appendChild(quantityDiv);
256
-
257
- // Toggle quantity box visibility
258
- toggleQuantityBox(typeId, quantityBoxId);
259
- });
260
- });
261
-
262
- // FORM SUBMISSION: Validate and send data to Flask
263
- const quizForm = document.getElementById('quiz-form');
264
- quizForm.addEventListener('submit', function(e) {
265
- e.preventDefault(); // Prevent default form submission
266
-
267
- // Get values for predefined question types and quantities
268
- let mcqVal = document.getElementById('type-mcq').checked ? parseInt(document.getElementById('mcq-quantity').value || "0") : 0;
269
- let shortVal = document.getElementById('type-short').checked ? parseInt(document.getElementById('short-quantity').value || "0") : 0;
270
- let tfVal = document.getElementById('type-truefalse').checked ? parseInt(document.getElementById('truefalse-quantity').value || "0") : 0;
271
-
272
- // Get values for custom question types
273
- const customTypes = customTypesInput.value.split(',').map(type => type.trim()).filter(type => type);
274
- const customVals = customTypes.map((type, index) => {
275
- const checkbox = document.getElementById(`type-custom-${index}`);
276
- const quantityInput = document.getElementById(`custom-quantity-${index}`);
277
- return checkbox && checkbox.checked && quantityInput ? parseInt(quantityInput.value || "0") : 0;
278
- });
279
-
280
- // Validation: no more than 10 for any type
281
- if (mcqVal > 10 || shortVal > 10 || tfVal > 10 || customVals.some(val => val > 10)) {
282
- document.querySelector('.error').style.display = 'block';
283
- document.querySelector('.error').textContent = "Error: Maximum allowed per category is 10.";
284
- return; // Stop submission if validation fails
285
- }
286
-
287
- // Validate that at least one question is requested
288
- const totalQuestions = mcqVal + shortVal + tfVal + customVals.reduce((sum, val) => sum + val, 0);
289
- if (totalQuestions <= 0) {
290
- document.querySelector('.loading').style.display = 'none';
291
- document.querySelector('.error').style.display = 'block';
292
- document.querySelector('.error').textContent = "Error: Please request at least one question.";
293
- return;
294
- }
295
-
296
- // Gather form data
297
- const topic = document.getElementById('topic').value;
298
- const difficulty = document.getElementById('difficulty').value;
299
- const subtopics = document.getElementById('subtopics').value.split(',').map(s => s.trim()).filter(Boolean);
300
- const keywords = document.getElementById('context').value.split(',').map(s => s.trim()).filter(Boolean);
301
- const audience = document.getElementById('audience').value;
302
- const language = document.getElementById('language').value || "en";
303
- const max_length = document.getElementById('max-length').value;
304
- const include_explanations = document.querySelector('input[name="include-explanations"]:checked').value === "yes";
305
-
306
- const question_types = [];
307
- const type_counts = {};
308
- if (document.getElementById('type-mcq').checked) {
309
- question_types.push("Multiple Choice");
310
- type_counts["Multiple Choice"] = mcqVal;
311
- }
312
- if (document.getElementById('type-short').checked) {
313
- question_types.push("Short Answer");
314
- type_counts["Short Answer"] = shortVal;
315
- }
316
- if (document.getElementById('type-truefalse').checked) {
317
- question_types.push("True/False");
318
- type_counts["True/False"] = tfVal;
319
- }
320
- customTypes.forEach((type, index) => {
321
- if (document.getElementById(`type-custom-${index}`).checked) {
322
- question_types.push(type);
323
- type_counts[type] = customVals[index];
324
- }
325
- });
326
-
327
- const parameters = {
328
- topic,
329
- difficulty,
330
- subtopics,
331
- keywords,
332
- audience,
333
- language,
334
- max_length,
335
- include_explanations,
336
- question_types,
337
- type_counts
338
- };
339
-
340
- // Show loading state
341
- document.querySelector('.loading').style.display = 'block';
342
- document.querySelector('.error').style.display = 'none';
343
- // Remove existing quiz output if present
344
- const existingOutput = document.getElementById('quiz-output');
345
- if (existingOutput) existingOutput.remove();
346
-
347
- // Send request to backend
348
- fetch('/generate_quiz', {
349
- method: 'POST',
350
- headers: {'Content-Type': 'application/json'},
351
- body: JSON.stringify(parameters)
352
- })
353
- .then(res => {
354
- if (!res.ok) {
355
- throw new Error(`HTTP error! status: ${res.status}`);
356
- }
357
- return res.json();
358
- })
359
- .then(data => {
360
- document.querySelector('.loading').style.display = 'none';
361
- if (data.quiz_text) {
362
- // Create quiz output div dynamically
363
- const quizOutput = document.createElement('div');
364
- quizOutput.id = 'quiz-output';
365
- quizOutput.setAttribute('aria-live', 'polite');
366
- quizOutput.innerHTML = parseMarkdown(data.quiz_text);
367
- quizForm.insertAdjacentElement('afterend', quizOutput);
368
- } else {
369
- throw new Error('No quiz generated.');
370
- }
371
- })
372
- .catch(err => {
373
- document.querySelector('.loading').style.display = 'none';
374
- document.querySelector('.error').style.display = 'block';
375
- document.querySelector('.error').textContent = "An error occurred: " + err.message;
376
- });
377
- });
378
- });
379
- </script>
380
- <script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'93e7959ecdefe590',t:'MTc0NzAyODQ1OS4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
381
- </body>
382
- </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>SparkQuiz</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&family=Montserrat:wght@700;900&display=swap" rel="stylesheet">
10
+ <link rel="stylesheet" href="/static/css/style.css" />
11
+ </head>
12
+ <body>
13
+ <div class="container">
14
+ <header>
15
+ <h1 class="spark-heading">
16
+ <span class="spark-gradient">SparkQuiz</span>
17
+ <span class="spark-underline"></span>
18
+ </h1>
19
+ </header>
20
+ <section class="info-box" aria-live="polite">
21
+ Instantly generate custom quizzes for any subject. Select your preferences below!
22
+ </section>
23
+ <form class="main-fields" id="quiz-form" autocomplete="off">
24
+ <div class="row">
25
+ <div class="input-group">
26
+ <label for="topic">Topic <span class="field-desc">(e.g., "Quantum Physics")</span></label>
27
+ <input type="text" id="topic" name="topic" placeholder="e.g. Quantum Physics" required />
28
+ </div>
29
+ <div class="input-group">
30
+ <label for="difficulty">Difficulty <span class="field-desc">(easy, medium, hard)</span></label>
31
+ <select id="difficulty" name="difficulty" required>
32
+ <option value="">Select</option>
33
+ <option value="easy">Easy</option>
34
+ <option value="medium">Medium</option>
35
+ <option value="hard">Hard</option>
36
+ </select>
37
+ </div>
38
+ </div>
39
+ <div class="row">
40
+ <div class="question-types-container">
41
+ <span style="font-weight:500; margin-bottom:8px;">Desired Question Types <span class="field-desc">(select one or more, e.g., "multiple choice", "short answer", "true/false")</span></span>
42
+ <div class="question-types-row">
43
+ <div class="question-type-group" id="question-type-group">
44
+ <label class="checkbox-label">
45
+ <input type="checkbox" name="type-mcq" id="type-mcq" checked />
46
+ Multiple Choice
47
+ </label>
48
+ <div class="type-quantity" id="mcq-quantity-box">
49
+ <label for="mcq-quantity" class="quantity-label">How many Multiple Choice?</label>
50
+ <input type="number" id="mcq-quantity" name="mcq-quantity" min="1" max="10" value="1" class="quantity-input-custom" />
51
+ </div>
52
+ <label class="checkbox-label">
53
+ <input type="checkbox" name="type-short" id="type-short" />
54
+ Short Answer
55
+ </label>
56
+ <div class="type-quantity" id="short-quantity-box" style="display:none;">
57
+ <label for="short-quantity" class="quantity-label">How many Short Answer?</label>
58
+ <input type="number" id="short-quantity" name="short-quantity" min="1" max="10" value="1" class="quantity-input-custom" />
59
+ </div>
60
+ <label class="checkbox-label">
61
+ <input type="checkbox" name="type-truefalse" id="type-truefalse" />
62
+ True / False
63
+ </label>
64
+ <div class="type-quantity" id="truefalse-quantity-box" style="display:none;">
65
+ <label for="truefalse-quantity" class="quantity-label">How many True/False?</label>
66
+ <input type="number" id="truefalse-quantity" name="truefalse-quantity" min="1" max="10" value="1" class="quantity-input-custom" />
67
+ </div>
68
+ </div>
69
+ </div>
70
+ <div class="input-group custom-question-types">
71
+ <label for="custom-types">Custom Question Types <span class="field-desc">(Optional, comma separated, e.g., "Fill-in-the-blank, Essay")</span></label>
72
+ <input type="text" id="custom-types" name="custom-types" placeholder="e.g. Fill-in-the-blank, Essay" />
73
+ </div>
74
+ </div>
75
+ </div>
76
+ <details class="advanced-options">
77
+ <summary>Advanced Options</summary>
78
+ <div class="advanced-content">
79
+ <div class="input-group">
80
+ <label for="subtopics">Specific Sub-topics <span class="field-desc">(Optional, comma separated)</span></label>
81
+ <input type="text" id="subtopics" name="subtopics" placeholder='e.g. Wave-particle duality, Quantum entanglement' />
82
+ </div>
83
+ <div class="input-group">
84
+ <label for="context">Context Keywords <span class="field-desc">(Optional, comma separated)</span></label>
85
+ <input type="text" id="context" name="context" placeholder='e.g. experiment, theory, equation' />
86
+ </div>
87
+ <div class="input-group">
88
+ <label for="audience">Target Audience <span class="field-desc">(Optional)</span></label>
89
+ <input type="text" id="audience" name="audience" placeholder='e.g. Undergraduate Physics Students' />
90
+ </div>
91
+ <div class="input-group">
92
+ <label for="language">Language <span class="field-desc">(Optional, default: English)</span></label>
93
+ <input type="text" id="language" name="language" placeholder='e.g. English, French' />
94
+ </div>
95
+ <div class="input-group">
96
+ <label for="max-length">Maximum Length per Question <span class="field-desc">(Optional)</span></label>
97
+ <input type="number" id="max-length" name="max-length" min="1" placeholder="e.g. 30" class="quantity-input-custom" />
98
+ </div>
99
+ <div class="input-group">
100
+ <label>Include Explanations?</label>
101
+ <div class="radio-group">
102
+ <label class="radio-label">
103
+ <input type="radio" name="include-explanations" value="yes" checked />
104
+ Yes
105
+ </label>
106
+ <label class="radio-label">
107
+ <input type="radio" name="include-explanations" value="no" />
108
+ No
109
+ </label>
110
+ </div>
111
+ </div>
112
+ </div>
113
+ </details>
114
+ <div class="btn-center">
115
+ <button type="submit" class="main-btn">Generate Quiz</button>
116
+ </div>
117
+ </form>
118
+ <div class="loading" style="display:none;">
119
+ <div class="spinner"></div>
120
+ Generating your quiz...
121
+ </div>
122
+ <div class="error" style="display:none;"></div>
123
+ <section class="about-box">
124
+ <div class="about-title">About SparkQuiz</div>
125
+ <div>
126
+ <strong>SparkQuiz</strong> is your AI-powered quiz generator for any subject, audience, or language.<br><br>
127
+ Powered by <b>Gemini 2.5 Flash</b> via free Google AI Studio.<br>
128
+ If not working, please try again later or check your request limits.<br><br>
129
+ SparkQuiz crafts high-quality, accurate quizzes tailored to your needs.<br>
130
+ Choose question types, difficulty, sub-topics, and more.<br>
131
+ Explanations are included for deeper learning and understanding.<br><br>
132
+ </a>
133
+ </div>
134
+ </section>
135
+ <footer>
136
+ © 2025 SparkQuiz — Crafted with <span aria-label="love">❤️</span> for learning.
137
+ </footer>
138
+ </div>
139
+ <script>
140
+ document.addEventListener('DOMContentLoaded', function() {
141
+ // Inline Markdown parser
142
+ function parseMarkdown(text) {
143
+ const lines = text.split('\n');
144
+ let html = '';
145
+ let inList = false;
146
+
147
+ for (let line of lines) {
148
+ line = line.trim();
149
+
150
+ // Headings
151
+ if (line.startsWith('# ')) {
152
+ html += `<h1>${line.slice(2)}</h1>`;
153
+ } else if (line.startsWith('## ')) {
154
+ html += `<h2>${line.slice(3)}</h2>`;
155
+ }
156
+ // Bold text (e.g., **Explanation:**)
157
+ else if (line.match(/\*\*.*\*\*/)) {
158
+ const boldText = line.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
159
+ html += `<p>${boldText}</p>`;
160
+ }
161
+ // Unordered lists (e.g., - A) Option)
162
+ else if (line.startsWith('- ')) {
163
+ if (!inList) {
164
+ html += '<ul>';
165
+ inList = true;
166
+ }
167
+ html += `<li>${line.slice(2)}</li>`;
168
+ }
169
+ // Close list if no longer in list item
170
+ else if (inList && line !== '') {
171
+ html += '</ul>';
172
+ inList = false;
173
+ html += `<p>${line}</p>`;
174
+ }
175
+ // Plain text or empty lines
176
+ else if (line !== '') {
177
+ html += `<p>${line}</p>`;
178
+ }
179
+ }
180
+
181
+ // Close any open list
182
+ if (inList) {
183
+ html += '</ul>';
184
+ }
185
+
186
+ return html;
187
+ }
188
+
189
+ // Show/hide quantity boxes for each question type
190
+ function toggleQuantityBox(checkboxId, boxId) {
191
+ const checkbox = document.getElementById(checkboxId);
192
+ const box = document.getElementById(boxId);
193
+ if (checkbox && box) {
194
+ checkbox.addEventListener('change', function() {
195
+ box.style.display = this.checked ? 'block' : 'none';
196
+ });
197
+ }
198
+ }
199
+
200
+ // Initialize quantity box visibility for predefined types
201
+ toggleQuantityBox('type-mcq', 'mcq-quantity-box');
202
+ toggleQuantityBox('type-short', 'short-quantity-box');
203
+ toggleQuantityBox('type-truefalse', 'truefalse-quantity-box');
204
+
205
+ document.getElementById('mcq-quantity-box').style.display = document.getElementById('type-mcq').checked ? 'block' : 'none';
206
+ document.getElementById('short-quantity-box').style.display = document.getElementById('type-short').checked ? 'block' : 'none';
207
+ document.getElementById('truefalse-quantity-box').style.display = document.getElementById('type-truefalse').checked ? 'block' : 'none';
208
+
209
+ // Handle custom question types
210
+ const customTypesInput = document.getElementById('custom-types');
211
+ const questionTypeGroup = document.getElementById('question-type-group');
212
+
213
+ customTypesInput.addEventListener('input', function() {
214
+ // Remove existing custom type elements
215
+ const existingCustomElements = document.querySelectorAll('.custom-type');
216
+ existingCustomElements.forEach(el => el.remove());
217
+
218
+ // Get custom types from input
219
+ const customTypes = customTypesInput.value.split(',').map(type => type.trim()).filter(type => type);
220
+
221
+ // Check if the number of custom types exceeds 10
222
+ if (customTypes.length > 10) {
223
+ document.querySelector('.error').style.display = 'block';
224
+ document.querySelector('.error').textContent = "Error: Maximum 10 custom question types allowed.";
225
+ return; // Stop further processing
226
+ } else {
227
+ document.querySelector('.error').style.display = 'none';
228
+ }
229
+
230
+ // Create checkbox and quantity input for each custom type
231
+ customTypes.forEach((type, index) => {
232
+ const typeId = `type-custom-${index}`;
233
+ const quantityBoxId = `custom-quantity-box-${index}`;
234
+
235
+ // Create checkbox
236
+ const checkboxLabel = document.createElement('label');
237
+ checkboxLabel.className = 'checkbox-label custom-type';
238
+ checkboxLabel.innerHTML = `
239
+ <input type="checkbox" name="type-custom-${index}" id="${typeId}" />
240
+ ${type}
241
+ `;
242
+ questionTypeGroup.appendChild(checkboxLabel);
243
+
244
+ // Create quantity box
245
+ const quantityDiv = document.createElement('div');
246
+ quantityDiv.className = 'type-quantity custom-type';
247
+ quantityDiv.id = quantityBoxId;
248
+ quantityDiv.style.display = 'none';
249
+ quantityDiv.innerHTML = `
250
+ <label for="custom-quantity-${index}" class="quantity-label">How many ${type}?</label>
251
+ <input type="number" id="custom-quantity-${index}" name="custom-quantity-${index}" min="1" max="10" value="1" class="quantity-input-custom" />
252
+ `;
253
+ questionTypeGroup.appendChild(quantityDiv);
254
+
255
+ // Toggle quantity box visibility
256
+ toggleQuantityBox(typeId, quantityBoxId);
257
+ });
258
+ });
259
+
260
+ // FORM SUBMISSION: Validate and send data to Flask
261
+ const quizForm = document.getElementById('quiz-form');
262
+ quizForm.addEventListener('submit', function(e) {
263
+ e.preventDefault(); // Prevent default form submission
264
+
265
+ // Get values for predefined question types and quantities
266
+ let mcqVal = document.getElementById('type-mcq').checked ? parseInt(document.getElementById('mcq-quantity').value || "0") : 0;
267
+ let shortVal = document.getElementById('type-short').checked ? parseInt(document.getElementById('short-quantity').value || "0") : 0;
268
+ let tfVal = document.getElementById('type-truefalse').checked ? parseInt(document.getElementById('truefalse-quantity').value || "0") : 0;
269
+
270
+ // Get values for custom question types
271
+ const customTypes = customTypesInput.value.split(',').map(type => type.trim()).filter(type => type);
272
+ const customVals = customTypes.map((type, index) => {
273
+ const checkbox = document.getElementById(`type-custom-${index}`);
274
+ const quantityInput = document.getElementById(`custom-quantity-${index}`);
275
+ return checkbox && checkbox.checked && quantityInput ? parseInt(quantityInput.value || "0") : 0;
276
+ });
277
+
278
+ // Validation: no more than 10 for any type
279
+ if (mcqVal > 10 || shortVal > 10 || tfVal > 10 || customVals.some(val => val > 10)) {
280
+ document.querySelector('.error').style.display = 'block';
281
+ document.querySelector('.error').textContent = "Error: Maximum allowed per category is 10.";
282
+ return; // Stop submission if validation fails
283
+ }
284
+
285
+ // Validate that at least one question is requested
286
+ const totalQuestions = mcqVal + shortVal + tfVal + customVals.reduce((sum, val) => sum + val, 0);
287
+ if (totalQuestions <= 0) {
288
+ document.querySelector('.loading').style.display = 'none';
289
+ document.querySelector('.error').style.display = 'block';
290
+ document.querySelector('.error').textContent = "Error: Please request at least one question.";
291
+ return;
292
+ }
293
+
294
+ // Gather form data
295
+ const topic = document.getElementById('topic').value;
296
+ const difficulty = document.getElementById('difficulty').value;
297
+ const subtopics = document.getElementById('subtopics').value.split(',').map(s => s.trim()).filter(Boolean);
298
+ const keywords = document.getElementById('context').value.split(',').map(s => s.trim()).filter(Boolean);
299
+ const audience = document.getElementById('audience').value;
300
+ const language = document.getElementById('language').value || "en";
301
+ const max_length = document.getElementById('max-length').value;
302
+ const include_explanations = document.querySelector('input[name="include-explanations"]:checked').value === "yes";
303
+
304
+ const question_types = [];
305
+ const type_counts = {};
306
+ if (document.getElementById('type-mcq').checked) {
307
+ question_types.push("Multiple Choice");
308
+ type_counts["Multiple Choice"] = mcqVal;
309
+ }
310
+ if (document.getElementById('type-short').checked) {
311
+ question_types.push("Short Answer");
312
+ type_counts["Short Answer"] = shortVal;
313
+ }
314
+ if (document.getElementById('type-truefalse').checked) {
315
+ question_types.push("True/False");
316
+ type_counts["True/False"] = tfVal;
317
+ }
318
+ customTypes.forEach((type, index) => {
319
+ if (document.getElementById(`type-custom-${index}`).checked) {
320
+ question_types.push(type);
321
+ type_counts[type] = customVals[index];
322
+ }
323
+ });
324
+
325
+ const parameters = {
326
+ topic,
327
+ difficulty,
328
+ subtopics,
329
+ keywords,
330
+ audience,
331
+ language,
332
+ max_length,
333
+ include_explanations,
334
+ question_types,
335
+ type_counts
336
+ };
337
+
338
+ // Show loading state
339
+ document.querySelector('.loading').style.display = 'block';
340
+ document.querySelector('.error').style.display = 'none';
341
+ // Remove existing quiz output if present
342
+ const existingOutput = document.getElementById('quiz-output');
343
+ if (existingOutput) existingOutput.remove();
344
+
345
+ // Send request to backend
346
+ fetch('/generate_quiz', {
347
+ method: 'POST',
348
+ headers: {'Content-Type': 'application/json'},
349
+ body: JSON.stringify(parameters)
350
+ })
351
+ .then(res => {
352
+ if (!res.ok) {
353
+ throw new Error(`HTTP error! status: ${res.status}`);
354
+ }
355
+ return res.json();
356
+ })
357
+ .then(data => {
358
+ document.querySelector('.loading').style.display = 'none';
359
+ if (data.quiz_text) {
360
+ // Create quiz output div dynamically
361
+ const quizOutput = document.createElement('div');
362
+ quizOutput.id = 'quiz-output';
363
+ quizOutput.setAttribute('aria-live', 'polite');
364
+ quizOutput.innerHTML = parseMarkdown(data.quiz_text);
365
+ quizForm.insertAdjacentElement('afterend', quizOutput);
366
+ } else {
367
+ throw new Error('No quiz generated.');
368
+ }
369
+ })
370
+ .catch(err => {
371
+ document.querySelector('.loading').style.display = 'none';
372
+ document.querySelector('.error').style.display = 'block';
373
+ document.querySelector('.error').textContent = "An error occurred: " + err.message;
374
+ });
375
+ });
376
+ });
377
+ </script>
378
+ <script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'93e7959ecdefe590',t:'MTc0NzAyODQ1OS4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
379
+ </body>
380
+ </html>