MediaWiki:Gadget-course.js: differenze tra le versioni
Pagina dell'interfaccia di MediaWiki
Altre azioni
Nessun oggetto della modifica |
Nuovi nomi classi |
||
| (2 versioni intermedie di uno stesso utente non sono mostrate) | |||
| Riga 2: | Riga 2: | ||
Port di assets/blocks.js + assets/quiz.js + assets/led-grid.js | Port di assets/blocks.js + assets/quiz.js + assets/led-grid.js | ||
per MediaWiki. Scoped: agisce solo sugli elementi con le classi attese. | per MediaWiki. Scoped: agisce solo sugli elementi con le classi attese. | ||
Tecnologia: legge la classe blk-<tech> sul blocco per scegliere la palette; | Tecnologia: legge la classe course-blk-<tech> sul blocco per scegliere la palette; | ||
fallback: classe course-<tech> sul wrapper. */ | fallback: classe course-<tech> sul wrapper. | ||
*/ | |||
(function () { | (function () { | ||
"use strict"; | "use strict"; | ||
| Riga 9: | Riga 10: | ||
var PALETTES = { | var PALETTES = { | ||
microbit: { | microbit: { | ||
basic: "#1e90ff", // Base | |||
input: "#d400d4", // Input | |||
music: "#e63022", // Musica | |||
} | led: "#5c2d91", // Led | ||
radio: "#e3008c", // Radio | |||
loops: "#00aa00", // Cicli | |||
logic: "#00a4a6", // Logica | |||
variables: "#dc143c", // Variabili | |||
math: "#9400d3", // Matematica | |||
game: "#007a4b", // Gioco | |||
pin: "#b22222", // Pin | |||
images: "#b8860b", // Immagini | |||
text: "#b8860b", // Testo (stringhe) | |||
serial: "#002050", // Seriale | |||
control: "#333333", // Controllo | |||
functions: "#3455db", // Funzioni | |||
arrays: "#e65722", // Array (elenchi) | |||
bluetooth: "#0082fb" // Bluetooth (non fornito, valore indicativo) | |||
} | |||
}; | }; | ||
| Riga 40: | Riga 56: | ||
function kindOf(el) { | function kindOf(el) { | ||
var c = classList(el); | var c = classList(el); | ||
if (c.indexOf("blk-hat") >= 0) return "hat"; | if (c.indexOf("course-blk-hat") >= 0) return "hat"; | ||
if (c.indexOf("blk-val") >= 0) return "val"; | if (c.indexOf("course-blk-val") >= 0) return "val"; | ||
if (c.indexOf("blk-bool") >= 0) return "bool"; | if (c.indexOf("course-blk-bool") >= 0) return "bool"; | ||
return "stmt"; | return "stmt"; | ||
} | } | ||
| Riga 50: | Riga 66: | ||
var tech = null; | var tech = null; | ||
for (var key in PALETTES) { | for (var key in PALETTES) { | ||
if (c.indexOf("blk-" + key) >= 0) tech = key; | if (c.indexOf("course-blk-" + key) >= 0) tech = key; | ||
} | } | ||
if (!tech) { | if (!tech) { | ||
| Riga 65: | Riga 81: | ||
var c = classList(el); | var c = classList(el); | ||
for (var key in palette) { | for (var key in palette) { | ||
if (c.indexOf("blk-" + key) >= 0) return palette[key]; | if (c.indexOf("course-blk-" + key) >= 0) return palette[key]; | ||
} | } | ||
return palette.basic; | return palette.basic; | ||
| Riga 97: | Riga 113: | ||
maskCircle = '<circle cx="' + W + '" cy="' + H / 2 + '" r="5" fill="#000"/>'; | maskCircle = '<circle cx="' + W + '" cy="' + H / 2 + '" r="5" fill="#000"/>'; | ||
} | } | ||
return '<svg class="blk-svg" viewBox="' + vbX + " " + vbY + " " + vbW + " " + vbH + | return '<svg class="course-blk-svg" viewBox="' + vbX + " " + vbY + " " + vbW + " " + vbH + | ||
'" width="' + vbW + '" height="' + vbH + '" role="img" aria-label="' + escapeHtml(text) + '">' + | '" width="' + vbW + '" height="' + vbH + '" role="img" aria-label="' + escapeHtml(text) + '">' + | ||
'<defs><mask id="' + id + '">' + | '<defs><mask id="' + id + '">' + | ||
| Riga 109: | Riga 125: | ||
function upgradeBlks(root) { | function upgradeBlks(root) { | ||
root.querySelectorAll(".blk").forEach(function (el) { | root.querySelectorAll(".course-blk").forEach(function (el) { | ||
if (el.querySelector("svg")) return; | if (el.querySelector("svg")) return; | ||
var text = (el.textContent || "").replace(/\s+/g, " ").trim(); | var text = (el.textContent || "").replace(/\s+/g, " ").trim(); | ||
| Riga 115: | Riga 131: | ||
var svg = build(text, kindOf(el), colorOf(el, root)); | var svg = build(text, kindOf(el), colorOf(el, root)); | ||
el.innerHTML = svg; | el.innerHTML = svg; | ||
el.classList.add("blk-upgraded"); | el.classList.add("course-blk-upgraded"); | ||
}); | }); | ||
} | } | ||
function wireQuiz(root) { | function wireQuiz(root) { | ||
root.querySelectorAll(".quiz-q").forEach(function (q) { | root.querySelectorAll(".course-quiz-q").forEach(function (q) { | ||
if (q.getAttribute("data-bound")) return; | if (q.getAttribute("data-bound")) return; | ||
q.setAttribute("data-bound", "1"); | q.setAttribute("data-bound", "1"); | ||
| Riga 126: | Riga 142: | ||
var okMsg = q.getAttribute("data-ok") || "Sì! Esatto. 👏"; | var okMsg = q.getAttribute("data-ok") || "Sì! Esatto. 👏"; | ||
var retryMsg = q.getAttribute("data-retry") || "Non ancora, riprova!"; | var retryMsg = q.getAttribute("data-retry") || "Non ancora, riprova!"; | ||
var feedback = q.querySelector(".quiz-feedback"); | var feedback = q.querySelector(".course-quiz-feedback"); | ||
function answerPick(btn) { | function answerPick(btn) { | ||
var correct = btn.getAttribute("data-opt") === answer; | var correct = btn.getAttribute("data-opt") === answer; | ||
btn.disabled = true; | btn.disabled = true; | ||
if (correct) { | if (correct) { | ||
btn.classList.add("correct"); | btn.classList.add("course-quiz-btn-correct"); | ||
q.querySelectorAll(".quiz-btn").forEach(function (b) { b.disabled = true; }); | q.querySelectorAll(".course-quiz-btn").forEach(function (b) { b.disabled = true; }); | ||
q.classList.add("answered"); | q.classList.add("course-quiz-answered"); | ||
if (feedback) { feedback.textContent = okMsg; feedback.className = "quiz-feedback ok"; } | if (feedback) { feedback.textContent = okMsg; feedback.className = "course-quiz-feedback course-quiz-feedback-ok"; } | ||
} else { | } else { | ||
btn.classList.add("wrong"); | btn.classList.add("course-quiz-btn-wrong"); | ||
if (feedback) { feedback.textContent = retryMsg; feedback.className = "quiz-feedback no"; } | if (feedback) { feedback.textContent = retryMsg; feedback.className = "course-quiz-feedback course-quiz-feedback-no"; } | ||
} | } | ||
} | } | ||
q.querySelectorAll(".quiz-btn").forEach(function (btn) { | q.querySelectorAll(".course-quiz-btn").forEach(function (btn) { | ||
btn.addEventListener("click", function () { answerPick(btn); }); | btn.addEventListener("click", function () { answerPick(btn); }); | ||
btn.addEventListener("keydown", function (e) { | btn.addEventListener("keydown", function (e) { | ||
| Riga 150: | Riga 166: | ||
function renderGrids(root) { | function renderGrids(root) { | ||
root.querySelectorAll(".ledgrid[data-leds]").forEach(function (grid) { | root.querySelectorAll(".course-ledgrid[data-leds]").forEach(function (grid) { | ||
if (grid.querySelector(".led")) return; | if (grid.querySelector(".course-led")) return; | ||
var pattern = (grid.getAttribute("data-leds") || "").replace(/[\/\s]+/g, ""); | var pattern = (grid.getAttribute("data-leds") || "").replace(/[\/\s]+/g, ""); | ||
for (var i = 0; i < 25; i++) { | for (var i = 0; i < 25; i++) { | ||
var led = document.createElement("span"); | var led = document.createElement("span"); | ||
led.className = "led" + (pattern[i] === "#" ? " on" : ""); | led.className = "course-led" + (pattern[i] === "#" ? " course-led-on" : ""); | ||
grid.appendChild(led); | grid.appendChild(led); | ||
} | } | ||
Versione attuale delle 20:02, 3 ago 2026
/* Gadget "course": rende interattivi blocchi MakeCode, quiz e griglie LED.
Port di assets/blocks.js + assets/quiz.js + assets/led-grid.js
per MediaWiki. Scoped: agisce solo sugli elementi con le classi attese.
Tecnologia: legge la classe course-blk-<tech> sul blocco per scegliere la palette;
fallback: classe course-<tech> sul wrapper.
*/
(function () {
"use strict";
var PALETTES = {
microbit: {
basic: "#1e90ff", // Base
input: "#d400d4", // Input
music: "#e63022", // Musica
led: "#5c2d91", // Led
radio: "#e3008c", // Radio
loops: "#00aa00", // Cicli
logic: "#00a4a6", // Logica
variables: "#dc143c", // Variabili
math: "#9400d3", // Matematica
game: "#007a4b", // Gioco
pin: "#b22222", // Pin
images: "#b8860b", // Immagini
text: "#b8860b", // Testo (stringhe)
serial: "#002050", // Seriale
control: "#333333", // Controllo
functions: "#3455db", // Funzioni
arrays: "#e65722", // Array (elenchi)
bluetooth: "#0082fb" // Bluetooth (non fornito, valore indicativo)
}
};
var FONT = "700 13px -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
var H = 26, PAD = 9, uid = 0;
function measure(text) {
try {
var c = document.createElement("canvas");
var ctx = c.getContext("2d");
if (!ctx) throw new Error("no canvas");
ctx.font = FONT;
return ctx.measureText(text).width;
} catch (e) {
return text.length * 7.5;
}
}
function escapeHtml(s) {
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
}
function classList(el) {
return (el.className || "").split(/\s+/).filter(Boolean);
}
function kindOf(el) {
var c = classList(el);
if (c.indexOf("course-blk-hat") >= 0) return "hat";
if (c.indexOf("course-blk-val") >= 0) return "val";
if (c.indexOf("course-blk-bool") >= 0) return "bool";
return "stmt";
}
function techOf(el, root) {
var c = classList(el);
var tech = null;
for (var key in PALETTES) {
if (c.indexOf("course-blk-" + key) >= 0) tech = key;
}
if (!tech) {
var w = root && root.className || document.body.className || "";
for (var k2 in PALETTES) {
if (w.indexOf("course-" + k2) >= 0) tech = k2;
}
}
return tech || Object.keys(PALETTES)[0];
}
function colorOf(el, root) {
var palette = PALETTES[techOf(el, root)];
var c = classList(el);
for (var key in palette) {
if (c.indexOf("course-blk-" + key) >= 0) return palette[key];
}
return palette.basic;
}
function build(text, kind, color) {
var tw = measure(text);
var W = Math.round(tw + PAD * 2);
var id = "blksvg" + (++uid);
var br = 5, nr = 5, vbX = 0, vbY = -br, vbW = W, vbH = H + br;
var shape = "", maskCircle = "";
if (kind === "stmt") {
shape = '<rect x="0" y="0" width="' + W + '" height="' + H + '" rx="4" fill="' + color + '"/>' +
'<circle cx="8" cy="0" r="' + br + '" fill="' + color + '"/>';
maskCircle = '<circle cx="8" cy="' + H + '" r="' + nr + '" fill="#000"/>';
} else if (kind === "hat") {
vbY = 0; vbH = H + 6;
shape = '<rect x="0" y="0" width="' + W + '" height="' + H + '" rx="8" fill="' + color + '"/>' +
'<circle cx="8" cy="0" r="' + br + '" fill="' + color + '"/>';
maskCircle = '<circle cx="8" cy="' + H + '" r="' + nr + '" fill="#000"/>';
} else if (kind === "val") {
vbX = -6; vbW = W + 6;
shape = '<rect x="0" y="0" width="' + W + '" height="' + H + '" rx="' + H / 2 + '" fill="' + color + '"/>' +
'<circle cx="0" cy="' + H / 2 + '" r="6" fill="' + color + '"/>';
maskCircle = '<circle cx="' + W + '" cy="' + H / 2 + '" r="6" fill="#000"/>';
} else {
vbX = -6; vbW = W + 12;
shape = '<path d="M0 ' + H / 2 + ' L8 0 L' + (W - 8) + ' 0 L' + W + ' ' + H / 2 +
' L' + (W - 8) + ' ' + H + ' L8 ' + H + ' Z" fill="' + color + '"/>' +
'<circle cx="0" cy="' + H / 2 + '" r="5" fill="' + color + '"/>';
maskCircle = '<circle cx="' + W + '" cy="' + H / 2 + '" r="5" fill="#000"/>';
}
return '<svg class="course-blk-svg" viewBox="' + vbX + " " + vbY + " " + vbW + " " + vbH +
'" width="' + vbW + '" height="' + vbH + '" role="img" aria-label="' + escapeHtml(text) + '">' +
'<defs><mask id="' + id + '">' +
'<rect x="-8" y="-8" width="' + (vbW + 16) + '" height="' + (vbH + 16) + '" fill="#fff"/>' +
maskCircle + "</mask></defs>" +
'<g mask="url(#' + id + ')">' + shape + "</g>" +
'<text x="' + W / 2 + '" y="' + H / 2 + '" dominant-baseline="central" text-anchor="middle" ' +
'fill="#fff" font-family="-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" ' +
'font-size="13" font-weight="700">' + escapeHtml(text) + "</text></svg>";
}
function upgradeBlks(root) {
root.querySelectorAll(".course-blk").forEach(function (el) {
if (el.querySelector("svg")) return;
var text = (el.textContent || "").replace(/\s+/g, " ").trim();
if (!text) return;
var svg = build(text, kindOf(el), colorOf(el, root));
el.innerHTML = svg;
el.classList.add("course-blk-upgraded");
});
}
function wireQuiz(root) {
root.querySelectorAll(".course-quiz-q").forEach(function (q) {
if (q.getAttribute("data-bound")) return;
q.setAttribute("data-bound", "1");
var answer = q.getAttribute("data-answer");
var okMsg = q.getAttribute("data-ok") || "Sì! Esatto. 👏";
var retryMsg = q.getAttribute("data-retry") || "Non ancora, riprova!";
var feedback = q.querySelector(".course-quiz-feedback");
function answerPick(btn) {
var correct = btn.getAttribute("data-opt") === answer;
btn.disabled = true;
if (correct) {
btn.classList.add("course-quiz-btn-correct");
q.querySelectorAll(".course-quiz-btn").forEach(function (b) { b.disabled = true; });
q.classList.add("course-quiz-answered");
if (feedback) { feedback.textContent = okMsg; feedback.className = "course-quiz-feedback course-quiz-feedback-ok"; }
} else {
btn.classList.add("course-quiz-btn-wrong");
if (feedback) { feedback.textContent = retryMsg; feedback.className = "course-quiz-feedback course-quiz-feedback-no"; }
}
}
q.querySelectorAll(".course-quiz-btn").forEach(function (btn) {
btn.addEventListener("click", function () { answerPick(btn); });
btn.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); answerPick(btn); }
});
});
});
}
function renderGrids(root) {
root.querySelectorAll(".course-ledgrid[data-leds]").forEach(function (grid) {
if (grid.querySelector(".course-led")) return;
var pattern = (grid.getAttribute("data-leds") || "").replace(/[\/\s]+/g, "");
for (var i = 0; i < 25; i++) {
var led = document.createElement("span");
led.className = "course-led" + (pattern[i] === "#" ? " course-led-on" : "");
grid.appendChild(led);
}
});
}
function init(root) {
var target = root || document;
upgradeBlks(target);
wireQuiz(target);
renderGrids(target);
}
window.course = { init: init };
if (typeof mw !== "undefined" && mw.hook) {
mw.hook("wikipage.content").add(function ($content) {
init(($content && $content[0]) || document);
});
} else {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function () { init(document); });
} else {
init(document);
}
}
})();