Files
NodeMCU_Lua/WebIDE/web_ide1.lua
Christian Zufferey 65fa2289ec Super bien avancé sur ma démo de chapeau
* ajouté un mini WEB IDE qui permet directement sur le NodeMCU d'éditer un script LUA
* refactorisé un poil mes deux WEB IDE
* rangé au bon endroit mes dexu WEB IDE
2018-12-05 21:19:44 +01:00

251 lines
8.9 KiB
Lua

-- Petit WEB IDE tout simple
-- ATTENTION: tourne sur le port 88 !
print("\n _web_ide1.lua zf181205.2112 \n")
--[[
XChip's NodeMCU IDE
Original Source: http://www.esp8266.com/viewtopic.php?f=19&t=1549
Petr Stehlik found the source in October 2016 and gave it a new home
at https://github.com/joysfera/nodemcu-web-ide under the GPL license.
Then updated it for new async socket send(), fixed, cleaned up,
added external editor with syntax highlighting and further improves it.
Create, Edit and run NodeMCU files using your web browser.
Examples:
http://<mcu_ip>/ will list all the files in the MCU
http://<mcu_ip>/newfile.lua displays the file on your browser
http://<mcu_ip>/newfile.lua?edit allows to creates or edits the specified script in your browser
http://<mcu_ip>/newfile.lua?run it will run the specified script and will show the returned value
--]]
local function editor(aceEnabled) -- feel free to disable the shiny Ajax.org Cloud Editor
local AceEnabled = aceEnabled == nil and true or aceEnabled
srv = net.createServer(net.TCP)
srv:listen(88, function(conn)
local rnrn = 0
local Status = 0
local DataToGet = 0
local method = ""
local url = ""
local vars = ""
conn:on("receive", function(sck, payload)
if Status == 0 then
_, _, method, url, vars = string.find(payload, "([A-Z]+) /([^?]*)%??(.*) HTTP")
-- print("Method, URL, vars: ", method, url, vars)
end
if method == "POST" then
if Status == 0 then
-- print("status", Status)
_, _, DataToGet, payload = string.find(payload, "Content%-Length: (%d+)(.+)")
if DataToGet then
DataToGet = tonumber(DataToGet)
-- print("DataToGet = "..DataToGet)
rnrn = 1
Status = 1
else
print("bad length")
end
end
-- find /r/n/r/n
if Status == 1 then
-- print("status", Status)
local payloadlen = string.len(payload)
local mark = "\r\n\r\n"
local i
for i=1, payloadlen do
if string.byte(mark, rnrn) == string.byte(payload, i) then
rnrn = rnrn + 1
if rnrn == 5 then
payload = string.sub(payload, i+1, payloadlen)
file.open(url, "w")
file.close()
Status = 2
break
end
else
rnrn = 1
end
end
if Status == 1 then
return
end
end
if Status == 2 then
-- print("status", Status)
if payload then
DataToGet = DataToGet - string.len(payload)
--print("DataToGet:", DataToGet, "payload len:", string.len(payload))
file.open(url, "a+")
file.write(payload)
file.close()
else
sck:send("HTTP/1.1 200 OK\r\n\r\nERROR")
Status = 0
end
if DataToGet == 0 then
sck:send("HTTP/1.1 200 OK\r\n\r\nOK")
Status = 0
end
end
return
end
-- end of POST method handling
DataToGet = -1
if url == "favicon.ico" then
-- print("favicon.ico handler sends 404")
sck:send("HTTP/1.1 404 file not found")
return
end
local sen = "HTTP/1.1 200 OK\r\n\r\n"
-- it wants a file in particular
if url ~= "" and vars == "" then
DataToGet = 0
sck:send(sen)
return
end
sen = sen .. "<html><body><h1><a href='/'>NodeMCU IDE</a></h1>"
if vars == "edit" then
if AceEnabled then
local mode = 'ace/mode/'
if url:match(".css") then mode = mode .. 'css'
elseif url:match(".html") then mode = mode .. 'html'
elseif url:match(".json") then mode = mode .. 'json'
elseif url:match(".js") then mode = mode .. 'javascript'
else mode = mode .. 'lua'
end
sen = sen .. "<style type='text/css'>#editor{width: 100%; height: 80%}</style><div id='editor'></div><script src='//rawgit.com/ajaxorg/ace-builds/master/src-min-noconflict/ace.js'></script>"
.. "<script>var e=ace.edit('editor');e.setTheme('ace/theme/monokai');e.getSession().setMode('"..mode.."');function getSource(){return e.getValue();};function setSource(s){e.setValue(s);}</script>"
else
sen = sen .. "<textarea name=t cols=79 rows=17></textarea></br>"
.. "<script>function getSource() {return document.getElementsByName('t')[0].value;};function setSource(s) {document.getElementsByName('t')[0].value = s;};</script>"
end
sen = sen .. "<script>function tag(c){document.getElementsByTagName('w')[0].innerHTML=c};var x=new XMLHttpRequest();x.onreadystatechange=function(){if(x.readyState==4) setSource(x.responseText);};"
.. "x.open('GET',location.pathname);x.send()</script><button onclick=\"tag('Saving, wait!');x.open('POST',location.pathname);x.onreadystatechange=function(){console.log(x.readyState);"
.. "if(x.readyState==4) tag(x.responseText);};x.send(new Blob([getSource()],{type:'text/plain'}));\">Save</button> <a href='?run'>run</a> <w></w>"
elseif vars == "run" then
sen = sen .. "<verbatim>"
function s_output(str) sen = sen .. str end
node.output(s_output, 0) -- re-direct output to function s_output.
local st, result = pcall(dofile, url)
-- delay the output capture by 1000 milliseconds to give some time to the user routine in pcall()
tmr.alarm(0, 1000, tmr.ALARM_SINGLE, function()
node.output(nil)
if result then
local outp = tostring(result):sub(1,1300) -- to fit in one send() packet
result = nil
sen = sen .. "<br>Result of the run: " .. outp .. "<br>"
end
sen = sen .. "</verbatim></body></html>"
sck:send(sen)
end)
return
elseif vars == "compile" then
collectgarbage()
node.compile(url)
url = ""
elseif vars == "delete" then
file.remove(url)
url = ""
elseif vars == "restart" then
node.restart()
return
end
local message = {}
message[#message + 1] = sen
sen = nil
if url == "" then
local l = file.list();
message[#message + 1] = "<table border=1 cellpadding=3><tr><th>Name</th><th>Size</th><th>Edit</th><th>Compile</th><th>Delete</th></tr>"
for k,v in pairs(l) do
local line = "<tr><td><a href='" ..k.. "'>" ..k.. "</a></td><td>" ..v.. "</td><td>"
local editable = k:sub(-4, -1) == ".lua" or k:sub(-4, -1) == ".css" or k:sub(-5, -1) == ".html" or k:sub(-5, -1) == ".json"
if editable then
line = line .. "<a href='" ..k.. "?edit'>edit</a>"
end
line = line .. "</td><td>"
if k:sub(-4, -1) == ".lua" then
line = line .. "<a href='" ..k.. "?compile'>compile</a>"
end
line = line .. "</td><td><a href='" ..k.. "?delete'>delete</a></td></tr>"
message[#message + 1] = line
end
message[#message + 1] = "</table><a href='#' onclick='v=prompt(\"Filename\");if (v!=null) { this.href=\"/\"+v+\"?edit\"; return true;} else return false;'>Create new</a> &nbsp; &nbsp; "
message[#message + 1] = "<a href='#' onclick='var x=new XMLHttpRequest();x.open(\"GET\",\"/?restart\");x.send();setTimeout(function(){location.href=\"/\"},5000);this.innerText=\"Please wait\";return false'>Restart</a>"
end
message[#message + 1] = "</body></html>"
local function send_table(sk)
if #message > 0 then
sk:send(table.remove(message, 1))
else
sk:close()
message = nil
end
end
sck:on("sent", send_table)
send_table(sck)
end)
conn:on("sent", function(sck)
if DataToGet >= 0 and method == "GET" then
if file.open(url, "r") then
file.seek("set", DataToGet)
local chunkSize = 512
local line = file.read(chunkSize)
file.close()
if line then
sck:send(line)
DataToGet = DataToGet + chunkSize
if string.len(line) == chunkSize then return end
end
end
end
sck:close()
sck = nil
end)
end)
end
--[[if wifi.sta.status() == wifi.STA_GOTIP then
print("http://"..wifi.sta.getip().."/")
editor()
else
print("WiFi connecting...")
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function()
wifi.eventmon.unregister(wifi.eventmon.STA_GOT_IP)
print("http://"..wifi.sta.getip().."/")
editor()
end)
end
]]
editor()