From c96e6ffafb62bde1e08987b1fdf3c0786487f6ec Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Tue, 24 Jan 2017 12:06:09 +0100 Subject: Initial commit --- src/scripts/controllers/modes/building.ts | 114 +++++++++ src/scripts/controllers/modes/node.ts | 297 +++++++++++++++++++++++ src/scripts/controllers/modes/object.ts | 297 +++++++++++++++++++++++ src/scripts/controllers/modes/room.ts | 382 ++++++++++++++++++++++++++++++ 4 files changed, 1090 insertions(+) create mode 100644 src/scripts/controllers/modes/building.ts create mode 100644 src/scripts/controllers/modes/node.ts create mode 100644 src/scripts/controllers/modes/object.ts create mode 100644 src/scripts/controllers/modes/room.ts (limited to 'src/scripts/controllers/modes') diff --git a/src/scripts/controllers/modes/building.ts b/src/scripts/controllers/modes/building.ts new file mode 100644 index 00000000..4d82f090 --- /dev/null +++ b/src/scripts/controllers/modes/building.ts @@ -0,0 +1,114 @@ +import {InteractionMode, MapController} from "../mapcontroller"; +import {MapView} from "../../views/mapview"; +import * as $ from "jquery"; + + +/** + * Class responsible for handling building mode interactions. + */ +export class BuildingModeController { + public newRoomId: number; + + private mapController: MapController; + private mapView: MapView; + + + constructor(mapController: MapController) { + this.mapController = mapController; + this.mapView = this.mapController.mapView; + } + + /** + * Connects all DOM event listeners to their respective element targets. + */ + public setupEventListeners() { + let resetConstructionButtons = () => { + this.mapController.interactionMode = InteractionMode.DEFAULT; + this.mapView.hoverLayer.setHoverTileVisibility(false); + $("#room-construction").text("Construct new room"); + $("#room-construction-cancel").slideToggle(300); + }; + + // Room construction button + $("#room-construction").on("click", (event: JQueryEventObject) => { + if (this.mapController.interactionMode === InteractionMode.DEFAULT) { + this.mapController.interactionMode = InteractionMode.SELECT_ROOM; + this.mapView.hoverLayer.setHoverTileVisibility(true); + this.mapController.api.addRoomToDatacenter(this.mapView.simulation.id, + this.mapView.currentDatacenter.id).then((room: IRoom) => { + this.newRoomId = room.id; + }); + $(event.target).text("Finalize room"); + $("#room-construction-cancel").slideToggle(300); + } else if (this.mapController.interactionMode === InteractionMode.SELECT_ROOM) { + resetConstructionButtons(); + this.finalizeRoom(); + } + }); + + // Cancel button for room construction + $("#room-construction-cancel").on("click", () => { + resetConstructionButtons(); + this.cancelRoomConstruction(); + }); + } + + /** + * Cancels room construction and deletes the temporary room created previously. + */ + public cancelRoomConstruction() { + this.mapController.api.deleteRoom(this.mapView.simulation.id, + this.mapView.currentDatacenter.id, this.newRoomId).then(() => { + this.mapView.roomLayer.cancelRoomConstruction(); + }); + } + + /** + * Finalizes room construction by triggering a redraw of the room layer with the new room added. + */ + public finalizeRoom() { + this.mapController.api.getRoom(this.mapView.simulation.id, + this.mapView.currentDatacenter.id, this.newRoomId).then((room: IRoom) => { + this.mapView.roomLayer.finalizeRoom(room); + }); + } + + /** + * Adds a newly selected tile to the list of selected tiles. + * + * @param position The new tile position to be added + */ + public addSelectedTile(position: IGridPosition): void { + let tile = { + id: -1, + roomId: this.newRoomId, + position: {x: position.x, y: position.y} + }; + this.mapController.api.addTileToRoom(this.mapView.simulation.id, + this.mapView.currentDatacenter.id, this.newRoomId, tile).then((tile: ITile) => { + this.mapView.roomLayer.addSelectedTile(tile); + }); + } + + /** + * Removes a previously selected tile. + * + * @param position The position of the tile to be removed + */ + public removeSelectedTile(position: IGridPosition): void { + let tile; + let objectIndex = -1; + + for (let i = 0; i < this.mapView.roomLayer.selectedTileObjects.length; i++) { + tile = this.mapView.roomLayer.selectedTileObjects[i]; + if (tile.position.x === position.x && tile.position.y === position.y) { + objectIndex = i; + } + } + this.mapController.api.deleteTile(this.mapView.simulation.id, + this.mapView.currentDatacenter.id, this.newRoomId, + this.mapView.roomLayer.selectedTileObjects[objectIndex].tileObject.id).then(() => { + this.mapView.roomLayer.removeSelectedTile(position, objectIndex); + }); + } +} \ No newline at end of file diff --git a/src/scripts/controllers/modes/node.ts b/src/scripts/controllers/modes/node.ts new file mode 100644 index 00000000..3b3f8a32 --- /dev/null +++ b/src/scripts/controllers/modes/node.ts @@ -0,0 +1,297 @@ +import {MapController, AppMode, InteractionLevel} from "../mapcontroller"; +import {MapView} from "../../views/mapview"; +import * as $ from "jquery"; + + +/** + * Class responsible for rendering node mode and handling UI interactions within it. + */ +export class NodeModeController { + public currentMachine: IMachine; + + private mapController: MapController; + private mapView: MapView; + + + constructor(mapController: MapController) { + this.mapController = mapController; + this.mapView = this.mapController.mapView; + + this.loadAddDropdowns(); + } + + /** + * Moves the UI model into node mode. + * + * @param machine The machine that was selected in rack mode + */ + public enterMode(machine: IMachine): void { + this.currentMachine = machine; + this.populateUnitLists(); + $("#node-menu").removeClass("hidden"); + + if (this.mapController.appMode === AppMode.SIMULATION) { + this.mapController.simulationController.transitionFromRackToNode(); + } + } + + /** + * Performs cleanup and closing actions before allowing transferal to rack mode. + */ + public goToObjectMode(): void { + $("#node-menu").addClass("hidden"); + $(".node-element-overlay").addClass("hidden"); + this.currentMachine = undefined; + this.mapController.interactionLevel = InteractionLevel.OBJECT; + + if (this.mapController.appMode === AppMode.SIMULATION) { + this.mapController.simulationController.transitionFromNodeToRack(); + } + } + + /** + * Connects all DOM event listeners to their respective element targets. + */ + public setupEventListeners(): void { + let nodeMenu = $("#node-menu"); + + nodeMenu.find(".panel-group").on("click", ".remove-unit", (event: JQueryEventObject) => { + MapController.showConfirmDeleteDialog("unit", () => { + let index = $(event.target).closest(".panel").index(); + + if (index === -1) { + return; + } + + let closestTabPane = $(event.target).closest(".panel-group"); + + let objectList, idList; + if (closestTabPane.is("#cpu-accordion")) { + objectList = this.currentMachine.cpus; + idList = this.currentMachine.cpuIds; + } else if (closestTabPane.is("#gpu-accordion")) { + objectList = this.currentMachine.gpus; + idList = this.currentMachine.gpuIds; + } else if (closestTabPane.is("#memory-accordion")) { + objectList = this.currentMachine.memories; + idList = this.currentMachine.memoryIds; + } else if (closestTabPane.is("#storage-accordion")) { + objectList = this.currentMachine.storages; + idList = this.currentMachine.storageIds; + } + + idList.splice(idList.indexOf(objectList[index]).id, 1); + objectList.splice(index, 1); + + this.mapController.api.updateMachine(this.mapView.simulation.id, + this.mapView.currentDatacenter.id, this.mapController.roomModeController.currentRoom.id, + this.mapController.objectModeController.currentObjectTile.id, this.currentMachine).then( + () => { + this.populateUnitLists(); + this.mapController.objectModeController.updateNodeComponentOverlays(); + }); + }); + }); + + nodeMenu.find(".add-unit").on("click", (event: JQueryEventObject) => { + let dropdown = $(event.target).closest(".input-group-btn").siblings("select").first(); + + let closestTabPane = $(event.target).closest(".input-group").siblings(".panel-group").first(); + let objectList, idList, typePlural; + if (closestTabPane.is("#cpu-accordion")) { + objectList = this.currentMachine.cpus; + idList = this.currentMachine.cpuIds; + typePlural = "cpus"; + } else if (closestTabPane.is("#gpu-accordion")) { + objectList = this.currentMachine.gpus; + idList = this.currentMachine.gpuIds; + typePlural = "gpus"; + } else if (closestTabPane.is("#memory-accordion")) { + objectList = this.currentMachine.memories; + idList = this.currentMachine.memoryIds; + typePlural = "memories"; + } else if (closestTabPane.is("#storage-accordion")) { + objectList = this.currentMachine.storages; + idList = this.currentMachine.storageIds; + typePlural = "storages"; + } + + if (idList.length + 1 > 4) { + this.mapController.showInfoBalloon("Machine has only 4 slots", "warning"); + return; + } + + let id = parseInt(dropdown.val(), 10); + idList.push(id); + this.mapController.api.getSpecificationOfType(typePlural, id).then((spec: INodeUnit) => { + objectList.push(spec); + + this.mapController.api.updateMachine(this.mapView.simulation.id, + this.mapView.currentDatacenter.id, this.mapController.roomModeController.currentRoom.id, + this.mapController.objectModeController.currentObjectTile.id, this.currentMachine).then( + () => { + this.populateUnitLists(); + this.mapController.objectModeController.updateNodeComponentOverlays(); + }); + }); + }); + } + + /** + * Populates the "add" dropdowns with all available unit options. + */ + private loadAddDropdowns(): void { + let unitTypes = [ + "cpus", "gpus", "memories", "storages" + ]; + let dropdowns = [ + $("#add-cpu-form").find("select"), + $("#add-gpu-form").find("select"), + $("#add-memory-form").find("select"), + $("#add-storage-form").find("select"), + ]; + + unitTypes.forEach((type: string, index: number) => { + this.mapController.api.getAllSpecificationsOfType(type).then((data: any) => { + data.forEach((option: INodeUnit) => { + dropdowns[index].append($("