Skip to content

Instantly share code, notes, and snippets.

@Pinjasaur
Last active May 1, 2026 17:35
Show Gist options
  • Select an option

  • Save Pinjasaur/4b612b9ddda2e25c7cd2b5cd0017469e to your computer and use it in GitHub Desktop.

Select an option

Save Pinjasaur/4b612b9ddda2e25c7cd2b5cd0017469e to your computer and use it in GitHub Desktop.
Minnebar Session Schedule .ics Bookmarklet https://sessions.minnestar.org/schedule
javascript:(function(){const r={},s=(document.querySelectorAll(".nav-top .timeslot[startsAt]").forEach(e=>{var t=e.getAttribute("href").replace("#","");r[t]={start:parseInt(e.getAttribute("startsAt"),10),end:parseInt(e.getAttribute("endsAt"),10)}}),[]);if(document.querySelectorAll(".timeslot[id]").forEach(e=>{var t=e.id;const o=r[t];o&&e.querySelectorAll(".session").forEach(t=>{var r=t.querySelector("button.toggle-attendance");if(r&&"true"===r.getAttribute("data-session-attending")){var r=t.querySelector("h3.title"),n=t.querySelector(".room"),t=t.querySelector(".description"),r=r?r.textContent.trim():"Untitled Session",n=n?n.textContent.trim():"";let e="";t&&((t=t.cloneNode(!0)).querySelectorAll("a[href]").forEach(e=>{var t=e.getAttribute("href"),r=e.textContent.trim();e.replaceWith(r===t?r:r+" ("+t+")")}),e=t.textContent.trim()),s.push({title:r,location:n,description:e,start:o.start,end:o.end})}})}),0===s.length)alert("No attended sessions found. Make sure you are marked as attending sessions.");else{const i=["BEGIN:VCALENDAR","VERSION:2.0","PRODID:-//Minnebar Schedule Bookmarklet//EN","CALSCALE:GREGORIAN","METHOD:PUBLISH"];s.forEach((e,t)=>{var t="minnebar-"+e.start+"-"+t+"@minnestar.org",r=n(Math.floor(Date.now()/1e3));i.push("BEGIN:VEVENT"),i.push(a("UID:"+t)),i.push("DTSTAMP:"+r),i.push("DTSTART:"+n(e.start)),i.push("DTEND:"+n(e.end)),i.push(a("SUMMARY:"+o(e.title))),e.location&&i.push(a("LOCATION:"+o(e.location))),e.description&&i.push(a("DESCRIPTION:"+o(e.description))),i.push("END:VEVENT")}),i.push("END:VCALENDAR");var e=i.join("\r\n"),e=new Blob([e],{type:"text/calendar;charset=utf-8"}),e=URL.createObjectURL(e),t=document.createElement("a");function n(e){return new Date(1e3*e).toISOString().replace(/[-:]/g,"").replace(/\.\d{3}/,"")}function o(e){return e.replace(/\\/g,"\\\\").replace(/;/g,"\\;").replace(/,/g,"\\,").replace(/\n/g,"\\n")}function a(e){for(var t=[];75<e.length;)t.push(e.substring(0,75)),e=" "+e.substring(75);return t.push(e),t.join("\r\n")}t.href=e,t.download="minnebar-schedule.ics",document.body.appendChild(t),t.click(),document.body.removeChild(t),URL.revokeObjectURL(e),alert("Downloaded "+s.length+" session(s) to minnebar-schedule.ics")}})();
javascript:(function(){
const timeMap = {};
document.querySelectorAll('.nav-top .timeslot[startsAt]').forEach(a => {
const href = a.getAttribute('href');
const id = href.replace('#', '');
timeMap[id] = {
start: parseInt(a.getAttribute('startsAt'), 10),
end: parseInt(a.getAttribute('endsAt'), 10)
};
});
const events = [];
document.querySelectorAll('.timeslot[id]').forEach(block => {
const id = block.id;
const times = timeMap[id];
if (!times) return;
block.querySelectorAll('.session').forEach(session => {
const btn = session.querySelector('button.toggle-attendance');
if (!btn || btn.getAttribute('data-session-attending') !== 'true') return;
const titleEl = session.querySelector('h3.title');
const roomEl = session.querySelector('.room');
const descEl = session.querySelector('.description');
const title = titleEl ? titleEl.textContent.trim() : 'Untitled Session';
const location = roomEl ? roomEl.textContent.trim() : '';
let description = '';
if (descEl) {
const clone = descEl.cloneNode(true);
clone.querySelectorAll('a[href]').forEach(a => {
const href = a.getAttribute('href');
const text = a.textContent.trim();
a.replaceWith(text === href ? text : text + ' (' + href + ')');
});
description = clone.textContent.trim();
}
events.push({ title, location, description, start: times.start, end: times.end });
});
});
if (events.length === 0) {
alert('No attended sessions found. Make sure you are marked as attending sessions.');
return;
}
function toICSDate(unixSec) {
const d = new Date(unixSec * 1000);
return d.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
}
function escapeICS(str) {
return str.replace(/\\/g, '\\\\').replace(/;/g, '\\;').replace(/,/g, '\\,').replace(/\n/g, '\\n');
}
function foldLine(line) {
const out = [];
while (line.length > 75) {
out.push(line.substring(0, 75));
line = ' ' + line.substring(75);
}
out.push(line);
return out.join('\r\n');
}
const lines = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//Minnebar Schedule Bookmarklet//EN',
'CALSCALE:GREGORIAN',
'METHOD:PUBLISH'
];
events.forEach((ev, i) => {
const uid = 'minnebar-' + ev.start + '-' + i + '@minnestar.org';
const now = toICSDate(Math.floor(Date.now() / 1000));
lines.push('BEGIN:VEVENT');
lines.push(foldLine('UID:' + uid));
lines.push('DTSTAMP:' + now);
lines.push('DTSTART:' + toICSDate(ev.start));
lines.push('DTEND:' + toICSDate(ev.end));
lines.push(foldLine('SUMMARY:' + escapeICS(ev.title)));
if (ev.location) lines.push(foldLine('LOCATION:' + escapeICS(ev.location)));
if (ev.description) lines.push(foldLine('DESCRIPTION:' + escapeICS(ev.description)));
lines.push('END:VEVENT');
});
lines.push('END:VCALENDAR');
const icsContent = lines.join('\r\n');
const blob = new Blob([icsContent], { type: 'text/calendar;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'minnebar-schedule.ics';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
alert('Downloaded ' + events.length + ' session(s) to minnebar-schedule.ics');
})();
@Pinjasaur

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment