live-schedule/main.js

245 lines
8.6 KiB
JavaScript

const scheduleDOM = document.getElementById('schedule');
const VERSION = "v1.0.0";
const days = [
'monday', 'tuesday', 'wednesday', 'thursday',
'friday', 'saturday', 'sunday',
];
let schedule = {
version: VERSION,
days: {},
};
const offlineTitle = 'OFFLINE';
const offlineSubtitle = 'takin a nap';
let altText;
let copyClipboard;
let saveImage;
document.addEventListener('readystatechange', () => {
document.getElementById('version').innerText = VERSION;
loadSchedule();
createSchedule();
document.getElementById('import').addEventListener('click', () => {
importJSON();
});
document.getElementById('save').addEventListener('click', () => {
saveSchedule();
});
document.getElementById('alt-text').addEventListener('click', () => {
altText();
});
document.getElementById('clipboard').addEventListener('click', () => {
copyClipboard();
});
document.getElementById('save-image').addEventListener('click', () => {
saveImage();
});
document.getElementById('export').addEventListener('click', () => {
exportJSON();
});
});
function importJSON() {
const filePicker = document.createElement('input');
filePicker.type = 'file';
filePicker.accept = 'application/json';
filePicker.addEventListener('input', () => {
if (filePicker.files.length == 0) return;
const file = filePicker.files[0];
file.text().then(text => {
try {
schedule = JSON.parse(text);
for (const day in schedule.days) {
const data = schedule.days[day];
const slot = document.getElementById(`slot-${day}`);
if (!data.enabled) {
slot.classList.add('disabled');
slot.querySelector('.timeslot-title').innerText = offlineTitle
slot.querySelector('.timeslot-subtitle').innerText = offlineSubtitle
continue;
}
slot.classList.remove('disabled');
slot.querySelector('.timeslot-time').innerText = data.time
slot.querySelector('.timeslot-title').innerText = data.title
slot.querySelector('.timeslot-subtitle').innerText = data.subtitle
}
} catch (err) {
console.error(err);
alert('Failed to import schedule. Check the browser console for details.');
}
}).catch(err => {
console.error(err);
alert('Failed to import schedule. Check the browser console for details.');
});
});
filePicker.click();
}
function saveSchedule() {
localStorage.setItem('streamschedule', btoa(JSON.stringify(schedule)));
}
function loadSchedule() {
const saved = localStorage.getItem('streamschedule');
if (!saved) return;
schedule = JSON.parse(atob(saved));
}
function generateAltText() {
let altText = "ari melody LIVE schedule: ";
const activeDays = Object.keys(schedule.days)
.filter(day => schedule.days[day].enabled)
.reduce((days, day) => (days[day] = schedule.days[day], days), {});
const nDays = Object.keys(activeDays).length;
for (let i = 0; i < nDays; i++) {
const day = Object.keys(activeDays)[i];
const slot = schedule.days[day];
if (!slot.enabled) continue;
if (i == nDays - 1)
altText += `and `;
altText += slot.title;
if (slot.subtitle.length > 0)
altText += ` (${slot.subtitle})`;
altText += ` on ${day} @ ${slot.time}`;
if (i < nDays - 1)
altText += `, `;
}
return altText;
}
altText = () => alert(generateAltText());
if ('webkit' in window && 'messageHandlers' in window.webkit) {
altText = () => window.webkit.messageHandlers.altText.postMessage([generateAltText()]);
copyClipboard = () => {
const schedule = document.getElementById('schedule');
const boundingRect = schedule.getBoundingClientRect();
window.webkit.messageHandlers.copySnapshot.postMessage([
'clipboard',
boundingRect.x + 2,
boundingRect.y + 2,
boundingRect.width - 3,
boundingRect.height - 3,
]);
};
saveImage = () => {
const schedule = document.getElementById('schedule');
const boundingRect = schedule.getBoundingClientRect();
window.webkit.messageHandlers.copySnapshot.postMessage([
'file',
boundingRect.x + 2,
boundingRect.y + 2,
boundingRect.width - 3,
boundingRect.height - 3,
]);
};
document.getElementById('clipboard').removeAttribute('disabled');
document.getElementById('save-image').removeAttribute('disabled');
}
function exportJSON() {
const json = JSON.stringify(schedule, null, '\t');
const data = new Blob([json], {
type: 'application/json',
});
const link = document.createElement('a');
link.href = URL.createObjectURL(data);
link.download = 'stream-schedule.json';
link.click();
}
function createSchedule() {
for (let i = 0; i < days.length; i++) {
const day = days[i];
if (schedule.days[day] == undefined) schedule.days[day] = {
enabled: false,
time: '6pm UTC',
title: 'Untitled Event',
subtitle: "yep, that sure is an event if i've ever seen one",
};
const container = document.createElement('div');
container.className = 'timeslot';
container.id = `slot-${day}`;
if (!schedule.days[day].enabled)
container.classList.add('disabled');
const timeslotDateContainer = document.createElement('div');
timeslotDateContainer.className = 'timeslot-date';
const timeslotDay = document.createElement('div');
timeslotDay.className = 'timeslot-day';
timeslotDay.innerText = day.substring(0, 3).toUpperCase();
timeslotDateContainer.appendChild(timeslotDay);
const timeslotTime = document.createElement('div');
timeslotTime.className = 'timeslot-time';
timeslotTime.innerText = schedule.days[day].time;
timeslotTime.contentEditable = true;
timeslotDateContainer.appendChild(timeslotTime);
container.appendChild(timeslotDateContainer);
const timeslotContentContainer = document.createElement('div');
timeslotContentContainer.className = 'timeslot-content';
const timeslotTitle = document.createElement('div');
timeslotTitle.className = 'timeslot-title';
timeslotTitle.innerText = schedule.days[day].enabled ?
schedule.days[day].title : offlineTitle;
timeslotTitle.contentEditable = schedule.days[day].enabled;
timeslotTitle.spellcheck = false;
timeslotContentContainer.appendChild(timeslotTitle);
const timeslotSubtitle = document.createElement('div');
timeslotSubtitle.className = 'timeslot-subtitle';
timeslotSubtitle.innerText = schedule.days[day].enabled ?
schedule.days[day].subtitle : offlineSubtitle;
timeslotSubtitle.contentEditable = schedule.days[day].enabled;
timeslotSubtitle.spellcheck = false;
timeslotContentContainer.appendChild(timeslotSubtitle);
container.appendChild(timeslotContentContainer);
scheduleDOM.appendChild(container);
timeslotDay.addEventListener('click', () => {
schedule.days[day].enabled = !schedule.days[day].enabled;
timeslotTitle.contentEditable = schedule.days[day].enabled;
timeslotSubtitle.contentEditable = schedule.days[day].enabled;
if (schedule.days[day].enabled) {
container.classList.remove('disabled');
timeslotTitle.innerText = schedule.days[day].title;
timeslotSubtitle.innerText = schedule.days[day].subtitle;
} else {
container.classList.add('disabled');
timeslotTitle.innerText = offlineTitle;
timeslotSubtitle.innerText = offlineSubtitle;
}
});
timeslotTitle.addEventListener('keyup', () => {
schedule.days[day].title = timeslotTitle.innerText;
});
timeslotSubtitle.addEventListener('keyup', () => {
if (timeslotSubtitle.innerHTML.includes('<br>'))
timeslotSubtitle.innerText = '';
schedule.days[day].subtitle = timeslotSubtitle.innerText;
});
timeslotTime.addEventListener('keyup', () => {
schedule.days[day].time = timeslotTime.innerText;
});
}
}