mirror of
				https://github.com/esp8266/Arduino.git
				synced 2025-11-03 14:33:37 +03:00 
			
		
		
		
	plaintext POST fixes and rework of the SD example
Added a single file web editor/browser/uploader and all needed methods to work with the files on the SD Card
This commit is contained in:
		@@ -23,8 +23,10 @@
 | 
			
		||||
  File extensions with more than 3 charecters are not supported by the SD Library
 | 
			
		||||
  File Names longer than 8 charecters will be truncated by the SD library, so keep filenames shorter
 | 
			
		||||
  index.htm is the default index (works on subfolders as well)
 | 
			
		||||
*/
 | 
			
		||||
  
 | 
			
		||||
  upload the contents of SdRoot to the root of the SDcard and access the editor by going to http://esp8266sd.local/edit
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
#include <ESP8266WiFi.h>
 | 
			
		||||
#include <WiFiClient.h>
 | 
			
		||||
#include <ESP8266WebServer.h>
 | 
			
		||||
@@ -32,8 +34,8 @@
 | 
			
		||||
#include <SPI.h>
 | 
			
		||||
#include <SD.h>
 | 
			
		||||
 | 
			
		||||
//do not go larger than 1460 bytes as that is the maximum that could fit in a packet
 | 
			
		||||
#define WWW_BUF_SIZE 1460
 | 
			
		||||
#define DBG_OUTPUT_PORT Serial
 | 
			
		||||
 | 
			
		||||
const char* ssid = "**********";
 | 
			
		||||
const char* password = "**********";
 | 
			
		||||
@@ -45,31 +47,36 @@ ESP8266WebServer server(80);
 | 
			
		||||
static bool hasSD = false;
 | 
			
		||||
File uploadFile;
 | 
			
		||||
 | 
			
		||||
void handleFileUpload(){
 | 
			
		||||
  if(server.uri() != "/upload") return;
 | 
			
		||||
  HTTPUpload upload = server.upload();
 | 
			
		||||
  if(upload.status == UPLOAD_FILE_START){
 | 
			
		||||
    Serial.print("Upload: START, filename:");
 | 
			
		||||
    Serial.println(upload.filename);
 | 
			
		||||
    if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str());
 | 
			
		||||
    uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
 | 
			
		||||
  } else if(upload.status == UPLOAD_FILE_WRITE){
 | 
			
		||||
    Serial.print("Upload: WRITE, Bytes:");
 | 
			
		||||
    Serial.println(upload.buflen);
 | 
			
		||||
    if(uploadFile) uploadFile.write(upload.buf, upload.buflen);
 | 
			
		||||
  } else if(upload.status == UPLOAD_FILE_END){
 | 
			
		||||
    Serial.print("Upload: END, Size:");
 | 
			
		||||
    Serial.println(upload.size);
 | 
			
		||||
    if(uploadFile) uploadFile.close();
 | 
			
		||||
  }
 | 
			
		||||
