aboutsummaryrefslogtreecommitdiff
path: root/static
diff options
context:
space:
mode:
Diffstat (limited to 'static')
-rw-r--r--static/Makefile28
-rw-r--r--static/components.ts (renamed from static/elements.ts)0
-rw-r--r--static/components/date-time-input.ts2
-rw-r--r--static/components/input-list.ts2
-rw-r--r--static/components/popup-element.ts7
-rw-r--r--static/components/slider.ts18
-rw-r--r--static/components/vevent-block.ts2
-rw-r--r--static/components/vevent-description.ts11
-rw-r--r--static/components/vevent-edit.ts46
-rw-r--r--static/components/vevent.ts4
-rw-r--r--static/event-creator.ts10
-rw-r--r--static/formatters.ts26
-rw-r--r--static/globals.ts2
-rwxr-xr-xstatic/make-watch5
-rw-r--r--static/package.json10
-rw-r--r--static/script.ts2
-rw-r--r--static/server_connect.ts2
-rw-r--r--static/style.scss2
-rw-r--r--static/user/user-additions.js48
-rw-r--r--static/vevent.ts10
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;