Voeg thematische kleurplaten en creatieve niveaus toe (v0.4.65-beta) #3
4 changed files with 103 additions and 25 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.3.64-beta
|
0.3.65-beta
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,9 @@
|
||||||
/* drawing layer on the board: sits ABOVE widgets like a transparency sheet,
|
/* drawing layer on the board: sits ABOVE widgets like a transparency sheet,
|
||||||
so you can annotate over everything; clicks pass through when pen is off */
|
so you can annotate over everything; clicks pass through when pen is off */
|
||||||
#boardInk{position:absolute; inset:0; pointer-events:none; z-index:4000;}
|
#boardInk{position:absolute; inset:0; pointer-events:none; z-index:4000;}
|
||||||
|
/* live-laag van de markeerstift: dekkend getekend, doorschijnend getoond;
|
||||||
|
bij het loslaten wordt de haal in één keer op de inktlaag gezet */
|
||||||
|
#boardMark{position:absolute; inset:0; pointer-events:none; z-index:4000; opacity:.32;}
|
||||||
body.pen #boardInk{pointer-events:auto; cursor:crosshair;}
|
body.pen #boardInk{pointer-events:auto; cursor:crosshair;}
|
||||||
body.pen.pen-text #boardInk{cursor:text;}
|
body.pen.pen-text #boardInk{cursor:text;}
|
||||||
body.pen.pen-hand #boardInk{pointer-events:none;} /* hand tool: interact with everything */
|
body.pen.pen-hand #boardInk{pointer-events:none;} /* hand tool: interact with everything */
|
||||||
|
|
@ -360,6 +363,16 @@
|
||||||
box-shadow:0 2px 6px rgba(0,0,0,.25);
|
box-shadow:0 2px 6px rgba(0,0,0,.25);
|
||||||
}
|
}
|
||||||
.dtool-opts button:hover{background:var(--accent-soft);}
|
.dtool-opts button:hover{background:var(--accent-soft);}
|
||||||
|
/* lengteknop toont de maat ("10 cm") en is dus breder dan een rond knopje */
|
||||||
|
.dtool-opts .do-wide{width:auto; min-width:48px; padding:0 9px; border-radius:12px;}
|
||||||
|
/* live hoek-uitlezing tijdens het draaien van liniaal/geodriehoek */
|
||||||
|
.dtool-deg{
|
||||||
|
display:none; position:absolute; left:50%; top:50%; margin:-16px 0 0 -24px;
|
||||||
|
width:48px; height:32px; line-height:32px; text-align:center; pointer-events:none;
|
||||||
|
background:rgba(30,50,90,.85); color:#fff; font-size:14px; font-weight:800;
|
||||||
|
border-radius:9px; z-index:3;
|
||||||
|
}
|
||||||
|
.dtool-deg.snapped{background:var(--green);}
|
||||||
.dtool-lens{z-index:4500;} /* the glass sits above the ink sheet */
|
.dtool-lens{z-index:4500;} /* the glass sits above the ink sheet */
|
||||||
.dtool-lens canvas{
|
.dtool-lens canvas{
|
||||||
border:5px solid #5a6c85; box-shadow:0 8px 22px rgba(0,0,0,.3);
|
border:5px solid #5a6c85; box-shadow:0 8px 22px rgba(0,0,0,.3);
|
||||||
|
|
|
||||||
|
|
@ -352,16 +352,27 @@ const bInk = document.createElement("canvas");
|
||||||
bInk.id = "boardInk";
|
bInk.id = "boardInk";
|
||||||
board.insertBefore(bInk, board.firstChild);
|
board.insertBefore(bInk, board.firstChild);
|
||||||
const bCtx = bInk.getContext("2d");
|
const bCtx = bInk.getContext("2d");
|
||||||
const bDpr = window.devicePixelRatio || 1;
|
/* aparte laag voor de markeerstift: dáár tekent hij dekkend, de laag zelf is
|
||||||
|
doorschijnend (CSS), en pas bij het loslaten wordt de hele haal in één keer
|
||||||
|
met 32% op de inktlaag gezet. Zo stapelen overlappende lijnstukjes binnen
|
||||||
|
één haal niet meer tot donkere vlekken op elke overgang. */
|
||||||
|
const bMark = document.createElement("canvas");
|
||||||
|
bMark.id = "boardMark";
|
||||||
|
board.insertBefore(bMark, bInk.nextSibling);
|
||||||
|
const bMarkCtx = bMark.getContext("2d");
|
||||||
|
let bDpr = window.devicePixelRatio || 1;
|
||||||
function bResize(){
|
function bResize(){
|
||||||
const w = board.clientWidth, h = board.clientHeight;
|
const w = board.clientWidth, h = board.clientHeight;
|
||||||
if(w<2 || h<2) return;
|
if(w<2 || h<2) return;
|
||||||
|
bDpr = window.devicePixelRatio || 1; /* vers: verhuizen naar ander scherm/zoom */
|
||||||
const keep = document.createElement("canvas");
|
const keep = document.createElement("canvas");
|
||||||
keep.width = bInk.width; keep.height = bInk.height;
|
keep.width = bInk.width; keep.height = bInk.height;
|
||||||
if(bInk.width) keep.getContext("2d").drawImage(bInk,0,0);
|
if(bInk.width) keep.getContext("2d").drawImage(bInk,0,0);
|
||||||
bInk.width = Math.round(w*bDpr); bInk.height = Math.round(h*bDpr);
|
bInk.width = Math.round(w*bDpr); bInk.height = Math.round(h*bDpr);
|
||||||
if(keep.width) bCtx.drawImage(keep,0,0);
|
if(keep.width) bCtx.drawImage(keep,0,0);
|
||||||
bCtx.lineCap = "round"; bCtx.lineJoin = "round";
|
bCtx.lineCap = "round"; bCtx.lineJoin = "round";
|
||||||
|
bMark.width = bInk.width; bMark.height = bInk.height;
|
||||||
|
bMarkCtx.lineCap = "round"; bMarkCtx.lineJoin = "round";
|
||||||
}
|
}
|
||||||
new ResizeObserver(bResize).observe(board);
|
new ResizeObserver(bResize).observe(board);
|
||||||
bResize();
|
bResize();
|
||||||
|
|
@ -555,11 +566,25 @@ function makeMovable(t){
|
||||||
const r = t.getBoundingClientRect();
|
const r = t.getBoundingClientRect();
|
||||||
const cx = r.left+r.width/2, cy = r.top+r.height/2;
|
const cx = r.left+r.width/2, cy = r.top+r.height/2;
|
||||||
const start = Math.atan2(e.clientY-cy, e.clientX-cx)*180/Math.PI - ang;
|
const start = Math.atan2(e.clientY-cy, e.clientX-cx)*180/Math.PI - ang;
|
||||||
|
/* klikt vast op veelvouden van 45° (waterpas/haaks in één beweging)
|
||||||
|
en toont tijdens het draaien de hoek in graden */
|
||||||
|
const badge = t.querySelector(".dtool-deg") || t.appendChild(Object.assign(document.createElement("div"), { className:"dtool-deg" }));
|
||||||
|
badge.style.display = "block";
|
||||||
const mv = ev=>{
|
const mv = ev=>{
|
||||||
ang = Math.atan2(ev.clientY-cy, ev.clientX-cx)*180/Math.PI - start;
|
let a = Math.atan2(ev.clientY-cy, ev.clientX-cx)*180/Math.PI - start;
|
||||||
|
const near = Math.round(a/45)*45;
|
||||||
|
if(Math.abs(a-near) < 4) a = near;
|
||||||
|
ang = a;
|
||||||
t.style.transform = `rotate(${ang}deg)`;
|
t.style.transform = `rotate(${ang}deg)`;
|
||||||
|
const shown = Math.round(((ang % 360) + 360) % 360);
|
||||||
|
badge.textContent = shown + "°";
|
||||||
|
badge.style.transform = `rotate(${-ang}deg)`; /* leesbaar houden */
|
||||||
|
badge.classList.toggle("snapped", Math.abs(((ang % 45) + 45) % 45) < 0.01);
|
||||||
|
};
|
||||||
|
const up = ()=>{
|
||||||
|
badge.style.display = "none";
|
||||||
|
document.removeEventListener("pointermove",mv); document.removeEventListener("pointerup",up);
|
||||||
};
|
};
|
||||||
const up = ()=>{ document.removeEventListener("pointermove",mv); document.removeEventListener("pointerup",up); };
|
|
||||||
document.addEventListener("pointermove",mv); document.addEventListener("pointerup",up);
|
document.addEventListener("pointermove",mv); document.addEventListener("pointerup",up);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -582,17 +607,21 @@ function toggleDTool(kind){
|
||||||
const t = document.createElement("div");
|
const t = document.createElement("div");
|
||||||
t.className = "dtool dtool-"+kind;
|
t.className = "dtool dtool-"+kind;
|
||||||
if(kind==="ruler"){
|
if(kind==="ruler"){
|
||||||
t._len = 600; t._hc = false;
|
/* 50px = 1 cm; logische lengtes in hele centimeters, schaal begint bij 0
|
||||||
|
met een kleine kantlijn zoals op een echte liniaal */
|
||||||
|
const RULER_CMS = [5, 10, 15, 20];
|
||||||
|
t._cm = 10; t._hc = false;
|
||||||
const drawRuler = ()=>{
|
const drawRuler = ()=>{
|
||||||
const L = t._len, hc = t._hc;
|
const cmLen = Math.min(t._cm, Math.max(5, Math.floor((board.clientWidth-60)/50)));
|
||||||
|
const pad = 18, scale = cmLen*50, L = scale + pad*2, hc = t._hc;
|
||||||
const ink = hc ? "#000" : "#5a4a20";
|
const ink = hc ? "#000" : "#5a4a20";
|
||||||
const bg = hc ? "rgba(255,214,0,.95)" : "rgba(255,243,205,.82)";
|
const bg = hc ? "rgba(255,214,0,.95)" : "rgba(255,243,205,.82)";
|
||||||
let ticks = "";
|
let ticks = "";
|
||||||
for(let i=5; i<L; i+=5){
|
for(let i=0; i<=scale; i+=5){
|
||||||
const cm = i%50===0, half = i%25===0;
|
const cm = i%50===0, half = i%25===0;
|
||||||
const len = cm ? 22 : half ? 14 : 8;
|
const len = cm ? 22 : half ? 14 : 8;
|
||||||
ticks += `<line x1="${i}" y1="2" x2="${i}" y2="${2+len}" stroke="${ink}" stroke-width="${cm ? (hc?2.5:1.6) : (hc?1.6:1)}"/>`;
|
ticks += `<line x1="${pad+i}" y1="2" x2="${pad+i}" y2="${2+len}" stroke="${ink}" stroke-width="${cm ? (hc?2.5:1.6) : (hc?1.6:1)}"/>`;
|
||||||
if(cm) ticks += `<text x="${i}" y="${hc?46:42}" font-size="${hc?18:14}" font-weight="bold" fill="${ink}" text-anchor="middle">${i/50}</text>`;
|
if(cm) ticks += `<text x="${pad+i}" y="${hc?46:42}" font-size="${hc?18:14}" font-weight="bold" fill="${ink}" text-anchor="middle">${i/50}</text>`;
|
||||||
}
|
}
|
||||||
t.style.width = L+"px";
|
t.style.width = L+"px";
|
||||||
t.querySelector(".dtool-svg").innerHTML = `<svg viewBox="0 0 ${L} 76" width="${L}" height="76">
|
t.querySelector(".dtool-svg").innerHTML = `<svg viewBox="0 0 ${L} 76" width="${L}" height="76">
|
||||||
|
|
@ -602,18 +631,23 @@ function toggleDTool(kind){
|
||||||
<rect x="0" y="0" width="${L}" height="76" rx="9" fill="${bg}" stroke="${ink}" stroke-width="${hc?3:1.5}"/>
|
<rect x="0" y="0" width="${L}" height="76" rx="9" fill="${bg}" stroke="${ink}" stroke-width="${hc?3:1.5}"/>
|
||||||
<rect x="0" y="0" width="${L}" height="76" rx="9" fill="url(#rg)"/>
|
<rect x="0" y="0" width="${L}" height="76" rx="9" fill="url(#rg)"/>
|
||||||
<line x1="0" y1="2" x2="${L}" y2="2" stroke="${ink}" stroke-width="${hc?3:2}"/>
|
<line x1="0" y1="2" x2="${L}" y2="2" stroke="${ink}" stroke-width="${hc?3:2}"/>
|
||||||
<text x="${L/2}" y="66" font-size="12" fill="${ink}" text-anchor="middle" opacity=".65">teach · cm</text>
|
<text x="${L/2}" y="68" font-size="12" fill="${ink}" text-anchor="middle" opacity=".65">${cmLen} cm</text>
|
||||||
${ticks}</svg>`;
|
${ticks}</svg>`;
|
||||||
|
const lenBtn = t.querySelector(".do-len");
|
||||||
|
lenBtn.textContent = cmLen + " cm";
|
||||||
|
lenBtn.title = T("dtLen");
|
||||||
};
|
};
|
||||||
t.innerHTML = `<div class="dtool-svg"></div>
|
t.innerHTML = `<div class="dtool-svg"></div>
|
||||||
<div class="dtool-opts">
|
<div class="dtool-opts">
|
||||||
<button class="do-len" title="↔">↔</button>
|
<button class="do-len do-wide"></button>
|
||||||
<button class="do-hc" title="◐">◐</button>
|
<button class="do-hc">◐</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
t.querySelector(".do-len").addEventListener("click", ()=>{
|
t.querySelector(".do-len").addEventListener("click", ()=>{
|
||||||
t._len = t._len===600 ? 800 : t._len===800 ? 400 : 600; drawRuler();
|
t._cm = RULER_CMS[(RULER_CMS.indexOf(t._cm)+1) % RULER_CMS.length]; drawRuler();
|
||||||
});
|
});
|
||||||
t.querySelector(".do-hc").addEventListener("click", ()=>{ t._hc = !t._hc; drawRuler(); });
|
const hcBtn = t.querySelector(".do-hc");
|
||||||
|
hcBtn.title = T("dtContrast");
|
||||||
|
hcBtn.addEventListener("click", ()=>{ t._hc = !t._hc; drawRuler(); });
|
||||||
t.style.height = "76px";
|
t.style.height = "76px";
|
||||||
drawRuler();
|
drawRuler();
|
||||||
}else if(kind==="geo"){
|
}else if(kind==="geo"){
|
||||||
|
|
@ -663,10 +697,14 @@ function toggleDTool(kind){
|
||||||
<button class="do-len">↔</button>
|
<button class="do-len">↔</button>
|
||||||
<button class="do-hc">◐</button>
|
<button class="do-hc">◐</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
t.querySelector(".do-len").addEventListener("click", ()=>{
|
const geoLen = t.querySelector(".do-len");
|
||||||
|
geoLen.title = T("dtLen");
|
||||||
|
geoLen.addEventListener("click", ()=>{
|
||||||
t._sz = t._sz===340 ? 440 : t._sz===440 ? 280 : 340; drawGeo();
|
t._sz = t._sz===340 ? 440 : t._sz===440 ? 280 : 340; drawGeo();
|
||||||
});
|
});
|
||||||
t.querySelector(".do-hc").addEventListener("click", ()=>{ t._hc = !t._hc; drawGeo(); });
|
const geoHc = t.querySelector(".do-hc");
|
||||||
|
geoHc.title = T("dtContrast");
|
||||||
|
geoHc.addEventListener("click", ()=>{ t._hc = !t._hc; drawGeo(); });
|
||||||
drawGeo();
|
drawGeo();
|
||||||
}else if(kind==="light"){ /* compact traffic light directly on the board */
|
}else if(kind==="light"){ /* compact traffic light directly on the board */
|
||||||
let on = "green";
|
let on = "green";
|
||||||
|
|
@ -794,7 +832,7 @@ function startBoardText(e){
|
||||||
inp.addEventListener("blur", commit);
|
inp.addEventListener("blur", commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
let bDraw = false, bx = 0, by = 0;
|
let bDraw = false, bx = 0, by = 0, bMidX = null, bMidY = null, bUsedMark = false;
|
||||||
const bPos = ev=>{
|
const bPos = ev=>{
|
||||||
const r = bInk.getBoundingClientRect();
|
const r = bInk.getBoundingClientRect();
|
||||||
/* ratio-based: correct under any display zoom */
|
/* ratio-based: correct under any display zoom */
|
||||||
|
|
@ -808,6 +846,7 @@ bInk.addEventListener("pointerdown", e=>{
|
||||||
bDraw = true;
|
bDraw = true;
|
||||||
PEN.nib = PEN.size*0.8;
|
PEN.nib = PEN.size*0.8;
|
||||||
[bx,by] = bPos(e);
|
[bx,by] = bPos(e);
|
||||||
|
bMidX = bMidY = null;
|
||||||
bStroke(bx+0.01, by+0.01, e.pressure, e.pointerType);
|
bStroke(bx+0.01, by+0.01, e.pressure, e.pointerType);
|
||||||
});
|
});
|
||||||
bInk.addEventListener("pointermove", e=>{
|
bInk.addEventListener("pointermove", e=>{
|
||||||
|
|
@ -817,7 +856,20 @@ bInk.addEventListener("pointermove", e=>{
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const bStop = ()=>{
|
const bStop = ()=>{
|
||||||
if(bDraw) markBoardChange();
|
if(bDraw){
|
||||||
|
/* markeerstift: de haal staat dekkend op de overlay - nu in één keer
|
||||||
|
met vaste transparantie op de inktlaag zetten (egale kleur, geen
|
||||||
|
donkere overgangen) en de overlay leegmaken */
|
||||||
|
if(bUsedMark && bMark.width){
|
||||||
|
bCtx.globalCompositeOperation = "source-over";
|
||||||
|
bCtx.globalAlpha = 0.32;
|
||||||
|
bCtx.drawImage(bMark, 0, 0);
|
||||||
|
bCtx.globalAlpha = 1;
|
||||||
|
bMarkCtx.clearRect(0, 0, bMark.width, bMark.height);
|
||||||
|
}
|
||||||
|
bUsedMark = false;
|
||||||
|
markBoardChange();
|
||||||
|
}
|
||||||
bDraw = false;
|
bDraw = false;
|
||||||
bCtx.globalAlpha = 1;
|
bCtx.globalAlpha = 1;
|
||||||
if(DTOOLS.lens) DTOOLS.lens.lensDraw();
|
if(DTOOLS.lens) DTOOLS.lens.lensDraw();
|
||||||
|
|
@ -825,12 +877,15 @@ const bStop = ()=>{
|
||||||
bInk.addEventListener("pointerup", bStop);
|
bInk.addEventListener("pointerup", bStop);
|
||||||
bInk.addEventListener("pointercancel", bStop);
|
bInk.addEventListener("pointercancel", bStop);
|
||||||
function bStroke(x,y,pressure,ptype){
|
function bStroke(x,y,pressure,ptype){
|
||||||
bCtx.globalCompositeOperation = PEN.tool==="erase" ? "destination-out" : "source-over";
|
const mark = PEN.tool==="mark";
|
||||||
bCtx.globalAlpha = PEN.tool==="mark" ? 0.32 : 1;
|
if(mark) bUsedMark = true;
|
||||||
bCtx.strokeStyle = PEN.color;
|
const ctx = mark ? bMarkCtx : bCtx; /* markeerstift op de eigen laag */
|
||||||
|
ctx.globalCompositeOperation = PEN.tool==="erase" ? "destination-out" : "source-over";
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
ctx.strokeStyle = PEN.color;
|
||||||
let w = PEN.size;
|
let w = PEN.size;
|
||||||
if(PEN.tool==="erase") w = PEN.size*6;
|
if(PEN.tool==="erase") w = PEN.size*6;
|
||||||
else if(PEN.tool==="mark") w = PEN.size*3.5;
|
else if(mark) w = PEN.size*3.5;
|
||||||
else if(PEN.tool==="vulp"){
|
else if(PEN.tool==="vulp"){
|
||||||
let target;
|
let target;
|
||||||
if(ptype==="pen" && pressure > 0){
|
if(ptype==="pen" && pressure > 0){
|
||||||
|
|
@ -842,8 +897,16 @@ function bStroke(x,y,pressure,ptype){
|
||||||
PEN.nib += (target - PEN.nib)*0.3;
|
PEN.nib += (target - PEN.nib)*0.3;
|
||||||
w = PEN.nib;
|
w = PEN.nib;
|
||||||
}
|
}
|
||||||
bCtx.lineWidth = w*bDpr;
|
ctx.lineWidth = w*bDpr;
|
||||||
bCtx.beginPath(); bCtx.moveTo(bx,by); bCtx.lineTo(x,y); bCtx.stroke();
|
/* vloeiende haal: teken van middelpunt naar middelpunt met het vorige
|
||||||
|
punt als stuurpunt (kwadratische curve) - geen hoekige knikken meer
|
||||||
|
tussen de losse pointer-stapjes, ook bij snelle halen */
|
||||||
|
const mx = (bx + x) / 2, my = (by + y) / 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(bMidX ?? bx, bMidY ?? by);
|
||||||
|
ctx.quadraticCurveTo(bx, by, mx, my);
|
||||||
|
ctx.stroke();
|
||||||
|
bMidX = mx; bMidY = my;
|
||||||
bx = x; by = y;
|
bx = x; by = y;
|
||||||
}
|
}
|
||||||
function boardInkURL(){ try{ return bInk.toDataURL("image/png"); }catch(e){ return null; } }
|
function boardInkURL(){ try{ return bInk.toDataURL("image/png"); }catch(e){ return null; } }
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
/* version — shown until /api/version resolves (or if the fetch fails, e.g. offline).
|
/* version — shown until /api/version resolves (or if the fetch fails, e.g. offline).
|
||||||
Kept in sync by hand with the VERSION file at the repo root on every release. */
|
Kept in sync by hand with the VERSION file at the repo root on every release. */
|
||||||
const VERSION = "0.3.64-beta";
|
const VERSION = "0.3.65-beta";
|
||||||
(function(){
|
(function(){
|
||||||
const tag = document.getElementById("verTag");
|
const tag = document.getElementById("verTag");
|
||||||
tag.textContent = "v"+VERSION;
|
tag.textContent = "v"+VERSION;
|
||||||
|
|
@ -231,6 +231,7 @@ const I18N = {
|
||||||
parentUnlink:"Ontkoppel",
|
parentUnlink:"Ontkoppel",
|
||||||
parentUnlinkWarn:"Verwijdert alleen de koppeling; het account van je kind blijft bestaan.",
|
parentUnlinkWarn:"Verwijdert alleen de koppeling; het account van je kind blijft bestaan.",
|
||||||
parentProgress:"📈 Voortgang", parentProgressEmpty:"Nog geen voortgang.",
|
parentProgress:"📈 Voortgang", parentProgressEmpty:"Nog geen voortgang.",
|
||||||
|
dtLen:"Lengte", dtContrast:"Hoog contrast",
|
||||||
pupilEmpty:"Je leerkracht heeft nog niets voor je klaargezet.",
|
pupilEmpty:"Je leerkracht heeft nog niets voor je klaargezet.",
|
||||||
pupilWatching:"meekijken",
|
pupilWatching:"meekijken",
|
||||||
amUsers:"Gebruikers", amClasses:"Klassen", amSchools:"Scholen", amOverarching:"Systeem",
|
amUsers:"Gebruikers", amClasses:"Klassen", amSchools:"Scholen", amOverarching:"Systeem",
|
||||||
|
|
@ -489,6 +490,7 @@ const I18N = {
|
||||||
parentUnlink:"Unlink",
|
parentUnlink:"Unlink",
|
||||||
parentUnlinkWarn:"Removes only the link; your child's account remains.",
|
parentUnlinkWarn:"Removes only the link; your child's account remains.",
|
||||||
parentProgress:"📈 Progress", parentProgressEmpty:"No progress yet.",
|
parentProgress:"📈 Progress", parentProgressEmpty:"No progress yet.",
|
||||||
|
dtLen:"Length", dtContrast:"High contrast",
|
||||||
pupilEmpty:"Your teacher hasn't set up anything for you yet.",
|
pupilEmpty:"Your teacher hasn't set up anything for you yet.",
|
||||||
pupilWatching:"watching",
|
pupilWatching:"watching",
|
||||||
amUsers:"Users", amClasses:"Classes", amSchools:"Schools", amOverarching:"System",
|
amUsers:"Users", amClasses:"Classes", amSchools:"Schools", amOverarching:"System",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue