From 4a4c3e42151da7a83bd863d31d8ad5f8f4d07396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Sat, 21 Nov 2020 00:13:20 +0100 Subject: Further work on breakout and rrule. --- module/calp/html/vcomponent.scm | 16 ++++-- static/binders.js | 96 +++++++++++++++++++++++++++++++++ static/clock.js | 10 ++-- static/recur.js | 0 static/rrule.js | 57 ++++++++++++++++++++ static/script.js | 117 +++++++++++++++++----------------------- 6 files changed, 219 insertions(+), 77 deletions(-) create mode 100644 static/binders.js create mode 100644 static/recur.js create mode 100644 static/rrule.js 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 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 = ""; + for (let f of this.fields) { + let v = this.fields[f]; + if (! v) continue; + str += `<${f}>${v}`; + } + str += ""; + 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 = `${str}`; } + } 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; -- cgit v1.2.3