aboutsummaryrefslogtreecommitdiff
path: root/static/ts/vevent.ts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--static/ts/vevent.ts (renamed from static/vevent.ts)173
1 files changed, 154 insertions, 19 deletions
diff --git a/static/vevent.ts b/static/ts/vevent.ts
index f3606f70..6aaa6984 100644
--- a/static/vevent.ts
+++ b/static/ts/vevent.ts
@@ -2,28 +2,49 @@ import { ical_type, valid_input_types, JCal, JCalProperty, ChangeLogEntry } from
import { parseDate } from './lib'
export {
- VEvent, xml_to_vcal,
RecurrenceRule,
+ Redrawable,
+ VEvent,
+ VEventValue,
+ freqType,
isRedrawable,
+ list_values,
+ weekday,
+ xml_to_vcal,
}
-/* Something which can be redrawn */
+/** Something which can be redrawn */
interface Redrawable extends HTMLElement {
+ /** Method which will be called upon a redraw request. */
redraw(data: VEvent): void
}
+/** Checks if the given element is an instance of Redrawable. */
function isRedrawable(x: HTMLElement): x is Redrawable {
return 'redraw' in x
}
+/**
+ A single value from a vcomponent.
+ This is basically a type tagged tuple, with an optional map of parameters.
+*/
class VEventValue {
+ /** The value type of the contained value. */
type: ical_type
- /* value should NEVER be a list, since multi-valued properties should
- be split into multiple VEventValue objects! */
+ /**
+ The actual value.
+
+ Should NEVER be a list, since those are coded as
+ lists of `VEventValue`:s in `Vevent.properties`
+ */
value: any
+
+ /**
+ VComponent parameters attached to the value.
+ */
parameters: Map<string, any>
constructor(type: ical_type, value: any, parameters = new Map) {
@@ -32,6 +53,10 @@ class VEventValue {
this.parameters = parameters;
}
+ /**
+ * The return value is *almost* a `JCalProperty`, just without
+ * the field name.
+ */
to_jcal(): [Record<string, any>, ical_type, any] {
let value;
let v = this.value;
@@ -77,40 +102,71 @@ class VEventValue {
}
/* TODO maybe ... */
-class VEventDuration extends VEventValue {
-}
+// class VEventDuration extends VEventValue {
+// }
+/** VComponent properties which contain lists */
type list_values
= 'categories' | 'resources' | 'freebusy' | 'exdate' | 'rdate'
| 'CATEGORIES' | 'RESOURCES' | 'FREEBUSY' | 'EXDATE' | 'RDATE';
-/*
- Abstract representation of a calendar event (or similar).
-All "live" calendar data in the frontend should live in an object of this type.
+/**
+ This class is the data container for the underlying VEVENT objects in the
+ backend calendar files. They also keep track on all Web Components which
+ wants to render part of the event.
+
+ Note that despite the name this component isn't limited to VEVENT:s, but is
+ used for all VComponents in the tree. This means that even calendars and
+ alarms can be instances of this class.
+
+ Property access is done through `getProperty` and `setProperty` (properties
+ are things such as 'SUMMARY', 'DTSTART', ...)
*/
class VEvent {
- /* Calendar properties */
+ /**
+ Properties bound directly on this object.
+
+ These are things such as 'DTSTART', 'SUMMARY', ...
+ */
private properties: Map<string, VEventValue | VEventValue[]>
- /* Children (such as alarms for events) */
+ /**
+ Children to this component.
+
+ Valid children depends on the type. For example, for calendars this is
+ primarily events, while for events it's alarm components
+ */
components: VEvent[]
- /* HTMLElements which wants to be redrawn when this object changes.
- Elements can be registered with the @code{register} method.
+ /**
+ HTMLElements which wants to be redrawn when this object changes.
+ Elements can be registered with the `register` method.
*/
registered: Redrawable[]
#calendar: string | null = null;
+ /**
+ * Every write through getProperty gets logged here, and can be
+ * consumed. Hopefully this will one day turn into an undo system.
+ * TODO ref ChangeLogEntry.
+ */
#changelog: ChangeLogEntry[] = []
- /* Iterator instead of direct return to ensure the receiver doesn't
- modify the array */
+ /**
+ The changelog for this component.
+
+ An iterator is returned rather than an array, to ensure modifications are
+ impossible.
+ */
get changelog(): IterableIterator<[number, ChangeLogEntry]> {
return this.#changelog.entries();
}
+ /**
+ Add an entry to the changelog.
+ */
addlog(entry: ChangeLogEntry) {
let len = this.#changelog.length
let last = this.#changelog[len - 1]
@@ -135,6 +191,17 @@ class VEvent {
}
}
+ /**
+ Construct a new Component.
+
+ @param properties
+ Initial properties for the component
+
+ @param components
+ Initial children for the component
+
+ TODO where is the type of the component registered?
+ */
constructor(
properties: Map<string, VEventValue | VEventValue[]> = new Map(),
components: VEvent[] = []
@@ -156,6 +223,19 @@ class VEvent {
// getProperty(key: 'categories'): string[] | undefined
+ /**
+ * Returns the value of the given property if set, or undefined otherwise.
+ *
+ * For the keys
+ *
+ * - `'CATEGORIES'`,
+ * - `'RESOURCES'`,
+ * - `'FREEBUSY'`,
+ * - `'EXDATE'`, and
+ * - `'RDATE'`
+ *
+ * instead returns a list list of values.
+ */
getProperty(key: string): any | any[] | undefined {
key = key.toUpperCase()
let e = this.properties.get(key);
@@ -166,11 +246,12 @@ class VEvent {
return e.value;
}
+ /** Returns an iterator of all our properties. */
get boundProperties(): IterableIterator<string> {
return this.properties.keys()
}
- private setPropertyInternal(key: string, value: any, type?: ical_type) {
+ #setPropertyInternal(key: string, value: any, type?: ical_type) {
function resolve_type(key: string, type?: ical_type): ical_type {
if (type) {
return type;
@@ -228,24 +309,40 @@ class VEvent {
setProperty(key: list_values, value: any[], type?: ical_type): void;
setProperty(key: string, value: any, type?: ical_type): void;
+ /**
+ * Sets the given property to the given value. If type is given it's
+ * stored alongside the value, possibly updating what is already
+ * there. Do however note that no validation between the given type and
+ * the type of the value is done.
+ *
+ * `value` may also be a list, but should only be so for the keys
+ * mentioned in `getProperty`.
+ *
+ * After the value is set, `redraw` is called on all registered
+ * objects, notifying them of the change.
+ */
setProperty(key: string, value: any, type?: ical_type) {
- this.setPropertyInternal(key, value, type);
+ this.#setPropertyInternal(key, value, type);
for (let el of this.registered) {
el.redraw(this);
}
}
+ /**
+ * Equivalent to running `setProperty` for each element in the input
+ * list, but only calls `redraw` once at the end.
+ */
setProperties(pairs: [string, any, ical_type?][]) {
for (let pair of pairs) {
- this.setPropertyInternal(...pair);
+ this.#setPropertyInternal(...pair);
}
for (let el of this.registered) {
el.redraw(this);
}
}
-
+ /** The name of the calendar which this event belongs to. */
set calendar(calendar: string | null) {
this.addlog({
type: 'calendar',
@@ -259,18 +356,29 @@ class VEvent {
}
}
+ /**
+ Get the name of the containing calendar for this component.
+
+ This is only valid for VEVENT components (I think)
+ */
get calendar(): string | null {
return this.#calendar;
}
+ /**
+ * Register something redrawable, which will be notified whenever this
+ * VEvents data is updated.
+ */
register(htmlNode: Redrawable) {
this.registered.push(htmlNode);
}
+ /** Stop recieving redraw events on the given component. */
unregister(htmlNode: Redrawable) {
this.registered = this.registered.filter(node => node !== htmlNode)
}
+ /** Converts the object to JCal data. */
to_jcal(): JCal {
let out_properties: JCalProperty[] = []
console.log(this.properties);
@@ -300,6 +408,7 @@ class VEvent {
}
}
+/** Helper procedure when converting xml to vcal */
function make_vevent_value(value_tag: Element): VEventValue {
/* TODO parameters */
return new VEventValue(
@@ -313,25 +422,49 @@ function make_vevent_value(value_tag: Element): VEventValue {
+/** Different frequency internals for recurrence rules. */
type freqType = 'SECONDLY' | 'MINUTELY' | 'HOURLY' | 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY'
+
+/** Alternatives for when a week start, for recurrence rules */
type weekday = 'MO' | 'TU' | 'WE' | 'TH' | 'FR' | 'SA' | 'SU'
+/**
+ A recurrence rule.
+
+ The basic semantics of this class is borrowed from RFC 5545, and maps 1-to-1
+ on those instances. See individual fields for mappings.
+ */
class RecurrenceRule {
+ /** The type of frequency of this rule */
freq?: freqType
+ /** Final instance of this rule. */
until?: Date
+ /** Maximum number of recurrences for this rule */
count?: number
+ /** The multiplier to `freq` */
interval?: number
+ /** Which seconds are relevant for this rule */
bysecond?: number[]
+ /** Which minutes are relevant for this rule */
byminute?: number[]
+ /** Which hours are relevant for this rule */
byhour?: number[]
+ /** Which weekday or weekday offsets are relevant for this rule */
byday?: (weekday | [number, weekday])[]
+ /** Which month days are relevant for this rule */
bymonthday?: number[]
+ /** Which year days are relevant for this rule */
byyearday?: number[]
+ /** Which week number are relevant for this rule */
byweekno?: number[]
+ /** Which months relevant for this rule (interval 1-12) */
bymonth?: number[]
+ /** TODO see the RFC */
bysetpos?: number[]
+ /** Which day the week start, according to this rule */
wkst?: weekday
+ /** Converts ourselves to JCal data. */
to_jcal(): Record<string, any> {
let obj: any = {}
if (this.freq) obj['freq'] = this.freq;
@@ -368,6 +501,7 @@ class RecurrenceRule {
}
}
+/** Parse a XCAL recurrence rule into a RecurrenceRule object. */
function xml_to_recurrence_rule(xml: Element): RecurrenceRule {
let rr = new RecurrenceRule;
@@ -507,6 +641,7 @@ function make_vevent_value_(value_tag: Element): string | boolean | Date | numbe
}
}
+/** Parse a complete XCAL object into a JS VEvent object. */
function xml_to_vcal(xml: Element): VEvent {
/* xml MUST have a VEVENT (or equivalent) as its root */
let properties = xml.getElementsByTagName('properties')[0];