From cbe3c46a898822b6ee0f10366e561c6a8055a1b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Tue, 2 Mar 2021 23:34:14 +0100 Subject: Start moving vcal stuff to own class. --- static/script.js | 307 +++---------------------------------------------------- 1 file changed, 14 insertions(+), 293 deletions(-) (limited to 'static/script.js') diff --git a/static/script.js b/static/script.js index 68773231..d809cc25 100644 --- a/static/script.js +++ b/static/script.js @@ -19,6 +19,8 @@ class EventCreator { let popup = document.getElementById("popup-template") .firstChild.cloneNode(true); + /* TODO shouldn't this use transferListeners (or similar)? + See input_list.js:transferListeners */ popup.getElementsByClassName("edit-form")[0].onsubmit = function () { create_event(event); return false; /* stop default */ @@ -113,7 +115,10 @@ class EventCreator { this.appendChild(event); /* requires that event is child of an '.event-container'. */ - bind_properties(event, wide_element); + event.properties = new VComponent( + event, + wide_element=wide_element); + // bind_properties(event, wide_element); /* requires that dtstart and dtend properties are initialized */ @@ -159,6 +164,8 @@ class EventCreator { let d1 = new Date(container_start.getTime() + start_in_duration) let d2 = new Date(container_start.getTime() + end_in_duration) + // console.log(that.event); + console.log(d1, d2); that.event.properties.dtstart = d1; that.event.properties.dtend = d2; } @@ -187,6 +194,8 @@ class EventCreator { } } + + /* This incarnation of this function only adds the calendar switcher dropdown. All events are already editable by switching to that tab. @@ -227,6 +236,8 @@ function place_in_edit_mode (event) { tab.querySelector("input[name='summary']").focus(); } + + window.onload = function () { // let start_time = document.querySelector("meta[name='start-time']").content; // let end_time = document.querySelector("meta[name='end-time']").content; @@ -309,9 +320,9 @@ window.onload = function () { /* Bind all vcomponent properties into javascript. */ if (el.closest(".longevents")) { - bind_properties(el, true); + el.properties = new VComponent(el, true); } else { - bind_properties(el, false); + el.properties = new VComponent(el, true); } } @@ -373,293 +384,3 @@ window.onload = function () { } - -/* - Returns the _value_ slot of given field in event, creating it if needed . - el - the event to work on - field - name of the field - default_value - default value when creating - bind_to_ical - should this property be added to the icalendar subtree? -*/ -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]) { - el.properties["_slot_" + field] = []; - el.properties["_value_" + field] = default_value; - - Object.defineProperty( - el.properties, field, - { - get: function () { - return this["_value_" + field]; - }, - set: function (value) { - this["_value_" + field] = value; - for (let [slot,updater] of el.properties["_slot_" + field]) { - updater(slot, value); - } - } - }); - } - - return el.properties["_slot_" + field]; -} - - - -/* - Properties are icalendar properties. - - p['name'] to get and set value (also updates any connected slots) - - p['_value_name'] for raw value - p['_slot_name'] for connected slots, Vector of pairs, where the - car should be a reference to the slot, and the - cdr a procedure which takes a slot and a value - and binds the value to the slot. - */ -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; - - /* actual component (not popup) */ - /* - for (let e of el.querySelectorAll(".bind")) { - } - */ - - /* bind_recur */ - - /* primary display tab */ - - let p; - let lst = [...popup.querySelectorAll(".bind"), - ...el.querySelectorAll('.bind')]; - for (let e of lst) { - if ((p = e.closest('[data-bindby]'))) { - // console.log(p.dataset.bindby); - eval(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]); - } - } - - // 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]); - // */ - // } - - /* 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']) { - - get_property(el, `--${field}-time`).push( - [el, (el, v) => { let date = el.properties[field]; - if (v == '') return; - let [h,m,s] = v.split(':') - date.setHours(Number(h)); - date.setMinutes(Number(m)); - date.setSeconds(0); - el.properties[field] = date; }]) - get_property(el, `--${field}-date`).push( - [el, (el, v) => { let date = el.properties[field]; - if (v == '') return; - let [y,m,d] = v.split('-') - date.setYear(Number(y)/* - 1900*/); - date.setMonth(Number(m) - 1); - date.setDate(d); - el.properties[field] = date; }]) - - - /* Manual fetch of the fields instead of the general method, - to avoid an infinite loop of dtstart setting --dtstart-time, - and vice versa. - NOTE if many more fields require special treatment then a - general solution is required. - */ - get_property(el, field).push( - [el, (el, v) => { popup - .querySelector(`.edit-tab input[name='${field}-time']`) - .value = v.format("~H:~M"); - popup - .querySelector(`.edit-tab input[name='${field}-date']`) - .value = v.format("~Y-~m-~d"); - }]); - } - - for (let property of property_names) { - el.properties.ical_properties.add(property) - } - - /* icalendar properties */ - for (let child of el.querySelector("vevent > properties").children) { - /* child ≡ ... */ - - 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)`)) { - /* s ≡ ... */ - - /* Binds value from XML-tree to javascript object - [parsedate] - - TODO capture xcal type here, to enable us to output it to jcal later. - */ - let parsedValue; - let type = s.tagName.toLowerCase(); - switch (type) { - case 'float': - case 'integer': - parsedValue = new Number(s.innerHTML); - parsedValue.type = type; - break; - - case 'date-time': - case 'date': - parsedValue = parseDate(s.innerHTML); - break; - - /* TODO */ - case 'duration': - let start = s.getElementsByTagName('start'); - let end = s.getElementsByTagName('end, duration'); - if (end.tagName === 'period') { - parsePeriod(end.innerHTML); - } - break; - /* TODO */ - case 'period': - parsePeriod(s.innerHTML); - break; - /* TODO */ - case 'utc-offset': - break; - - case 'recur': - parsedValue = recur_xml_to_rrule(s); - break; - - case 'boolean': - switch (s.innerHTML) { - case 'true': parsedValue = true; break; - case 'false': parsedValue = false; break; - default: throw "Value error" - } - break; - - - case 'binary': - /* Binary is going to be BASE64 decoded, allowing us to ignore - it and handle it as a string for the time being */ - case 'cal-address': - case 'text': - case 'uri': - /* TODO Attributes on strings doesn't work - They do however work on String:s - */ - parsedValue = s.innerHTML; - // parsedValue.type = type; - break; - - default: - parsedValue.type = 'unknown'; - parsedValue = s.innerHTML; - } - el.properties['_value_rrule'] = parsedValue; - } - } - - /* set up graphical display changes */ - let container = el.closest(".event-container"); - if (container === null) { - console.log("No enclosing event container for", el); - return; - } - let start = parseDate(container.dataset.start); - let end = parseDate(container.dataset.end); - - if (el.properties.dtstart) { - /* [parsedate] */ - // el.properties.dtstart = parseDate(el.properties.dtstart); - get_property(el, 'dtstart').push( - [el.style, (s, v) => - s[wide_event?'left':'top'] = 100 * (to_local(v) - start)/(end - start) + "%"]); - } - - - if (el.properties.dtend) { - // el.properties.dtend = parseDate(el.properties.dtend); - get_property(el, 'dtend').push( - // TODO right and bottom only works if used from the start. However, - // events from the backend instead use top/left and width/height. - // Normalize so all use the same, or find a way to convert between. - [el.style, - (s, v) => s[wide_event?'right':'bottom'] = 100 * (1 - (to_local(v)-start)/(end-start)) + "%"]); - } - - - /* ---------- Calendar ------------------------------ */ - - if (! el.dataset.calendar) { - el.dataset.calendar = "Unknown"; - } - - let calprop = get_property(el, 'calendar', el.dataset.calendar); - - const rplcs = (s, v) => { - let [_, calclass] = s.classList.find(/^CAL_/); - s.classList.replace(calclass, "CAL_" + v); - } - - calprop.push([popup, rplcs]); - calprop.push([el, rplcs]); - calprop.push([el, (s, v) => s.dataset.calendar = v]); - - - - /* ---------- Calendar ------------------------------ */ -} -- cgit v1.2.3