void returnOK(){
 | 
			
		||||
  WiFiClient client = server.client();
 | 
			
		||||
  String message = "HTTP/1.1 200 OK\r\n";
 | 
			
		||||
  message += "Content-Type: text/plain\r\n";
 | 
			
		||||
  message += "Connection: close\r\n";
 | 
			
		||||
  message += "Access-Control-Allow-Origin: *\r\n";
 | 
			
		||||
  message += "\r\n";
 | 
			
		||||
  client.print(message);
 | 
			
		||||
  message = 0;
 | 
			
		||||
  client.stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void returnFail(String msg){
 | 
			
		||||
  WiFiClient client = server.client();
 | 
			
		||||
  String message = "HTTP/1.1 500 Fail\r\n";
 | 
			
		||||
  message += "Content-Type: text/plain\r\n";
 | 
			
		||||
  message += "Connection: close\r\n";
 | 
			
		||||
  message += "Access-Control-Allow-Origin: *\r\n";
 | 
			
		||||
  message += "\r\n";
 | 
			
		||||
  message += msg;
 | 
			
		||||
  message += "\r\n";
 | 
			
		||||
  client.print(message);
 | 
			
		||||
  message = 0;
 | 
			
		||||
  client.stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool loadFromSdCard(String path){
 | 
			
		||||
  String dataType = "text/plain";
 | 
			
		||||
  //handle default index
 | 
			
		||||
  if(path.endsWith("/")) path += "index.htm";
 | 
			
		||||
  
 | 
			
		||||
  //set proper Content-Type for the most common extensions
 | 
			
		||||
  if(path.endsWith(".src")) path = path.substring(0, path.lastIndexOf("."));
 | 
			
		||||
  else if(path.endsWith(".htm")) dataType = "text/html";
 | 
			
		||||
  else if(path.endsWith(".css")) dataType = "text/css";
 | 
			
		||||
@@ -82,121 +89,225 @@ bool loadFromSdCard(String path){
 | 
			
		||||
  else if(path.endsWith(".pdf")) dataType = "application/pdf";
 | 
			
		||||
  else if(path.endsWith(".zip")) dataType = "application/zip";
 | 
			
		||||
  
 | 
			
		||||
  //Try to open the file
 | 
			
		||||
  File dataFile = SD.open(path.c_str());
 | 
			
		||||
  
 | 
			
		||||
  //if it's a folder, try to open the default index
 | 
			
		||||
  if(dataFile && dataFile.isDirectory()){
 | 
			
		||||
  if(dataFile.isDirectory()){
 | 
			
		||||
    path += "/index.htm";
 | 
			
		||||
    dataType = "text/html";
 | 
			
		||||
    dataFile = SD.open(path.c_str());
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  //and finally if the file exists, stream the content to the client
 | 
			
		||||
  if(server.hasArg("download")) dataType = "application/octet-stream";
 | 
			
		||||
  
 | 
			
		||||
  if (dataFile) {
 | 
			
		||||
    WiFiClient client = server.client();
 | 
			
		||||
    //send the file headers
 | 
			
		||||
    String head = "HTTP/1.1 200 OK\r\nContent-Type: ";
 | 
			
		||||
    head += dataType;
 | 
			
		||||
    head += "\r\nContent-Length: ";
 | 
			
		||||
    head += dataFile.size();
 | 
			
		||||
    head += "\r\nConnection: close";
 | 
			
		||||
    head += "\r\nAccess-Control-Allow-Origin: *";
 | 
			
		||||
    head += "\r\n\r\n";
 | 
			
		||||
    client.print(head);
 | 
			
		||||
    dataType = 0;
 | 
			
		||||
    path = 0;
 | 
			
		||||
    
 | 
			
		||||
    //partition the data packets to fit in a TCP packet (1460 bytes MAX)
 | 
			
		||||
    uint8_t obuf[WWW_BUF_SIZE];
 | 
			
		||||
    
 | 
			
		||||
    while (dataFile.available() > WWW_BUF_SIZE){
 | 
			
		||||
      dataFile.read(obuf, WWW_BUF_SIZE);
 | 
			
		||||
      if(client.write(obuf, WWW_BUF_SIZE) != WWW_BUF_SIZE){
 | 
			
		||||
        Serial.println("Sent less data than expected!");
 | 
			
		||||
        DBG_OUTPUT_PORT.println("Sent less data than expected!");
 | 
			
		||||
        dataFile.close();
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    //stream the last data left (size is at most WWW_BUF_SIZE bytes)
 | 
			
		||||
    uint16_t leftLen = dataFile.available();
 | 
			
		||||
    dataFile.read(obuf, leftLen);
 | 
			
		||||
    if(client.write(obuf, leftLen) != leftLen){
 | 
			
		||||
      Serial.println("Sent less data than expected!");
 | 
			
		||||
      DBG_OUTPUT_PORT.println("Sent less data than expected!");
 | 
			
		||||
      dataFile.close();
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    dataFile.close();
 | 
			
		||||
    client.stop();
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tryLoadFromSdCard(){
 | 
			
		||||
  String message = "FileNotFound\n\n";
 | 
			
		||||
  if(hasSD){
 | 
			
		||||
    //try to load the URL from SD Card
 | 
			
		||||
    if(loadFromSdCard(server.uri())) return;
 | 
			
		||||
void handleFileUpload(){
 | 
			
		||||
  if(server.uri() != "/edit") return;
 | 
			
		||||
  HTTPUpload upload = server.upload();
 | 
			
		||||
  if(upload.status == UPLOAD_FILE_START){
 | 
			
		||||
    if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str());
 | 
			
		||||
    uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
 | 
			
		||||
    DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename);
 | 
			
		||||
  } else if(upload.status == UPLOAD_FILE_WRITE){
 | 
			
		||||
    if(uploadFile) uploadFile.write(upload.buf, upload.buflen);
 | 
			
		||||
    DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.buflen);
 | 
			
		||||
  } else if(upload.status == UPLOAD_FILE_END){
 | 
			
		||||
    if(uploadFile) uploadFile.close();
 | 
			
		||||
    DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.size);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void deleteRecursive(String path){
 | 
			
		||||
  File file = SD.open((char *)path.c_str());
 | 
			
		||||
  if(!file.isDirectory()){
 | 
			
		||||
    file.close();
 | 
			
		||||
    SD.remove((char *)path.c_str());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  file.rewindDirectory();
 | 
			
		||||
  File entry;
 | 
			
		||||
  String entryPath;
 | 
			
		||||
  while(true) {
 | 
			
		||||
    entry = file.openNextFile();
 | 
			
		||||
    if (!entry) break;
 | 
			
		||||
    entryPath = path + "/" +entry.name();
 | 
			
		||||
    if(entry.isDirectory()){
 | 
			
		||||
      entry.close();
 | 
			
		||||
      deleteRecursive(entryPath);
 | 
			
		||||
    } else {
 | 
			
		||||
      entry.close();
 | 
			
		||||
      SD.remove((char *)entryPath.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    entryPath = 0;
 | 
			
		||||
    yield();
 | 
			
		||||
  }
 | 
			
		||||
  SD.rmdir((char *)path.c_str());
 | 
			
		||||
  path = 0;
 | 
			
		||||
  file.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handleDelete(){
 | 
			
		||||
  if(server.args() == 0) return returnFail("BAD ARGS");
 | 
			
		||||
  String path = server.arg(0);
 | 
			
		||||
  if(path == "/" || !SD.exists((char *)path.c_str())) return returnFail("BAD PATH");
 | 
			
		||||
  deleteRecursive(path);
 | 
			
		||||
  returnOK();
 | 
			
		||||
  path = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handleCreate(){
 | 
			
		||||
  if(server.args() == 0) return returnFail("BAD ARGS");
 | 
			
		||||
  String path = server.arg(0);
 | 
			
		||||
  if(path == "/" || SD.exists((char *)path.c_str())) return returnFail("BAD PATH");
 | 
			
		||||
  if(path.indexOf('.') > 0){
 | 
			
		||||
    File file = SD.open((char *)path.c_str(), FILE_WRITE);
 | 
			
		||||
    if(file){
 | 
			
		||||
      file.write((const char *)0);
 | 
			
		||||
      file.close();
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    message = "SDCARD Not Detected\n\n";
 | 
			
		||||
    SD.mkdir((char *)path.c_str());
 | 
			
		||||
  }
 | 
			
		||||
  returnOK();
 | 
			
		||||
  path = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printDirectory() {
 | 
			
		||||
  if(!server.hasArg("dir")) return returnFail("BAD ARGS");
 | 
			
		||||
  String path = server.arg("dir");
 | 
			
		||||
  if(path != "/" && !SD.exists((char *)path.c_str())) return returnFail("BAD PATH");
 | 
			
		||||
  File dir = SD.open((char *)path.c_str());
 | 
			
		||||
  path = 0;
 | 
			
		||||
  if(!dir.isDirectory()){
 | 
			
		||||
    dir.close();
 | 
			
		||||
    return returnFail("NOT DIR");
 | 
			
		||||
  }
 | 
			
		||||
  dir.rewindDirectory();
 | 
			
		||||
  
 | 
			
		||||
  File entry;
 | 
			
		||||
  WiFiClient client = server.client();
 | 
			
		||||
  client.print("HTTP/1.1 200 OK\r\nContent-Type: text/json\r\n\r\n");
 | 
			
		||||
  String output = "[";
 | 
			
		||||
  while(true) {
 | 
			
		||||
   entry = dir.openNextFile();
 | 
			
		||||
   if (!entry) break;
 | 
			
		||||
   if(output != "[") output += ',';
 | 
			
		||||
   output += "{\"type\":\"";
 | 
			
		||||
   output += (entry.isDirectory())?"dir":"file";
 | 
			
		||||
   output += "\",\"name\":\"";
 | 
			
		||||
   output += entry.name();
 | 
			
		||||
   output += "\"";
 | 
			
		||||
   output += "}";
 | 
			
		||||
   entry.close();
 | 
			
		||||
   if(output.length() > 1460){
 | 
			
		||||
     client.write(output.substring(0, 1460).c_str(), 1460);
 | 
			
		||||
     output = output.substring(1460);
 | 
			
		||||
   }
 | 
			
		||||
 }
 | 
			
		||||
 dir.close();
 | 
			
		||||
 output += "]";
 | 
			
		||||
 client.write(output.c_str(), output.length());
 | 
			
		||||
 client.stop();
 | 
			
		||||
 output = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handleNotFound(){
 | 
			
		||||
  if(hasSD && loadFromSdCard(server.uri())) return;
 | 
			
		||||
  String message = "SDCARD Not Detected\n\n";
 | 
			
		||||
  message += "URI: ";
 | 
			
		||||
  message += server.uri();
 | 
			
		||||
  message += "\nMethod: ";
 | 
			
		||||
  message += (server.method() == HTTP_GET)?"GET":"POST";
 | 
			
		||||
  message += "\nArguments: ";
 | 
			
		||||
  message += server.args();
 | 
			
		||||
  message += "\n";
 | 
			
		||||
  for (uint8_t i=0; i<server.args(); i++){
 | 
			
		||||
    message += " NAME:"+server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
 | 
			
		||||
  }
 | 
			
		||||
  server.send(404, "text/plain", message);
 | 
			
		||||
  DBG_OUTPUT_PORT.print(message);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void setup(void){
 | 
			
		||||
  uint8_t i = 0;
 | 
			
		||||
  Serial.begin(115200);
 | 
			
		||||
  
 | 
			
		||||
  //setup WiFi
 | 
			
		||||
  WiFi.begin(ssid, password);
 | 
			
		||||
  Serial.print("\nConnecting to ");
 | 
			
		||||
  Serial.println(ssid);
 | 
			
		||||
 | 
			
		||||
  //wait for WiFi to connect
 | 
			
		||||
  while (WiFi.status() != WL_CONNECTED && i++ < 20) delay(500);
 | 
			
		||||
  
 | 
			
		||||
  //check if we have connected?
 | 
			
		||||
void setup(void){
 | 
			
		||||
  DBG_OUTPUT_PORT.begin(115200);
 | 
			
		||||
  DBG_OUTPUT_PORT.setDebugOutput(true);
 | 
			
		||||
  DBG_OUTPUT_PORT.print("\n");
 | 
			
		||||
  WiFi.begin(ssid, password);
 | 
			
		||||
  DBG_OUTPUT_PORT.print("Connecting to ");
 | 
			
		||||
  DBG_OUTPUT_PORT.println(ssid);
 | 
			
		||||
 | 
			
		||||
  // Wait for connection
 | 
			
		||||
  uint8_t i = 0;
 | 
			
		||||
  while (WiFi.status() != WL_CONNECTED && i++ < 20) {//wait 10 seconds
 | 
			
		||||
    delay(500);
 | 
			
		||||
  }
 | 
			
		||||
  if(i == 21){
 | 
			
		||||
    Serial.print("Could not connect to");
 | 
			
		||||
    Serial.println(ssid);
 | 
			
		||||
    //stop execution and wait forever
 | 
			
		||||
    DBG_OUTPUT_PORT.print("Could not connect to");
 | 
			
		||||
    DBG_OUTPUT_PORT.println(ssid);
 | 
			
		||||
    while(1) delay(500);
 | 
			
		||||
  }
 | 
			
		||||
  Serial.print("Connected! IP address: ");
 | 
			
		||||
  Serial.println(WiFi.localIP());
 | 
			
		||||
  
 | 
			
		||||
  //start mDNS Server
 | 
			
		||||
  DBG_OUTPUT_PORT.print("Connected! IP address: ");
 | 
			
		||||
  DBG_OUTPUT_PORT.println(WiFi.localIP());
 | 
			
		||||
  /*
 | 
			
		||||
  if (mdns.begin(hostname, WiFi.localIP())) {
 | 
			
		||||
    Serial.println("MDNS responder started");
 | 
			
		||||
    Serial.print("You can now connect to http://");
 | 
			
		||||
    Serial.print(hostname);
 | 
			
		||||
    Serial.println(".local");
 | 
			
		||||
    DBG_OUTPUT_PORT.println("MDNS responder started");
 | 
			
		||||
    DBG_OUTPUT_PORT.print("You can now connect to http://");
 | 
			
		||||
    DBG_OUTPUT_PORT.print(hostname);
 | 
			
		||||
    DBG_OUTPUT_PORT.println(".local");
 | 
			
		||||
  }
 | 
			
		||||
  */
 | 
			
		||||
  
 | 
			
		||||
  //Attach handler
 | 
			
		||||
  server.onNotFound(tryLoadFromSdCard);
 | 
			
		||||
  
 | 
			
		||||
  //Attach Upload handler
 | 
			
		||||
  server.on("/list", HTTP_GET, printDirectory);
 | 
			
		||||
  server.on("/edit", HTTP_DELETE, handleDelete);
 | 
			
		||||
  server.on("/edit", HTTP_PUT, handleCreate);
 | 
			
		||||
  server.on("/edit", HTTP_POST, [](){ returnOK(); });
 | 
			
		||||
  server.onNotFound(handleNotFound);
 | 
			
		||||
  server.onFileUpload(handleFileUpload);
 | 
			
		||||
  
 | 
			
		||||
  //Attach handler for the Upload location
 | 
			
		||||
  server.on("/upload", HTTP_POST, [](){
 | 
			
		||||
    WiFiClient client = server.client();
 | 
			
		||||
    String message = "HTTP/1.1 200 OK\r\n";
 | 
			
		||||
    message += "Content-Type: text/plain\r\n";
 | 
			
		||||
    message += "Access-Control-Allow-Origin: *\r\n";
 | 
			
		||||
    message += "\r\n";
 | 
			
		||||
    client.print(message);
 | 
			
		||||
  });
 | 
			
		||||
  
 | 
			
		||||
  //start server
 | 
			
		||||
  server.begin();
 | 
			
		||||
  Serial.println("HTTP server started");
 | 
			
		||||
  DBG_OUTPUT_PORT.println("HTTP server started");
 | 
			
		||||
  
 | 
			
		||||
  //init SD Card
 | 
			
		||||
  if (SD.begin(SS)){
 | 
			
		||||
     Serial.println("SD Card initialized.");
 | 
			
		||||
     DBG_OUTPUT_PORT.println("SD Card initialized.");
 | 
			
		||||
     hasSD = true;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
void loop(void){
 | 
			
		||||
  mdns.update();
 | 
			
		||||
  //mdns.update();
 | 
			
		||||
  server.handleClient();
 | 
			
		||||
} 
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,670 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <title>SD Editor</title>
 | 
			
		||||
    <style type="text/css" media="screen">
 | 
			
		||||
      .contextMenu {
 | 
			
		||||
        z-index: 300;
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        left: 5px;
 | 
			
		||||
        border: 1px solid #444;
 | 
			
		||||
        background-color: #F5F5F5;
 | 
			
		||||
        display: none;
 | 
			
		||||
        box-shadow: 0 0 10px rgba( 0, 0, 0, .4 );
 | 
			
		||||
        font-size: 12px;
 | 
			
		||||
        font-family: sans-serif;
 | 
			
		||||
        font-weight:bold;
 | 
			
		||||
      }
 | 
			
		||||
      .contextMenu ul {
 | 
			
		||||
        list-style: none;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        margin: 0;
 | 
			
		||||
        padding: 0;
 | 
			
		||||
      }
 | 
			
		||||
      .contextMenu li {
 | 
			
		||||
        position: relative;
 | 
			
		||||
        min-width: 60px;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
      }
 | 
			
		||||
      .contextMenu span {
 | 
			
		||||
        color: #444;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        padding: 6px;
 | 
			
		||||
      }
 | 
			
		||||
      .contextMenu li:hover { background: #444; }
 | 
			
		||||
      .contextMenu li:hover span { color: #EEE; }
 | 
			
		||||
    
 | 
			
		||||
      .css-treeview ul, .css-treeview li {
 | 
			
		||||
        padding: 0;
 | 
			
		||||
        margin: 0;
 | 
			
		||||
        list-style: none;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview input {
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        opacity: 0;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview {
 | 
			
		||||
        font: normal 11px Verdana, Arial, Sans-serif;
 | 
			
		||||
        -moz-user-select: none;
 | 
			
		||||
        -webkit-user-select: none;
 | 
			
		||||
        user-select: none;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview span {
 | 
			
		||||
        color: #00f;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview span:hover {
 | 
			
		||||
        text-decoration: underline;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview input + label + ul {
 | 
			
		||||
        margin: 0 0 0 22px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview input ~ ul {
 | 
			
		||||
        display: none;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview label, .css-treeview label::before {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview input:disabled + label {
 | 
			
		||||
        cursor: default;
 | 
			
		||||
        opacity: .6;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview input:checked:not(:disabled) ~ ul {
 | 
			
		||||
        display: block;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview label, .css-treeview label::before {
 | 
			
		||||
        background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAACgCAYAAAAFOewUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAApxJREFUeNrslM1u00AQgGdthyalFFOK+ClIIKQKyqUVQvTEE3DmAhLwAhU8QZoH4A2Q2gMSFace4MCtJ8SPBFwAkRuiHKpA6sRN/Lu7zG5i14kctaUqRGhGXnu9O/Pt7MzsMiklvF+9t2kWTDvyIrAsA0aKRRi1T0C/hJ4LUbt5/8rNpWVlp8RSr9J40b48fxFaTQ9+ft8EZ6MJYb0Ok+dnYGpmPgXwKIAvLx8vYXc5GdMAQJgQEkpjRTh36TS2U+DWW/D17WuYgm8pwJyY1npZsZKOxImOV1I/h4+O6vEg5GCZBpgmA6hX8wHKUHDRBXQYicQ4rlc3Tf0VMs8DHBS864F2YFspjgUYjKX/Az3gsdQd2eeBHwmdGWXHcgBGSkZXOXohcEXebRoQcAgjqediNY+AVyu3Z3sAKqfKoGMsewBeEIOPgQxxPJIjcGH6qtL/0AdADzKGnuuD+2tLK7Q8DhHHbOBW+KEzcHLuYc82MkEUekLiwuvVH+guQBQzOG4XdAb8EOcRcqQvDkY2iCLuxECJ43JobMXoutqGgDa2T7UqLKwt9KRyuxKVByqVXXqIoCCUCAqhUOioTWC7G4TQEOD0APy2/7G2Xpu1J4+lxeQ4TXBbITDpoVelRN/BVFbwu5oMMJUBhoXy5tmdRcMwymP2OLQaLjx9/vnBo6V3K6izATmSnMa0Dq7ferIohJhr1p01zrlz49rZF4OMs8JkX23vVQzYp+wbYGV/KpXKjvspl8tsIKCrMNAYFxj2GKS5ZWxg4ewKsJfaGMIY5KXqPz8LBBj6+yDvVP79+yDp/9F9oIx3OisHWwe7Oal0HxCAAAQgAAEIQAACEIAABCAAAQhAAAIQgAAEIAABCEAAAhCAAAQgwD8E/BZgAP0qhKj3rXO7AAAAAElFTkSuQmCC") no-repeat;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview label, .css-treeview span, .css-treeview label::before {
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        height: 16px;
 | 
			
		||||
        line-height: 16px;
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview label {
 | 
			
		||||
        background-position: 18px 0;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview label::before {
 | 
			
		||||
        content: "";
 | 
			
		||||
        width: 16px;
 | 
			
		||||
        margin: 0 22px 0 0;
 | 
			
		||||
        vertical-align: middle;
 | 
			
		||||
        background-position: 0 -32px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .css-treeview input:checked + label::before {
 | 
			
		||||
        background-position: 0 -16px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* webkit adjacent element selector bugfix */
 | 
			
		||||
      @media screen and (-webkit-min-device-pixel-ratio:0)
 | 
			
		||||
      {
 | 
			
		||||
        .css-treeview{
 | 
			
		||||
          -webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @-webkit-keyframes webkit-adjacent-element-selector-bugfix 
 | 
			
		||||
        {
 | 
			
		||||
          from  { 
 | 
			
		||||
            padding: 0;
 | 
			
		||||
          } 
 | 
			
		||||
          to  { 
 | 
			
		||||
            padding: 0;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      #uploader { 
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 0;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        height:28px;
 | 
			
		||||
        line-height: 24px;
 | 
			
		||||
        padding-left: 10px;
 | 
			
		||||
        background-color: #444;
 | 
			
		||||
        color:#EEE;
 | 
			
		||||
      }
 | 
			
		||||
      #tree { 
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 28px;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        left: 0;
 | 
			
		||||
        width:200px;
 | 
			
		||||
        padding: 8px;
 | 
			
		||||
      }
 | 
			
		||||
      #editor, #preview { 
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        top: 28px;
 | 
			
		||||
        right: 0;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        left: 200px;
 | 
			
		||||
      }
 | 
			
		||||
      #preview {
 | 
			
		||||
        background-color: #EEE;
 | 
			
		||||
        padding:5px;
 | 
			
		||||
      }
 | 
			
		||||
    </style>
 | 
			
		||||
    <script>
 | 
			
		||||
      function createFileUploader(element, tree, editor){
 | 
			
		||||
        var xmlHttp;
 | 
			
		||||
        var input = document.createElement("input");
 | 
			
		||||
        input.type = "file";
 | 
			
		||||
        input.multiple = false;
 | 
			
		||||
        input.name = "data";
 | 
			
		||||
        document.getElementById(element).appendChild(input);
 | 
			
		||||
        var path = document.createElement("input");
 | 
			
		||||
        path.id = "upload-path";
 | 
			
		||||
        path.type = "text";
 | 
			
		||||
        path.name = "path";
 | 
			
		||||
        path.defaultValue = "/";
 | 
			
		||||
        document.getElementById(element).appendChild(path);
 | 
			
		||||
        var button = document.createElement("button");
 | 
			
		||||
        button.innerHTML = 'Upload';
 | 
			
		||||
        document.getElementById(element).appendChild(button);
 | 
			
		||||
        var mkdir = document.createElement("button");
 | 
			
		||||
        mkdir.innerHTML = 'MkDir';
 | 
			
		||||
        document.getElementById(element).appendChild(mkdir);
 | 
			
		||||
        var mkfile = document.createElement("button");
 | 
			
		||||
        mkfile.innerHTML = 'MkFile';
 | 
			
		||||
        document.getElementById(element).appendChild(mkfile);
 | 
			
		||||
  
 | 
			
		||||
        function httpPostProcessRequest(){
 | 
			
		||||
          if (xmlHttp.readyState == 4){
 | 
			
		||||
            if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
 | 
			
		||||
            else {
 | 
			
		||||
              tree.refreshPath(path.value);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        function createPath(p){
 | 
			
		||||
          xmlHttp = new XMLHttpRequest();
 | 
			
		||||
          xmlHttp.onreadystatechange = httpPostProcessRequest;
 | 
			
		||||
          var formData = new FormData();
 | 
			
		||||
          formData.append("path", p);
 | 
			
		||||
          xmlHttp.open("PUT", "/edit");
 | 
			
		||||
          xmlHttp.send(formData);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        mkfile.onclick = function(e){
 | 
			
		||||
          if(path.value.indexOf(".") === -1) return;
 | 
			
		||||
          createPath(path.value);
 | 
			
		||||
          editor.loadUrl(path.value);
 | 
			
		||||
        };
 | 
			
		||||
        mkdir.onclick = function(e){
 | 
			
		||||
          if(path.value.length < 2) return;
 | 
			
		||||
          var dir = path.value
 | 
			
		||||
          if(dir.indexOf(".") !== -1){
 | 
			
		||||
            if(dir.lastIndexOf("/") === 0) return;
 | 
			
		||||
            dir = dir.substring(0, dir.lastIndexOf("/"));
 | 
			
		||||
          }
 | 
			
		||||
          createPath(dir);
 | 
			
		||||
        };
 | 
			
		||||
        button.onclick = function(e){
 | 
			
		||||
          if(input.files.length === 0){
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          xmlHttp = new XMLHttpRequest();
 | 
			
		||||
          xmlHttp.onreadystatechange = httpPostProcessRequest;
 | 
			
		||||
          var formData = new FormData();
 | 
			
		||||
          formData.append("data", input.files[0], path.value);
 | 
			
		||||
          xmlHttp.open("POST", "/edit");
 | 
			
		||||
          xmlHttp.send(formData);
 | 
			
		||||
        }
 | 
			
		||||
        input.onchange = function(e){
 | 
			
		||||
          if(input.files.length === 0) return;
 | 
			
		||||
          var filename = input.files[0].name;
 | 
			
		||||
          var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
 | 
			
		||||
          var name = /(.*)\.[^.]+$/.exec(filename)[1];
 | 
			
		||||
          if(typeof name !== undefined){
 | 
			
		||||
            if(name.length > 8) name = name.substring(0, 8);
 | 
			
		||||
            filename = name;
 | 
			
		||||
          }
 | 
			
		||||
          if(typeof ext !== undefined){
 | 
			
		||||
            if(ext === "html") ext = "htm";
 | 
			
		||||
            else if(ext === "jpeg") ext = "jpg";
 | 
			
		||||
            filename = filename + "." + ext;
 | 
			
		||||
          }
 | 
			
		||||
          if(path.value === "/" || path.value.lastIndexOf("/") === 0){
 | 
			
		||||
            path.value = "/"+filename;
 | 
			
		||||
          } else {
 | 
			
		||||
            path.value = path.value.substring(0, path.value.lastIndexOf("/")+1)+filename;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function createTree(element, editor){
 | 
			
		||||
        var preview = document.getElementById("preview");
 | 
			
		||||
        var treeRoot = document.createElement("div");
 | 
			
		||||
        treeRoot.className = "css-treeview";
 | 
			
		||||
        document.getElementById(element).appendChild(treeRoot);
 | 
			
		||||
  
 | 
			
		||||
        function loadDownload(path){
 | 
			
		||||
          document.getElementById('download-frame').src = path+"?download=true";
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function loadPreview(path){
 | 
			
		||||
          document.getElementById("editor").style.display = "none";
 | 
			
		||||
          preview.style.display = "block";
 | 
			
		||||
          preview.innerHTML = '<img src="'+path+'" style="max-width:100%; max-height:100%; margin:auto; display:block;" />';
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function fillFolderMenu(el, path){
 | 
			
		||||
          var list = document.createElement("ul");
 | 
			
		||||
          el.appendChild(list);
 | 
			
		||||
          var action = document.createElement("li");
 | 
			
		||||
          list.appendChild(action);
 | 
			
		||||
          var isChecked = document.getElementById(path).checked;
 | 
			
		||||
          var expnd = document.createElement("li");
 | 
			
		||||
          list.appendChild(expnd);
 | 
			
		||||
          if(isChecked){
 | 
			
		||||
            expnd.innerHTML = "<span>Collapse</span>";
 | 
			
		||||
            expnd.onclick = function(e){
 | 
			
		||||
              document.getElementById(path).checked = false;
 | 
			
		||||
              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
            };
 | 
			
		||||
            var refrsh = document.createElement("li");
 | 
			
		||||
            list.appendChild(refrsh);
 | 
			
		||||
            refrsh.innerHTML = "<span>Refresh</span>";
 | 
			
		||||
            refrsh.onclick = function(e){
 | 
			
		||||
              var leaf = document.getElementById(path).parentNode;
 | 
			
		||||
              if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
 | 
			
		||||
              httpGet(leaf, path);
 | 
			
		||||
              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
            };
 | 
			
		||||
          } else {
 | 
			
		||||
            expnd.innerHTML = "<span>Expand</span>";
 | 
			
		||||
            expnd.onclick = function(e){
 | 
			
		||||
              document.getElementById(path).checked = true;
 | 
			
		||||
              var leaf = document.getElementById(path).parentNode;
 | 
			
		||||
              if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
 | 
			
		||||
              httpGet(leaf, path);
 | 
			
		||||
              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
          var upload = document.createElement("li");
 | 
			
		||||
          list.appendChild(upload);
 | 
			
		||||
          upload.innerHTML = "<span>Upload</span>";
 | 
			
		||||
          upload.onclick = function(e){
 | 
			
		||||
            var pathEl = document.getElementById("upload-path");
 | 
			
		||||
            if(pathEl){
 | 
			
		||||
              var subPath = pathEl.value;
 | 
			
		||||
              if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath;
 | 
			
		||||
              else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath;
 | 
			
		||||
            }
 | 
			
		||||
            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
          };
 | 
			
		||||
          var delFile = document.createElement("li");
 | 
			
		||||
          list.appendChild(delFile);
 | 
			
		||||
          delFile.innerHTML = "<span>Delete</span>";
 | 
			
		||||
          delFile.onclick = function(e){
 | 
			
		||||
            httpDelete(path);
 | 
			
		||||
            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function fillFileMenu(el, path){
 | 
			
		||||
          var list = document.createElement("ul");
 | 
			
		||||
          el.appendChild(list);
 | 
			
		||||
          var action = document.createElement("li");
 | 
			
		||||
          list.appendChild(action);
 | 
			
		||||
          if(isTextFile(path)){
 | 
			
		||||
            action.innerHTML = "<span>Edit</span>";
 | 
			
		||||
            action.onclick = function(e){
 | 
			
		||||
              editor.loadUrl(path);
 | 
			
		||||
              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
            };
 | 
			
		||||
          } else if(isImageFile(path)){
 | 
			
		||||
            action.innerHTML = "<span>Preview</span>";
 | 
			
		||||
            action.onclick = function(e){
 | 
			
		||||
              loadPreview(path);
 | 
			
		||||
              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
          var download = document.createElement("li");
 | 
			
		||||
          list.appendChild(download);
 | 
			
		||||
          download.innerHTML = "<span>Download</span>";
 | 
			
		||||
          download.onclick = function(e){
 | 
			
		||||
            loadDownload(path);
 | 
			
		||||
            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
          };
 | 
			
		||||
          var delFile = document.createElement("li");
 | 
			
		||||
          list.appendChild(delFile);
 | 
			
		||||
          delFile.innerHTML = "<span>Delete</span>";
 | 
			
		||||
          delFile.onclick = function(e){
 | 
			
		||||
            httpDelete(path);
 | 
			
		||||
            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function showContextMenu(e, path, isfile){
 | 
			
		||||
          var divContext = document.createElement("div");
 | 
			
		||||
          var scrollTop = document.body.scrollTop ? document.body.scrollTop : document.documentElement.scrollTop;
 | 
			
		||||
          var scrollLeft = document.body.scrollLeft ? document.body.scrollLeft : document.documentElement.scrollLeft;
 | 
			
		||||
          var left = event.clientX + scrollLeft;
 | 
			
		||||
          var top = event.clientY + scrollTop;
 | 
			
		||||
          divContext.className = 'contextMenu';
 | 
			
		||||
          divContext.style.display = 'block';
 | 
			
		||||
          divContext.style.left = left + 'px';
 | 
			
		||||
          divContext.style.top = top + 'px';
 | 
			
		||||
          if(isfile) fillFileMenu(divContext, path);
 | 
			
		||||
          else fillFolderMenu(divContext, path);
 | 
			
		||||
          document.body.appendChild(divContext);
 | 
			
		||||
          var width = divContext.offsetWidth;
 | 
			
		||||
          var height = divContext.offsetHeight;
 | 
			
		||||
          divContext.onmouseout = function(e){
 | 
			
		||||
            if(e.clientX < left || e.clientX > (left + width) || e.clientY < top || e.clientY > (top + height)){
 | 
			
		||||
              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(divContext);
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function createTreeLeaf(path, name, size){
 | 
			
		||||
          var leaf = document.createElement("li");
 | 
			
		||||
          leaf.id = (((path == "/")?"":path)+"/"+name).toLowerCase();
 | 
			
		||||
          var label = document.createElement("span");
 | 
			
		||||
          label.innerText = name.toLowerCase();
 | 
			
		||||
          leaf.appendChild(label);
 | 
			
		||||
          leaf.onclick = function(e){
 | 
			
		||||
            if(isTextFile(leaf.id)){
 | 
			
		||||
              editor.loadUrl(leaf.id);
 | 
			
		||||
            } else if(isImageFile(leaf.id)){
 | 
			
		||||
              loadPreview(leaf.id);
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
          leaf.oncontextmenu = function(e){
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            e.stopPropagation();
 | 
			
		||||
            showContextMenu(e, leaf.id, true);
 | 
			
		||||
          };
 | 
			
		||||
          return leaf;
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function createTreeBranch(path, name, disabled){
 | 
			
		||||
          var leaf = document.createElement("li");
 | 
			
		||||
          var check = document.createElement("input");
 | 
			
		||||
          check.type = "checkbox";
 | 
			
		||||
          check.id = (((path == "/")?"":path)+"/"+name).toLowerCase();
 | 
			
		||||
          if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled";
 | 
			
		||||
          leaf.appendChild(check);
 | 
			
		||||
          var label = document.createElement("label");
 | 
			
		||||
          label.for = check.id;
 | 
			
		||||
          label.innerText = name.toLowerCase();
 | 
			
		||||
          leaf.appendChild(label);
 | 
			
		||||
          check.onchange = function(e){
 | 
			
		||||
            if(check.checked){
 | 
			
		||||
              if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
 | 
			
		||||
              httpGet(leaf, check.id);
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
          label.onclick = function(e){
 | 
			
		||||
            if(!check.checked){
 | 
			
		||||
              check.checked = true;
 | 
			
		||||
              if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
 | 
			
		||||
              httpGet(leaf, check.id);
 | 
			
		||||
            } else {
 | 
			
		||||
              check.checked = false;
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
          leaf.oncontextmenu = function(e){
 | 
			
		||||
            e.preventDefault();
 | 
			
		||||
            e.stopPropagation();
 | 
			
		||||
            showContextMenu(e, check.id, false);
 | 
			
		||||
          }
 | 
			
		||||
          return leaf;
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function addList(parent, path, items){
 | 
			
		||||
          var list = document.createElement("ul");
 | 
			
		||||
          parent.appendChild(list);
 | 
			
		||||
          var ll = items.length;
 | 
			
		||||
          for(var i = 0; i < ll; i++){
 | 
			
		||||
            var item = items[i];
 | 
			
		||||
            var itemEl;
 | 
			
		||||
            if(item.type === "file"){
 | 
			
		||||
              itemEl = createTreeLeaf(path, item.name, item.size);
 | 
			
		||||
            } else {
 | 
			
		||||
              itemEl = createTreeBranch(path, item.name);
 | 
			
		||||
            }
 | 
			
		||||
            list.appendChild(itemEl);
 | 
			
		||||
          }
 | 
			
		||||
    
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function isTextFile(path){
 | 
			
		||||
          var ext = /(?:\.([^.]+))?$/.exec(path)[1];
 | 
			
		||||
          if(typeof ext !== undefined){
 | 
			
		||||
            switch(ext){
 | 
			
		||||
              case "txt":
 | 
			
		||||
              case "htm":
 | 
			
		||||
              case "js":
 | 
			
		||||
              case "c":
 | 
			
		||||
              case "cpp":
 | 
			
		||||
              case "css":
 | 
			
		||||
              case "xml":
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function isImageFile(path){
 | 
			
		||||
          var ext = /(?:\.([^.]+))?$/.exec(path)[1];
 | 
			
		||||
          if(typeof ext !== undefined){
 | 
			
		||||
            switch(ext){
 | 
			
		||||
              case "png":
 | 
			
		||||
              case "jpg":
 | 
			
		||||
              case "gif":
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        this.refreshPath = function(path){
 | 
			
		||||
          if(path.lastIndexOf('/') < 1){
 | 
			
		||||
            path = '/';
 | 
			
		||||
            treeRoot.removeChild(treeRoot.childNodes[0]);
 | 
			
		||||
            httpGet(treeRoot, "/");
 | 
			
		||||
          } else {
 | 
			
		||||
            path = path.substring(0, path.lastIndexOf('/'));
 | 
			
		||||
            var leaf = document.getElementById(path).parentNode;
 | 
			
		||||
            if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
 | 
			
		||||
            httpGet(leaf, path);
 | 
			
		||||
          }
 | 
			
		||||
        };
 | 
			
		||||
  
 | 
			
		||||
        function delCb(path){
 | 
			
		||||
          return function(){
 | 
			
		||||
            if (xmlHttp.readyState == 4){
 | 
			
		||||
              if(xmlHttp.status != 200){
 | 
			
		||||
                alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
 | 
			
		||||
              } else {
 | 
			
		||||
                if(path.lastIndexOf('/') < 1){
 | 
			
		||||
                  path = '/';
 | 
			
		||||
                  treeRoot.removeChild(treeRoot.childNodes[0]);
 | 
			
		||||
                  httpGet(treeRoot, "/");
 | 
			
		||||
                } else {
 | 
			
		||||
                  path = path.substring(0, path.lastIndexOf('/'));
 | 
			
		||||
                  var leaf = document.getElementById(path).parentNode;
 | 
			
		||||
                  if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]);
 | 
			
		||||
                  httpGet(leaf, path);
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function httpDelete(filename){
 | 
			
		||||
          xmlHttp = new XMLHttpRequest();
 | 
			
		||||
          xmlHttp.onreadystatechange = delCb(filename);
 | 
			
		||||
          var formData = new FormData();
 | 
			
		||||
          formData.append("path", filename);
 | 
			
		||||
          xmlHttp.open("DELETE", "/edit");
 | 
			
		||||
          xmlHttp.send(formData);
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function getCb(parent, path){
 | 
			
		||||
          return function(){
 | 
			
		||||
            if (xmlHttp.readyState == 4){
 | 
			
		||||
              //clear loading
 | 
			
		||||
              if(xmlHttp.status == 200) addList(parent, path, JSON.parse(xmlHttp.responseText));
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        function httpGet(parent, path){
 | 
			
		||||
          xmlHttp = new XMLHttpRequest(parent, path);
 | 
			
		||||
          xmlHttp.onreadystatechange = getCb(parent, path);
 | 
			
		||||
          xmlHttp.open("GET", "/list?dir="+path, true);
 | 
			
		||||
          xmlHttp.send(null);
 | 
			
		||||
          //start loading
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        httpGet(treeRoot, "/");
 | 
			
		||||
        return this;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      function createEditor(element, file, lang, theme, type){
 | 
			
		||||
        function getLangFromFilename(filename){
 | 
			
		||||
          var lang = "plain";
 | 
			
		||||
          var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
 | 
			
		||||
          if(typeof ext !== undefined){
 | 
			
		||||
            switch(ext){
 | 
			
		||||
              case "txt": lang = "plain"; break;
 | 
			
		||||
              case "htm": lang = "html"; break;
 | 
			
		||||
              case "js": lang = "javascript"; break;
 | 
			
		||||
              case "c": lang = "c_cpp"; break;
 | 
			
		||||
              case "cpp": lang = "c_cpp"; break;
 | 
			
		||||
              case "css":
 | 
			
		||||
              case "scss":
 | 
			
		||||
              case "php":
 | 
			
		||||
              case "html":
 | 
			
		||||
              case "json":
 | 
			
		||||
              case "xml":
 | 
			
		||||
                lang = ext;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          return lang;
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        if(typeof file === "undefined") file = "/index.htm";
 | 
			
		||||
  
 | 
			
		||||
        if(typeof lang === "undefined"){
 | 
			
		||||
          lang = getLangFromFilename(file);
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        if(typeof theme === "undefined") theme = "textmate";
 | 
			
		||||
  
 | 
			
		||||
        if(typeof type === "undefined"){
 | 
			
		||||
          type = "text/"+lang;
 | 
			
		||||
          if(lang === "c_cpp") type = "text/plain";
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        var xmlHttp = null;
 | 
			
		||||
        var editor = ace.edit(element);
 | 
			
		||||
  
 | 
			
		||||
        //post
 | 
			
		||||
        function httpPostProcessRequest(){
 | 
			
		||||
          if (xmlHttp.readyState == 4){
 | 
			
		||||
            if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        function httpPost(filename, data, type){
 | 
			
		||||
          xmlHttp = new XMLHttpRequest();
 | 
			
		||||
          xmlHttp.onreadystatechange = httpPostProcessRequest;
 | 
			
		||||
          var formData = new FormData();
 | 
			
		||||
          formData.append("data", new Blob([data], { type: type }), filename);
 | 
			
		||||
          xmlHttp.open("POST", "/edit");
 | 
			
		||||
          xmlHttp.send(formData);
 | 
			
		||||
        }
 | 
			
		||||
        //get
 | 
			
		||||
        function httpGetProcessRequest(){
 | 
			
		||||
          if (xmlHttp.readyState == 4){
 | 
			
		||||
            document.getElementById("preview").style.display = "none";
 | 
			
		||||
            document.getElementById("editor").style.display = "block";
 | 
			
		||||
            if(xmlHttp.status == 200) editor.setValue(xmlHttp.responseText);
 | 
			
		||||
            else editor.setValue("");
 | 
			
		||||
            editor.clearSelection();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        function httpGet(theUrl){
 | 
			
		||||
            xmlHttp = new XMLHttpRequest();
 | 
			
		||||
            xmlHttp.onreadystatechange = httpGetProcessRequest;
 | 
			
		||||
            xmlHttp.open("GET", theUrl, true);
 | 
			
		||||
            xmlHttp.send(null);
 | 
			
		||||
        }
 | 
			
		||||
  
 | 
			
		||||
        if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
 | 
			
		||||
        editor.setTheme("ace/theme/"+theme);
 | 
			
		||||
        editor.$blockScrolling = Infinity;
 | 
			
		||||
        editor.getSession().setUseSoftTabs(true);
 | 
			
		||||
        editor.getSession().setTabSize(2);
 | 
			
		||||
        editor.setHighlightActiveLine(true);
 | 
			
		||||
        editor.setShowPrintMargin(false);
 | 
			
		||||
        editor.commands.addCommand({
 | 
			
		||||
            name: 'saveCommand',
 | 
			
		||||
            bindKey: {win: 'Ctrl-S',  mac: 'Command-S'},
 | 
			
		||||
            exec: function(editor) {
 | 
			
		||||
              httpPost(file, editor.getValue()+"", type);
 | 
			
		||||
            },
 | 
			
		||||
            readOnly: false
 | 
			
		||||
        });
 | 
			
		||||
        editor.commands.addCommand({
 | 
			
		||||
            name: 'undoCommand',
 | 
			
		||||
            bindKey: {win: 'Ctrl-Z',  mac: 'Command-Z'},
 | 
			
		||||
            exec: function(editor) {
 | 
			
		||||
              editor.getSession().getUndoManager().undo(false);
 | 
			
		||||
            },
 | 
			
		||||
            readOnly: false
 | 
			
		||||
        });
 | 
			
		||||
        editor.commands.addCommand({
 | 
			
		||||
            name: 'redoCommand',
 | 
			
		||||
            bindKey: {win: 'Ctrl-Shift-Z',  mac: 'Command-Shift-Z'},
 | 
			
		||||
            exec: function(editor) {
 | 
			
		||||
              editor.getSession().getUndoManager().redo(false);
 | 
			
		||||
            },
 | 
			
		||||
            readOnly: false
 | 
			
		||||
        });
 | 
			
		||||
        httpGet(file);
 | 
			
		||||
        editor.loadUrl = function(filename){
 | 
			
		||||
          file = filename;
 | 
			
		||||
          lang = getLangFromFilename(file);
 | 
			
		||||
          type = "text/"+lang;
 | 
			
		||||
          if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
 | 
			
		||||
          httpGet(file);
 | 
			
		||||
        }
 | 
			
		||||
        return editor;
 | 
			
		||||
      }
 | 
			
		||||
      function onBodyLoad(){
 | 
			
		||||
        var vars = {};
 | 
			
		||||
        var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; });
 | 
			
		||||
        var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
 | 
			
		||||
        var tree = createTree("tree", editor);
 | 
			
		||||
        createFileUploader("uploader", tree, editor);
 | 
			
		||||
      };
 | 
			
		||||
    </script>
 | 
			
		||||
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.9/ace.js" type="text/javascript" charset="utf-8"></script>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body onload="onBodyLoad();">
 | 
			
		||||
    <div id="uploader"></div>
 | 
			
		||||
    <div id="tree"></div>
 | 
			
		||||
    <div id="editor"></div>
 | 
			
		||||
    <div id="preview" style="display:none;"></div>
 | 
			
		||||
    <iframe id=download-frame style='display:none;'></iframe>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
 | 
			
		||||
  <title>ESP Index</title>
 | 
			
		||||
  <style>
 | 
			
		||||
    body {
 | 
			
		||||
      background-color:black;
 | 
			
		||||
      color:white;
 | 
			
		||||
    }
 | 
			
		||||
  </style>
 | 
			
		||||
  <script type="text/javascript">
 | 
			
		||||
    function onBodyLoad(){
 | 
			
		||||
      console.log("we are loaded!!");
 | 
			
		||||
    }
 | 
			
		||||
  </script>
 | 
			
		||||
</head>
 | 
			
		||||
<body id="index" onload="onBodyLoad()">
 | 
			
		||||
  <h1>ESP8266 Pin Functions</h1>
 | 
			
		||||
<img src="pins.png" />
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 174 KiB  | 
@@ -152,7 +152,7 @@ void ESP8266WebServer::handleClient()
 | 
			
		||||
 | 
			
		||||
  String formData;
 | 
			
		||||
  //bellow is needed only when POST type request
 | 
			
		||||
  if(method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH){
 | 
			
		||||
  if(method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
 | 
			
		||||
    String boundaryStr;
 | 
			
		||||
    String headerName;
 | 
			
		||||
    String headerValue;
 | 
			
		||||
@@ -391,7 +391,8 @@ void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t
 | 
			
		||||
              line = client.readStringUntil('\r');
 | 
			
		||||
              client.readStringUntil('\n');
 | 
			
		||||
              if(line.startsWith("--"+boundary)) break;
 | 
			
		||||
              argValue += line+"\n";
 | 
			
		||||
              if(argValue.length() > 0) argValue += "\n";
 | 
			
		||||
              argValue += line;
 | 
			
		||||
            }
 | 
			
		||||
  #ifdef DEBUG
 | 
			
		||||
            DEBUG_OUTPUT.print("PostArg Value: ");
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user