|
188 | 188 |
|
189 | 189 | if (actuallySubmit) return;
|
190 | 190 |
|
| 191 | + const inputs = Array.from(document.querySelectorAll('input, select')); |
| 192 | + |
| 193 | + // All problem numbers are represented by a probstatus hidden input. Use those to determine the problem |
| 194 | + // numbers of problems in the test. Note that problem numbering displayed on the page will not match these |
| 195 | + // numbers in the cases that the test definition has non-consecutive numbering or that problem order is |
| 196 | + // randomized. But the problem numbering will always match the quiz prefix numbering. |
| 197 | + const problems = []; |
| 198 | + for (const input of inputs.filter((i) => /^probstatus\d*/.test(i.name))) { |
| 199 | + problems[parseInt(input.name.replace('probstatus', ''))] = {}; |
| 200 | + } |
| 201 | + |
| 202 | + // Determine which questions have been answered. Note that there can be multiple inputs for a |
| 203 | + // given question (for example for checkbox or radio answers). |
| 204 | + for (const input of inputs.filter( |
| 205 | + (i) => /Q\d{4}_/.test(i.name) && !/^MaThQuIlL_/.test(i.name) && !/^previous_/.test(i.name) |
| 206 | + )) { |
| 207 | + const answered = |
| 208 | + input.type === 'radio' || input.type === 'checkbox' ? !!input.checked : /\S/.test(input.value); |
| 209 | + const match = /Q(\d{4})_/.exec(input.name); |
| 210 | + const problemNumber = parseInt(match?.[1] ?? '0'); |
| 211 | + if (!(input.name in problems[problemNumber])) problems[problemNumber][input.name] = answered; |
| 212 | + else if (answered) problems[problemNumber][input.name] = 1; |
| 213 | + } |
| 214 | + |
| 215 | + // Determine if there are any unanswered questions in each problem. |
| 216 | + let numProblemsWithUnanswered = 0; |
| 217 | + for (const problem of problems) { |
| 218 | + // Skip problem 0 and any problems that don't exist in the test |
| 219 | + // due to non-consecutive numbering in the test definition. |
| 220 | + if (!problem) continue; |
| 221 | + |
| 222 | + if (!Object.keys(problem).length || !Object.values(problem).every((answered) => answered)) |
| 223 | + ++numProblemsWithUnanswered; |
| 224 | + } |
| 225 | + |
191 | 226 | // Prevent the gwquiz form from being submitted until after confirmation.
|
192 | 227 | evt.preventDefault();
|
193 | 228 |
|
|
224 | 259 | modalBodyContent.textContent = submitAnswers.dataset.confirmDialogMessage;
|
225 | 260 | modalBody.append(modalBodyContent);
|
226 | 261 |
|
| 262 | + if (numProblemsWithUnanswered) { |
| 263 | + const modalSecondaryContent = document.createElement('div'); |
| 264 | + modalSecondaryContent.classList.add('mt-3'); |
| 265 | + modalSecondaryContent.textContent = |
| 266 | + (numProblemsWithUnanswered > 1 |
| 267 | + ? submitAnswers.dataset.unansweredQuestionsMessage |
| 268 | + ? submitAnswers.dataset.unansweredQuestionsMessage.replace('%d', numProblemsWithUnanswered) |
| 269 | + : `There are ${numProblemsWithUnanswered} problems with unanswered questions.` |
| 270 | + : submitAnswers.dataset.unansweredQuestionMessage ?? |
| 271 | + 'There is a problem with unanswered questions.') + |
| 272 | + ' ' + |
| 273 | + (submitAnswers.dataset.returnToTestMessage ?? |
| 274 | + 'Are you sure you want to grade the test? ' + |
| 275 | + 'Select "No" if you would like to return to the test to enter more answers.'); |
| 276 | + modalBody.append(modalSecondaryContent); |
| 277 | + } |
| 278 | + |
227 | 279 | const modalFooter = document.createElement('div');
|
228 | 280 | modalFooter.classList.add('modal-footer');
|
229 | 281 |
|
|
0 commit comments