var hide_ext = []; var reset_reason = -1 var chip_MAC = "00" var socket, keepalive, socket_alive = false var modes = {"js":"ace/mode/javascript","py":"ace/mode/python", "html":"ace/mode/html", "css":"ace/mode/css"} var mode_colors = { "js" : '#f0fff0', "py":'#fff0ff', "html":'#f0f0ff', "css":'#f0ffff'} var files=[]; //------------------------------------------------------------------------ // Generic HTML functions //------------------------------------------------------------------------ //Manage Tabs - Generic function tab(evt, tabName) { var i, navcontent, navlinks, tab; //Remove active from all links in the same parent if (evt.srcElement!= undefined){ navlinks = evt.srcElement.parentElement.children for (i = 0; i < navlinks.length; i++) { navlinks[i].className = navlinks[i].className.replace(" active", ""); } evt.currentTarget.className += " active"; } //Hide all other tabs tab = document.getElementById(tabName) if (tab != null || tab.parentElement != undefined){ navcontent = tab.parentElement.children; for (i = 0; i < navcontent.length; i++) { navcontent[i].style.display = "none"; } } tab.style.display = "block"; } //------------------------------------------------------------------------ // Editor functions //------------------------------------------------------------------------ //Load the file from the ESP function loadFile(filename){ //something was p if (files[filename]==undefined){ //open a new file files[filename]=[]; //create the filetab on top of the editor files[filename].tab=document.createElement('div'); files[filename].tab.innerHTML =filename; files[filename].tab.setAttribute('class', 'filetab'); files[filename].tab.addEventListener('click', function (event) {switchFileTab(filename);tab(event,'wrap_' + filename)}); files[filename].tab.style.display = "none"; let a = document.createElement('div'); a.innerHTML="x" a.setAttribute('class', 'fileclose'); a.addEventListener('click', function (event) {closeFile(filename);}); files[filename].tab.appendChild(a) document.getElementById("filetabs").appendChild(files[filename].tab); //create the editor wrap div inside the workspace files[filename].wrap=document.createElement('div'); //create the editor let y = document.createElement('div'); y.id='e_' + filename; y.setAttribute('class', 'editor'); files[filename].wrap.appendChild(y); document.getElementById("editors").appendChild(files[filename].wrap); //boot up ace after getting the file get_file(filename,function(text, code){ var e = ace.edit('e_' + filename); var s; if (mode_colors[filename.split('.').pop()]!==null){ s=ace.createEditSession(text, modes[filename.split('.').pop()]); }else{ s=ace.createEditSession(text, "ace/mode/text");} s.setOptions({ tabSize: 2, useSoftTabs: true ,wrap: true}); e.setSession(s) var activeColor='#ffffff'; if (mode_colors[filename.split('.').pop()]!==null){activeColor=mode_colors[filename.split('.').pop()]; } e.container.style.background=activeColor; e.commands.addCommand({name: "saveCommand", bindKey: { win: "Ctrl-S", mac: "Command-S" },exec: function() {saveFile(filename) }}); e.commands.addCommand({name: "reloadCommand", bindKey: { win: "Ctrl-R", mac: "Command-R" },exec: function() {reloadFile(filename) } }); e.commands.addCommand({name: "executeCommand", bindKey: { win: "Ctrl-U", mac: "Command-U" },exec: function() {run("exec(open('"+filename+"').read(),{})\r\n") } }); files[filename].editor=e files[filename].tab.style.display = "inline-block"; files[filename].editor.focus(); let b = document.createElement('div'); b.setAttribute('class', 'button_box'); b.innerHTML="
" files[filename].wrap.appendChild(b) }) }else{ //switch to file switchFileTab(filename); } } //Load the file from the ESP function newFile(){ filename = prompt("What should the file be named","main.py") if (filename != null){ //something was p if (files[filename]==undefined){ //open a new file files[filename]=[]; //create the filetab on top of the editor files[filename].tab=document.createElement('div'); files[filename].tab.innerHTML =filename; files[filename].tab.setAttribute('class', 'filetab'); files[filename].tab.addEventListener('click', function (event) {switchFileTab(filename);tab(event,'wrap_' + filename)}); files[filename].tab.style.display = "none"; let a = document.createElement('div'); a.innerHTML="x" a.setAttribute('class', 'fileclose'); a.addEventListener('click', function (event) {closeFile(filename);}); files[filename].tab.appendChild(a) document.getElementById("filetabs").appendChild(files[filename].tab); //create the editor wrap div inside the workspace files[filename].wrap=document.createElement('div'); //create the editor let y = document.createElement('div'); y.id='e_' + filename; y.setAttribute('class', 'editor'); files[filename].wrap.appendChild(y); document.getElementById("editors").appendChild(files[filename].wrap); var e = ace.edit('e_' + filename); var s; if (mode_colors[filename.split('.').pop()]!==null){ s=ace.createEditSession("" ,modes[filename.split('.').pop()]); }else{ s=ace.createEditSession("" ,"ace/mode/text"); } s.setOptions({ tabSize: 2, useSoftTabs: true ,wrap: true}); e.setSession(s) var activeColor='#ffffff'; if (mode_colors[filename.split('.').pop()]!==null){activeColor=mode_colors[filename.split('.').pop()]; } e.container.style.background=activeColor; e.commands.addCommand({name: "saveCommand", bindKey: { win: "Ctrl-S", mac: "Command-S" },exec: function() {saveFile(filename) }}); e.commands.addCommand({name: "reloadCommand", bindKey: { win: "Ctrl-R", mac: "Command-R" },exec: function() {reloadFile(filename) } }); files[filename].editor=e files[filename].tab.style.display = "inline-block"; files[filename].editor.focus(); let b = document.createElement('div'); b.setAttribute('class', 'button_box'); b.innerHTML="" files[filename].wrap.appendChild(b) }else{ //switch to file switchFileTab(filename); } tab(0,"edit_work") }} //reload a File form the ESP32 function reloadFile(filename){ if (files[filename]==undefined){ //file isn't loaded loadFile(filename) }else{ get_file(filename,function(text, code){ files[filename].editor.setValue(text) }); } switchFileTab(filename) } //Switch between files function switchFileTab(filename){ for (x in files) {files[x].tab.style.background='#aaa';} for (x in files) {files[x].wrap.style.display = "none";} files[filename].wrap.style.display = "block"; files[filename].tab.style.background='#fff'; if (mode_colors[filename.split('.').pop()]!==null){ files[filename].tab.style.background=mode_colors[filename.split('.').pop()]; }else{ files[filename].tab.style.background='#fff'; } tab(0,"edit_work") } //Save the file to the ESP function saveFile(filename){ put_file(filename,files[filename].editor.getValue()) } //Closes a file function closeFile(filename){ files[filename].tab.remove(); files[filename].editor.remove(); files[filename].wrap.remove(); files[filename]=undefined; delete files[filename]; } //Delete a file from the ESP and close the Editor function deleteFile(filename){ if (confirm("Do you wish to delete " + filename + "?")){ run("import os\r\nos.remove('" + filename + "')\r\n", function (event) { console.log("Deleted " + filename); tree(); }); closeFile(filename); }} //------------------------------------------------------------------------ // Terminal emulation functions //------------------------------------------------------------------------ // Create WebSocket connection and setup Event handlers function startTerminal(){ if (!socket_alive){ socket_alive=true; socket = new WebSocket("ws://" + window.location.href.split("/")[2] + ":80"); socket.onopen = function (){ document.getElementById("term_status").style.display = "none"; document.getElementById("terminal_menu").innerHTML="Terminal (active)" keepalive =setInterval(function () {socket.send('')}, 500); } socket.onclose = function (){ document.getElementById("term_status").style.display = "block"; clearInterval(keepalive); document.getElementById("terminal_menu").innerHTML="Terminal" socket_alive=false; startTerminal() } // Listen for messages socket.onmessage = function (event){ if (event.data !="\r\n"){ addToTerminal(event.data); } } } } function stopTerminal(){ socket.onclose=null clearInterval(keepalive); socket.close(); socket_alive=false; document.getElementById("term_status").style.display = "block"; } //Add a line to the terminal window function addToTerminal(data){ let terminal = document.getElementsByClassName("terminal")[0]; //trim down to 50 lines while (terminal.children.length > 50){ terminal.children[0].remove() } if (data.length <= 2){ terminal.lastChild.textContent += data }else{ let line = document.createElement('div'); line.textContent = data.replace(">>> >>> ", ">>> "); terminal.appendChild(line); } } //------------------------------------------------------------------------ // Logistics functions //------------------------------------------------------------------------ //Draggable area function setupDrop(){ let dropArea = document.getElementById('upload') dropArea.addEventListener('dragenter', preventDrop, false) dropArea.addEventListener('dragleave', preventDrop, false) dropArea.addEventListener('dragover', preventDrop, false) dropArea.addEventListener('drop', drop_file, false) } function preventDrop(e) { e.preventDefault() e.stopPropagation() } function drop_file(e) { preventDrop(e) let dt = e.dataTransfer let files = dt.files const reader = new FileReader(); reader.addEventListener('loadend', function (event) { put_file(files[0].name,reader.result,tree) }) reader.readAsArrayBuffer(files[0]); } //Generic Command to execute some code on the ESP32 - temporarily pauses terminal logging function run(cmd_text,callback = noop,notify=true) { if (notify){ var div_node = status('Running Command') } var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4){if (notify){ if (this.status == 200){ div_node.textContent="Command sucessfull"; pop(div_node,15,'#dfd'); }else{ div_node.textContent="Command failed"; pop(div_node,15,'#fdd'); }} callback(xhr.responseText,this.status); }}; xhr.open("PUT", '/', true); xhr.overrideMimeType("text/html") xhr.send(cmd_text+'\r\n'); } //Generic Command to retreive files on the ESP32 function get_file(file,callback = noop,notify=true) { if (notify){ var div_node = status("Loading " + file) } var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4){if (notify){ if (this.status == 200){ div_node.textContent= "Loaded " + file ; pop(div_node,15,'#dfd'); }else{ div_node.textContent="Loading " + file + " failed"; pop(div_node,15,'#fdd'); }} callback(xhr.responseText,this.status); }}; xhr.open("GET", file, true); xhr.send('\r\n'); } //Generic Command to write files to the ESP32 function put_file(file,content,callback = noop,notify=true) { if (notify){ var div_node = status("Saving " + file) } var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4){if (notify){ if (this.status == 200){ div_node.textContent= "Saved " + file; pop(div_node,15,'#dfd'); }else{ div_node.textContent="Saving " + file + " failed"; pop(div_node,15,'#fdd'); }} callback(xhr.responseText,this.status); }}; xhr.open("PUT", file, true); xhr.send(content); } //Spawn Status reports function status (msg_text,color='#ddd'){ var div_node = document.createElement('div'); div_node.textContent = msg_text; div_node.setAttribute('class', 'note'); div_node.style.backgroundColor = color; document.getElementById("notes").appendChild(div_node); return div_node; } //Pop Status reports function pop (div_node,delay_time=3,color='#ddd'){ div_node.style.backgroundColor = color; setTimeout(function() {document.getElementById("notes").removeChild(div_node);}, delay_time*1000); } //Populate the file directory function tree(){ var div_node = status('Scanning Files') var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4){if (this.status == 200){ div_node.textContent="Scanning Files sucessfull"; pop(div_node,15,'#dfd'); let lines = xhr.responseText.split(";\r\n"); let properties = lines[0].split("\r\n"); //Update the Time diplayed at the top document.getElementById("chip_mac").innerHTML=properties[4].split(': ')[1] //Update the DISK meter let disk_meter = document.getElementById("disk_meter") disk_meter.max = properties[1].split(' / ')[1] disk_meter.value = properties[1].split(' / ')[0].split(': ')[1] // Update the RAM meter let ram_meter = document.getElementById("ram_meter") ram_meter.max = properties[2].split(' / ')[1] ram_meter.value = properties[2].split(' / ')[0].split(': ')[1] reset_reason = properties[3].split(': ')[1] chip_MAC = properties[4].split(': ')[1] var htmltext= '