|
// ==UserScript== |
|
// @name USOS nice timetable display / copy / download |
|
// @description A script that replaces the buildings frame under USOS's timetable. It also allows to easily copy and download (as a txt file) the timetable. |
|
// @version 2.0.1 |
|
// @author Eryk Darnowski (GH: ErykDarnowski TW: @erykdarnowski) |
|
// @match *://usosweb.ansb.pl/kontroler.php?_action=home/plan* |
|
// @match *://usosweb.ansb.pl/kontroler.php?_action=katalog2/przedmioty/* |
|
// @match *://usosweb.ansb.pl/kontroler.php?_action=katalog2%2Fprzedmioty* |
|
// @run-at document-idle |
|
// @grant none |
|
// @namespace https://gist.github.com/ErykDarnowski/fd08f94ca139b1f575d8e7205e2e3a90 |
|
// @supportURL https://gist.github.com/ErykDarnowski/fd08f94ca139b1f575d8e7205e2e3a90 |
|
// @updateURL https://gist.githubusercontent.com/ErykDarnowski/fd08f94ca139b1f575d8e7205e2e3a90/raw/78d31712592da3ab2a8c34ba47c30203d54edf32/script.js |
|
// @downloadURL https://gist.githubusercontent.com/ErykDarnowski/fd08f94ca139b1f575d8e7205e2e3a90/raw/78d31712592da3ab2a8c34ba47c30203d54edf32/script.js |
|
// ==/UserScript== |
|
|
|
/* Releases |
|
- 1.0.0 Initial |
|
- 1.1.0 Add support for my [USOS timetable day date adder... Userscript](https://gist.github.com/ErykDarnowski/a0566eaa2ed6aab400a6b6ec7355fde0) |
|
- 1.1.1 Replace lesson name Regex with `split` |
|
- 1.2.0 Update to new UI + add `cleanLessonTypes` functionality |
|
|
|
- 2.0.0 Add 'display in USOS' functionality + fix bugs with `uniqe` and `ignoreList` functionality + release as Tampermonkey script |
|
- 2.0.1 Fix download button not getting disabled and script activating on wrong kinds of timetables (diff than `HTML (stary)`) |
|
*/ |
|
|
|
const config = { |
|
unique: true, // only print each subject name once (for instance if you have the same lesson twice / thrice in the same day) |
|
removeEmptyDays: true, // won't print days that have no lessons / only the ones you added to the `ignoreList` |
|
cleanLessonTypes: true, // when gathering lesson names, don't include the type of lesson (laboratory / lecture etc.) |
|
cleanLessonNames: true, // when printing lesson names, don't print the classroom number and teacher name + surname |
|
ignoreList: [ // list of subjects you'd like to ignore (for instance because you've already passed them) - it works based on inlcude + is case insensitive! |
|
], |
|
}; |
|
|
|
(() => { |
|
"use strict"; |
|
|
|
// Get information from table |
|
// 10.04.2024 |
|
let finalDays = []; |
|
let finalString = ''; |
|
const basePath = '#layout-c22 > div'; |
|
const viewSwitchElChildren = document.querySelector(`${basePath} > div > usos-frame:nth-child(2) > div > fieldset`).children; |
|
const headers = [...document.querySelectorAll(`${basePath} > div > div > table > tbody > tr > th`)]; |
|
const lessons = [...document.querySelectorAll(`${basePath} > div > div > table > tbody > tr > td > div`)]; |
|
const cleanTypeRegex = new RegExp('\\s-[^-]*(?=\\s\\()'); |
|
const cleanNameRegex = new RegExp(' \\(.*'); |
|
|
|
const makeArrUniq = arr => Array.from(new Set(arr)); |
|
|
|
const downloadFile = (filename, text) => { // discretely creates and automatically downloads a text file -> <https://ourcodeworld.com/articles/read/189/how-to-create-a-file-and-generate-a-download-with-javascript-in-the-browser-without-a-server> |
|
const el = document.createElement('a'); |
|
el.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); |
|
el.setAttribute('download', filename); |
|
el.style.display = 'none'; |
|
document.body.appendChild(el); |
|
el.click(); |
|
document.body.removeChild(el); |
|
}; |
|
|
|
// only display on right type of timetable (`HTML (stary)`) |
|
if (viewSwitchElChildren.length === 3 && viewSwitchElChildren[1].textContent.includes('HTML (nowy)')) { |
|
// creating day dicts from headers |
|
headers.map(header => { |
|
const headerRect = header.getBoundingClientRect(); |
|
finalDays.push({ |
|
name: header.textContent.replace('(', ' ('), |
|
startPx: headerRect.x, |
|
endPx: headerRect.x + headerRect.width, |
|
lessons: [], |
|
}); |
|
}); |
|
|
|
// putting lessons in the correct days |
|
lessons.map(lesson => { |
|
finalDays.map(header => { |
|
// check wheter lesson elements is in the day (header) borders |
|
const lessonPosX = lesson.getBoundingClientRect().x; |
|
if (header.startPx < lessonPosX && lessonPosX < header.endPx) { |
|
header.lessons.push(config.cleanLessonTypes ? lesson.textContent.replace(cleanTypeRegex, '') : lesson.textContent); |
|
}; |
|
}); |
|
}); |
|
|
|
// applying some settings |
|
finalDays.map(day => { |
|
if (config.cleanLessonNames) day.lessons = day.lessons.map(lesson => lesson.replace(cleanNameRegex, '')); |
|
|
|
if (config.ignoreList.length > 0) { |
|
day.lessons = day.lessons.filter(lesson => { |
|
return !(config.ignoreList.map(x => x.toLowerCase())).includes(lesson.toLowerCase()); |
|
}); |
|
}; |
|
|
|
if (config.unique) day.lessons = makeArrUniq(day.lessons); |
|
|
|
if (!config.removeEmptyDays && day.lessons.length === 0) day.lessons = ['(empty)']; |
|
}); |
|
|
|
// removing empty days |
|
if (config.removeEmptyDays) finalDays = finalDays.filter(x => x.lessons.length !== 0); |
|
|
|
// output |
|
finalDays.map(day => { |
|
// don't add margin before first day |
|
(day.name !== finalDays[0].name) && (finalString += '\n\n'); |
|
finalString += `${day.name}\n`; |
|
|
|
let lessons = day.lessons; |
|
finalString += lessons.map(lesson => `- ${lesson}`).join('\n'); |
|
}); |
|
|
|
// --- |
|
|
|
// Display in USOS frame |
|
const copyBtnFunc = () => { |
|
window.navigator.clipboard.writeText(finalString) |
|
document.getElementById("btn-copy").disabled = true; |
|
}; |
|
|
|
const downloadBtnFunc = () => { |
|
if (finalString.length !== 0) { |
|
const timetableDateStr = document.querySelector(`${basePath} > div > div > div > b`).textContent; |
|
downloadFile(`${timetableDateStr}.txt`, finalString); |
|
}; |
|
document.getElementById("btn-download").disabled = true; |
|
}; |
|
|
|
const createBtnEl = (id, text, onclickFunc, anchorEl) => { |
|
const btnEl = document.createElement('button'); |
|
btnEl.id = id; |
|
btnEl.textContent = text; |
|
btnEl.setAttribute('style', 'margin-top: 4px'); |
|
btnEl.addEventListener('click', onclickFunc); |
|
anchorEl.appendChild(btnEl); |
|
}; |
|
|
|
const createUlEl = (elementsArr, anchorEl, addPadding=false) => { |
|
const renderProductList = (text, index, arr) => { |
|
const li = document.createElement('li'); |
|
|
|
if (addPadding) ul.setAttribute('style', 'padding-bottom: 20px'); |
|
|
|
ul.appendChild(li); |
|
li.textContent = li.textContent + text; |
|
}; |
|
|
|
const ul = document.createElement('ul'); |
|
|
|
anchorEl.appendChild(ul); |
|
elementsArr.forEach(renderProductList); |
|
}; |
|
|
|
|
|
const usosFrame = document.querySelector(`${basePath} > usos-frame:nth-child(6) > div`); |
|
if (usosFrame) { |
|
// clear current content |
|
usosFrame.innerHTML = ''; |
|
|
|
|
|
// create and add title <p> |
|
const titleEl = document.createElement('p').appendChild(document.createTextNode("Przedmioty:")); |
|
usosFrame.appendChild(titleEl); |
|
|
|
// create days ul |
|
createUlEl(finalDays.map(day => day.name), usosFrame); |
|
|
|
// create subjects uls |
|
for (let i = 0; i < finalDays.length; i++) { |
|
createUlEl(finalDays[i].lessons, usosFrame.querySelectorAll('div > ul > li')[i], (i < finalDays.length - 1 ? true : false)); |
|
}; |
|
|
|
// create btns |
|
createBtnEl("btn-copy", "Copy", copyBtnFunc, usosFrame); |
|
createBtnEl("btn-download", "Download txt", downloadBtnFunc, usosFrame); |
|
}; |
|
}; |
|
})(); |