283 lines
9.6 KiB
JavaScript
283 lines
9.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) {
|
|
let _promiseId = 0;
|
|
let _promises = new Map();
|
|
|
|
window.addEventListener('message', (event) => {
|
|
const [id] = event.detail;
|
|
const [resolve,] = _promises.get(id) ?? [];
|
|
if (!resolve) return;
|
|
_promises.delete(id);
|
|
resolve();
|
|
});
|
|
|
|
window.addEventListener('message', (event) => {
|
|
const [id] = event.detail;
|
|
const [,reject] = _promises.get(id) ?? [];
|
|
if (!reject) return;
|
|
_promises.delete(id);
|
|
reject();
|
|
});
|
|
|
|
altText = () => new Promise((resolve, reject) => {
|
|
const thisPromiseId = ++_promiseId;
|
|
|
|
_promises.set(thisPromiseId, [resolve, reject]);
|
|
|
|
window.webkit.messageHandlers.altText.postMessage([
|
|
thisPromiseId,
|
|
generateAltText(),
|
|
]);
|
|
});
|
|
|
|
copyClipboard = () => new Promise((resolve, reject) => {
|
|
const thisPromiseId = ++_promiseId;
|
|
|
|
_promises.set(thisPromiseId, [resolve, reject]);
|
|
|
|
const schedule = document.getElementById('schedule');
|
|
const boundingRect = schedule.getBoundingClientRect();
|
|
|
|
window.webkit.messageHandlers.copySnapshot.postMessage([
|
|
thisPromiseId,
|
|
'clipboard',
|
|
boundingRect.x + 1,
|
|
boundingRect.y + 1,
|
|
boundingRect.width - 2,
|
|
boundingRect.height - 2,
|
|
]);
|
|
});
|
|
|
|
saveImage = () => new Promise((resolve, reject) => {
|
|
const thisPromiseId = ++_promiseId;
|
|
|
|
_promises.set(thisPromiseId, [resolve, reject]);
|
|
|
|
const schedule = document.getElementById('schedule');
|
|
const boundingRect = schedule.getBoundingClientRect();
|
|
|
|
window.webkit.messageHandlers.copySnapshot.postMessage([
|
|
thisPromiseId,
|
|
'file',
|
|
boundingRect.x + 1,
|
|
boundingRect.y + 1,
|
|
boundingRect.width - 2,
|
|
boundingRect.height - 2,
|
|
]);
|
|
});
|
|
|
|
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;
|
|
});
|
|
}
|
|
}
|