aboutsummaryrefslogtreecommitdiff
path: root/static/script.ts
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2021-10-04 17:40:59 +0200
committerHugo Hörnquist <hugo@lysator.liu.se>2021-10-04 17:43:45 +0200
commitc6c65f9e8273a5bc1b2ac1155d66003d2b98591c (patch)
treeda25ccd8af897dbc2671008e06f22d08d1208035 /static/script.ts
parentwork (diff)
downloadcalp-c6c65f9e8273a5bc1b2ac1155d66003d2b98591c.tar.gz
calp-c6c65f9e8273a5bc1b2ac1155d66003d2b98591c.tar.xz
{.js => .ts} on relavant files.
Diffstat (limited to 'static/script.ts')
-rw-r--r--static/script.ts414
1 files changed, 414 insertions, 0 deletions
diff --git a/static/script.ts b/static/script.ts
new file mode 100644
index 00000000..16ff7bbd
--- /dev/null
+++ b/static/script.ts
@@ -0,0 +1,414 @@
+'use strict';
+
+/*
+ calp specific stuff
+*/
+
+class EventCreator {
+
+ /* dynamicly created event when dragging */
+ constructor() {
+ this.event = false;
+ this.event_start = { x: NaN, y: NaN };
+ this.down_on_event = false;
+ }
+
+ create_empty_event () {
+ /* TODO this doesn't clone JS attributes */
+
+ // let event = document.getElementById("event-template")
+ // .firstChild.cloneNode(true);
+ // let popup = document.getElementById("popup-template")
+ // .firstChild.cloneNode(true);
+
+ // document.createElement('vevent-block');
+
+ /* -------------------- */
+ /* Manually transfer or recreate attributes which we still need */
+ /* TODO shouldn't these use transferListeners (or similar)?
+ See input_list.js:transferListeners */
+
+ // for (let dt of popup.getElementsByClassName("date-time")) {
+ // init_date_time_single(dt);
+ // }
+
+ // popup.getElementsByClassName("edit-form")[0].onsubmit = function () {
+ // create_event(event);
+ // return false; /* stop default */
+ // }
+
+ /* -------------------- */
+ /* Fix tabs for newly created popup */
+
+ let id = gensym ("__js_event");
+
+ // TODO remove button?
+ // $("button 2??").onclick = `remove_event(document.getElementById('${id}'))`
+
+ /*
+ let tabgroup_id = gensym();
+ for (let tab of popup.querySelectorAll(".tabgroup .tab")) {
+ let new_id = gensym();
+ let input = tab.querySelector("input");
+ input.id = new_id;
+ input.name = tabgroup_id;
+ tab.querySelector("label").setAttribute('for', new_id);
+ }
+
+ let nav = popup.getElementsByClassName("popup-control")[0];
+ bind_popup_control(nav);
+ */
+
+ /* -------------------- */
+
+ // TODO download links
+
+ /* -------------------- */
+
+ event.id = id;
+ popup.id = "popup" + id;
+
+ return [popup, event];
+ }
+
+ create_event_down (intended_target) {
+ let that = this;
+ return function (e) {
+ /* Only trigger event creation stuff on actuall events background,
+ NOT on its children */
+ that.down_on_event = false;
+ if (e.target != intended_target) return;
+ that.down_on_event = true;
+
+ that.event_start.x = e.clientX;
+ that.event_start.y = e.clientY;
+ }
+ }
+
+ /*
+ round_to: what start and end times should round to when dragging, in fractionsb
+ of the width of the containing container.
+
+ TODO limit this to only continue when on the intended event_container.
+
+ (event → [0, 1)), 𝐑, bool → event → ()
+ */
+ create_event_move(pos_in, round_to=1, wide_element=false) {
+ let that = this;
+ return function (e) {
+ if (e.buttons != 1 || ! that.down_on_event) return;
+
+ /* Create event when we start moving the mouse. */
+ if (! that.event) {
+ /* Small deadzone so tiny click and drags aren't registered */
+ if (Math.abs(that.event_start.x - e.clientX) < 10
+ && Math.abs(that.event_start.y - e.clientY) < 5)
+ { return; }
+
+ /* only allow start of dragging on background */
+ if (e.target != this) return;
+
+ /* only on left click */
+ if (e.buttons != 1) return;
+
+ // let [popup, event] = that.create_empty_event();
+ // that.event = event;
+ that.event = document.createElement('vevent-block');
+
+ /* TODO better solution to add popup to DOM */
+ // document.getElementsByTagName("main")[0].append(popup);
+
+ /* [0, 1) -- where are we in the container */
+ /* Ronud to force steps of quarters */
+ /* NOTE for in-day events a floor here work better, while for
+ all day events I want a round, but which has the tip over point
+ around 0.7 instead of 0.5.
+ It might also be an idea to subtract a tiny bit from the short events
+ mouse position, since I feel I always get to late starts.
+ */
+ let time = round_time(pos_in(this, e), round_to);
+
+ that.event.dataset.time1 = time;
+ that.event.dataset.time2 = time;
+
+ /* ---------------------------------------- */
+
+ this.appendChild(that.event);
+
+ /* requires that event is child of an '.event-container'. */
+ // new VComponent(
+ // event,
+ // wide_element=wide_element);
+ // bind_properties(event, wide_element);
+
+ /* requires that dtstart and dtend properties are initialized */
+
+ /* ---------------------------------------- */
+
+ /* Makes all current events transparent when dragging over them.
+ Without this weird stuff happens when moving over them
+
+ This includes ourselves.
+ */
+ for (let e of this.children) {
+ e.style.pointerEvents = "none";
+ }
+
+ }
+
+ let time1 = Number(that.event.dataset.time1);
+ let time2 = that.event.dataset.time2 =
+ round_time(pos_in(that.event.parentElement, e),
+ round_to);
+
+ /* ---------------------------------------- */
+
+ let event_container = that.event.closest(".event-container");
+
+ /* These two are in UTC */
+ let container_start = parseDate(event_container.dataset.start);
+ let container_end = parseDate(event_container.dataset.end);
+
+ /* ---------------------------------------- */
+
+ /* ms */
+ let duration = container_end - container_start;
+
+ let start_in_duration = duration * Math.min(time1,time2);
+ let end_in_duration = duration * Math.max(time1,time2);
+
+ /* Notice that these are converted to UTC, since the intervals are given
+ in utc, and I only really care about local time (which a specific local
+ timezone doesn't give me)
+ */
+ /* TODO Should these inherit UTC from container_*? */
+ 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.format("~L~H:~M"), d2.format("~L~H:~M"));
+ that.event.redraw({ getProperty: (name) =>
+ ({ dtstart: d1, dtend: d2 })[name]});
+ // that.event.properties.dtstart = d1;
+ // that.event.properties.dtend = d2;
+ }
+ }
+
+ create_event_finisher (callback) {
+ let that = this;
+ return function create_event_up (e) {
+ if (! that.event) return;
+
+ /* Restore pointer events for all existing events.
+ Allow pointer events on our new event
+ */
+ for (let e of that.event.parentElement.children) {
+ e.style.pointerEvents = "";
+ }
+
+ // place_in_edit_mode(that.event);
+
+ let localevent = that.event;
+ that.event = null;
+
+ callback (localevent);
+
+ }
+ }
+}
+
+
+
+/* This incarnation of this function only adds the calendar switcher dropdown.
+ All events are already editable by switching to that tab.
+
+ TODO stop requiring a weird button press to change calendar.
+*/
+// function place_in_edit_mode (event) {
+// let popup = document.getElementById("popup" + event.id)
+// let container = popup.getElementsByClassName('dropdown-goes-here')[0]
+// let calendar_dropdown = document.getElementById('calendar-dropdown-template').firstChild.cloneNode(true);
+//
+// let [_, calclass] = popup.classList.find(/^CAL_/);
+// label: {
+// for (let [i, option] of calendar_dropdown.childNodes.entries()) {
+// if (option.value === calclass.substr(4)) {
+// calendar_dropdown.selectedIndex = i;
+// break label;
+// }
+// }
+// /* no match, try find default calendar */
+// let t;
+// if ((t = calendar_dropdown.querySelector("[selected]"))) {
+// event.properties.calendar = t.value;
+// }
+// }
+//
+//
+// /* Instant change while user is stepping through would be
+// * preferable. But I believe that <option> first gives us the
+// * input once selected */
+// calendar_dropdown.onchange = function () {
+// event.properties.calendar = this.value;
+// }
+// container.appendChild(calendar_dropdown);
+//
+// let tab = popup.getElementsByClassName("tab")[1];
+// let radio = tab.getElementsByTagName("input")[0];
+// radio.click();
+// tab.querySelector("input[name='summary']").focus();
+// }
+
+
+
+window.addEventListener('load', function () {
+ // let start_time = document.querySelector("meta[name='start-time']").content;
+ // let end_time = document.querySelector("meta[name='end-time']").content;
+
+ const sch = new SmallcalCellHighlight(
+ document.querySelector('.small-calendar'))
+
+ const timebar = new Timebar(/*start_time, end_time*/);
+
+ timebar.update(new Date);
+ sch.update(new Date);
+ window.setInterval(() => {
+ let d = new Date;
+ timebar.update(d);
+ sch.update(d);
+ }, 1000 * 60);
+
+ /* Is event creation active? */
+ if (true && EDIT_MODE) {
+ let eventCreator = new EventCreator;
+ for (let c of document.getElementsByClassName("events")) {
+ c.onmousedown = eventCreator.create_event_down(c);
+ c.onmousemove = eventCreator.create_event_move(
+ (c,e) => e.offsetY / c.clientHeight,
+ /* every quarter, every hour */
+ 1/(24*4), false
+ );
+ c.onmouseup = eventCreator.create_event_finisher(
+ function (event) {
+ let popupElement = document.getElementById("popup" + event.id);
+ open_popup(popupElement);
+
+ popupElement.querySelector("input[name='summary']").focus();
+
+ });
+ }
+
+ for (let c of document.getElementsByClassName("longevents")) {
+ c.onmousedown = eventCreator.create_event_down(c);
+ c.onmousemove = eventCreator.create_event_move(
+ (c,e) => e.offsetX / c.clientWidth,
+ /* every day, NOTE should be changed to check
+ interval of longevents */
+ 1/7, true
+ );
+ c.onmouseup = eventCreator.create_event_finisher(
+ function (event) {
+ let popupElement = document.getElementById("popup" + event.id);
+ open_popup(popupElement);
+
+ popupElement.querySelector("input[name='summary']").focus();
+
+ /* This assumes that it's unchecked beforehand.
+ Preferably we would just ensure that it's checked here,
+ But we also need to make sure that the proper handlers
+ are run then */
+ popupElement.querySelector("input[name='wholeday']").click();
+
+ });
+ }
+ }
+
+ // for (let nav of document.getElementsByClassName("popup-control")) {
+ // bind_popup_control(nav);
+ // }
+
+ for (let el of document.getElementsByClassName("event")) {
+ /* Popup script replaces need for anchors to events.
+ On mobile they also have the problem that they make
+ the whole page scroll there.
+ */
+ el.parentElement.removeAttribute("href");
+
+ let popup = document.getElementById("popup" + el.id);
+ // popup.getElementsByClassName("edit-form")[0].onsubmit = function () {
+ // create_event(el);
+ // return false; /* stop default */
+ // }
+
+ /* Bind all vcomponent properties into javascript. */
+ // if (el.closest(".longevents")) {
+ // new VComponent(el, true);
+ // } else {
+ // new VComponent(el, false);
+ // }
+
+ }
+
+ document.onkeydown = function (evt) {
+ evt = evt || window.event;
+ if (! evt.key) return;
+ if (evt.key.startsWith("Esc")) {
+ close_all_popups();
+ }
+ }
+
+
+ /* Replace backend-driven [today] link with frontend, with one that
+ gets correctly set in the frontend. Similarly, update the go to
+ specific date button into a link which updates wheneven the date
+ form updates.
+ */
+
+ let gotodatebtn = document.querySelector("#jump-to .btn");
+ let target_href = (new Date).format("~Y-~m-~d") + ".html";
+ let golink = makeElement('a', {
+ className: 'btn',
+ href: target_href,
+ innerHTML: gotodatebtn.innerHTML,
+ });
+ gotodatebtn.replaceWith(golink);
+
+ document.querySelector("#jump-to input[name='date']").onchange = function () {
+ let date = this.valueAsDate.format("~Y-~m-~d");
+ console.log(date);
+ golink.href = date + ".html";
+ }
+
+ /* ---------------------------------------- */
+
+ /* needs to be called AFTER bind_properties, but BEFORE init_input_list
+ After bind_properties since that initializes categories to a possible field
+ Before init_input_list since we need this listener to be propagated to clones.
+ [CATEGORIES_BIND]
+ */
+ for (let lst of document.querySelectorAll(".input-list[data-property='categories']")) {
+ let f = function () {
+ console.log(lst, lst.closest('.popup-container'));
+ let event = event_from_popup(lst.closest('.popup-container'))
+ event.properties.categories = lst.get_value();
+ };
+
+ for (let inp of lst.querySelectorAll('input')) {
+ inp.addEventListener('input', f);
+ }
+ }
+
+ // init_arbitary_kv();
+
+ // init_input_list();
+
+
+ document.addEventListener('keydown', function (event) {
+ if (event.key == '/') {
+ let searchbox = document.querySelector('.simplesearch [name=q]')
+ // focuses the input, and selects all the text in it
+ searchbox.select();
+ event.preventDefault();
+ }
+ });
+})