aboutsummaryrefslogtreecommitdiff
path: root/static
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2020-12-18 23:18:47 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2020-12-18 23:18:47 +0100
commit0e1eb02c1a6d596bd1e620b9c9bb13e6c125f5cb (patch)
treefef88b7581e8e3f2b41f739137ad42859e67915c /static
parentAdd convert entry-point. (diff)
downloadcalp-0e1eb02c1a6d596bd1e620b9c9bb13e6c125f5cb.tar.gz
calp-0e1eb02c1a6d596bd1e620b9c9bb13e6c125f5cb.tar.xz
Start work on jcal system.
Diffstat (limited to '')
-rw-r--r--static/input_list.js6
-rw-r--r--static/jcal.js156
-rw-r--r--static/lib.js8
-rw-r--r--static/recur.js0
-rw-r--r--static/rrule.js36
-rw-r--r--static/script.js71
-rw-r--r--static/server_connect.js60
-rw-r--r--static/types.js11
8 files changed, 274 insertions, 74 deletions
diff --git a/static/input_list.js b/static/input_list.js
index 9397e6ee..4430154a 100644
--- a/static/input_list.js
+++ b/static/input_list.js
@@ -75,7 +75,7 @@ function init_input_list() {
if (lst.dataset.bindby) {
lst.get_value = lst.dataset.bindby;
} else if (lst.dataset.joinby) {
- lst.get_value = get_value(lst.dataset.joinby);
+ lst.get_value = get_get_value(lst.dataset.joinby);
} else {
lst.get_value = get_get_value();
}
@@ -101,8 +101,8 @@ function init_input_list() {
const get_get_value = (join=',') => function () {
return [...this.querySelectorAll('input')]
.map(x => x.value)
- .filter(x => x != '')
- .join(join);
+ .filter(x => x != '');
+ // .join(join);
}
/* -------------------------------------------------- */
diff --git a/static/jcal.js b/static/jcal.js
new file mode 100644
index 00000000..ebf80cbd
--- /dev/null
+++ b/static/jcal.js
@@ -0,0 +1,156 @@
+function jcal_type_to_xcal(doc, type, value) {
+ let el = doc.createElementNS(xcal, type);
+ switch (type) {
+ case 'boolean':
+ el.innerHTML = value ? "true" : "false";
+ break;
+
+ case 'float':
+ case 'integer':
+ el.innerHTML = '' + value;
+ break;
+
+ case 'period':
+ let [start, end] = value;
+ let startEl = doc.createElementNS(xcal, 'start');
+ startEl.innerHTML = start;
+ let endEL;
+ if (end.find('P')) {
+ endEl = doc.createElementNS(xcal, 'duration');
+ } else {
+ endEl = doc.createElementNS(xcal, 'end');
+ }
+ endEl.innerHTML = end;
+ el.appendChild(startEl);
+ el.appendChild(endEl);
+ break;
+
+ case 'recur':
+ el.appendChild(recur_jcal_to_rrule(value).asXcal(doc));
+ break;
+
+ case 'date':
+ case 'time':
+ case 'date-time':
+
+ case 'duration':
+
+ case 'binary':
+ case 'text':
+ case 'uri':
+ case 'cal-address':
+ case 'utc-offset':
+ el.innerHTML = value;
+ break;
+
+ default:
+ /* TODO error */
+ }
+ return el;
+}
+
+function jcal_property_to_xcal_property(doc, jcal) {
+ let [propertyName, params, type, ...values] = jcal;
+
+ let tag = doc.createElementNS(xcal, propertyName);
+
+ if (params !== {}) {
+ let params = doc.createElementNS(xcal, params);
+ for (var key in params) {
+ let el = doc.createElementNS(xcal, key);
+
+ for (let v of asList(params[key])) {
+ let text = doc.createElementNS(xcal, 'text');
+ text.innerHTML = '' + v;
+ el.appendChild(text);
+ }
+
+ params.appendChild(el);
+ }
+
+ tag.appendChild(params);
+ }
+
+ // let typeEl = doc.createElementNS(xcal, type);
+
+ switch (propertyName) {
+ case 'geo':
+ if (type == 'float') {
+ // assert values[0] == [x, y]
+ let [x, y] = values[0];
+ let lat = doc.createElementNS(xcal, 'latitude')
+ let lon = doc.createElementNS(xcal, 'longitude')
+ lat.innerHTML = x;
+ lon.innerHTML = y;
+ tag.appendChild(lat);
+ tag.appendChild(lon);
+ } else {
+ /* TODO, error */
+ }
+ break;
+ case 'request-status':
+ if (type == 'text') {
+ // assert values[0] instanceof Array
+ let [code, desc, ...data] = values[0];
+ let codeEl = doc.createElementNS(xcal, 'code')
+ code.innerHTML = code;
+ tag.appendChild(codeEl);
+
+
+ let descEl = doc.createElementNS(xcal, 'description')
+ desc.innerHTML = desc;
+ tag.appendChild(descEl);
+
+ if (data !== []) {
+ data = data[0];
+ let dataEl = doc.createElementNS(xcal, 'data')
+ data.innerHTML = data;
+ tag.appendChild(dataEl);
+ }
+ } else {
+ /* TODO, error */
+ }
+ break;
+ default:
+ for (let value of values) {
+ tag.appendChild(jcal_type_to_xcal(doc, type, value))
+ }
+ }
+
+ return tag;
+}
+
+
+function jcal_to_xcal(...jcals) {
+ let doc = document.implementation.createDocument(xcal, 'icalendar');
+ for (let jcal of jcals) {
+ doc.documentElement.appendChild(jcal_to_xcal_inner(doc, jcal));
+ }
+ return doc;
+}
+
+function jcal_to_xcal_inner(doc, jcal) {
+ let [tagname, properties, components] = jcal;
+
+ let xcal_tag = doc.createElementNS(xcal, tagname);
+
+ /* I'm not sure if the properties and components tag should be left out
+ when empty. It should however NOT be an error to leave them in.
+ */
+
+ let xcal_properties = doc.createElementNS(xcal, 'properties');
+ for (let property of properties) {
+ xcal_properties.appendChild(jcal_property_to_xcal_property(property));
+ }
+
+ let xcal_children = doc.createElementNS(xcal, 'components');
+ for (let child of components) {
+ xcal_children.appendChild(jcal_to_xcal_inner(doc, child));
+ }
+
+ xcal_tag.appendChild(xcal_properties);
+ xcal_tag.appendChild(xcal_children);
+
+ return xcal_tag;
+
+}
diff --git a/static/lib.js b/static/lib.js
index ab279353..84ca97c8 100644
--- a/static/lib.js
+++ b/static/lib.js
@@ -117,6 +117,14 @@ function setVar(str, val) {
}
+function asList(thing) {
+ if (thing instanceof Array) {
+ return thing;
+ } else {
+ return [thing];
+ }
+}
+
function datepad(thing, width=2) {
return (thing + "").padStart(width, "0");
diff --git a/static/recur.js b/static/recur.js
deleted file mode 100644
index e69de29b..00000000
--- a/static/recur.js
+++ /dev/null
diff --git a/static/rrule.js b/static/rrule.js
index abc648af..f11cc640 100644
--- a/static/rrule.js
+++ b/static/rrule.js
@@ -8,6 +8,14 @@ function recur_xml_to_rrule(dom_element) {
return rr;
}
+function recur_jcal_to_rrule(jcal) {
+ let rr = new RRule;
+ for (var key in jcal) {
+ rr[key] = jcal[key];
+ }
+ return rr;
+}
+
class RRule {
/* direct access to fields is fine */
@@ -17,7 +25,9 @@ class RRule {
fields = ['freq', 'until', 'count', 'interval',
'bysecond', 'byminute', 'byhour',
'bymonthday', 'byyearday', 'byweekno',
- 'bymonth', 'bysetpos', 'wkst']
+ 'bymonth', 'bysetpos', 'wkst',
+ 'byday'
+ ]
constructor() {
@@ -49,16 +59,30 @@ class RRule {
this.listeners[field].push(proc);
}
- asXcal() {
+ asXcal(doc) {
/* TODO empty case */
- let str = "<recur>";
+ // let str = "<recur>";
+ let root = doc.createElementNS(xcal, 'recur');
+ for (let f of this.fields) {
+ let v = this.fields[f];
+ if (! v) continue;
+ let tag = doc.createElementNS(xcal, f);
+ /* TODO type formatting */
+ tag.innerHTML = `${v}`;
+ root.appendChild(tag);
+ }
+ return root;
+ }
+
+ asJcal() {
+ let obj = {};
for (let f of this.fields) {
let v = this.fields[f];
if (! v) continue;
- str += `<${f}>${v}</${f}>`;
+ /* TODO special formatting for some types */
+ obj[f] = v;
}
- str += "</recur>";
- return str;
+ return obj;
}
/*
diff --git a/static/script.js b/static/script.js
index 3a3148b5..596caf6d 100644
--- a/static/script.js
+++ b/static/script.js
@@ -382,7 +382,9 @@ window.onload = function () {
*/
function get_property(el, field, default_value) {
if (! el.properties) {
+ /* TODO only have construction once */
el.properties = {};
+ el.properties.ical_properties = new Set()
}
if (! el.properties["_slot_" + field]) {
@@ -423,6 +425,7 @@ function get_property(el, field, default_value) {
function bind_properties (el, wide_event=false) {
el.properties = {}
+ el.properties.ical_properties = new Set()
let popup = popup_from_event(el);
// let children = el.getElementsByTagName("properties")[0].children;
@@ -523,6 +526,9 @@ function bind_properties (el, wide_event=false) {
}]);
}
+ for (let property of property_names) {
+ el.properties.ical_properties.add(property)
+ }
/* icalendar properties */
for (let child of el.querySelector("vevent > properties").children) {
@@ -531,29 +537,15 @@ function bind_properties (el, wide_event=false) {
let field = child.tagName;
let lst = get_property(el, field);
+ el.properties.ical_properties.add(field)
/* Bind vcomponent fields for this event */
for (let s of el.querySelectorAll(`${field} > :not(parameters)`)) {
- lst.push([s, (s, v) => {
- if (v instanceof Date) {
- if (v.isWholeDay) {
- let str = v.format('~Y-~m-~d');
- child.innerHTML = `<date>${str}</date>`;
- } else {
- let str = v.format('~Y-~m-~dT~H:~M:00~Z');
- child.innerHTML = `<date-time>${str}</date-time>`;
- }
- } else if (v instanceof RRule) {
- child.innerHTML = v.asXcal();
- } else {
- /* assume that type already is correct */
- s.innerHTML = v;
- }
- }]);
-
/* Binds value from XML-tree to javascript object
[parsedate]
+
+ TODO capture xcal type here, to enable us to output it to jcal later.
*/
switch (field) {
case 'rrule':
@@ -565,29 +557,6 @@ function bind_properties (el, wide_event=false) {
}
}
- /* Dynamicly add or remove the <location/> and <description/> elements
- from the <vevent><properties/> list.
-
- TODO generalize this to all fields, /especially/ those which are
- dynamicly added.
- */
- for (let field of ['location', 'description', 'categories']) {
- get_property(el, field).push(
- [el.querySelector('vevent > properties'),
- (s, v) => {
- let slot = s.querySelector(field);
- if (v === '' && slot) {
- slot.remove();
- } else {
- if (! slot) {
- /* finns det verkligen inget bättre sätt... */
- s.innerHTML += `<${field}><text/></${field}>`;
- }
- s.querySelector(`${field} > text`).innerHTML = v;
- }
- }]);
- }
-
/* set up graphical display changes */
let container = el.closest(".event-container");
if (container === null) {
@@ -617,28 +586,6 @@ function bind_properties (el, wide_event=false) {
}
- /* Update XML on rrule field change */
- if (el.properties.rrule) {
- for (let f of el.properties.rrule.fields) {
- el.properties.rrule.addListener(
- f, v => {
- console.log(v);
- let recur = el.querySelector('rrule recur');
- let field = recur.querySelector(f);
- if (field) {
- if (! v) {
- field.remove();
- } else {
- field.innerHTML = v;
- }
- } else {
- if (v) recur.innerHTML += `<${f}>${v}</${f}>`;
- }
- });
- }
- }
-
-
/* ---------- Calendar ------------------------------ */
if (! el.dataset.calendar) {
diff --git a/static/server_connect.js b/static/server_connect.js
index e789d72c..6c4e4496 100644
--- a/static/server_connect.js
+++ b/static/server_connect.js
@@ -23,14 +23,68 @@ async function remove_event (element) {
async function create_event (event) {
- let xml = event.getElementsByTagName("icalendar")[0].outerHTML
+ // let xml = event.getElementsByTagName("icalendar")[0].outerHTML
let calendar = event.properties.calendar;
- console.log(calendar, xml);
+ console.log(calendar/*, xml*/);
let data = new URLSearchParams();
data.append("cal", calendar);
- data.append("data", xml);
+ // data.append("data", xml);
+
+ for (let prop of event.properties.ical_properties) {
+ let v = event.properties[prop];
+ if (v !== undefined) {
+ [prop, {}, /*type*/, v];
+ /* TODO , here */
+
+ let type = 'text';
+ let value;
+
+ if (v instanceof Array) {
+ } else if (v instanceof Date) {
+ if (v.isWholeDay) {
+ type = 'date';
+ value = v.format("~Y-~m-~d");
+ } else {
+ type = 'date-time';
+ /* TODO TZ */
+ value = v.format("~Y-~m~dT~H:~M:~S");
+ }
+ } else if (v === true || v === false) {
+ type = 'boolean';
+ value = v;
+ } else if (typeof(v) == 'number') {
+ /* TODO float or integer */
+ type = 'integer';
+ value = v;
+ }
+ /* TODO period */
+ /* TODO recur */
+ else {
+ /* text types */
+ }
+
+ }
+ }
+
+
+ let jcal =
+ ['vcalendar',
+ ['vevent',
+ [
+ ['summary', {}, 'text', 'Example summary'],
+ ],
+ []
+ ]
+ ];
+
+ let doc = jcal_to_xcal(jcal);
+ console.log(doc);
+
+ console.log(event.properties);
+
+ return;
let response = await fetch ( '/insert', {
method: 'POST',
diff --git a/static/types.js b/static/types.js
index cfed8584..9a4aa01c 100644
--- a/static/types.js
+++ b/static/types.js
@@ -15,6 +15,17 @@ let all_types = [
'boolean',
]
+let property_names = [
+ 'calscale', 'method', 'prodid', 'version', 'attach', 'categories',
+ 'class', 'comment', 'description', 'geo', 'location', 'percent-complete',
+ 'priority', 'resources', 'status', 'summary', 'completed', 'dtend', 'due',
+ 'dtstart', 'duration', 'freebusy', 'transp', 'tzid', 'tzname', 'tzoffsetfrom',
+ 'tzoffsetto', 'tzurl', 'attendee', 'contact', 'organizer', 'recurrence-id',
+ 'related-to', 'url', 'uid', 'exdate', 'exrule', 'rdate', 'rrule', 'action',
+ 'repeat', 'trigger', 'created', 'dtstamp', 'last-modified', 'sequence', 'request-status'
+];
+
+
let valid_fields = {
'VCALENDAR': ['PRODID', 'VERSION', 'CALSCALE', 'METHOD'],
'VEVENT': ['DTSTAMP', 'UID', 'DTSTART', 'CLASS', 'CREATED',