455 lines
17 KiB
JavaScript
455 lines
17 KiB
JavaScript
'use strict';
|
||
|
||
document.getElementById('kumoa').addEventListener('click', () => {
|
||
suorita(_tietokanta.kumoa());
|
||
});
|
||
|
||
document.getElementById('luokat-uusi').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
const luokanNimi = document.getElementById('luokat-uusi-nimi').value;
|
||
t.lisää(taulut.luokat, luokanNimi)
|
||
document.getElementById('luokat-uusi-nimi').value = '';
|
||
}));
|
||
});
|
||
|
||
document.getElementById('opettajat-uusi').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
const nimi = document.getElementById('opettajat-uusi-nimi').value;
|
||
const lyhenne = document.getElementById('opettajat-uusi-lyhenne').value;
|
||
t.lisää(taulut.opettajat, {nimi, lyhenne});
|
||
document.getElementById('opettajat-uusi-nimi').value = '';
|
||
document.getElementById('opettajat-uusi-lyhenne').value = '';
|
||
}));
|
||
});
|
||
|
||
document.getElementById('opettajat-uusi-nimi').addEventListener('change', () => {
|
||
const nimi = document.getElementById('opettajat-uusi-nimi').value;
|
||
// TODO: Kunnollinen tuki grafeemiklustereille
|
||
// TODO: Älä ehdota lyhennettä, joka on jo käytössä
|
||
const lyhenne = nimi.split(' ')
|
||
.map((x) => String.fromCodePoint(x.codePointAt(0))).join('');
|
||
document.getElementById('opettajat-uusi-lyhenne').value = lyhenne;
|
||
});
|
||
|
||
document.getElementById('tilat-uusi').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
const tilanNimi = document.getElementById('tilat-uusi-nimi').value;
|
||
t.lisää(taulut.tilat, tilanNimi)
|
||
document.getElementById('tilat-uusi-nimi').value = '';
|
||
}));
|
||
});
|
||
|
||
document.getElementById('tunnit-uusi').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
const nimi = document.getElementById('tunnit-uusi-nimi').value;
|
||
const kertaa =
|
||
Number.parseInt(document.getElementById('tunnit-uusi-kertaa').value);
|
||
const luokat = valitutHTMLLuokalla('tunnit-uusi-luokka');
|
||
const opettajat = valitutHTMLLuokalla('tunnit-uusi-opettaja');
|
||
const tilat = valitutHTMLLuokalla('tunnit-uusi-tila');
|
||
t.lisää(taulut.tunnit, {
|
||
nimi, luokat, opettajat, tilat,
|
||
milloin: new Array(kertaa).fill(null),
|
||
});
|
||
document.getElementById('tunnit-uusi-nimi').value = '';
|
||
document.getElementById('tunnit-uusi-kertaa').value = 1;
|
||
for (const valinta of document.getElementsByClassName('tunnit-uusi-valinta')) {
|
||
valinta.checked = false;
|
||
}
|
||
}));
|
||
});
|
||
|
||
document.getElementById('lukkari-valinta').addEventListener('input', () => {
|
||
päivitäLukkari();
|
||
});
|
||
|
||
let raahattava;
|
||
document.addEventListener('dragstart', (e) => {
|
||
raahattava = e.target;
|
||
});
|
||
|
||
function teePudotuskohteeksi(elementti) {
|
||
elementti.addEventListener('dragover', (e) => {
|
||
e.preventDefault();
|
||
});
|
||
|
||
elementti.addEventListener('drop', (e) => {
|
||
e.preventDefault();
|
||
const raahattavaId = ilmanPrefiksiä('lukkari-tunti-', raahattava.id);
|
||
const [tunti, toteutus] = raahattavaId.split('-').map((x) => Number.parseInt(x));
|
||
// XXX: Transaktio ja suoritus
|
||
if (elementti.id !== 'sijoittamattomat') {
|
||
const kohdeId = ilmanPrefiksiä('lukkari-solu-', elementti.id);
|
||
const ajoitus = kohdeId.split('-').map((x) => Number.parseInt(x));
|
||
_tietokanta.hae(taulut.tunnit, tunti).milloin[toteutus] = ajoitus;
|
||
} else {
|
||
_tietokanta.hae(taulut.tunnit, tunti).milloin[toteutus] = null;
|
||
}
|
||
päivitäLukkari();
|
||
});
|
||
}
|
||
|
||
function ilmanPrefiksiä(prefiksi, teksti) {
|
||
if (!teksti.startsWith(prefiksi)) {
|
||
throw new Error(`"${teksti}" ei ala prefiksillä "${prefiksi}"`);
|
||
}
|
||
return teksti.slice(prefiksi.length);
|
||
}
|
||
|
||
function valitutHTMLLuokalla(htmlLuokka) {
|
||
const valitut = [];
|
||
for (const valinta of document.getElementsByClassName(htmlLuokka)) {
|
||
if (valinta.checked) {
|
||
valitut.push(Number.parseInt(valinta.value));
|
||
}
|
||
}
|
||
return valitut;
|
||
}
|
||
|
||
function suorita([tietokanta, muutokset]) {
|
||
for (const muutos of muutokset) {
|
||
suoritaMuutos(tietokanta, muutos);
|
||
}
|
||
tallennaTietokanta(tietokanta);
|
||
päivitäLukkari();
|
||
}
|
||
|
||
function suoritaMuutos(tietokanta, muutos) {
|
||
const {taulu, id, vanha, uusi} = muutos;
|
||
if (taulu === taulut.luokat && vanha === undefined) {
|
||
// Uusi luokka
|
||
const seuraavaId = idJälkeen(tietokanta, taulu, id, vertaa);
|
||
const luokatLista = document.getElementById('luokat-lista');
|
||
// getElementById palauttaa null:n, jos id:tä ei löydy. Jos tämä luokka
|
||
// on viimeinen, seuraavaId on undefined, eikä DOM:ssa ole luokkaa
|
||
// "luokka-undefined". seuraava on siis null silloin kuin tämä luokka
|
||
// tulee lisätä listan loppuun, joka vastaa insertBefore:n toimintaa
|
||
let seuraava = document.getElementById(`luokka-${seuraavaId}`);
|
||
luokatLista.insertBefore(luoLuokka(tietokanta, id, uusi), seuraava);
|
||
const tunnitUusiLuokat = document.getElementById('tunnit-uusi-luokat');
|
||
seuraava = document.getElementById(`tunnit-uusi-luokka-${seuraavaId}`);
|
||
tunnitUusiLuokat.insertBefore(luoLuokkaValinta(id, uusi), seuraava);
|
||
const lukkariValintaLuokat = document.getElementById('lukkari-valinta-luokat');
|
||
seuraava = document.getElementById(`lukkari-valinta-luokka-${seuraavaId}`);
|
||
lukkariValintaLuokat.insertBefore(luoLuokkaVaihtoehto(id, uusi), seuraava);
|
||
} else if (taulu === taulut.luokat && uusi === undefined) {
|
||
// Luokka poistettu
|
||
poistaElementti(document.getElementById(`luokka-${id}`));
|
||
poistaElementti(document.getElementById(`tunnit-uusi-luokka-${id}`));
|
||
poistaElementti(document.getElementById(`lukkari-valinta-luokka-${id}`));
|
||
// TODO: luokka muutos
|
||
} else if (taulu === taulut.opettajat && vanha === undefined) {
|
||
// Uusi opettaja
|
||
const seuraavaId = idJälkeen(tietokanta, taulu, id,
|
||
(a, b) => vertaa(a.nimi, b.nimi)
|
||
);
|
||
const opettajatLista = document.getElementById('opettajat-lista');
|
||
// ks. kommentti uuden luokan tapauksessa
|
||
let seuraava = document.getElementById(`opettaja-${seuraavaId}`);
|
||
opettajatLista.insertBefore(luoOpettaja(tietokanta, id, uusi), seuraava);
|
||
const tunnitUusiOpettajat = document.getElementById('tunnit-uusi-opettajat');
|
||
seuraava = document.getElementById(`tunnit-uusi-opettaja-${seuraavaId}`);
|
||
tunnitUusiOpettajat.insertBefore(luoOpettajaValinta(id, uusi), seuraava);
|
||
const lukkariValintaOpettajat = document.getElementById('lukkari-valinta-opettajat');
|
||
seuraava = document.getElementById(`lukkari-valinta-opettaja-${seuraavaId}`);
|
||
lukkariValintaOpettajat.insertBefore(luoOpettajaVaihtoehto(id, uusi), seuraava);
|
||
} else if (taulu === taulut.opettajat && uusi === undefined) {
|
||
// Opettaja poistettu
|
||
poistaElementti(document.getElementById(`opettaja-${id}`));
|
||
poistaElementti(document.getElementById(`tunnit-uusi-opettaja-${id}`));
|
||
poistaElementti(document.getElementById(`lukkari-valinta-opettaja-${id}`));
|
||
// TODO: opettaja muutos
|
||
} else if (taulu === taulut.tilat && vanha === undefined) {
|
||
// Uusi tila
|
||
const seuraavaId = idJälkeen(tietokanta, taulu, id, vertaa);
|
||
const tilatLista = document.getElementById('tilat-lista');
|
||
// ks. kommentti uuden luokan tapauksessa
|
||
let seuraava = document.getElementById(`tila-${seuraavaId}`);
|
||
tilatLista.insertBefore(luoTila(tietokanta, id, uusi), seuraava);
|
||
const tunnitUusiTilat = document.getElementById(`tunnit-uusi-tilat`);
|
||
seuraava = document.getElementById(`tunnit-uusi-tila-${seuraavaId}`);
|
||
tunnitUusiTilat.insertBefore(luoTilaValinta(id, uusi), seuraava);
|
||
const lukkariValintaTilat = document.getElementById('lukkari-valinta-tilat');
|
||
seuraava = document.getElementById(`lukkari-valinta-tila-${seuraavaId}`);
|
||
lukkariValintaTilat.insertBefore(luoTilaVaihtoehto(id, uusi), seuraava);
|
||
} else if (taulu === taulut.tilat && uusi === undefined) {
|
||
// Tila poistettu
|
||
poistaElementti(document.getElementById(`tila-${id}`));
|
||
poistaElementti(document.getElementById(`tunnit-uusi-tila-${id}`));
|
||
poistaElementti(document.getElementById(`lukkari-valinta-tila-${id}`));
|
||
// TODO: tila muutos
|
||
} else if (taulu === taulut.tunnit && vanha === undefined) {
|
||
// Uusi tunti
|
||
const seuraavaId = idJälkeen(tietokanta, taulu, id,
|
||
(a, b) => vertaa(tuntiTeksti(tietokanta, a), tuntiTeksti(tietokanta, b))
|
||
);
|
||
const tunnitLista = document.getElementById('tunnit-lista');
|
||
// ks. kommentti uuden luokan tapauksessa
|
||
const seuraava = document.getElementById(`tunti-${seuraavaId}`);
|
||
tunnitLista.insertBefore(luoTunti(tietokanta, id, uusi), seuraava);
|
||
} else if (taulu === taulut.tunnit && uusi === undefined) {
|
||
poistaElementti(document.getElementById(`tunti-${id}`));
|
||
// TODO: tunti muutos
|
||
} else {
|
||
throw new Error(`Ei toteutettu ${taulu} ${id} ${vanha} ${uusi}`);
|
||
}
|
||
}
|
||
|
||
function idJälkeen(tietokanta, taulu, id, vertaa) {
|
||
const järjestys = tietokanta.järjestyksessä(taulu, vertaa);
|
||
return järjestys[järjestys.indexOf(id) + 1];
|
||
}
|
||
|
||
function vertaa(a, b) {
|
||
// TODO: Parempi vertailufunktio?
|
||
return a.localeCompare(b);
|
||
}
|
||
|
||
function poistaElementti(elementti) {
|
||
elementti.parentElement.removeChild(elementti);
|
||
}
|
||
|
||
function luoLuokka(tietokanta, id, nimi) {
|
||
const li = document.createElement('li');
|
||
li.id = `luokka-${id}`;
|
||
const poistoPainike = document.createElement('input');
|
||
poistoPainike.type = 'button';
|
||
poistoPainike.value = '-';
|
||
poistoPainike.addEventListener('click', () => {
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
const käyttävät =
|
||
t.suodata(taulut.tunnit, (tunti) => tunti.luokat.includes(id))
|
||
.map((x) => tuntiTeksti(tietokanta, t.hae(taulut.tunnit, x)));
|
||
if (käyttävät.length === 0) {
|
||
t.poista(taulut.luokat, id);
|
||
} else {
|
||
const lista = käyttävät.join('\n- ');
|
||
alert(`Ei voida poistaa ennen tunteja:\n- ${lista}`);
|
||
}
|
||
}));
|
||
});
|
||
li.appendChild(poistoPainike);
|
||
li.appendChild(document.createTextNode(nimi));
|
||
return li;
|
||
}
|
||
|
||
function luoOpettaja(tietokanta, id, {nimi, lyhenne}) {
|
||
const li = document.createElement('li');
|
||
li.id = `opettaja-${id}`;
|
||
const poistoPainike = document.createElement('input');
|
||
poistoPainike.type = 'button';
|
||
poistoPainike.value = '-';
|
||
poistoPainike.addEventListener('click', () => {
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
const käyttävät =
|
||
t.suodata(taulut.tunnit, (tunti) => tunti.opettajat.includes(id))
|
||
.map((x) => tuntiTeksti(tietokanta, t.hae(taulut.tunnit, x)));
|
||
if (käyttävät.length === 0) {
|
||
t.poista(taulut.opettajat, id);
|
||
} else {
|
||
const lista = käyttävät.join('\n- ');
|
||
alert(`Ei voida poistaa ennen tunteja:\n- ${lista}`);
|
||
}
|
||
}));
|
||
});
|
||
li.appendChild(poistoPainike);
|
||
li.appendChild(document.createTextNode(`${nimi} (${lyhenne})`));
|
||
return li;
|
||
}
|
||
|
||
function luoTila(tietokanta, id, nimi) {
|
||
const li = document.createElement('li');
|
||
li.id = `tila-${id}`;
|
||
const poistoPainike = document.createElement('input');
|
||
poistoPainike.type = 'button';
|
||
poistoPainike.value = '-';
|
||
poistoPainike.addEventListener('click', () => {
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
const käyttävät =
|
||
t.suodata(taulut.tunnit, (tunti) => tunti.tilat.includes(id))
|
||
.map((x) => tuntiTeksti(tietokanta, t.hae(taulut.tunnit, x)));
|
||
if (käyttävät.length === 0) {
|
||
t.poista(taulut.tilat, id);
|
||
} else {
|
||
const lista = käyttävät.join('\n- ');
|
||
alert(`Ei voida poistaa ennen tunteja:\n- ${lista}`);
|
||
}
|
||
}));
|
||
});
|
||
li.appendChild(poistoPainike);
|
||
li.appendChild(document.createTextNode(nimi));
|
||
return li;
|
||
}
|
||
|
||
function luoTunti(tietokanta, id, tunti) {
|
||
const li = document.createElement('li');
|
||
li.id = `tunti-${id}`;
|
||
const poistoPainike = document.createElement('input');
|
||
poistoPainike.type = 'button';
|
||
poistoPainike.value = '-';
|
||
poistoPainike.addEventListener('click', () => {
|
||
suorita(_tietokanta.transaktio((t) => {
|
||
t.poista(taulut.tunnit, id);
|
||
}));
|
||
});
|
||
li.appendChild(poistoPainike);
|
||
li.appendChild(document.createTextNode(tuntiTeksti(tietokanta, tunti)));
|
||
return li;
|
||
}
|
||
|
||
function tuntiTeksti(tietokanta, tunti) {
|
||
const kertaa = tunti.milloin.length;
|
||
const nimi = tunti.nimi;
|
||
const luokat = tunti.luokat.map((x) => tietokanta.hae(taulut.luokat, x));
|
||
const opettajat = tunti.opettajat
|
||
.map((x) => tietokanta.hae(taulut.opettajat, x).lyhenne);
|
||
const tilat = tunti.tilat.map((x) => tietokanta.hae(taulut.tilat, x));
|
||
return `${luokat} ${opettajat} ${nimi}×${kertaa} tilassa ${tilat}`;
|
||
}
|
||
|
||
function luoLuokkaValinta(id, nimi) {
|
||
const li = document.createElement('li');
|
||
li.id = `tunnit-uusi-luokka-${id}`;
|
||
const valinta = document.createElement('input');
|
||
valinta.type = 'checkbox';
|
||
valinta.classList.add('tunnit-uusi-valinta');
|
||
valinta.classList.add('tunnit-uusi-luokka');
|
||
valinta.value = id;
|
||
li.appendChild(valinta);
|
||
li.appendChild(document.createTextNode(nimi));
|
||
return li;
|
||
}
|
||
|
||
function luoOpettajaValinta(id, {nimi, lyhenne}) {
|
||
const li = document.createElement('li');
|
||
li.id = `tunnit-uusi-opettaja-${id}`;
|
||
const valinta = document.createElement('input');
|
||
valinta.type = 'checkbox';
|
||
valinta.classList.add('tunnit-uusi-valinta');
|
||
valinta.classList.add('tunnit-uusi-opettaja');
|
||
valinta.value = id;
|
||
li.appendChild(valinta);
|
||
li.appendChild(document.createTextNode(`${nimi} (${lyhenne})`));
|
||
return li;
|
||
}
|
||
|
||
function luoTilaValinta(id, nimi) {
|
||
const li = document.createElement('li');
|
||
li.id = `tunnit-uusi-tila-${id}`;
|
||
const valinta = document.createElement('input');
|
||
valinta.type = 'checkbox';
|
||
valinta.classList.add('tunnit-uusi-valinta');
|
||
valinta.classList.add('tunnit-uusi-tila');
|
||
valinta.value = id;
|
||
li.appendChild(valinta);
|
||
li.appendChild(document.createTextNode(nimi));
|
||
return li;
|
||
}
|
||
|
||
function luoLuokkaVaihtoehto(id, nimi) {
|
||
const option = document.createElement('option');
|
||
option.id = `lukkari-valinta-luokka-${id}`;
|
||
option.value = `luokka-${id}`;
|
||
option.appendChild(document.createTextNode(nimi));
|
||
return option;
|
||
}
|
||
|
||
function luoOpettajaVaihtoehto(id, {nimi, lyhenne}) {
|
||
const option = document.createElement('option');
|
||
option.id = `lukkari-valinta-opettaja-${id}`;
|
||
option.value = `opettaja-${id}`;
|
||
option.appendChild(document.createTextNode(`${nimi} (${lyhenne})`));
|
||
return option;
|
||
}
|
||
|
||
function luoTilaVaihtoehto(id, nimi) {
|
||
const option = document.createElement('option');
|
||
option.id = `lukkari-valinta-tila-${id}`;
|
||
option.value = `tila-${id}`;
|
||
option.appendChild(document.createTextNode(nimi));
|
||
return option;
|
||
}
|
||
|
||
let päivät = ['ma', 'ti', 'ke', 'to', 'pe'];
|
||
let ajat = ['8', '9', '10', '11', '12', '13', '14', '15'];
|
||
|
||
function päivitäLukkari() {
|
||
const taulukko = document.createElement('table');
|
||
taulukko.id = 'lukkari';
|
||
const taulukonOtsikko = document.createElement('caption');
|
||
taulukko.appendChild(taulukonOtsikko);
|
||
const rivit = document.createElement('tbody');
|
||
const otsikkoRivi = document.createElement('tr');
|
||
otsikkoRivi.appendChild(document.createElement('td'));
|
||
for (const päivä of päivät) {
|
||
const otsikko = document.createElement('th');
|
||
otsikko.appendChild(document.createTextNode(päivä));
|
||
otsikkoRivi.appendChild(otsikko);
|
||
}
|
||
rivit.appendChild(otsikkoRivi);
|
||
for (const [aikaId, aika] of numeroi(ajat)) {
|
||
const rivi = document.createElement('tr');
|
||
const otsikko = document.createElement('th');
|
||
otsikko.appendChild(document.createTextNode(aika));
|
||
rivi.appendChild(otsikko);
|
||
for (const [päiväId, päivä] of numeroi(päivät)) {
|
||
const solu = document.createElement('td');
|
||
solu.id = `lukkari-solu-${päiväId}-${aikaId}`;
|
||
teePudotuskohteeksi(solu);
|
||
const lista = document.createElement('ul');
|
||
solu.appendChild(lista);
|
||
rivi.appendChild(solu)
|
||
}
|
||
rivit.appendChild(rivi);
|
||
}
|
||
taulukko.appendChild(rivit);
|
||
|
||
const lista = document.createElement('ul');
|
||
lista.id = 'sijoittamattomat';
|
||
teePudotuskohteeksi(lista);
|
||
|
||
const valinta = document.getElementById('lukkari-valinta').value;
|
||
if (valinta !== '') {
|
||
let [tyyppi, valintaId] = valinta.split('-');
|
||
valintaId = Number.parseInt(valintaId);
|
||
const tunnit = _tietokanta.järjestyksessä(taulut.tunnit,
|
||
(a, b) => vertaa(tuntiTeksti(_tietokanta, a), tuntiTeksti(_tietokanta, b))
|
||
);
|
||
for (const id of tunnit) {
|
||
const tunti = _tietokanta.hae(taulut.tunnit, id);
|
||
if (
|
||
(tyyppi === 'luokka' && !tunti.luokat.includes(valintaId)) ||
|
||
(tyyppi === 'opettaja' && !tunti.opettajat.includes(valintaId)) ||
|
||
(tyyppi === 'tila' && !tunti.tilat.includes(valintaId))
|
||
) {
|
||
continue;
|
||
}
|
||
for (const [numero, toteutus] of numeroi(tunti.milloin)) {
|
||
const laatta = document.createElement('li');
|
||
laatta.id = `lukkari-tunti-${id}-${numero}`;
|
||
laatta.draggable = true;
|
||
laatta.appendChild(document.createTextNode(tuntiTeksti(_tietokanta, tunti)));
|
||
if (toteutus === null) {
|
||
lista.appendChild(laatta);
|
||
} else {
|
||
const [päiväId, aikaId] = toteutus;
|
||
const solu = rivit.children[aikaId + 1].children[päiväId + 1];
|
||
solu.firstChild.appendChild(laatta);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const vanhaTaulukko = document.getElementById('lukkari');
|
||
vanhaTaulukko.parentElement.replaceChild(taulukko, vanhaTaulukko);
|
||
const vanhaLista = document.getElementById('sijoittamattomat');
|
||
vanhaLista.parentElement.replaceChild(lista, vanhaLista);
|
||
}
|
||
|
||
function numeroi(iteroitava) {
|
||
return iteroitava.map((elementti, numero) => [numero, elementti]);
|
||
}
|