diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index bd25fad582..caf8634926 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -1,5 +1,6 @@ /* - FSWebServer - Example WebServer with SPIFFS backend for esp8266 + FSBrowser - A web-based FileSystem Browser for ESP8266 filesystems + Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the ESP8266WebServer library for Arduino environment. @@ -7,30 +8,65 @@ modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - upload the contents of the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu in Arduino IDE) - or you can upload the contents of a folder if you CD in that folder and run the following command: - for file in `\ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done - - access the sample web page at http://esp8266fs.local - edit the page by going to http://esp8266fs.local/edit + See readme.md for more information. */ + +//////////////////////////////// + +// Select the FileSystem by uncommenting one of the lines below + +#define USE_SPIFFS +//#define USE_LITTLEFS +//#define USE_SDFS + +// Uncomment the following line to embed a version of the web page in the code +// (program code will be larger, but no file will have to be written to the filesystem). +// Note: the source file "extras/index_htm.h" must have been generated by "extras/reduce_index.sh" + +//#define INCLUDE_FALLBACK_INDEX_HTM + +//////////////////////////////// + #include #include #include #include +#include + +#ifdef INCLUDE_FALLBACK_INDEX_HTM +#include "extras/index_htm.h" +#endif + +#if defined USE_SPIFFS #include +const char* fsName = "SPIFFS"; +FS* fileSystem = &SPIFFS; +SPIFFSConfig fileSystemConfig = SPIFFSConfig(); +#elif defined USE_LITTLEFS #include +const char* fsName = "LittleFS"; +FS* fileSystem = &LittleFS; +LittleFSConfig fileSystemConfig = LittleFSConfig(); +#elif defined USE_SDFS +#include +const char* fsName = "SDFS"; +FS* fileSystem = &SDFS; +SDFSConfig fileSystemConfig = SDFSConfig(); +// fileSystemConfig.setCSPin(chipSelectPin); +#else +#error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. +#endif -//FS* filesystem = &SPIFFS; -FS* filesystem = &LittleFS; #define DBG_OUTPUT_PORT Serial @@ -41,154 +77,166 @@ FS* filesystem = &LittleFS; const char* ssid = STASSID; const char* password = STAPSK; -const char* host = "esp8266fs"; +const char* host = "fsbrowser"; ESP8266WebServer server(80); -//holds the current upload -File fsUploadFile; - -//format bytes -String formatBytes(size_t bytes) { - if (bytes < 1024) { - return String(bytes) + "B"; - } else if (bytes < (1024 * 1024)) { - return String(bytes / 1024.0) + "KB"; - } else if (bytes < (1024 * 1024 * 1024)) { - return String(bytes / 1024.0 / 1024.0) + "MB"; - } else { - return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; - } + +static bool fsOK; +String unsupportedFiles = String(); + +File uploadFile; + +static const char TEXT_PLAIN[] PROGMEM = "text/plain"; +static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR"; +static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound"; + +//////////////////////////////// +// Utils to return HTTP codes, and determine content-type + +void replyOK() { + server.send(200, FPSTR(TEXT_PLAIN), ""); +} + +void replyOKWithMsg(String msg) { + server.send(200, FPSTR(TEXT_PLAIN), msg); +} + +void replyNotFound(String msg) { + server.send(404, FPSTR(TEXT_PLAIN), msg); +} + +void replyBadRequest(String msg) { + DBG_OUTPUT_PORT.println(msg); + server.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n"); +} + +void replyServerError(String msg) { + DBG_OUTPUT_PORT.println(msg); + server.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n"); } String getContentType(String filename) { - if (server.hasArg("download")) { - return "application/octet-stream"; - } else if (filename.endsWith(".htm")) { + if (filename.endsWith(".htm")) { return "text/html"; - } else if (filename.endsWith(".html")) { + } + if (filename.endsWith(".html")) { return "text/html"; - } else if (filename.endsWith(".css")) { + } + if (filename.endsWith(".css")) { return "text/css"; - } else if (filename.endsWith(".js")) { + } + if (filename.endsWith(".js")) { return "application/javascript"; - } else if (filename.endsWith(".png")) { + } + if (filename.endsWith(".png")) { return "image/png"; - } else if (filename.endsWith(".gif")) { + } + if (filename.endsWith(".gif")) { return "image/gif"; - } else if (filename.endsWith(".jpg")) { + } + if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } + if (filename.endsWith(".jpeg")) { return "image/jpeg"; - } else if (filename.endsWith(".ico")) { + } + if (filename.endsWith(".ico")) { return "image/x-icon"; - } else if (filename.endsWith(".xml")) { + } + if (filename.endsWith(".xml")) { return "text/xml"; - } else if (filename.endsWith(".pdf")) { + } + if (filename.endsWith(".pdf")) { return "application/x-pdf"; - } else if (filename.endsWith(".zip")) { + } + if (filename.endsWith(".zip")) { return "application/x-zip"; - } else if (filename.endsWith(".gz")) { + } + if (filename.endsWith(".gz")) { return "application/x-gzip"; } - return "text/plain"; + return FPSTR(TEXT_PLAIN); } -bool handleFileRead(String path) { - DBG_OUTPUT_PORT.println("handleFileRead: " + path); - if (path.endsWith("/")) { - path += "index.htm"; +#ifdef USE_SPIFFS +/* + Checks filename for character combinations that are not supported by FSBrowser (alhtough valid on SPIFFS). + Returns an empty String if supported, or detail of error(s) if unsupported +*/ +String checkForUnsupportedPath(String filename) { + String error = String(); + if (!filename.startsWith("/")) { + error += F("!NO_LEADING_SLASH! "); } - String contentType = getContentType(path); - String pathWithGz = path + ".gz"; - if (filesystem->exists(pathWithGz) || filesystem->exists(path)) { - if (filesystem->exists(pathWithGz)) { - path += ".gz"; - } - File file = filesystem->open(path, "r"); - server.streamFile(file, contentType); - file.close(); - return true; + if (filename.indexOf("//") != -1) { + error += F("!DOUBLE_SLASH! "); } - return false; -} - -void handleFileUpload() { - if (server.uri() != "/edit") { - return; - } - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - String filename = upload.filename; - if (!filename.startsWith("/")) { - filename = "/" + filename; - } - DBG_OUTPUT_PORT.print("handleFileUpload Name: "); DBG_OUTPUT_PORT.println(filename); - fsUploadFile = filesystem->open(filename, "w"); - filename.clear(); - } else if (upload.status == UPLOAD_FILE_WRITE) { - //DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize); - if (fsUploadFile) { - fsUploadFile.write(upload.buf, upload.currentSize); - } - } else if (upload.status == UPLOAD_FILE_END) { - if (fsUploadFile) { - fsUploadFile.close(); - } - DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); + if (filename.endsWith("/")) { + error += F("!TRAILING_SLASH! "); } + return error; } +#endif -void handleFileDelete() { - if (server.args() == 0) { - return server.send(500, "text/plain", "BAD ARGS"); - } - String path = server.arg(0); - DBG_OUTPUT_PORT.println("handleFileDelete: " + path); - if (path == "/") { - return server.send(500, "text/plain", "BAD PATH"); - } - if (!filesystem->exists(path)) { - return server.send(404, "text/plain", "FileNotFound"); - } - filesystem->remove(path); - server.send(200, "text/plain", ""); - path.clear(); -} -void handleFileCreate() { - if (server.args() == 0) { - return server.send(500, "text/plain", "BAD ARGS"); - } - String path = server.arg(0); - DBG_OUTPUT_PORT.println("handleFileCreate: " + path); - if (path == "/") { - return server.send(500, "text/plain", "BAD PATH"); - } - if (filesystem->exists(path)) { - return server.send(500, "text/plain", "FILE EXISTS"); - } - File file = filesystem->open(path, "w"); - if (file) { - file.close(); +//////////////////////////////// +// Request handlers + +/* + Return the FS type, status and size info +*/ +void handleStatus() { + DBG_OUTPUT_PORT.println("handleStatus"); + FSInfo fs_info; + String json; + json.reserve(128); + + json = "{\"type\":\""; + json += fsName; + json += "\", \"isOk\":"; + if (fsOK) { + fileSystem->info(fs_info); + json += F("\"true\", \"totalBytes\":\""); + json += fs_info.totalBytes; + json += F("\", \"usedBytes\":\""); + json += fs_info.usedBytes; + json += "\""; } else { - return server.send(500, "text/plain", "CREATE FAILED"); + json += "\"false\""; } - server.send(200, "text/plain", ""); - path.clear(); + json += F(",\"unsupportedFiles\":\""); + json += unsupportedFiles; + json += "\"}"; + + server.send(200, "application/json", json); } + +/* + Return the list of files in the directory specified by the "dir" query string parameter. + Also demonstrates the use of chuncked responses. +*/ void handleFileList() { + if (!fsOK) { + return replyServerError(FPSTR(FS_INIT_ERROR)); + } + if (!server.hasArg("dir")) { - server.send(500, "text/plain", "BAD ARGS"); - return; + return replyBadRequest(F("DIR ARG MISSING")); } String path = server.arg("dir"); - DBG_OUTPUT_PORT.println("handleFileList: " + path); - Dir dir = filesystem->openDir(path); + if (path != "/" && !fileSystem->exists(path)) { + return replyBadRequest("BAD PATH"); + } + + DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); + Dir dir = fileSystem->openDir(path); path.clear(); // use HTTP/1.1 Chunked response to avoid building a huge temporary string if (!server.chunkedResponseModeStart(200, "text/json")) { - server.send(505, FPSTR("text/html"), FPSTR("HTTP1.1 required")); + server.send(505, F("text/html"), F("HTTP1.1 required")); return; } @@ -196,7 +244,13 @@ void handleFileList() { String output; output.reserve(64); while (dir.next()) { - +#ifdef USE_SPIFFS + String error = checkForUnsupportedPath(dir.fileName()); + if (error.length() > 0) { + DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); + continue; + } +#endif if (output.length()) { // send string from previous iteration // as an HTTP chunk @@ -206,18 +260,23 @@ void handleFileList() { output = '['; } - File entry = dir.openFile("r"); - bool isDir = false; output += "{\"type\":\""; - output += (isDir) ? "dir" : "file"; - output += "\",\"name\":\""; - if (entry.name()[0] == '/') { - output += &(entry.name()[1]); + if (dir.isDirectory()) { + output += "dir"; + } else { + output += F("file\",\"size\":\""); + output += dir.fileSize(); + } + + output += F("\",\"name\":\""); + // Always return names without leading "/" + if (dir.fileName()[0] == '/') { + output += &(dir.fileName()[1]); } else { - output += entry.name(); + output += dir.fileName(); } + output += "\"}"; - entry.close(); } // send last string @@ -226,85 +285,393 @@ void handleFileList() { server.chunkedResponseFinalize(); } + +/* + Read the given file from the filesystem and stream it back to the client +*/ +bool handleFileRead(String path) { + DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); + if (!fsOK) { + replyServerError(FPSTR(FS_INIT_ERROR)); + return true; + } + + if (path.endsWith("/")) { + path += "index.htm"; + } + + String contentType; + if (server.hasArg("download")) { + contentType = F("application/octet-stream"); + } else { + contentType = getContentType(path); + } + + if (!fileSystem->exists(path)) { + // File not found, try gzip version + path = path + ".gz"; + } + if (fileSystem->exists(path)) { + File file = fileSystem->open(path, "r"); + if (server.streamFile(file, contentType) != file.size()) { + DBG_OUTPUT_PORT.println("Sent less data than expected!"); + } + file.close(); + return true; + } + + return false; +} + + +/* + As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, + return the path of the closest parent still existing +*/ +String lastExistingParent(String path) { + while (!path.isEmpty() && !fileSystem->exists(path)) { + if (path.lastIndexOf('/') > 0) { + path = path.substring(0, path.lastIndexOf('/')); + } else { + path = String(); // No slash => the top folder does not exist + } + } + DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); + return path; +} + +/* + Handle the creation/rename of a new file + Operation | req.responseText + ---------------+-------------------------------------------------------------- + Create file | parent of created file + Create folder | parent of created folder + Rename file | parent of source file + Move file | parent of source file, or remaining ancestor + Rename folder | parent of source folder + Move folder | parent of source folder, or remaining ancestor +*/ +void handleFileCreate() { + if (!fsOK) { + return replyServerError(FPSTR(FS_INIT_ERROR)); + } + + String path = server.arg("path"); + if (path.isEmpty()) { + return replyBadRequest(F("PATH ARG MISSING")); + } + +#ifdef USE_SPIFFS + if (checkForUnsupportedPath(path).length() > 0) { + return replyServerError(F("INVALID FILENAME")); + } +#endif + + if (path == "/") { + return replyBadRequest("BAD PATH"); + } + if (fileSystem->exists(path)) { + return replyBadRequest(F("PATH FILE EXISTS")); + } + + String src = server.arg("src"); + if (src.isEmpty()) { + // No source specified: creation + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + if (path.endsWith("/")) { + // Create a folder + path.remove(path.length() - 1); + if (!fileSystem->mkdir(path)) { + return replyServerError(F("MKDIR FAILED")); + } + } else { + // Create a file + File file = fileSystem->open(path, "w"); + if (file) { + file.write((const char *)0); + file.close(); + } else { + return replyServerError(F("CREATE FAILED")); + } + } + if (path.lastIndexOf('/') > -1) { + path = path.substring(0, path.lastIndexOf('/')); + } + replyOKWithMsg(path); + } else { + // Source specified: rename + if (src == "/") { + return replyBadRequest("BAD SRC"); + } + if (!fileSystem->exists(src)) { + return replyBadRequest(F("SRC FILE NOT FOUND")); + } + + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); + + if (path.endsWith("/")) { + path.remove(path.length() - 1); + } + if (src.endsWith("/")) { + src.remove(src.length() - 1); + } + if (!fileSystem->rename(src, path)) { + return replyServerError(F("RENAME FAILED")); + } + replyOKWithMsg(lastExistingParent(src)); + } +} + + +/* + Delete the file or folder designed by the given path. + If it's a file, delete it. + If it's a folder, delete all nested contents first then the folder itself + + IMPORTANT NOTE: using recursion is generally not recommended on embedded devices and can lead to crashes (stack overflow errors). + This use is just for demonstration purpose, and FSBrowser might crash in case of deeply nested filesystems. + Please don't do this on a production system. +*/ +void deleteRecursive(String path) { + File file = fileSystem->open(path, "r"); + bool isDir = file.isDirectory(); + file.close(); + + // If it's a plain file, delete it + if (!isDir) { + fileSystem->remove(path); + return; + } + + // Otherwise delete its contents first + Dir dir = fileSystem->openDir(path); + + while (dir.next()) { + deleteRecursive(path + '/' + dir.fileName()); + } + + // Then delete the folder itself + fileSystem->rmdir(path); +} + + +/* + Handle a file deletion request + Operation | req.responseText + ---------------+-------------------------------------------------------------- + Delete file | parent of deleted file, or remaining ancestor + Delete folder | parent of deleted folder, or remaining ancestor +*/ +void handleFileDelete() { + if (!fsOK) { + return replyServerError(FPSTR(FS_INIT_ERROR)); + } + + String path = server.arg(0); + if (path.isEmpty() || path == "/") { + return replyBadRequest("BAD PATH"); + } + + DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); + if (!fileSystem->exists(path)) { + return replyNotFound(FPSTR(FILE_NOT_FOUND)); + } + deleteRecursive(path); + + replyOKWithMsg(lastExistingParent(path)); +} + +/* + Handle a file upload request +*/ +void handleFileUpload() { + if (!fsOK) { + return replyServerError(FPSTR(FS_INIT_ERROR)); + } + if (server.uri() != "/edit") { + return; + } + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + String filename = upload.filename; + // Make sure paths always start with "/" + if (!filename.startsWith("/")) { + filename = "/" + filename; + } + DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); + uploadFile = fileSystem->open(filename, "w"); + if (!uploadFile) { + return replyServerError(F("CREATE FAILED")); + } + DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (uploadFile) { + size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize); + if (bytesWritten != upload.currentSize) { + return replyServerError(F("WRITE FAILED")); + } + } + DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); + } else if (upload.status == UPLOAD_FILE_END) { + if (uploadFile) { + uploadFile.close(); + } + DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); + } +} + + +/* + The "Not Found" handler catches all URI not explicitely declared in code + First try to find and return the requested file from the filesystem, + and if it fails, return a 404 page with debug information +*/ +void handleNotFound() { + if (!fsOK) { + return replyServerError(FPSTR(FS_INIT_ERROR)); + } + + String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks + + if (handleFileRead(uri)) { + return; + } + + // Dump debug data + String message; + message.reserve(100); + message = F("Error: File not found\n\nURI: "); + message += uri; + message += F("\nMethod: "); + message += (server.method() == HTTP_GET) ? "GET" : "POST"; + message += F("\nArguments: "); + message += server.args(); + message += '\n'; + for (uint8_t i = 0; i < server.args(); i++) { + message += F(" NAME:"); + message += server.argName(i); + message += F("\n VALUE:"); + message += server.arg(i); + message += '\n'; + } + message += "path="; + message += server.arg("path"); + message += '\n'; + DBG_OUTPUT_PORT.print(message); + + return replyNotFound(message); +} + +/* + This specific handler returns the index.htm (or a gzipped version) from the /edit folder. + If the file is not present but the flag INCLUDE_FALLBACK_INDEX_HTM has been set, falls back to the version + embedded in the program code. + Otherwise, fails with a 404 page with debug information +*/ +void handleGetEdit() { + if (handleFileRead(F("/edit/index.htm"))) { + return; + } + +#ifdef INCLUDE_FALLBACK_INDEX_HTM + server.sendHeader(F("Content-Encoding"), "gzip"); + server.send(200, "text/html", index_htm_gz, index_htm_gz_len); +#else + replyNotFound(FPSTR(FILE_NOT_FOUND)); +#endif + +} + void setup(void) { + //////////////////////////////// + // SERIAL INIT DBG_OUTPUT_PORT.begin(115200); - DBG_OUTPUT_PORT.print("\n"); DBG_OUTPUT_PORT.setDebugOutput(true); - filesystem->begin(); - { - Dir dir = filesystem->openDir("/"); - while (dir.next()) { - String fileName = dir.fileName(); - size_t fileSize = dir.fileSize(); - DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); + DBG_OUTPUT_PORT.print('\n'); + + //////////////////////////////// + // FILESYSTEM INIT + + fileSystemConfig.setAutoFormat(false); + fileSystem->setConfig(fileSystemConfig); + fsOK = fileSystem->begin(); + DBG_OUTPUT_PORT.println(fsOK ? F("Filesystem initialized.") : F("Filesystem init failed!")); + +#ifdef USE_SPIFFS + // Debug: dump on console contents of filessytem with no filter and check filenames validity + Dir dir = fileSystem->openDir(""); + DBG_OUTPUT_PORT.println(F("List of files at root of filesystem:")); + while (dir.next()) { + String error = checkForUnsupportedPath(dir.fileName()); + String fileInfo = dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + "b)"); + DBG_OUTPUT_PORT.println(error + fileInfo); + if (error.length() > 0) { + unsupportedFiles += error + fileInfo + '\n'; } - DBG_OUTPUT_PORT.printf("\n"); } + DBG_OUTPUT_PORT.println(); + // Keep the "unsupportedFiles" variable to show it, but clean it up + unsupportedFiles.replace("\n", "
"); + unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); +#endif - //WIFI INIT + //////////////////////////////// + // WI-FI INIT DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); - if (String(WiFi.SSID()) != String(ssid)) { - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - } - + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + // Wait for connection while (WiFi.status() != WL_CONNECTED) { delay(500); DBG_OUTPUT_PORT.print("."); } DBG_OUTPUT_PORT.println(""); - DBG_OUTPUT_PORT.print("Connected! IP address: "); + DBG_OUTPUT_PORT.print(F("Connected! IP address: ")); DBG_OUTPUT_PORT.println(WiFi.localIP()); - MDNS.begin(host); - DBG_OUTPUT_PORT.print("Open http://"); - DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local/edit to see the file browser"); + //////////////////////////////// + // MDNS INIT + if (MDNS.begin(host)) { + MDNS.addService("http", "tcp", 80); + DBG_OUTPUT_PORT.print(F("Open http://")); + DBG_OUTPUT_PORT.print(host); + DBG_OUTPUT_PORT.println(F(".local/edit to open the FileSystem Browser")); + } + + //////////////////////////////// + // WEB SERVER INIT + // Filesystem status + server.on("/status", HTTP_GET, handleStatus); - //SERVER INIT - //list directory + // List directory server.on("/list", HTTP_GET, handleFileList); - //load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit.htm")) { - server.send(404, "text/plain", "FileNotFound"); - } - }); - //create file - server.on("/edit", HTTP_PUT, handleFileCreate); - //delete file - server.on("/edit", HTTP_DELETE, handleFileDelete); - //first callback is called after the request has ended with all parsed arguments - //second callback handles file uploads at that location - server.on("/edit", HTTP_POST, []() { - server.send(200, "text/plain", ""); - }, handleFileUpload); - - //called when the url is not defined here - //use it to load content from SPIFFS - server.onNotFound([]() { - if (!handleFileRead(server.uri())) { - server.send(404, "text/plain", "FileNotFound"); - } - }); - - //get heap status, analog input value and all GPIO statuses in one json call - server.on("/all", HTTP_GET, []() { - String json('{'); - json += "\"heap\":" + String(ESP.getFreeHeap()); - json += ", \"analog\":" + String(analogRead(A0)); - json += ", \"gpio\":" + String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16))); - json += "}"; - server.send(200, "text/json", json); - json.clear(); - }); + + // Load editor + server.on("/edit", HTTP_GET, handleGetEdit); + + // Create file + server.on("/edit", HTTP_PUT, handleFileCreate); + + // Delete file + server.on("/edit", HTTP_DELETE, handleFileDelete); + + // Upload file + // - first callback is called after the request has ended with all parsed arguments + // - second callback handles file upload at that location + server.on("/edit", HTTP_POST, replyOK, handleFileUpload); + + // Default handler for all URIs not defined above + // Use it to read files from filesystem + server.onNotFound(handleNotFound); + + // Start server server.begin(); DBG_OUTPUT_PORT.println("HTTP server started"); - } + void loop(void) { server.handleClient(); MDNS.update(); diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit.htm.gz deleted file mode 100644 index 69ce414f47..0000000000 Binary files a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit.htm.gz and /dev/null differ diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm new file mode 100644 index 0000000000..31c9613a0f --- /dev/null +++ b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm @@ -0,0 +1,1128 @@ + + + + + File manager + + + + + + + +
+
+ +

