Aloita uudestaan
This commit is contained in:
parent
fa1246b318
commit
9bcfb1cf40
5 changed files with 539 additions and 411 deletions
108
käyttöliittymä.js
Normal file
108
käyttöliittymä.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
'use strict';
|
||||||
|
document.getElementById('kumoa').addEventListener('click', () => {
|
||||||
|
kumoa();
|
||||||
|
piirräKaikki();
|
||||||
|
});
|
||||||
|
document.getElementById('tee-uudelleen').addEventListener('click', () => {
|
||||||
|
teeUudelleen();
|
||||||
|
piirräKaikki();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('lisää-luokka-aste').addEventListener('click', () => {
|
||||||
|
suorita(tapahtumaTyypit.lisääAste);
|
||||||
|
piirräLuokat();
|
||||||
|
});
|
||||||
|
|
||||||
|
function piirräKaikki() {
|
||||||
|
piirräLuokat();
|
||||||
|
}
|
||||||
|
|
||||||
|
function piirräLuokat() {
|
||||||
|
let vanhaLuokkaAsteLista = document.getElementById('luokka-asteet');
|
||||||
|
let luokkaAsteLista = luoLuokkaAsteLista();
|
||||||
|
vanhaLuokkaAsteLista.parentNode.replaceChild(
|
||||||
|
luokkaAsteLista,
|
||||||
|
vanhaLuokkaAsteLista
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function luoLuokkaAsteLista() {
|
||||||
|
let luokkaAsteLista = document.createElement('ul');
|
||||||
|
luokkaAsteLista.id = 'luokka-asteet';
|
||||||
|
|
||||||
|
for (let aste = 0; aste < luokkaAsteet.asteet.length; aste++) {
|
||||||
|
if (luokkaAsteet.asteet[aste] !== undefined) {
|
||||||
|
let luokkaAsteListassa = luoLuokkaAsteListassa(aste);
|
||||||
|
luokkaAsteLista.appendChild(luokkaAsteListassa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return luokkaAsteLista;
|
||||||
|
}
|
||||||
|
|
||||||
|
function luoLuokkaAsteListassa(aste) {
|
||||||
|
let luokkaAsteListassa = document.createElement('li');
|
||||||
|
luokkaAsteListassa.classList.add('luokka-aste');
|
||||||
|
|
||||||
|
luokkaAsteListassa.appendChild(luoPainike('+', () => {
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, aste);
|
||||||
|
piirräLuokat();
|
||||||
|
}));
|
||||||
|
luokkaAsteListassa.appendChild(luoPainike('-', () => {
|
||||||
|
if (luokkaAsteet.asteet[aste].luokat().length > 1) {
|
||||||
|
suorita(tapahtumaTyypit.poistaLuokka, aste);
|
||||||
|
} else {
|
||||||
|
suorita(tapahtumaTyypit.poistaAste, aste);
|
||||||
|
}
|
||||||
|
piirräLuokat();
|
||||||
|
}));
|
||||||
|
|
||||||
|
luokkaAsteListassa.appendChild(luoLuokkaLista(aste));
|
||||||
|
|
||||||
|
luokkaAsteListassa.appendChild(luoPainike('✎', () => {
|
||||||
|
// TODO: Älä käytä prompt():ia ja alert():ia
|
||||||
|
let vastaus = prompt('Uusi luokka-aste');
|
||||||
|
if (vastaus === null) {
|
||||||
|
// Käytääjä painoi 'Peruuta'
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vastaus = parseInt(vastaus, 10);
|
||||||
|
if (vastaus === NaN) {
|
||||||
|
// Ei numero
|
||||||
|
alert('Luokka-asteen pitää olla numero');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (vastaus < 1) {
|
||||||
|
// Luokka-aste alkaa 1:stä
|
||||||
|
alert('Luokka-asteeen pitää olla vähintään 1');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
suorita(tapahtumaTyypit.muutaAste, aste, vastaus);
|
||||||
|
piirräLuokat();
|
||||||
|
}));
|
||||||
|
|
||||||
|
return luokkaAsteListassa;
|
||||||
|
}
|
||||||
|
|
||||||
|
function luoLuokkaLista(aste) {
|
||||||
|
let luokkaLista = document.createElement('ul');
|
||||||
|
luokkaLista.classList.add('luokat');
|
||||||
|
|
||||||
|
for (let luokka of luokkaAsteet.asteet[aste].luokat()) {
|
||||||
|
let luokkaListassa = document.createElement('li');
|
||||||
|
luokkaListassa.appendChild(document.createTextNode(`${aste}${luokka}`));
|
||||||
|
luokkaLista.appendChild(luokkaListassa);
|
||||||
|
}
|
||||||
|
|
||||||
|
return luokkaLista;
|
||||||
|
}
|
||||||
|
|
||||||
|
function luoPainike(teksti, funktio) {
|
||||||
|
let painike = document.createElement('input');
|
||||||
|
painike.type = 'button';
|
||||||
|
painike.value = teksti;
|
||||||
|
painike.addEventListener('click', funktio);
|
||||||
|
return painike;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOOD: Käyttöliittymätestit
|
|
@ -4,427 +4,35 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Lukujärjestäjä 0.1</title>
|
<title>Lukujärjestäjä 0.1</title>
|
||||||
<style>
|
<style>
|
||||||
.pane {
|
.testit-epäonnistunut {
|
||||||
|
background-color: #e77;
|
||||||
}
|
}
|
||||||
.items-list {
|
#luokka-asteet {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
.list-element-text {
|
.luokat {
|
||||||
padding-left: 0.2em;
|
display: inline;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
#schedule-table td {
|
.luokat > li {
|
||||||
min-width: 10ch;
|
margin: 0 0.5em;
|
||||||
text-align: center;
|
display: inline;
|
||||||
border: 1px solid;
|
|
||||||
}
|
|
||||||
#unscheduled-lessons-box {
|
|
||||||
border: 1px solid;
|
|
||||||
min-height: 1;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body lang="fi">
|
<body lang="fi">
|
||||||
<details class="pane" id="classes" open>
|
<input id="kumoa" type="button" value="Kumoa">
|
||||||
|
<input id="tee-uudelleen" type="button" value="Tee uudelleen">
|
||||||
|
<details class="ruutu" id="luokat" open>
|
||||||
<summary>Luokat</summary>
|
<summary>Luokat</summary>
|
||||||
<ul class="items-list" id="class-list"></ul>
|
<input id="lisää-luokka-aste" type="button" value="+ Aste">
|
||||||
<form id="add-class">
|
<ul id="luokka-asteet"></ul>
|
||||||
<input id="class-name" type="text">
|
|
||||||
<input type="submit" value="+">
|
|
||||||
</form>
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details class="pane" id="teachers">
|
<script src="testit.js"></script>
|
||||||
<summary>Opettajat</summary>
|
<script src="tietotyypit.js"></script>
|
||||||
<ul class="items-list" id="teacher-list"></ul>
|
<script src="tietomalli.js"></script>
|
||||||
<form id="add-teacher">
|
<script src="käyttöliittymä.js"></script>
|
||||||
<input id="teacher-name" type="text">
|
<script>ajaTestit();</script>
|
||||||
<input type="submit" value="+">
|
|
||||||
</form>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details class="pane" id="lessons">
|
|
||||||
<summary>Tunnit</summary>
|
|
||||||
<ul class="items-list" id="lesson-list"></ul>
|
|
||||||
<form id="add-lesson">
|
|
||||||
<label>Tunti
|
|
||||||
<input id="lesson-name" type="text">
|
|
||||||
</label>
|
|
||||||
<label>Luokka
|
|
||||||
<select id="lesson-class"></select>
|
|
||||||
</label>
|
|
||||||
<label>Opettaja
|
|
||||||
<select id="lesson-teacher"></select>
|
|
||||||
</label>
|
|
||||||
<input type="submit" value="+">
|
|
||||||
</form>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details class="pane" id="schedules">
|
|
||||||
<summary>Lukujärjestykset</summary>
|
|
||||||
<select id="schedule-select">
|
|
||||||
<optgroup id="schedule-select-classes" label="Luokat"></optgroup>
|
|
||||||
<optgroup id="schedule-select-teachers" label="Opettajat"></optgroup>
|
|
||||||
</select>
|
|
||||||
<table id="schedule-table"></table>
|
|
||||||
<div class="dropzone" id="unscheduled-lessons-box">
|
|
||||||
<ul id="unscheduled-lessons"></ul>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
'use strict';
|
|
||||||
const days = ['ma', 'ti', 'ke', 'to', 'pe'];
|
|
||||||
const hours = [8, 9, 10, 11, 12, 13, 14, 15];
|
|
||||||
|
|
||||||
let classes = [];
|
|
||||||
let teachers = [];
|
|
||||||
let lessons = [];
|
|
||||||
|
|
||||||
listClasses();
|
|
||||||
listTeachers();
|
|
||||||
listLessons();
|
|
||||||
|
|
||||||
function replacementElement(id) {
|
|
||||||
let oldElement = document.getElementById(id);
|
|
||||||
let newElement = document.createElement(oldElement.nodeName);
|
|
||||||
newElement.id = oldElement.id;
|
|
||||||
newElement.classList = oldElement.classList;
|
|
||||||
newElement.label = oldElement.label;
|
|
||||||
return newElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceElement(id, newElement) {
|
|
||||||
let oldElement = document.getElementById(id);
|
|
||||||
oldElement.parentElement.replaceChild(newElement, oldElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newDeleteButton(deleteFunction) {
|
|
||||||
let deleteButton = document.createElement('input');
|
|
||||||
deleteButton.type = 'button';
|
|
||||||
deleteButton.value = '-';
|
|
||||||
deleteButton.onclick = deleteFunction;
|
|
||||||
return deleteButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
function newListElementText(text) {
|
|
||||||
let element = document.createElement('span');
|
|
||||||
element.className = 'list-element-text';
|
|
||||||
element.innerText = text;
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
|
|
||||||
function newOption(text) {
|
|
||||||
let option = document.createElement('option');
|
|
||||||
option.value = text;
|
|
||||||
option.innerText = text;
|
|
||||||
return option;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('add-class').onsubmit = addClass;
|
|
||||||
function addClass(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let nameField = document.getElementById('class-name');
|
|
||||||
let name = nameField.value;
|
|
||||||
|
|
||||||
if (name === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classes.includes(name)) {
|
|
||||||
alert(`Luokka ${name} on jo listassa`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nameField.value = '';
|
|
||||||
|
|
||||||
classes.push(name);
|
|
||||||
classes.sort();
|
|
||||||
|
|
||||||
listClasses();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteClass(name) {
|
|
||||||
classes = classes.filter((i) => i !== name);
|
|
||||||
listClasses();
|
|
||||||
}
|
|
||||||
|
|
||||||
function listClasses() {
|
|
||||||
let classList = replacementElement('class-list');
|
|
||||||
let classSelect = replacementElement('lesson-class');
|
|
||||||
classSelect.appendChild(document.createElement('option'));
|
|
||||||
let scheduleClassSelect = replacementElement('schedule-select-classes');
|
|
||||||
|
|
||||||
for (let name of classes) {
|
|
||||||
let listElement = document.createElement('li');
|
|
||||||
listElement.appendChild(newDeleteButton(function() { deleteClass(name); }));
|
|
||||||
listElement.appendChild(newListElementText(name));
|
|
||||||
classList.appendChild(listElement);
|
|
||||||
|
|
||||||
classSelect.appendChild(newOption(name));
|
|
||||||
scheduleClassSelect.appendChild(newOption(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceElement('class-list', classList);
|
|
||||||
replaceElement('lesson-class', classSelect);
|
|
||||||
replaceElement('schedule-select-classes', scheduleClassSelect);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('add-teacher').onsubmit = addTeacher;
|
|
||||||
function addTeacher(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let nameField = document.getElementById('teacher-name');
|
|
||||||
let name = nameField.value;
|
|
||||||
|
|
||||||
if (name === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (teachers.includes(name)) {
|
|
||||||
alert(`Opettaja ${name} on jo listassa`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nameField.value = '';
|
|
||||||
|
|
||||||
teachers.push(name);
|
|
||||||
teachers.sort();
|
|
||||||
|
|
||||||
listTeachers();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteTeacher(name) {
|
|
||||||
teachers = teachers.filter((i) => i !== name);
|
|
||||||
listTeachers();
|
|
||||||
}
|
|
||||||
|
|
||||||
function listTeachers() {
|
|
||||||
let teacherList = replacementElement('teacher-list');
|
|
||||||
let teacherSelect = replacementElement('lesson-teacher');
|
|
||||||
teacherSelect.appendChild(document.createElement('option'));
|
|
||||||
let scheduleTeacherSelect = replacementElement('schedule-select-teachers');
|
|
||||||
|
|
||||||
for (let name of teachers) {
|
|
||||||
let listElement = document.createElement('li');
|
|
||||||
listElement.appendChild(newDeleteButton(function() { deleteTeacher(name); }));
|
|
||||||
listElement.appendChild(newListElementText(name));
|
|
||||||
teacherList.appendChild(listElement);
|
|
||||||
|
|
||||||
teacherSelect.appendChild(newOption(name));
|
|
||||||
scheduleTeacherSelect.appendChild(newOption(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceElement('teacher-list', teacherList);
|
|
||||||
replaceElement('lesson-teacher', teacherSelect);
|
|
||||||
replaceElement('schedule-select-teachers', scheduleTeacherSelect);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('add-lesson').onsubmit = addLesson;
|
|
||||||
function addLesson(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let nameField = document.getElementById('lesson-name');
|
|
||||||
let name = nameField.value;
|
|
||||||
let classField = document.getElementById('lesson-class');
|
|
||||||
let className = classField.value;
|
|
||||||
let teacherField = document.getElementById('lesson-teacher');
|
|
||||||
let teacherName = teacherField.value;
|
|
||||||
|
|
||||||
if (name === '' && className === '' && teacherName === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (className === '' && teacherName === '') {
|
|
||||||
alert('Tunti tarvitsee joko luokan, opettajan tai molemmat');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nameField.value = '';
|
|
||||||
classField.value = '';
|
|
||||||
teacherField.value = '';
|
|
||||||
|
|
||||||
lessons.push({name: name, class: className, teacher: teacherName});
|
|
||||||
lessons.sort(function(a, b) {
|
|
||||||
if (a.class < b.class) {
|
|
||||||
return -1;
|
|
||||||
} else if (a.class > b.class) {
|
|
||||||
return 1;
|
|
||||||
} else if (a.teacher < b.teacher) {
|
|
||||||
return -1;
|
|
||||||
} else if (a.teacher > b.teacher) {
|
|
||||||
return 1;
|
|
||||||
} else if (a.name < b.name) {
|
|
||||||
return -1;
|
|
||||||
} else if (a.name > b.name) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
listLessons();
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteLesson(key) {
|
|
||||||
lessons = lessons.filter((_, i) => i !== key);
|
|
||||||
listLessons();
|
|
||||||
}
|
|
||||||
|
|
||||||
function listLessons() {
|
|
||||||
let lessonList = replacementElement('lesson-list');
|
|
||||||
for (let key of lessons.keys()) {
|
|
||||||
let lesson = lessons[key];
|
|
||||||
|
|
||||||
let listElement = document.createElement('li');
|
|
||||||
listElement.appendChild(newDeleteButton(function() { deleteLesson(key); }));
|
|
||||||
listElement.appendChild(newListElementText(lesson.name));
|
|
||||||
listElement.appendChild(newListElementText(lesson.class));
|
|
||||||
listElement.appendChild(newListElementText(lesson.teacher));
|
|
||||||
lessonList.appendChild(listElement);
|
|
||||||
}
|
|
||||||
replaceElement('lesson-list', lessonList);
|
|
||||||
|
|
||||||
showSchedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('schedule-select').onchange = showSchedule;
|
|
||||||
function showSchedule() {
|
|
||||||
let filter = document.getElementById('schedule-select').value;
|
|
||||||
let scheduleLessons = [];
|
|
||||||
for (let key of lessons.keys()) {
|
|
||||||
let lesson = lessons[key];
|
|
||||||
if (lesson.class === filter || lesson.teacher === filter) {
|
|
||||||
scheduleLessons.push({key: key, lesson: lesson});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let schedule = {};
|
|
||||||
for (let day of days) {
|
|
||||||
let daySchedule = {}
|
|
||||||
for (let hour of hours) {
|
|
||||||
daySchedule[hour] = null;
|
|
||||||
}
|
|
||||||
schedule[day] = daySchedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
let unscheduledLessons = replacementElement('unscheduled-lessons');
|
|
||||||
for (let lesson of scheduleLessons) {
|
|
||||||
if (lesson.lesson.at) {
|
|
||||||
schedule[lesson.lesson.at.day][lesson.lesson.at.hour] = lesson;
|
|
||||||
} else {
|
|
||||||
let listElement = document.createElement('li');
|
|
||||||
listElement.id = `lesson-${lesson.key}`;
|
|
||||||
listElement.draggable = true;
|
|
||||||
listElement.appendChild(newListElementText(lesson.lesson.name));
|
|
||||||
if (lesson.lesson.class !== filter) {
|
|
||||||
listElement.appendChild(newListElementText(lesson.lesson.class));
|
|
||||||
}
|
|
||||||
if (lesson.lesson.teacher !== filter) {
|
|
||||||
listElement.appendChild(newListElementText(lesson.lesson.teacher));
|
|
||||||
}
|
|
||||||
unscheduledLessons.appendChild(listElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
replaceElement('unscheduled-lessons', unscheduledLessons);
|
|
||||||
|
|
||||||
let scheduleTable = replacementElement('schedule-table');
|
|
||||||
|
|
||||||
let tableHeader = document.createElement('tr');
|
|
||||||
tableHeader.appendChild(document.createElement('th'));
|
|
||||||
for (let day of days) {
|
|
||||||
let header = document.createElement('th');
|
|
||||||
header.innerText = day;
|
|
||||||
tableHeader.appendChild(header);
|
|
||||||
}
|
|
||||||
scheduleTable.appendChild(tableHeader);
|
|
||||||
|
|
||||||
for (let hour of hours) {
|
|
||||||
let row = document.createElement('tr');
|
|
||||||
let header = document.createElement('th');
|
|
||||||
header.innerText = hour;
|
|
||||||
row.appendChild(header);
|
|
||||||
|
|
||||||
for (let day of days) {
|
|
||||||
let cell = document.createElement('td');
|
|
||||||
if (schedule[day][hour]) {
|
|
||||||
cell.id = `lesson-${schedule[day][hour].key}`;
|
|
||||||
cell.draggable = true;
|
|
||||||
let lesson = schedule[day][hour].lesson;
|
|
||||||
|
|
||||||
let nameField = document.createElement('div');
|
|
||||||
nameField.class = 'schedule-namefield';
|
|
||||||
nameField.innerText = lesson.name;
|
|
||||||
cell.appendChild(nameField);
|
|
||||||
|
|
||||||
let otherField = document.createElement('div');
|
|
||||||
otherField.class = 'schedule-otherfield';
|
|
||||||
if (lesson.class !== filter) {
|
|
||||||
otherField.innerText = lesson.class;
|
|
||||||
} else {
|
|
||||||
otherField.innerText = lesson.teacher;
|
|
||||||
}
|
|
||||||
cell.appendChild(otherField);
|
|
||||||
} else {
|
|
||||||
cell.id = `cell-${day}-${hour}`;
|
|
||||||
cell.className = 'dropzone';
|
|
||||||
}
|
|
||||||
row.appendChild(cell);
|
|
||||||
}
|
|
||||||
scheduleTable.appendChild(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceElement('schedule-table', scheduleTable);
|
|
||||||
}
|
|
||||||
|
|
||||||
let dragged;
|
|
||||||
document.addEventListener('dragstart', (event) => {
|
|
||||||
dragged = event.target;
|
|
||||||
});
|
|
||||||
document.addEventListener('dragover', (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
document.addEventListener('dragenter', (event) => {
|
|
||||||
if (event.target.className === 'dropzone') {
|
|
||||||
event.target.style.backgroundColor = '#ccc';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.addEventListener('dragleave', (event) => {
|
|
||||||
if (event.target.className === 'dropzone') {
|
|
||||||
event.target.style.backgroundColor = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.addEventListener('drop', (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (event.target.className === 'dropzone') {
|
|
||||||
event.target.style.backgroundColor = '';
|
|
||||||
|
|
||||||
let key = Number.parseInt(dragged.id.split('lesson-')[1]);
|
|
||||||
let lesson = lessons[key];
|
|
||||||
if (event.target.id === 'unscheduled-lessons-box') {
|
|
||||||
lesson.at = null;
|
|
||||||
} else {
|
|
||||||
let cell = event.target.id.split('cell-')[1];
|
|
||||||
let day = cell.split('-')[0];
|
|
||||||
let hour = Number.parseInt(cell.split('-')[1]);
|
|
||||||
for (let otherLesson of lessons) {
|
|
||||||
if (!otherLesson.at || otherLesson.at.day !== day || otherLesson.at.hour !== hour) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (otherLesson.teacher && otherLesson.teacher === lesson.teacher) {
|
|
||||||
alert(`Päällekäisyys: ${otherLesson.name} ${otherLesson.class}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (otherLesson.class && otherLesson.class === lesson.class) {
|
|
||||||
alert(`Päällekäisyys: ${otherLesson.name} ${otherLesson.teacher}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lesson.at = {day: day, hour: hour};
|
|
||||||
}
|
|
||||||
showSchedule();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
81
testit.js
Normal file
81
testit.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
'use strict';
|
||||||
|
let testit = [];
|
||||||
|
|
||||||
|
function testi(nimi, funktio) {
|
||||||
|
testit.push([nimi, funktio]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEq(konteksti, a, b) {
|
||||||
|
if (a !== b) {
|
||||||
|
throw new Error(`${konteksti}: ${a}, pitäisi olla ${b}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertNe(konteksti, a, b) {
|
||||||
|
if (a === b) {
|
||||||
|
throw new Error(`${konteksti}: ei tulisi olla ${b}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertRange(konteksti, a, min, max) {
|
||||||
|
if (a < min || a > max) {
|
||||||
|
throw new Error(`${konteksti}: ${a}, pitäisi olla välillä [${min}, ${max}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertThrow(konteksti, viesti, funktio) {
|
||||||
|
try {
|
||||||
|
funktio();
|
||||||
|
} catch(poikkeus) {
|
||||||
|
if (poikkeus.message !== viesti) {
|
||||||
|
throw new Error(`${konteksti}: poikkeus "${poikkeus}", pitäisi olla "${viesti}"`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Error(`${konteksti}: pitäisi heittää "${viesti}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ajaTestit() {
|
||||||
|
// Luo otsikko ja lista testeistä sivulle
|
||||||
|
let otsikkoTeksti = document.createTextNode('Testit käynnissä…');
|
||||||
|
let otsikko = document.createElement('h1');
|
||||||
|
otsikko.id = 'testit-otsikko';
|
||||||
|
otsikko.appendChild(otsikkoTeksti);
|
||||||
|
|
||||||
|
let testilista = document.createElement('ul');
|
||||||
|
testilista.id = 'testit-lista';
|
||||||
|
|
||||||
|
let body = document.getElementsByTagName('body')[0];
|
||||||
|
body.appendChild(otsikko);
|
||||||
|
body.appendChild(testilista);
|
||||||
|
|
||||||
|
// Aja testit
|
||||||
|
let epäonnistuneitaTestejä = 0;
|
||||||
|
for (let [nimi, funktio] of testit) {
|
||||||
|
// Lisää testi listaan
|
||||||
|
let testiListassa = document.createElement('li');
|
||||||
|
let testiTeksti = document.createTextNode(`${nimi} …`);
|
||||||
|
testiListassa.appendChild(testiTeksti);
|
||||||
|
testilista.appendChild(testiListassa);
|
||||||
|
|
||||||
|
try {
|
||||||
|
funktio();
|
||||||
|
} catch (poikkeus) {
|
||||||
|
// Virhe testiä suoritettaessa — kerro virhe käyttäjälle ja päivitä
|
||||||
|
// epäonnistuneiden testien laskuria
|
||||||
|
testiTeksti.textContent = `${nimi}: ${poikkeus}`;
|
||||||
|
testiListassa.classList.add('testit-epäonnistunut');
|
||||||
|
epäonnistuneitaTestejä++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
testiTeksti.textContent = `${nimi}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Päivitä otsikko – onnistuivatko testit?
|
||||||
|
if (epäonnistuneitaTestejä === 0) {
|
||||||
|
otsikko.textContent = 'Testit suoritettu onnistuneesti';
|
||||||
|
} else {
|
||||||
|
otsikko.textContent = `${epäonnistuneitaTestejä} epäonnistui`;
|
||||||
|
}
|
||||||
|
}
|
186
tietomalli.js
Normal file
186
tietomalli.js
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
'use strict';
|
||||||
|
const tapahtumaTyypit = {
|
||||||
|
lisääAste: 'lisääAste',
|
||||||
|
poistaAste: 'poistaAste',
|
||||||
|
muutaAste: 'muutaAste',
|
||||||
|
|
||||||
|
lisääLuokka: 'lisääLuokka',
|
||||||
|
poistaLuokka: 'poistaLuokka',
|
||||||
|
};
|
||||||
|
|
||||||
|
class Tapahtuma {
|
||||||
|
constructor(tyyppi, argumentit) {
|
||||||
|
this.tyyppi = tyyppi;
|
||||||
|
this.argumentit = argumentit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let historia, tulevaisuus;
|
||||||
|
let luokkaAsteet;
|
||||||
|
alustaMalli();
|
||||||
|
|
||||||
|
function alustaMalli() {
|
||||||
|
historia = [];
|
||||||
|
tulevaisuus = [];
|
||||||
|
luokkaAsteet = new LuokkaAsteet();
|
||||||
|
}
|
||||||
|
|
||||||
|
function suorita(tyyppi, ...argumentit) {
|
||||||
|
let paluuarvo = undefined;
|
||||||
|
switch (tyyppi) {
|
||||||
|
case tapahtumaTyypit.lisääAste:
|
||||||
|
assertRange('lisääAste argumentit määrä', argumentit.length, 0, 1);
|
||||||
|
paluuarvo = luokkaAsteet.lisää(...argumentit)
|
||||||
|
break;
|
||||||
|
case tapahtumaTyypit.poistaAste:
|
||||||
|
assertEq('poistaAste argumentit määrä', argumentit.length, 1);
|
||||||
|
paluuarvo = luokkaAsteet.poista(...argumentit)
|
||||||
|
break;
|
||||||
|
case tapahtumaTyypit.muutaAste:
|
||||||
|
assertEq('muutaAste argumentit määrä', argumentit.length, 2);
|
||||||
|
luokkaAsteet.muuta(...argumentit)
|
||||||
|
break;
|
||||||
|
case tapahtumaTyypit.lisääLuokka:
|
||||||
|
assertEq('lisääLuokka argumentit määrä', argumentit.length, 1);
|
||||||
|
luokkaAsteet.asteet[argumentit[0]].lisää();
|
||||||
|
break;
|
||||||
|
case tapahtumaTyypit.poistaLuokka:
|
||||||
|
assertEq('poistaLuokka argumentit määrä', argumentit.length, 1);
|
||||||
|
luokkaAsteet.asteet[argumentit[0]].poista();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`tuntematon tapahtumatyyppi ${tyyppi}`);
|
||||||
|
}
|
||||||
|
historia.push(new Tapahtuma(tyyppi, argumentit));
|
||||||
|
tulevaisuus = [];
|
||||||
|
return paluuarvo;
|
||||||
|
}
|
||||||
|
|
||||||
|
function kumoa() {
|
||||||
|
if (historia.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kumoaminen tapahtuu ottamalla historia uusinta tapahtumaa lukuun
|
||||||
|
// ottamatta ja suorittamalla se siihen asti uudestaan tyhjältä mallilta
|
||||||
|
let kumottu = historia.pop();
|
||||||
|
let uusi_tulevaisuus = tulevaisuus.concat(kumottu);
|
||||||
|
let vanha_historia = historia;
|
||||||
|
alustaMalli();
|
||||||
|
for (let {tyyppi, argumentit} of vanha_historia) {
|
||||||
|
suorita(tyyppi, ...argumentit);
|
||||||
|
}
|
||||||
|
tulevaisuus = uusi_tulevaisuus;
|
||||||
|
}
|
||||||
|
|
||||||
|
function teeUudelleen() {
|
||||||
|
if (tulevaisuus.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {tyyppi, argumentit} = tulevaisuus.pop();
|
||||||
|
// Tulevaisuus tulee tallentaa, sillä suorita() tuhoaa sen
|
||||||
|
let uusi_tulevaisuus = tulevaisuus;
|
||||||
|
suorita(tyyppi, ...argumentit);
|
||||||
|
tulevaisuus = uusi_tulevaisuus;
|
||||||
|
}
|
||||||
|
|
||||||
|
testi('mallin alustaminen', () => {
|
||||||
|
historia = undefined;
|
||||||
|
tulevaisuus = undefined;
|
||||||
|
luokkaAsteet = undefined;
|
||||||
|
alustaMalli();
|
||||||
|
assertNe('historia', historia, undefined);
|
||||||
|
assertNe('tulevaisuus', tulevaisuus, undefined);
|
||||||
|
assertNe('luokkaAsteet', luokkaAsteet, undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('tapahtumahistoria', () => {
|
||||||
|
alustaMalli();
|
||||||
|
suorita(tapahtumaTyypit.lisääAste);
|
||||||
|
suorita(tapahtumaTyypit.poistaAste, 1);
|
||||||
|
assertEq('historia.length', historia.length, 2);
|
||||||
|
assertEq('historia[0].tyyppi', historia[0].tyyppi, tapahtumaTyypit.lisääAste);
|
||||||
|
assertEq('historia[0].argumentit.length', historia[0].argumentit.length, 0);
|
||||||
|
assertEq('historia[1].tyyppi', historia[1].tyyppi, tapahtumaTyypit.poistaAste);
|
||||||
|
assertEq('historia[1].argumentit.length', historia[1].argumentit.length, 1);
|
||||||
|
assertEq('historia[1].argumentit[0]', historia[1].argumentit[0], 1);
|
||||||
|
assertThrow('poistaAste 1', 'luokka-astetta 1 ei ole olemassa', () => {
|
||||||
|
suorita(tapahtumaTyypit.poistaAste, 1);
|
||||||
|
});
|
||||||
|
assertEq('historia.length poikkeuksen jälkeen', historia.length, 2);
|
||||||
|
alustaMalli();
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('kumoamiminen', () => {
|
||||||
|
alustaMalli();
|
||||||
|
kumoa();
|
||||||
|
suorita(tapahtumaTyypit.lisääAste);
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 1);
|
||||||
|
suorita(tapahtumaTyypit.lisääAste);
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 2);
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 1);
|
||||||
|
assertEq('aluksi 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'ABC');
|
||||||
|
assertEq('aluksi 2. aste', luokkaAsteet.asteet[2].luokat().join(''), 'AB');
|
||||||
|
kumoa();
|
||||||
|
assertEq('kerran 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'AB');
|
||||||
|
assertEq('kerran 2. aste', luokkaAsteet.asteet[2].luokat().join(''), 'AB');
|
||||||
|
kumoa();
|
||||||
|
assertEq('kahdesti 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'AB');
|
||||||
|
assertEq('kahdesti 2. aste', luokkaAsteet.asteet[2].luokat().join(''), 'A');
|
||||||
|
kumoa();
|
||||||
|
assertEq('kolmesti 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'AB');
|
||||||
|
assertEq('kolmesti 2. aste', luokkaAsteet.asteet[2], undefined);
|
||||||
|
kumoa();
|
||||||
|
assertEq('neljästi 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'A');
|
||||||
|
kumoa();
|
||||||
|
assertEq('viidesti 1. aste', luokkaAsteet.asteet[1], undefined);
|
||||||
|
alustaMalli();
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('uudelleen tekeminen', () => {
|
||||||
|
alustaMalli();
|
||||||
|
teeUudelleen();
|
||||||
|
suorita(tapahtumaTyypit.lisääAste);
|
||||||
|
suorita(tapahtumaTyypit.lisääAste);
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 1);
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 2);
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 2);
|
||||||
|
kumoa();
|
||||||
|
kumoa();
|
||||||
|
kumoa();
|
||||||
|
assertEq('aluksi 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'A');
|
||||||
|
assertEq('aluksi 2. aste', luokkaAsteet.asteet[2].luokat().join(''), 'A');
|
||||||
|
teeUudelleen();
|
||||||
|
assertEq('kerran 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'AB');
|
||||||
|
assertEq('kerran 2. aste', luokkaAsteet.asteet[2].luokat().join(''), 'A');
|
||||||
|
teeUudelleen();
|
||||||
|
assertEq('kahdesti 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'AB');
|
||||||
|
assertEq('kahdesti 2. aste', luokkaAsteet.asteet[2].luokat().join(''), 'AB');
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 1);
|
||||||
|
teeUudelleen();
|
||||||
|
assertEq('kolmesti 1. aste', luokkaAsteet.asteet[1].luokat().join(''), 'ABC');
|
||||||
|
assertEq('kolmesti 2. aste', luokkaAsteet.asteet[2].luokat().join(''), 'AB');
|
||||||
|
alustaMalli();
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('asteiden käsittely', () => {
|
||||||
|
alustaMalli();
|
||||||
|
assertEq('lisää', suorita(tapahtumaTyypit.lisääAste), 1);
|
||||||
|
suorita(tapahtumaTyypit.muutaAste, 1, 2);
|
||||||
|
assertEq('muutettua seuraava aste', luokkaAsteet.seuraavaAste(), 3);
|
||||||
|
suorita(tapahtumaTyypit.poistaAste, 2);
|
||||||
|
assertEq('lopuksi seuraava aste', luokkaAsteet.seuraavaAste(), 1);
|
||||||
|
alustaMalli();
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('luokkien käsittely', () => {
|
||||||
|
alustaMalli();
|
||||||
|
suorita(tapahtumaTyypit.lisääAste);
|
||||||
|
assertEq('aluksi', luokkaAsteet.asteet[1].luokat().join(''), 'A');
|
||||||
|
suorita(tapahtumaTyypit.lisääLuokka, 1);
|
||||||
|
assertEq('lisättyä', luokkaAsteet.asteet[1].luokat().join(''), 'AB');
|
||||||
|
suorita(tapahtumaTyypit.poistaLuokka, 1);
|
||||||
|
assertEq('poistettua', luokkaAsteet.asteet[1].luokat().join(''), 'A');
|
||||||
|
alustaMalli();
|
||||||
|
});
|
145
tietotyypit.js
Normal file
145
tietotyypit.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
'use strict';
|
||||||
|
class LuokkaAste {
|
||||||
|
#luokat = 1;
|
||||||
|
|
||||||
|
luokat() {
|
||||||
|
// Tee lista, jossa on #luokat ensimmäistä isoa kirjainta
|
||||||
|
let luokat = [];
|
||||||
|
for (let i = 0; i < this.#luokat; i++) {
|
||||||
|
luokat.push(String.fromCharCode(65 + i));
|
||||||
|
}
|
||||||
|
return luokat;
|
||||||
|
}
|
||||||
|
|
||||||
|
lisää() {
|
||||||
|
this.#luokat++;
|
||||||
|
}
|
||||||
|
|
||||||
|
poista() {
|
||||||
|
if (this.#luokat <= 1) {
|
||||||
|
throw new Error('viimeistä luokkaa ei voi poistaa asteelta');
|
||||||
|
}
|
||||||
|
this.#luokat--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LuokkaAsteet {
|
||||||
|
asteet = [];
|
||||||
|
|
||||||
|
seuraavaAste() {
|
||||||
|
// Seuraava aste on yksi suurinta listassa jo olevaa astetta isompi, tai
|
||||||
|
// 1 jos listassa ei vielä ole asteita
|
||||||
|
let aste = 1;
|
||||||
|
for (let i = 0; i < this.asteet.length; i++) {
|
||||||
|
if (this.asteet[i] !== undefined) {
|
||||||
|
aste = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aste;
|
||||||
|
}
|
||||||
|
|
||||||
|
lisää(aste) {
|
||||||
|
aste ??= this.seuraavaAste()
|
||||||
|
if (this.asteet[aste] !== undefined) {
|
||||||
|
throw new Error(`luokka-aste ${aste} on jo olemassa`);
|
||||||
|
}
|
||||||
|
let asteObjekti = new LuokkaAste();
|
||||||
|
this.asteet[aste] = asteObjekti;
|
||||||
|
return aste;
|
||||||
|
}
|
||||||
|
|
||||||
|
poista(aste) {
|
||||||
|
if (this.asteet[aste] === undefined) {
|
||||||
|
throw new Error(`luokka-astetta ${aste} ei ole olemassa`);
|
||||||
|
}
|
||||||
|
this.asteet[aste] = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
muuta(vanha, uusi) {
|
||||||
|
if (vanha === uusi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Heitä poikkeus jo ennen poistoa, jotta asteet-taulukkoon ei tule
|
||||||
|
// mitään muutoksia poikkeustilanteessa
|
||||||
|
if (this.asteet[uusi] !== undefined) {
|
||||||
|
throw new Error(`luokka-aste ${uusi} on jo olemassa`);
|
||||||
|
}
|
||||||
|
let aste = this.asteet[vanha];
|
||||||
|
this.poista(vanha);
|
||||||
|
this.asteet[uusi] = aste;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testi('seuraava aste', () => {
|
||||||
|
let luokkaAsteet = new LuokkaAsteet();
|
||||||
|
assertEq('aluksi', luokkaAsteet.seuraavaAste(), 1);
|
||||||
|
luokkaAsteet.lisää();
|
||||||
|
assertEq('asteen 1 jälkeen', luokkaAsteet.seuraavaAste(), 2);
|
||||||
|
luokkaAsteet.lisää(3);
|
||||||
|
assertEq('asteiden 1 ja 3 jälkeen', luokkaAsteet.seuraavaAste(), 4);
|
||||||
|
luokkaAsteet.lisää();
|
||||||
|
assertEq('asteiden 1, 3, 4 jälkeen', luokkaAsteet.seuraavaAste(), 5);
|
||||||
|
luokkaAsteet.poista(4);
|
||||||
|
luokkaAsteet.poista(3);
|
||||||
|
assertEq('asteet 3 ja 4 poistettua', luokkaAsteet.seuraavaAste(), 2);
|
||||||
|
luokkaAsteet.muuta(1, 5);
|
||||||
|
assertEq('aste 1 muutettua 5:ksi', luokkaAsteet.seuraavaAste(), 6);
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('asteiden lisääminen', () => {
|
||||||
|
let luokkaAsteet = new LuokkaAsteet();
|
||||||
|
assertEq('1. aste', luokkaAsteet.lisää(), 1);
|
||||||
|
assertEq('2. aste', luokkaAsteet.lisää(), 2);
|
||||||
|
assertEq('3. aste', luokkaAsteet.lisää(3), 3);
|
||||||
|
assertThrow('3. aste uudelleen', 'luokka-aste 3 on jo olemassa', () => {
|
||||||
|
luokkaAsteet.lisää(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('asteiden poistaminen', () => {
|
||||||
|
let luokkaAsteet = new LuokkaAsteet();
|
||||||
|
luokkaAsteet.lisää();
|
||||||
|
luokkaAsteet.lisää();
|
||||||
|
luokkaAsteet.poista(1);
|
||||||
|
luokkaAsteet.poista(2);
|
||||||
|
assertThrow('3. asteen poisto', 'luokka-astetta 3 ei ole olemassa', () => {
|
||||||
|
luokkaAsteet.poista(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('asteiden muuttaminen', () => {
|
||||||
|
let luokkaAsteet = new LuokkaAsteet();
|
||||||
|
assertEq('lisääminen', luokkaAsteet.lisää(), 1);
|
||||||
|
luokkaAsteet.muuta(1, 5);
|
||||||
|
assertThrow('poisto', 'luokka-astetta 1 ei ole olemassa', () => {
|
||||||
|
luokkaAsteet.poista(1);
|
||||||
|
});
|
||||||
|
luokkaAsteet.poista(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('luokkien lisääminen', () => {
|
||||||
|
let luokkaAsteet = new LuokkaAsteet();
|
||||||
|
let aste = luokkaAsteet.asteet[luokkaAsteet.lisää()];
|
||||||
|
assertEq('aluksi pituus', aste.luokat().length, 1);
|
||||||
|
assertEq('aluksi', aste.luokat()[0], 'A');
|
||||||
|
aste.lisää();
|
||||||
|
aste.lisää();
|
||||||
|
assertEq('lisättyä pituus', aste.luokat().length, 3);
|
||||||
|
assertEq('lisättyä[0]', aste.luokat()[0], 'A');
|
||||||
|
assertEq('lisättyä[1]', aste.luokat()[1], 'B');
|
||||||
|
assertEq('lisättyä[2]', aste.luokat()[2], 'C');
|
||||||
|
});
|
||||||
|
|
||||||
|
testi('luokkien poistaminen', () => {
|
||||||
|
let luokkaAsteet = new LuokkaAsteet();
|
||||||
|
let aste = luokkaAsteet.asteet[luokkaAsteet.lisää()];
|
||||||
|
aste.lisää();
|
||||||
|
aste.lisää();
|
||||||
|
aste.poista();
|
||||||
|
aste.poista();
|
||||||
|
assertThrow('viimeisen poisto', 'viimeistä luokkaa ei voi poistaa asteelta', () => {
|
||||||
|
aste.poista();
|
||||||
|
});
|
||||||
|
assertEq('poistettua pituus', aste.luokat().length, 1);
|
||||||
|
assertEq('poistettua', aste.luokat()[0], 'A');
|
||||||
|
});
|
Loading…
Reference in a new issue