summaryrefslogtreecommitdiffstats
path: root/VegaData/wwwroot
diff options
context:
space:
mode:
authorivar <i@oiee.no>2025-10-06 12:06:46 +0200
committerivar <i@oiee.no>2025-10-06 12:06:46 +0200
commitf18f3fee8844df98a30c2d248dfca04643dd4365 (patch)
tree599c1b6a97777a84cdcd7fe8333f4718d2e55c85 /VegaData/wwwroot
downloadvegadata-f18f3fee8844df98a30c2d248dfca04643dd4365.tar.xz
vegadata-f18f3fee8844df98a30c2d248dfca04643dd4365.zip
Initial
Diffstat (limited to 'VegaData/wwwroot')
-rw-r--r--VegaData/wwwroot/.DS_Storebin0 -> 6148 bytes
-rw-r--r--VegaData/wwwroot/Baskerville No.2 Regular/Baskerville No.2 Regular.otfbin0 -> 33308 bytes
-rw-r--r--VegaData/wwwroot/Baskerville No.2 Regular/readme.html188
-rw-r--r--VegaData/wwwroot/Broadsheet Italic/Broadsheet Italic.ttfbin0 -> 132156 bytes
-rw-r--r--VegaData/wwwroot/Broadsheet Italic/readme.html188
-rw-r--r--VegaData/wwwroot/framework.js150
-rw-r--r--VegaData/wwwroot/index.html113
-rw-r--r--VegaData/wwwroot/index.js72
8 files changed, 711 insertions, 0 deletions
diff --git a/VegaData/wwwroot/.DS_Store b/VegaData/wwwroot/.DS_Store
new file mode 100644
index 0000000..0fb4e25
--- /dev/null
+++ b/VegaData/wwwroot/.DS_Store
Binary files differ
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
new file mode 100644
index 0000000..35da359
--- /dev/null
+++ b/VegaData/wwwroot/Baskerville No.2 Regular/Baskerville No.2 Regular.otf
Binary files differ
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
new file mode 100644
index 0000000..443d2ab
--- /dev/null
+++ b/VegaData/wwwroot/Broadsheet Italic/Broadsheet Italic.ttf
Binary files differ
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()
+