Loading...
+ + + diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/graphs.js.gz b/libraries/ESP8266WebServer/examples/FSBrowser/data/graphs.js.gz deleted file mode 100644 index 72435445a7..0000000000 Binary files a/libraries/ESP8266WebServer/examples/FSBrowser/data/graphs.js.gz and /dev/null differ diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm index 4e1dc7d9bb..55fe5a66c4 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm +++ b/libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm @@ -1,97 +1,22 @@ - - ESP Monitor - - - -
- - - - -
-
-
-
+ +

ESP8266 Pin Functions

+ - \ No newline at end of file + diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/pins.png b/libraries/ESP8266WebServer/examples/FSBrowser/data/pins.png new file mode 100644 index 0000000000..f988bf45e3 Binary files /dev/null and b/libraries/ESP8266WebServer/examples/FSBrowser/data/pins.png differ diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/feathericons.png b/libraries/ESP8266WebServer/examples/FSBrowser/extras/feathericons.png new file mode 100644 index 0000000000..5bb2cf63dc Binary files /dev/null and b/libraries/ESP8266WebServer/examples/FSBrowser/extras/feathericons.png differ diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz new file mode 100644 index 0000000000..34797b0589 Binary files /dev/null and b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz differ diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h new file mode 100644 index 0000000000..a90d290535 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h @@ -0,0 +1,529 @@ +// WARNING: Auto-generated file. Please do not modify by hand. +// This file is an embeddable version of the gzipped index.htm file. +// To update it, please rerun the `reduce_index.sh` script located in the `extras` subfolder +// then recompile the sketch after each change to the `index.html` file. +unsigned char index_htm_gz[] = { + 0x1f, 0x8b, 0x08, 0x08, 0x96, 0xc9, 0xa8, 0x5e, 0x00, 0x03, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xdc, 0x3b, 0x89, 0x7b, + 0xda, 0xb8, 0x97, 0xff, 0x8a, 0xe3, 0xee, 0x24, 0xf6, 0x02, 0x06, 0x92, + 0xe6, 0x28, 0xc4, 0xc9, 0xe4, 0x4e, 0x9a, 0xb3, 0xb9, 0xd3, 0x6e, 0xf7, + 0xfb, 0x04, 0x16, 0xa0, 0xc4, 0xd8, 0xae, 0x2d, 0x07, 0x48, 0xca, 0xfe, + 0xed, 0xfb, 0x9e, 0xe4, 0x13, 0x4c, 0x32, 0xdd, 0x9d, 0xf9, 0xcd, 0xec, + 0xb6, 0xf3, 0x15, 0x5b, 0x96, 0x9e, 0xde, 0x7d, 0x49, 0xb3, 0x3e, 0xb7, + 0x7b, 0xbe, 0x73, 0xfd, 0x70, 0xb1, 0xa7, 0xf4, 0x78, 0xdf, 0x56, 0x2e, + 0x6e, 0xb6, 0x4f, 0x8e, 0x76, 0x14, 0xb5, 0x52, 0xad, 0xde, 0x2d, 0xed, + 0x54, 0xab, 0xbb, 0xd7, 0xbb, 0xca, 0xfd, 0xe1, 0xf5, 0xe9, 0x89, 0x52, + 0x37, 0x6a, 0xca, 0xb5, 0x4f, 0x9c, 0x80, 0x71, 0xe6, 0x3a, 0xc4, 0xae, + 0x56, 0xf7, 0xce, 0x54, 0x45, 0xed, 0x71, 0xee, 0x35, 0xaa, 0xd5, 0xc1, + 0x60, 0x60, 0x0c, 0x96, 0x0c, 0xd7, 0xef, 0x56, 0xaf, 0x2f, 0xab, 0x43, + 0x84, 0x55, 0xc7, 0xc5, 0xd1, 0x63, 0x85, 0x67, 0x56, 0x1a, 0x16, 0xb7, + 0xd4, 0x8d, 0x75, 0xb1, 0x9f, 0x4d, 0x9c, 0xae, 0x49, 0x9d, 0x8d, 0x75, + 0xce, 0xb8, 0x4d, 0x37, 0xf6, 0x99, 0x4d, 0x95, 0x3e, 0x71, 0x48, 0x97, + 0xfa, 0xeb, 0x55, 0x39, 0xb6, 0x1e, 0xf0, 0x11, 0x8e, 0x52, 0x8b, 0x11, + 0x33, 0x68, 0xfb, 0x14, 0xa6, 0xb7, 0x5c, 0x6b, 0xf4, 0xda, 0x71, 0x1d, + 0x5e, 0x09, 0xd8, 0x0b, 0x6d, 0xd4, 0x17, 0xbd, 0x61, 0x53, 0xbc, 0x76, + 0x48, 0x9f, 0xd9, 0xa3, 0xc6, 0x2d, 0xf5, 0x2d, 0x80, 0x52, 0xde, 0xf2, + 0x19, 0xb1, 0xcb, 0x57, 0xb0, 0x77, 0x25, 0xa0, 0x3e, 0xeb, 0x8c, 0x8d, + 0x36, 0xcc, 0xa2, 0x43, 0x7e, 0x4a, 0x9d, 0xf0, 0xf5, 0xa5, 0xc2, 0x1c, + 0x8b, 0x0e, 0x1b, 0x4b, 0xb5, 0x5a, 0xd3, 0x73, 0x25, 0x7a, 0x0d, 0xd2, + 0x0a, 0x5c, 0x3b, 0xe4, 0xb4, 0x69, 0xd3, 0x0e, 0x6f, 0x2c, 0x03, 0xe0, + 0x96, 0xeb, 0x5b, 0xd4, 0x6f, 0xd4, 0xbd, 0xa1, 0x02, 0x9f, 0x98, 0xa5, + 0x7c, 0xf8, 0xf8, 0xf1, 0x63, 0xb3, 0x45, 0xda, 0x4f, 0x5d, 0xdf, 0x0d, + 0x1d, 0xab, 0xd2, 0x76, 0x6d, 0xd7, 0x6f, 0x7c, 0xe8, 0x2c, 0xe3, 0xdf, + 0xa6, 0xc5, 0x02, 0xcf, 0x26, 0xa3, 0x86, 0xe3, 0x3a, 0x14, 0xd6, 0x0e, + 0x2b, 0x41, 0x8f, 0x58, 0xee, 0xa0, 0x51, 0x53, 0x6a, 0x4a, 0xbd, 0x06, + 0x40, 0xfc, 0x6e, 0x8b, 0x68, 0xb5, 0x32, 0xfe, 0x35, 0x3e, 0xea, 0xcd, + 0x0c, 0x19, 0xf5, 0x98, 0x8c, 0x01, 0x65, 0xdd, 0x1e, 0x6f, 0xac, 0xd6, + 0x6a, 0x39, 0x8c, 0x95, 0xd0, 0x7e, 0xb5, 0x59, 0x00, 0xd3, 0x91, 0x25, + 0x72, 0x07, 0xee, 0x7a, 0x8d, 0x9a, 0x44, 0xb6, 0xd6, 0xec, 0x13, 0xbf, + 0xcb, 0x1c, 0x78, 0xf0, 0x88, 0x65, 0x31, 0xa7, 0xdb, 0x98, 0x58, 0x6f, + 0xb3, 0xd7, 0x84, 0x50, 0x9f, 0xda, 0x84, 0xb3, 0x67, 0xda, 0xec, 0x33, + 0xa7, 0x32, 0x60, 0x16, 0xef, 0x35, 0x56, 0x00, 0xbd, 0x66, 0x3b, 0xf4, + 0x03, 0x20, 0xc7, 0x73, 0x19, 0xac, 0xf3, 0xf3, 0xeb, 0x03, 0x8f, 0x38, + 0xaf, 0x11, 0xb9, 0xc8, 0x84, 0x98, 0x56, 0xe6, 0xd8, 0xcc, 0xa1, 0x95, + 0x96, 0xed, 0xb6, 0x9f, 0x92, 0xbd, 0x57, 0xbc, 0xe1, 0xe4, 0xee, 0x8d, + 0x9e, 0xfb, 0x4c, 0xfd, 0xd7, 0x94, 0x77, 0x02, 0x4c, 0xf1, 0xac, 0xdc, + 0x66, 0x94, 0x52, 0x98, 0x15, 0x04, 0xa0, 0x48, 0x94, 0x3e, 0x33, 0x3a, + 0x80, 0x69, 0xe5, 0xfc, 0x00, 0xf0, 0x26, 0xa1, 0x3a, 0x65, 0xc4, 0x04, + 0xbb, 0x26, 0x80, 0x30, 0xc7, 0x0b, 0xf9, 0xeb, 0xb4, 0xec, 0x5d, 0x8f, + 0xb4, 0x19, 0x1f, 0x09, 0xf6, 0x65, 0xe6, 0xbf, 0x4e, 0xc8, 0xaa, 0xd2, + 0x77, 0x5f, 0x2a, 0x21, 0x68, 0x16, 0x68, 0x97, 0x4d, 0xdb, 0x5c, 0x4a, + 0x04, 0xa4, 0xd7, 0x7a, 0x62, 0x7c, 0xfa, 0xc3, 0xe4, 0xc0, 0x04, 0x32, + 0x59, 0x7a, 0x6b, 0xb5, 0xce, 0xb4, 0x20, 0x26, 0x27, 0x47, 0xdc, 0x44, + 0xce, 0x55, 0x2c, 0xda, 0x76, 0x7d, 0x22, 0xa8, 0x00, 0xb6, 0x52, 0x1f, + 0xe5, 0x51, 0x44, 0x6c, 0xc9, 0x26, 0x2d, 0x6a, 0x97, 0x80, 0x59, 0x31, + 0x8b, 0x14, 0xfc, 0xbb, 0xb8, 0x28, 0x84, 0x35, 0x35, 0xfd, 0xbf, 0x60, + 0x62, 0x56, 0xa1, 0x27, 0x85, 0x80, 0xc0, 0xca, 0x05, 0x63, 0x8d, 0x46, + 0x8b, 0x76, 0x5c, 0x9f, 0xbe, 0xbe, 0x49, 0x84, 0xd8, 0xa2, 0x01, 0xf0, + 0x49, 0xcb, 0xa6, 0x96, 0x44, 0x2d, 0x5e, 0x61, 0xd1, 0x0e, 0x09, 0x6d, + 0x9e, 0x88, 0xc2, 0x58, 0x29, 0x5c, 0xdc, 0xee, 0xd1, 0xf6, 0x13, 0xb5, + 0x00, 0x39, 0xae, 0x25, 0x90, 0xf4, 0x2c, 0xda, 0x42, 0x29, 0x7f, 0x15, + 0xef, 0x72, 0x81, 0x68, 0x32, 0x5a, 0x1b, 0xfa, 0xb6, 0x66, 0x11, 0x4e, + 0x1a, 0xac, 0x0f, 0xae, 0xaa, 0xea, 0x39, 0x5d, 0xf0, 0x07, 0x01, 0x5d, + 0xf9, 0x58, 0x66, 0xb7, 0xdb, 0xe7, 0x97, 0x83, 0xda, 0xf1, 0x41, 0xd7, + 0xdd, 0x82, 0x3f, 0x67, 0x57, 0x37, 0xbd, 0xbd, 0x9b, 0x2e, 0x3c, 0x6d, + 0xe3, 0xeb, 0x4e, 0x77, 0x67, 0xeb, 0x14, 0x1f, 0x46, 0xcb, 0xc3, 0x41, + 0x1f, 0x1f, 0x5a, 0xf5, 0xed, 0xd3, 0xdb, 0xbd, 0xdb, 0xc3, 0xf6, 0xde, + 0xe8, 0xae, 0xbf, 0xbc, 0x7c, 0x77, 0xb7, 0xb8, 0xb7, 0xf2, 0xe5, 0xc6, + 0xda, 0xfa, 0xb2, 0xb7, 0xcd, 0xc8, 0x41, 0xfd, 0x91, 0x1c, 0xac, 0x56, + 0xab, 0xd5, 0xb5, 0xe7, 0xb3, 0xc7, 0xa5, 0xe3, 0x97, 0xd3, 0xd5, 0x9d, + 0xe1, 0x69, 0xab, 0xbf, 0x1c, 0x76, 0x4e, 0x5f, 0xda, 0xd5, 0x87, 0x45, + 0xeb, 0xc7, 0x90, 0x9f, 0x90, 0x03, 0xe6, 0x2e, 0xaf, 0x75, 0x1f, 0xee, + 0x3e, 0x3f, 0x7e, 0xdd, 0xbf, 0xbc, 0xdd, 0xff, 0xfa, 0xf9, 0x7a, 0xaf, + 0x7a, 0xf2, 0xd2, 0x2e, 0x3d, 0x07, 0xad, 0x33, 0xeb, 0xfe, 0x76, 0xf5, + 0x63, 0xe9, 0xa2, 0xf7, 0x6c, 0x1d, 0xda, 0x41, 0xeb, 0x6e, 0xf1, 0xc9, + 0x5b, 0xf9, 0xb1, 0xfa, 0x7c, 0xf2, 0x32, 0x5a, 0x7b, 0x3e, 0x0d, 0xcf, + 0xae, 0x5f, 0x3a, 0x4b, 0x9f, 0x4a, 0x3d, 0x77, 0xe5, 0x66, 0x74, 0x7e, + 0xb3, 0xb3, 0xdf, 0x7b, 0xb8, 0xbb, 0xb1, 0x97, 0x9d, 0xe7, 0xd5, 0x52, + 0xd5, 0x5b, 0xa1, 0x4f, 0x5f, 0x58, 0xf5, 0xe0, 0x12, 0x71, 0xdc, 0xba, + 0xbf, 0xbc, 0xba, 0xb6, 0x4f, 0xb7, 0xbe, 0x9c, 0xb7, 0x1e, 0xbe, 0x22, + 0x2d, 0x57, 0x97, 0x9f, 0x2f, 0xf7, 0xf6, 0x6f, 0xae, 0x4e, 0x3b, 0xfc, + 0xe9, 0x13, 0x1f, 0x0d, 0xd8, 0xd6, 0x97, 0x9e, 0x7b, 0xb3, 0xd5, 0xbb, + 0xdd, 0x1a, 0x7c, 0xf6, 0x7e, 0xec, 0x5e, 0xfe, 0xe8, 0x90, 0xe7, 0xe7, + 0xb5, 0x17, 0x3b, 0x3c, 0x3b, 0x7e, 0x0a, 0xfd, 0xbd, 0x47, 0xff, 0x61, + 0xa9, 0x44, 0x57, 0x3f, 0x7e, 0x66, 0x2f, 0x27, 0xce, 0xe2, 0x5d, 0xbd, + 0xcf, 0xb6, 0x8e, 0x87, 0x5e, 0xef, 0x7c, 0xfb, 0x94, 0xde, 0x3c, 0xfc, + 0x58, 0x09, 0x0f, 0xab, 0x5b, 0x4b, 0x5b, 0x2b, 0x2b, 0x0f, 0xde, 0xe5, + 0xf6, 0xe5, 0x8f, 0xcf, 0x5f, 0xc9, 0xe9, 0xd1, 0x1a, 0x1b, 0x04, 0xb7, + 0xd5, 0x1d, 0xeb, 0x74, 0x65, 0x6b, 0x71, 0xf8, 0xb8, 0xec, 0x1c, 0xdd, + 0x04, 0xc7, 0xb5, 0x2a, 0xbb, 0xbe, 0xb9, 0xf0, 0x0f, 0xce, 0xfa, 0xb5, + 0xd3, 0x9b, 0xdd, 0xa3, 0x27, 0x7a, 0x50, 0xfd, 0xbc, 0xfc, 0x31, 0x3c, + 0x67, 0x4f, 0x41, 0xeb, 0x53, 0xef, 0xbe, 0xb7, 0xbc, 0x7c, 0xd1, 0x3b, + 0x3a, 0x7a, 0xec, 0x1c, 0x77, 0xad, 0xcf, 0xd7, 0x87, 0x57, 0x7b, 0xa3, + 0xc5, 0xea, 0xfe, 0x6e, 0x6d, 0xe5, 0xbe, 0xef, 0x5a, 0x6b, 0x67, 0xe7, + 0x03, 0xdf, 0x1f, 0xec, 0xdf, 0x04, 0x5f, 0xfa, 0xf7, 0x5f, 0x0f, 0xbf, + 0xf6, 0x7a, 0xf4, 0xe9, 0x70, 0x9b, 0x6d, 0x8f, 0x1e, 0x8e, 0x5c, 0x72, + 0xf4, 0x79, 0xeb, 0xf1, 0x62, 0xed, 0xe6, 0xea, 0x8e, 0xed, 0x6c, 0xad, + 0x1d, 0xf7, 0xf6, 0xee, 0xd6, 0x6e, 0x0e, 0xae, 0x57, 0x8f, 0x2f, 0xc8, + 0xd7, 0xbd, 0x61, 0x70, 0xde, 0x3a, 0x1c, 0xf9, 0x37, 0xdd, 0xeb, 0xa7, + 0xc7, 0xeb, 0x97, 0x35, 0x9b, 0x5d, 0xdc, 0x0f, 0x5e, 0x06, 0x7b, 0xdb, + 0xa5, 0xf3, 0x8b, 0xfd, 0xdb, 0xe1, 0xe1, 0xde, 0xda, 0xfd, 0x62, 0xfb, + 0xe9, 0x72, 0x7b, 0x74, 0x42, 0x6e, 0x47, 0xbd, 0xdb, 0xe3, 0xe1, 0xc5, + 0xe2, 0xea, 0xf1, 0x59, 0xc9, 0xd9, 0xe2, 0x87, 0xab, 0x97, 0xcf, 0xa1, + 0xbf, 0xb8, 0xeb, 0xaf, 0x2c, 0xd6, 0x39, 0x7d, 0x3a, 0xa5, 0x41, 0xe9, + 0x8e, 0x1d, 0xac, 0xad, 0x1c, 0xfa, 0x2b, 0x8f, 0xc7, 0x0f, 0x8f, 0xa5, + 0xd5, 0x2f, 0xf5, 0x63, 0xab, 0x76, 0xe1, 0x0d, 0x8f, 0x96, 0xd7, 0xce, + 0x82, 0x2f, 0xd6, 0x59, 0x75, 0x71, 0xf9, 0xc5, 0xfe, 0xb2, 0xfb, 0xc5, + 0x3a, 0x6e, 0x7d, 0xda, 0x72, 0x4e, 0x57, 0x3a, 0x87, 0x57, 0x07, 0x4f, + 0x17, 0xc1, 0x17, 0xf2, 0x99, 0xf4, 0x8f, 0xbc, 0x2f, 0x2f, 0x3b, 0xfe, + 0x68, 0xd0, 0xdb, 0xad, 0xb3, 0xeb, 0xc5, 0xfb, 0xa7, 0xe0, 0x64, 0x67, + 0x10, 0x54, 0x8f, 0xbe, 0x3e, 0xaf, 0x7d, 0x75, 0x3b, 0xab, 0x7c, 0x71, + 0xf9, 0xc1, 0x7e, 0x12, 0x62, 0xba, 0xba, 0xb9, 0x3d, 0xbf, 0x3c, 0x5e, + 0xde, 0x79, 0x38, 0x3a, 0x32, 0x75, 0xc5, 0x71, 0x2b, 0x3e, 0xf5, 0x28, + 0xe1, 0x7f, 0x82, 0xee, 0x17, 0x0c, 0x25, 0x16, 0x5e, 0x18, 0x03, 0x7a, + 0x32, 0x80, 0xd5, 0x21, 0x06, 0x34, 0xc5, 0x70, 0x76, 0x00, 0x9c, 0x16, + 0x67, 0x6d, 0x62, 0x57, 0x88, 0xcd, 0xba, 0x4e, 0xa3, 0xcf, 0x2c, 0xcb, + 0x2e, 0xf4, 0x2c, 0x19, 0x93, 0xab, 0x24, 0x4e, 0xba, 0xbe, 0x06, 0x31, + 0xb4, 0x56, 0xe4, 0x3b, 0x67, 0xce, 0xae, 0x7c, 0x5c, 0x9b, 0x72, 0x6f, + 0xb8, 0xc2, 0xe0, 0x43, 0xfe, 0xc6, 0xaa, 0x95, 0x8f, 0xc5, 0xab, 0x58, + 0xbf, 0xfb, 0xc6, 0xaa, 0xb5, 0xda, 0xd4, 0xaa, 0x09, 0x97, 0x88, 0xc1, + 0xce, 0xe1, 0x0d, 0x55, 0x6d, 0xca, 0xb0, 0x2b, 0x98, 0x92, 0xb8, 0x63, + 0x74, 0xc5, 0xe8, 0x93, 0x8b, 0xd9, 0xd4, 0x2c, 0xda, 0xb8, 0xa6, 0x54, + 0x96, 0xa6, 0x1d, 0x78, 0x4e, 0x4a, 0x7f, 0x70, 0xd3, 0xb7, 0x7c, 0x6c, + 0x29, 0x4f, 0xc6, 0x0c, 0x44, 0x10, 0xee, 0xf8, 0x77, 0x91, 0xad, 0x29, + 0x32, 0x5b, 0x53, 0x88, 0x63, 0x29, 0x5a, 0x1c, 0x15, 0x31, 0xdb, 0xb0, + 0x00, 0x7a, 0x9b, 0x56, 0x3c, 0x36, 0xa4, 0x76, 0x45, 0x44, 0xad, 0x46, + 0x4d, 0x7f, 0xcd, 0x87, 0xda, 0x78, 0x3e, 0x71, 0xc0, 0xc7, 0x0a, 0xe0, + 0xf1, 0x80, 0xf5, 0x48, 0xda, 0x40, 0x4a, 0x05, 0x02, 0x69, 0x1f, 0x7f, + 0x65, 0x44, 0x75, 0xfd, 0x4a, 0x2b, 0xec, 0x76, 0xd8, 0x10, 0x90, 0xee, + 0x30, 0x87, 0x71, 0xaa, 0xd4, 0x83, 0xf1, 0xef, 0x31, 0x98, 0x27, 0x3a, + 0xea, 0xf8, 0xa4, 0x4f, 0x03, 0xe5, 0x0f, 0x82, 0x79, 0xed, 0xf8, 0x6e, + 0x3f, 0xcd, 0x28, 0xc6, 0xdc, 0xcd, 0xbc, 0x8c, 0xc7, 0x1f, 0x7a, 0x94, + 0x40, 0x98, 0x2d, 0xc8, 0x1d, 0x64, 0x42, 0xe6, 0x0b, 0x75, 0x4f, 0x12, + 0xb3, 0x48, 0xfd, 0x17, 0x41, 0xa3, 0x92, 0xfc, 0x08, 0x13, 0xca, 0xba, + 0x60, 0x3b, 0xa6, 0x85, 0x05, 0x09, 0x25, 0x66, 0x58, 0x99, 0xfc, 0xe7, + 0x03, 0xf2, 0x66, 0xc6, 0x86, 0x8b, 0x32, 0x51, 0xe5, 0xdc, 0xed, 0xa7, + 0x9b, 0x4a, 0x41, 0x2f, 0xd6, 0x7e, 0x4b, 0xb6, 0x44, 0x33, 0xf8, 0x00, + 0xa2, 0x01, 0x32, 0xcb, 0x1f, 0x3c, 0x5f, 0x72, 0xfa, 0x0d, 0x88, 0x31, + 0x15, 0x79, 0xc8, 0x00, 0x71, 0x9c, 0xac, 0x9e, 0x46, 0x1b, 0x70, 0x4d, + 0x36, 0x5c, 0xc6, 0x0d, 0x03, 0x4e, 0x78, 0x18, 0xcc, 0xd8, 0x67, 0x29, + 0xd9, 0x46, 0x70, 0x21, 0x93, 0x61, 0x89, 0xb5, 0x9d, 0xe0, 0x94, 0x42, + 0xe2, 0xf0, 0x1a, 0x29, 0x6d, 0xad, 0x96, 0x32, 0xb0, 0x12, 0x61, 0x85, + 0x9a, 0xff, 0x61, 0x40, 0x7c, 0x07, 0xc6, 0x5e, 0x63, 0x3f, 0x53, 0x03, + 0xaa, 0xa7, 0x50, 0x83, 0x0c, 0xc9, 0xe9, 0xd2, 0x66, 0x92, 0x63, 0x81, + 0xba, 0x73, 0xd7, 0xb5, 0x39, 0xf3, 0x0a, 0x90, 0x8b, 0xab, 0x83, 0xc5, + 0x84, 0x0b, 0x82, 0x2d, 0x88, 0xc0, 0x33, 0x0b, 0x58, 0x8b, 0xd9, 0x98, + 0x96, 0xf4, 0xc0, 0x2a, 0xa9, 0x53, 0x54, 0x0d, 0x74, 0x3a, 0x99, 0x8d, + 0x9a, 0x22, 0x49, 0x93, 0x86, 0x8c, 0x5a, 0x47, 0xfd, 0x82, 0xb2, 0x02, + 0xe7, 0xc5, 0x8c, 0x5b, 0xca, 0xf3, 0xa2, 0x96, 0xa1, 0x31, 0x4a, 0x8f, + 0x13, 0xd4, 0x33, 0xd8, 0x88, 0x47, 0xf0, 0xa5, 0x1f, 0x6c, 0x97, 0x20, + 0x98, 0x19, 0x3c, 0xaf, 0x00, 0x7b, 0x9e, 0x7b, 0x79, 0x35, 0xc1, 0xa1, + 0x41, 0x33, 0x65, 0x1f, 0x7c, 0x8f, 0x39, 0x00, 0x6f, 0xd3, 0x04, 0x66, + 0xab, 0x98, 0x65, 0x3d, 0xcd, 0x98, 0x9b, 0x69, 0xa9, 0xd7, 0x88, 0x06, + 0x15, 0x63, 0x39, 0x50, 0x28, 0xe4, 0x47, 0x00, 0xaf, 0xe2, 0x86, 0x3c, + 0x41, 0xcf, 0x08, 0x7a, 0xee, 0xc0, 0x79, 0x95, 0x16, 0x13, 0x43, 0xa8, + 0x27, 0x9f, 0x2b, 0xfd, 0xa0, 0x5b, 0x1c, 0x5e, 0x66, 0x59, 0x9d, 0xac, + 0xd9, 0x40, 0xf4, 0x02, 0x09, 0x70, 0x53, 0xfd, 0x86, 0x78, 0x82, 0x4a, + 0x87, 0xde, 0x6b, 0x15, 0xf8, 0xa2, 0x37, 0x33, 0xf2, 0x49, 0x19, 0x2c, + 0xdc, 0xe7, 0xef, 0xa9, 0x93, 0x08, 0x3c, 0xe6, 0x38, 0x90, 0xad, 0xa3, + 0xf7, 0x79, 0xad, 0xfd, 0xf6, 0x9a, 0xc2, 0xf3, 0x5d, 0xd0, 0x66, 0xaa, + 0xd5, 0xf4, 0x31, 0xea, 0xd8, 0xf4, 0x87, 0xa5, 0x95, 0x9a, 0x45, 0xbb, + 0xfa, 0x78, 0x6c, 0x64, 0x61, 0xc8, 0xfc, 0xd4, 0xa7, 0x3f, 0x42, 0xe6, + 0x43, 0x7e, 0xfa, 0x0e, 0x55, 0x49, 0x69, 0x86, 0x54, 0x21, 0x39, 0x6f, + 0xd1, 0x25, 0xc8, 0x2a, 0x4b, 0xda, 0x52, 0x67, 0x99, 0xdd, 0x1c, 0x1c, + 0x61, 0xea, 0x14, 0x71, 0x3f, 0x92, 0x6a, 0xdf, 0x4a, 0xaa, 0x7e, 0x68, + 0xb7, 0x72, 0xb8, 0x22, 0x34, 0x3e, 0x12, 0xb4, 0xd8, 0xc8, 0x23, 0x3e, + 0x68, 0x6d, 0xf2, 0x19, 0xc4, 0x13, 0x06, 0x82, 0x69, 0xb2, 0xc2, 0x65, + 0x2f, 0xa8, 0x97, 0xd1, 0x57, 0x18, 0x69, 0xa2, 0x86, 0x76, 0x6c, 0xa8, + 0x7a, 0x23, 0xfb, 0x10, 0xea, 0x8f, 0xda, 0x04, 0x21, 0xa8, 0xf2, 0x09, + 0xfe, 0xc0, 0xca, 0xa8, 0xe6, 0x44, 0x97, 0x18, 0xa9, 0x9d, 0x08, 0xb8, + 0xeb, 0x55, 0x51, 0xae, 0x41, 0xdd, 0xdf, 0xf6, 0x99, 0xc7, 0x37, 0x9e, + 0x89, 0xaf, 0xa0, 0xe7, 0x2b, 0x77, 0x82, 0x23, 0xa7, 0xe3, 0x36, 0x3b, + 0xa1, 0xd3, 0x46, 0x12, 0x95, 0x80, 0xf2, 0x13, 0xa9, 0x28, 0x1a, 0x2d, + 0x73, 0xfd, 0x95, 0xcf, 0xcf, 0x43, 0x94, 0x03, 0x62, 0xa8, 0x61, 0xbb, + 0x5d, 0x8d, 0xeb, 0x65, 0xcb, 0x6d, 0x87, 0xe8, 0xdb, 0x8d, 0x2e, 0xe5, + 0x7b, 0xd2, 0xcd, 0x6f, 0x8f, 0x8e, 0x2c, 0x4d, 0xcd, 0x28, 0x98, 0xaa, + 0x1b, 0x82, 0x53, 0xd8, 0xe4, 0x30, 0xf9, 0xcf, 0x9f, 0xaa, 0x5a, 0xa6, + 0x9b, 0xef, 0x2d, 0x84, 0x45, 0x6d, 0x9b, 0x04, 0xc1, 0x09, 0x54, 0x97, + 0x06, 0x58, 0xac, 0xa6, 0x0a, 0x4d, 0x56, 0xf5, 0xc6, 0x2f, 0xad, 0xf4, + 0x69, 0x1f, 0x18, 0x95, 0x2c, 0x4e, 0xf1, 0xc5, 0xfe, 0x86, 0x21, 0xd8, + 0x60, 0xc8, 0x72, 0xc8, 0xa4, 0x9b, 0xea, 0x80, 0x30, 0xae, 0x36, 0xd4, + 0xa8, 0x30, 0x52, 0xc7, 0x09, 0x23, 0x7c, 0x08, 0x45, 0x58, 0xf7, 0x5c, + 0x81, 0x26, 0x6b, 0x54, 0x7f, 0x65, 0x1d, 0x8d, 0xae, 0xd7, 0x6b, 0x8b, + 0x1f, 0x75, 0x9f, 0xf2, 0xd0, 0x77, 0x14, 0x5a, 0x52, 0x95, 0x6d, 0x15, + 0xd4, 0xdd, 0xd7, 0x04, 0x37, 0xcd, 0x4a, 0xbd, 0xc9, 0x4b, 0xa5, 0x32, + 0xce, 0x59, 0xd7, 0x68, 0xd5, 0x14, 0x93, 0x9b, 0x7a, 0x33, 0x9e, 0x0f, + 0xbe, 0x65, 0x1f, 0x82, 0xb3, 0xa5, 0x2d, 0xea, 0xa5, 0x6f, 0xaa, 0x72, + 0xcc, 0xb6, 0xd5, 0xb2, 0xaa, 0x9c, 0xca, 0x9f, 0x03, 0xf9, 0x73, 0x2d, + 0x7e, 0x2e, 0xe0, 0xdf, 0xef, 0xdf, 0xf8, 0xf7, 0x2c, 0x36, 0x1d, 0x9f, + 0x06, 0xbd, 0x2b, 0xe1, 0xf4, 0x35, 0x50, 0xf5, 0x59, 0x1c, 0x91, 0x61, + 0x21, 0xc7, 0x7f, 0x55, 0x8b, 0x56, 0xa3, 0x73, 0x30, 0x0c, 0x5d, 0x6d, + 0x22, 0xbe, 0xae, 0xe9, 0x40, 0x2e, 0x72, 0x7f, 0x7a, 0x72, 0xc8, 0xb9, + 0x77, 0x09, 0x36, 0x44, 0x03, 0xa8, 0x0b, 0x0d, 0xd7, 0x41, 0x9e, 0x9a, + 0xf1, 0xc6, 0x9a, 0x20, 0x7c, 0xb1, 0x56, 0x9b, 0x33, 0x5d, 0x43, 0xc2, + 0xd6, 0x91, 0xb1, 0xb8, 0x68, 0xcf, 0xf7, 0x81, 0x76, 0x57, 0x6f, 0x52, + 0x3b, 0xa0, 0xaf, 0x08, 0x93, 0x9a, 0x9a, 0xd4, 0x27, 0xf3, 0xf3, 0xd5, + 0xf9, 0x99, 0x01, 0xfa, 0x1d, 0x50, 0xcd, 0x05, 0x89, 0x04, 0x1e, 0xe8, + 0x10, 0xbd, 0x06, 0x7d, 0xd5, 0x75, 0x83, 0x8f, 0x3c, 0xe4, 0x5d, 0x45, + 0x51, 0x9b, 0x00, 0x5b, 0x2e, 0x30, 0x58, 0x70, 0xfe, 0xa4, 0xbf, 0x4a, + 0x46, 0xd6, 0x4b, 0xa7, 0x84, 0xf7, 0x0c, 0xe1, 0x18, 0xb5, 0x4f, 0x9f, + 0xfe, 0x3d, 0x9a, 0x02, 0x95, 0xbe, 0xb5, 0x3d, 0xe2, 0x34, 0xa8, 0x46, + 0x03, 0x1c, 0x9c, 0x83, 0x2d, 0x46, 0xf4, 0xb2, 0x63, 0xe6, 0x04, 0x36, + 0x35, 0xa3, 0x32, 0x09, 0x44, 0x07, 0x14, 0x80, 0x2b, 0x54, 0x71, 0x3b, + 0x8a, 0x5a, 0x7a, 0x7b, 0x31, 0x90, 0x58, 0x32, 0xd5, 0xf5, 0x3e, 0x06, + 0x4d, 0x85, 0x59, 0xe6, 0x42, 0x14, 0x40, 0x17, 0x14, 0x48, 0xbc, 0xcc, + 0x85, 0xda, 0x82, 0xe2, 0x7a, 0x9c, 0xf5, 0xc3, 0x3e, 0x3e, 0x83, 0x81, + 0x9a, 0x0b, 0x9f, 0x60, 0xac, 0x07, 0xe6, 0x07, 0x4f, 0xcb, 0x30, 0x8b, + 0x0c, 0xcd, 0x05, 0xf0, 0x6c, 0x0b, 0xca, 0x33, 0xb1, 0x43, 0x6a, 0x2e, + 0xa8, 0x25, 0x5e, 0x52, 0x17, 0x14, 0xd1, 0x8b, 0xc3, 0x37, 0x07, 0xde, + 0x36, 0xc4, 0xcf, 0x7a, 0x55, 0xec, 0xb2, 0xa1, 0x96, 0x63, 0x84, 0x9d, + 0x20, 0xf4, 0x3c, 0xd7, 0xe7, 0xd4, 0xc2, 0x46, 0x5e, 0x30, 0x3f, 0xaf, + 0x21, 0x32, 0xca, 0x3a, 0x66, 0xa2, 0x02, 0x99, 0x28, 0x82, 0x2d, 0x6c, + 0xdc, 0x6d, 0x5d, 0x9e, 0x1d, 0x9d, 0x1d, 0xc8, 0x2f, 0xc2, 0x20, 0xcc, + 0x85, 0x28, 0x9e, 0x2d, 0x88, 0x2e, 0x60, 0x30, 0x0a, 0x38, 0xed, 0xcf, + 0x3b, 0xad, 0xc0, 0x6b, 0x62, 0xee, 0x4a, 0x98, 0x13, 0xc8, 0xb7, 0xcc, + 0x2e, 0x72, 0xa0, 0x03, 0xd3, 0x1d, 0xf4, 0xda, 0x8d, 0xf5, 0x96, 0x5f, + 0x05, 0xdc, 0x66, 0xa0, 0x83, 0x18, 0xe3, 0x7e, 0x1b, 0xd1, 0x8f, 0xaa, + 0x8f, 0x51, 0x1d, 0x14, 0xc1, 0x30, 0x81, 0x88, 0xb0, 0x38, 0x73, 0x61, + 0x3a, 0xd6, 0x51, 0x2b, 0x0a, 0x1b, 0x83, 0x1e, 0xf8, 0xd0, 0x5c, 0x8f, + 0xae, 0xe5, 0xda, 0xd6, 0xc2, 0xc6, 0xd1, 0xd9, 0xd1, 0xb5, 0xb2, 0x77, + 0x79, 0x79, 0x7e, 0xa9, 0xcc, 0xc5, 0xe0, 0x9b, 0xbf, 0xa2, 0xf9, 0xb4, + 0xac, 0x5e, 0x5d, 0x1c, 0xed, 0xef, 0x5f, 0xa9, 0x73, 0x66, 0x2c, 0x54, + 0x50, 0x3e, 0x60, 0xe1, 0x4c, 0x28, 0xfd, 0x27, 0x8b, 0xf9, 0x00, 0x44, + 0xfa, 0x89, 0x28, 0xa2, 0x98, 0xaa, 0x0c, 0x29, 0x40, 0xdc, 0xb8, 0x0c, + 0x36, 0xe2, 0x51, 0x47, 0x53, 0x0f, 0xf6, 0xae, 0xc1, 0x54, 0xab, 0xd1, + 0xb6, 0xe5, 0xb9, 0x9a, 0x0e, 0x9f, 0x02, 0x0a, 0x2a, 0xeb, 0x84, 0xb6, + 0xad, 0xa7, 0xb6, 0x9b, 0xb7, 0x17, 0x70, 0x25, 0xc4, 0x86, 0x22, 0x44, + 0x53, 0x05, 0x65, 0x0d, 0xe5, 0x9b, 0x5a, 0xa2, 0x91, 0x69, 0x95, 0xd4, + 0xef, 0x0a, 0xbe, 0xe5, 0x0c, 0x26, 0x05, 0xd4, 0x26, 0x0e, 0xfa, 0xe6, + 0x33, 0x3a, 0xd8, 0x91, 0x85, 0x07, 0x7a, 0x02, 0xe9, 0x5d, 0xe6, 0xe6, + 0x66, 0xf3, 0x85, 0x3c, 0xd3, 0x6d, 0x0e, 0x9e, 0xd0, 0x88, 0x3b, 0x39, + 0x3f, 0x7f, 0xce, 0xcd, 0x81, 0xf8, 0x3b, 0xcc, 0xef, 0x6b, 0xea, 0x4e, + 0x0f, 0xd3, 0xb7, 0x40, 0xe1, 0xae, 0x32, 0x72, 0x43, 0x5f, 0x89, 0xe1, + 0x28, 0x03, 0x66, 0xdb, 0x4a, 0x0b, 0x62, 0x9b, 0x1b, 0x70, 0x85, 0x75, + 0xf0, 0xab, 0x82, 0x4a, 0xc3, 0x9c, 0x10, 0x18, 0x81, 0x6a, 0xe8, 0x08, + 0x8b, 0x01, 0xf0, 0xbb, 0x2c, 0x68, 0x13, 0xdf, 0x82, 0x5d, 0x02, 0x6d, + 0xae, 0xae, 0x23, 0x2f, 0x52, 0xac, 0x8b, 0xa7, 0xd1, 0xb7, 0x7c, 0xd8, + 0x14, 0xc6, 0xe6, 0x1c, 0x9d, 0x1d, 0x77, 0xac, 0x04, 0x6c, 0x7e, 0x45, + 0x8a, 0x02, 0xac, 0xb8, 0x10, 0xb1, 0x76, 0x1f, 0xf4, 0x8a, 0x0a, 0x19, + 0x24, 0x4e, 0x19, 0x38, 0xef, 0xf3, 0xe0, 0x8e, 0xf1, 0x9e, 0xa6, 0x56, + 0x55, 0xfd, 0xe7, 0x4f, 0x8d, 0x9a, 0xf0, 0x50, 0xa2, 0x7a, 0x99, 0x1a, + 0x20, 0xcd, 0xf4, 0x13, 0x92, 0x6c, 0xc2, 0x02, 0x1b, 0xca, 0x2c, 0x48, + 0xd2, 0x2a, 0x75, 0x1d, 0xa7, 0x04, 0x61, 0x2b, 0xe0, 0x3e, 0xc6, 0xcb, + 0x1a, 0xbc, 0x81, 0xd1, 0xf1, 0x23, 0xcc, 0xef, 0xce, 0x3b, 0x62, 0x4d, + 0x56, 0x7a, 0xe0, 0x63, 0x38, 0x3d, 0x14, 0x15, 0x0e, 0xc4, 0x56, 0x07, + 0xa3, 0xab, 0x74, 0xc4, 0xb3, 0x28, 0x03, 0x14, 0x48, 0xfa, 0x51, 0xae, + 0x8f, 0xbe, 0x6b, 0xaa, 0xa8, 0x22, 0x55, 0xc8, 0x4e, 0x84, 0x4a, 0x9b, + 0x2a, 0x1a, 0xac, 0x5a, 0x26, 0x46, 0x3f, 0x44, 0x7b, 0x07, 0x8b, 0x9b, + 0xab, 0xc3, 0x1b, 0x9a, 0xb0, 0xa9, 0x62, 0x57, 0x4d, 0x05, 0xed, 0x24, + 0x1e, 0x68, 0xae, 0xb5, 0xd3, 0x63, 0xb6, 0xa5, 0x11, 0x5d, 0x84, 0x01, + 0xeb, 0xdd, 0x0d, 0x2c, 0x03, 0x9c, 0x8c, 0xea, 0x81, 0x47, 0x3e, 0x12, + 0x23, 0x65, 0x2b, 0xda, 0x11, 0xd3, 0x0f, 0x7c, 0x93, 0x7b, 0xe0, 0x04, + 0x7c, 0x8b, 0x22, 0xe9, 0xad, 0x70, 0x74, 0xc0, 0x82, 0x89, 0x6d, 0x2d, + 0xb9, 0xad, 0x3d, 0x73, 0xdb, 0x56, 0x08, 0xf5, 0x07, 0x48, 0xb2, 0x69, + 0x67, 0x43, 0xd8, 0x8d, 0x87, 0x61, 0x69, 0x12, 0x98, 0x2d, 0x81, 0xb1, + 0xf7, 0x81, 0x31, 0x41, 0x84, 0x34, 0xee, 0x32, 0x9b, 0xb4, 0x6e, 0x6c, + 0xc2, 0xe2, 0x70, 0x66, 0xc3, 0xd3, 0xa7, 0x5d, 0x9c, 0x9a, 0xdf, 0x8f, + 0xc9, 0xfd, 0x82, 0xf7, 0xf7, 0x0b, 0xf2, 0xb0, 0xf6, 0x85, 0x70, 0xf2, + 0xc0, 0x02, 0x09, 0xac, 0x3d, 0x13, 0x18, 0x3a, 0x3c, 0x00, 0xd5, 0x16, + 0xa8, 0xcb, 0xfa, 0x72, 0x5b, 0xc0, 0x07, 0x6f, 0xd3, 0x2e, 0x26, 0x21, + 0xbf, 0x43, 0x5b, 0xee, 0xe0, 0xbf, 0x8f, 0xae, 0x2f, 0xf6, 0x88, 0xed, + 0xae, 0xec, 0x67, 0xb1, 0x47, 0xcb, 0xc5, 0x21, 0xb9, 0xa1, 0x6c, 0x6e, + 0x9c, 0x40, 0xea, 0x6c, 0xaa, 0x4b, 0x50, 0x40, 0xe1, 0x97, 0xd4, 0xe6, + 0x6a, 0x80, 0x58, 0x16, 0x03, 0x5f, 0x62, 0xd0, 0x7f, 0x1f, 0x83, 0xbe, + 0xc0, 0x20, 0x63, 0xca, 0xe5, 0x7e, 0x16, 0x89, 0xc8, 0x75, 0xe0, 0xe8, + 0xec, 0xdd, 0xfa, 0x72, 0xb7, 0xf0, 0xfd, 0xdd, 0x42, 0xb1, 0x5b, 0x8f, + 0xda, 0x9e, 0xd8, 0x2a, 0xcc, 0x6e, 0xb5, 0xa9, 0x4e, 0x80, 0x0d, 0x25, + 0x58, 0xef, 0x3d, 0x41, 0x79, 0x92, 0x89, 0x51, 0x3c, 0xf0, 0x72, 0x19, + 0x58, 0x94, 0xa8, 0xea, 0x93, 0x22, 0xf2, 0xc0, 0xc4, 0x21, 0xe7, 0x6a, + 0x0b, 0x27, 0x9c, 0x66, 0x5d, 0x32, 0xdf, 0x84, 0xa4, 0xcb, 0x24, 0x06, + 0x1a, 0x76, 0x60, 0x40, 0x34, 0xee, 0xf2, 0x5e, 0x9c, 0x21, 0x45, 0xa3, + 0xdf, 0x6a, 0xdf, 0x85, 0xf5, 0x35, 0x9f, 0x5d, 0xa8, 0x35, 0x6a, 0x60, + 0x7d, 0x22, 0xbf, 0x28, 0xf0, 0x69, 0xd1, 0x17, 0xe1, 0xd9, 0xa2, 0x67, + 0x3d, 0x9e, 0x6e, 0x4e, 0xba, 0xc7, 0x78, 0x42, 0x09, 0x67, 0x73, 0x08, + 0x79, 0x36, 0xa2, 0x08, 0x6e, 0xef, 0x29, 0x87, 0xe1, 0x34, 0x7a, 0xf3, + 0xf3, 0x8e, 0x81, 0x67, 0x97, 0xd2, 0x5a, 0xb5, 0x14, 0xcb, 0x78, 0x2b, + 0x7d, 0x5c, 0x0e, 0x0a, 0x61, 0x49, 0xb2, 0x62, 0xfc, 0xc1, 0x9f, 0xf6, + 0x35, 0xbd, 0xa9, 0xaa, 0xa6, 0x69, 0xf2, 0xcd, 0x28, 0x5c, 0x6e, 0x29, + 0x71, 0x4e, 0xa2, 0xf4, 0x43, 0x08, 0x49, 0x10, 0x9a, 0xba, 0x50, 0xce, + 0x61, 0x7d, 0xc0, 0xf3, 0x8e, 0x3a, 0x5e, 0xb1, 0x1f, 0xe7, 0x30, 0x72, + 0x01, 0x54, 0x8b, 0x10, 0x8c, 0x2c, 0x08, 0x6c, 0xbc, 0xa7, 0x10, 0x65, + 0xa1, 0xba, 0xa0, 0x00, 0xdf, 0x7d, 0xd2, 0x86, 0x7c, 0x0b, 0x80, 0x48, + 0xd4, 0x77, 0x84, 0x68, 0xa1, 0xce, 0x19, 0x83, 0x3f, 0xf8, 0x9f, 0x63, + 0x2a, 0x18, 0xa9, 0x14, 0x22, 0xab, 0x4d, 0x60, 0x0b, 0xd2, 0xe1, 0x25, + 0x14, 0x0c, 0x24, 0xb1, 0x79, 0x14, 0x00, 0x07, 0xbf, 0x10, 0x07, 0x6e, + 0xa0, 0xa9, 0x6a, 0xf0, 0xbd, 0x3f, 0xe3, 0x7b, 0x64, 0x48, 0x38, 0x25, + 0x9c, 0x05, 0x02, 0xd2, 0x92, 0xab, 0x1e, 0x64, 0x72, 0xed, 0x10, 0x13, + 0x89, 0xf1, 0x64, 0xa4, 0xba, 0x86, 0xe4, 0x18, 0xe2, 0x14, 0x93, 0x24, + 0x3b, 0x33, 0xa3, 0x94, 0x1a, 0x35, 0xad, 0x00, 0x7f, 0x3e, 0xd3, 0x44, + 0x2c, 0xf6, 0x0c, 0x16, 0x92, 0xa6, 0x44, 0x6f, 0xc6, 0xff, 0x34, 0xd6, + 0xe8, 0x91, 0x86, 0xbe, 0x11, 0xfd, 0xa5, 0x63, 0x9c, 0xce, 0xd8, 0xa4, + 0x43, 0x7c, 0x67, 0x59, 0xec, 0x4f, 0x67, 0xac, 0x76, 0x26, 0x87, 0x45, + 0x4b, 0x01, 0xc7, 0x33, 0xb6, 0xbd, 0xce, 0xfa, 0x5d, 0x25, 0xf0, 0xdb, + 0x98, 0xc7, 0x53, 0xcc, 0xea, 0xa3, 0x84, 0x17, 0x92, 0xfe, 0x4a, 0xd2, + 0x05, 0xfa, 0xad, 0x89, 0x45, 0x40, 0x25, 0xdb, 0x48, 0x53, 0xa2, 0x3e, + 0x31, 0x09, 0xb9, 0xdb, 0x54, 0x72, 0x07, 0x6b, 0xcd, 0x05, 0x05, 0x52, + 0xee, 0x8c, 0x40, 0xfe, 0x3f, 0xb1, 0x2b, 0x5b, 0x14, 0xa4, 0x95, 0xc0, + 0xc2, 0xc6, 0x56, 0x1b, 0xaa, 0x06, 0xb1, 0x0d, 0xa4, 0x99, 0xa1, 0x6d, + 0x09, 0x7b, 0x15, 0xf9, 0x27, 0x64, 0x4c, 0x96, 0x82, 0x9d, 0x63, 0x85, + 0xf7, 0xa8, 0x22, 0xce, 0x3c, 0x1d, 0x8a, 0xf6, 0xec, 0xcb, 0xd1, 0x2a, + 0x2e, 0xab, 0x92, 0x36, 0x35, 0x1e, 0x03, 0xc5, 0x50, 0x76, 0x65, 0x06, + 0x02, 0xee, 0x16, 0x93, 0x5a, 0xcc, 0x52, 0x14, 0xd4, 0x4f, 0xea, 0x43, + 0xf1, 0x1b, 0x17, 0x2b, 0xa0, 0xb3, 0xa2, 0x76, 0xe2, 0x43, 0x1e, 0xe7, + 0xd2, 0x89, 0xe0, 0x92, 0x36, 0x8b, 0x22, 0x44, 0xb3, 0x00, 0xa5, 0x0d, + 0x4c, 0xdf, 0x90, 0x65, 0x33, 0x2f, 0x2a, 0x9b, 0x79, 0x41, 0xd9, 0x3c, + 0x93, 0x95, 0x99, 0x2d, 0x81, 0x91, 0x88, 0x5e, 0xf4, 0x6a, 0xf2, 0x1e, + 0x0b, 0x72, 0xb9, 0xff, 0xb8, 0xcc, 0xb3, 0xc5, 0x06, 0xb8, 0x6c, 0x2e, + 0x2b, 0x8c, 0x4c, 0x5a, 0xe9, 0xa7, 0x4e, 0xa9, 0xaa, 0x6d, 0x36, 0xfe, + 0xc3, 0xd0, 0xbe, 0xfd, 0xa7, 0xf1, 0xbd, 0xa4, 0xeb, 0x9b, 0xff, 0x56, + 0x35, 0xe8, 0x90, 0xa2, 0xee, 0x7c, 0xab, 0x7f, 0xc7, 0x52, 0x5b, 0x46, + 0x09, 0xf0, 0xda, 0x5c, 0x0f, 0xc0, 0x09, 0xb6, 0x7b, 0xe0, 0x8a, 0xb8, + 0x7b, 0xe2, 0x02, 0x6b, 0x76, 0x08, 0x54, 0xea, 0xba, 0xfe, 0xda, 0x86, + 0x5f, 0xc4, 0x50, 0x6d, 0x88, 0xa7, 0x1e, 0xef, 0xa7, 0x4f, 0x76, 0xf4, + 0xf8, 0x18, 0x24, 0x0f, 0x10, 0x4c, 0xe5, 0x63, 0x3b, 0x9e, 0x16, 0xbf, + 0x7b, 0x5e, 0xfc, 0x14, 0xc4, 0xd3, 0x87, 0x08, 0x21, 0xca, 0xbb, 0xeb, + 0x19, 0x77, 0xd3, 0xff, 0xf3, 0x29, 0xf0, 0x9c, 0x6e, 0x8c, 0xa3, 0x97, + 0x3e, 0xd1, 0xf8, 0xb1, 0xcb, 0x3a, 0xd1, 0x13, 0x6b, 0xbb, 0x85, 0x38, + 0x85, 0x9a, 0x53, 0x76, 0xf5, 0xa8, 0xa9, 0x31, 0xcb, 0xa9, 0x85, 0x36, + 0xf8, 0x34, 0x27, 0x17, 0xcf, 0xa9, 0x1e, 0x69, 0xc9, 0xac, 0x35, 0x36, + 0x83, 0x35, 0x34, 0xb7, 0x86, 0xeb, 0x65, 0x6c, 0xa3, 0x6c, 0xaa, 0x78, + 0xc7, 0xa0, 0x03, 0x65, 0xa6, 0x05, 0xd1, 0x04, 0x53, 0x6c, 0xb7, 0xa3, + 0x80, 0x52, 0x6f, 0x02, 0x8d, 0x93, 0xf6, 0xb3, 0x71, 0x0b, 0x0a, 0x1d, + 0x97, 0xc4, 0xa8, 0x24, 0x45, 0xe1, 0x79, 0x3d, 0xdf, 0xf9, 0x4a, 0xf5, + 0x30, 0xd8, 0x1e, 0xed, 0x60, 0x77, 0xe0, 0x0c, 0xc2, 0x93, 0xa6, 0x66, + 0xae, 0x89, 0x80, 0x3e, 0xc6, 0x81, 0x3c, 0xbf, 0x56, 0xf6, 0xd4, 0x24, + 0xba, 0x8e, 0x5e, 0x2e, 0x2a, 0x44, 0xe7, 0xe7, 0xdb, 0x40, 0xc4, 0x58, + 0xc4, 0xb7, 0x29, 0x74, 0xf7, 0xc0, 0x40, 0xff, 0x69, 0xe8, 0x32, 0x03, + 0x4d, 0xf6, 0xc6, 0xb7, 0x25, 0xda, 0x7d, 0xf8, 0x81, 0x0a, 0xaf, 0x00, + 0xf9, 0x0b, 0x19, 0xdf, 0xfe, 0x69, 0xf8, 0x07, 0x02, 0x6f, 0xa1, 0x6e, + 0xb3, 0xab, 0xc4, 0x02, 0x75, 0x23, 0x98, 0x72, 0x4e, 0x11, 0xb9, 0xeb, + 0x0e, 0x84, 0x0b, 0x4b, 0xa8, 0x24, 0xb3, 0xb3, 0x9f, 0xe6, 0x5f, 0x48, + 0x2b, 0x37, 0xdd, 0x37, 0x2a, 0xfd, 0x08, 0xc9, 0x8a, 0x38, 0x50, 0xc0, + 0x30, 0x04, 0x61, 0x97, 0x97, 0xd4, 0xcd, 0xf8, 0x83, 0xc9, 0xfd, 0x90, + 0xaa, 0xe3, 0x77, 0x2a, 0xdb, 0x02, 0xa6, 0x58, 0x98, 0x0d, 0x4f, 0x31, + 0xe5, 0x52, 0xa4, 0x8f, 0xd5, 0x53, 0x40, 0x30, 0xe1, 0x8b, 0xf5, 0x2f, + 0x96, 0x7e, 0xe4, 0x4f, 0x3c, 0x08, 0x74, 0x1e, 0xe0, 0x2e, 0x51, 0x52, + 0xd4, 0x92, 0x5b, 0x52, 0x21, 0xbc, 0x41, 0x3d, 0x01, 0x2e, 0x28, 0xb4, + 0xed, 0x39, 0x93, 0xcf, 0xcf, 0xf3, 0x39, 0xd3, 0x9d, 0x9f, 0x1f, 0x69, + 0x6e, 0x19, 0xf2, 0xd7, 0x77, 0xea, 0xec, 0x02, 0x26, 0xd8, 0x7a, 0xd9, + 0x2e, 0xd0, 0x0c, 0x6a, 0x53, 0x9e, 0xd2, 0x3f, 0xa3, 0x16, 0xf8, 0xeb, + 0x34, 0xa2, 0x8b, 0x7a, 0x9e, 0x7a, 0x66, 0x1b, 0xcf, 0x25, 0xca, 0xce, + 0x54, 0xef, 0xa4, 0x28, 0xe1, 0xcc, 0xb6, 0x4f, 0x64, 0xf7, 0xbf, 0xed, + 0xbb, 0xb6, 0x7d, 0xed, 0x7a, 0x9b, 0x33, 0xc6, 0xd3, 0xa3, 0x86, 0xf8, + 0x21, 0x82, 0x98, 0x4e, 0x29, 0x5b, 0x85, 0x40, 0xb1, 0x24, 0x2e, 0x84, + 0x8a, 0x1f, 0xde, 0x03, 0x8b, 0x73, 0xca, 0xb6, 0x49, 0x0d, 0xe0, 0x2c, + 0x8c, 0xde, 0x97, 0xac, 0x32, 0x4b, 0xde, 0x1e, 0x4a, 0xa4, 0xe9, 0xca, + 0x03, 0x8e, 0x33, 0xd1, 0x6a, 0xc9, 0x32, 0x11, 0x7b, 0x8e, 0x85, 0x49, + 0x57, 0x3c, 0x8e, 0xa7, 0x5c, 0xa6, 0x5d, 0x52, 0xb1, 0x54, 0x8f, 0xc7, + 0xb8, 0xeb, 0x99, 0x4c, 0x0e, 0x69, 0xce, 0x66, 0xd8, 0x48, 0x24, 0xf9, + 0xaf, 0x0c, 0x7a, 0x93, 0x9e, 0x6b, 0xc2, 0xd8, 0x5d, 0xdd, 0x68, 0xe3, + 0xcc, 0x33, 0xd7, 0x92, 0x85, 0x6e, 0x74, 0x61, 0xa2, 0xfc, 0x8e, 0x5d, + 0xe3, 0x39, 0xcd, 0xa4, 0x69, 0x13, 0x48, 0xc7, 0xa6, 0x15, 0x7b, 0x07, + 0x18, 0x4f, 0xbc, 0xe0, 0xef, 0x33, 0xed, 0x99, 0x8e, 0x6e, 0x16, 0xed, + 0xe6, 0x5c, 0xfd, 0x4f, 0x33, 0xeb, 0x4b, 0x79, 0x26, 0xf4, 0xf7, 0xd9, + 0x75, 0x4f, 0xd8, 0xb5, 0x38, 0x2c, 0x28, 0x10, 0xce, 0xde, 0x10, 0xfe, + 0xb5, 0xfe, 0x2f, 0x89, 0xa6, 0x26, 0x29, 0x7a, 0xa7, 0x23, 0x59, 0x20, + 0x1f, 0xa6, 0xe7, 0x5b, 0x8f, 0xb3, 0x63, 0x4f, 0x71, 0x47, 0xe2, 0x9f, + 0x1a, 0x7b, 0x66, 0xb7, 0x49, 0x0b, 0x98, 0x10, 0xe8, 0xe5, 0xe0, 0xdd, + 0xd8, 0x53, 0xdc, 0x3b, 0xfa, 0xcb, 0x63, 0x8f, 0x2e, 0x88, 0x9a, 0x38, + 0x48, 0xce, 0x22, 0xef, 0xc6, 0x7d, 0x61, 0xd7, 0x70, 0x3b, 0x9d, 0x80, + 0xf2, 0x3b, 0xac, 0xfb, 0xcb, 0xed, 0xe4, 0xfd, 0x50, 0xd4, 0xfd, 0xe2, + 0x78, 0xb5, 0xef, 0x86, 0x01, 0x75, 0x43, 0x9e, 0x23, 0x41, 0x4b, 0x3c, + 0xff, 0xba, 0xfd, 0xf3, 0x67, 0xf2, 0xb2, 0x61, 0x97, 0x82, 0xf4, 0xf5, + 0x61, 0x9d, 0x65, 0x5e, 0x36, 0x58, 0xa9, 0x0d, 0x59, 0xe0, 0x5f, 0x45, + 0x7c, 0x2e, 0xe6, 0x7a, 0x7f, 0x2c, 0xe6, 0x0a, 0xb1, 0xba, 0xd8, 0x05, + 0xc5, 0xc6, 0x96, 0x89, 0xc7, 0xeb, 0x6a, 0x23, 0x6e, 0x23, 0xbe, 0x93, + 0xa8, 0x46, 0x3d, 0xd4, 0xa8, 0x14, 0xf3, 0x21, 0x3e, 0x6c, 0x92, 0xc9, + 0xeb, 0x00, 0x58, 0x97, 0x62, 0xa2, 0xce, 0x81, 0xee, 0xa9, 0x8f, 0xac, + 0xdf, 0x55, 0xf3, 0x99, 0x2d, 0x24, 0x86, 0xca, 0x3a, 0xdb, 0xd0, 0x26, + 0x4e, 0x77, 0x1d, 0x40, 0x48, 0x5f, 0xaf, 0xb2, 0x8d, 0xe9, 0x13, 0x11, + 0x3c, 0xdb, 0x2b, 0x50, 0x30, 0xc2, 0x39, 0x05, 0x03, 0xc0, 0x1c, 0x5c, + 0x43, 0xf2, 0xf4, 0xb1, 0x9c, 0x28, 0x59, 0x0a, 0xf8, 0x87, 0xb9, 0xe9, + 0xd4, 0xc0, 0x8e, 0x18, 0x50, 0x15, 0x35, 0x22, 0x34, 0x71, 0x4c, 0x04, + 0x31, 0xf7, 0xc2, 0x77, 0x3d, 0xd2, 0x25, 0xb2, 0x41, 0x50, 0xc6, 0x44, + 0x06, 0xa1, 0x89, 0xd3, 0xb2, 0xb2, 0x9b, 0x72, 0xbb, 0xf3, 0xd7, 0x73, + 0x7b, 0xf2, 0xf0, 0x48, 0x38, 0xb1, 0x96, 0x0b, 0xf9, 0x40, 0x52, 0x61, + 0x3b, 0xf3, 0xf3, 0xf0, 0x9f, 0x46, 0xd2, 0x7e, 0xbb, 0x1a, 0x3f, 0xa9, + 0xfa, 0xaf, 0x9e, 0x25, 0x89, 0x4b, 0x8e, 0xa9, 0x78, 0xad, 0x7c, 0xdb, + 0x63, 0xea, 0x88, 0x68, 0x56, 0x4f, 0x9c, 0xc4, 0xde, 0x76, 0x7e, 0xbe, + 0x17, 0x4b, 0xa2, 0x38, 0x32, 0x24, 0x33, 0x37, 0x49, 0x26, 0x78, 0x36, + 0x34, 0x32, 0xe1, 0xaf, 0x11, 0xc6, 0x9f, 0x2d, 0xce, 0x7a, 0x5e, 0x9c, + 0x2e, 0x66, 0x55, 0x98, 0x82, 0x44, 0xc4, 0xe7, 0xaf, 0x56, 0x64, 0xee, + 0xdb, 0xe0, 0xc9, 0xa9, 0xb8, 0x69, 0xe1, 0x14, 0xdf, 0xb4, 0x70, 0x72, + 0x37, 0x2d, 0x66, 0x07, 0x28, 0x91, 0x02, 0xc5, 0x1d, 0x15, 0x79, 0x24, + 0x19, 0x4d, 0x10, 0xfc, 0x6d, 0x82, 0xb3, 0x56, 0x6f, 0x4e, 0xb0, 0xc7, + 0x60, 0x70, 0xd2, 0x45, 0xf7, 0x30, 0x3f, 0x4f, 0x73, 0xc6, 0x0f, 0x1e, + 0x7c, 0x2e, 0x65, 0x81, 0xb8, 0x06, 0x64, 0x04, 0xae, 0xcf, 0xb5, 0xfc, + 0x60, 0x7a, 0xb1, 0x05, 0x95, 0x08, 0xc1, 0xc1, 0xef, 0x26, 0x15, 0x47, + 0x12, 0x50, 0x5c, 0xb7, 0x89, 0x4d, 0x77, 0x20, 0x72, 0x10, 0x9f, 0x42, + 0x59, 0x8d, 0x83, 0x7a, 0x43, 0xce, 0x9d, 0xfa, 0x88, 0x83, 0x71, 0x31, + 0xeb, 0xbc, 0x9d, 0x7a, 0xce, 0x3e, 0x2c, 0xcd, 0x29, 0x91, 0xa3, 0x27, + 0x37, 0x73, 0x5c, 0xc0, 0x4c, 0x3a, 0x3c, 0xa8, 0x07, 0x6a, 0x4d, 0xb2, + 0xee, 0x36, 0x49, 0xa9, 0x24, 0x19, 0x64, 0x41, 0xd6, 0xcd, 0xbf, 0x91, + 0xef, 0x4d, 0x2b, 0x3a, 0x41, 0x35, 0x4d, 0xd3, 0x96, 0x74, 0xa0, 0xd3, + 0xb3, 0x05, 0xda, 0xf0, 0x83, 0xf7, 0xd9, 0xf4, 0x46, 0x27, 0x19, 0xc2, + 0x5e, 0x7d, 0x5e, 0x67, 0xc7, 0x63, 0x88, 0x12, 0x99, 0x6b, 0x2f, 0xce, + 0xc4, 0xb5, 0x97, 0xd4, 0xb2, 0x7e, 0x35, 0xb5, 0x78, 0xe3, 0xba, 0xc2, + 0x1b, 0xd9, 0x08, 0x18, 0x11, 0x7a, 0xc9, 0xc4, 0xd3, 0x83, 0x91, 0x8e, + 0x33, 0x2e, 0x9d, 0x68, 0xc2, 0xc5, 0xfc, 0x61, 0x9d, 0xe4, 0xc5, 0x3a, + 0xc9, 0xa5, 0x4e, 0x2a, 0xb0, 0xca, 0x89, 0x2b, 0x87, 0xc9, 0xe3, 0x24, + 0x10, 0x05, 0xcf, 0x31, 0x03, 0x92, 0x85, 0xdc, 0xfb, 0x9c, 0x09, 0x1a, + 0x18, 0xdd, 0x51, 0xba, 0x20, 0x1c, 0x9b, 0x7a, 0x39, 0xde, 0x95, 0xb3, + 0xdf, 0xf0, 0x50, 0x3c, 0xe3, 0x8f, 0x9d, 0xe8, 0xbe, 0xc9, 0x1b, 0xcb, + 0x9b, 0x13, 0x97, 0xa7, 0x32, 0x5c, 0xe8, 0x49, 0x45, 0xce, 0x12, 0x5c, + 0x2b, 0xab, 0x18, 0x54, 0xb0, 0x7b, 0x1c, 0x35, 0xf4, 0x0d, 0xc3, 0x00, + 0x8f, 0x37, 0xec, 0xdb, 0x48, 0x76, 0x41, 0xfb, 0x37, 0xfe, 0x14, 0x37, + 0x81, 0x5d, 0x2d, 0x1a, 0x28, 0x23, 0xf0, 0xf4, 0x6b, 0xf6, 0xd6, 0x08, + 0xfe, 0x1f, 0x54, 0x9b, 0x16, 0xf3, 0x4d, 0xd8, 0x42, 0x5c, 0x1d, 0x89, + 0x67, 0x15, 0x5d, 0x20, 0xb1, 0xb2, 0xe6, 0xec, 0xb9, 0x9e, 0xa6, 0xa3, + 0x0d, 0x03, 0x9d, 0x65, 0x9a, 0x99, 0x35, 0x2a, 0xa4, 0x45, 0xe4, 0x6c, + 0x19, 0x62, 0xb0, 0x25, 0x1e, 0x5d, 0x3f, 0x92, 0x74, 0xf1, 0x82, 0xd3, + 0x42, 0x2e, 0xce, 0x09, 0xf9, 0xaf, 0x10, 0x4d, 0x12, 0xa2, 0x79, 0x6c, + 0xc6, 0xb8, 0x68, 0xdf, 0xf5, 0xfb, 0xbb, 0x84, 0x93, 0xa4, 0x62, 0xd4, + 0xa2, 0x8b, 0x02, 0x3c, 0xb5, 0x20, 0x08, 0xfe, 0x7e, 0x5b, 0x74, 0xb7, + 0xf3, 0xac, 0xba, 0xb8, 0x11, 0xac, 0xc2, 0xe6, 0xbe, 0x3a, 0xc9, 0xa0, + 0x0c, 0xdd, 0x5d, 0xe4, 0xce, 0x04, 0xd5, 0x22, 0x6f, 0xfc, 0x5f, 0x88, + 0x30, 0xa1, 0x46, 0xcf, 0x34, 0xfd, 0x13, 0x5a, 0xf8, 0x04, 0x2d, 0x53, + 0x98, 0xef, 0xee, 0x9d, 0xec, 0x5d, 0xef, 0xcd, 0x42, 0x1e, 0xbc, 0x6b, + 0x64, 0x76, 0x3c, 0x57, 0xd5, 0x67, 0x2e, 0xea, 0x63, 0x93, 0x11, 0x0f, + 0x93, 0xab, 0xb3, 0x8f, 0x64, 0x26, 0xfc, 0x1d, 0xf0, 0x53, 0x1c, 0x1f, + 0xb4, 0x6d, 0x4a, 0xfc, 0x53, 0xc2, 0x9c, 0x0b, 0xe2, 0x50, 0xfb, 0x0f, + 0x1d, 0x48, 0xfc, 0xfd, 0x47, 0x42, 0x16, 0x7b, 0x8e, 0x0f, 0x5f, 0xa6, + 0xae, 0x74, 0x2f, 0x6c, 0x68, 0xe8, 0x98, 0xc5, 0x69, 0x50, 0x07, 0xaf, + 0x8f, 0x29, 0x78, 0xec, 0x03, 0xa2, 0x20, 0xf2, 0x44, 0x37, 0xb9, 0x8e, + 0x06, 0x39, 0x1d, 0xc0, 0xd9, 0x50, 0xc7, 0x92, 0x11, 0x19, 0x27, 0x91, + 0x0b, 0xe7, 0xd9, 0xdb, 0xac, 0xea, 0x96, 0x9c, 0x84, 0x9a, 0x82, 0x72, + 0xc7, 0xbb, 0x80, 0x39, 0x8d, 0xf9, 0xa5, 0x33, 0xb7, 0xe2, 0x36, 0xad, + 0x86, 0x07, 0x34, 0x33, 0x7b, 0xfb, 0x78, 0xb6, 0xd1, 0x48, 0x3b, 0xd1, + 0x14, 0xd3, 0x5b, 0x98, 0x8e, 0xe7, 0xa3, 0x8d, 0xbc, 0x28, 0x35, 0xcc, + 0x53, 0xa2, 0x13, 0xa2, 0xc4, 0xd5, 0x4d, 0xde, 0x18, 0x88, 0xaf, 0xbb, + 0x99, 0xd9, 0xeb, 0x6e, 0x7a, 0x8e, 0xe4, 0xcb, 0xe4, 0x2e, 0x28, 0x9e, + 0x80, 0x4b, 0x32, 0xa5, 0xe1, 0x27, 0x9e, 0x7c, 0xe6, 0xfc, 0x2c, 0x6b, + 0x2a, 0x75, 0xd3, 0x2c, 0xb8, 0xef, 0x94, 0x01, 0xf5, 0x9a, 0x5e, 0x8d, + 0xfd, 0xf6, 0xbd, 0xec, 0x98, 0xb4, 0x59, 0xa9, 0x63, 0x72, 0x33, 0xb9, + 0x66, 0x7e, 0x7e, 0xe6, 0xe5, 0x35, 0x08, 0x1d, 0x3a, 0x37, 0xbc, 0x30, + 0xe8, 0x61, 0x35, 0xe6, 0x14, 0x85, 0x16, 0x3c, 0x7a, 0x77, 0x36, 0xa3, + 0x49, 0x08, 0xae, 0x91, 0x2e, 0x10, 0xb6, 0x16, 0x71, 0x2d, 0xbd, 0x97, + 0x60, 0xe6, 0xb3, 0x98, 0x09, 0xd7, 0x21, 0xe7, 0x44, 0xe4, 0xfe, 0x8d, + 0x6e, 0x52, 0xde, 0xd9, 0x2a, 0x88, 0x20, 0x17, 0xff, 0xdd, 0xc9, 0xb5, + 0xf5, 0xb4, 0x0d, 0x43, 0xe1, 0xbf, 0x02, 0xd6, 0x54, 0x25, 0x23, 0x34, + 0x30, 0xed, 0x61, 0x4b, 0x49, 0x10, 0xa0, 0x49, 0x93, 0x36, 0xc4, 0xa4, + 0xb0, 0x27, 0x84, 0x50, 0x92, 0x06, 0x1a, 0x48, 0x63, 0xd4, 0x84, 0xb1, + 0xaa, 0xea, 0x7f, 0xdf, 0xb9, 0x38, 0xa9, 0xed, 0xa6, 0x65, 0xf4, 0xa5, + 0x4d, 0x63, 0xc7, 0xb1, 0x8f, 0x8f, 0xcf, 0xf5, 0x3b, 0xbd, 0x8a, 0xb7, + 0xc9, 0xc5, 0xd5, 0x6a, 0x19, 0x47, 0x60, 0xb0, 0x88, 0xb5, 0x56, 0xea, + 0xb1, 0xb6, 0xb3, 0x5b, 0x31, 0x71, 0xbb, 0x2c, 0x35, 0xdf, 0x4d, 0x8a, + 0x6e, 0x91, 0xff, 0x08, 0xcf, 0xe0, 0xb9, 0xd1, 0x72, 0x6d, 0xe8, 0xc2, + 0x37, 0x12, 0x4d, 0xec, 0x46, 0xb1, 0xed, 0xdd, 0xa7, 0x4b, 0x05, 0x48, + 0xa4, 0xa2, 0x02, 0x59, 0xf4, 0xff, 0x69, 0xc7, 0xaa, 0x4d, 0x3b, 0x56, + 0x7a, 0xaa, 0xb4, 0x1b, 0x6a, 0x94, 0xc2, 0xeb, 0x9f, 0x46, 0xab, 0xd4, + 0x29, 0xb4, 0x50, 0xe2, 0x54, 0x6f, 0xc0, 0xf4, 0x29, 0xdc, 0x7f, 0x4c, + 0xfe, 0x24, 0x0c, 0xc6, 0x37, 0x5a, 0x33, 0x3d, 0x81, 0x0a, 0xdd, 0xb2, + 0x3b, 0xbc, 0x32, 0x7a, 0x74, 0x09, 0xd5, 0x7a, 0x75, 0xf9, 0x3c, 0x79, + 0xee, 0x4b, 0xd4, 0x76, 0xf9, 0x59, 0xca, 0xbd, 0xc2, 0x06, 0x74, 0x9a, + 0x67, 0xc9, 0x6b, 0x42, 0xd8, 0x0a, 0x66, 0xbd, 0x60, 0x7b, 0xa9, 0x2c, + 0x65, 0x88, 0xd3, 0x6e, 0x6d, 0xd5, 0x90, 0xbc, 0x40, 0x07, 0x0c, 0x72, + 0x67, 0x65, 0xc0, 0xc2, 0x4d, 0x09, 0x37, 0x25, 0xe3, 0xff, 0x40, 0x0a, + 0xe7, 0x7a, 0xff, 0x04, 0x5d, 0x46, 0x6e, 0x02, 0x7e, 0xa9, 0x3c, 0x35, + 0x7f, 0x35, 0x52, 0xdb, 0xc2, 0xd4, 0x72, 0x5b, 0x28, 0x20, 0x9a, 0x3b, + 0x5e, 0x11, 0x62, 0x02, 0x1f, 0x37, 0x1b, 0xe3, 0xc9, 0x1a, 0x52, 0xc4, + 0xe4, 0xda, 0xd6, 0x1e, 0x2d, 0x95, 0x3d, 0x3a, 0x18, 0x98, 0x06, 0x69, + 0x09, 0x1c, 0x01, 0x4a, 0x54, 0x17, 0x92, 0x8e, 0x2d, 0x35, 0x70, 0x31, + 0xb6, 0x4d, 0xa8, 0x61, 0x2d, 0xd6, 0x5e, 0xf8, 0x36, 0xf2, 0x65, 0x27, + 0xd5, 0xb9, 0xfe, 0x98, 0x52, 0x8e, 0xbb, 0xaa, 0x5c, 0x05, 0x52, 0x46, + 0x02, 0x85, 0x1d, 0x81, 0x4e, 0x9d, 0x02, 0xce, 0x0c, 0xe3, 0x31, 0x9d, + 0xd2, 0x32, 0xac, 0x0b, 0xb6, 0x17, 0x62, 0xaa, 0xd2, 0x63, 0x3f, 0x76, + 0x23, 0xaa, 0x17, 0x04, 0x2b, 0x12, 0xd6, 0xd6, 0x4a, 0x16, 0x12, 0xc1, + 0x36, 0xa5, 0x57, 0xb2, 0x74, 0x25, 0x60, 0x9c, 0xb2, 0x47, 0x7c, 0xb8, + 0xad, 0xdc, 0xc8, 0x30, 0x14, 0xad, 0x81, 0x1e, 0xc8, 0x3c, 0x2e, 0x75, + 0xc3, 0x98, 0x79, 0x58, 0x9d, 0x39, 0x0e, 0x55, 0x14, 0x48, 0xab, 0x38, + 0xaf, 0x6b, 0x5a, 0x04, 0x2e, 0xf8, 0x12, 0x5c, 0x22, 0x47, 0x00, 0x4f, + 0xf9, 0x53, 0xb8, 0x42, 0x66, 0xc4, 0xe5, 0x42, 0xc3, 0xf5, 0x24, 0x9f, + 0xaa, 0x96, 0x06, 0x2f, 0xa1, 0x49, 0x62, 0xd3, 0x07, 0xa2, 0x7e, 0x4c, + 0xf9, 0x18, 0x98, 0x72, 0x78, 0xec, 0x1f, 0x79, 0xeb, 0xc3, 0xfe, 0xae, + 0xf3, 0x58, 0xde, 0x37, 0xd7, 0x49, 0x0a, 0x44, 0x39, 0x72, 0x7b, 0x7a, + 0x40, 0x13, 0xc5, 0x99, 0x3e, 0xa9, 0xf7, 0x7d, 0x2f, 0x1e, 0x26, 0x25, + 0x86, 0xff, 0xce, 0x32, 0x2c, 0x3f, 0xfa, 0x09, 0x5b, 0xa4, 0x9e, 0x84, + 0xc6, 0x18, 0x98, 0xf7, 0xd7, 0xac, 0xa8, 0x9a, 0x4b, 0x02, 0x03, 0x11, + 0xbb, 0xc1, 0xa6, 0xc8, 0xe9, 0x34, 0xa9, 0xc6, 0x35, 0x46, 0xb6, 0x2e, + 0xf8, 0xda, 0x59, 0xa0, 0xdb, 0x19, 0x10, 0x46, 0x53, 0x78, 0x29, 0x1c, + 0xd6, 0x1f, 0xf9, 0x3c, 0x58, 0xbc, 0x16, 0x55, 0x20, 0x2e, 0x9a, 0x59, + 0x79, 0x18, 0x0b, 0x6f, 0x9a, 0x64, 0xf0, 0x83, 0xfb, 0xc3, 0xef, 0xa5, + 0x87, 0x52, 0x2c, 0x30, 0x63, 0x19, 0x2d, 0x70, 0x0c, 0x43, 0x62, 0x57, + 0x55, 0x39, 0x0f, 0xf6, 0x8f, 0x97, 0x6f, 0xbd, 0x12, 0xe6, 0x08, 0x2f, + 0x4b, 0x25, 0x30, 0x43, 0x07, 0x18, 0xeb, 0x9d, 0xc3, 0x59, 0xd9, 0x1c, + 0x4e, 0xac, 0x79, 0xf0, 0xbd, 0xfe, 0xb9, 0xd8, 0x08, 0x34, 0x26, 0x0a, + 0xd1, 0x12, 0xb8, 0xc1, 0x11, 0x1c, 0x02, 0x12, 0x9e, 0xf1, 0x58, 0x3f, + 0x7f, 0x1e, 0xb9, 0xf4, 0xb4, 0x32, 0xa4, 0x0c, 0xa5, 0xf7, 0x2e, 0x23, + 0x6e, 0x33, 0xa8, 0x9d, 0xc4, 0x60, 0x48, 0x98, 0x6c, 0x4d, 0xbe, 0xbd, + 0x9f, 0x09, 0x67, 0x0a, 0x5c, 0x88, 0x3b, 0xa1, 0xdb, 0xe7, 0x9b, 0xdf, + 0xac, 0xdb, 0x2b, 0xe8, 0xb4, 0x5b, 0x67, 0x0c, 0x1e, 0x79, 0xe7, 0x11, + 0xab, 0xb5, 0x0a, 0xa0, 0x4e, 0x1f, 0x4b, 0xcb, 0xf4, 0xc0, 0xc6, 0xf3, + 0x52, 0xa6, 0xce, 0x4d, 0x73, 0xeb, 0x2d, 0xd0, 0x90, 0x0c, 0x2a, 0xa0, + 0x71, 0xee, 0x76, 0xe7, 0xd3, 0xb2, 0x44, 0xd4, 0x19, 0x95, 0xee, 0x12, + 0x3c, 0x53, 0xa2, 0x05, 0x0b, 0x1d, 0xf7, 0x40, 0x08, 0xd0, 0xc1, 0xb8, + 0x66, 0x05, 0x4b, 0x34, 0xca, 0x8b, 0x3a, 0xe3, 0xb7, 0xd9, 0x22, 0x7c, + 0x88, 0x60, 0x3a, 0xbb, 0xe8, 0x43, 0xa0, 0xe2, 0xa0, 0x7a, 0x86, 0x07, + 0x1a, 0x0b, 0x88, 0xfe, 0x52, 0x2a, 0xb2, 0xe3, 0x3e, 0x3d, 0x01, 0xef, + 0x02, 0xaf, 0x02, 0x8d, 0xee, 0xa6, 0x94, 0x32, 0x35, 0xb9, 0x10, 0x2b, + 0xf8, 0x38, 0x07, 0xd3, 0xcb, 0xe8, 0xc0, 0x97, 0x78, 0x5c, 0x70, 0x76, + 0x85, 0x16, 0xd4, 0xab, 0xce, 0xe5, 0x78, 0x4e, 0x11, 0x88, 0x36, 0x4e, + 0xbb, 0x58, 0x8e, 0xe0, 0x24, 0x8c, 0xe5, 0x2b, 0x45, 0xb7, 0xb0, 0xd7, + 0x70, 0x02, 0x5a, 0x06, 0x24, 0x2e, 0xf0, 0x48, 0x96, 0x3b, 0xfe, 0xcd, + 0xe9, 0xe0, 0xf6, 0x00, 0x8c, 0x8c, 0x10, 0xbe, 0xdc, 0x10, 0x2e, 0x06, + 0xb7, 0x1f, 0x5d, 0xff, 0xa1, 0x58, 0xdb, 0x5f, 0x09, 0x14, 0x07, 0x55, + 0x4d, 0x81, 0x3c, 0xcd, 0x71, 0xd8, 0xd7, 0x1c, 0x07, 0x97, 0x43, 0x2d, + 0x86, 0xb9, 0xd3, 0xaa, 0x15, 0x4f, 0x12, 0xee, 0x16, 0xbe, 0xf0, 0x8f, + 0x80, 0xe0, 0x8b, 0xc4, 0x9c, 0x3b, 0x32, 0x0a, 0x10, 0x04, 0x97, 0x5a, + 0x0b, 0xd2, 0x96, 0xa1, 0x86, 0xf8, 0x14, 0x78, 0x83, 0xac, 0xb1, 0x7c, + 0x5d, 0x4b, 0x9e, 0xf8, 0xaa, 0x7e, 0x50, 0xd5, 0x11, 0x12, 0xee, 0x11, + 0x6d, 0xcd, 0x3a, 0xf0, 0xfd, 0x6c, 0x5c, 0x3d, 0xa2, 0x03, 0x2a, 0x5f, + 0xc6, 0xf7, 0x25, 0xe8, 0x5b, 0x94, 0x29, 0x7e, 0xf2, 0x98, 0xfc, 0xf5, + 0xcb, 0x22, 0xad, 0x11, 0x9f, 0xe7, 0x1f, 0x0f, 0x3f, 0x0f, 0xbf, 0xb6, + 0x48, 0x3d, 0x04, 0xdf, 0x02, 0x2b, 0x87, 0x2f, 0xcd, 0xfd, 0xe1, 0x97, + 0xc8, 0x1e, 0x3a, 0x32, 0x17, 0xaf, 0x7b, 0x4d, 0x96, 0x13, 0x77, 0x91, + 0x54, 0xe8, 0x08, 0x92, 0x03, 0xa7, 0x86, 0xee, 0xc0, 0x82, 0xaf, 0x79, + 0xea, 0xed, 0x35, 0xb3, 0x39, 0x7b, 0x78, 0x59, 0x52, 0xee, 0x65, 0xf2, + 0x79, 0x2e, 0x54, 0xbe, 0x86, 0x5e, 0xb4, 0x39, 0x25, 0xc1, 0xd6, 0x99, + 0x3b, 0xe2, 0x0b, 0x02, 0x9b, 0x08, 0x1d, 0x6b, 0x28, 0x3c, 0xd5, 0x92, + 0xd4, 0xf3, 0x2a, 0xc3, 0x82, 0x8b, 0x6e, 0x28, 0xa4, 0xae, 0x99, 0xdf, + 0xa2, 0x9e, 0x3a, 0x05, 0x31, 0xe5, 0xb2, 0xd7, 0x86, 0x8c, 0x34, 0x76, + 0x8a, 0xc8, 0x1d, 0x06, 0xef, 0x9f, 0x77, 0x28, 0x62, 0xb7, 0xb6, 0xbd, + 0x89, 0xbb, 0x63, 0xdd, 0xe2, 0x6d, 0xb7, 0x6e, 0x2a, 0xd3, 0x44, 0xb9, + 0xd5, 0xfa, 0xbf, 0xcd, 0x58, 0x1d, 0x95, 0xb7, 0x13, 0x75, 0xf5, 0x64, + 0x5a, 0x19, 0x67, 0x5b, 0xd4, 0x75, 0x92, 0xce, 0xb8, 0x3f, 0x17, 0x95, + 0xe9, 0x65, 0xb0, 0x91, 0x92, 0x44, 0x84, 0xad, 0xa4, 0x71, 0xf9, 0xb3, + 0x20, 0x88, 0x0e, 0x8e, 0x67, 0x82, 0x76, 0xfa, 0x27, 0xc4, 0xbd, 0xa3, + 0x7f, 0xb9, 0xa1, 0x68, 0x0e, 0x38, 0x4b, 0x00, 0x00 +}; +unsigned int index_htm_gz_len = 6261; diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh new file mode 100755 index 0000000000..969f2a768d --- /dev/null +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -0,0 +1,60 @@ +#/bin/sh + +# Processing script to optionally reduce filesystem use by miniying, gzipping and preparing index.htm for embedding in code. +# Please see readme.md for more information. + +# Requires xdd which is part of the VIM package +# Requires npm +# sudo apt install npm +# ln -s /usr/bin/nodejs /usr/bin/node +# Requires html-minifier +# sudo npm install html-minifier -g + +html-minifier \ + --case-sensitive \ + --collapse-boolean-attributes \ + --collapse-whitespace \ + --minify-css true \ + --minify-js true \ + --process-conditional-comments \ + --remove-attribute-quotes \ + --remove-comments \ + --remove-empty-attributes \ + --remove-optional-tags \ + --remove-redundant-attributes \ + --remove-script-type-attributes \ + --remove-style-link-type-attributes \ + -o index.htm \ + ../data/edit/index.htm + +if [ $? -ne 0 ] +then + echo "Error minifying index.htm" + exit -1 +fi + +if [ -e index.htm.gz ] +then + rm index.htm.gz +fi + +gzip index.htm +if [ $? -ne 0 ] +then + echo "Error gzipping minified index.htm" + exit -1 +fi + +echo '// WARNING: Auto-generated file. Please do not modify by hand.' > index_htm.h +echo '// This file is an embeddable version of the gzipped index.htm file.' >> index_htm.h +echo '// To update it, please rerun the `reduce_index.sh` script located in the `extras` subfolder' >> index_htm.h +echo '// then recompile the sketch after each change to the `index.html` file.' >> index_htm.h +xxd -i index.htm.gz >> index_htm.h +if [ $? -ne 0 ] +then + echo "Error creating include file from index.htm.gz" + exit -1 +fi + +echo Reduce complete. + diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md new file mode 100644 index 0000000000..c3ebb82a3e --- /dev/null +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -0,0 +1,138 @@ +# FSBrowser readme + +## What is this sketch about ? + +This example is a FileSystem Browser for the ESP8266 using http requests and a html/javascript frontend, +working for all of SPIFFS, LittleFS and SDFS. +This unified version is based on the previous examples named FSWebServer, FSBrowser and SDWebServer, Copyright (c) 2015 Hristo Gochkov. All rights reserved. + +## How to use it ? +1. Uncomment one of the `#define USE_xxx` directives in the sketch +2. Add the credentials of your WiFi network (search for `STASSID`) +3. Compile and upload the sketch to your ESP8266 device +4. For normal use, copy the contents of the `data` folder to the filesystem. To do so: +- for SDFS, copy that contents (not the data folder itself, just its contents) to the root of a FAT/FAT32-formated SD card connected to the SPI port of the ESP8266 +- for SPIFFS or LittleFS, please follow the instructions at https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#uploading-files-to-file-system +5. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit + +## Options +If you need to free some space on the ESP filesystem, you can delete all the sample files at the root but also replace the `index.htm` file in the `data/edit` subfolder by the `index.htm.gz` file from the `extras` folder. That compressed version is not suited for learning or debugging, but will bring the total FS usage under 7KB. +If you want to use the browser on a an existing filesystem or don't want to perform step 4 above, you have two possibilities : +- either upload the `index.htm` file to the filesystem by opening a command shell in the `data` folder and running the following cURL command: +`curl -F file=@edit/index.htm;filename=/edit/index.htm fsbrowser.local/edit` +- or embed a version of the html page in the source code itself by uncommenting the following line in the sketch and rebuilding: +`#define INCLUDE_FALLBACK_INDEX_HTM` +That embedded version is functionally equivalent and will be returned if no `/edit/index.htm` or `/edit/index.htm.gz` file can be found on the filesystem, at the expense of a higher binary size. + +If you use the gzipped or `INCLUDE_FALLBACK_INDEX_HTM` options, please remember to rerun the `reduce_index.sh` script located in the `extras` subfolder and recompile the sketch after each change to the `index.html` file. + +## Dependency +The html page uses the [Ace.js](https://ace.c9.io/) (v1.4.9 at the time of writing) text editor which is loaded from a CDN. Consequently, internet access from your web browser is required for the FSBrowser editing feature to work as-is. + +If your browser has no web access (e.g. if you are connected to the ESP8266 as an access-point), you can copy the `ace.js` file to the `edit` subfolder of the ESP filesystem, along with optional plugins etc. according to your needs. A typical set might be: +``` +ace.js +ext-keybinding_menu.js +ext-searchbox.js +mode-html.js +worker-html.js +worker-css.js +worker-javascript.js +mode-xml.js +worker-xml.js +mode-json.js +worker-json.js +``` +(see https://github.com/ajaxorg/ace-builds for a full list). + +If `ace.js` cannot be found on the ESP filesystem either, the page will default to a plain text viewer, with a warning message. + +## Notes +- See https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html for more information on FileSystems supported by the ESP8266. +- For SDFS, if your card's CS pin is not connected to the default pin (4), uncomment the `fileSystemConfig.setCSPin(chipSelectPin);` line, specifying the GPIO the CS pin is connected to +- `index.htm` is the default index returned if your URL does not end with a filename (works on subfolders as well) +- Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - see https://en.wikipedia.org/wiki/8.3_filename - SPIFFS and LittleFS also have limitations, please see https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#spiffs-file-system-limitations +- Directories are supported on SDFS and LittleFS. On SPIFFS, all files are at the root, although their names may contain the "/" character. +- The convention here is that the root of the filesystem is "/". On SPIFFS, paths not started with a slash are not supported +- For creation, the convention is that a path ending with a "/" means create a folder, while without a "/" we create a file. Having an extension or not does not matter. + +## Changelog since original FSBrowser + +### Fixes to work on LittleFS based on SDFS +- #define logic to select FS +- switched from SD to SDFS +- begin() does not support parameters > removed SS and added optional config +- LittleFS.open() second parametsr is mandatory > specified "r" where needed +- 'FILE_WRITE' was not declared in this scope > replaced by "w" + +### UI/usability improvements +- Never format filesystem, just return "FS INIT ERROR" when FS cannot be mounted +- Tree panel width is now proportional (20%) to see long names on big screens +- Added icons for files, and indented them to the same level as folders +- Changed file/folder icon set to use a lighter and more neutral one, and added specific "text" and "image" icons for formats recognized as such +- Items are now sorted (folders first, then plain files, each in alphabetic order) +- Added file size after each file name +- Added FS status information at the top right +- Made clear that an async operation is in progress by dimming screen and showing operation status +- Filled filename box in header with the name of the last clicked file +- Selecting a file for upload defaults to putting it in the same folder as the last clicked file +- Removed limitation to 8.3 lowercase filenames +- Support Filenames without extension, Dirnames with extension +- Improved recursive refresh of parts of the tree (e.g. refresh folder upon file delete, show last folder upon creating nested file) +- Added Save/Discard/Help buttons to ACE editor, discard confirmation on leave, and refresh tree and status upon save +- Removed "Upload" from context menu (which didn't work anyway) +- Added "Rename/Move" feature to context menu +- Sketch can be used on a preexisting filesystem by embedding the index.htm file in the program + +## TODO (maybe) +- ? How can we query the fatType of the SDFS (FAT16 or FAT32) to limit filenames to 8.3 on FAT16 ? +- ? Add a visible root node "/" (with no delete option) + add the FS type next to it, like LittleFS +- ? move "Mkdir" and "MkFile" to context menu, with prompt like for Rename/Move +- ? implement drag/drop for move + make "rename" only a local rename operation (no move) +- ? Optionally present SPIFFS as a hierarchical FS too +- ? Optionally mount several filesystems at the same time (SPIFFS + SDFS or LittleFS + SDFS) + +## Test suite +These tests are a checklist of operations to verify the FSBrowser behaviour. +### On SPIFFS +#### 8.3 filenames +- At root : MkFile '/1.txt' / List / Edit / Download / Delete / Upload '/1.png' / View image / Delete image +- In subdir : MkFile '/dir/2.txt' / List / Edit / Download / Delete / Upload '/dir/2.png' / View image +- Create nested file '/a/b.txt' and delete it +- Attempt creation of unsupported filenames +#### Long filenames +- At root : MkFile '/My text file 1.txt' / List / Edit / Download / Delete / Upload '/My image file 1.png' / View image / Delete image +- In subdir : MkFile '/My Directory/My text 2.txt' / List / Edit / Download / Delete / Upload '/My Directory/My image 2.png' / View image +- Create nested file '/My folder/My test file.txt' and delete it + +### On LittleFS +#### 8.3 filenames +- At root : MkFile '/1.txt' / List / Edit / Download / Delete / Upload '/1.png' / View image / Delete image / Mkdir '/dir' +- In subdir : MkFile '/dir/2.txt' / List / Edit / Download / Delete / Upload '/dir/2.png' / View image / Mkdir '/dir/sub' +- Delete root folder '/dir' +- Create nested file '/a/b.txt' and delete file 'b.txt' +#### Long filenames +- At root : MkFile '/My text file 1.txt' / List / Edit / Download / Delete / Upload '/My image file 1.png' / View image / Delete image / Mkdir '/My Directory' +- In subdir : MkFile '/My Directory/My text file 2.txt' / List / Edit / Download / Delete / Upload '/My Directory/My image file 2.png' / View image / Mkdir '/My Directory/My Subdirectory' +- Delete root folder '/My Directory' +- Create nested file '/My folder/My test file.txt' and delete file 'My test file.txt' + +### On SDFS +#### 8.3 filenames +- At root : MkFile '/1.txt' / List / Edit / Download / Delete / Upload '/1.png' / View image / Delete image / Mkdir '/dir' +- In subdir : MkFile '/dir/2.txt' / List / Edit / Download / Delete / Upload '/dir/2.png' / View image / Mkdir '/dir/sub' +- Delete root folder '/dir' +- Create nested file '/a/b.txt' and delete file 'b.txt', then delete '/a' +#### Long filenames +- At root : MkFile '/My text file 1.txt' / List / Edit / Download / Delete / Upload '/My image file 1.png' / View image / Delete image / Mkdir '/My Directory' +- In subdir : MkFile '/My Directory/My text file 2.txt' / List / Edit / Download / Delete / Upload '/My Directory/My image file 2.png' / View image / Mkdir '/My Directory/My Subdirectory' +- Delete root folder '/My Directory' +- Create nested file '/My folder/My test file.txt' and delete file 'My test file.txt' + +## Credits +- Original version of FSBrowser written by me-no-dev, contributions over time by various contributors +- Icons are from https://feathericons.com/ . The resulting PNG is passed first through https://compresspng.com/ before being converted to base64 using https://www.base64-image.de/ +- The spinner is based on https://github.com/jlong/css-spinners +- Minifiying of index.htm is done using the command line version of https://kangax.github.io/html-minifier/ +- Idea of embedding webpage in code borrowed from https://github.com/me-no-dev/ESPAsyncWebServer + diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SDWebServer.ino b/libraries/ESP8266WebServer/examples/SDWebServer/SDWebServer.ino deleted file mode 100644 index 1679b5806a..0000000000 --- a/libraries/ESP8266WebServer/examples/SDWebServer/SDWebServer.ino +++ /dev/null @@ -1,320 +0,0 @@ -/* - SDWebServer - Example WebServer with SD Card backend for esp8266 - - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the ESP8266WebServer library for Arduino environment. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Have a FAT Formatted SD Card connected to the SPI port of the ESP8266 - The web root is the SD Card root folder - 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 -#include -#include -#include -#include -#include - -#define DBG_OUTPUT_PORT Serial - -#ifndef STASSID -#define STASSID "your-ssid" -#define STAPSK "your-password" -#endif - -const char* ssid = STASSID; -const char* password = STAPSK; -const char* host = "esp8266sd"; - -ESP8266WebServer server(80); - -static bool hasSD = false; -File uploadFile; - - -void returnOK() { - server.send(200, "text/plain", ""); -} - -void returnFail(String msg) { - server.send(500, "text/plain", msg + "\r\n"); -} - -bool loadFromSdCard(String path) { - String dataType = "text/plain"; - if (path.endsWith("/")) { - path += "index.htm"; - } - - 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"; - } else if (path.endsWith(".js")) { - dataType = "application/javascript"; - } else if (path.endsWith(".png")) { - dataType = "image/png"; - } else if (path.endsWith(".gif")) { - dataType = "image/gif"; - } else if (path.endsWith(".jpg")) { - dataType = "image/jpeg"; - } else if (path.endsWith(".ico")) { - dataType = "image/x-icon"; - } else if (path.endsWith(".xml")) { - dataType = "text/xml"; - } else if (path.endsWith(".pdf")) { - dataType = "application/pdf"; - } else if (path.endsWith(".zip")) { - dataType = "application/zip"; - } - - File dataFile = SD.open(path.c_str()); - if (dataFile.isDirectory()) { - path += "/index.htm"; - dataType = "text/html"; - dataFile = SD.open(path.c_str()); - } - - if (!dataFile) { - return false; - } - - if (server.hasArg("download")) { - dataType = "application/octet-stream"; - } - - if (server.streamFile(dataFile, dataType) != dataFile.size()) { - DBG_OUTPUT_PORT.println("Sent less data than expected!"); - } - - dataFile.close(); - return true; -} - -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.currentSize); - } - DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize); - } else if (upload.status == UPLOAD_FILE_END) { - if (uploadFile) { - uploadFile.close(); - } - DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); - } -} - -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(); - while (true) { - File entry = file.openNextFile(); - if (!entry) { - break; - } - String entryPath = path + "/" + entry.name(); - if (entry.isDirectory()) { - entry.close(); - deleteRecursive(entryPath); - } else { - entry.close(); - SD.remove((char *)entryPath.c_str()); - } - yield(); - } - - SD.rmdir((char *)path.c_str()); - 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())) { - returnFail("BAD PATH"); - return; - } - deleteRecursive(path); - returnOK(); -} - -void handleCreate() { - if (server.args() == 0) { - return returnFail("BAD ARGS"); - } - String path = server.arg(0); - if (path == "/" || SD.exists((char *)path.c_str())) { - returnFail("BAD PATH"); - return; - } - - if (path.indexOf('.') > 0) { - File file = SD.open((char *)path.c_str(), FILE_WRITE); - if (file) { - file.write((const char *)0); - file.close(); - } - } else { - SD.mkdir((char *)path.c_str()); - } - returnOK(); -} - -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.clear(); - if (!dir.isDirectory()) { - dir.close(); - return returnFail("NOT DIR"); - } - dir.rewindDirectory(); - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send(200, "text/json", ""); - WiFiClient client = server.client(); - - server.sendContent("["); - for (int cnt = 0; true; ++cnt) { - File entry = dir.openNextFile(); - if (!entry) { - break; - } - - String output; - if (cnt > 0) { - output = ','; - } - - output += "{\"type\":\""; - output += (entry.isDirectory()) ? "dir" : "file"; - output += "\",\"name\":\""; - output += entry.name(); - output += "\""; - output += "}"; - server.sendContent(output); - entry.close(); - } - server.sendContent("]"); - server.sendContent(""); // Terminate the HTTP chunked transmission with a 0-length chunk - dir.close(); -} - -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) { - DBG_OUTPUT_PORT.begin(115200); - DBG_OUTPUT_PORT.setDebugOutput(true); - DBG_OUTPUT_PORT.print("\n"); - WiFi.mode(WIFI_STA); - 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) { - DBG_OUTPUT_PORT.print("Could not connect to"); - DBG_OUTPUT_PORT.println(ssid); - while (1) { - delay(500); - } - } - DBG_OUTPUT_PORT.print("Connected! IP address: "); - DBG_OUTPUT_PORT.println(WiFi.localIP()); - - if (MDNS.begin(host)) { - MDNS.addService("http", "tcp", 80); - DBG_OUTPUT_PORT.println("MDNS responder started"); - DBG_OUTPUT_PORT.print("You can now connect to http://"); - DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local"); - } - - - server.on("/list", HTTP_GET, printDirectory); - server.on("/edit", HTTP_DELETE, handleDelete); - server.on("/edit", HTTP_PUT, handleCreate); - server.on("/edit", HTTP_POST, []() { - returnOK(); - }, handleFileUpload); - server.onNotFound(handleNotFound); - - server.begin(); - DBG_OUTPUT_PORT.println("HTTP server started"); - - if (SD.begin(SS)) { - DBG_OUTPUT_PORT.println("SD Card initialized."); - hasSD = true; - } -} - -void loop(void) { - server.handleClient(); - MDNS.update(); -} diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/edit/index.htm b/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/edit/index.htm deleted file mode 100644 index f535601e7d..0000000000 --- a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/edit/index.htm +++ /dev/null @@ -1,674 +0,0 @@ - - - - SD Editor - - - - - -
-
-
- - - - diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/index.htm b/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/index.htm deleted file mode 100644 index 55fe5a66c4..0000000000 --- a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/index.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - - - ESP Index - - - - -

ESP8266 Pin Functions

- - - diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png b/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png deleted file mode 100644 index ac7fc0f9cb..0000000000 Binary files a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png and /dev/null differ