-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexams.js
More file actions
167 lines (153 loc) · 5.59 KB
/
exams.js
File metadata and controls
167 lines (153 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
(function (global) {
'use strict';
const registry = new Map();
const normalizeId = (value) => (value || '').toString().trim().toLowerCase();
const requireFunction = (fn, name) => {
if (typeof fn !== 'function') {
throw new Error(`${name} must be a function.`);
}
return fn;
};
const ensureCreateExamApp = () => {
if (typeof global.createExamApp !== 'function') {
throw new Error('createExamApp is unavailable. Make sure exam-app.js is loaded first.');
}
return global.createExamApp;
};
const cloneConfig = (config) => {
if (!config || typeof config !== 'object') return {};
const clone = Array.isArray(config) ? [...config] : { ...config };
if (clone.messages) clone.messages = { ...clone.messages };
if (clone.figure) clone.figure = { ...clone.figure, sources: { ...(clone.figure.sources || {}) } };
return clone;
};
const register = (id, getConfig) => {
const key = normalizeId(id);
if (!key) {
throw new Error('An exam id is required to register a configuration.');
}
registry.set(key, requireFunction(getConfig, `Exam configuration for "${key}"`));
};
const resolve = (id) => {
const key = normalizeId(id);
if (!registry.has(key)) return null;
return cloneConfig(registry.get(key)());
};
const init = (id, overrides = {}) => {
const base = resolve(id);
if (!base) {
throw new Error(`Unknown exam id "${id}".`);
}
const config = Object.assign({}, base, overrides || {});
return ensureCreateExamApp()(config);
};
const list = () => Array.from(registry.keys());
const api = { register, resolve, init, list };
const setupDefaultExams = () => {
const poolApi = global.HAMLEARN_POOL;
if (!poolApi || typeof poolApi.load !== 'function') {
throw new Error('HAMLEARN_POOL must be available before registering exams.');
}
register('technician', () => ({
pageTitle: 'Hamlearn test generator',
totalQuestions: 35,
passingScore: 26,
loadPool: () => poolApi.load('technician'),
messages: {
landingTitle: 'Technician class exam',
runTitle: 'Technician Practice Test',
pass: 'congratulations, you passed!',
fail: 'Sorry, you failed. Better luck next time!',
scoreLine: (score, total) => `You scored ${score} out of ${total}.`,
},
figure: {
sources: {
T1: { src: 'tech-1.jpg', alt: 'Figure T1' },
T2: { src: 'tech-2.jpg', alt: 'Figure T2' },
T3: { src: 'tech-3.jpg', alt: 'Figure T3' },
},
hintText: 'Figure reference shown below.',
detect: (text, sources) => {
if (!text) return [];
const regex = /\bfigure\s*T\s*-?\s*([123])\b/gi;
const codes = new Set();
let match;
while ((match = regex.exec(text)) !== null) {
const key = `T${match[1]}`;
if (sources[key]) codes.add(key);
}
return Array.from(codes);
},
},
}));
register('general', () => ({
pageTitle: 'Hamlearn General exam',
totalQuestions: 35,
passingScore: 26,
loadPool: () => poolApi.load('general'),
messages: {
landingTitle: 'General class exam',
runTitle: 'General Practice Test',
pass: 'congratulations, you passed!',
fail: 'Sorry, you failed. Better luck next time!',
scoreLine: (score, total) => `You scored ${score} out of ${total}.`,
},
figure: {
sources: {
'G7-1': { src: 'gen-7-1.jpg', alt: 'Figure G7-1' },
},
hintText: 'Figure reference shown below.',
detect: (text, sources) => {
if (!text) return [];
const regex = /\bfigure\s*G\s*7\s*[-–]?\s*1\b/gi;
return regex.test(text)
? ['G7-1'].filter((key) => sources[key])
: [];
},
},
}));
register('extra', () => ({
pageTitle: 'Hamlearn Extra exam',
totalQuestions: 50,
passingScore: 37,
loadPool: () => poolApi.load('extra'),
messages: {
landingTitle: 'Extra class exam',
runTitle: 'Extra Practice Test',
pass: 'congratulations, you passed!',
fail: 'Sorry, you failed. Better luck next time!',
scoreLine: (score, total) => `You scored ${score} out of ${total}.`,
},
figure: {
sources: {
'E5-1': { src: 'E5-1.png', alt: 'Figure E5-1' },
'E6-1': { src: 'E6-1.png', alt: 'Figure E6-1' },
'E6-2': { src: 'E6-2.png', alt: 'Figure E6-2' },
'E6-3': { src: 'E6-3.png', alt: 'Figure E6-3' },
'E7-1': { src: 'E7-1.png', alt: 'Figure E7-1' },
'E7-2': { src: 'E7-2.png', alt: 'Figure E7-2' },
'E7-3': { src: 'E7-3.png', alt: 'Figure E7-3' },
'E9-1': { src: 'E9-1.png', alt: 'Figure E9-1' },
'E9-2': { src: 'E9-2.png', alt: 'Figure E9-2' },
'E9-3': { src: 'E9-3.png', alt: 'Figure E9-3' },
},
hintText: 'Figure reference shown below.',
detect: (text, sources) => {
if (!text) return [];
const regex = /\bfigure\s*E\s*([0-9]+)\s*[-–]?\s*([0-9]+)?\b/gi;
const codes = new Set();
let match;
while ((match = regex.exec(text)) !== null) {
const figNum = match[1];
const subNum = match[2] || '';
const key = `E${figNum}${subNum ? '-' + subNum : ''}`;
if (sources[key]) codes.add(key);
}
return Array.from(codes);
},
},
}));
};
global.HAMLEARN_EXAMS = api;
setupDefaultExams();
})(typeof window !== 'undefined' ? window : globalThis);