search
This commit is contained in:
parent
2850588b58
commit
91235defd8
10 changed files with 736 additions and 57 deletions
|
@ -5,6 +5,19 @@ export class EventManager {
|
|||
|
||||
constructor() {}
|
||||
|
||||
async getAllEvents() {
|
||||
// DO NOT use this function for general use - may overload the server(s). get ONCE and then cache!
|
||||
const currentLocalization = this.mainmanager.getCurrentLangCode();
|
||||
|
||||
let allEventsReqSend = fetch(`/data/${currentLocalization}/events/`);
|
||||
|
||||
let allEventsReq = await allEventsReqSend;
|
||||
let allEvents;
|
||||
if (allEventsReq.ok) allEvents = await allEventsReq.json();
|
||||
|
||||
return allEvents;
|
||||
}
|
||||
|
||||
async getEvents() {
|
||||
const currentLocalization = this.mainmanager.getCurrentLangCode();
|
||||
const currentFloor = this.mainmanager.getCurrentFocus("floor");
|
||||
|
|
|
@ -12,6 +12,8 @@ export class GameObjManager {
|
|||
font: "monospace",
|
||||
fontSize: 24,
|
||||
maxFontWidth: 16,
|
||||
floorOpacity: 0.75,
|
||||
roomOpacity: 0.75,
|
||||
};
|
||||
|
||||
mainmanager;
|
||||
|
@ -38,8 +40,8 @@ export class GameObjManager {
|
|||
|
||||
this.floorObject = this.map.kp.make([
|
||||
this.map.kp.polygon(polygon.pts),
|
||||
this.map.kp.outline(1, this.map.kp.BLACK),
|
||||
this.map.kp.color(this.map.kp.Color.fromHex("303030")),
|
||||
this.map.kp.opacity(this.opts.floorOpacity),
|
||||
this.map.kp.pos(),
|
||||
this.map.kp.z(5),
|
||||
]);
|
||||
|
@ -48,13 +50,19 @@ export class GameObjManager {
|
|||
|
||||
this.map.camBounds = bounds;
|
||||
|
||||
this.floorObject.onUpdate(() => {
|
||||
const camScale = 1 / this.map.kp.camScale().y;
|
||||
this.floorObject.outline.width = 8 * camScale;
|
||||
});
|
||||
|
||||
this.floorObject.onDraw(() => {
|
||||
const camScale = 1 / this.map.kp.camScale().y;
|
||||
|
||||
this.map.kp.drawPolygon({
|
||||
pts: polygon.pts,
|
||||
pos: this.map.kp.vec2(0),
|
||||
fill: false,
|
||||
outline: {
|
||||
color: this.map.kp.Color.fromHex(this.opts.borderColor),
|
||||
width: 8 * camScale,
|
||||
},
|
||||
});
|
||||
|
||||
this.map.kp.drawText({
|
||||
text: currentFloor.name,
|
||||
size: 24 * camScale,
|
||||
|
@ -120,7 +128,7 @@ export class GameObjManager {
|
|||
const obj = this.map.kp.make([
|
||||
this.map.kp.polygon(polygon.pts),
|
||||
this.map.kp.color(this.map.kp.Color.fromHex(this.opts.bgColor)),
|
||||
this.map.kp.opacity(0.5),
|
||||
this.map.kp.opacity(this.opts.roomOpacity),
|
||||
this.map.kp.area(),
|
||||
this.map.kp.pos(),
|
||||
this.map.kp.z(6),
|
||||
|
@ -133,8 +141,6 @@ export class GameObjManager {
|
|||
this.roomObjects.set(room.id, obj);
|
||||
|
||||
obj.onUpdate(() => {
|
||||
const camScale = 1 / this.map.kp.camScale().y;
|
||||
|
||||
const roomFocused = this.mainmanager.getCurrentFocus("room") == room.id;
|
||||
|
||||
if (roomFocused) {
|
||||
|
|
|
@ -6,7 +6,10 @@ export default new Map([
|
|||
date_starting: "Starting",
|
||||
date_started: "Started",
|
||||
date_summary: (prefix, time) => `${prefix} ${time}`,
|
||||
list_index: (index) => `, ${index}`,
|
||||
parenthesis: (content) => `(${content})`,
|
||||
openParenthesis: "(",
|
||||
closeParenthesis: ")",
|
||||
separator: (p1, p2) => `${p1} - ${p2}`,
|
||||
hours_long: (hours) =>
|
||||
hours == 1 ? `${hours} hour long` : `${hours} hours long`,
|
||||
|
@ -19,6 +22,22 @@ export default new Map([
|
|||
event_inspector_header: (name) => `Event ${name}`,
|
||||
event_inspector_back: "Return to events panel",
|
||||
event_inspector_minimize: "Toggle event inspector docking",
|
||||
zoom_in_button: "Zoom in",
|
||||
zoom_out_button: "Zoom out",
|
||||
menu_search_button: "Search events",
|
||||
menu_language_button: "Change language",
|
||||
menu_about_button: "About EventMapper",
|
||||
search_dialog_close_button: "Close search",
|
||||
search_dialog_search_button: "Run search",
|
||||
search_dialog_header_title: "Search Events",
|
||||
search_filters: {
|
||||
name: ({ src }) => `Found by title`,
|
||||
description: ({ src }) => `Found by description`,
|
||||
url: ({ src }) => `Found by URL ${src}`,
|
||||
floor: ({ src }) => `Found by floor ${src}`,
|
||||
room: ({ src }) => `Found by room ${src}`,
|
||||
},
|
||||
regexAnyWordCharacter: /[^\w]/g,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { EventManager } from "./events.js";
|
|||
import { FloorManager } from "./floors.js";
|
||||
import { GameObjManager } from "./gameobj.js";
|
||||
import { LangManager } from "./lang.js";
|
||||
import { TagManager } from "./tags.js";
|
||||
import { UIManager } from "./ui.js";
|
||||
|
||||
export class EventMapperManager {
|
||||
|
@ -32,6 +33,9 @@ export class EventMapperManager {
|
|||
this.floormanager = new FloorManager();
|
||||
this.floormanager.mainmanager = this;
|
||||
|
||||
this.tagmanager = new TagManager();
|
||||
this.tagmanager.mainmanager = this;
|
||||
|
||||
this.uimanager = new UIManager(mapUi);
|
||||
this.uimanager.mainmanager = this;
|
||||
|
||||
|
@ -54,6 +58,14 @@ export class EventMapperManager {
|
|||
|
||||
this.uimanager.setLoading(true);
|
||||
|
||||
await this.tagmanager.getTags();
|
||||
|
||||
this.uimanager.__updateSearchUI();
|
||||
|
||||
this.uimanager.setLoading(false);
|
||||
|
||||
this.uimanager.setLoading(true);
|
||||
|
||||
window.addEventListener("hashchange", () => this.hashchange());
|
||||
|
||||
await this.hashchange();
|
||||
|
@ -113,6 +125,8 @@ export class EventMapperManager {
|
|||
|
||||
if (!this.uimanager.getEventsEmpty())
|
||||
this.uimanager.setEventsMinimized(false);
|
||||
// this.uimanager.setSearchDialogOpen(false);
|
||||
await this.uimanager.__updateSearch();
|
||||
|
||||
this.uimanager.setLoading(false);
|
||||
}
|
||||
|
@ -127,6 +141,9 @@ export class EventMapperManager {
|
|||
|
||||
if (!this.uimanager.getEventsEmpty())
|
||||
this.uimanager.setEventsMinimized(false);
|
||||
if (id != null) {
|
||||
this.uimanager.setSearchDialogOpen(false);
|
||||
}
|
||||
this.uimanager.setEventsInspector(false);
|
||||
}
|
||||
|
||||
|
@ -137,6 +154,7 @@ export class EventMapperManager {
|
|||
this.gameobjmanager.zoomToRoom(this.convertIdFocus(id, "room"));
|
||||
|
||||
this.uimanager.updateInspector();
|
||||
this.uimanager.setSearchDialogOpen(false);
|
||||
}
|
||||
|
||||
this.uimanager.setEventsInspector(id != null);
|
||||
|
@ -294,6 +312,8 @@ export class EventMapperManager {
|
|||
return this.eventmanager[func](...args);
|
||||
case "floor":
|
||||
return this.floormanager[func](...args);
|
||||
case "tag":
|
||||
return this.tagmanager[func](...args);
|
||||
case "ui":
|
||||
return this.uimanager[func](...args);
|
||||
case "gameobj":
|
||||
|
@ -311,6 +331,8 @@ export class EventMapperManager {
|
|||
return this.eventmanager[variable];
|
||||
case "floor":
|
||||
return this.floormanager[variable];
|
||||
case "tag":
|
||||
return this.tagmanager[variable];
|
||||
case "ui":
|
||||
return this.uimanager[variable];
|
||||
case "gameobj":
|
||||
|
|
25
assets/scripts/KaplayMap/tags.js
Normal file
25
assets/scripts/KaplayMap/tags.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
export class TagManager {
|
||||
mainmanager;
|
||||
|
||||
#tags = new Map([]);
|
||||
|
||||
constructor() {}
|
||||
|
||||
async getTags() {
|
||||
const currentLocalization = this.mainmanager.getCurrentLangCode();
|
||||
|
||||
let allTagsReqSend = fetch(`/data/${currentLocalization}/tags`);
|
||||
|
||||
let allTagsReq = await allTagsReqSend;
|
||||
let allTags;
|
||||
if (allTagsReq.ok) allTags = await allTagsReq.json();
|
||||
|
||||
this.#tags.clear();
|
||||
|
||||
allTags.forEach((tag) => this.#tags.set(tag.id, tag));
|
||||
}
|
||||
|
||||
get allTags() {
|
||||
return this.#tags;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,10 @@ export class UIManager {
|
|||
|
||||
this.__initZoom();
|
||||
|
||||
this.__initMenu();
|
||||
|
||||
this.__initSearchUI();
|
||||
|
||||
this.__initEvents();
|
||||
|
||||
this.__initInspector();
|
||||
|
@ -61,8 +65,10 @@ export class UIManager {
|
|||
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 {
|
||||
|
@ -70,6 +76,15 @@ export class UIManager {
|
|||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
@ -77,6 +92,8 @@ export class UIManager {
|
|||
// Init
|
||||
|
||||
__initUIElements() {
|
||||
this.uiElements.controls = this.ui.querySelector("#controls");
|
||||
|
||||
// Zoom
|
||||
this.uiElements.zoomButtons = this.ui.querySelector("#zoom");
|
||||
this.uiElements.zoomIn =
|
||||
|
@ -89,6 +106,12 @@ export class UIManager {
|
|||
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");
|
||||
|
@ -126,11 +149,34 @@ export class UIManager {
|
|||
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")
|
||||
);
|
||||
|
@ -146,6 +192,320 @@ export class UIManager {
|
|||
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() {
|
||||
|
@ -213,8 +573,7 @@ export class UIManager {
|
|||
""
|
||||
);
|
||||
|
||||
const eventWhere = document.createElement("a");
|
||||
eventWhere.href = "#" + eventRoom.id;
|
||||
const eventWhere = document.createElement("span");
|
||||
eventWhere.textContent = eventRoom.name;
|
||||
|
||||
this.uiElements.eventsInspectorEventLength.appendChild(eventWhere);
|
||||
|
@ -416,9 +775,10 @@ export class UIManager {
|
|||
this.uiElements.floorButtons.replaceChildren();
|
||||
|
||||
// Put them back
|
||||
currentFloors.forEach(({ name }, id) => {
|
||||
currentFloors.forEach(({ name, shortName }, id) => {
|
||||
const floorButton = document.createElement("button");
|
||||
floorButton.textContent = name;
|
||||
floorButton.textContent = shortName;
|
||||
floorButton.title = name;
|
||||
floorButton.classList.add("floor-button");
|
||||
floorButton.id = "floor-" + id;
|
||||
if (id === mainFocus) floorButton.classList.add("selected");
|
||||
|
@ -430,16 +790,15 @@ export class UIManager {
|
|||
});
|
||||
|
||||
if (currentFloors.size < 1) {
|
||||
this.uiElements.floors.classList.add("empty");
|
||||
this.uiElements.controls.classList.add("empty");
|
||||
this.floorsEmpty = true;
|
||||
} else {
|
||||
this.uiElements.floors.classList.remove("empty");
|
||||
this.uiElements.controls.classList.remove("empty");
|
||||
this.floorsEmpty = false;
|
||||
}
|
||||
}
|
||||
|
||||
__updateFloorsSoft() {
|
||||
const currentLocalization = this.mainmanager.getCurrentLocalization();
|
||||
const currentFloors = this.mainmanager.getAllFocusObject("floor");
|
||||
const mainFocus = this.mainmanager.getCurrentFocus("floor");
|
||||
|
||||
|
|
90
assets/styles/dialog.css
Normal file
90
assets/styles/dialog.css
Normal file
|
@ -0,0 +1,90 @@
|
|||
/* I fell into a burning ring of fire / Went down down down / And the flames went higher */
|
||||
#search-dialog-scrim {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100%;
|
||||
background-color: #0008;
|
||||
opacity: 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: opacity 0.125s;
|
||||
}
|
||||
|
||||
#search-dialog-scrim.open {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
#search-dialog {
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #202020;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 8px 0 #0008;
|
||||
flex-direction: column;
|
||||
width: calc(100% - 16px);
|
||||
max-width: 624px;
|
||||
}
|
||||
|
||||
#search-dialog #search-dialog-header {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: auto 36px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#search-dialog #search-dialog-bar {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: auto;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#search-dialog #search-dialog-tags {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
#search-dialog #search-dialog-tags .search-tag {
|
||||
white-space: nowrap;
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
#search-dialog #search-dialog-tags .search-tag.selected {
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
#search-dialog #search-list {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
padding: 8px;
|
||||
background-color: #0002;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#search-dialog #search-list .event-list-container p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#search-dialog #search-list .event-list-container p .reason {
|
||||
opacity: 0.5;
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
body, html {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
|
@ -42,6 +42,7 @@ button:not(.link) {
|
|||
border-radius: 4px;
|
||||
height: 36px;
|
||||
border: 1px solid #0008;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
button:not(.link):hover {
|
||||
|
@ -68,6 +69,23 @@ button.link {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:not(.link):focus,
|
||||
input:focus {
|
||||
outline: 1px solid #808080;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: #0004;
|
||||
border: none;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
box-sizing: border-box;
|
||||
padding: 6px;
|
||||
border-radius: 4px;
|
||||
height: 36px;
|
||||
border: 1px solid #0008;
|
||||
}
|
||||
|
||||
a {
|
||||
color: currentColor;
|
||||
}
|
||||
|
@ -85,25 +103,39 @@ a {
|
|||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#map-ui #controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 8px;
|
||||
background-color: #fff4;
|
||||
border-radius: 0 0 0 4px;
|
||||
}
|
||||
|
||||
#map-ui #controls.empty {
|
||||
right: -100%;
|
||||
}
|
||||
|
||||
#map-ui #zoom {
|
||||
pointer-events: all;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
position: absolute;
|
||||
padding: 8px;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
border-radius: 4px;
|
||||
background-color: #202020;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 8px 0 #0008;
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
#map-ui #zoom .zoom-button {
|
||||
|
@ -114,19 +146,12 @@ a {
|
|||
pointer-events: all;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
padding: 8px;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
border-radius: 4px;
|
||||
background-color: #202020;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 8px 0 #0008;
|
||||
transition: right 0.5s;
|
||||
}
|
||||
|
||||
#map-ui #floors.empty {
|
||||
right: -100%;
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
#map-ui #floors #floor-buttons {
|
||||
|
@ -145,6 +170,27 @@ a {
|
|||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
#map-ui #menu-bar {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
pointer-events: all;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #202020;
|
||||
color: #fff;
|
||||
box-shadow: 0 0 8px 0 #0008;
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
#map-ui #menu-bar .menu-button {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
#map-ui #events-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
@ -156,13 +202,14 @@ a {
|
|||
#map-ui #events-container #events,
|
||||
#map-ui #events-container #events-inspector {
|
||||
pointer-events: all;
|
||||
/* user-select: text; */
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
position: absolute;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
left: 16px;
|
||||
bottom: 16px;
|
||||
left: 8px;
|
||||
bottom: 8px;
|
||||
border-radius: 4px;
|
||||
translate: 0 calc(100% + 32px);
|
||||
background-color: #202020;
|
||||
|
@ -172,7 +219,7 @@ a {
|
|||
max-height: calc(50vh - 16px);
|
||||
flex-direction: column;
|
||||
width: 400px;
|
||||
max-width: calc(100% - 32px);
|
||||
max-width: calc(100% - 16px);
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
|
@ -269,6 +316,7 @@ a {
|
|||
background-color: #2228;
|
||||
border-radius: 8px;
|
||||
opacity: 0;
|
||||
z-index: 1000;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,18 +10,29 @@
|
|||
rel="stylesheet"
|
||||
href="/assets/styles/style.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/assets/styles/dialog.css"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="map"></canvas>
|
||||
<div id="map-ui" class="loading">
|
||||
<div id="zoom">
|
||||
<button class="zoom-button" id="zoom-in">+</button>
|
||||
<button class="zoom-button" id="zoom-out" disabled>-</button>
|
||||
</div>
|
||||
<div id="floors" class="empty">
|
||||
<div id="floor-buttons">
|
||||
|
||||
<div id="controls" class="empty">
|
||||
<div id="zoom">
|
||||
<button class="zoom-button" id="zoom-in">+</button>
|
||||
<button class="zoom-button" id="zoom-out" disabled>-</button>
|
||||
</div>
|
||||
<div id="floors">
|
||||
<div id="floor-buttons">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="menu-bar">
|
||||
<button class="menu-button" id="menu-search">Sc</button>
|
||||
<button class="menu-button" id="menu-lang">Ln</button>
|
||||
<button class="menu-button" id="menu-about">(i)</button>
|
||||
</div>
|
||||
<div id="events-container" class="empty">
|
||||
<div id="events">
|
||||
|
@ -51,6 +62,24 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="search-dialog-scrim">
|
||||
<div id="search-dialog">
|
||||
<div id="search-dialog-header">
|
||||
<div>
|
||||
<h1 class="widget-heading" id="search-dialog-title"></h1>
|
||||
</div>
|
||||
<button id="close">X</button>
|
||||
</div>
|
||||
<div id="search-dialog-bar">
|
||||
<input type="text" id="search-dialog-input" />
|
||||
</div>
|
||||
<div id="search-dialog-tags">
|
||||
</div>
|
||||
<div id="search-list">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loading">
|
||||
<div id="loading-circle">
|
||||
|
||||
|
|
|
@ -22,9 +22,48 @@ app.get('/data/:lang/events/:floor', async (req, res) => {
|
|||
// Merge events and localization
|
||||
const lang = req.params.lang;
|
||||
const merged = events.map(event => {
|
||||
event.name = event.lang[lang]?.name ?? "";
|
||||
event.description = event.lang[lang]?.description ?? "";
|
||||
event.url = event.lang[lang]?.url ?? "";
|
||||
const curLang = event.lang[lang] ?? event.lang[config.default_language];
|
||||
|
||||
event.name = curLang.name ?? "";
|
||||
event.description = curLang.description ?? "";
|
||||
event.url = curLang.url ?? "";
|
||||
delete event.lang;
|
||||
return event;
|
||||
});
|
||||
|
||||
return res.send(merged);
|
||||
})
|
||||
|
||||
app.get('/data/:lang/events/', async (req, res) => {
|
||||
// Get floors
|
||||
let floorsReq = await fetch(new URL("floors.json", config.data_url));
|
||||
let floors;
|
||||
if (floorsReq.ok)
|
||||
floors = await floorsReq.json();
|
||||
else
|
||||
return res.status(400).send("Bad Request");
|
||||
|
||||
let allEvents = [];
|
||||
|
||||
await Promise.allSettled(floors.map(async (curFloor) => {
|
||||
let eventsReq = await fetch(new URL(`events/${curFloor.id}.json`, config.data_url));
|
||||
let events;
|
||||
if (eventsReq.ok)
|
||||
events = await eventsReq.json();
|
||||
else
|
||||
return null;
|
||||
|
||||
allEvents = allEvents.concat(events);
|
||||
}));
|
||||
|
||||
// Merge events and localization
|
||||
const lang = req.params.lang;
|
||||
const merged = allEvents.map(event => {
|
||||
const curLang = event.lang[lang] ?? event.lang[config.default_language];
|
||||
|
||||
event.name = curLang.name ?? "";
|
||||
event.description = curLang.description ?? "";
|
||||
event.url = curLang.url ?? "";
|
||||
delete event.lang;
|
||||
return event;
|
||||
});
|
||||
|
@ -33,21 +72,48 @@ app.get('/data/:lang/events/:floor', async (req, res) => {
|
|||
})
|
||||
|
||||
app.get('/data/:lang/floors', async (req, res) => {
|
||||
// Get layers
|
||||
let layersReq = await fetch(new URL("floors.json", config.data_url));
|
||||
let layers;
|
||||
if (layersReq.ok)
|
||||
layers = await layersReq.json();
|
||||
// Get floors
|
||||
let floorsReq = await fetch(new URL("floors.json", config.data_url));
|
||||
let floors;
|
||||
if (floorsReq.ok)
|
||||
floors = await floorsReq.json();
|
||||
else
|
||||
return res.status(400).send("Bad Request");
|
||||
|
||||
// Merge layers and localization
|
||||
// Merge floors and localization
|
||||
const lang = req.params.lang;
|
||||
const merged = layers.map(layer => {
|
||||
layer.name = layer.lang[lang]?.name ?? "";
|
||||
layer.description = layer.lang[lang]?.description ?? "";
|
||||
delete layer.lang;
|
||||
return layer;
|
||||
const merged = floors.map(floor => {
|
||||
const curLang = floor.lang[lang] ?? floor.lang[config.default_language];
|
||||
|
||||
floor.name = curLang.name ?? "";
|
||||
floor.shortName = curLang.shortName ?? "";
|
||||
floor.description = curLang.description ?? "";
|
||||
delete floor.lang;
|
||||
return floor;
|
||||
});
|
||||
|
||||
return res.send(merged);
|
||||
})
|
||||
|
||||
app.get('/data/:lang/tags', async (req, res) => {
|
||||
// Get tags
|
||||
let tagsReq = await fetch(new URL("tags.json", config.data_url));
|
||||
let tags;
|
||||
if (tagsReq.ok)
|
||||
tags = await tagsReq.json();
|
||||
else
|
||||
return res.status(400).send("Bad Request");
|
||||
|
||||
// Merge floors and localization
|
||||
const lang = req.params.lang;
|
||||
const merged = tags.map(tag => {
|
||||
const curLang = tag.lang[lang] ?? tag.lang[config.default_language];
|
||||
|
||||
tag.name = curLang.name ?? "";
|
||||
tag.shortName = curLang.shortName ?? "";
|
||||
tag.description = curLang.description ?? "";
|
||||
delete tag.lang;
|
||||
return tag;
|
||||
});
|
||||
|
||||
return res.send(merged);
|
||||
|
@ -65,9 +131,11 @@ app.get('/data/:lang/rooms/:floor', async (req, res) => {
|
|||
// Merge rooms and localization
|
||||
const lang = req.params.lang;
|
||||
const merged = rooms.map(room => {
|
||||
room.name = room.lang[lang]?.name ?? "";
|
||||
room.shortName = room.lang[lang]?.shortName ?? "";
|
||||
room.description = room.lang[lang]?.description ?? "";
|
||||
const curLang = room.lang[lang] ?? room.lang[config.default_language];
|
||||
|
||||
room.name = curLang.name ?? "";
|
||||
room.shortName = curLang.shortName ?? "";
|
||||
room.description = curLang.description ?? "";
|
||||
delete room.lang;
|
||||
return room;
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue