Skip to content

Instantly share code, notes, and snippets.

@dzfranklin
Last active April 19, 2020 15:27
Show Gist options
  • Save dzfranklin/2120485e9594ad652a7fc0d999b91829 to your computer and use it in GitHub Desktop.
Save dzfranklin/2120485e9594ad652a7fc0d999b91829 to your computer and use it in GitHub Desktop.
Adds a calendar to the wesmaps planning page
<html>
<head>
<title>Add a Calendar to WesMaps</title>
<style>
body {
max-width: 600px;
margin-left: auto;
margin-right: auto;
margin-top: 80px;
margin-bottom: auto;
line-height: 1.5em;
}
.install-row {
text-align: center;
margin: 50px;
}
.install-button {
padding: 15px;
border-radius: 5px;
border: 1px solid hsl(0, 0%, 50.2%);
background-color: hsl(0, 100%, 40.6%);
color: white;
text-decoration: none;
}
</style>
</head>
<body>
<h1>Add a Calendar to WesMaps</h1>
<p>
Drag the button to your bookmarks toolbar to install. Click on the button on your bookmarks toolbar while visiting a wesmaps page that shows your schedule at the bottom and a calendar will appear.
</p>
<div class="install-row">
<a class="install-button" href="javascript:(function(){let s=document.createElement('script');s.src='https://gist.githack.com/danielzfranklin/2120485e9594ad652a7fc0d999b91829/raw/a570f5280650e5103572296e497bd843e2690526/remote.js';document.head.append(s)})()">WesMaps Calendar</a>
</div>
<script>
document.querySelector('.install-button').addEventListener('click', e => {
e.preventDefault();
alert('Drag the button to your bookmarks toolbar and then click on it when you visit a WesMaps page. Clicking on it here does nothing.');
});
</script>
</body>
</html>
function displayCalendar() {
let classes = parsePage();
const DAYS = ['M', 'T', 'W', 'R', 'F'];
let calendar = document.createElement('div');
calendar.className = 'calendar';
let close = document.createElement('button');
close.innerText = 'X';
close.title = 'Close';
close.className = 'calendar-close';
close.addEventListener('click', () => {
document.body.removeChild(calendar);
});
calendar.append(close);
for (let day_name of DAYS) {
let day = document.createElement('div');
day.className = 'calendar-day calendar-' + day_name;
calendar.append(day);
}
for (let c of classes) {
for (let time of c.times) {
let period = document.createElement('div');
period.className = 'calendar-period';
let start = toPixels(time.from);
let height = toPixels(time.to) - start;
period.style.top = `${start}px`;
period.style.height = `${height}px`;
period.innerHTML = `<h2 class="calendar-period-name">${c.name}</h2> <p class="calendar-period-times">${time.from}-${time.to}</p>`;
calendar.querySelector('.calendar-' + time.day).append(period);
}
}
if (document.body.tagName === 'FRAMESET') {
let root = document.querySelector('html');
let body = document.createElement('body');
body.append(document.body);
root.append(body);
}
if (!document.querySelector('style.calendar-styles')) {
let styles = document.createElement('style');
styles.className = 'calendar-styles';
styles.innerText = `
.calendar {
position: fixed;
top: 30px;
left: 30px;
right: 30px;
bottom: 30px;
background-color: white;
display: flex;
overflow: auto;
}
.calendar-close {
position: fixed;
z-index: 999;
top: 40px;
right: 40px;
background-color: red;
color: hsl(0, 76.5%, 93.3%);
border-radius: 1.5em;
font-size: 17px;
width: 1.5em;
height: 1.5em;
border: none;
}
.calendar-day, .calendar-period {
/* since period is absolute they both have the same parent for size purposes */
width: 20%;
}
.calendar-period {
position: absolute;
background-color: lightblue;
overflow: auto;
box-sizing: border-box; /* so that the padding subtracts from the width */
border-left: 5px solid white;
border-right: 5px solid white;
padding: 10px;
}
.calendar-period h2 {
font-size: 17px;
margin: 0;
margin-bottom: 5px;
}
.calendar-period p {
margin: 0;
}
`;
document.head.append(styles);
}
let prev_calendar = document.body.querySelector('.calendar');
if (prev_calendar) {
document.body.removeChild(prev_calendar);
}
document.body.append(calendar);
}
function toPixels(time) {
const DAY_START = 8 * 60;
const PIXELS_PER_MINUTE = 1.2;
let [hours_part, minutes_part] = time.replace('AM', '').replace('PM', '').split(':');
hours_part = Number.parseInt(hours_part, 10);
minutes_part = Number.parseInt(minutes_part, 10);
if (time.endsWith('PM')) {
if (hours_part !== 12) {
hours_part += 12;
}
}
let in_minutes = (hours_part * 60) + minutes_part - DAY_START;
return in_minutes * PIXELS_PER_MINUTE;
}
function parsePage() {
return parseTable(document.querySelector('frame[name=planning]').contentDocument.querySelector('#course_table'));
}
function parseTable(table) {
return Array.from(table.querySelectorAll('tr')).filter(e => e.id.indexOf('header') === -1).map(parseRow);
}
function parseRow(row) {
let name = parseName(row);
let times = parseTimes(row);
return { name, times };
}
function parseName(row) {
let xlist = row.querySelector('.xlist_cell span');
if (!xlist) {
xlist = row.querySelector('.xlist_cell select option[selected]');
}
return `${xlist.innerText.replace(' ', '')}.${row.querySelector('span.section_cell').innerText} ${row.querySelector('.title_cell a').innerText}`;
}
function parseTimes(row) {
let s = row.querySelector('.meeting_cell').innerText;
let [dates, time_of_day] = s.split(';')[0].split(' ');
time_of_day = time_of_day.split('-');
dates = dates.split('');
let out = [];
for (let date of dates) {
if (date != '.') {
out.push({ 'day': date, 'from': time_of_day[0], 'to': time_of_day[1] });
}
}
return out;
}
if (document.readyState == 'complete') {
displayCalendar();
} else {
document.addEventListener('load', () => displayCalendar());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment