diff options
Diffstat (limited to 'static')
-rw-r--r-- | static/Makefile | 28 | ||||
-rw-r--r-- | static/components.ts (renamed from static/elements.ts) | 0 | ||||
-rw-r--r-- | static/components/date-time-input.ts | 2 | ||||
-rw-r--r-- | static/components/input-list.ts | 2 | ||||
-rw-r--r-- | static/components/popup-element.ts | 7 | ||||
-rw-r--r-- | static/components/slider.ts | 18 | ||||
-rw-r--r-- | static/components/vevent-block.ts | 2 | ||||
-rw-r--r-- | static/components/vevent-description.ts | 11 | ||||
-rw-r--r-- | static/components/vevent-edit.ts | 46 | ||||
-rw-r--r-- | static/components/vevent.ts | 4 | ||||
-rw-r--r-- | static/event-creator.ts | 10 | ||||
-rw-r--r-- | static/formatters.ts | 26 | ||||
-rw-r--r-- | static/globals.ts | 2 | ||||
-rwxr-xr-x | static/make-watch | 5 | ||||
-rw-r--r-- | static/package.json | 10 | ||||
-rw-r--r-- | static/script.ts | 2 | ||||
-rw-r--r-- | static/server_connect.ts | 2 | ||||
-rw-r--r-- | static/style.scss | 2 | ||||
-rw-r--r-- | static/user/user-additions.js | 48 | ||||
-rw-r--r-- | static/vevent.ts | 10 |
20 files changed, 129 insertions, 108 deletions
diff --git a/static/Makefile b/static/Makefile index 9292ed8a..2f715f7e 100644 --- a/static/Makefile +++ b/static/Makefile @@ -1,22 +1,29 @@ -.PHONY: all install clean watch +.PHONY: all install clean watch watch-esbuild -TARGETS := style.css smallcal.css script.out.js directory-listing.css +TARGETS := style.css smallcal.css script.js directory-listing.css WATCH= TS_FILES = $(shell find . -type f -name \*.ts -not -path */node_modules/*) JS_FILES = $(TS_FILES:%.ts=%.js) +ESBUILD_LOGLEVEL=warning +# Variable for adding extra flags +ESBUILD_FLAGS = +# Used flags +__ESBUILD_FLAGS = --log-level=$(ESBUILD_LOGLEVEL) \ + --sourcemap --bundle --outdir=$(CURDIR) \ + $(ESBUILD_FLAGS) + export PATH := $(shell npm bin):$(PATH) all: $(TARGETS) -%.map.json: %.out.js - tail -n1 $< | tail -c+65 | base64 --decode | jq '.' > $@ - -# r!browserify --list script.ts -p tsify | xargs -L1 basename | tac # script explicitly named, since that is our entry point -script.out.js: script.ts $(TS_FILES) - browserify $< -p tsify --noImplicitAny --debug -o $@ +script.js: script.ts $(TS_FILES) + esbuild $< $(__ESBUILD_FLAGS) + +watch-esbuild: + $(MAKE) ESBUILD_FLAGS+='--watch' ESBUILD_LOGLEVEL=info -B script.js deps.svg: $(TS_FILES) madge --image $@ $^ @@ -25,10 +32,11 @@ watch: ./make-watch install: all + install -d $(DESTDIR)/usr/share/calp/www install -m644 -t $(DESTDIR)/usr/share/calp/www/ $(TARGETS) clean: - rm $(TARGETS) + -rm $(TARGETS) %.css: %.scss - scss $(WATCH) -I. $< $@ + scss -E UTF-8 $(WATCH) -I. $< $@ diff --git a/static/elements.ts b/static/components.ts index e5fabba6..e5fabba6 100644 --- a/static/elements.ts +++ b/static/components.ts diff --git a/static/components/date-time-input.ts b/static/components/date-time-input.ts index 005e4190..20e9a505 100644 --- a/static/components/date-time-input.ts +++ b/static/components/date-time-input.ts @@ -68,7 +68,6 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement { set value(date: Date) { 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; @@ -98,7 +97,6 @@ class DateTimeInput extends /* HTMLInputElement */ HTMLElement { } set stringValue(new_value: Date | string) { - // console.log('Setting date'); let date, time, dateonly = false; if (new_value instanceof Date) { date = new_value.format("~L~Y-~m-~d"); diff --git a/static/components/input-list.ts b/static/components/input-list.ts index 34696e3e..0afd4999 100644 --- a/static/components/input-list.ts +++ b/static/components/input-list.ts @@ -1,7 +1,5 @@ export { InputList } -/* This file replaces input_list.js */ - /* TODO allow each item to be a larger unit, possibly containing multiple input fields. diff --git a/static/components/popup-element.ts b/static/components/popup-element.ts index 3300f885..458f543c 100644 --- a/static/components/popup-element.ts +++ b/static/components/popup-element.ts @@ -71,10 +71,12 @@ class PopupElement extends ComponentVEvent { return ['visible']; } - attributeChangedCallback(name: string, oldValue?: string, newValue?: string) { + attributeChangedCallback(name: string, _?: string, newValue?: string) { switch (name) { case 'visible': - this.onVisibilityChange() + if (newValue !== null) + /* Only run resize code when showing the popup */ + this.onVisibilityChange() break; } } @@ -92,6 +94,7 @@ class PopupElement extends ComponentVEvent { } private onVisibilityChange() { + console.log('here'); /* TODO better way to find root */ let root; diff --git a/static/components/slider.ts b/static/components/slider.ts index a48d5a40..48abc91b 100644 --- a/static/components/slider.ts +++ b/static/components/slider.ts @@ -24,14 +24,14 @@ class SliderInput extends HTMLElement { constructor(min?: number, max?: number, step?: number, value?: number) { super(); - this.min = min || parseFloat(this.getAttribute('min') || ""+dflt['min']); - this.max = max || parseFloat(this.getAttribute('max') || ""+dflt['max']); - this.step = step || parseFloat(this.getAttribute('step') || ""+dflt['step']); + this.min = min || parseFloat(this.getAttribute('min') || "" + dflt['min']); + this.max = max || parseFloat(this.getAttribute('max') || "" + dflt['max']); + this.step = step || parseFloat(this.getAttribute('step') || "" + dflt['step']); // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#value const defaultValue = (this.max < this.min) - ? this.min - : this.min + (this.max - this.min)/2; + ? this.min + : this.min + (this.max - this.min) / 2; this.slider = makeElement('input', { type: 'range', @@ -48,8 +48,8 @@ class SliderInput extends HTMLElement { value: this.value, }) as HTMLInputElement - this.slider.addEventListener('input', (e) => this.propagate(e)); - this.textIn.addEventListener('input', (e) => this.propagate(e)); + this.slider.addEventListener('input', e => this.propagate(e)); + this.textIn.addEventListener('input', e => this.propagate(e)); /* MUST be after sub components are bound */ this.value = "" + (value || this.getAttribute('value') || defaultValue); @@ -64,7 +64,7 @@ class SliderInput extends HTMLElement { return ['min', 'max', 'step'] } - attributeChangedCallback(name: Attribute, _: string|null, to: string|null): void { + attributeChangedCallback(name: Attribute, _?: string, to?: string): void { if (to) { this.slider.setAttribute(name, to); this.textIn.setAttribute(name, to); @@ -72,7 +72,7 @@ class SliderInput extends HTMLElement { this.slider.removeAttribute(name); this.textIn.removeAttribute(name); } - this[name] = parseFloat(to || ""+dflt[name]) + this[name] = parseFloat(to || "" + dflt[name]) } propagate(e: Event) { diff --git a/static/components/vevent-block.ts b/static/components/vevent-block.ts index 8cf61d30..9bbb8e7e 100644 --- a/static/components/vevent-block.ts +++ b/static/components/vevent-block.ts @@ -91,7 +91,7 @@ class ComponentBlock extends ComponentVEvent { if (data.getProperty('rrule') !== undefined) { let rep = this.getElementsByClassName('repeating') - if (rep && rep.length !== 0) { + if (rep.length !== 0) { (rep[0] as HTMLElement).innerText = '↺' } } diff --git a/static/components/vevent-description.ts b/static/components/vevent-description.ts index 463725f1..b44185e7 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 { formatters } from '../formatters' +import { format } from '../formatters' /* <vevent-description /> @@ -23,14 +23,7 @@ class ComponentDescription extends ComponentVEvent { for (let el of body.querySelectorAll('[data-property]')) { if (!(el instanceof HTMLElement)) continue; - let p = el.dataset.property!; - let d; - if ((d = data.getProperty(p))) { - let key = p.toLowerCase(); - let f = formatters.get(key); - if (f) f(el, data, d); - else window.formatters.get('default')!(el, data, d); - } + format(el, data, el.dataset.property!); } let repeating = body.getElementsByClassName('repeating')[0] as HTMLElement diff --git a/static/components/vevent-edit.ts b/static/components/vevent-edit.ts index bf72678c..e3b5d105 100644 --- a/static/components/vevent-edit.ts +++ b/static/components/vevent-edit.ts @@ -25,31 +25,22 @@ class ComponentEdit extends ComponentVEvent { let body = frag.firstElementChild! this.replaceChildren(body); + let data = vcal_objects.get(this.uid) + if (!data) { + throw `Data missing for uid ${this.dataset.uid}.` + } + for (let el of this.querySelectorAll('[data-label]')) { let label = document.createElement('label'); let id = el.id || gensym('input'); el.id = id; label.htmlFor = id; label.textContent = (el as HTMLElement).dataset.label!; + el.parentElement!.insertBefore(label, el); } - } - - connectedCallback() { - - /* Edit tab is rendered here. It's left blank server-side, since - it only makes sense to have something here if we have javascript */ - - let data = vcal_objects.get(this.uid) - - if (!data) { - throw `Data missing for uid ${this.dataset.uid}.` - } - - - // return; /* Handle calendar dropdown */ - for (let el of this.getElementsByClassName('calendar-selection')) { + for (let el of this.querySelectorAll('select.calendar-selection')) { for (let opt of el.getElementsByTagName('option')) { opt.selected = false; } @@ -57,21 +48,19 @@ class ComponentEdit extends ComponentVEvent { (el as HTMLSelectElement).value = data.calendar; } - el.addEventListener('change', (e) => { + el.addEventListener('change', e => { let v = (e.target as HTMLSelectElement).selectedOptions[0].value let obj = vcal_objects.get(this.uid)! obj.calendar = v; }); } - this.redraw(data); // for (let el of this.getElementsByClassName("interactive")) { for (let el of this.querySelectorAll("[data-property]")) { // console.log(el); - el.addEventListener('input', (e) => { + el.addEventListener('input', () => { let obj = vcal_objects.get(this.uid) - // console.log(el, e); if (obj === undefined) { throw 'No object with uid ' + this.uid } @@ -83,7 +72,6 @@ class ComponentEdit extends ComponentVEvent { console.log(el, 'not an HTMLInputElement'); return; } - // console.log(`obj[${el.dataset.property!}] = `, el.value); obj.setProperty( el.dataset.property!, el.value) @@ -135,6 +123,22 @@ class ComponentEdit extends ComponentVEvent { }); } + connectedCallback() { + + /* Edit tab is rendered here. It's left blank server-side, since + it only makes sense to have something here if we have javascript */ + + let data = vcal_objects.get(this.uid) + + if (!data) { + throw `Data missing for uid ${this.dataset.uid}.` + } + + this.redraw(data); + + // return; + } + redraw(data: VEvent) { /* We only update our fields, instead of reinstansiating ourselves from the template, in hope that it's faster */ diff --git a/static/components/vevent.ts b/static/components/vevent.ts index 5852a2ff..7487cbb6 100644 --- a/static/components/vevent.ts +++ b/static/components/vevent.ts @@ -10,12 +10,12 @@ Lacks an accompaning tag, and shouldn't be directly instanciated. */ abstract class ComponentVEvent extends HTMLElement { - template: HTMLTemplateElement | null + template?: HTMLTemplateElement uid: string constructor(uid?: string) { super(); - this.template = document.getElementById(this.tagName.toLowerCase()) as HTMLTemplateElement | null + this.template = document.getElementById(this.tagName.toLowerCase()) as HTMLTemplateElement | undefined let real_uid; diff --git a/static/event-creator.ts b/static/event-creator.ts index 0f2c42b4..5e55e64e 100644 --- a/static/event-creator.ts +++ b/static/event-creator.ts @@ -9,11 +9,11 @@ import { ical_type } from './types' class EventCreator { /* Event which we are trying to create */ - ev: VEvent | null = null; + ev?: VEvent /* Graphical block for event. Only here so we can find its siblings, and update pointer events accordingly */ - event: Element | null = null; + event?: Element event_start: { x: number, y: number } = { x: NaN, y: NaN } down_on_event: boolean = false @@ -160,7 +160,7 @@ class EventCreator { create_event_finisher(callback: ((ev: VEvent) => void)) { let that = this; - return function create_event_up(e: MouseEvent) { + return function create_event_up(_: MouseEvent) { if (!that.ev) return; /* Restore pointer events for all existing events. @@ -171,8 +171,8 @@ class EventCreator { } let localevent = that.ev; - that.ev = null - that.event = null; + that.ev = undefined + that.event = undefined; callback(localevent); diff --git a/static/formatters.ts b/static/formatters.ts index 5605e051..e0018278 100644 --- a/static/formatters.ts +++ b/static/formatters.ts @@ -1,11 +1,11 @@ export { - formatters, + format } import { makeElement } from './lib' import { VEvent } from './vevent' -type formatter = (e: HTMLElement, d: VEvent, s: any) => void +type formatter = (e: HTMLElement, d: VEvent, s: any) => Promise<void> declare global { interface Window { @@ -16,8 +16,24 @@ declare global { let formatters: Map<string, formatter>; formatters = window.formatters = new Map(); +async function format(targetElement: HTMLElement, data: VEvent, key: string): Promise<void> { + let d = data.getProperty(key); + if (!d) return + let formatter = formatters.get(key.toLowerCase()); + if (formatter) { + try { + await formatter(targetElement, data, d); + } catch (error) { + console.warn('Formatter failed') + console.warn(error); + formatters.get('default')!(targetElement, data, d); + } + } else { + formatters.get('default')!(targetElement, data, d); + } +} -formatters.set('categories', (el, _, d) => { +formatters.set('categories', async (el, _, d) => { for (let item of d) { let q = encodeURIComponent( `(member "${item}" (or (prop event (quote CATEGORIES)) (quote ())))`) @@ -28,7 +44,7 @@ formatters.set('categories', (el, _, d) => { } }) -function format_time_tag(el: HTMLElement, ev: VEvent, d: any): void { +async function format_time_tag(el: HTMLElement, ev: VEvent, d: any): Promise<void> { if (el instanceof HTMLTimeElement) { if (d instanceof Date) { let fmt = ''; @@ -49,7 +65,7 @@ function format_time_tag(el: HTMLElement, ev: VEvent, d: any): void { formatters.set('dtstart', format_time_tag) formatters.set('dtend', format_time_tag) -formatters.set('default', (el, _, d) => { +formatters.set('default', async (el, _, d) => { let fmt; if ((fmt = el.dataset.fmt)) { el.textContent = d.format(fmt); diff --git a/static/globals.ts b/static/globals.ts index d90a3681..243e15e4 100644 --- a/static/globals.ts +++ b/static/globals.ts @@ -20,7 +20,7 @@ declare global { EDIT_MODE: boolean; default_calendar: string; - addNewEvent: ((e: any) => void); + addNewEvent(): void; } } window.vcal_objects = vcal_objects; diff --git a/static/make-watch b/static/make-watch index c985b37f..b328038a 100755 --- a/static/make-watch +++ b/static/make-watch @@ -8,15 +8,14 @@ cd "$here" || { 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" \; \ + split-window "tsc --watch --noEmit" \; \ + split-window "make watch-esbuild" \; \ rename-session "calp watch" \; \ select-layout even-vertical diff --git a/static/package.json b/static/package.json index 27ea218a..81db3a61 100644 --- a/static/package.json +++ b/static/package.json @@ -1,13 +1,11 @@ { - "dependencies": { - "browserify": "^17.0.0", - "tsify": "^5.0.4" - }, "devDependencies": { - "@types/uuid": "^8.3.1", - "uuid": "^8.3.2" + "@types/uuid": "^8.3.1" }, "optionalDependencies": { "madge": "^5.0.1" + }, + "dependencies": { + "uuid": "^8.3.2" } } diff --git a/static/script.ts b/static/script.ts index ec771773..9238d834 100644 --- a/static/script.ts +++ b/static/script.ts @@ -6,7 +6,7 @@ import { import { vcal_objects, event_calendar_mapping } from './globals' import { EventCreator } from './event-creator' import { PopupElement, setup_popup_element } from './components/popup-element' -import { initialize_components } from './elements' +import { initialize_components } from './components' /* calp specific stuff diff --git a/static/server_connect.ts b/static/server_connect.ts index d1a544eb..29f5bab2 100644 --- a/static/server_connect.ts +++ b/static/server_connect.ts @@ -64,7 +64,7 @@ async function create_event(event: VEvent) { return; } - console.log('calendar=', calendar/*, xml*/); + console.log('calendar =', atob(calendar)/*, xml*/); let data = new URLSearchParams(); data.append("cal", calendar); diff --git a/static/style.scss b/static/style.scss index 578288b4..d5920f79 100644 --- a/static/style.scss +++ b/static/style.scss @@ -819,7 +819,7 @@ popup-element { /* some form of sensible minimi and default size for the popup (s main area). */ min-width: 150px; width: 350px; - height: 250px; + height: 300px; } } diff --git a/static/user/user-additions.js b/static/user/user-additions.js index 7291f232..3a2951e0 100644 --- a/static/user/user-additions.js +++ b/static/user/user-additions.js @@ -1,4 +1,4 @@ -window.formatters.set('description', (el, ev, d) => { +window.formatters.set('description', async (el, ev, d) => { if (ev.getProperty('X-MICROSOFT-SKYPETEAMSMEETINGURL')) { /* parse Microsoft Teams meeting entries */ /* Replace lines with propper <hr> tags */ @@ -32,7 +32,7 @@ window.formatters.set('description', (el, ev, d) => { idx = match.index + match[0].length } children.push(d.substring(idx)); - el.replaceChildren(...children); + el.replaceChildren(...children); } else if (/<\/?\w+( +\w+(=["']?\w+["']?)?)* *\/?>/.exec(d)) { /* Assume that the text is HTML if it contains something which looks like an HTML tag */ @@ -68,37 +68,41 @@ window.formatters.set('description', (el, ev, d) => { window.salar = new Promise((resolve, reject) => fetch('/static/user/salar.json') - .then(resp => { if (! resp.ok) reject("404"); else resp.json() }) + .then(resp => ! resp.ok ? reject("404") : resp.json()) .then(d => resolve(d)) .catch(err => reject(err)) ) - window.formatters.set('location', async function(el, _, d) { - let rx = /Lokal: (.*)/ - let m = rx.exec(d) - if (! m) { - el.textContent = d; - return; - } + let salar; try { - let salar = await window.salar; + salar = await window.salar; } catch (e) { console.warn("Location formatter failed", e); return; } - 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}` + let rx = /Lokal: ([A-Za-z0-9]*)?/g + + let idx = 0; + let children = [] + for (let match of d.matchAll(rx)) { + children.push(d.substring(idx, match.index)) + let name = match[1] + let frag = salar[name.toUpperCase()]; + if (frag) { + let anch = document.createElement('a'); + anch.href = `https://old.liu.se/karta/${frag}` + anch.target = '_blank' + anch.textContent = name; + children.push('Lokal: '); + children.push(anch); + } else { + children.push(`Lokal: ${name}`) + } + idx = match.index + match[0].length } + children.push(d.substring(idx)); + el.replaceChildren(...children) }) diff --git a/static/vevent.ts b/static/vevent.ts index 6a2c6f0f..f3606f70 100644 --- a/static/vevent.ts +++ b/static/vevent.ts @@ -9,7 +9,7 @@ export { /* Something which can be redrawn */ interface Redrawable extends HTMLElement { - redraw: ((data: VEvent) => void) + redraw(data: VEvent): void } function isRedrawable(x: HTMLElement): x is Redrawable { @@ -26,7 +26,7 @@ class VEventValue { value: any parameters: Map<string, any> - constructor(type: ical_type, value: any, parameters = new Map()) { + constructor(type: ical_type, value: any, parameters = new Map) { this.type = type; this.value = value; this.parameters = parameters; @@ -37,7 +37,7 @@ class VEventValue { let v = this.value; switch (this.type) { case 'binary': - /* TOOD */ + /* TODO */ value = 'BINARY DATA GOES HERE'; break; case 'date-time': @@ -76,7 +76,7 @@ class VEventValue { } } -/* maybe ... */ +/* TODO maybe ... */ class VEventDuration extends VEventValue { } @@ -514,7 +514,7 @@ function xml_to_vcal(xml: Element): VEvent { let property_map: Map<string, VEventValue | VEventValue[]> = new Map; if (properties) { - property_loop: + /* property_loop: */ for (var i = 0; i < properties.childElementCount; i++) { let tag = properties.childNodes[i]; if (!(tag instanceof Element)) continue; |