aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2020-11-21 00:13:20 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2020-11-21 00:13:20 +0100
commit4a4c3e42151da7a83bd863d31d8ad5f8f4d07396 (patch)
tree84a91b007d75979c102bdbc35ac8c7a721f75cb3
parentMerge branch 'edit-rrule' into js (diff)
downloadcalp-4a4c3e42151da7a83bd863d31d8ad5f8f4d07396.tar.gz
calp-4a4c3e42151da7a83bd863d31d8ad5f8f4d07396.tar.xz
Further work on breakout and rrule.
Diffstat (limited to '')
-rw-r--r--module/calp/html/vcomponent.scm16
-rw-r--r--static/binders.js96
-rw-r--r--static/clock.js10
-rw-r--r--static/recur.js0
-rw-r--r--static/rrule.js57
-rw-r--r--static/script.js117
6 files changed, 219 insertions, 77 deletions
diff --git a/module/calp/html/vcomponent.scm b/module/calp/html/vcomponent.scm
index 60f270f7..b8bd3bb8 100644
--- a/module/calp/html/vcomponent.scm
+++ b/module/calp/html/vcomponent.scm
@@ -60,7 +60,8 @@
;; (format (current-error-port) "fmt-single-event: ~a~%" (prop ev 'X-HNH-FILENAME))
`(div (@ ,@(assq-merge
attributes
- `((class " eventtext summary-tab "
+ `((data-bindby "bind_view")
+ (class " eventtext summary-tab "
,(when (and (prop ev 'PARTSTAT)
(eq? 'TENTATIVE (prop ev 'PARTSTAT)))
" tentative ")))))
@@ -145,7 +146,8 @@
(define*-public (fmt-for-edit ev
optional: (attributes '())
key: (fmt-header list))
- `(div (@ (class " eventtext edit-tab "))
+ `(div (@ (class " eventtext edit-tab ")
+ (data-bindby "bind_edit"))
(form (@ (class "edit-form"))
(div (@ (class "dropdown-goes-here")))
(h3 (input (@ (type "text")
@@ -174,7 +176,10 @@
,@(with-label
"Heldag?"
- `(input (@ (type "checkbox") (style "display:none")
+ `(input (@ (type "checkbox")
+ (class "bind")
+ (data-bindby "bind_wholeday")
+ (style "display:none")
(name "wholeday"))))
(input (@ (type "time")
@@ -306,6 +311,7 @@
extra-attributes
`((id ,(html-id ev))
(data-calendar ,(html-attr (or (prop (parent ev) 'NAME) "unknown")))
+ ;; (data-bindon "bind_view")
(class "event CAL_" ,(html-attr (or (prop (parent ev) 'NAME)
"unknown"))
,(when (and (prop ev 'PARTSTAT)
@@ -365,7 +371,9 @@
(define (editable-repeat-info event)
`(div (@ (class "eventtext"))
(h2 "Upprepningar")
- (table (@ (class "recur-components"))
+ (table (@ (class "recur-components bind")
+ (name "rrule")
+ (data-bindby "bind_recur"))
,@(map ; (@@ (vcomponent recurrence internal) map-fields)
(lambda (key )
`(tr (@ (class ,key)) (th ,key)
diff --git a/static/binders.js b/static/binders.js
new file mode 100644
index 00000000..d030084d
--- /dev/null
+++ b/static/binders.js
@@ -0,0 +1,96 @@
+
+/* vcalendar element */
+
+function bind_recur(el, e) {
+ /* todo bind default slots of rrule */
+
+ let p = get_property(el, 'rrule', new rrule);
+ let rrule = el.rrule;
+
+ for (let rr of e.querySelectorAll('.bind-rr')) {
+ rrule.addListener(rr.dataset.name, v => {
+ /* TODO Different depending on tag type */
+ /* TODO scoope of rr? */
+ if (! v) {
+ rr.value = '';
+ } else {
+ rr.vaule = v;
+ }
+ });
+ }
+
+ p.push([e, function (s, v) {
+ /* v is an rrule object */
+ for (let f of v.fields) {
+ let input_field = s.querySelector(`[name=${f}]`);
+ switch (input_field.tagName) {
+ case 'input':
+ input_field.value = v;
+ break;
+ case 'select':
+ /* TODO */
+ break;
+ default:
+ if (input_field.classList.contains('date-time')) {
+ let date = input_field.querySelector('input[type=date]');
+ let time = input_field.querySelector('input[type=time]');
+ } else if (e.classList.contains('input-list')) {
+ } else {
+ throw Error();
+ }
+ }
+ }
+ }]);
+
+
+}
+
+function bind_edit(el, e) {
+ let p = get_property(el, e.dataset.property);
+ e.addEventListener('input', function () {
+ el.properties[e.dataset.property] = this.value;
+ });
+ let f;
+ switch (e.tagName) {
+ case 'input':
+ switch (e.type) {
+ case 'time': f = (s, v) => s.value = v.format("~H:~M"); break;
+ case 'date': f = (s, v) => s.value = v.format("~Y-~m-~d"); break;
+ // TODO remaining types cases
+ default: f = (s, v) => s.value = v;
+ }
+ p.push([e, f])
+ break;
+ case 'textarea':
+ f = (s, v) => s.innerHTML = v;
+ p.push([e, f])
+ break;
+ default:
+ alert("How did you get here??? " + e.tagName)
+ break;
+ }
+
+}
+
+function bind_view(el, e) {
+ let f = (s, v) => s.innerHTML = v.format(s.dataset && s.dataset.fmt);
+ get_property(el, e.dataset.property).push([e, f]);
+}
+
+
+function bind_wholeday(el, e) {
+ // let wholeday = popup.querySelector("input[name='wholeday']");
+ let popup = popup_from_event(el);
+ wholeday.addEventListener('click', function (event) {
+ for (let f of popup.querySelectorAll("input[type='time']")) {
+ f.disabled = wholeday.checked;
+ }
+
+ for (let f of ['dtstart', 'dtend']) {
+ let d = el.properties[f];
+ if (! d) continue; /* dtend optional */
+ d.isWholeDay = wholeday.checked;
+ el.properties[f] = d;
+ }
+ });
+}
diff --git a/static/clock.js b/static/clock.js
index badfd1db..40382faa 100644
--- a/static/clock.js
+++ b/static/clock.js
@@ -45,14 +45,14 @@ class SmallcalCellHighlight extends Clock {
}
update(now) {
- if (current_cell) {
- current_cell.style.border = "";
+ if (this.current_cell) {
+ this.current_cell.style.border = "";
}
- current_cell = this.small_cal.querySelector(
+ this.current_cell = this.small_cal.querySelector(
"time[datetime='" + now.format("~Y-~m-~d") + "']");
- current_cell.style.border = "1px solid black";
+ this.current_cell.style.border = "1px solid black";
}
}
@@ -65,6 +65,6 @@ class ButtonUpdater extends Clock {
}
update(now) {
- this.proc(e, now);
+ this.proc(this.el, now);
}
}
diff --git a/static/recur.js b/static/recur.js
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/static/recur.js
diff --git a/static/rrule.js b/static/rrule.js
new file mode 100644
index 00000000..30bed919
--- /dev/null
+++ b/static/rrule.js
@@ -0,0 +1,57 @@
+class RRule {
+
+ /* direct access to fields is fine */
+ /* setting them however requires methods, since there might
+ be listeners */
+
+ const fields = ['freq', 'until', 'count', 'interval',
+ 'bysecond', 'byminute', 'byhour',
+ 'bymonthday', 'byyearday', 'byweekno',
+ 'bymonth', 'bysetpos', 'wkst']
+
+ constructor() {
+
+ this.listeners = {}
+
+ for (let f of this.fields) {
+ this[f] = false;
+ Object.defineProperty(
+ this, f, {
+ get: () => this['_' + f];
+ set: (v) => {
+ this['_' + f] = v
+ for (let l of this.listeners[f]) {
+ l(v);
+ }
+ }
+ });
+ this.listeners[f] = [];
+ }
+ }
+
+ addListener(field, proc) {
+ this.listeners[field].append(proc);
+ }
+
+ asXcal() {
+ /* TODO empty case */
+ let str = "<recur>";
+ for (let f of this.fields) {
+ let v = this.fields[f];
+ if (! v) continue;
+ str += `<${f}>${v}</${f}>`;
+ }
+ str += "</recur>";
+ return str;
+ }
+
+ /*
+ asIcal() {
+ return this.fields
+ .map(f => [f, this.fields[f]])
+ .filter([_, v] => v)
+ .map(([k, v]) => `${k}=${v}`)
+ .join(';');
+ }
+ */
+};
diff --git a/static/script.js b/static/script.js
index ec956d5a..133140d2 100644
--- a/static/script.js
+++ b/static/script.js
@@ -426,85 +426,63 @@ function bind_properties (el, wide_event=false) {
// let children = el.getElementsByTagName("properties")[0].children;
/* actual component (not popup) */
+ /*
for (let e of el.querySelectorAll(".bind")) {
- let f = ((s, v) => s.innerHTML = v.format(s.dataset && s.dataset.fmt));
- get_property(el, e.dataset.property).push([e, f]);
}
+ */
- get_property(el, 'rrule');
- /* aside for rrule */
- for (let e of el.querySelectorAll('.recur-components .bind-rr')) {
-
- // e.name
-
- switch (e.tagName) {
- case 'input':
- e.value;
- break;
- case 'select':
- e.options[e.selectedIndex].value;
- break;
- default:
- if (e.classList.contains("date-time")) {
- let date = e.querySelector('input[type=date]');
- let time = e.querySelector('input[type=time]');
- } else if (e.classList.contains("input-list")) {
- e.get_value()
- } else {
- error();
- }
- }
- }
+ /* bind_recur */
/* primary display tab */
- for (let e of popup.querySelectorAll(".summary-tab .bind")) {
- let f = (s, v) => s.innerHTML = v.format(s.dataset && s.dataset.fmt);
- get_property(el, e.dataset.property).push([e, f]);
- }
-
- /* edit tab */
- for (let e of popup.querySelectorAll(".edit-tab .bind")) {
- let p = get_property(el, e.dataset.property);
- e.addEventListener('input', function () {
- el.properties[e.dataset.property] = this.value;
- });
- let f;
- switch (e.tagName) {
- case 'input':
- switch (e.type) {
- case 'time': f = (s, v) => s.value = v.format("~H:~M"); break;
- case 'date': f = (s, v) => s.value = v.format("~Y-~m-~d"); break;
- // TODO remaining types cases
- default: f = (s, v) => s.value = v;
- }
- p.push([e, f])
- break;
- case 'textarea':
- f = (s, v) => s.innerHTML = v;
- p.push([e, f])
- break;
- default:
- alert("How did you get here??? " + e.tagName)
- break;
+ let p;
+ for (let e of [...popup.querySelectorAll(".bind"),
+ ...el.querySelectorAll('.bind')]) {
+ if ((p = e.closest('[data-bindby=*]'))) {
+ p.dataset.bindby(el, e);
+ } else {
+ let f = ((s, v) => s.innerHTML = v.format(s.dataset && s.dataset.fmt));
+ get_property(el, e.dataset.property).push([e, f]);
}
}
- /* checkbox for whole day */
- let wholeday = popup.querySelector("input[name='wholeday']");
- wholeday.addEventListener('click', function (event) {
- for (let f of popup.querySelectorAll("input[type='time']")) {
- f.disabled = wholeday.checked;
- }
+ // for (let e of popup.querySelectorAll(".summary-tab .bind")) {
+ // /* bind_view
+ // let f = (s, v) => s.innerHTML = v.format(s.dataset && s.dataset.fmt);
+ // get_property(el, e.dataset.property).push([e, f]);
+ // */
+ // }
- for (let f of ['dtstart', 'dtend']) {
- let d = el.properties[f];
- if (! d) continue; /* dtend optional */
- d.isWholeDay = wholeday.checked;
- el.properties[f] = d;
- }
- });
+ /* edit tab */
+ // for (let e of popup.querySelectorAll(".edit-tab .bind")) {
+ // /* bind-edit
+ // let p = get_property(el, e.dataset.property);
+ // e.addEventListener('input', function () {
+ // el.properties[e.dataset.property] = this.value;
+ // });
+ // let f;
+ // switch (e.tagName) {
+ // case 'input':
+ // switch (e.type) {
+ // case 'time': f = (s, v) => s.value = v.format("~H:~M"); break;
+ // case 'date': f = (s, v) => s.value = v.format("~Y-~m-~d"); break;
+ // // TODO remaining types cases
+ // default: f = (s, v) => s.value = v;
+ // }
+ // p.push([e, f])
+ // break;
+ // case 'textarea':
+ // f = (s, v) => s.innerHTML = v;
+ // p.push([e, f])
+ // break;
+ // default:
+ // alert("How did you get here??? " + e.tagName)
+ // break;
+ // }
+ // */
+ // }
+ /* checkbox for whole day */
for (let field of ['dtstart', 'dtend']) {
@@ -561,6 +539,9 @@ function bind_properties (el, wide_event=false) {
let str = v.format('~Y-~m-~dT~H:~M:00~Z');
child.innerHTML = `<date-time>${str}</date-time>`;
}
+ } else if (v instanceof RRule) {
+ /* TODO also recalculate whenever any field changes */
+ child.innerHTML = v.asXcal();
} else {
/* assume that type already is correct */
s.innerHTML = v;