import { DatetoLocaleDynamicString, getMinutesDifference, getRelativeTime, } from "../modules/relative_time.js"; export class UIManager { ui; mainmanager; uiElements = {}; floorsEmpty = true; constructor(ui) { this.ui = ui; } mainmanagerInit() { this.__initUIElements(); this.__initZoom(); this.__initMenu(); this.__initSearchUI(); this.__initEvents(); this.__initInspector(); } // State setLoading(state) { if (state) { this.ui.classList.add("loading"); } else { this.ui.classList.remove("loading"); } } setZoomDisabled(state = 0) { if (state > 0) { this.uiElements.zoomIn.disabled = true; this.uiElements.zoomOut.disabled = false; } else if (state < 0) { this.uiElements.zoomIn.disabled = false; this.uiElements.zoomOut.disabled = true; } else { this.uiElements.zoomIn.disabled = false; this.uiElements.zoomOut.disabled = false; } } setEventsMinimized(state) { if (state == null) { this.uiElements.eventsContainer.classList.toggle("minimized"); } else if (state) { this.uiElements.eventsContainer.classList.add("minimized"); } else { this.uiElements.eventsContainer.classList.remove("minimized"); } } setEventsInspector(state) { if ( state != this.uiElements.eventsContainer.classList.contains("inspector") ) { this.setEventsMinimized(false); this.setSearchDialogOpen(false); } if (state) { this.uiElements.eventsContainer.classList.add("inspector"); } else { this.uiElements.eventsContainer.classList.remove("inspector"); } } setSearchDialogOpen(state) { if (state) { this.uiElements.searchDialogScrim.classList.add("open"); this.__updateSearch(); } else { this.uiElements.searchDialogScrim.classList.remove("open"); } } getEventsEmpty() { return this.uiElements.events.classList.contains("empty"); } // Init __initUIElements() { this.uiElements.controls = this.ui.querySelector("#controls"); // Zoom this.uiElements.zoomButtons = this.ui.querySelector("#zoom"); this.uiElements.zoomIn = this.uiElements.zoomButtons.querySelector("#zoom-in"); this.uiElements.zoomOut = this.uiElements.zoomButtons.querySelector("#zoom-out"); // Floors this.uiElements.floors = this.ui.querySelector("#floors"); this.uiElements.floorButtons = this.uiElements.floors.querySelector("#floor-buttons"); // Menu Bar this.uiElements.menuBar = this.ui.querySelector("#menu-bar"); this.uiElements.menuBarSearch = this.ui.querySelector("#menu-search"); this.uiElements.menuBarLang = this.ui.querySelector("#menu-lang"); this.uiElements.menuBarAbout = this.ui.querySelector("#menu-about"); // Events this.uiElements.eventsContainer = this.ui.querySelector("#events-container"); this.uiElements.events = this.uiElements.eventsContainer.querySelector("#events"); this.uiElements.eventsBackButton = this.uiElements.events.querySelector( "#events-header #back" ); this.uiElements.eventsRoomName = this.uiElements.events.querySelector( "#events-header #room-name" ); this.uiElements.eventsRoomDescription = this.uiElements.events.querySelector("#events-header #room-description"); this.uiElements.eventList = this.uiElements.events.querySelector("#event-list"); this.uiElements.eventsInspector = this.uiElements.eventsContainer.querySelector("#events-inspector"); this.uiElements.eventsInspectorHeader = this.uiElements.eventsInspector.querySelector("#events-header"); this.uiElements.eventsInspectorRoomName = this.uiElements.eventsInspectorHeader.querySelector("#room-name"); this.uiElements.eventsInspectorEventLength = this.uiElements.eventsInspectorHeader.querySelector("#event-length"); this.uiElements.eventsInspectorRoomTime = this.uiElements.eventsInspector.querySelector("#room-time"); this.uiElements.eventsInspectorBackButton = this.uiElements.eventsInspector.querySelector("#back"); this.uiElements.eventsInspectorBody = this.uiElements.eventsInspector.querySelector("#event-body"); this.uiElements.minimizeButton = this.uiElements.events.querySelector("#minimize"); this.uiElements.eventsInspectorMinimizeButton = this.uiElements.eventsInspector.querySelector("#minimize"); // Search Dialog this.uiElements.searchDialogScrim = this.ui.querySelector( "#search-dialog-scrim" ); this.uiElements.searchDialog = this.uiElements.searchDialogScrim.querySelector("#search-dialog"); this.uiElements.searchDialogHeader = this.uiElements.searchDialog.querySelector("#search-dialog-header"); this.uiElements.searchDialogHeaderClose = this.uiElements.searchDialogHeader.querySelector("#close"); this.uiElements.searchDialogHeaderTitle = this.uiElements.searchDialogHeader.querySelector("#search-dialog-title"); this.uiElements.searchDialogBar = this.uiElements.searchDialog.querySelector("#search-dialog-bar"); this.uiElements.searchDialogBarInput = this.uiElements.searchDialogBar.querySelector("#search-dialog-input"); this.uiElements.searchDialogTags = this.uiElements.searchDialog.querySelector("#search-dialog-tags"); this.uiElements.searchDialogList = this.uiElements.searchDialog.querySelector("#search-list"); // Loading this.uiElements.loading = this.ui.querySelector("#loading"); } __initZoom() { const currentLocalization = this.mainmanager.getCurrentLocalization(); this.uiElements.zoomIn.addEventListener("click", () => this.mainmanager.callManagerFunction("map", "zoomIn") ); this.uiElements.zoomOut.addEventListener("click", () => this.mainmanager.callManagerFunction("map", "zoomOut") ); this.mainmanager.addManagerEventListener("map", "zoomMin", () => { this.setZoomDisabled(-1); }); this.mainmanager.addManagerEventListener("map", "zoomMax", () => { this.setZoomDisabled(1); }); this.mainmanager.addManagerEventListener("map", "zoom", () => { this.setZoomDisabled(0); }); this.uiElements.zoomIn.title = currentLocalization.zoom_in_button; this.uiElements.zoomOut.title = currentLocalization.zoom_out_button; } __initMenu() { this.uiElements.menuBarSearch.addEventListener("click", () => { this.setSearchDialogOpen(true); }); this.__updateMenu(); } __updateMenu() { const currentLocalization = this.mainmanager.getCurrentLocalization(); this.uiElements.menuBarSearch.title = currentLocalization.menu_search_button; this.uiElements.menuBarLang.title = currentLocalization.menu_language_button; this.uiElements.menuBarAbout.title = currentLocalization.menu_about_button; } __initSearchUI() { this.uiElements.searchDialogHeaderClose.addEventListener("click", () => { this.setSearchDialogOpen(false); }); this.uiElements.searchDialogScrim.addEventListener("click", () => { this.setSearchDialogOpen(false); }); this.uiElements.searchDialog.addEventListener("click", (event) => { event.stopPropagation(); }); this.uiElements.searchDialogBarInput.addEventListener("input", () => { this.__updateSearch(); }); } #Search__selectedTags = []; #Search__allEvents = null; __updateSearchUI() { const currentLocalization = this.mainmanager.getCurrentLocalization(); this.uiElements.searchDialogHeaderClose.title = currentLocalization.search_dialog_close_button; this.uiElements.searchDialogHeaderTitle.textContent = currentLocalization.search_dialog_header_title; const tags = this.mainmanager.getManagerVariable("tag", "allTags"); this.uiElements.searchDialogTags.replaceChildren(); tags.forEach((value, id) => { const tagButton = document.createElement("button"); tagButton.classList.add("search-tag"); tagButton.textContent = value.name; tagButton.title = value.description; tagButton.id = "tag-" + id; tagButton.addEventListener("click", () => this.__updateTagState(id)); this.uiElements.searchDialogTags.appendChild(tagButton); }); } __updateTagState(tag) { const tags = this.mainmanager.getManagerVariable("tag", "allTags"); const tagPos = this.#Search__selectedTags.indexOf(tag); if (tagPos >= 0) this.#Search__selectedTags.splice(tagPos, 1); else this.#Search__selectedTags.push(tag); this.#Search__selectedTags = this.#Search__selectedTags.filter( (val) => val == tag || tags.get(val).radiogroup != tags.get(tag).radiogroup ); this.__updateSearchUISoft(); this.__updateSearch(); } __updateSearchUISoft() { const tags = this.mainmanager.getManagerVariable("tag", "allTags"); tags.forEach((_, id) => { const child = this.uiElements.searchDialogTags.querySelector( "#tag-" + id ); if (this.#Search__selectedTags.includes(id)) child.classList.add("selected"); else child.classList.remove("selected"); }); } async __initSearch() { // Runs on first search, as to not make the client wait too long when starting EventMapper (loading the same thing twice) this.setLoading(true); this.#Search__allEvents = await this.mainmanager.callManagerFunction( "event", "getAllEvents" ); this.setLoading(false); } async __updateSearch() { const currentLocalization = this.mainmanager.getCurrentLocalization(); if (this.#Search__allEvents == null) await this.__initSearch(); const searchTransform = (x) => x.toLowerCase().replace(currentLocalization.regexAnyWordCharacter, ""); const searchValue = searchTransform( this.uiElements.searchDialogBarInput.value ); let whatFilter = []; let filteredEvents = this.#Search__allEvents.filter((event) => { if (searchValue.length < 1) { whatFilter.push(null); return true; } // First, filter by text if (searchTransform(event.name).includes(searchValue)) { whatFilter.push({ type: "name", src: event.name, }); return true; } else if (searchTransform(event.description).includes(searchValue)) { whatFilter.push({ type: "description", src: event.description, }); return true; } else if (searchTransform(event.url).includes(searchValue)) { whatFilter.push({ type: "url", src: event.url, }); return true; } else { // Filter by room/floor const eventFloorId = this.mainmanager.convertIdFocus(event.id, "floor"); const eventFloor = this.mainmanager .getAllFocusObject("floor") .get(eventFloorId); if (eventFloor != null) { if (searchTransform(eventFloor.name).includes(searchValue)) { whatFilter.push({ type: "floor", src: eventFloor.name, }); return true; } } else { if (searchTransform(eventFloorId).includes(searchValue)) { whatFilter.push({ type: "floor", src: eventFloorId, }); return true; } } const eventRoomId = this.mainmanager.convertIdFocus(event.id, "room"); const eventRoom = this.mainmanager .getAllFocusObject("room") .get(eventRoomId); if (eventRoom != null) { if (searchTransform(eventRoom.name).includes(searchValue)) { whatFilter.push({ type: "room", src: eventRoom.name, }); return true; } } else { if (searchTransform(eventRoomId).includes(searchValue)) { whatFilter.push({ type: "room", src: eventRoomId, }); return true; } } } }); if (this.#Search__selectedTags.length > 0) filteredEvents = filteredEvents.filter((event, idx) => { return this.#Search__selectedTags.every((tag) => event.tags.includes(tag) ); }); this.uiElements.searchDialogList.replaceChildren(); const currentDate = new Date(); filteredEvents.forEach((event, idx) => { const eventFloorId = this.mainmanager.convertIdFocus(event.id, "floor"); const eventDateStart = new Date(event.when.start); const eventDateEnd = new Date(event.when.end); const filterReason = whatFilter[idx]; const eventStarted = currentDate > eventDateStart; const eventEnded = currentDate > eventDateEnd; let datePrefix; if (eventEnded) { return; // Don't display this event } else if (eventStarted) { datePrefix = currentLocalization.date_started; } else { datePrefix = currentLocalization.date_starting; } const eventListContainer = document.createElement("div"); eventListContainer.classList.add("event-list-container"); eventListContainer.id = "event-" + event.id; const eventListContainerSummary = document.createElement("p"); if (filterReason != null) { const eventListContainerSummaryReason = document.createElement("small"); eventListContainerSummaryReason.textContent = currentLocalization.search_filters[filterReason.type](filterReason); eventListContainerSummaryReason.classList.add("reason"); eventListContainerSummary.appendChild(eventListContainerSummaryReason); eventListContainerSummary.appendChild(document.createElement("br")); } const eventListContainerSummaryName = document.createElement("a"); eventListContainerSummaryName.href = "#" + event.id; eventListContainerSummaryName.textContent = event.name; eventListContainerSummary.appendChild(eventListContainerSummaryName); eventListContainerSummary.appendChild(document.createTextNode(" ")); if (this.mainmanager.getCurrentFocus("floor") != eventFloorId) { const eventFloor = this.mainmanager .getAllFocusObject("floor") .get(eventFloorId); const eventListContainerSummaryFloor = document.createElement("a"); eventListContainerSummaryFloor.href = "#" + eventFloorId; eventListContainerSummaryFloor.textContent = currentLocalization.parenthesis(eventFloor?.name ?? eventFloorId); eventListContainerSummary.appendChild(eventListContainerSummaryFloor); } else { const eventRoomId = this.mainmanager.convertIdFocus(event.id, "room"); const eventRoom = this.mainmanager .getAllFocusObject("room") .get(eventRoomId); const eventListContainerSummaryRoom = document.createElement("a"); eventListContainerSummaryRoom.href = "#" + eventRoomId; eventListContainerSummaryRoom.textContent = currentLocalization.parenthesis(eventRoom?.name ?? eventRoomId); eventListContainerSummary.appendChild(eventListContainerSummaryRoom); } eventListContainerSummary.appendChild(document.createElement("br")); const eventListContainerSummarySmall = document.createElement("small"); const eventListContainerSummaryTime = document.createElement("span"); eventListContainerSummaryTime.classList.add("clarification"); eventListContainerSummaryTime.title = eventDateStart.toLocaleString( this.mainmanager.getCurrentLangCode(), { dateStyle: "short", timeStyle: "short", } ); eventListContainerSummaryTime.textContent = currentLocalization.date_summary( datePrefix, getRelativeTime( eventDateStart, currentDate, this.mainmanager.getCurrentLangCode() ) ); eventListContainerSummarySmall.appendChild(eventListContainerSummaryTime); eventListContainerSummarySmall.appendChild(document.createTextNode(" ")); const eventListContainerSummaryLength = document.createElement("span"); eventListContainerSummaryLength.textContent = currentLocalization.parenthesis( currentLocalization.minutes_long( getMinutesDifference(eventDateStart, eventDateEnd) ) ); eventListContainerSummarySmall.appendChild( eventListContainerSummaryLength ); eventListContainerSummary.appendChild(eventListContainerSummarySmall); eventListContainer.appendChild(eventListContainerSummary); this.uiElements.searchDialogList.appendChild(eventListContainer); }); } __initInspector() { this.uiElements.eventsInspectorMinimizeButton.addEventListener( "click", () => this.setEventsMinimized() ); this.uiElements.eventsInspectorBackButton.addEventListener("click", () => { window.location.hash = this.mainmanager.getCurrentFocus("room"); }); } updateInspector() { const currentLocalization = this.mainmanager.getCurrentLocalization(); const currentEvent = this.mainmanager.getCurrentFocusObject("event"); const eventRoom = this.mainmanager.getCurrentFocusObject("room"); this.uiElements.eventsInspectorBackButton.title = currentLocalization.event_inspector_back; this.uiElements.eventsInspectorMinimizeButton.title = currentLocalization.event_inspector_minimize; const currentDate = new Date(); const eventDateStart = new Date(currentEvent.when.start); const eventDateEnd = new Date(currentEvent.when.end); const eventStarted = currentDate > eventDateStart; const eventEnded = currentDate > eventDateEnd; let datePrefix; if (eventEnded) { return; // Don't display this event } else if (eventStarted) { datePrefix = currentLocalization.date_started; } else { datePrefix = currentLocalization.date_starting; } this.uiElements.eventsInspectorRoomName.textContent = currentLocalization.event_inspector_header(currentEvent.name); this.uiElements.eventsInspectorRoomTime.textContent = currentLocalization.date_summary( datePrefix, getRelativeTime( eventDateStart, currentDate, this.mainmanager.getCurrentLangCode() ) ) + " " + currentLocalization.parenthesis( DatetoLocaleDynamicString( eventDateStart, currentDate, this.mainmanager.getCurrentLangCode() ) ); this.uiElements.eventsInspectorEventLength.textContent = currentLocalization.separator( currentLocalization.minutes_long( getMinutesDifference(eventDateStart, eventDateEnd) ), "" ); const eventWhere = document.createElement("span"); eventWhere.textContent = eventRoom.name; this.uiElements.eventsInspectorEventLength.appendChild(eventWhere); // Remove all children this.uiElements.eventsInspectorBody.replaceChildren(); if (currentEvent.url) { const eventLink = document.createElement("p"); eventLink.classList.add("widget-description"); const eventAnchor = document.createElement("a"); const eventURL = new URL(currentEvent.url); eventAnchor.href = eventURL; eventAnchor.target = "_blank"; eventAnchor.textContent = eventURL.toString(); eventLink.appendChild(eventAnchor); this.uiElements.eventsInspectorBody.appendChild(eventLink); } this.uiElements.eventsInspectorBody.appendChild( document.createElement("hr") ); const eventDescription = document.createElement("p"); eventDescription.classList.add("widget-description"); eventDescription.textContent = currentEvent.description; this.uiElements.eventsInspectorBody.appendChild(eventDescription); } __initEvents() { this.uiElements.eventsBackButton.addEventListener("click", () => { window.location.hash = this.mainmanager.getCurrentFocus("floor"); }); this.uiElements.minimizeButton.addEventListener("click", () => this.setEventsMinimized() ); } __updateEventsSoft() { const focusedFloor = this.mainmanager.getCurrentFocusObject("floor"); if (focusedFloor == null) { this.uiElements.eventsContainer.classList.add("empty"); } else { this.uiElements.eventsContainer.classList.remove("empty"); } } updateEvents() { const currentLocalization = this.mainmanager.getCurrentLocalization(); const currentEvents = this.mainmanager.getAllFocusObject("event"); const focusedFloor = this.mainmanager.getCurrentFocusObject("floor"); const focusedRoom = this.mainmanager.getCurrentFocusObject("room"); this.uiElements.minimizeButton.title = currentLocalization.minimize; // Figure out header if (this.mainmanager.getIsFocused("room")) { if (currentEvents.size < 1) { this.uiElements.eventsRoomName.textContent = focusedRoom.name; } else { this.uiElements.eventsRoomName.textContent = currentLocalization.events_in(focusedRoom.name); } this.uiElements.eventsRoomDescription.textContent = focusedRoom.description; this.uiElements.eventsBackButton.disabled = false; this.uiElements.eventsBackButton.title = currentLocalization.view_events_in(focusedFloor.name); } else { if (currentEvents.size < 1) { this.uiElements.eventsRoomName.textContent = focusedFloor.name; } else { this.uiElements.eventsRoomName.textContent = currentLocalization.events_in(focusedFloor.name); } this.uiElements.eventsRoomDescription.textContent = focusedFloor.description; this.uiElements.eventsBackButton.disabled = true; this.uiElements.eventsBackButton.title = ""; } // Remove all children this.uiElements.eventList.replaceChildren(); // Put them back const currentDate = new Date(); currentEvents.forEach((event, id) => { const eventRoom = this.mainmanager .getAllFocusObject("room") .get(this.mainmanager.convertIdFocus(event.id, "room")); const eventDateStart = new Date(event.when.start); const eventDateEnd = new Date(event.when.end); const eventStarted = currentDate > eventDateStart; const eventEnded = currentDate > eventDateEnd; let datePrefix; if (eventEnded) { return; // Don't display this event } else if (eventStarted) { datePrefix = currentLocalization.date_started; } else { datePrefix = currentLocalization.date_starting; } const eventListContainer = document.createElement("div"); eventListContainer.classList.add("event-list-container"); eventListContainer.id = "event-" + id; const eventListContainerSummary = document.createElement("p"); const eventListContainerSummaryName = document.createElement("a"); eventListContainerSummaryName.href = "#" + id; eventListContainerSummaryName.textContent = event.name; eventListContainerSummary.appendChild(eventListContainerSummaryName); eventListContainerSummary.appendChild(document.createTextNode(" ")); if (!this.mainmanager.getIsFocused("room")) { const eventListContainerSummaryRoom = document.createElement("a"); eventListContainerSummaryRoom.href = "#" + eventRoom.id; eventListContainerSummaryRoom.textContent = currentLocalization.parenthesis(eventRoom.name); eventListContainerSummary.appendChild(eventListContainerSummaryRoom); } eventListContainerSummary.appendChild(document.createElement("br")); const eventListContainerSummarySmall = document.createElement("small"); const eventListContainerSummaryTime = document.createElement("span"); eventListContainerSummaryTime.classList.add("clarification"); eventListContainerSummaryTime.title = eventDateStart.toLocaleString( this.mainmanager.getCurrentLangCode(), { dateStyle: "short", timeStyle: "short", } ); eventListContainerSummaryTime.textContent = currentLocalization.date_summary( datePrefix, getRelativeTime( eventDateStart, currentDate, this.mainmanager.getCurrentLangCode() ) ); eventListContainerSummarySmall.appendChild(eventListContainerSummaryTime); eventListContainerSummarySmall.appendChild(document.createTextNode(" ")); const eventListContainerSummaryLength = document.createElement("span"); eventListContainerSummaryLength.textContent = currentLocalization.parenthesis( currentLocalization.minutes_long( getMinutesDifference(eventDateStart, eventDateEnd) ) ); eventListContainerSummarySmall.appendChild( eventListContainerSummaryLength ); eventListContainerSummary.appendChild(eventListContainerSummarySmall); eventListContainer.appendChild(eventListContainerSummary); this.uiElements.eventList.appendChild(eventListContainer); }); if (currentEvents.size < 1) { this.uiElements.events.classList.add("empty"); } else { this.uiElements.events.classList.remove("empty"); } } __updateFloorsHard() { const currentLocalization = this.mainmanager.getCurrentLocalization(); const currentFloors = this.mainmanager.getAllFocusObject("floor"); const mainFocus = this.mainmanager.getCurrentFocus("floor"); // Remove all children this.uiElements.floorButtons.replaceChildren(); // Put them back currentFloors.forEach(({ name, shortName }, id) => { const floorButton = document.createElement("button"); floorButton.textContent = shortName; floorButton.title = name; floorButton.classList.add("floor-button"); floorButton.id = "floor-" + id; if (id === mainFocus) floorButton.classList.add("selected"); floorButton.addEventListener("click", () => { window.location.hash = id; }); this.uiElements.floorButtons.appendChild(floorButton); }); if (currentFloors.size < 1) { this.uiElements.controls.classList.add("empty"); this.floorsEmpty = true; } else { this.uiElements.controls.classList.remove("empty"); this.floorsEmpty = false; } } __updateFloorsSoft() { const currentFloors = this.mainmanager.getAllFocusObject("floor"); const mainFocus = this.mainmanager.getCurrentFocus("floor"); currentFloors.forEach((_, id) => { const child = this.uiElements.floorButtons.querySelector("#floor-" + id); if (id === mainFocus) child.classList.add("selected"); else child.classList.remove("selected"); }); } }