diff options
Diffstat (limited to '')
-rw-r--r-- | static/Makefile | 15 | ||||
-rw-r--r-- | static/_global.scss | 1 | ||||
-rw-r--r-- | static/components/date-time-input.ts | 2 | ||||
-rw-r--r-- | static/components/vevent-description.ts | 26 | ||||
-rw-r--r-- | static/directory-listing.scss | 5 | ||||
-rw-r--r-- | static/formatters.ts | 35 | ||||
-rw-r--r-- | static/globals.ts | 2 | ||||
-rwxr-xr-x | static/make-watch | 23 | ||||
-rw-r--r-- | static/style.scss | 16 | ||||
-rw-r--r-- | static/user/.gitignore | 2 | ||||
-rw-r--r-- | static/user/user-additions.js | 62 | ||||
-rw-r--r-- | static/vevent.ts | 5 |
12 files changed, 159 insertions, 35 deletions
diff --git a/static/Makefile b/static/Makefile index b85422a3..1979575d 100644 --- a/static/Makefile +++ b/static/Makefile @@ -1,10 +1,11 @@ -.PHONY: all clean watch +.PHONY: all install clean watch -TARGETS := style.css smallcal.css script.out.js +TARGETS := style.css smallcal.css script.out.js directory-listing.css WATCH= # script explicitly named, since that is our entry point TS_FILES = script.ts $(shell find . -type f -name \*.ts -not -path */node_modules/*) +JS_FILES = $(TS_FILES:%.ts=%.js) export PATH := $(shell npm bin):$(PATH) @@ -20,15 +21,11 @@ script.out.js: $(TS_FILES) deps.svg: $(TS_FILES) madge --image $@ $^ -# Note that 'tsc --watch' doesn't provide the files we are using. It's -# just here for debug. watch: - tmux \ - new-session "scss --watch -I. style.scss:style.css" \; \ - split-window "tsc --watch" \; \ - rename-session "calp watch" \; \ - select-layout even-vertical + ./make-watch +install: all + install -m644 -t $(DESTDIR)/usr/share/calp/www/ $(TARGETS) clean: rm $(TARGETS) diff --git a/static/_global.scss b/static/_global.scss index 41f426f9..58e05155 100644 --- a/static/_global.scss +++ b/static/_global.scss @@ -13,4 +13,5 @@ $popup-style: "left"; /* Each popup can have a different amoutn of tabs. Override this as appropriate */ --tabcount: 4; + --background-color: white; } diff --git a/static/components/date-time-input.ts b/static/components/date-time-input.ts index a6d5df18..005e4190 100644 --- a/static/components/date-time-input.ts +++ b/static/components/date-time-input.ts @@ -67,7 +67,7 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement { } set value(date: Date) { - let [d, t] = date.format("~L~Y-~m-~dT~H:~M:~S").split('T'); + let [d, t] = date.format("~L~Y-~m-~dT~H:~M").split('T'); // console.log(d, t); this.date.value = d; this.time.value = t; diff --git a/static/components/vevent-description.ts b/static/components/vevent-description.ts index 4d81d6b3..f0d224be 100644 --- a/static/components/vevent-description.ts +++ b/static/components/vevent-description.ts @@ -2,7 +2,7 @@ export { ComponentDescription } import { VEvent } from '../vevent' import { ComponentVEvent } from './vevent' -import { makeElement } from '../lib' +import { formatters } from '../formatters' /* <vevent-description /> @@ -24,26 +24,12 @@ class ComponentDescription extends ComponentVEvent { for (let el of body.querySelectorAll('[data-property]')) { if (!(el instanceof HTMLElement)) continue; let p = el.dataset.property!; - let d, fmt; + let d; if ((d = data.getProperty(p))) { - switch (p.toLowerCase()) { - case 'categories': - for (let item of d) { - let q = encodeURIComponent( - `(member "${item}" (or (prop event (quote CATEGORIES)) (quote ())))`) - el.appendChild(makeElement('a', { - textContent: item, - href: `/search/?q=${q}`, - })) - } - break; - default: - if ((fmt = el.dataset.fmt)) { - el.textContent = d.format(fmt); - } else { - el.textContent = d; - } - } + let key = p.toLowerCase(); + let f = formatters.get(key); + if (f) f(el, d); + else window.formatters.get('default')!(el, d); } } diff --git a/static/directory-listing.scss b/static/directory-listing.scss new file mode 100644 index 00000000..745b5bc3 --- /dev/null +++ b/static/directory-listing.scss @@ -0,0 +1,5 @@ +@import 'global'; + +.directory-table td:nth-child(3) { + text-align: end; +} diff --git a/static/formatters.ts b/static/formatters.ts new file mode 100644 index 00000000..828a0e8b --- /dev/null +++ b/static/formatters.ts @@ -0,0 +1,35 @@ +export { + formatters, +} + +import { makeElement } from './lib' + +declare global { + interface Window { + formatters : Map<string, (e : HTMLElement, s : any) => void>; + } +} + +let formatters : Map<string, (e : HTMLElement, s : any) => void>; +formatters = window.formatters = new Map(); + + +formatters.set('categories', (el, d) => { + for (let item of d) { + let q = encodeURIComponent( + `(member "${item}" (or (prop event (quote CATEGORIES)) (quote ())))`) + el.appendChild(makeElement('a', { + textContent: item, + href: `/search/?q=${q}`, + })) + } +}) + +formatters.set('default', (el, d) => { + let fmt; + if ((fmt = el.dataset.fmt)) { + el.textContent = d.format(fmt); + } else { + el.textContent = d; + } +}) diff --git a/static/globals.ts b/static/globals.ts index eb7488c0..2fc12933 100644 --- a/static/globals.ts +++ b/static/globals.ts @@ -30,6 +30,8 @@ window.addNewEvent = () => { let ev = new VEvent(); let uid = uuid() let now = new Date() + /* Round seconds to 0, since time inputs wants exact seconds */ + now.setUTCSeconds(0); ev.setProperties([ ['uid', uid], ['dtstart', now, 'date-time'], diff --git a/static/make-watch b/static/make-watch new file mode 100755 index 00000000..c985b37f --- /dev/null +++ b/static/make-watch @@ -0,0 +1,23 @@ +#!/bin/bash + +here=$(dirname "$(realpath "$0")") +cd "$here" || { + echo "Failed to cd to location of script ($here)" + exit 1 +} + +export PATH="$here/node_modules/.bin:$PATH" + +# Note that 'tsc --watch' doesn't provide the files we are using. It's +# just here for debug. +if [ -n "$TMUX" ]; then + tmux new-window "scss --watch -I. style.scss:style.css" + tmux new-window "tsc --watch" +else + tmux \ + new-session "scss --watch -I. style.scss:style.css" \; \ + split-window "tsc --watch" \; \ + rename-session "calp watch" \; \ + select-layout even-vertical + + fi diff --git a/static/style.scss b/static/style.scss index efe8291d..c5705e24 100644 --- a/static/style.scss +++ b/static/style.scss @@ -11,6 +11,8 @@ html, body { max-width: 100%; height: 100%; + background-color: var(--background-color); + grid-template-columns: auto auto 20em; grid-template-rows: auto; @@ -227,10 +229,16 @@ Each event within the eventlist padding-left: 2px; margin-top: 1em; - header h2 { - width: 100%; - text-align: center; - font-size: 14pt; + header { + position: sticky; + top: 0; + background-color: var(--background-color); + + h2 { + width: 100%; + text-align: center; + font-size: 14pt; + } } } diff --git a/static/user/.gitignore b/static/user/.gitignore new file mode 100644 index 00000000..6039f77d --- /dev/null +++ b/static/user/.gitignore @@ -0,0 +1,2 @@ +!*.js +salar.json diff --git a/static/user/user-additions.js b/static/user/user-additions.js new file mode 100644 index 00000000..c9ebe1a4 --- /dev/null +++ b/static/user/user-additions.js @@ -0,0 +1,62 @@ +window.formatters.set('description', (el, d) => { + if (/<br\/?>/.exec(d)) { + /* Assume that the text is HTML iff it contains a <br/> tag */ + let parser = new DOMParser(); + let doc = parser.parseFromString(d, 'text/html'); + el.replaceChildren(doc.body); + } else { + /* Otherwise it should be plain(er) text, parse "all" links */ + let rx = /https?:\/\/\S+/g + let idx = 0; + let children = [] + for (let match of d.matchAll(rx)) { + let anch = document.createElement('a') + anch.href = match[0] + anch.textContent = match[0] + + children.push(d.substring(idx, match.index)) + children.push(anch) + + idx = match.index + match[0].length + } + children.push(d.substring(idx)) + el.replaceChildren(...children); + } +}) + +/* This location formatter is generally not for general use. + It holds a small lookup table of "all" locations at Linköping University, + and makes location names from their calendar system clickable. + + To obtain salar.json, run scripts/fetch-liu-map-index.scm from calps source tree. +*/ + +window.salar = new Promise((resolve, reject) => + fetch('/static/user/salar.json') + .then(d => d.json()) + .then(d => resolve(d))) + + +window.formatters.set('location', async function(el, d) { + let rx = /Lokal: (.*)/ + let m = rx.exec(d) + if (! m) { + el.textContent = d; + return; + } + + let salar = await window.salar; + + let name = m[1] + let frag = salar[name]; + if (frag) { + let anch = document.createElement('a'); + anch.href = `https://old.liu.se/karta/${frag}` + anch.target = '_blank' + anch.textContent = name; + el.append('Lokal: '); + el.append(anch); + } else { + el.textContent = `Lokal: ${name}` + } +}) diff --git a/static/vevent.ts b/static/vevent.ts index cee26727..56c9019a 100644 --- a/static/vevent.ts +++ b/static/vevent.ts @@ -396,10 +396,13 @@ function xml_to_recurrence_rule(xml: Element): RecurrenceRule { break; case 'count': - case 'interval': rr.count = Number(t) break; + case 'interval': + rr.interval = Number(t) + break; + case 'bysecond': case 'byminute': case 'byhour': |