-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
439 lines (405 loc) · 16.1 KB
/
Copy pathscript.js
File metadata and controls
439 lines (405 loc) · 16.1 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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
const situations = [
{
id: "scuola",
title: "A scuola",
icon: "🏫",
description: "In aula con i compagni e la maestra."
},
{
id: "casa",
title: "A casa",
icon: "🏠",
description: "In salotto con la famiglia."
},
{
id: "parco",
title: "Al parco",
icon: "🌳",
description: "All'aperto tra giochi e altalene."
},
{
id: "viaggio",
title: "In viaggio",
icon: "🚌",
description: "In auto o in pulmino."
}
];
const events = [
{
id: "terremoto",
title: "Terremoto",
icon: "🌎",
description: "La terra trema!"
},
{
id: "incendio",
title: "Incendio",
icon: "🔥",
description: "C'\u00e8 del fumo o fuoco."
},
{
id: "pioggia",
title: "Pioggia forte",
icon: "🌧️",
description: "Piove tantissimo."
},
{
id: "calore",
title: "Ondata di calore",
icon: "☀️",
description: "Fa caldissimo."
}
];
const scenarios = {
scuola: {
terremoto: {
question: "Durante un terremoto a scuola, cosa fai per stare al sicuro?",
imagePrompt: "Bambini in una classe primaria si riparano sotto i banchi durante un terremoto",
actions: [
{ text: "Mi riparo sotto il banco proteggendo la testa.", correct: true },
{ text: "Corro fuori subito mentre tutto trema.", correct: false }
]
},
incendio: {
question: "Scatta l'allarme incendi a scuola, qual \u00e8 la scelta migliore?",
imagePrompt: "Classe elementare che segue l'insegnante in fila ordinata verso l'uscita durante un incendio",
actions: [
{ text: "Seguo l'insegnante in fila calma verso l'uscita di emergenza.", correct: true },
{ text: "Mi nascondo nell'armadio finch\u00e9 finisce tutto.", correct: false }
]
},
pioggia: {
question: "Fuori piove fortissimo, cosa fai mentre sei a scuola?",
imagePrompt: "Bambini in aula guardano la pioggia forte dalle finestre chiuse, atmosfera sicura e serena",
actions: [
{ text: "Resto in aula lontano dalle finestre e ascolto la maestra.", correct: true },
{ text: "Apro la finestra per guardare meglio la pioggia.", correct: false }
]
},
calore: {
question: "Fa molto caldo a scuola, qual \u00e8 la scelta giusta?",
imagePrompt: "Classe primaria con ventilatori e bambini che bevono acqua durante un'ondata di caldo",
actions: [
{ text: "Bevo acqua e resto in aula all'ombra, seguendo le indicazioni.", correct: true },
{ text: "Corro in cortile sotto il sole per prendere aria.", correct: false }
]
}
},
casa: {
terremoto: {
question: "Senti un terremoto mentre sei a casa, cosa fai?",
imagePrompt: "Famiglia in casa che si ripara sotto il tavolo durante un terremoto",
actions: [
{ text: "Mi proteggo sotto un tavolo robusto coprendo la testa.", correct: true },
{ text: "Mi metto vicino alla finestra per vedere fuori.", correct: false }
]
},
incendio: {
question: "C'\u00e8 un incendio in casa, qual \u00e8 la scelta giusta?",
imagePrompt: "Bambino che lascia la casa con un adulto seguendo il fumo verso l'uscita",
actions: [
{ text: "Esco subito con un adulto e chiudo la porta dietro di me.", correct: true },
{ text: "Torno a prendere i miei giocattoli preferiti.", correct: false }
]
},
pioggia: {
question: "Piove fortissimo e sei a casa, come ti comporti?",
imagePrompt: "Bambino chiude finestre durante pioggia intensa dentro casa accogliente",
actions: [
{ text: "Chiudo bene le finestre e resto lontano dai balconi.", correct: true },
{ text: "Esco sul balcone per guardare i fulmini.", correct: false }
]
},
calore: {
question: "C'\u00e8 un'ondata di calore, cosa fai a casa?",
imagePrompt: "Bimbo beve acqua e usa un ventilatore a casa durante un'ondata di calore",
actions: [
{ text: "Bevo acqua fresca e mi riposo in una stanza fresca.", correct: true },
{ text: "Chiudo tutte le finestre e gioco al sole sul balcone.", correct: false }
]
}
},
parco: {
terremoto: {
question: "Arriva un terremoto mentre sei al parco, cosa fai?",
imagePrompt: "Bambini in un parco che si allontanano da alberi e giochi durante un terremoto",
actions: [
{ text: "Mi allontano da alberi e giochi restando in uno spazio aperto.", correct: true },
{ text: "Mi rifugio sotto il grande albero vicino alla giostra.", correct: false }
]
},
incendio: {
question: "Vedi del fumo nel parco, qual \u00e8 la scelta migliore?",
imagePrompt: "Bambini notificano un adulto e si allontanano dal fumo in un parco",
actions: [
{ text: "Avviso un adulto e mi allontano dal fumo.", correct: true },
{ text: "Vado a controllare da vicino dove c'\u00e8 il fuoco.", correct: false }
]
},
pioggia: {
question: "Inizia una pioggia fortissima al parco, cosa fai?",
imagePrompt: "Bambini che corrono verso un riparo coperto durante un temporale al parco",
actions: [
{ text: "Cerco un riparo sicuro e chiuso con un adulto.", correct: true },
{ text: "Continuo a giocare sulle altalene bagnate.", correct: false }
]
},
calore: {
question: "Fa molto caldo al parco, qual \u00e8 la cosa giusta da fare?",
imagePrompt: "Bambino al parco beve da una borraccia all'ombra con cappellino",
actions: [
{ text: "Bevo dalla borraccia e gioco all'ombra.", correct: true },
{ text: "Corro al sole senza bere perch\u00e9 \u00e8 divertente.", correct: false }
]
}
},
viaggio: {
terremoto: {
question: "Senti tremare mentre sei in viaggio, cosa fai?",
imagePrompt: "Famiglia in auto accostata in sicurezza durante un terremoto",
actions: [
{ text: "Se possibile, il guidatore accosta e aspettiamo lontani da ponti.", correct: true },
{ text: "Scendo dall'auto in mezzo alla strada subito.", correct: false }
]
},
incendio: {
question: "Noti un incendio vicino alla strada, cosa fai durante il viaggio?",
imagePrompt: "Auto che si allontana da un incendio boschivo con calma",
actions: [
{ text: "Chiudiamo i finestrini e ci allontaniamo seguendo le indicazioni.", correct: true },
{ text: "Ci fermiamo a guardare da vicino il fuoco.", correct: false }
]
},
pioggia: {
question: "Piove fortissimo mentre sei in viaggio, qual \u00e8 la scelta corretta?",
imagePrompt: "Auto ferma in un luogo sicuro durante nubifragio, conducente usa luci d'emergenza",
actions: [
{ text: "Accostiamo in un luogo sicuro aspettando che diminuisca la pioggia.", correct: true },
{ text: "Andiamo pi\u00f9 veloci per finire la strada prima.", correct: false }
]
},
calore: {
question: "Fa molto caldo durante il viaggio, cosa fai per stare bene?",
imagePrompt: "Bambini in auto con aria condizionata e bottigliette d'acqua durante ondata di calore",
actions: [
{ text: "Bevo acqua e facciamo pause all'ombra se necessario.", correct: true },
{ text: "Tengo i finestrini chiusi senza aria e non bevo per non fermarci.", correct: false }
]
}
}
};
const state = {
score: 0,
currentStep: "step-situation",
selectedSituation: null,
selectedEvent: null,
answered: false
};
const stepSituation = document.getElementById("step-situation");
const stepEvent = document.getElementById("step-event");
const stepAction = document.getElementById("step-action");
const scoreEl = document.getElementById("score");
const imagePlaceholder = document.getElementById("generated-image");
const imagePrompt = document.getElementById("image-prompt");
const actionQuestion = document.getElementById("action-question");
const actionOptions = document.querySelector("#step-action .action-options");
const feedbackEl = document.getElementById("feedback");
const playAgainButton = document.getElementById("play-again");
let toastTimer = null;
let feedbackPopTimer = null;
const toastBanner = document.createElement("div");
toastBanner.className = "toast hidden";
toastBanner.setAttribute("role", "status");
toastBanner.setAttribute("aria-live", "polite");
document.body.appendChild(toastBanner);
function createCard({ id, title, icon, description }, type) {
const card = document.createElement("button");
card.className = `card card--${type}`;
card.setAttribute("type", "button");
card.setAttribute("role", "listitem");
card.dataset.id = id;
const iconEl = document.createElement("span");
iconEl.className = "card__icon";
iconEl.textContent = icon;
const titleEl = document.createElement("span");
titleEl.className = "card__title";
titleEl.textContent = title;
const descEl = document.createElement("span");
descEl.className = "card__description";
descEl.textContent = description;
card.append(iconEl, titleEl, descEl);
return card;
}
function populateStep(stepEl, items, type, onSelect) {
const grid = stepEl.querySelector(".card-grid");
grid.innerHTML = "";
items.forEach((item) => {
const card = createCard(item, type);
card.addEventListener("click", () => onSelect(item));
grid.appendChild(card);
});
}
function shuffleActions(actions) {
const items = actions.slice();
for (let i = items.length - 1; i > 0; i -= 1) {
const j = Math.floor(Math.random() * (i + 1));
[items[i], items[j]] = [items[j], items[i]];
}
return items;
}
function showStep(stepId) {
[stepSituation, stepEvent, stepAction].forEach((section) => {
section.classList.toggle("step--active", section.id === stepId);
});
state.currentStep = stepId;
}
function handleSituationSelect(situation) {
state.selectedSituation = situation.id;
state.selectedEvent = null;
feedbackEl.textContent = "";
feedbackEl.className = "feedback";
playAgainButton.classList.add("hidden");
showStep("step-event");
}
function handleEventSelect(event) {
state.selectedEvent = event.id;
state.answered = false;
feedbackEl.textContent = "";
feedbackEl.className = "feedback";
renderScenario();
showStep("step-action");
}
function renderScenario() {
const scenario = getScenario();
if (!scenario) {
actionQuestion.textContent = "Ops! Non abbiamo ancora creato un quiz per questa combinazione.";
imagePrompt.textContent = "Descrivi la scena della calamit\u00e0 per generare un'immagine.";
imagePlaceholder.textContent = "Aggiungi qui l'immagine generata con DALL-E";
actionOptions.innerHTML = "";
return;
}
actionQuestion.textContent = scenario.question;
imagePrompt.textContent = scenario.imagePrompt;
const imagePath = scenario.imageSrc ?? `images/${state.selectedSituation}-${state.selectedEvent}.png`;
imagePlaceholder.innerHTML = `<img src="${imagePath}" alt="${scenario.question}" class="scenario-image">`;
actionOptions.innerHTML = "";
const shuffledActions = shuffleActions(scenario.actions);
shuffledActions.forEach((action) => {
const optionBtn = document.createElement("button");
optionBtn.className = "action-option";
optionBtn.type = "button";
optionBtn.textContent = action.text;
optionBtn.dataset.correct = action.correct;
optionBtn.addEventListener("click", () => handleAnswer(optionBtn));
actionOptions.appendChild(optionBtn);
});
}
function handleAnswer(optionBtn) {
if (state.answered) {
return;
}
const correct = optionBtn.dataset.correct === "true";
state.answered = true;
const buttons = actionOptions.querySelectorAll("button");
buttons.forEach((btn) => {
btn.disabled = true;
const isCorrect = btn.dataset.correct === "true";
if (isCorrect) {
btn.classList.add("action-option--correct");
}
if (!isCorrect) {
if (btn === optionBtn) {
btn.classList.add("action-option--incorrect");
} else {
btn.classList.add("action-option--incorrect-passive");
}
}
});
if (correct) {
state.score += 1;
updateScore();
setFeedback("✅ Ben fatto! Questa \u00e8 la scelta pi\u00f9 sicura.", "correct");
showBanner("✅ Risposta corretta!", "correct");
} else {
setFeedback("\u26a0\ufe0f Quasi! Prova a ripensare a come restare al sicuro.", "incorrect");
showBanner("\u26a0\ufe0f Risposta errata", "incorrect");
}
playAgainButton.classList.remove("hidden");
}
function updateScore() {
scoreEl.textContent = state.score;
}
function resetToStart() {
state.selectedSituation = null;
state.selectedEvent = null;
state.answered = false;
feedbackEl.textContent = "";
feedbackEl.className = "feedback";
actionQuestion.textContent = "";
imagePrompt.textContent = "";
imagePlaceholder.textContent = "Scegli una situazione e un evento per vedere un'illustrazione.";
playAgainButton.classList.add("hidden");
const buttons = actionOptions.querySelectorAll("button");
buttons.forEach((btn) => btn.remove());
showStep("step-situation");
}
function showBanner(message, type) {
toastBanner.textContent = message;
toastBanner.classList.remove("hidden", "toast--correct", "toast--incorrect", "toast--visible");
toastBanner.classList.add(`toast--${type}`);
// force reflow to restart animation
void toastBanner.offsetWidth; // eslint-disable-line no-unused-expressions
toastBanner.classList.add("toast--visible");
if (toastTimer) {
clearTimeout(toastTimer);
}
toastTimer = setTimeout(() => {
toastBanner.classList.remove("toast--visible");
toastTimer = null;
}, 1200);
}
function setFeedback(message, type) {
feedbackEl.innerHTML = message;
feedbackEl.className = `feedback feedback--${type}`;
feedbackEl.classList.remove("feedback--pop");
void feedbackEl.offsetWidth; // restart animation
feedbackEl.classList.add("feedback--pop");
if (feedbackPopTimer) {
clearTimeout(feedbackPopTimer);
}
feedbackPopTimer = setTimeout(() => {
feedbackEl.classList.remove("feedback--pop");
feedbackPopTimer = null;
}, 400);
}
function getScenario() {
const { selectedSituation, selectedEvent } = state;
if (!selectedSituation || !selectedEvent) {
return null;
}
return scenarios[selectedSituation]?.[selectedEvent] ?? null;
}
function setupNavigation() {
document.querySelectorAll(".back-button").forEach((button) => {
button.addEventListener("click", () => {
const target = button.dataset.back;
if (target === "situation") {
showStep("step-situation");
}
if (target === "event") {
showStep("step-event");
}
});
});
playAgainButton.addEventListener("click", () => {
resetToStart();
});
}
function init() {
populateStep(stepSituation, situations, "situation", handleSituationSelect);
populateStep(stepEvent, events, "event", handleEventSelect);
playAgainButton.classList.add("hidden");
setupNavigation();
resetToStart();
}
init();