☰

Sleazy Fork is available in English.

FL Tools Calendar

Export the current FetLife event to a calendar file

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu budete musieť nainštalovať rozšírenie, ako je napríklad Tampermonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         FL Tools Calendar
// @namespace    https://gitlab.com/fl-tools-lite/fl-tools-calendar
// @version      0.1.0
// @description  Export the current FetLife event to a calendar file
// @author       TypicalBits
// @icon         data:image/png;base64,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
// @icon64       data:image/png;base64,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
// @license      PolyForm-Noncommercial-1.0.0
// @match        https://fetlife.com/*
// @run-at       document-end
// @noframes
// @grant        GM.getValue
// @grant        GM.setValue
// @homepageURL  https://gitlab.com/fl-tools-lite/fl-tools-calendar
// @supportURL   https://gitlab.com/fl-tools-lite/fl-tools-calendar/-/issues
// ==/UserScript==

// Copyright 2026 TypicalBits.
(function(){
'use strict';
// dock.js
// Copyright 2026 TypicalBits. Shared FL Tools Lite coordination.
const LITE_ORDER = ['view','read','calendar','notes','return'];
const LITE_CORNERS = ['bottom-left','bottom-right','top-left','top-right'];
const LITE_COLORS = {view:'#62A8DE',read:'#B9A0F5',calendar:'#65D8EB',notes:'#F39BC8',return:'#CCD4E0'};
const LITE_REPAIR = 'flt-lite:repair-v1';
function claimProduct(document,id) {
  if (!LITE_ORDER.includes(id) || document.querySelector(`[data-flt-lite-claim="${id}"]`)) return null;
  const claim=document.createElement('span');claim.hidden=true;claim.dataset.fltLiteClaim=id;
  document.documentElement.append(claim);
  return {ensure(){if(!claim.isConnected)document.documentElement.append(claim);},release(){claim.remove();}};
}
function setLiteCorner(document,host,corner) {
  host.dataset.corner=corner; document.documentElement.dataset.fltLiteCorner=corner;
  for(const panel of document.querySelectorAll('[data-flt-lite-panel]'))panel.dataset.fltLiteCorner=corner;
}
function ownLiteDock(document,host,storage,restoreSaved=true) {
  const w=document.defaultView; const shadow=host.attachShadow({mode:'open'});
  const style=document.createElement('style');style.textContent=`
  :host{position:fixed;bottom:16px;left:16px;z-index:2147483001;color-scheme:dark;font:12px/1.4 system-ui,sans-serif}
  :host([data-corner="bottom-right"]){left:auto;right:16px}
  :host([data-corner="top-left"]){bottom:auto;top:16px}
  :host([data-corner="top-right"]){left:auto;right:16px;bottom:auto;top:16px}
  :host([data-fallback="true"]){transform:translateY(-58px)}:host([data-fallback="true"][data-corner^="top"]){transform:translateY(58px)}
  .dock{display:flex;flex-wrap:wrap;gap:5px;max-width:calc(100vw - 32px)}
  button{display:flex;align-items:center;justify-content:center;padding:3px;width:46px;height:46px;border:1px solid var(--accent);border-radius:8px;background:#17171a;cursor:pointer;color:var(--accent);font:inherit}
  button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}button[aria-expanded="true"]{box-shadow:0 0 0 1px var(--accent)}img{width:38px;height:38px;display:block}
  `;const bar=document.createElement('nav');bar.className='dock';bar.setAttribute('aria-label','FL Tools Lite');shadow.append(style,bar);
  let cornerRevision=0;
  let current=null;const buttons=new Map(); let disposed=false;let writes=Promise.resolve();
  function ports(){return [...host.children].filter(x=>x.hasAttribute('data-flt-lite-port'));}
  function close(port,focus=false){port?.dispatchEvent(new w.Event('flt-lite:close'));buttons.get(port)?.setAttribute('aria-expanded','false');if(current===port)current=null;if(focus)buttons.get(port)?.focus();}
  function open(port){if(current===port){close(port,true);return;}if(current)close(current);current=port;buttons.get(port)?.setAttribute('aria-expanded','true');port.dispatchEvent(new w.Event('flt-lite:open'));}
  function render(){
    const active=ports().sort((a,b)=>LITE_ORDER.indexOf(a.dataset.product)-LITE_ORDER.indexOf(b.dataset.product));
    for(const [port,button]of buttons)if(!active.includes(port)){if(current===port)close(port);button.remove();buttons.delete(port);}
    for(const port of active){
      let button=buttons.get(port);
      if(!button){port.dispatchEvent(new w.Event('flt-lite:close'));button=document.createElement('button');button.type='button';button.dataset.product=port.dataset.product;button.setAttribute('aria-label','Open FL Tools '+port.dataset.label);button.setAttribute('aria-expanded','false');button.style.setProperty('--accent',LITE_COLORS[port.dataset.product]);
        if(port.dataset.icon){const image=document.createElement('img');image.src=port.dataset.icon;image.alt='';button.append(image);}else button.textContent=port.dataset.label;
        button.addEventListener('click',()=>open(port));buttons.set(port,button);
      }
      if(bar.children[active.indexOf(port)]!==button)bar.append(button);
    }
    setLiteCorner(document,host,host.dataset.corner||'bottom-left');
  }
  const route=event=>{const port=event.target;if(port.parentNode!==host||!port.hasAttribute('data-flt-lite-port'))return;
    if(event.type==='flt-lite:register')render();
    if(event.type==='flt-lite:toggle')open(port);
    if(event.type==='flt-lite:hide')close(port,true);
    if(event.type==='flt-lite:focus')buttons.get(port)?.focus();
    if(event.type==='flt-lite:corner'&&LITE_CORNERS.includes(port.dataset.corner)){
      cornerRevision++;const corner=port.dataset.corner;setLiteCorner(document,host,corner);
      if(storage)writes=writes.then(()=>storage.setValue('flt-lite:dock:v1',corner)).catch(()=>{});
    }
  };
  for(const name of ['register','toggle','hide','focus','corner'])host.addEventListener('flt-lite:'+name,route);
  const escape=event=>{if(current&&event.key==='Escape'){close(current,true);event.stopPropagation();}};
  document.addEventListener('keydown',escape);
  const observer=new w.MutationObserver(()=>{if(!disposed)render();});observer.observe(host,{childList:true});render();
  if(storage&&restoreSaved)Promise.resolve().then(()=>storage.getValue('flt-lite:dock:v1','bottom-left')).then(corner=>{if(!disposed&&cornerRevision===0&&host.isConnected&&LITE_CORNERS.includes(corner))setLiteCorner(document,host,corner);}).catch(()=>{});
  return {destroy(){disposed=true;observer.disconnect();document.removeEventListener('keydown',escape);for(const name of ['register','toggle','hide','focus','corner'])host.removeEventListener('flt-lite:'+name,route);},render};
}
function registerDock(document,{id,label,icon,onOpen=()=>{},onClose=()=>{},storage}) {
  const w=document.defaultView;const port=document.createElement('span');port.hidden=true;port.dataset.fltLitePort='';port.dataset.product=id;port.dataset.label=label;port.dataset.icon=icon;
  port.addEventListener('flt-lite:open',onOpen);port.addEventListener('flt-lite:close',onClose);
  let owner=null,ownedHost=null,destroyed=false,scheduled=false;
  function ensure(){
    if(destroyed||!document.body)return;
    let host=document.querySelector('[data-flt-lite-dock="1"]');
    if(!host){const restoreSaved=!document.documentElement.dataset.fltLiteCorner;owner?.destroy();host=document.createElement('div');host.dataset.fltLiteDock='1';host.dataset.corner=document.documentElement.dataset.fltLiteCorner||ownedHost?.dataset.corner||'bottom-left';host.dataset.fallback=String(!!document.querySelector('[data-flt-lite-dock]:not([data-flt-lite-dock="1"])'));document.body.append(host);ownedHost=host;owner=ownLiteDock(document,host,storage,restoreSaved);}
    if(port.parentNode!==host){host.append(port);port.dispatchEvent(new w.Event('flt-lite:register',{bubbles:true}));}
    owner?.render();
  }
  const schedule=()=>{if(scheduled||destroyed)return;scheduled=true;w.queueMicrotask(()=>{scheduled=false;ensure();});};
  const observer=new w.MutationObserver(records=>{if(!port.isConnected||!document.querySelector('[data-flt-lite-dock="1"]'))schedule();});observer.observe(document.documentElement,{childList:true,subtree:true});
  document.addEventListener(LITE_REPAIR,schedule);ensure();
  const send=name=>{ensure();port.dispatchEvent(new w.Event('flt-lite:'+name,{bubbles:true}));};
  return {
    ensure,open(){send('toggle');},close(){send('hide');},focus(){send('focus');},
    setCorner(corner){if(!LITE_CORNERS.includes(corner))return;port.dataset.corner=corner;send('corner');},
    get corner(){return port.parentNode?.dataset.corner||'bottom-left';},
    destroy(){if(destroyed)return;destroyed=true;observer.disconnect();document.removeEventListener(LITE_REPAIR,schedule);onClose();const host=port.parentNode;port.remove();
      if(owner&&host===ownedHost){const survivors=[...host.children];owner.destroy();for(const survivor of survivors)document.body.append(survivor);host.remove();document.dispatchEvent(new w.Event(LITE_REPAIR));}
      else if(host&&!host.querySelector('[data-flt-lite-port]'))host.remove();
      port.removeEventListener('flt-lite:open',onOpen);port.removeEventListener('flt-lite:close',onClose);
    }
  };
}

// storage.js
// Copyright 2026 TypicalBits.
function createRecordStore(storage,key,normalize,locks=globalThis.navigator?.locks) {
  let value={},loaded=false,error='',queue=Promise.resolve();
  return {
    get value(){return structuredClone(value);},get error(){return error;},
    async load(){try{value=normalize(await storage.getValue(key,{}));loaded=true;error='';}catch{error='Saved data could not be read. Reload before saving.';}return structuredClone(value);},
    transact(change){const job=queue.then(async()=>{
      if(!loaded)throw Error(error||'Saved data has not loaded.');
      if(!locks?.request){error='This browser cannot safely save across tabs. Use a browser with Web Locks support.';throw Error(error);}
      const controller=new AbortController();const timeout=setTimeout(()=>controller.abort(),10000);
      try{return await locks.request('flt-lite:storage:'+key,{mode:'exclusive',signal:controller.signal},async()=>{
        let latest;try{latest=normalize(await storage.getValue(key,{}));}catch{error='Saved data could not be read. Nothing was overwritten.';throw Error(error);}
        const next=normalize(change(structuredClone(latest)));
        try{await storage.setValue(key,next);value=next;error='';return structuredClone(value);}catch{error='Could not save. Your previous saved data is unchanged.';throw Error(error);}
      });}catch(problem){if(controller.signal.aborted){error='Another tab is busy saving. Please try again.';throw Error(error);}throw problem;}finally{clearTimeout(timeout);}
    });queue=job.catch(()=>{});return job;}

  };
}
function pageKey(input) {
  const url=new URL(input);
  if(url.protocol!=='https:'||url.hostname!=='fetlife.com'||url.username||url.password||url.port)throw Error('This is not a FetLife page.');
  url.hash='';return url.href;
}

// panel.js
// Copyright 2026 TypicalBits.
function liteElement(document,tag,attributes={},text='') {
  const element=document.createElement(tag);for(const [key,value]of Object.entries(attributes))element.setAttribute(key,String(value));if(text)element.textContent=text;return element;
}
function createLitePanel(document,{id,name,accent,icon,version,storage,onOpen=()=>{}}) {
  const e=(tag,attrs,text)=>liteElement(document,tag,attrs,text);
  const host=e('div',{'data-flt-lite-panel':id,'data-flt-lite-corner':'bottom-left'});const shadow=host.attachShadow({mode:'open'});
  const style=e('style');style.textContent=`
  :host{all:initial;position:fixed;left:16px;bottom:16px;z-index:2147483002;font:12px/1.5 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:#eeeef4;color-scheme:dark;--accent:${accent}}
  :host([data-flt-lite-corner$="right"]){left:auto;right:16px}:host([data-flt-lite-corner^="top"]){bottom:auto;top:16px}
  *,*::before,*::after{box-sizing:border-box}[hidden]{display:none!important}button,input,textarea,select{font:inherit;color:inherit}button{cursor:pointer}button:disabled{opacity:.5;cursor:default}
  button,input,textarea,select{background:#222228;border:1px solid #4b4b59;border-radius:6px;padding:6px 8px;min-width:0}button{min-height:32px}button:focus-visible,input:focus-visible,textarea:focus-visible,summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .panel{position:absolute;bottom:54px;left:0;width:min(320px,calc(100vw - 32px));max-height:calc(100dvh - 90px);overflow:auto;padding:12px;background:#131318;border:1px solid var(--accent);border-radius:10px;box-shadow:0 8px 26px #0008}
  :host([data-flt-lite-corner$="right"]) .panel{left:auto;right:0}:host([data-flt-lite-corner^="top"]) .panel{bottom:auto;top:54px}
  .heading{display:flex;align-items:center;gap:8px;margin-bottom:12px}.heading strong{flex:1;font-size:14px}.heading img{width:30px;height:30px}.version{font-size:10px;color:#b5b5c4}.close{width:30px;min-height:30px;padding:0}
  p{margin:0 0 10px}fieldset{border:0;padding:0;margin:0 0 12px;min-width:0}legend,label{font-weight:600;padding:0;margin-bottom:6px;display:block}input,textarea,select{display:block;width:100%;margin-bottom:10px}textarea{min-height:160px;resize:vertical}
  .choices,.actions{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px}.choices button{flex:1}.choices button[aria-pressed=true],.primary{background:var(--accent);border-color:var(--accent);color:#101015;font-weight:600}
  .row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}.row label{margin:0;min-width:0}.switch{position:relative;flex:none;width:34px;min-height:20px;height:20px;padding:0;border-radius:5px}.switch::after{content:"";position:absolute;top:3px;left:3px;width:12px;height:12px;background:#b5b5c4;border-radius:3px}.switch[aria-checked=true]{background:var(--accent)}.switch[aria-checked=true]::after{left:17px;background:#131318}
  .note,.status,.page{font-size:11px;color:#b5b5c4;overflow-wrap:anywhere}.status{border-top:1px solid #3d3d46;padding-top:8px;margin:10px 0 0}.status:empty{display:none}details{border-top:1px solid #3d3d46;margin-top:10px;padding-top:8px}summary{cursor:pointer;color:var(--accent);font-weight:600;margin-bottom:8px}.record{border-top:1px solid #3d3d46;padding:8px 0;overflow-wrap:anywhere}.record strong{display:block;margin-bottom:4px}.confirmation{border:1px solid var(--accent);padding:8px;border-radius:6px;margin-bottom:10px}
  `;
  const panel=e('section',{class:'panel','aria-label':'FL Tools '+name});panel.hidden=true;
  const heading=e('div',{class:'heading'});if(icon)heading.append(e('img',{src:icon,alt:''}));heading.append(e('strong',{},'FL Tools '+name),e('span',{class:'version'},version));const close=e('button',{class:'close',type:'button','aria-label':'Close '+name},'×');heading.append(close);
  const body=e('div',{'data-content':''}),settings=e('details');settings.append(e('summary',{},'Settings'));const corners=e('div',{class:'choices','aria-label':'Dock position'});
  let dock;for(const corner of ['bottom-left','bottom-right','top-left','top-right']){const button=e('button',{type:'button','aria-label':corner.replace('-',' '),'aria-pressed':'false'},corner.startsWith('top')?'↑ '+(corner.endsWith('left')?'Left':'Right'):'↓ '+(corner.endsWith('left')?'Left':'Right'));button.addEventListener('click',()=>{dock.setCorner(corner);for(const b of corners.children)b.setAttribute('aria-pressed',String(b===button));});corners.append(button);}settings.append(e('p',{class:'note'},'Dock position'),corners);
  const status=e('p',{class:'status',role:'status','data-status':''});panel.append(heading,body,settings,status);shadow.append(style,panel);document.body.append(host);
  dock=registerDock(document,{id,label:name,icon,storage,onOpen:()=>{panel.hidden=false;onOpen();close.focus();},onClose:()=>{panel.hidden=true;}});close.addEventListener('click',()=>dock.close());
  return {host,shadow,panel,body,settings,dock,e,setStatus(text){if(status.textContent!==text)status.textContent=text;},ensure(){if(!host.isConnected)document.body.append(host);dock.ensure();},destroy(){dock.destroy();host.remove();}};
}
function watchLitePage(document,callback) {
  const window=document.defaultView;let queued=false,destroyed=false;
  const owned=node=>{const element=node.nodeType===1?node:node.parentElement;const host=element?.getRootNode()?.host;return !!element?.closest('[data-flt-lite-panel],[data-flt-lite-dock],[data-flt-lite-port],[data-flt-lite-claim],[data-flt-lite-style],[data-flt-view-root],[data-flt-view-style]')||(typeof host?.hasAttribute==='function'&&host.hasAttribute('data-flt-lite-panel'));};
  const schedule=()=>{if(queued||destroyed)return;queued=true;window.queueMicrotask(()=>{queued=false;if(!destroyed)callback();});};
  const observer=new window.MutationObserver(records=>{if(records.some(record=>!owned(record.target)&&[...record.addedNodes,...record.removedNodes].some(node=>!owned(node))))schedule();});observer.observe(document.documentElement,{childList:true,subtree:true});
  const events=['popstate','pageshow','turbo:load','turbo:render','turbolinks:load'];for(const event of events)window.addEventListener(event,schedule);
  return ()=>{destroyed=true;observer.disconnect();for(const event of events)window.removeEventListener(event,schedule);};
}

// bootstrap.js
// Copyright 2026 TypicalBits.
async function bootstrapLite({document,window,storage,product,setup}) {
  const claim=claimProduct(document,product.id);if(!claim)return {duplicate:true,destroy(){}};
  let feature,stop=()=>{},ui;
  try {
    const refresh=()=>{claim.ensure();ui.ensure();feature?.refresh?.();};
    ui=createLitePanel(document,{...product,storage,onOpen:refresh});ui.setStatus('Loading saved settings…');
    feature=await setup({ui,document,window,storage});stop=watchLitePage(document,refresh);refresh();
    return {refresh,destroy(){stop();feature?.destroy?.();ui.destroy();claim.release();}};
  }catch(error){stop();feature?.destroy?.();ui?.destroy();claim.release();throw error;}
}
function liteButton(ui,label,attributes={},handler=()=>{}) {
  const button=ui.e('button',{type:'button',...attributes},label);button.addEventListener('click',handler);return button;
}
function liteField(ui,name,label,value='') {
  const id='flt-'+ui.host.dataset.fltLitePanel+'-'+name;const node=ui.e('input',{id,type:'text','data-field':name});node.value=value;
  ui.body.append(ui.e('label',{for:id},label),node);return node;
}

// icon.js
const PRODUCT_ICON = "data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%20role%3D%22img%22%20aria-labelledby%3D%22title%20desc%22%3E%0A%20%20%3Ctitle%20id%3D%22title%22%3EFL%20Tools%20Calendar%20badge%3C%2Ftitle%3E%0A%20%20%3Cdesc%20id%3D%22desc%22%3EFL%20Tools%20Calendar%20badge.%20Artwork%20is%20border-free%3B%20CSS%20supplies%20the%20launcher%20and%20menu%20frames.%3C%2Fdesc%3E%0A%20%20%3Cdefs%3E%0A%20%20%20%20%3CradialGradient%20id%3D%22surface%22%20cx%3D%2238%25%22%20cy%3D%2228%25%22%20r%3D%2285%25%22%3E%0A%20%20%20%20%20%20%3Cstop%20offset%3D%220%22%20stop-color%3D%22%2325242a%22%2F%3E%0A%20%20%20%20%20%20%3Cstop%20offset%3D%22.58%22%20stop-color%3D%22%2317171a%22%2F%3E%0A%20%20%20%20%20%20%3Cstop%20offset%3D%221%22%20stop-color%3D%22%230e0e10%22%2F%3E%0A%20%20%20%20%3C%2FradialGradient%3E%0A%20%20%20%20%3Cfilter%20id%3D%22labelGlow%22%20x%3D%22-40%25%22%20y%3D%22-60%25%22%20width%3D%22180%25%22%20height%3D%22220%25%22%3E%0A%20%20%20%20%20%20%3CfeGaussianBlur%20in%3D%22SourceGraphic%22%20stdDeviation%3D%22.85%22%20result%3D%22blur%22%2F%3E%0A%20%20%20%20%20%20%3CfeFlood%20flood-color%3D%22%2365D8EB%22%20flood-opacity%3D%22.42%22%20result%3D%22glowColor%22%2F%3E%0A%20%20%20%20%20%20%3CfeComposite%20in%3D%22glowColor%22%20in2%3D%22blur%22%20operator%3D%22in%22%20result%3D%22softGlow%22%2F%3E%0A%20%20%20%20%20%20%3CfeMerge%3E%0A%20%20%20%20%20%20%20%20%3CfeMergeNode%20in%3D%22softGlow%22%2F%3E%0A%20%20%20%20%20%20%20%20%3CfeMergeNode%20in%3D%22SourceGraphic%22%2F%3E%0A%20%20%20%20%20%20%3C%2FfeMerge%3E%0A%20%20%20%20%3C%2Ffilter%3E%0A%20%20%3C%2Fdefs%3E%0A%20%20%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22url%28%23surface%29%22%2F%3E%0A%20%20%3Cimage%20href%3D%22data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAE90lEQVR4AcyZW4hWVRTH%2FzM5NamVGXQhrKjHLCqsJgeMLhBGQaCkZEqXyWns60JBRUJkD0UQZMVoWSFJt4dIKOqpHoIQqgepXkpKRcuKKCu0LK%2B%2F5ezvzNnn7HPxm%2FMdz8daZ%2B%2B199prrf86%2B%2By9z%2Fl6VfB7SZpfoNKV7tXSqa9IVxcZzwWAkYsx8BBcOxHYrgPS2lFpap5z9MLdB6WeHmmVpD64djpewr3OmiQ9rpxfb1Yft%2B9GLAzAJ2fpdLP9P6nt915mwplZvoIALPvwCjfoNFfWWuyXTncO%2B4%2BRHnH1VBEE8Kp0FZoXwUZTmEftbJhcF0dZJ5l3viCdGHIcBMDDsySuTAYujct11Jm6cZ%2BT%2BzNWwxSAl6XJBDgPjtPlcaGm%2BkDCz%2BKEfFhMAQD5bHqmwhHRdkMk1FAhiSfhxuKgGCOm0SDtltyxBndNAUAxlW3aLmPwBW5M1wsStggnrKRcx6mP6iWwRykA9KYA0GbUsku3%2BQlpEgkbCfkBWHJaqTegeE6gzZqGOFbMsko3%2BQzpbuzPhFMEsLOTjSEAJySVnNxLBlaztc8wZlk7zrVPuFgn2VJ9LpvnhRh7Es4ieza8vhCA4HrrRs1ia99mTPR7eC5%2BhNetkebZrXc6hcVaaRpjlsEfMv6vf6VdLNU%2FsHx%2FxeDMPYcEpmILATgWI2XJNpvF3Np3ufWbCaaVB4QNcjrTcOX%2F0nbGjMLX4ygVFG1Z1JfsCAH4I6lUUp6B3osA2eimAuI4EfgCjgebyOL9tHrLNHIpAnAqthCAbaWsZSvNZCp8zvSwQMV06efOPE%2Fg7zDkFLhjwsbW5OAUAJQ2J5U6kPvJ1koCX7VX%2Bpjx98ETJhKTis0DYIc2HFe5645gb3DCkTsDJPc2V40KDwArgW0g06Pe5lXmcFe9jdYDQLwL4abTLfEAIwA8bNPoOB%2FOoMY0e4e8CAAPmx0hIrkx4aYDOS%2FeFAXMGj0l3tHgureHRAB4gP9scNDx0HbGhQgAe%2FRPdLDUcm0wsSxvj4cXAbhdsjvwrRr%2BI%2BAN8RCR46Le96QGCkyRD%2BJheQDY6VhNhU5cpVH1Lb9In8Qj8gAslTbR%2BSbcVFrBcd1LsAfAot4nLaf8G24UMTu%2B4E3njWRQKQD38LKBUiWnR%2BxURbtJ7JKbJbYr32QKgHUPS69T8k2V69Gng2R%2FaJn0XSiUIACnaC8kR31VIviHeTbtZciF5ReZALgLHI80n41jvT%2BkVmk5wT%2Bb5zETgA0yEGRgAfX34FoJv4%2Fh%2F6kip7kAbDBG7E4sxOBrJtfAe%2FG1lMw%2FXcZXIQAzYiAwOIThB5C9dRi5StrJlJ2LLz5slDNbCkDbFIbt64L9a7m73VZh%2BT1r5BV8V%2FR22iL7RwTAjAFiPYPsrWiLyRXxp5yGZ2ctlXk%2BiCWvO9x3l%2FQ1D4b9g2KfTMJK5VvX%2FCxde4f0W%2Fkh45odAbDhLel3HM%2Bl%2FhzcCfFJVIt4voY537DRdmJC6hiAuTPHw9KD1O0PiT2UY1R83dEjXcnYt4pV8zUmBKBt2gIhoGuQf4WLaCPpHuBZ%2BrJIsUx%2FJQDMEQFtYH21f1C%2BMTnEgHyb5WvQHRhDKkfcVhkA8zwibeXb%2FBzqn8FJGt0h3cp8s7mf7OtYrhSAReHera8j2x%2BZbEz9GaZZi2eGm2Qt1XHlACw0gv2HHfUm6vYC0mJ6PUq9K3QIAAD%2F%2F1wKF9MAAAAGSURBVAMAjeXXP35naSYAAAAASUVORK5CYII%3D%22%20x%3D%2216%22%20y%3D%226%22%20width%3D%2232%22%20height%3D%2232%22%20preserveAspectRatio%3D%22xMidYMid%20meet%22%2F%3E%0A%20%20%3Ctext%20x%3D%2232%22%20y%3D%2254%22%20fill%3D%22%2365D8EB%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%228.2%22%20font-weight%3D%22800%22%20letter-spacing%3D%22.1%22%20text-anchor%3D%22middle%22%20filter%3D%22url%28%23labelGlow%29%22%3ECALENDAR%3C%2Ftext%3E%0A%3C%2Fsvg%3E%0A";

// calendar.js
// Copyright 2026 TypicalBits.
function dateParts(input) {
  const match=/^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2}))?)?(Z|[+-]\d{2}:\d{2})?$/.exec(input);
  if(!match||(+match[1]<1000)||(!match[4]&&match[7]))throw Error('Use YYYY-MM-DD, or YYYY-MM-DDTHH:mm with a timezone.');
  const values=match.slice(1,7).map(x=>+(x||0));const [year,month,day,hour,minute,second]=values;
  const wall=Date.UTC(year,month-1,day,hour,minute,second);const parsed=new Date(wall);
  if(parsed.getUTCFullYear()!==year||parsed.getUTCMonth()!==month-1||parsed.getUTCDate()!==day||hour>23||minute>59||second>59)throw Error('The event date or time is invalid.');
  return {wall,values,allDay:!match[4],offset:match[7]||''};
}
function zonedParts(time,timezone) {
  const formatter=new Intl.DateTimeFormat('en-US',{timeZone:timezone,year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',hourCycle:'h23'});
  const fields=Object.fromEntries(formatter.formatToParts(time).map(x=>[x.type,x.value]));return ['year','month','day','hour','minute','second'].map(x=>+fields[x]);
}
function calendarInstant(input,timezone) {
  const data=dateParts(input);if(data.allDay)return {allDay:true,time:data.wall};
  if(data.offset){const offset=data.offset==='Z'?0:(()=>{const hours=+data.offset.slice(1,3),minutes=+data.offset.slice(4);if(hours>23||minutes>59)throw Error('Invalid UTC offset.');return (hours*60+minutes)*(data.offset[0]==='-'?-1:1);})();return {allDay:false,time:data.wall-offset*60000};}
  if(!timezone?.trim())throw Error('Supply a timezone or include an explicit UTC offset.');
  const offsets=new Set();try{
    for(let h=-36;h<=36;h+=6){const sample=data.wall+h*3600000;const p=zonedParts(sample,timezone);offsets.add(Date.UTC(p[0],p[1]-1,p[2],p[3],p[4],p[5])-sample);}
  }catch{throw Error('Use a valid timezone such as America/Los_Angeles, or an explicit UTC offset.');}
  const candidates=[...offsets].map(offset=>data.wall-offset).filter(time=>zonedParts(time,timezone).every((v,i)=>v===data.values[i]));
  if(candidates.length!==1)throw Error('This local time is missing or ambiguous at a clock change. Include an explicit UTC offset.');
  return {allDay:false,time:candidates[0]};
}
function calendarText(input) {return String(input).replaceAll('\\','\\\\').replace(/\r\n|\r|\n/g,'\\n').replaceAll(';','\\;').replaceAll(',','\\,').replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/g,'');}
function foldCalendar(line) {
  const encoder=new TextEncoder();let output='',part='',size=0;
  for(const char of line){const n=encoder.encode(char).length;if(size+n>75){output+=part+'\r\n';part=' ';size=1;}part+=char;size+=n;}return output+part;
}
function calendarTime(time,allDay=false){const iso=new Date(time).toISOString();return allDay?iso.slice(0,10).replaceAll('-',''):iso.replace(/[-:]/g,'').slice(0,15)+'Z';}
function eventUID(url){let a=2166136261,b=5381;for(const char of url){a=Math.imul(a^char.charCodeAt(0),16777619);b=Math.imul(b,33)^char.charCodeAt(0);}return (a>>>0).toString(16)+(b>>>0).toString(16)+'@fl-tools-lite';}
function buildCalendar(fields,now=new Date()) {
  const title=String(fields.title||'').trim();if(!title)throw Error('Enter the event title.');if(title.length>1000||String(fields.location||'').length>2000)throw Error('The title or location is too long.');
  const url=pageKey(fields.url),start=calendarInstant(String(fields.start||'').trim(),fields.timezone),end=fields.end?calendarInstant(String(fields.end).trim(),fields.timezone):null;
  if(end&&(end.allDay!==start.allDay||end.time<=start.time))throw Error('End must be after start and use the same date format. For all-day events, end is the first day after the event.');
  if(!Number.isFinite(now.getTime()))throw Error('Invalid export date.');
  const lines=['BEGIN:VCALENDAR','VERSION:2.0','PRODID:-//TypicalBits//FL Tools Calendar//EN','CALSCALE:GREGORIAN','BEGIN:VEVENT','UID:'+eventUID(url),'DTSTAMP:'+calendarTime(now.getTime()),`DTSTART${start.allDay?';VALUE=DATE':''}:`+calendarTime(start.time,start.allDay)];
  if(end)lines.push(`DTEND${end.allDay?';VALUE=DATE':''}:`+calendarTime(end.time,end.allDay));
  lines.push('SUMMARY:'+calendarText(title));if(fields.location)lines.push('LOCATION:'+calendarText(fields.location));lines.push('URL:'+url,'END:VEVENT','END:VCALENDAR');
  return lines.map(foldCalendar).join('\r\n')+'\r\n';
}
function readEvent(document,input) {
  const url=pageKey(input),path=new URL(url).pathname;
  if(!/^\/events\/(?:\d+|\d{4}\/\d{2}\/\d{2}\/[^/]+)\/?$/.test(path))return {available:false,title:'',start:'',end:'',timezone:'',location:'',url};
  const nodes=[];function collect(node){if(Array.isArray(node)){node.forEach(collect);return;}if(!node||typeof node!=='object')return;if([node['@type']].flat().includes('Event'))nodes.push(node);if(node['@graph'])collect(node['@graph']);}
  for(const script of document.querySelectorAll('script[type="application/ld+json"]')){try{if(script.textContent.length<200000)collect(JSON.parse(script.textContent));}catch{}}
  const matching=nodes.filter(node=>{try{return node.url&&pageKey(node.url)===url;}catch{return false;}});const event=matching.length===1?matching[0]:nodes.length===1&&!nodes[0].url?nodes[0]:null;
  const main=document.querySelector('main');const property=name=>{const matches=main?.querySelectorAll(`[itemprop="${name}"]`);return matches?.length===1?(matches[0].getAttribute('content')||matches[0].getAttribute('datetime')||matches[0].textContent.trim()):'';};
  const location=event?.location;const address=location?.address;const textLocation=typeof location==='string'?location:[location?.name,typeof address==='string'?address:address?.addressLocality].filter(Boolean).join(', ');
  return {available:true,title:String(event?.name||main?.querySelector('h1')?.textContent?.trim()||''),start:String(event?.startDate||property('startDate')||''),end:String(event?.endDate||property('endDate')||''),timezone:'',location:String(textLocation||property('location')||''),url};
}

// main.js
// Copyright 2026 TypicalBits.
function downloadCalendar(window,document,text) {
  const blob=new window.Blob([text],{type:'text/calendar;charset=utf-8'});const url=window.URL.createObjectURL(blob);const link=document.createElement('a');link.href=url;link.download='fetlife-event.ics';link.hidden=true;document.body.append(link);link.click();link.remove();window.setTimeout(()=>window.URL.revokeObjectURL(url),1000);
}
function startCalendar({document,window,storage,download=text=>downloadCalendar(window,document,text)}) {
  return bootstrapLite({document,window,storage,product:{id:'calendar',name:'Calendar',version:'0.1.0',icon:PRODUCT_ICON,accent:'#65D8EB'},setup:async({ui})=>{
    let current='',dirty=false,available=false,message='';const fields={};
    ui.body.append(ui.e('p',{class:'note'},'Preview the current event before exporting. All-day end is the first day after the event.'));
    for(const [key,label]of [['title','Title'],['start','Start'],['end','End (optional)'],['timezone','Timezone (for times without an offset)'],['location','Location'],['url','Event link']]){fields[key]=liteField(ui,key,label);fields[key].addEventListener('input',()=>{dirty=true;message='';validate();});}
    fields.start.placeholder='2026-10-03T18:00:00-07:00';fields.end.placeholder='Optional';fields.timezone.placeholder='America/Los_Angeles';fields.url.readOnly=true;
    const actions=ui.e('div',{class:'actions'});const exportButton=liteButton(ui,'Export .ics',{class:'primary','data-export':''},()=>{try{if(!available)throw Error('Open an individual event first.');download(buildCalendar(values()));message='Calendar file exported.';}catch(error){message=error.message;}validate();});
    const reload=liteButton(ui,'Read page again',{},()=>{if(dirty){confirmation.hidden=false;return;}load();});actions.append(exportButton,reload);ui.body.append(actions);
    const confirmation=ui.e('div',{class:'confirmation'});confirmation.hidden=true;confirmation.append(ui.e('p',{},'Replace your edited preview with this page’s event?'),liteButton(ui,'Replace preview',{},()=>{confirmation.hidden=true;dirty=false;load();}),liteButton(ui,'Keep edits',{},()=>confirmation.hidden=true));ui.body.append(confirmation);
    function values(){return Object.fromEntries(Object.entries(fields).map(([key,node])=>[key,node.value]));}
    function validate(){let error='';try{if(!available)throw Error('Open an individual event to export its calendar file.');buildCalendar(values());}catch(problem){error=problem.message;}exportButton.disabled=!!error;ui.setStatus(message||error||'Ready to export. Review the event details first.');}
    function load(){const data=readEvent(document,window.location.href);current=window.location.href;available=data.available;for(const [key,node]of Object.entries(fields))node.value=data[key]||'';message='';dirty=false;validate();}
    load();return {refresh(){if(window.location.href!==current){if(dirty){message='Page changed. Your edited preview is retained. Use Read page again to replace it.';available=false;validate();}else load();}else if(!dirty)load();}};
  }});
}

void startCalendar({document,window,storage:GM}).catch(()=>{});
})();