From 423b8f6b398f62919c359412ca023ea58b1ee616 Mon Sep 17 00:00:00 2001 From: Tyler Clarke Date: Thu, 6 Feb 2025 08:42:34 -0500 Subject: [PATCH] add skedul --- skedul/.gitignore | 1 + skedul/README | 36 ++++++++++++++ skedul/index.html | 43 ++++++++++++++++ skedul/main.css | 121 ++++++++++++++++++++++++++++++++++++++++++++++ skedul/main.js | 104 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 305 insertions(+) create mode 100644 skedul/.gitignore create mode 100644 skedul/README create mode 100644 skedul/index.html create mode 100644 skedul/main.css create mode 100644 skedul/main.js diff --git a/skedul/.gitignore b/skedul/.gitignore new file mode 100644 index 0000000..114ea57 --- /dev/null +++ b/skedul/.gitignore @@ -0,0 +1 @@ +data.json \ No newline at end of file diff --git a/skedul/README b/skedul/README new file mode 100644 index 0000000..44e414f --- /dev/null +++ b/skedul/README @@ -0,0 +1,36 @@ +very simple customized class schedules applet. to use it, create a data.json file in the project root and put all the files in the static directory of your webserver! + +features +* shows first, next, and last classes for the day +* shows all your classes for the week on a grid +* configure links for class buildings (google maps or whatever) + +data.json is structured like so: +{ + "year": 2025, + "semester": "Semester", // spring, fall, whatever + "credits": 30, // your class credits this semester [purely cosmetic] + "maps": { + "building name": "link to the building in google maps or whatever" + }, + "week": [ + { + "days": [1, 3, 5], // 1 is monday, 2 is tuesday, etc. weekend courses currently aren't supported. + "start_time": 1100, // 24-hour timestamps. + "end_time": 1215, + "name": "Physics", + "type": "Lecture", // options are Lecture, Lab, Studio, Class, and Test. + "building": "The Bestest Building", // must be exactly the same as the building name in `maps` for the links to work + "room": "101" // your room in the building + }, + { + "days": [2, 4], + "start_time": 1230, + "end_time": 1515, + "name": "Chemistry", + "type": "Lab", + "building": "Laboratorium!", + "room": "66" + } + ] +} \ No newline at end of file diff --git a/skedul/index.html b/skedul/index.html new file mode 100644 index 0000000..3c7b3ea --- /dev/null +++ b/skedul/index.html @@ -0,0 +1,43 @@ + + + + Tyler's Course Schedule + + + +
+
+ +

credits

+
+
+ FIRST CLASS
+ at from to +
+
+ NEXT CLASS
+ at from to +
+
+ LAST CLASS
+ at from to +
+
+ + + + + + + + + + + + + +
MondayTuesdayWednesdayThursdayFriday
+ + + \ No newline at end of file diff --git a/skedul/main.css b/skedul/main.css new file mode 100644 index 0000000..989e3d3 --- /dev/null +++ b/skedul/main.css @@ -0,0 +1,121 @@ +#topline { + padding: 10px; + background-color: rgb(0, 51, 68); + color: white; + font-family: monospace; + display: flex; +} + +#topline > * { + margin-left: 20px; + margin-right: 20px; +} + +#topline a { + color: white; +} + +#firstclass { + margin-left: auto; +} + +#schedule>tbody td:first-child { + font-size: 0.7em; + color: transparent; + position: relative; + padding-right: 20px; +} + +td:first-child>span { + position: absolute; + top: -0.7em; + left: 0px; + background-color: white; + width: auto; + color: black; +} + +thead td { + font-weight: bold; + text-align: center; +} + +td:not(:first-child) { + width: 20%; +} + +table { + border-collapse: collapse; + width: auto; +} + +tr { + border-top: 1px solid rgb(167, 167, 167); + position: relative; + height: 1.1em; +} + +.coursename { + font-weight: bold; + display: block; + text-align: center; + font-size: 1.1em; +} + +.coursetype { + font-weight: bold; +} + +td:not(:first-child) { + vertical-align: top; + font-family: monospace; + padding: 4px; + position: relative; +} + +td.Lecture { + background-color: rgb(255, 207, 255); +} + +td.Lab { + background-color: lightblue; +} + +td.Class { + background-color: yellow; +} + +td.Studio { + background-color: lightgreen; +} + +td.Test { + background-color: red; + box-shadow: 0px 0px 20px 10px rgb(88, 0, 0); +} + +.youarehere { + color: white; + background-color: black; + text-align: center; +} + +.youarehere::after{ + font-weight: bold; + content: 'you are HERE!'; +} + +@media (max-width:1000px) { + td { + font-size: 0.5em !important; + } + #topline { + flex-direction: column; + } + #topline > * { + margin: 20px !important; + } + tr { + height: 0.7em; + } +} \ No newline at end of file diff --git a/skedul/main.js b/skedul/main.js new file mode 100644 index 0000000..ea40fcd --- /dev/null +++ b/skedul/main.js @@ -0,0 +1,104 @@ +function tohours(mns) { // convert a value in minutes to a 24-hour timestamp, e.g. 330 -> 0530 + let hour = Math.floor(mns / 60); + let minute = mns % 60; + return hour * 100 + minute; +} + +function tomins(hrs) { // convert a value in 24-hour to minutes, the inverse of tohours + let hour = Math.floor(hrs / 100); + let minute = hrs % 100; + return hour * 60 + minute; +} + +const daystart = tomins(1000); + +function zpad(thing, zeroes) { // left-pad a string with zeroes + thing = "" + thing; + while (thing.length < zeroes) { + thing = "0" + thing; + } + return thing; +} + +function tonicehrs(hrs) { // convert a value in 24-hour to a string (800 -> "08:00", 1645 -> "16:45") + let hour = Math.floor(hrs / 100); + if (hour > 12) { + hour -= 12; + } + let minute = hrs % 100; + return zpad(hour, 2) + ":" + zpad(minute, 2); +} + +function tablesetup() { + for (var i = 0; i < 11 * 4; i ++) { + let row = document.createElement("tr"); + let time = tonicehrs(tohours(daystart + i * 15)) + row.innerHTML = `${time}${time}`; + document.querySelector("#schedule > tbody").appendChild(row); + } +} + + +function setClassTo(el, c, map) { + el.querySelector(".cname").innerText = c.name; + el.querySelector(".ctype").innerText = c.type; + el.querySelector(".bloc").href = map[c.building]; + el.querySelector(".building").innerText = c.building; + el.querySelector(".room").innerText = c.room; + el.querySelector(".start").innerText = tonicehrs(c.start_time); + el.querySelector(".end").innerText = tonicehrs(c.end_time); +} + + +window.addEventListener("load", async () => { + let data = await (await fetch("data.json")).json(); + document.getElementById("year").innerHTML = data.year; + document.getElementById("semester").innerHTML = data.semester; + document.getElementById("credits").innerHTML = data.credits; + tablesetup(); + let tbody = document.querySelector("#schedule > tbody") + let curday = new Date().getDay(); + let firstClass = undefined; + let nextClass = undefined; + let lastClass = undefined; + let now = new Date(); + let curTime = now.getMinutes() + now.getHours() * 100; + let curTimeSnap = Math.round((tomins(curTime) - daystart) / 15); + tbody.children[curTimeSnap].children[curday].classList.add("youarehere"); + for (let cls of data.week) { + for (let day of cls.days) { + if (day == curday) { + if (firstClass == undefined || cls.start_time < firstClass.start_time) { + firstClass = cls; + } + if (lastClass == undefined || cls.end_time > lastClass.end_time) { + lastClass = cls; + } + if (cls.start_time > curTime) { + if (nextClass == undefined || cls.start_time < nextClass.start_time) { + nextClass = cls; + } + } + } + let start = Math.floor((tomins(cls.start_time) - daystart) / 15); + let el = tbody.children[start].children[day]; + el.classList.add("class"); + let rows = Math.floor((tomins(cls.end_time) - tomins(cls.start_time)) / 15); + el.rowSpan = rows; + for (var i = start + 1; i < start + rows; i ++) { // cull the tds that this td is overlapping + tbody.children[i].removeChild(tbody.children[i].children[day]); + } + el.classList.add(cls.type); + el.innerHTML = `${cls.name}${cls.type} at ${cls.building} ${cls.room}` + } + } + if (firstClass) { + setClassTo(document.getElementById("firstclass"), firstClass, data.maps); + } + if (nextClass) { + setClassTo(document.getElementById("nextclass"), nextClass, data.maps); + } + if (lastClass) { + setClassTo(document.getElementById("lastclass"), lastClass, data.maps); + } +}); \ No newline at end of file