'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]); }