1(function () { 2 function clamp(value, min, max) { 3 return Math.max(min, Math.min(max, value)); 4 } 5 6 function escapeHtml(value) { 7 return String(value) 8 .replace(/&/g, '&') 9 .replace(/</g, '<') 10 .replace(/>/g, '>') 11 .replace(/"/g, '"') 12 .replace(/'/g, '''); 13 } 14 15 function getResultText(config, percentage) { 16 const levels = Array.isArray(config.results) ? config.results : []; 17 for (let index = 0; index < levels.length; index += 1) { 18 const level = levels[index]; 19 if (percentage >= Number(level.min) && percentage <= Number(level.max)) { 20 return level.text || ''; 21 } 22 } 23 return ''; 24 } 25 26 function setQuestionVisibility(questions, currentIndex) { 27 questions.forEach(function (question, index) { 28 question.hidden = index !== currentIndex; 29 }); 30 } 31 32 function initSelfCheck(root) { 33 if (!root || root.__baytmSelfCheckInit) { 34 return; 35 } 36 root.__baytmSelfCheckInit = true; 37 38 const config = JSON.parse(root.getAttribute('data-config') || '{}'); 39 const strings = config.strings || {}; 40 const questionElements = Array.from(root.querySelectorAll('[data-question-index]')); 41 const progressLabel = root.querySelector('[data-self-check-progress-label]'); 42 const progressFill = root.querySelector('[data-self-check-progress-fill]'); 43 const result = root.querySelector('[data-self-check-result]'); 44 const resultActions = root.querySelector('[data-self-check-result-actions]'); 45 const restartButton = root.querySelector('[data-self-check-restart]'); 46 const answers = new Map(); 47 let currentIndex = 0; 48 let evaluated = false; 49 50 function setFormLocked(isLocked) { 51 questionElements.forEach(function (questionElement) { 52 Array.from(questionElement.querySelectorAll('[data-option-value], [data-self-check-prev], [data-self-check-next]')).forEach(function (control) { 53 control.disabled = isLocked; 54 }); 55 }); 56 } 57 58 function clearQuestionOptions(questionElement) { 59 Array.from(questionElement.querySelectorAll('[data-option-value]')).forEach(function (button) { 60 button.classList.remove('is-selected'); 61 button.classList.remove('is-correct'); 62 button.classList.remove('is-wrong'); 63 button.setAttribute('aria-checked', 'false'); 64 }); 65 } 66 67 questionElements.forEach(clearQuestionOptions); 68 69 function updateProgress() { 70 const total = questionElements.length; 71 const current = Math.min(currentIndex + 1, total); 72 if (progressLabel) { 73 const template = strings.progress || 'Frage %1$d von %2$d'; 74 progressLabel.textContent = template 75 .replace('%1$d', String(current)) 76 .replace('%2$d', String(total)); 77 } 78 if (progressFill) { 79 const percent = total > 0 ? (answers.size / total) * 100 : 0; 80 progressFill.style.width = percent + '%'; 81 } 82 } 83 84 function updatePager() { 85 if (evaluated) { 86 setFormLocked(true); 87 return; 88 } 89 90 questionElements.forEach(function (questionElement, index) { 91 const prevButton = questionElement.querySelector('[data-self-check-prev]'); 92 const nextButton = questionElement.querySelector('[data-self-check-next]'); 93 const answered = answers.has(index); 94 if (prevButton) { 95 prevButton.disabled = index === 0; 96 } 97 if (nextButton) { 98 nextButton.disabled = !answered; 99 } 100 }); 101 } 102 103 function renderResult() { 104 if (!result || evaluated) { 105 return; 106 } 107 evaluated = true; 108 109 let html = ''; 110 111 if (config.mode === 'score') { 112 let achieved = 0; 113 let total = 0; 114 (config.questions || []).forEach(function (question, index) { 115 const currentAnswer = answers.get(index); 116 const currentValue = Number(currentAnswer && Number.isFinite(currentAnswer.value) ? currentAnswer.value : 0); 117 achieved += currentValue; 118 total += Number(question.maxPoints || 0); 119 }); 120 const percentage = total > 0 ? Math.round((achieved / total) * 100) : 0; 121 const resultText = getResultText(config, percentage); 122 html = 123 '<div class="baytm-self-check__resultLabel">' + escapeHtml(strings.scoreLabel || 'Ihr Ergebnis') + '</div>' + 124 '<div class="baytm-self-check__resultValue">' + percentage + 'Â %</div>' + 125 (resultText ? '<p class="baytm-self-check__resultText">' + escapeHtml(resultText) + '</p>' : ''); 126 } else if (config.mode === 'quiz') { 127 let correctCount = 0; 128 const totalQuestions = (config.questions || []).length;
129 (config.questions || []).forEach(function (question, index) { 130 const currentAnswer = answers.get(index) || {}; 131 const selectedOptionIndex = Number(currentAnswer.optionIndex); 132 const correctOptionIndex = Number(question.correctOption); 133 if (Number.isFinite(selectedOptionIndex) && selectedOptionIndex === correctOptionIndex) { 134 correctCount += 1; 135 } 136 }); 137 const quizResultText = getResultText(config, correctCount); 138 html = 139 '<div class="baytm-self-check__resultLabel">' + escapeHtml(strings.quizLabel || 'Quiz-Ergebnis') + '</div>' + 140 '<div class="baytm-self-check__resultValue">' + correctCount + 'Â /Â ' + totalQuestions + '</div>' + 141 (quizResultText ? '<p class="baytm-self-check__resultText">' + escapeHtml(quizResultText) + '</p>' : ''); 142 } else { 143 const closingText = config.closingText || strings.reflectionDone || 'Alle Fragen sind beantwortet.'; 144 html = 145 '<div class="baytm-self-check__resultLabel">' + escapeHtml(strings.reflectionLabel || 'Abgeschlossen') + '</div>' + 146 '<p class="baytm-self-check__resultText">' + closingText + '</p>'; 147 } 148 149 result.innerHTML = html; 150 result.hidden = false; 151 if (resultActions) { 152 resultActions.hidden = false; 153 } 154 setFormLocked(true); 155 result.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); 156 } 157 158 function resetAll() { 159 answers.clear(); 160 currentIndex = 0; 161 evaluated = false; 162 questionElements.forEach(function (questionElement) { 163 clearQuestionOptions(questionElement); 164 const noteElement = questionElement.querySelector('[data-self-check-note]'); 165 if (noteElement) { 166 noteElement.hidden = true; 167 } 168 }); 169 if (result) { 170 result.innerHTML = ''; 171 result.hidden = true; 172 } 173 if (resultActions) { 174 resultActions.hidden = true; 175 } 176 setFormLocked(false); 177 refreshView(); 178 } 179 180 function refreshView() { 181 setQuestionVisibility(questionElements, currentIndex); 182 updateProgress(); 183 updatePager(); 184 } 185 186 questionElements.forEach(function (questionElement) { 187 const questionIndex = Number(questionElement.getAttribute('data-question-index')); 188 const options = Array.from(questionElement.querySelectorAll('[data-option-value]')); 189 const noteElement = questionElement.querySelector('[data-self-check-note]'); 190 191 function applySelection(selectedButton) { 192 options.forEach(function (button) { 193 const isSelected = button === selectedButton; 194 button.setAttribute('aria-checked', isSelected ? 'true' : 'false'); 195 button.classList.toggle('is-selected', isSelected); 196 button.classList.remove('is-correct'); 197 button.classList.remove('is-wrong'); 198 }); 199 } 200 201 options.forEach(function (button, optionIndex) { 202 button.addEventListener('click', function () { 203 if (evaluated) { 204 return; 205 } 206 207 if (config.mode === 'quiz' && answers.has(questionIndex)) { 208 return; 209 } 210 211 const value = Number(button.getAttribute('data-option-value') || 0); 212 const selectedIndex = Number(button.getAttribute('data-option-index') || 0); 213 answers.set(questionIndex, { 214 value: value, 215 optionIndex: selectedIndex, 216 }); 217 applySelection(button); 218 219 if (config.mode === 'quiz') { 220 const questionConfig = (config.questions || [])[questionIndex] || {}; 221 const correctOption = Number(questionConfig.correctOption); 222 const correctButton = options.find(function (item) { 223 return Number(item.getAttribute('data-option-index')) === correctOption; 224 }); 225 if (selectedIndex === correctOption) { 226 button.classList.add('is-correct'); 227 } else { 228 button.classList.add('is-wrong'); 229 if (correctButton) { 230 correctButton.classList.add('is-correct'); 231 } 232 } 233 if (noteElement) { 234 noteElement.hidden = false; 235 } 236 } 237 238 updatePager(); 239 updateProgress(); 240 }); 241 242 button.addEventListener('keydown', function (event) { 243 if (event.key !== 'ArrowRight' && event.key !== 'ArrowDown' && event.key !== 'ArrowLeft' && event.key !== 'ArrowUp') { 244 return; 245 } 246 event.preventDefault(); 247 const direction = event.key === 'ArrowRight' || event.key === 'ArrowDown' ? 1 : -1; 248 const nextIndex = clamp(optionIndex + direction, 0, options.length - 1); 249 options[nextIndex].focus(); 250 }); 251 }); 252 253 const prevButton = questionElement.querySelector('[data-self-check-prev]'); 254 const nextButton = questionElement.querySelector('[data-self-check-next]'); 255 256 if (prevButton) { 257 prevButton.addEventListener('click', function () { 258 currentIndex = clamp(questionIndex - 1, 0, questionElements.length - 1); 259 refreshView(); 260 const target = questionElements[currentIndex].querySelector('[data-option-value]'); 261 if (target) {
262 target.focus(); 263 } 264 }); 265 } 266 267 if (nextButton) { 268 nextButton.addEventListener('click', function () { 269 if (!answers.has(questionIndex)) { 270 return; 271 } 272 const isLast = questionIndex === questionElements.length - 1; 273 if (isLast) { 274 renderResult(); 275 return; 276 } 277 currentIndex = clamp(questionIndex + 1, 0, questionElements.length - 1); 278 refreshView(); 279 const target = questionElements[currentIndex].querySelector('[data-option-value]'); 280 if (target) { 281 target.focus(); 282 } 283 }); 284 } 285 }); 286 287 if (restartButton) { 288 restartButton.addEventListener('click', function () { 289 resetAll(); 290 const target = questionElements[0] && questionElements[0].querySelector('[data-option-value]'); 291 if (target) { 292 target.focus(); 293 } 294 }); 295 } 296 297 refreshView(); 298 } 299 300 function initAllSelfChecks() { 301 document.querySelectorAll('[data-baytm-self-check]').forEach(initSelfCheck); 302 } 303 304 if (document.readyState === 'loading') { 305 document.addEventListener('DOMContentLoaded', initAllSelfChecks); 306 } else { 307 initAllSelfChecks(); 308 } 309})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.