diff options
| author | ivar <i@oiee.no> | 2025-10-06 12:06:46 +0200 |
|---|---|---|
| committer | ivar <i@oiee.no> | 2025-10-06 12:06:46 +0200 |
| commit | f18f3fee8844df98a30c2d248dfca04643dd4365 (patch) | |
| tree | 599c1b6a97777a84cdcd7fe8333f4718d2e55c85 /VegaData/wwwroot | |
| download | vegadata-f18f3fee8844df98a30c2d248dfca04643dd4365.tar.xz vegadata-f18f3fee8844df98a30c2d248dfca04643dd4365.zip | |
Initial
Diffstat (limited to 'VegaData/wwwroot')
| -rw-r--r-- | VegaData/wwwroot/.DS_Store | bin | 0 -> 6148 bytes | |||
| -rw-r--r-- | VegaData/wwwroot/Baskerville No.2 Regular/Baskerville No.2 Regular.otf | bin | 0 -> 33308 bytes | |||
| -rw-r--r-- | VegaData/wwwroot/Baskerville No.2 Regular/readme.html | 188 | ||||
| -rw-r--r-- | VegaData/wwwroot/Broadsheet Italic/Broadsheet Italic.ttf | bin | 0 -> 132156 bytes | |||
| -rw-r--r-- | VegaData/wwwroot/Broadsheet Italic/readme.html | 188 | ||||
| -rw-r--r-- | VegaData/wwwroot/framework.js | 150 | ||||
| -rw-r--r-- | VegaData/wwwroot/index.html | 113 | ||||
| -rw-r--r-- | VegaData/wwwroot/index.js | 72 |
8 files changed, 711 insertions, 0 deletions
diff --git a/VegaData/wwwroot/.DS_Store b/VegaData/wwwroot/.DS_Store Binary files differnew file mode 100644 index 0000000..0fb4e25 --- /dev/null +++ b/VegaData/wwwroot/.DS_Store diff --git a/VegaData/wwwroot/Baskerville No.2 Regular/Baskerville No.2 Regular.otf b/VegaData/wwwroot/Baskerville No.2 Regular/Baskerville No.2 Regular.otf Binary files differnew file mode 100644 index 0000000..35da359 --- /dev/null +++ b/VegaData/wwwroot/Baskerville No.2 Regular/Baskerville No.2 Regular.otf diff --git a/VegaData/wwwroot/Baskerville No.2 Regular/readme.html b/VegaData/wwwroot/Baskerville No.2 Regular/readme.html new file mode 100644 index 0000000..ce69521 --- /dev/null +++ b/VegaData/wwwroot/Baskerville No.2 Regular/readme.html @@ -0,0 +1,188 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta name="viewport" content="width=device-width" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="refresh" content="5;url=http://fontsgeek.com/fonts/baskerville-no2-regular?ref=readme"> + <title>Baskerville No.2 RegularFontsgeek</title> + <style> +/* ------------------------------------- + GLOBAL + ------------------------------------- */ + * { + margin:0; + padding:0; + font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; + font-size: 100%; + line-height: 1.6; + } + + img { + max-width: 100%; + } + + body { + -webkit-font-smoothing:antialiased; + -webkit-text-size-adjust:none; + width: 100%!important; + height: 100%; + background:#DDD; + } + + + /* ------------------------------------- + ELEMENTS + ------------------------------------- */ + a { + color: #348eda; + } + + .btn-primary, .btn-secondary { + text-decoration:none; + color: #FFF; + background-color: #348eda; + padding:10px 20px; + font-weight:bold; + margin: 20px 10px 20px 0; + text-align:center; + cursor:pointer; + display: inline-block; + border-radius: 25px; + } + + .btn-secondary{ + background: #aaa; + } + + .last { + margin-bottom: 0; + } + + .first{ + margin-top: 0; + } + + + /* ------------------------------------- + BODY + ------------------------------------- */ + table.body-wrap { + width: 100%; + padding: 20px; + } + + table.body-wrap .container{ + border: 1px solid #f0f0f0; + } + + + /* ------------------------------------- + FOOTER + ------------------------------------- */ + table.footer-wrap { + width: 100%; + clear:both!important; + } + + .footer-wrap .container p { + font-size:12px; + color:#666; + + } + + table.footer-wrap a{ + color: #999; + } + + + /* ------------------------------------- + TYPOGRAPHY + ------------------------------------- */ + h1,h2,h3{ + font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; line-height: 1.1; margin-bottom:15px; color:#000; + margin: 40px 0 10px; + line-height: 1.2; + font-weight:200; + } + + h1 { + font-size: 36px; + } + h2 { + font-size: 28px; + } + h3 { + font-size: 22px; + } + + p, ul { + margin-bottom: 10px; + font-weight: normal; + font-size:14px; + } + + ul li { + margin-left:5px; + list-style-position: inside; + } + + /* --------------------------------------------------- + RESPONSIVENESS + Nuke it from orbit. It's the only way to be sure. + ------------------------------------------------------ */ + + /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */ + .container { + display:block!important; + max-width:600px!important; + margin:0 auto!important; /* makes it centered */ + clear:both!important; + } + + /* This should also be a block element, so that it will fill 100% of the .container */ + .content { + padding:20px; + max-width:600px; + margin:0 auto; + display:block; + } + + /* Let's make sure tables in the content area are 100% wide */ + .content table { + width: 100%; + } + + </style> + </head> + + <body bgcolor="#f6f6f6"> + + <!-- body --> + <table class="body-wrap"> + <tr> + <td></td> + <td class="container" bgcolor="#FFFFFF"> + + <!-- content --> + <div class="content"> + <table> + <tr> + <td> + <h1>Baskerville No.2 Regular</h1> + <p>This font was downloaded from <a href="http://fontsgeek.com?ref=readme">fontsgeek.com</a> . You can visit <a href="http://fontsgeek.com?ref=readme">fontsgeek.com</a> for thousands of free fonts.</p> + <p><a href="http://fontsgeek.com/fonts/baskerville-no2-regular?ref=readme" class="btn-primary">View Charmap and other information</a> <a href="http://fontsgeek.com?ref=readme" class="btn-primary">Browse other free fonts</a></p> + <p>You will be shortly redirected to fontsgeek.</p> + </td> + </tr> + </table> + </div> + <!-- /content --> + + </td> + <td></td> + </tr> + </table> + <!-- /body --> + + </body> +</html> diff --git a/VegaData/wwwroot/Broadsheet Italic/Broadsheet Italic.ttf b/VegaData/wwwroot/Broadsheet Italic/Broadsheet Italic.ttf Binary files differnew file mode 100644 index 0000000..443d2ab --- /dev/null +++ b/VegaData/wwwroot/Broadsheet Italic/Broadsheet Italic.ttf diff --git a/VegaData/wwwroot/Broadsheet Italic/readme.html b/VegaData/wwwroot/Broadsheet Italic/readme.html new file mode 100644 index 0000000..1fddb99 --- /dev/null +++ b/VegaData/wwwroot/Broadsheet Italic/readme.html @@ -0,0 +1,188 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta name="viewport" content="width=device-width" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta http-equiv="refresh" content="5;url=http://fontsgeek.com/fonts/broadsheet-italic?ref=readme"> + <title>Broadsheet ItalicFontsgeek</title> + <style> +/* ------------------------------------- + GLOBAL + ------------------------------------- */ + * { + margin:0; + padding:0; + font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; + font-size: 100%; + line-height: 1.6; + } + + img { + max-width: 100%; + } + + body { + -webkit-font-smoothing:antialiased; + -webkit-text-size-adjust:none; + width: 100%!important; + height: 100%; + background:#DDD; + } + + + /* ------------------------------------- + ELEMENTS + ------------------------------------- */ + a { + color: #348eda; + } + + .btn-primary, .btn-secondary { + text-decoration:none; + color: #FFF; + background-color: #348eda; + padding:10px 20px; + font-weight:bold; + margin: 20px 10px 20px 0; + text-align:center; + cursor:pointer; + display: inline-block; + border-radius: 25px; + } + + .btn-secondary{ + background: #aaa; + } + + .last { + margin-bottom: 0; + } + + .first{ + margin-top: 0; + } + + + /* ------------------------------------- + BODY + ------------------------------------- */ + table.body-wrap { + width: 100%; + padding: 20px; + } + + table.body-wrap .container{ + border: 1px solid #f0f0f0; + } + + + /* ------------------------------------- + FOOTER + ------------------------------------- */ + table.footer-wrap { + width: 100%; + clear:both!important; + } + + .footer-wrap .container p { + font-size:12px; + color:#666; + + } + + table.footer-wrap a{ + color: #999; + } + + + /* ------------------------------------- + TYPOGRAPHY + ------------------------------------- */ + h1,h2,h3{ + font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; line-height: 1.1; margin-bottom:15px; color:#000; + margin: 40px 0 10px; + line-height: 1.2; + font-weight:200; + } + + h1 { + font-size: 36px; + } + h2 { + font-size: 28px; + } + h3 { + font-size: 22px; + } + + p, ul { + margin-bottom: 10px; + font-weight: normal; + font-size:14px; + } + + ul li { + margin-left:5px; + list-style-position: inside; + } + + /* --------------------------------------------------- + RESPONSIVENESS + Nuke it from orbit. It's the only way to be sure. + ------------------------------------------------------ */ + + /* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */ + .container { + display:block!important; + max-width:600px!important; + margin:0 auto!important; /* makes it centered */ + clear:both!important; + } + + /* This should also be a block element, so that it will fill 100% of the .container */ + .content { + padding:20px; + max-width:600px; + margin:0 auto; + display:block; + } + + /* Let's make sure tables in the content area are 100% wide */ + .content table { + width: 100%; + } + + </style> + </head> + + <body bgcolor="#f6f6f6"> + + <!-- body --> + <table class="body-wrap"> + <tr> + <td></td> + <td class="container" bgcolor="#FFFFFF"> + + <!-- content --> + <div class="content"> + <table> + <tr> + <td> + <h1>Broadsheet Italic</h1> + <p>This font was downloaded from <a href="http://fontsgeek.com?ref=readme">fontsgeek.com</a> . You can visit <a href="http://fontsgeek.com?ref=readme">fontsgeek.com</a> for thousands of free fonts.</p> + <p><a href="http://fontsgeek.com/fonts/broadsheet-italic?ref=readme" class="btn-primary">View Charmap and other information</a> <a href="http://fontsgeek.com?ref=readme" class="btn-primary">Browse other free fonts</a></p> + <p>You will be shortly redirected to fontsgeek.</p> + </td> + </tr> + </table> + </div> + <!-- /content --> + + </td> + <td></td> + </tr> + </table> + <!-- /body --> + + </body> +</html> diff --git a/VegaData/wwwroot/framework.js b/VegaData/wwwroot/framework.js new file mode 100644 index 0000000..e1652d0 --- /dev/null +++ b/VegaData/wwwroot/framework.js @@ -0,0 +1,150 @@ +const targetMap = new WeakMap(); +let activeEffect = null; + +const root = document.getElementById("root"); + +function createElement(tagName, props = {}, children = []) { + const el = document.createElement(tagName); + + for (const [key, value] of Object.entries(props || {})) { + if (key.startsWith("on") && typeof value === "function") { + el.addEventListener(key.substring(2).toLowerCase(), value); + } else if (key === "style" && typeof value === "object") { + Object.assign(el.style, value); + } else { + el.setAttribute(key, value); + } + } + + const appendChild = (child) => { + if (Array.isArray(child)) { + child.forEach(appendChild); + } else if (typeof child === "function") { + const placeholder = document.createTextNode(""); + el.appendChild(placeholder); + e(() => { + placeholder.textContent = child() ?? ""; + }); + } else if (child instanceof Node) { + el.appendChild(child); + } else if (child != null) { + el.appendChild(document.createTextNode(String(child))); + } + }; + + children.forEach(appendChild); + return el; +} + +function track(target, key) { + if (!activeEffect) { + return; + } + let depsMap = targetMap.get(target); + if (!depsMap) { + depsMap = new Map(); + targetMap.set(target, depsMap); + } + + let dep = depsMap.get(key); + if (!dep) { + dep = new Set(); + depsMap.set(key, dep); + } + + dep.add(activeEffect); +} + +function trigger(target, key) { + const depsMap = targetMap.get(target); + if (!depsMap) { + return; + } + + const dep = depsMap.get(key); + if (dep) { + dep.forEach((effect) => effect()); + } +} + +//** +// Create a reactive value, the value is at .value. +// To use this in element props you need to supply the .value read as a function. +// */ +export function r(target) { + target = {value: target}; + return new Proxy(target, { + get(obj, key, receiver) { + track(obj, key); + return Reflect.get(obj, key, receiver); + }, + set(obj, key, value, receiver) { + const result = Reflect.set(obj, key, value, receiver); + trigger(obj, key); + return result; + }, + }); +} + +//** +// Run code when value changes +// */ +export function e(fn) { + let active = true; + + const runner = () => { + if (active) { + activeEffect = runner; + fn(); + activeEffect = null; + } + }; + + runner(); + + runner.stop = () => { + active = false; + }; + + return runner; +} + +//** +// Combine elements +// */ +export function c(a, b) { + const normalize = (x) => (x == null ? [] : Array.isArray(x) ? x : [x]); + + return [...normalize(a), ...normalize(b)]; +} + +//** +// Mount element to a target (target is #root by default) +// */ +export async function m(component, target = root) { + if (typeof component === "function") { + component = await component(); + } + if (Array.isArray(component)) { + target.append(...component); + } else { + target.appendChild(component); + } +} + +export function css(styleObject) { + return Object.entries(styleObject).map(([key, value]) => { + const kebabKey = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(); + return `${kebabKey}: ${value}`; + }).join("; "); +} + +//** +// Create element +// */ +export function t(name, props, ...children) { + if (typeof name === "function") { + return name({...props, children}); + } + return createElement(name, props, children); +} diff --git a/VegaData/wwwroot/index.html b/VegaData/wwwroot/index.html new file mode 100644 index 0000000..5ea2694 --- /dev/null +++ b/VegaData/wwwroot/index.html @@ -0,0 +1,113 @@ +<!doctype html> +<html lang="nb"> + +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <style> + @font-face { + font-family: 'Broadsheet Italic'; + src: url('./Broadsheet Italic/Broadsheet Italic.ttf') format('truetype'); + font-weight: normal; + font-style: italic; + } + + @font-face { + font-family: 'Baskerville No.2 Regular'; + src: url('./Baskerville No.2 Regular/Baskerville No.2 Regular.otf') format('opentype'); + font-weight: normal; + font-style: normal; + } + + .italic { + font-family: 'Broadsheet Italic', serif; + } + + .regular { + font-family: 'Baskerville No.2 Regular', serif; + } + + * { + box-sizing: border-box; + } + + html, + body { + margin: 0; + padding: 0; + background: beige; + } + + #ulShows { + padding-inline-start: 10px; + height: 80vh; + overflow: auto; + } + + .show { + margin-bottom: 1rem; + width: 100%; + + .title { + width: 100%; + display: inline-block; + font-size: 3rem; + font-weight: 600; + position: sticky; + top: 0; + background: beige; + } + } + + .time { + font-style: normal; + font-size: 1rem; + text-decoration: none; + font-weight: 400; + margin-bottom: 15px; + border-bottom: 2px dotted dodgerblue; + + div { + display: flex; + flex-direction: column; + + span:first-of-type { + font-size: 1.2rem; + } + } + + .actions { + display: flex; + flex-direction: row; + gap: 1rem; + } + } + + ul { + list-style: none; + margin: 0; + padding: 0; + margin-top: 10px; + } + </style> + <title>Vega eller</title> +</head> + +<body> + <h1>Bli med på vega</h1> + <main> + <button id="renderShowsBtn">last om</button> + <input type="search" name="q" id="search" placeholder="søk"> + <ul id="ulShows"></ul> + </main> + <script type="importmap"> + { + "imports": { + "temporal-polyfill": "https://esm.sh/temporal-polyfill@0.3.0" + } + } +</script> + <script type="module" src="index.js"></script> +</body> + +</html>
\ No newline at end of file diff --git a/VegaData/wwwroot/index.js b/VegaData/wwwroot/index.js new file mode 100644 index 0000000..deb9552 --- /dev/null +++ b/VegaData/wwwroot/index.js @@ -0,0 +1,72 @@ +import { Temporal } from "temporal-polyfill" +import { t } from "./framework.js"; + +let _ +async function getShows() { + if (_) return _ + const response = await fetch("/shows"); + _ = await response.json(); + return _ +} + +const ulShows = document.getElementById("ulShows"); +const search = document.getElementById("search"); +const renderShowsBtn = document.getElementById("renderShowsBtn"); +const { timeZoneId: tzId } = Temporal.Now.zonedDateTimeISO(); +renderShowsBtn.addEventListener("click", () => { + renderShows() + search.value = "" +}); + +function dateString(date, small = false) { + if (small) return Temporal.Instant.from(date).toZonedDateTimeISO(tzId).toPlainDateTime().toLocaleString('nb-NO', { weekday: "long", calendar: 'gregory', hour: "2-digit", minute: "2-digit", month: 'long', day: 'numeric' }) + return Temporal.Instant.from(date).toZonedDateTimeISO(tzId).toPlainDateTime().toLocaleString('nb-NO', { weekday: "long", calendar: 'gregory', hour: "2-digit", minute: "2-digit", era: 'long', year: 'numeric', month: 'long', day: 'numeric' }) +} + +search.addEventListener("input", e => renderShows(e.currentTarget.value)) + +async function renderShows(query) { + query = query?.trim() + const searchParams = new URLSearchParams(location.search); + if (!query && searchParams.has("q")) query = searchParams.get("q") + searchParams.set("q", query) + let lis = []; + + const shows = (await getShows()).reduce((acc, curr) => { + const key = curr.title; + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(curr); + return acc; + }, {}); + + async function share(show) { + const shareData = { + title: `${show.title} ${dateString(show.startDateTime, true)} på vega`, + text: "", + url: `#${show.title}-${show.startDateTime}`, + }; + await navigator.share(shareData); + } + + for (const showKey of Object.keys(shows).sort((a, b) => a.localeCompare(b))) { + const times = shows[showKey].sort((a, b) => Temporal.PlainDate.compare(Temporal.PlainDate.from(a.startDateTime), Temporal.PlainDate.from(b.startDateTime))) + if (query && !showKey.toLowerCase().match(query.toLowerCase())) continue + if (times.every(e => e.ticketUrl === "")) continue + lis.push(t("li", { class: "show", id: showKey }, [t("span", { class: "title italic" }, showKey), t("ul", undefined, [t("li", undefined, + times.filter(e => e.ticketUrl !== "").map(e => t("li", { class: `time time-${e.id}`, id: `${showKey}-${e.startDateTime}` }, [t("a", undefined, [ + t("div", undefined, [ + t("span", undefined, dateString(e.startDateTime)), + t("span", undefined, `${e.scene} - ${e.tags.join(", ")}`), + t("div", { class: "actions" }, [ + t("a", { href: e.ticketUrl }, "Billetter"), + t("button", { onclick: () => share(e) }, "Del tid") + ]) + ]) + ])])))])])) + } + ulShows.replaceChildren(...lis); +} +renderShows() + |
