/* ================================================================
PURPLE CITY MUSIC FESTIVAL — SHARED DATA FILE
================================================================
This file is the single source of truth for BOTH the main
schedule page and the "My Schedule" personal planner page.
Edit venue info, the lineup/set times, or genre notes here
ONCE and both pages update automatically the next time they
load — no need to touch the two HTML files themselves.
HOW THE TWO PAGES USE THIS FILE
Each HTML page loads this file with a plain
<script src="purple-city-data.js"></script>
tag placed BEFORE that page's own <script> block. Because
this is a normal (non-module) script, everything defined
below — VENUES, DATA, GENRE_NOTES, ARTIST_LINKS, the walking-
time helpers, etc. — becomes available as a global variable
that the page's own script can just use directly.
MOVING THIS TO A DIFFERENT HOST (e.g. Concrete → Squarespace)
All that matters is that this file is reachable at a URL and
that both pages' <script src="..."> tags point to that URL.
- On Concrete (or any host where you can upload files into a
folder you control), keep this file in the same folder as
the two HTML embeds and use a relative path, e.g.:
<script src="purple-city-data.js"></script>
or a site-relative path if your embed sits in a subfolder,
e.g.: <script src="/schedule/purple-city-data.js"></script>
- Squarespace does NOT give you a browsable folder the way
Concrete does. Your two options there:
1) Upload this .js file through Squarespace's own file/
asset manager (Settings/Assets, or drag it into a Code
Block's file upload where offered) — it will give you
back a long CDN URL (something like
https://static1.squarespace.com/.../purple-city-data.js).
Paste that FULL URL into the <script src="..."> tag on
both pages instead of the relative path.
2) Host it anywhere else reachable over HTTPS (your own
domain, a GitHub raw link, etc.) and again just use
that full URL in the <script src="..."> tag.
Either way, you only ever have ONE line to update per page
when the file's location changes — the src attribute.
================================================================ */
/* ============================================================
VENUE DIRECTORY — single source of truth
Rename a venue, fix an address, or update a description here
and it updates everywhere the venue appears in the schedule.
Keys (e.g. "starlite") are internal IDs — do not need to match
the display name. If a venue's real name changes, only edit
the "name" field below; nothing else in this file needs to change.
============================================================ */
const VENUES = {
block_party: {
name: "The Block Party",
color: "#ffe066",
address: "10025 102 St NW, Edmonton, AB",
description: "Outdoor stage on 102 Street, closed to traffic for the day. Free and all-ages."
},
starlite: {
name: "The Starlite Room",
color: "#ff3fa4",
address: "10030 102 St NW, Edmonton, AB T5J 0V6",
description: "Historic 1925 brick building, one block south of Jasper Ave. Standing room with a raised area behind the stage."
},
temple: {
name: "Temple Ballroom",
color: "#4fe0e8",
address: "10030 102 St NW, Edmonton, AB T5J 0V6",
description: "Upstairs stage in the same building as The Starlite Room."
},
allsaints: {
name: "All Saints Church",
color: "#ffb84d",
address: "10035 103 St NW, Edmonton, AB T5J 0X5",
description: "All Saints' Anglican Cathedral, half a block south of Jasper Ave."
},
freemasons_main: {
name: "Freemasons' Hall Main Floor",
color: "#b983ff",
address: "10318 100 Ave NW, Edmonton, AB T5J 0A2",
description: "Main auditorium of the historic 1932 Masonic Temple."
},
freemasons_down: {
name: "Freemasons' Hall Basement",
color: "#7cff8a",
address: "10318 100 Ave NW, Edmonton, AB T5J 0A2",
description: "Basement lounge/banquet room below the main Freemasons' Hall auditorium."
},
y_main: {
name: "The Y Main Floor",
color: "#ff7a59",
address: "10211 102 Ave NW, Edmonton, AB T5J 0A5",
description: "Don Wheaton Family YMCA, main-floor stage."
},
y_down: {
name: "The Y Downstairs",
color: "#5ac8ff",
address: "10211 102 Ave NW, Edmonton, AB T5J 0A5",
description: "Don Wheaton Family YMCA, downstairs stage."
},
conference: {
name: "Conference",
color: "#c792ea",
address: "10111 Bellamy Hill Rd NW, Edmonton, AB",
description: "Talk City Music Conference — industry panels and discussions."
},
};
/* ============================================================
ARTIST LINKS — where each artist name links to.
Key must match the artist name exactly as it appears in DATA
below. If an artist isn't listed here, clicking their block
falls back to the general lineup page so nothing is ever a
dead link. Fill in real per-artist URLs as you get them
(e.g. once you know the anchor/slug pattern used on the
lineup page, or a direct URL to that artist's profile).
============================================================ */
const LINEUP_PAGE = "https://www.purplecityfest.com/lineup";
/* Auto-generated from each artist name using the site's anchor
pattern: lowercase, words hyphenated, ending in "-2".
e.g. "Big Thief" -> https://www.purplecityfest.com/lineup#big-thief-2
Edit any individual line below if the real anchor differs
(this is plain JS, so just overwrite the string on the right). */
const ARTIST_LINKS = {
"??": LINEUP_PAGE + "#-2",
"ANCIENT GREECE": LINEUP_PAGE + "#ancient-greece-2",
"ANI": LINEUP_PAGE + "#ani-2",
"ASKO": LINEUP_PAGE + "#asko-2",
"Alix Fernz": LINEUP_PAGE + "#alix-fernz-2",
"Andy and The Dannys": LINEUP_PAGE + "#andy-and-the-dannys-2",
"BLXPUNKS": LINEUP_PAGE + "#blxpunks-2",
"Bad Buddy": LINEUP_PAGE + "#bad-buddy-2",
"Bang": LINEUP_PAGE + "#bang-2",
"Begrime Exemius": LINEUP_PAGE + "#begrime-exemius-2",
"Bestial Mouths": LINEUP_PAGE + "#bestial-mouths-2",
"Big Evil Rat": LINEUP_PAGE + "#big-evil-rat-2",
"Bridgeland": LINEUP_PAGE + "#bridgeland-2",
"CALVIN LOVE": LINEUP_PAGE + "#calvin-love-2",
"CHAIRMAN": LINEUP_PAGE + "#chairman-2",
"Cherry Pick": LINEUP_PAGE + "#cherry-pick-2",
"Chinese Medicine": LINEUP_PAGE + "#chinese-medicine-2",
"Criminal Piss": LINEUP_PAGE + "#criminal-piss-2",
"Crune": LINEUP_PAGE + "#crune-2",
"DARK CHISME": LINEUP_PAGE + "#dark-chisme-2",
"DEEP DARK WOODS": LINEUP_PAGE + "#deep-dark-woods-2",
"DEER LADY": LINEUP_PAGE + "#deer-lady-2",
"DESIRE": LINEUP_PAGE + "#desire-2",
"DOUR": LINEUP_PAGE + "#dour-2",
"DansePlus": LINEUP_PAGE + "#danseplus-2",
"Death Cassette": LINEUP_PAGE + "#death-cassette-2",
"DoFlame": LINEUP_PAGE + "#doflame-2",
"Dominator": LINEUP_PAGE + "#dominator-2",
"Doom Ritual Bastard": LINEUP_PAGE + "#doom-ritual-bastard-2",
"ET": LINEUP_PAGE + "#et-2",
"FREE SHOW": LINEUP_PAGE + "#free-show-2",
"Fille Fille": LINEUP_PAGE + "#fille-fille-2",
"Fishlips": LINEUP_PAGE + "#fishlips-2",
"Gadfly": LINEUP_PAGE + "#gadfly-2",
"Ghost Fetish": LINEUP_PAGE + "#ghost-fetish-2",
"Ghost Twin": LINEUP_PAGE + "#ghost-twin-2",
"Girlfriends & Boyfriends": LINEUP_PAGE + "#girlfriends-boyfriends-2",
"Gratutius": LINEUP_PAGE + "#gratutius-2",
"Grond": LINEUP_PAGE + "#grond-2",
"Gucci Chain Letter": LINEUP_PAGE + "#gucci-chain-letter-2",
"HOT GARBAGE": LINEUP_PAGE + "#hot-garbage-2",
"HYFY": LINEUP_PAGE + "#hyfy-2",
"Harlotte? abrupt decay?": LINEUP_PAGE + "#harlotte-abrupt-decay-2",
"Hillsboro": LINEUP_PAGE + "#hillsboro-2",
"Hip Hop 1": LINEUP_PAGE + "#hip-hop-1-2",
"Hip Hop 2": LINEUP_PAGE + "#hip-hop-2-2",
"Hip Hop 3": LINEUP_PAGE + "#hip-hop-3-2",
"His HIs": LINEUP_PAGE + "#his-his-2",
"Hype Phyllis": LINEUP_PAGE + "#hype-phyllis-2",
"IGUANA DEATH CULT": LINEUP_PAGE + "#iguana-death-cult-2",
"INITIATE": LINEUP_PAGE + "#initiate-2",
"Jet Cemetary": LINEUP_PAGE + "#jet-cemetary-2",
"Jody Glenham": LINEUP_PAGE + "#jody-glenham-2",
"Kali Horse": LINEUP_PAGE + "#kali-horse-2",
"LAND LADY": LINEUP_PAGE + "#land-lady-2",
"Lee & Gamble": LINEUP_PAGE + "#lee-gamble-2",
"Lovelet": LINEUP_PAGE + "#lovelet-2",
"Lung": LINEUP_PAGE + "#lung-2",
"Lushclot": LINEUP_PAGE + "#lushclot-2",
"MIXER (LOUNGE)": LINEUP_PAGE + "#mixer-lounge-2",
"MOAA": LINEUP_PAGE + "#moaa-2",
"Miesha and the Spanks": LINEUP_PAGE + "#miesha-and-the-spanks-2",
"Minot": LINEUP_PAGE + "#minot-2",
"Molten Lava": LINEUP_PAGE + "#molten-lava-2",
"Morning Coyote": LINEUP_PAGE + "#morning-coyote-2",
"Mox Jet": LINEUP_PAGE + "#mox-jet-2",
"Noise 1": LINEUP_PAGE + "#noise-1-2",
"Noise 2": LINEUP_PAGE + "#noise-2-2",
"Numa Echos": LINEUP_PAGE + "#numa-echos-2",
"OUT OF ORDER": LINEUP_PAGE + "#out-of-order-2",
"Panic Pixie": LINEUP_PAGE + "#panic-pixie-2",
"Parasian Orgy": LINEUP_PAGE + "#parasian-orgy-2",
"Paul & The Feels": LINEUP_PAGE + "#paul-the-feels-2",
"Peace Within": LINEUP_PAGE + "#peace-within-2",
"Penny and The Pits": LINEUP_PAGE + "#penny-and-the-pits-2",
"Pre Breaker": LINEUP_PAGE + "#pre-breaker-2",
"RARE DM": LINEUP_PAGE + "#rare-dm-2",
"RIG TIME!": LINEUP_PAGE + "#rig-time-2",
"Radiation Flowers": LINEUP_PAGE + "#radiation-flowers-2",
"Rebel Girlzzz": LINEUP_PAGE + "#rebel-girlzzz-2",
"Revolution Above Disorder": LINEUP_PAGE + "#revolution-above-disorder-2",
"Ring Finger": LINEUP_PAGE + "#ring-finger-2",
"SAMWOY": LINEUP_PAGE + "#samwoy-2",
"SESSION 1": LINEUP_PAGE + "#session-1-2",
"SESSION 2": LINEUP_PAGE + "#session-2-2",
"SESSION 3": LINEUP_PAGE + "#session-3-2",
"SHANNON SHAW": LINEUP_PAGE + "#shannon-shaw-2",
"SISTER SUZIE": LINEUP_PAGE + "#sister-suzie-2",
"STATIC GHOST": LINEUP_PAGE + "#static-ghost-2",
"Sloosh": LINEUP_PAGE + "#sloosh-2",
"Sook Yin Lee": LINEUP_PAGE + "#sook-yin-lee-2",
"Still Depths": LINEUP_PAGE + "#still-depths-2",
"Still Waves": LINEUP_PAGE + "#still-waves-2",
"Sundress": LINEUP_PAGE + "#sundress-2",
"Sunglaciers": LINEUP_PAGE + "#sunglaciers-2",
"TBD": LINEUP_PAGE + "#tbd-2",
"TEKERRA": LINEUP_PAGE + "#tekerra-2",
"THROAT PISS": LINEUP_PAGE + "#throat-piss-2",
"TRUSTER": LINEUP_PAGE + "#truster-2",
"Taxi Girls": LINEUP_PAGE + "#taxi-girls-2",
"The Blue": LINEUP_PAGE + "#the-blue-2",
"The Carpoolers": LINEUP_PAGE + "#the-carpoolers-2",
"The Wet Secrets": LINEUP_PAGE + "#the-wet-secrets-2",
"Theo Vandenhoff": LINEUP_PAGE + "#theo-vandenhoff-2",
"Thespian": LINEUP_PAGE + "#thespian-2",
"Tiffadelic": LINEUP_PAGE + "#tiffadelic-2",
"Total Chroma": LINEUP_PAGE + "#total-chroma-2",
"Uncanny Valley": LINEUP_PAGE + "#uncanny-valley-2",
"VAIN": LINEUP_PAGE + "#vain-2",
"VAIN?? **so far scheduled on friday": LINEUP_PAGE + "#vain-so-far-scheduled-on-friday-2",
"Verttigo": LINEUP_PAGE + "#verttigo-2",
"Visitor": LINEUP_PAGE + "#visitor-2",
"WANTS": LINEUP_PAGE + "#wants-2",
"WIMC CONVERSATION CIRCLE": LINEUP_PAGE + "#wimc-conversation-circle-2",
"Weak Material": LINEUP_PAGE + "#weak-material-2",
"You, Me and Zach": LINEUP_PAGE + "#you-me-and-zach-2",
};
function artistLink(name){
return ARTIST_LINKS[name] || LINEUP_PAGE;
}
/* ============================================================
GENRE NOTES — one short description per venue, per day.
Keyed by day label (must match a "label" in DATA.days below),
then by venue id (must match a key in VENUES above).
Leave a string empty ("") to show no note for that venue/day.
This is the single block to edit for all genre descriptions.
============================================================ */
const GENRE_NOTES = {
"Fri, Sep 11": {
starlite: "",
temple: "",
allsaints: "",
freemasons_main: "",
freemasons_down: "",
y_main: "",
y_down: "",
},
"Sat, Sep 12": {
block_party: "",
starlite: "",
temple: "",
allsaints: "",
freemasons_main: "",
freemasons_down: "",
y_main: "",
y_down: "",
},
"Sun, Sep 13": {
block_party: "",
starlite: "",
temple: "",
freemasons_main: "",
freemasons_down: "",
y_main: "",
y_down: "",
conference: "",
},
};
function genreNote(dayLabel, venueId){
return (GENRE_NOTES[dayLabel] && GENRE_NOTES[dayLabel][venueId]) || "";
}
const DATA = {
"venueOrder": [
"block_party",
"starlite",
"temple",
"allsaints",
"freemasons_main",
"freemasons_down",
"y_main",
"y_down",
"conference"
],
"days": [
{
"label": "Fri, Sep 11",
"dayStart": 18.0,
"dayEnd": 26,
"stages": [
{
"id": "starlite",
"events": [
{
"name": "TBD",
"start": 20.5,
"end": 21.0,
"flagged": true,
"note": ""
},
{
"name": "VAIN",
"start": 21.5,
"end": 22.0,
"flagged": false,
"note": ""
},
{
"name": "Bang",
"start": 22.33,
"end": 22.83,
"flagged": false,
"note": ""
},
{
"name": "Lushclot",
"start": 23.17,
"end": 23.83,
"flagged": false,
"note": ""
},
{
"name": "INITIATE",
"start": 24.17,
"end": 25.17,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 7:30 PM"
},
{
"id": "temple",
"events": [
{
"name": "Lung",
"start": 20.0,
"end": 20.5,
"flagged": false,
"note": ""
},
{
"name": "Molten Lava",
"start": 20.83,
"end": 21.33,
"flagged": false,
"note": ""
},
{
"name": "TRUSTER",
"start": 21.67,
"end": 22.17,
"flagged": false,
"note": ""
},
{
"name": "TEKERRA",
"start": 22.5,
"end": 23.5,
"flagged": false,
"note": ""
},
{
"name": "THROAT PISS",
"start": 23.83,
"end": 24.67,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 7:00 PM"
},
{
"id": "allsaints",
"events": [
{
"name": "Morning Coyote",
"start": 19.0,
"end": 19.5,
"flagged": false,
"note": ""
},
{
"name": "Paul & The Feels",
"start": 19.83,
"end": 20.33,
"flagged": false,
"note": ""
},
{
"name": "LAND LADY",
"start": 20.67,
"end": 21.33,
"flagged": false,
"note": ""
},
{
"name": "DEEP DARK WOODS",
"start": 21.67,
"end": 22.67,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 6:30 PM"
},
{
"id": "freemasons_main",
"events": [
{
"name": "Criminal Piss",
"start": 20.5,
"end": 21.0,
"flagged": false,
"note": ""
},
{
"name": "SISTER SUZIE",
"start": 21.5,
"end": 21.83,
"flagged": false,
"note": ""
},
{
"name": "OUT OF ORDER",
"start": 22.17,
"end": 22.67,
"flagged": false,
"note": ""
},
{
"name": "CHAIRMAN",
"start": 23.0,
"end": 23.67,
"flagged": false,
"note": ""
},
{
"name": "IGUANA DEATH CULT",
"start": 24.0,
"end": 25.0,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 8:00 PM"
},
{
"id": "freemasons_down",
"events": [
{
"name": "Panic Pixie",
"start": 19.5,
"end": 20.0,
"flagged": false,
"note": ""
},
{
"name": "Peace Within",
"start": 20.33,
"end": 20.83,
"flagged": false,
"note": ""
},
{
"name": "Andy and The Dannys",
"start": 21.17,
"end": 21.67,
"flagged": false,
"note": ""
},
{
"name": "ANCIENT GREECE",
"start": 22.0,
"end": 22.5,
"flagged": false,
"note": ""
},
{
"name": "HOT GARBAGE",
"start": 22.83,
"end": 23.83,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 7:00 PM"
},
{
"id": "y_main",
"events": [
{
"name": "Hype Phyllis",
"start": 18.5,
"end": 19.0,
"flagged": false,
"note": ""
},
{
"name": "Mox Jet",
"start": 19.5,
"end": 20.0,
"flagged": false,
"note": ""
},
{
"name": "Ghost Fetish",
"start": 20.5,
"end": 21.0,
"flagged": false,
"note": ""
},
{
"name": "MOAA",
"start": 21.5,
"end": 22.17,
"flagged": false,
"note": ""
},
{
"name": "RARE DM",
"start": 22.67,
"end": 23.67,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 6:00 PM"
},
{
"id": "y_down",
"events": [
{
"name": "DansePlus",
"start": 19.0,
"end": 19.5,
"flagged": false,
"note": ""
},
{
"name": "Still Waves",
"start": 20.0,
"end": 20.5,
"flagged": false,
"note": ""
},
{
"name": "Girlfriends & Boyfriends",
"start": 21.0,
"end": 21.5,
"flagged": false,
"note": ""
},
{
"name": "Bridgeland",
"start": 22.17,
"end": 22.67,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 6:00 PM"
}
]
},
{
"label": "Sat, Sep 12",
"dayStart": 12.0,
"dayEnd": 26,
"stages": [
{
"id": "block_party",
"events": [
{
"name": "You, Me and Zach",
"start": 14.0,
"end": 14.5,
"flagged": false,
"note": ""
},
{
"name": "Lovelet",
"start": 14.0,
"end": 14.5,
"flagged": false,
"note": ""
},
{
"name": "Parasian Orgy",
"start": 14.83,
"end": 15.33,
"flagged": false,
"note": ""
},
{
"name": "Jody Glenham",
"start": 15.67,
"end": 16.33,
"flagged": false,
"note": ""
},
{
"name": "Kali Horse",
"start": 16.67,
"end": 17.33,
"flagged": false,
"note": ""
},
{
"name": "CALVIN LOVE",
"start": 17.67,
"end": 18.42,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 12:00 PM"
},
{
"id": "starlite",
"events": [
{
"name": "Penny and The Pits",
"start": 21.5,
"end": 22.0,
"flagged": false,
"note": ""
},
{
"name": "Radiation Flowers",
"start": 22.33,
"end": 22.83,
"flagged": false,
"note": ""
},
{
"name": "Bad Buddy",
"start": 23.17,
"end": 23.83,
"flagged": false,
"note": ""
},
{
"name": "SHANNON SHAW",
"start": 24.17,
"end": 25.67,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 8:30 PM"
},
{
"id": "temple",
"events": [
{
"name": "VAIN?? **so far scheduled on friday",
"start": 12.5,
"end": 13.0,
"flagged": true,
"note": ""
},
{
"name": "Harlotte? abrupt decay?",
"start": 13.33,
"end": 14.0,
"flagged": true,
"note": ""
},
{
"name": "BLXPUNKS",
"start": 14.33,
"end": 15.0,
"flagged": false,
"note": ""
},
{
"name": "DoFlame",
"start": 15.33,
"end": 16.0,
"flagged": false,
"note": "$25 ADV / $30 DOOR / Hardcore - Punk - Metal / CO-PRESENTER: Clean Up Your Act Productions"
},
{
"name": "Fille Fille",
"start": 19.67,
"end": 20.0,
"flagged": false,
"note": ""
},
{
"name": "Thespian",
"start": 20.33,
"end": 20.83,
"flagged": false,
"note": ""
},
{
"name": "SAMWOY",
"start": 21.17,
"end": 22.33,
"flagged": false,
"note": ""
},
{
"name": "Sunglaciers",
"start": 22.67,
"end": 23.67,
"flagged": false,
"note": ""
},
{
"name": "Alix Fernz",
"start": 24.0,
"end": 25.0,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 12:00 PM, 7:00 PM"
},
{
"id": "allsaints",
"events": [
{
"name": "TBD",
"start": 18.0,
"end": 18.5,
"flagged": true,
"note": ""
},
{
"name": "TBD",
"start": 18.83,
"end": 19.33,
"flagged": true,
"note": "**RYAN's SCHEDULE SAYS 6:45"
},
{
"name": "ASKO",
"start": 19.67,
"end": 20.17,
"flagged": false,
"note": "**RYAN's SCHEDULE SAYS 7:30"
},
{
"name": "DEER LADY",
"start": 20.5,
"end": 21.5,
"flagged": false,
"note": "**RYAN's SCHEDULE SAYS 8:50-9:50"
}
],
"doorsNote": "Doors 5:00 PM"
},
{
"id": "freemasons_main",
"events": [
{
"name": "The Carpoolers",
"start": 21.5,
"end": 22.0,
"flagged": false,
"note": ""
},
{
"name": "DOUR",
"start": 22.5,
"end": 23.0,
"flagged": false,
"note": ""
},
{
"name": "Big Evil Rat",
"start": 23.67,
"end": 24.17,
"flagged": false,
"note": ""
},
{
"name": "Revolution Above Disorder",
"start": 24.5,
"end": 25.5,
"flagged": true,
"note": "BAMBARA?"
}
],
"doorsNote": "Doors 6:30 PM"
},
{
"id": "freemasons_down",
"events": [
{
"name": "WIMC CONVERSATION CIRCLE",
"start": 17.0,
"end": 18.0,
"flagged": false,
"note": "SESSION 4 / WIMC CONVERSATION CIRCLE"
},
{
"name": "MIXER (LOUNGE)",
"start": 18.0,
"end": 19.0,
"flagged": false,
"note": ""
},
{
"name": "Fishlips",
"start": 19.5,
"end": 20.0,
"flagged": false,
"note": ""
},
{
"name": "Still Depths",
"start": 20.5,
"end": 21.0,
"flagged": false,
"note": ""
},
{
"name": "Sundress",
"start": 21.5,
"end": 22.0,
"flagged": false,
"note": ""
},
{
"name": "Gadfly",
"start": 22.5,
"end": 23.0,
"flagged": false,
"note": ""
},
{
"name": "Cherry Pick",
"start": 23.5,
"end": 24.33,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 6:30 PM"
},
{
"id": "y_main",
"events": [
{
"name": "Hip Hop 1",
"start": 12.83,
"end": 13.17,
"flagged": false,
"note": ""
},
{
"name": "Hip Hop 2",
"start": 13.5,
"end": 13.83,
"flagged": false,
"note": ""
},
{
"name": "Hip Hop 3",
"start": 14.17,
"end": 14.67,
"flagged": false,
"note": ""
},
{
"name": "The Blue",
"start": 15.33,
"end": 16.33,
"flagged": false,
"note": "FREE SHOW / Hip-Hop"
},
{
"name": "Jet Cemetary",
"start": 18.5,
"end": 19.0,
"flagged": false,
"note": ""
},
{
"name": "Ring Finger",
"start": 19.5,
"end": 20.0,
"flagged": false,
"note": ""
},
{
"name": "Crune",
"start": 20.5,
"end": 21.0,
"flagged": false,
"note": ""
},
{
"name": "ANI",
"start": 21.5,
"end": 22.0,
"flagged": false,
"note": ""
},
{
"name": "DARK CHISME",
"start": 22.5,
"end": 24.0,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 12:00 PM, 6:00 PM"
},
{
"id": "y_down",
"events": [
{
"name": "Noise 1",
"start": 12.5,
"end": 12.83,
"flagged": false,
"note": ""
},
{
"name": "Noise 2",
"start": 13.17,
"end": 13.5,
"flagged": false,
"note": ""
},
{
"name": "Lee & Gamble",
"start": 13.83,
"end": 14.17,
"flagged": false,
"note": ""
},
{
"name": "FREE SHOW",
"start": 14.67,
"end": 15.33,
"flagged": false,
"note": "Experimental"
},
{
"name": "Sloosh",
"start": 19.0,
"end": 19.5,
"flagged": false,
"note": ""
},
{
"name": "ET",
"start": 20.0,
"end": 20.5,
"flagged": false,
"note": ""
},
{
"name": "Tiffadelic",
"start": 21.0,
"end": 21.5,
"flagged": false,
"note": ""
},
{
"name": "Theo Vandenhoff",
"start": 22.0,
"end": 22.5,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 12:00 PM, 6:00 PM"
}
]
},
{
"label": "Sun, Sep 13",
"dayStart": 12.0,
"dayEnd": 25,
"stages": [
{
"id": "block_party",
"events": [
{
"name": "Gratutius",
"start": 13.0,
"end": 13.5,
"flagged": false,
"note": ""
},
{
"name": "Weak Material",
"start": 14.0,
"end": 14.5,
"flagged": false,
"note": ""
},
{
"name": "His HIs",
"start": 14.83,
"end": 15.33,
"flagged": false,
"note": ""
},
{
"name": "Death Cassette",
"start": 15.67,
"end": 16.33,
"flagged": false,
"note": ""
},
{
"name": "Taxi Girls",
"start": 16.67,
"end": 17.33,
"flagged": false,
"note": ""
},
{
"name": "The Wet Secrets",
"start": 17.67,
"end": 18.33,
"flagged": false,
"note": ""
},
{
"name": "??",
"start": 18.67,
"end": 19.67,
"flagged": true,
"note": ""
}
],
"doorsNote": "Doors 12:00 PM"
},
{
"id": "starlite",
"events": [
{
"name": "Uncanny Valley",
"start": 20.0,
"end": 20.5,
"flagged": false,
"note": ""
},
{
"name": "Verttigo",
"start": 20.83,
"end": 21.33,
"flagged": false,
"note": ""
},
{
"name": "Sook Yin Lee",
"start": 21.67,
"end": 22.33,
"flagged": false,
"note": ""
},
{
"name": "DESIRE",
"start": 22.67,
"end": 23.67,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 7:00 PM"
},
{
"id": "temple",
"events": [
{
"name": "Minot",
"start": 20.5,
"end": 21.0,
"flagged": false,
"note": ""
},
{
"name": "HYFY",
"start": 21.33,
"end": 22.0,
"flagged": false,
"note": ""
},
{
"name": "Miesha and the Spanks",
"start": 22.33,
"end": 23.33,
"flagged": true,
"note": "The Garrys??"
}
],
"doorsNote": "Doors 7:30 PM"
},
{
"id": "freemasons_main",
"events": [
{
"name": "Rebel Girlzzz",
"start": 20.0,
"end": 20.5,
"flagged": false,
"note": ""
},
{
"name": "Hillsboro",
"start": 21.0,
"end": 21.5,
"flagged": false,
"note": ""
},
{
"name": "Gucci Chain Letter",
"start": 22.0,
"end": 22.5,
"flagged": false,
"note": ""
},
{
"name": "Chinese Medicine",
"start": 23.0,
"end": 24.5,
"flagged": true,
"note": "WAR ON WOMEN???"
}
],
"doorsNote": "Doors 6:30 PM"
},
{
"id": "freemasons_down",
"events": [
{
"name": "Grond",
"start": 19.5,
"end": 20.0,
"flagged": false,
"note": ""
},
{
"name": "Pre Breaker",
"start": 20.5,
"end": 21.0,
"flagged": false,
"note": ""
},
{
"name": "RIG TIME!",
"start": 21.5,
"end": 22.0,
"flagged": false,
"note": ""
},
{
"name": "Begrime Exemius",
"start": 22.67,
"end": 23.17,
"flagged": true,
"note": "Feartililizer 1492??"
}
],
"doorsNote": "Doors 6:30 PM"
},
{
"id": "y_main",
"events": [
{
"name": "WANTS",
"start": 18.5,
"end": 19.0,
"flagged": false,
"note": ""
},
{
"name": "Ghost Twin",
"start": 19.0,
"end": 19.5,
"flagged": false,
"note": ""
},
{
"name": "Total Chroma",
"start": 20.0,
"end": 20.5,
"flagged": false,
"note": ""
},
{
"name": "Visitor",
"start": 21.0,
"end": 21.5,
"flagged": false,
"note": ""
},
{
"name": "Bestial Mouths",
"start": 22.0,
"end": 23.0,
"flagged": false,
"note": ""
}
],
"doorsNote": "Doors 6:00 PM"
},
{
"id": "y_down",
"events": [
{
"name": "Doom Ritual Bastard",
"start": 19.0,
"end": 19.5,
"flagged": false,
"note": ""
},
{
"name": "Numa Echos",
"start": 19.5,
"end": 20.0,
"flagged": false,
"note": ""
},
{
"name": "Dominator",
"start": 20.5,
"end": 21.0,
"flagged": false,
"note": ""
},
{
"name": "STATIC GHOST",
"start": 21.5,
"end": 22.0,
"flagged": false,
"note": ""
}
],
"doorsNote": ""
},
{
"id": "conference",
"events": [
{
"name": "SESSION 1",
"start": 13.0,
"end": 14.0,
"flagged": false,
"note": ""
},
{
"name": "SESSION 2",
"start": 14.5,
"end": 15.5,
"flagged": false,
"note": ""
},
{
"name": "SESSION 3",
"start": 16.0,
"end": 17.5,
"flagged": false,
"note": "KEYNOTE"
}
],
"doorsNote": "Doors 12:30 PM"
}
]
}
]
};
/* ============================================================
WALKING TIME ESTIMATE
Venues are grouped into rough geographic clusters, and this
table gives an estimated walk time (minutes) between any two
clusters. This is a starting estimate from the venues' street
addresses — once you've actually walked the routes, adjust
the numbers below (or add a specific override in
VENUE_WALK_OVERRIDES for a particular venue pair).
============================================================ */
const CLUSTER_OF = {
starlite: "core", temple: "core", block_party: "core",
allsaints: "allsaints",
freemasons_main: "freemasons", freemasons_down: "freemasons",
y_main: "y", y_down: "y",
conference: "conference"
};
const CLUSTER_WALK_MIN = {
core: { core: 0, allsaints: 3, freemasons: 6, y: 5, conference: 13 },
allsaints: { core: 3, allsaints: 0, freemasons: 5, y: 6, conference: 12 },
freemasons: { core: 6, allsaints: 5, freemasons: 0, y: 6, conference: 10 },
y: { core: 5, allsaints: 6, freemasons: 6, y: 0, conference: 9 },
conference: { core: 13, allsaints: 12, freemasons: 10, y: 9, conference: 0 },
};
const SAME_BUILDING_DIFFERENT_FLOOR_MIN = 1; // e.g. Freemasons Main <-> Freemasons Basement
// Optional precise overrides: VENUE_WALK_OVERRIDES["idA|idB"] = minutes (either key order works)
const VENUE_WALK_OVERRIDES = {
// "starlite|allsaints": 4,
};
function walkMinutes(idA, idB){
if(idA === idB) return 0;
const k1 = idA + "|" + idB, k2 = idB + "|" + idA;
if(VENUE_WALK_OVERRIDES[k1] !== undefined) return VENUE_WALK_OVERRIDES[k1];
if(VENUE_WALK_OVERRIDES[k2] !== undefined) return VENUE_WALK_OVERRIDES[k2];
const cA = CLUSTER_OF[idA], cB = CLUSTER_OF[idB];
if(cA === cB) return SAME_BUILDING_DIFFERENT_FLOOR_MIN;
return CLUSTER_WALK_MIN[cA][cB];
}
const ARRIVE_BUFFER_MIN = 5; // extra minutes you want to have in hand beyond the walk itself
const DAY_DATES = {
"Fri, Sep 11": "2026-09-11",
"Sat, Sep 12": "2026-09-12",
"Sun, Sep 13": "2026-09-13",
};
/* ============================================================
SHARED FORMATTING HELPER
Used by both pages to turn a 24h decimal hour (e.g. 19.5)
into a display string like "7:30 PM".
============================================================ */
function fmtTime(dec){
let h = Math.floor(dec) % 24;
let m = Math.round((dec - Math.floor(dec)) * 60);
const ampm = h >= 12 ? "PM" : "AM";
let h12 = h % 12; if (h12 === 0) h12 = 12;
return `${h12}:${m.toString().padStart(2,'0')} ${ampm}`;
}
/* ============================================================
SHARED DATE HELPER (used by the personal schedule page to
compute real leave-by times and schedule browser reminders)
============================================================ */
function dateForDayHour(dayLabel, decHour){
const base = new Date(DAY_DATES[dayLabel] + "T00:00:00");
base.setMinutes(base.getMinutes() + Math.round(decHour * 60));
return base;
}