/* TODO: New capture panel TODO: Make delete button work TODO: Recordings list TODO: New recording panel TODO: Somehow have Python make funky thumbnails for videos? TODO: Remember a position and restore that position TODO: Right click to auto-focus, centered on that region TODO: Box to specify device IP TODO: Dynamically add stream image src from JS (allows streaming from an external IP) */ var baseURI = "/api/v1" // Key IDs var pgupKeyID = 33; var pgdnKeyID = 34; var leftKeyID = 37; var upKeyID = 38; var rightKeyID = 39; var downKeyID = 40; var enterKeyID = 13; var escKeyID = 27; var keysDown = {}; //Array of keys down var stageVelocity = 50; var focusVelocity = 20; var requestLock = false; // Microscope client-side callibration //TODO: Move to serverside microscope property var fovX = 4100; var fovY = 3146; window.onload = function() { getStagePositions() updateTextBoxes() getCaptures() } function getCaptures() { function updateCapturesCallback(response, status) { console.log(status); updateCaptures(response); } safeRequest("GET", baseURI+"/capture", null, updateCapturesCallback, false) } function updateCaptures(response) { // Clear captures list var capturesNode = document.getElementById("captures"); while (capturesNode.firstChild) { capturesNode.removeChild(capturesNode.firstChild); } // For each capture in the response array response.forEach(function(element) { // Store the capture object URI element_uri = `${baseURI}/capture/${element.id}` // Generate inner HTML from capture object html = `
` // Create a new capture div, and append HTML var div = document.createElement("div"); div.className = "capture"; div.innerHTML = html; capturesNode.appendChild(div); }); } function updateTextBoxes() { document.getElementById('stageVelocityText').value = stageVelocity; document.getElementById('stageVelocityInput').value = stageVelocity; document.getElementById('focusVelocityText').value = focusVelocity; document.getElementById('focusVelocityInput').value = focusVelocity; document.getElementById('fovXText').value = fovX; document.getElementById('fovYText').value = fovY; } function updateStagePositions(response) { document.getElementById('x_abs').value = response.x; document.getElementById('y_abs').value = response.y; document.getElementById('z_abs').value = response.z; } function getStagePositions() { function updatePositionCallback(response, status) { console.log(status, response); updateStagePositions(response); } safeRequest("GET", baseURI+"/position", null, updatePositionCallback, false) } // Capture methods function newCapture(filename, keep_on_disk, use_video_port, resizeWidth=null, resizeHeight=null) { // Make a position request function newCaptureCallback(response, status) { if (status != 200) { alert("Capture failed."); } console.log(status, response); getCaptures(); // Update full list of captures when done } payload = { "filename": filename, "keep_on_disk": keep_on_disk, "use_video_port": use_video_port } if ((resizeWidth) && (resizeHeight)) { payload.size = { "width": resizeWidth, "height": resizeHeight } } console.log(payload); safeRequest("POST", baseURI+"/capture/", payload, newCaptureCallback); } function newCaptureFromInput() { captureFilenameInput = document.getElementById('captureFilenameInput'); captureKeepOnDiskCheck = document.getElementById('captureKeepOnDiskCheck'); captureFullResolutionCheck = document.getElementById('captureFullResolutionCheck'); captureResizeCheck = document.getElementById('captureResizeCheck'); captureWidthInput = document.getElementById('captureWidthInput') captureHeightInput = document.getElementById('captureHeightInput') // Get filename if one is given if (captureFilenameInput.value === "") { filename = null; } else { filename = captureFilenameInput.value; } if (captureResizeCheck.checked) { resizeWidth = Number(captureWidthInput.value); resizeHeight = Number(captureHeightInput.value); } else { resizeWidth = null; resizeHeight = null; } newCapture(filename, captureKeepOnDiskCheck.checked, !(captureFullResolutionCheck.checked), resizeWidth, resizeHeight); } function newCaptureResizeToggle(checkbox) { captureWidthInput = document.getElementById('captureWidthInput') captureHeightInput = document.getElementById('captureHeightInput') captureWidthInput.disabled = !(checkbox.checked) captureHeightInput.disabled = !(checkbox.checked) } // Move stage methods // TODO: Combine setStagePositions and moveStagePositions, with bool argument for 'absolute' function setStagePositions(x_abs, y_abs, z_abs) { // Make a position request function absMoveCallback(response, status) { if (status != 200) { alert("Error moving stage."); } console.log(status, response); updateStagePositions(response); } safeRequest("POST", baseURI+"/position", { "absolute": true, "x": x_abs, "y": y_abs, "z": z_abs}, absMoveCallback) } function moveStagePositions(x_rel, y_rel, z_rel) { // Make a position request function relMoveCallback(response, status) { if (status != 200) { alert("Error moving stage."); } console.log(status, response); updateStagePositions(response); } safeRequest("POST", baseURI+"/position", { "absolute": false, "x": x_rel, "y": y_rel, "z": z_rel}, relMoveCallback) } function setStagePositionsFromInput() { x_abs = Number(document.getElementById('x_abs').value); y_abs = Number(document.getElementById('y_abs').value); z_abs = Number(document.getElementById('z_abs').value); setStagePositions(x_abs, y_abs, z_abs) } // Standard callback for user-controller movement function keyMoveCallback(response, status) { console.log(status, response); updateStagePositions(response); } // Methods for input events // Click-to-move function clickHotspotImage(event) { if (document.getElementById("clickPositionCheck").checked == true) { xCoordinate = event.offsetX; yCoordinate = event.offsetY; console.log(xCoordinate, yCoordinate) xRelative = (0.5*event.target.offsetWidth - xCoordinate)/event.target.offsetWidth; yRelative = (0.5*event.target.offsetHeight - yCoordinate)/event.target.offsetHeight; console.log(xRelative, yRelative) xSteps = xRelative * fovX; ySteps = yRelative * fovY; console.log(xSteps, ySteps, 0) // Make a position request moveStagePositions(xSteps, ySteps, 0) } } // Scroll-to-focus window.addEventListener('wheel', function(e) { multiplier = 1/100; z_delta = e.deltaY * multiplier * focusVelocity; if ((document.getElementById("scrollFocusCheck").checked == true) && (e.target.parentNode.classList.value == "column middle")) { console.log(z_delta); console.log(e.target.parentNode.classList.value); // Make a position request safeRequest("POST", baseURI+"/position", { "absolute": false, "z": z_delta}, keyMoveCallback) } }); // Keyboard to move addEventListener("keydown", function (e) { keysDown[e.keyCode] = true; //Add key to array console.log(keysDown) // If not currently in an input box if (!(e.target instanceof HTMLInputElement)) { // TODO: Remove this if condition? Pointless? // If stage movement keys are pressed if ((leftKeyID in keysDown) || (rightKeyID in keysDown) || (upKeyID in keysDown) || (downKeyID in keysDown) || (pgupKeyID in keysDown) || (pgdnKeyID in keysDown)) { // Calculate movement array x_rel = 0; y_rel = 0; z_rel = 0; if (leftKeyID in keysDown) { x_rel = x_rel + stageVelocity; } if (rightKeyID in keysDown) { x_rel = x_rel - stageVelocity; } if (upKeyID in keysDown) { y_rel = y_rel + stageVelocity; } if (downKeyID in keysDown) { y_rel = y_rel - stageVelocity; } if (pgupKeyID in keysDown) { z_rel = z_rel - focusVelocity; } if (pgdnKeyID in keysDown) { z_rel = z_rel + focusVelocity; } // Make a position request moveStagePositions(x_rel, y_rel, z_rel) } } }, false); addEventListener("keyup", function (e) { delete keysDown[e.keyCode]; //Remove key from array }, false); // Methods for making requests function safeRequest(method, url, payload, callback, lock=true, force=false) { if (force == true) { allowRequest = true; } else { allowRequest = !(requestLock); } if (allowRequest == true) { var xhr = new XMLHttpRequest(); // new HttpRequest instance xhr.open(method, url); if (lock == true) { // If request is using the lock requestLock = true; // Hold the lock } xhr.onload = function (e) { if (xhr.readyState === 4) { if (lock == true) { // If request is using the lock requestLock = false; // Release the lock } if (xhr.status !== 200) { console.error(xhr.statusText); } callback(JSON.parse(xhr.responseText), xhr.status); } }; if (method == "POST" || method == "PUT") { xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); } xhr.send(JSON.stringify(payload)); } else { console.log("Cannot start the following request while the previous requets is pending:") console.log(method, url, payload) } }