import { Room } from "./room.class"; export class App { static floorLabels = [ 'Ground floor', '1st floor', '2nd floor', '3rd floor', '4th floor', '5th floor', '6th floor', ]; static removeClasses = ['active', 'lower', 'raise', 'hilite']; private rooms: Array = []; private roomsDataListElement: HTMLDataListElement; private floorSelectElement: HTMLSelectElement; private filterElement: HTMLInputElement; private labelElement: HTMLHeadingElement; private svgElements: Array; private svgRooms: Array; constructor(params: {rooms: Array}) { this.rooms = params.rooms; this.roomsDataListElement = document.getElementById('roomList'); this.floorSelectElement = document.getElementById('floorSelect'); this.filterElement = document.getElementById('filter'); this.labelElement = document.getElementById('label'); const mapContainer = document.getElementById('map-container'); this.svgElements = Array.from(mapContainer.getElementsByTagName('svg')); this.svgRooms = Array.from(mapContainer.getElementsByClassName('room')); } run() { // init floor selector and input datalist this.initFloorSelect(App.floorLabels) .populateDataList(); // add event listeners for restoring application state document.addEventListener('DOMContentLoaded', () => { const hash = (document.location).hash; return hash ? this.restoreState(hash.split('.')) : null; }); window.addEventListener('popstate', event => this.restoreState(event.state)); // what happens when the floor selector is changed this.floorSelectElement.addEventListener('change', event => this.applyFloorFilter(+(event.target).value)); // what happens when the room filter is changed this.filterElement.addEventListener('change', event => { const room = this.rooms.find(room => room.name.toLocaleLowerCase() === this.filterElement.value.toLocaleLowerCase()); this.selectFloor(room ? room.floor : 7).deselectAllRooms(); if (room) this.selectRoom(room.id).pushRoomState(room); }); // what should happen when a floor is clicked on this.svgElements.map(svg => svg.addEventListener('click', event => { event.stopPropagation(); const svgRoom = this.svgRooms.find(svgRoom => svgRoom.classList.contains('hilite')); if (svgRoom) this.setFloorLabel((this.rooms.find(room => room.id == svgRoom.id)).floor); this.deselectAllRooms(); })); // what should happen when a room is clicked on this.svgRooms.map(svgRoom => svgRoom.addEventListener('click', event => { event.stopPropagation(); const room = this.rooms.find(room => room.id === svgRoom.id); if ((event.target).classList.contains('hilite')) { this.deselectAllRooms().setFloorLabel(room.floor); } else { this.deselectAllRooms().selectRoom(room.id).pushRoomState(room); } })); // add roomname as title to all rooms when user hovers mouse over the room this.rooms.map(room => { const titleElement = document.createElementNS("http://www.w3.org/2000/svg", "title"); titleElement.textContent = `${room.name}`; const svgRoom = document.getElementById(room.id); svgRoom.appendChild(titleElement); svgRoom.classList.add(...Room.typeClassMap[room.type]); }); } genFloorHash(idx: number): string { return `#f.${idx}`; } genRoomHash(room: Room): string { return `#r.${room.floor}.${room.id}`; } initFloorSelect(labels: Array): App { labels.map((floor, idx) => this.floorSelectElement.appendChild(new Option(floor, idx.toLocaleString()))); return this; } populateDataList(floor: number = -1): App { this.rooms.filter(room => floor == -1 ? true : room.floor == floor) .map(room => room.name).sort() .map(roomName => this.roomsDataListElement.appendChild(new Option(roomName))); return this; } clearDataList(): App { while(this.roomsDataListElement.lastChild) { this.roomsDataListElement.removeChild(this.roomsDataListElement.lastChild); } return this; } setLabel(label: string): App { this.labelElement.innerHTML = label; return this; } setFloorLabel(idx: number): App { this.setLabel(App.floorLabels[idx]); return this; } setRoomLabel(room: Room): App { this.setLabel(`${App.floorLabels[room.floor]} - ${room.formattedName}`); return this; } selectFloor(idx: number): App { this.svgElements.map((svg, svgidx) => { svg.classList.remove(...App.removeClasses); svg.classList.add(idx == svgidx ? 'active' : ( svgidx > idx ? 'raise' : 'lower' )); }); return this; } selectRoom(id: string): App { const room = this.rooms.find(room => room.id === id); this.setRoomLabel(room).filterElement.value = room.name; (document.getElementById(id)).classList.add('hilite'); Array.from(document.getElementsByClassName(`floor-${room.floor}`)) .filter(floor => !floor.classList.contains('hilite')) .map(floor => floor.classList.add('hilite')); return this; } deselectAllRooms(): App { this.svgElements.map(svg => svg.classList.remove('hilite')); this.svgRooms.map(room => room.classList.remove('hilite')); return this; } applyFloorFilter(idx: number): App { this.filterElement.value = ''; this.deselectAllRooms().clearDataList().populateDataList(idx); if (idx == -1) { this.setLabel('').svgElements .map(svg => svg.classList.remove(...App.removeClasses)); return this; } return this.setFloorLabel(idx).selectFloor(idx).pushFloorState(idx); } pushState(state: {}, title: string, url: string): App { window.history.pushState(state, title, url); return this; } pushFloorState(idx: number): App { return this.pushState( ['#f', idx], `ETH map - ${App.floorLabels[idx]}`, this.genFloorHash(idx) ); } pushRoomState(room: Room): App { return this.pushState( ['#r', room.floor, room.id], `ETH map - ${App.floorLabels[room.floor]} / ${room.formattedName}`, this.genRoomHash(room) ); } restoreState(params: Array): App { const [cmd,floor,roomId] = [...params]; this.deselectAllRooms(); switch(cmd) { case '#r': this.selectFloor(floor).selectRoom(roomId); break; case '#f': this.selectFloor(floor); break; } return this; } }