diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..5cbde52 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,248 @@ +// The local/remote server URL +//var ROBOT_SERVER = "10.42.0.1"; +var ROBOT_SERVER = "ws.achex.ca:4010"; + +// The sumorobot object +var sumorobot = null; +// The sumorobot state +var sumostart = false; +// Disable / enable coding mode +var codingEnabled = false; +// Disable / enable live stream +var liveStreamVisible = false; + +// Where Blockly Python code is shown +var readOnlyCodingEditor = null; +// Last hightlighted block id +var lastHighlighted = ""; +// Block highlight WebSocket +var blockHighlight = null; + +window.addEventListener("load", function() { + $("#robot-id").val(getLocalStorageItem("sumorobot.robotId")); + // Load read only ace editor + readOnlyCodingEditor = ace.edit("readOnlyBlocklyCode"); + // Set the style + readOnlyCodingEditor.setTheme("ace/theme/textmate"); + readOnlyCodingEditor.session.setMode("ace/mode/python"); + readOnlyCodingEditor.session.setTabSize(2); + // Make as read only + readOnlyCodingEditor.setReadOnly(true); + // Disable scrolling warning + readOnlyCodingEditor.$blockScrolling = Infinity; + + // Load ace editor + var codingEditor = ace.edit("blocklyCode"); + // Set the style + codingEditor.setTheme("ace/theme/textmate"); + codingEditor.session.setMode("ace/mode/python"); + codingEditor.session.setTabSize(2); + // Disable scrolling warning + codingEditor.$blockScrolling = Infinity; + // Enable autocomplete + ace.require("ace/ext/language_tools"); + codingEditor.setOptions({ + enableSnippets: true, + enableLiveAutocompletion: true, + enableBasicAutocompletion: true + }); + // Add autocomplete keywords + codingEditor.completers.push({ + getCompletions: function(editor, session, pos, prefix, callback) { + callback(null, [ + {value: "STOP", score: 1000, meta: "sumorobot"}, + {value: "LEFT", score: 1000, meta: "sumorobot"}, + {value: "RIGHT", score: 1000, meta: "sumorobot"}, + {value: "SEARCH", score: 1000, meta: "sumorobot"}, + {value: "FORWARD", score: 1000, meta: "sumorobot"}, + {value: "BACKWARD", score: 1000, meta: "sumorobot"}, + {value: "STATUS", score: 1000, meta: "sumorobot"}, + {value: "LEFT_LINE", score: 1000, meta: "sumorobot"}, + {value: "RIGHT_LINE", score: 1000, meta: "sumorobot"}, + {value: "sumorobot", score: 1000, meta: "sumorobot"}, + {value: "move", score: 1000, meta: "sumorobot"}, + {value: "sleep", score: 1000, meta: "sumorobot"}, + {value: "set_led", score: 1000, meta: "sumorobot"}, + {value: "is_line", score: 1000, meta: "sumorobot"}, + {value: "get_line", score: 1000, meta: "sumorobot"}, + {value: "set_servo", score: 1000, meta: "sumorobot"}, + {value: "is_opponent", score: 1000, meta: "sumorobot"}, + {value: "calibrate_line", score: 1000, meta: "sumorobot"}, + {value: "get_battery_voltage", score: 1000, meta: "sumorobot"}, + {value: "get_opponent_distance", score: 1000, meta: "sumorobot"} + ]); + } + }); + // Set the code to the saved code from local storage or empty + codingEditor.setValue(getLocalStorageItem("sumorobot.code") || ""); + // Clear the selection after setting the value + codingEditor.clearSelection(); + // Add an change listener for the code editor + codingEditor.on("change", function() { + // When change occurs, save the new code to the localstorage + setLocalStorageItem("sumorobot.code", codingEditor.getValue()) + }); + + // Key down event + $(document).keydown(function(e) { + // When the alt key is not pressed, don't process hotkeys + if (e.altKey == false) return; + + // Prevent typing in textfields + e.preventDefault(); + + // Select the hotkey + switch(e.which) { + case 32: // space bar + sumostart = !sumostart; + if (sumostart) { + $(".btn-start").addClass("hover"); + $(".btn-start").click(); + } else { + $(".btn-stop").addClass("hover"); + $(".btn-stop").click(); + } + break; + case 37: // left + sumorobot.send("left"); + break; + case 38: // up + sumorobot.send("forward"); + break; + case 39: // right + sumorobot.send("right"); + break; + case 40: // down + sumorobot.send("backward"); + break; + case 67: // c + $("#panel").toggle(); + break; + case 76: // l + // No live stream if cookies are disabled + if (!cookiesEnabled) { + // TODO: Open the cookie consent popup + return; + } + // Load the Mixer stream + if ($("#stream").is(':empty')) { + $("#stream").html(''); + } + $("#stream").toggle(); + // Toggle live steam visible + liveStreamVisible = !liveStreamVisible; + // If not in coding mode + if (codingEnabled == false) { + $("#readOnlyBlocklyCode").toggle(); + } + break; + case 80: // p + $("#blocklyDiv").toggle(); + $("#blocklyArea").toggle(); + $("#blocklyCode").toggle(); + if (liveStreamVisible == false) { + $("#readOnlyBlocklyCode").toggle(); + } + // Toggle coding enabled + codingEnabled = !codingEnabled; + if (codingEnabled) { + // Resize the coding editor + codingEditor.resize(); + // Focus, so the user can start coding + codingEditor.focus(); + } + break; + case 82: // r + // Implement something + break; + case 83: // s + $(".btn-stop").addClass("hover"); + $(".btn-stop").click(); + break; + case 84: // t + sumorobot.send("calibrate_line"); + break; + case 87: // w + $(".btn-start").addClass("hover"); + $(".btn-start").click(); + break; + } + }); + + // Key up event + $(document).keyup(function(e) { + // When the alt key is not pressed, don't process hotkeys + if (e.altKey == false) return; + // Remove hover from buttons + $('.btn').removeClass('hover'); + // If arrow keys were pressed + if (e.which == 37 || e.which == 38 || e.which == 39 || e.which == 40) { + sumorobot.send("stop"); + } + }); + + // Start button listener + $(".btn-start").click(function() { + sumostart = true; + // When we are in coding mode + if (codingEnabled) { + var parsedCode = codingEditor.getValue().replace(/"/g, '\\"').replace(/\n/g, ';;'); + // Send the code from the textarea to the SumoRobot + sumorobot.send("code", parsedCode); + // Otherwise when we are in Blockly mode + } else { + // Send the code from the blocks to the SumoRobot + var parsedCode = sumorobot.getBlocklyCode().replace(/"/g, '\\"').replace(/\n/g, ';;'); + sumorobot.send("code", parsedCode); + } + }); + + // Stop button listener + $(".btn-stop").click(function() { + sumostart = false; + sumorobot.send("stop"); + //workspace.highlightBlock(lastHighlighted, false); + }); + + // Enter listener on robot ID field + $("#robot-id").keypress(function(e) { + if (e.which == 13) { + // Simulate robot GO button click + $(".btn-robot-go").click(); + } + }); + + // Robot number button listener + $(".btn-robot-go").click(function() { + // Extract and validate the selected robot ID + var robotId = $("#robot-id").val().trim(); + if (robotId === "" || /^([a-f0-9]{6})$/.test(robotId) == false) { + $("#robot-id, #robot-label").addClass("has-error"); + return; + } else { + $("#robot-id, #robot-label").removeClass("has-error"); + } + // Update robot IDs in local storage + setLocalStorageItem("sumorobot.robotId", robotId); + // In case there is a open connection + if (sumorobot/* && blockHighlight*/) { + // Close the connections + sumorobot.close(); + //blockHighlight.close(); + } + // Connect to the selected robots WebSocket + sumorobot = new Sumorobot(`ws://${ROBOT_SERVER}`, robotId); + // connect to the other block highlight WebSocket + //blockHighlight = new WebSocket("ws://" + robotServer + ":80/p2p/browser/" + robotId + "-highlight/"); + // when there is a message from the WebSocket + /*blockHighlight.onmessage = function(evt) { + // when scope is received + if (evt.data.length == 20 && sumostart) { + workspace.highlightBlock(evt.data); + lastHighlighted = evt.data; + } + };*/ + // Hide the configuration panel + $("#panel").hide(); + }); +});