//Make sure canvasses are in the correct positionG var maincanvas= document.querySelector("MainCanvas"); var MainCanvas = document.getElementById("MainCanvas"); var MainCanvas_ctx = MainCanvas.getContext("2d"); var maincanvasBackground="lightgrey"; var mouseIsDown=false; var mouseDownLocation; var mouseDidMovement=false; var imageSize=40; var small_button_size = 25; var uiDeviceInfoLevel = 1; //What do we display when we look at the network var uiActions = [];//a list of things on the screen we can click on or process. var ui_highlightRect = null; var ui_HadHighlight = false; var ui_status_height = 25; var ui_StatusText=""; //The user interface mode. 0=network, 1=network information, 2=puzzle-selection menu var uiMode=1; const imageCollection = loadImages( ["ArrowUp", "ArrowDown", "animations", "burnmark", "cellphone", "circle", "copier", "firewall", "fluorescent", "hub", "ip_phone", "laptop", "link", "microwave", "pc", "printer", "router", "select", "server", "shapes", "square", "switch", "tablet", "tree", "vidimage", "wap", "wbridge", "wrepeater", "wrouter", "x", "info", "menu", "eye", "queryuser", "thumb", ], ["img/ArrowUp.png", "img/ArrowDown.png", "img/Animations.png", "img/BurnMark.png", "img/cellphone.png", "img/Circle.png", "img/Copier.png", "img/firewall.png", "img/fluorescent.png", "img/Hub.png", "img/ip_phone.png", "img/Laptop.png", "img/link.png", "img/microwave.png", "img/PC.png", "img/Printer.png", "img/Router.png", "img/select.png", "img/Server.png", "img/Shapes.png", "img/Square.png", "img/Switch.png", "img/tablet.png", "img/tree.png", "img/VidImage.png", "img/WAP.png", "img/WBridge.png", "img/WRepeater.png", "img/WRouter.png", "img/X.png", "img/info.png", "img/menu.png", "img/eye.png", "img/menu.png", "img/thumb.png", ], InitializeGameMenu // this is called when all images have loaded. ); var menuItemSize=50; function loadImages(names, files, onAllLoaded) { var i = 0, numLoading = names.length; const onload = () => --numLoading === 0 && onAllLoaded(); const images = {}; while (i < names.length) { const img = images[names[i]] = new Image; img.src = files[i++]; img.onload = onload; } return images; } function imageFromName(name) { return imageCollection[name]; } function InitializeGameMenu() { console.log("Initializing"); MainCanvas.addEventListener("touchstart", handleTouchStart); MainCanvas.addEventListener("touchend", handleTouchEnd); MainCanvas.addEventListener("touchcancel", handleTouchCancel); MainCanvas.addEventListener("touchmove", handleTouchMove); MainCanvas.addEventListener('mousedown',handleMouseDown); MainCanvas.addEventListener('mouseup',handleMouseUp); MainCanvas.addEventListener('mousemove',handleMouseMove); //MainCanvas_ctx.drawImage(imageCollection['router'],100,100,50,50); //MainCanvas_ctx.drawImage(imageCollection['firewall'],150,150,50,50); InitializeSelectMenu(); //It should be printed PrintScreen(); } //Print the screen. Figure out what needs to be printed based on the mode function PrintScreen(WhatPassedIn=-1) { //allow us to override what is printed var what=uiMode; if(WhatPassedIn >=0) what=WhatPassedIn; //Clear out any old ActionStructs. They will get filled in as we print the screen. clearActionStructs(); console.log("PrintingScreen for mode: " + what); var rect; if(what == 0) { //The network drawing mode. Print the network //Clear the old screen MainCanvas_ctx.fillStyle = maincanvasBackground; MainCanvas_ctx.fillRect(0, 0, MainCanvas.width, MainCanvas.height); //Do any highlight we need to if (ui_highlightRect !== null) { //console.log("trying to highlight something: " + JSON.stringify(ui_highlightRect)); MainCanvas_ctx.fillStyle = "white"; MainCanvas_ctx.globalAlpha = 0.4; //mostly transparent if (ui_highlightRect.shapeText == "square") MainCanvas_ctx.fillRect(ui_highlightRect.sx, ui_highlightRect.sy, ui_highlightRect.deltax, ui_highlightRect.deltay); else if (ui_highlightRect.shapeText == "line") { var oldWidth = MainCanvas_ctx.lineWidth; MainCanvas_ctx.lineWidth += 6; MainCanvas_ctx.strokeStyle = "white"; MainCanvas_ctx.beginPath(); MainCanvas_ctx.moveTo(ui_highlightRect.sx, ui_highlightRect.sy); MainCanvas_ctx.lineTo(ui_highlightRect.dx, ui_highlightRect.dy); MainCanvas_ctx.stroke(); MainCanvas_ctx.lineWidth = oldWidth; MainCanvas_ctx.strokeStyle = "black"; } MainCanvas_ctx.globalAlpha = 1.0; //reset MainCanvas_ctx.fillStyle = "black"; //reset ui_HadHighlight = true; } else ui_HadHighlight = false; //Draw the puzzle-select button //Put the X there so we can click on it rect = makeRectangle(MainCanvas.width - small_button_size, 0, small_button_size, small_button_size); MainCanvas_ctx.drawImage(imageFromName("menu"),rect.sx,rect.sy,rect.deltax,rect.deltay); registerActionStruct("square", rect, null, ui_PuzzleChoiceMenuLeftClick, null, generic_mouseoverHighlight); //Draw the info button rect = makeRectangle(MainCanvas.width - small_button_size, small_button_size, small_button_size, small_button_size); MainCanvas_ctx.drawImage(imageFromName("info"), rect.sx, rect.sy, rect.deltax, rect.deltay); registerActionStruct("square", rect, null, ui_InfoLeftClick, null, generic_mouseoverHighlight); //Draw the eye button rect = makeRectangle(MainCanvas.width - small_button_size, small_button_size * 2, small_button_size, small_button_size); MainCanvas_ctx.drawImage(imageFromName("eye"), rect.sx, rect.sy, rect.deltax, rect.deltay); registerActionStruct("square", rect, null, ui_eyeLeftClick, null, generic_mouseoverHighlight); //Draw simple lines to show boundaries //Select menu separation MainCanvas_ctx.lineWidth = 2; MainCanvas_ctx.strokeStyle = "white"; MainCanvas_ctx.beginPath(); MainCanvas_ctx.moveTo(menuItemSize, 0); //top side MainCanvas_ctx.lineTo(menuItemSize, MainCanvas.height); //Bottom MainCanvas_ctx.stroke(); //Right-side menu separator MainCanvas_ctx.beginPath(); MainCanvas_ctx.moveTo(MainCanvas.width - small_button_size, 0); //top side MainCanvas_ctx.lineTo(MainCanvas.width - small_button_size, MainCanvas.height - ui_status_height); //Bottom MainCanvas_ctx.stroke(); //Status Bar separator MainCanvas_ctx.beginPath(); MainCanvas_ctx.moveTo(menuItemSize, MainCanvas.height - ui_status_height); //top side MainCanvas_ctx.lineTo(MainCanvas.width, MainCanvas.height - ui_status_height); //Bottom MainCanvas_ctx.stroke(); var statustext = puzzle.name + ": " + ui_StatusText; printLeftJustifiedText(MainCanvas_ctx, statustext, menuItemSize + 5, (MainCanvas.height - ui_status_height) + 7); MainCanvas_ctx.strokeStyle = "black"; drawSelectMenu(); PrintAllNetworkLinks(); PrintAllNetworkDevices(); } else if(what == 1) //PuzzleDescription/Info { //Display the text about the puzzle textMenuPrint(puzzle.en_message); } else if(what == 2) //PuzzleSelect { //TextMenuSelection PrintPuzzleSelectMenu(0); } } function handleTouchStart(evt) { handleMouseDown(copyLocation(evt)); } function handleTouchEnd(evt) { handleMouseUp(copyLocation(evt)); } function handleTouchCancel(evt) { evt.preventDefault(); console.log("canceling touch"); } function handleTouchMove(evt) { //evt.preventDefault(); if(evt.touches.length > 0) { handleMouseMove(copyLocation(evt.touches[0])); } else{ //console.log("not enough touches"); } //console.log("moving touch"); } function handleMouseDown(evt) { mouseDownLocation = copyLocation(evt); mouseIsDown=true; //console.log("mousedown"); if(uiMode==0) SelectMenu_handleMouseDown(mouseDownLocation); } function CheckForActions(actionPoint, action) { if (uiActions.length >= 0) { var checkit = false; var inside = false; for (var index = 0; index < uiActions.length; index++) { if (action == "leftclick" && uiActions[index].funcLeftClick !== null) checkit = true; if (action == "rightclick" && uiActions[index].funcRightClick !== null) checkit = true; if (action == "mouseover" && uiActions[index].funcMouseover !== null) checkit = true; checklocation = uiActions[index]; var point = newPointFromPair(actionPoint.pageX - checklocation.shapePoints.offsetx, actionPoint.pageY - checklocation.shapePoints.offsety); if (checkit) { //See if the click is inside the shape if (checklocation.shapeText == "square") { if (pointInRect(point, checklocation.shapePoints)) inside = true; } if (checklocation.shapeText == "line") { if (pointInRect(point, checklocation.shapePoints)) { //console.log("inside line square"); //We are inside the box. Now determine if we are on the line... var d1 = distance(checklocation.shapePoints.sx, checklocation.shapePoints.sy, actionPoint.pageX, actionPoint.pageY); d1 += distance(checklocation.shapePoints.dx, checklocation.shapePoints.dy, actionPoint.pageX, actionPoint.pageY); var d2 = distance(checklocation.shapePoints.sx, checklocation.shapePoints.sy, checklocation.shapePoints.dx, checklocation.shapePoints.dy); if (Math.abs(d1 - d2) < 3) { inside = true; //console.log("on a line!"); } } } } if (inside) { //console.log("Is inside"); switch (action) { case "leftclick": if (checklocation.funcLeftClick != null) { checklocation.funcLeftClick(actionPoint, checklocation); //console.log("Successfully did a UI action"); return true; } break; case "mouseover": if (checklocation.funcMouseover != null) { checklocation.funcMouseover(actionPoint, checklocation); //console.log("Successfully did a UI action"); return true; } break; } } } } return false; } function pointInRect(point, rectangle) { if (point.x >= Math.min(rectangle.sx,rectangle.dx)) { if (point.x <= Math.max(rectangle.sx, rectangle.dx)) { if (point.y >= Math.min(rectangle.sy, rectangle.dy)) { if (point.y <= Math.max(rectangle.sy, rectangle.dy)) { return true; } } } } return false; } //return the distance between two points function distance(x1, y1, x2, y2) { return Math.hypot(x2 - x1, y2 - y1); } function handleMouseUp(evt) { //evt.preventDefault(); mouseIsDown=false; //See if we have a mouse click let deltaX = Math.abs(evt.pageX - mouseDownLocation.pageX); let deltaY = Math.abs(evt.pageY - mouseDownLocation.pageY); //console.log("delta " + deltaX + "," + deltaY); if(deltaY < 3 && deltaX <3) { //We did not move much. Assume click if(evt.pageX <= menuItemSize && !mouseDidMovement) { if(uiMode==0) SelectMenu_handleMouseUp(evt); //We are in the item select menu. } if(!mouseDidMovement) { //If we are not dragging, it is a click var myevt = copyLocation(evt); //console.log("evt:" + JSON.stringify(myevt)); if (CheckForActions(myevt, "leftclick")) return; //If we did this, do not do anything else. if(uiMode==1) TextWindow_handleMouseUp(evt); else if(uiMode==2) TextWindow_handleMouseUp(evt); } } mouseDidMovement=false; //reset it after we raise the button } function ui_PuzzleChoiceMenuLeftClick(evt) { //We clicked on the puzzle-select menu console.log("PuzzleSelect pressed in action"); uiMode = 2; PrintScreen(); } function ui_eyeLeftClick(evt) { console.log("Selected 'eye' button in action"); //It is the eye button uiDeviceInfoLevel++; if (uiDeviceInfoLevel > 3) uiDeviceInfoLevel = 0; PrintScreen(); } function ui_InfoLeftClick(evt) { console.log("Selected info button in action"); //It is the info button uiMode = 1; PrintScreen(); } function handleMouseMove(evt) { //evt.preventDefault(); if(mouseIsDown) { let deltaX = evt.pageX - mouseDownLocation.pageX; let deltaY = evt.pageY - mouseDownLocation.pageY; if(isNaN(deltaY)) deltaY=0; if(isNaN(deltaX)) deltaX=0; //we are dragging //console.log('mousemove ' + evt.pageX + " " + evt.pageY + " delta " + deltaY ); if(uiMode == 0) { SelectMenu_handleMouseMove(evt); } mouseDidMovement=true; } else //Mouse is not down. Not dragging { var needrefresh = false; var oldRect = structuredClone(ui_highlightRect); if (!CheckForActions(evt, "mouseover")) { //We did not find anything if (JSON.stringify(ui_highlightRect) === JSON.stringify(oldRect)) { } else { //console.log("Rects are not equal:" + JSON.stringify(ui_highlightRect) + " - " + JSON.stringify(oldRect) ) needrefresh = true; } ui_highlightRect = null; //nothing to highlight if (ui_HadHighlight) { needrefresh = true; } if (needrefresh) { PrintScreen(); } } if(uiMode==2) { textMenu_HandleMouseMove(evt); } } return; } function copyLocation({ pageX, pageY }) { return { pageX, pageY }; } function PrintNetworkLink(linkToPrint) { //We should have passed in a working link, make sure it exists if(linkToPrint !== null) { if(linkToPrint.SrcNic !== null && linkToPrint.DestNic !== null) { //console.log("printing link from " + linkToPrint.SrcNic.hostname); sdevice = deviceFromID(linkToPrint.SrcNic.hostid); ddevice = deviceFromID(linkToPrint.DstNic.hostid); if(sdevice !== null && ddevice !== null) { //We have an existing link with two devices. Find the device locations and print var spoint = convertXYPointToActual(newPointFromString(sdevice.location)); var dpoint = convertXYPointToActual(newPointFromString(ddevice.location)); //Make an actionstruct var actionLine = makeLine(spoint.x, spoint.y, dpoint.x, dpoint.y); registerActionStruct("line", actionLine, linkToPrint, null, null, generic_mouseoverHighlight); var old //Now we draw a line between them MainCanvas_ctx.beginPath(); MainCanvas_ctx.moveTo(spoint.x,spoint.y); MainCanvas_ctx.lineTo(dpoint.x,dpoint.y); MainCanvas_ctx.stroke(); } } } } function PrintAllNetworkLinks() { if (puzzle == null) return; //If the puzzle has not been set, exit let index=0; if(puzzle.link !== null && typeof(puzzle.link) === "object") { while (index < puzzle.link.length) { PrintNetworkLink(puzzle.link[index]); index++; } } } function PrintNetworkDevice(ToPrint) { //We should have passed in a working device, make sure it exists if(ToPrint !== null) { var rect = deviceRectangle(ToPrint); var actionrect = makeRectangle(rect.spoint.x, rect.spoint.y, rect.width, rect.height); var dname = ToPrint.mytype; if(dname=="net_switch") dname="switch"; if(dname=="net_hub") dname="hub"; //console.log("printing device " + dname); MainCanvas_ctx.drawImage(imageFromName(dname), rect.spoint.x, rect.spoint.y, rect.width, rect.height); registerActionStruct("square", actionrect, ToPrint, device_clickOn, null, generic_mouseoverHighlight); //Now, we see if we need to print the name, or a list of IPs.. var xpoint = rect.center.x; var ystart = rect.epoint.y; //the bottom-most Y point var gap = 3; var delta; var ipaddresses = ipsFromDevice(ToPrint); //console.log("addresses: " + JSON.stringify(ipaddresses)); switch (uiDeviceInfoLevel) { case 0: //Do not print anything break; case 1: //Print the name printCenteredText(MainCanvas_ctx, ToPrint.hostname, xpoint, ystart); break; case 2: case 3: //console.log("printing device " + ToPrint.hostname); //Print both the name and the IP addresses if (uiDeviceInfoLevel == 2) { delta = printCenteredText(MainCanvas_ctx, ToPrint.hostname, xpoint, ystart) + gap; ystart += delta / 2; } //print the ip addresses for (var x = 0; x < ipaddresses.length; x++) { //Print the IP address if the type is correct. //console.log(JSON.stringify(ipaddresses[x])); switch (ipaddresses[x].nictype) { case "eth": case "management_interface": //console.log("Found a " + ipaddresses[x].nictype) let mystring = ipaddresses[x].cidrip; delta = printCenteredText(MainCanvas_ctx, mystring, xpoint, ystart); ystart += (delta / 2); break; } } break; } } } function PrintAllNetworkDevices() { if (puzzle == null) return; //If the puzzle has not been set, exit let index=0; while (index < puzzle.device.length) { PrintNetworkDevice(puzzle.device[index]); index++; } } //print centered text. We use y as the top-most y position. But we center around the x position function printCenteredText(canvas_context, text, centerx, top_y, font = "15px serif", textcolor="black") { var metrics = canvas_context.measureText(text); var yHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent + tmTextYGap; var xWidth = metrics.width; var oldfill = canvas_context.fillStyle; var oldstroke = canvas_context.strokeStyle; canvas_context.font = font; canvas_context.fillStyle = textcolor; canvas_context.strokeStyle = textcolor; canvas_context.fillText(text, centerx - (xWidth / 2), top_y + (yHeight / 3)); //reset stuff when done canvas_context.fillStyle = oldfill; canvas_context.strokeStyle = oldstroke; return yHeight; //report back how much space we used. Just in case they want it. } //print text. We use y as the top-most y position. function printLeftJustifiedText(canvas_context, text, LeftX, top_y, font = "15px serif", textcolor = "black") { var metrics = canvas_context.measureText(text); var yHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent + tmTextYGap; var xWidth = metrics.width; var oldfill = canvas_context.fillStyle; var oldstroke = canvas_context.strokeStyle; canvas_context.font = font; canvas_context.fillStyle = textcolor; canvas_context.strokeStyle = textcolor; canvas_context.fillText(text, LeftX, top_y + (yHeight / 3)); //reset stuff when done canvas_context.fillStyle = oldfill; canvas_context.strokeStyle = oldstroke; return yHeight; //report back how much space we used. Just in case they want it. } function convertXYPointToActual(point) { //We have an x and y coordinate which needs to be converted to the canvas size var deltax = (MainCanvas.width - menuItemSize) / puzzle.width; var deltay = MainCanvas.height / puzzle.height; return newPointFromPair((point.x * deltax) + menuItemSize, point.y * deltay); } function convertXYpairToActual(x,y) { return convertXYPointToActual(newPointFromPair(x,y)); } function newPointFromPair(x,y) { var point = { 'x' : Math.floor(x), 'y' : Math.floor(y) } return point; } function newPointFromString(pointasstring) { if(typeof(pointasstring) == "string") { var tarray=pointasstring.split(","); return newPointFromPair(Number(tarray[0]),Number(tarray[1])); } } //return a rectangle for the device function deviceRectangle(theDevice) { var centerpoint = convertXYPointToActual(newPointFromString(theDevice.location)); var delta = imageSize / 2; var rect = { spoint : newPointFromPair(centerpoint.x-delta, centerpoint.y-delta), height : imageSize, width : imageSize, epoint : newPointFromPair(centerpoint.x + delta, centerpoint.y + delta), center: centerpoint, } return rect; } function makeRectangle(x1, y1, deltax, deltay, offsetx = 0, offsety = 0) { //The offset is for when we are drawing on a cached surface. It adds x or y to the point we are looking at var struct = { sx: x1, sy: y1, dx: x1 + deltax, dy: y1 + deltay, deltay: deltay, deltax: deltax, offsetx: offsetx, offsety: offsety, } return struct; } function makeLine(x1, y1, x2, y2, offsetx = 0, offsety = 0) { var linestruct = makeRectangle(x1, y1, x2 - x1, y2 - y1, offsetx, offsety); //console.log("Creating a line: " + JSON.stringify(linestruct)); return linestruct; } //Make a structure to hold all our data function actionStruct(shapeText, shapePoints, theObject=null, funcLeftClick=null, funcRightClick=null, funcMouseover=null) { var struct = { shapeText: shapeText, shapePoints: structuredClone(shapePoints), theObject: theObject, funcLeftClick: funcLeftClick, funcRightClick: funcRightClick, funcMouseover: funcMouseover, } shapePoints.shapeText = shapeText; return struct; } function registerActionStruct(shapeText, shapePoints, theObject=null, funcLeftClick=null, funcRightClick=null, funcMouseover=null) { //Make an object with all the data var what = actionStruct(shapeText, shapePoints, theObject, funcLeftClick, funcRightClick, funcMouseover); //console.log("Pushing an action: " + shapeText); //Push it onto the uiActions list uiActions.unshift(what); //Put it at the beginning of the list //console.log("ActionList: " + JSON.stringify(uiActions)); } function clearActionStructs() { uiActions = []; } function setStatus(text) { ui_StatusText = text; //set the text. } //This takes generic information for us to highlight the background function generic_mouseoverHighlight(point, actionrec) { //console.log("Found highlight " + JSON.stringify(actionrec)); //The point is the place where the mouse is, but the actionrec.shapePoints is the rectangle (or shape) we want to highlight var oldrec = structuredClone(ui_highlightRect); if (actionrec.shapeText == "square") { ui_highlightRect = structuredClone(actionrec.shapePoints); ui_highlightRect.shapeText = "square"; //console.log("setting highlights to:" + JSON.stringify(ui_highlightRect)); } if (actionrec.shapeText == "line") { ui_highlightRect = structuredClone(actionrec.shapePoints); ui_highlightRect.shapeText = "line"; //console.log("setting highlights to:" + JSON.stringify(ui_highlightRect)); } if (JSON.stringify(ui_highlightRect) === JSON.stringify(oldrec)) { //they are the same. Nothing to do } else PrintScreen(); //two different areas. Need to print the screen } function device_clickOn(point, actionrec) { if (actionrec.theObject !== null && actionrec.theObject.name !== null) { setStatus(actionrec.theObject.hostname); //We probably do not want to do printecreen here, but we are doing it here now... PrintScreen(); } }