From 3d65027d21b0b24630c6f6b47695c2cec579c965 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 21 Oct 2017 22:20:41 +0200 Subject: [PATCH 001/111] Minimal file with a few ESP8266-specific keywords - github issue #3701 --- keywords.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 keywords.txt diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000000..3272304b92 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,21 @@ +####################################### +# Syntax Coloring Map +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +analogWriteFreq KEYWORD2 +analogWriteRange KEYWORD2 +baudrate KEYWORD2 +swap KEYWORD2 + +###################################### +# Constants (LITERAL1) +####################################### +INPUT_PULLDOWN_16 LITERAL1 +PWMRANGE LITERAL1 From 211929c0f2943f968644c4004f055291624f987c Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:26:27 +0100 Subject: [PATCH 002/111] Renamed "SDWebServer" to the more universal "WebFileManager" --- .../WebFileManager.ino} | 0 .../SdRoot => WebFileManager/data}/edit/index.htm | 0 .../SdRoot => WebFileManager/data}/index.htm | 0 .../SdRoot => WebFileManager/data}/pins.png | Bin 4 files changed, 0 insertions(+), 0 deletions(-) rename libraries/ESP8266WebServer/examples/{SDWebServer/SDWebServer.ino => WebFileManager/WebFileManager.ino} (100%) rename libraries/ESP8266WebServer/examples/{SDWebServer/SdRoot => WebFileManager/data}/edit/index.htm (100%) rename libraries/ESP8266WebServer/examples/{SDWebServer/SdRoot => WebFileManager/data}/index.htm (100%) rename libraries/ESP8266WebServer/examples/{SDWebServer/SdRoot => WebFileManager/data}/pins.png (100%) diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SDWebServer.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SDWebServer.ino rename to libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/edit/index.htm rename to libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/index.htm rename to libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png b/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png rename to libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png From a1ed3ba8fbf1092a64e6b82b96c2cba34dd1f3ff Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:40:05 +0100 Subject: [PATCH 003/111] SD was replaced by SDFS, and sketch now works on either SDFS, SPIFFS or LittleFS based on a #define logic (required adding a second param to open() and replacing 'FILE_WRITE' by "w") + Added size information to file list and a /status request handler to return filesystem status --- .../WebFileManager/WebFileManager.ino | 145 ++++++++++++++---- 1 file changed, 113 insertions(+), 32 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 1679b5806a..06a74c534e 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -1,5 +1,31 @@ +/* Changes : + * + * Sketch: + * - #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" + * + * Web page: + * - Tree panel width is now proportional (20%) to see long names on big screens + * - Added an icon for files, and indented them to the same level as folders + * - Changed file/folder icon set to use lighter and more neutral ones from https://feathericons.com/ (passed the result through compresspng.com and base64-image.de) + * - 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 + * - Replaced that FS status by operation status when upload/delete is in progress + * - Filled filename box in header with the name of the currently selected file + * + * TODO: + * - Fix Editor (add Save/Discard buttons ?) + * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? + * - Test on SPIFFS + */ + /* - SDWebServer - Example WebServer with SD Card backend for esp8266 + WebFileManager - A web-based File Manager for ESP8266 filesystems + (unified from former FSBrowser, FSWebServer and SDWebServer examples) Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the ESP8266WebServer library for Arduino environment. @@ -18,21 +44,56 @@ 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 + + This example implements a file manager using http requests and a html/javascript frontend. + The filesystem itself can be SDFS, SPIFFS, LittleFS and has to be selected using one of the "#define USE_xxx" directives below. + See https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html for more information on FileSystems. + + This example requires that a copy of the 'data' folder be first copied to the filesystem. To do so: + - for SDFS, copy that 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 + + Once the data and sketch have been uploaded, access the editor by going to http://espfilemanager.local/edit + + Notes: + - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line just before "FILESYSTEM.begin()", + specifying the GPIO the CS pin is connected to + - index.htm is the default index (works on subfolders as well) + - Filesystem limitations apply. For example, SDFS is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . + SPIFFS and LittleFS also have limitations. Plese see the 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 */ + +////////////////// + +// Select the FileSystem by uncommenting one of the lines below + +#define USE_SDFS +//#define USE_SPIFFS +//#define USE_LITTLEFS + +////////////////// + #include #include #include #include #include -#include + +#if defined USE_SDFS + #define FILESYSTEM SDFS + #include +#elif defined USE_SPIFFS + #define FILESYSTEM SPIFFS + #include +#elif defined USE_LITTLEFS + #define FILESYSTEM LittleFS + #include +#else + #error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. +#endif #define DBG_OUTPUT_PORT Serial @@ -43,7 +104,7 @@ const char* ssid = STASSID; const char* password = STAPSK; -const char* host = "esp8266sd"; +const char* host = "webfilemanager"; ESP8266WebServer server(80); @@ -89,11 +150,11 @@ bool loadFromSdCard(String path) { dataType = "application/zip"; } - File dataFile = SD.open(path.c_str()); + File dataFile = FILESYSTEM.open(path.c_str(), "r"); if (dataFile.isDirectory()) { path += "/index.htm"; dataType = "text/html"; - dataFile = SD.open(path.c_str()); + dataFile = FILESYSTEM.open(path.c_str(), "r"); } if (!dataFile) { @@ -118,10 +179,10 @@ void handleFileUpload() { } 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()); + if (FILESYSTEM.exists((char *)upload.filename.c_str())) { + FILESYSTEM.remove((char *)upload.filename.c_str()); } - uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE); + uploadFile = FILESYSTEM.open(upload.filename.c_str(), "w"); DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { @@ -137,10 +198,10 @@ void handleFileUpload() { } void deleteRecursive(String path) { - File file = SD.open((char *)path.c_str()); + File file = FILESYSTEM.open((char *)path.c_str(), "r"); if (!file.isDirectory()) { file.close(); - SD.remove((char *)path.c_str()); + FILESYSTEM.remove((char *)path.c_str()); return; } @@ -156,12 +217,12 @@ void deleteRecursive(String path) { deleteRecursive(entryPath); } else { entry.close(); - SD.remove((char *)entryPath.c_str()); + FILESYSTEM.remove((char *)entryPath.c_str()); } yield(); } - SD.rmdir((char *)path.c_str()); + FILESYSTEM.rmdir((char *)path.c_str()); file.close(); } @@ -170,7 +231,7 @@ void handleDelete() { return returnFail("BAD ARGS"); } String path = server.arg(0); - if (path == "/" || !SD.exists((char *)path.c_str())) { + if (path == "/" || !FILESYSTEM.exists((char *)path.c_str())) { returnFail("BAD PATH"); return; } @@ -183,33 +244,33 @@ void handleCreate() { return returnFail("BAD ARGS"); } String path = server.arg(0); - if (path == "/" || SD.exists((char *)path.c_str())) { + if (path == "/" || FILESYSTEM.exists((char *)path.c_str())) { returnFail("BAD PATH"); return; } if (path.indexOf('.') > 0) { - File file = SD.open((char *)path.c_str(), FILE_WRITE); + File file = FILESYSTEM.open((char *)path.c_str(), "w"); if (file) { file.write((const char *)0); file.close(); } } else { - SD.mkdir((char *)path.c_str()); + FILESYSTEM.mkdir((char *)path.c_str()); } returnOK(); } -void printDirectory() { +void handleList() { if (!server.hasArg("dir")) { return returnFail("BAD ARGS"); } String path = server.arg("dir"); - if (path != "/" && !SD.exists((char *)path.c_str())) { + if (path != "/" && !FILESYSTEM.exists((char *)path.c_str())) { return returnFail("BAD PATH"); } - File dir = SD.open((char *)path.c_str()); - path.clear(); + File dir = FILESYSTEM.open((char *)path.c_str(), "r"); + path = String(); if (!dir.isDirectory()) { dir.close(); return returnFail("NOT DIR"); @@ -232,7 +293,14 @@ void printDirectory() { } output += "{\"type\":\""; - output += (entry.isDirectory()) ? "dir" : "file"; + if (entry.isDirectory()) { + output += "dir"; + } + else { + output += "file"; + output += "\",\"size\":\""; + output += entry.size(); + } output += "\",\"name\":\""; output += entry.name(); output += "\""; @@ -245,6 +313,17 @@ void printDirectory() { dir.close(); } +void handleStatus() { + FSInfo fs_info; + FILESYSTEM.info(fs_info); + + String json = String("{\"totalBytes\":\"") + fs_info.totalBytes + + "\", \"usedBytes\":\"" + fs_info.usedBytes + + "\"}"; + + server.send(200, "application/json", json); +} + void handleNotFound() { if (hasSD && loadFromSdCard(server.uri())) { return; @@ -297,10 +376,11 @@ void setup(void) { } - server.on("/list", HTTP_GET, printDirectory); - server.on("/edit", HTTP_DELETE, handleDelete); - server.on("/edit", HTTP_PUT, handleCreate); - server.on("/edit", HTTP_POST, []() { + server.on("/status",HTTP_GET, handleStatus); + server.on("/list", HTTP_GET, handleList); + server.on("/edit", HTTP_DELETE, handleDelete); + server.on("/edit", HTTP_PUT, handleCreate); + server.on("/edit", HTTP_POST, []() { returnOK(); }, handleFileUpload); server.onNotFound(handleNotFound); @@ -308,7 +388,8 @@ void setup(void) { server.begin(); DBG_OUTPUT_PORT.println("HTTP server started"); - if (SD.begin(SS)) { + // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); + if (FILESYSTEM.begin()) { DBG_OUTPUT_PORT.println("SD Card initialized."); hasSD = true; } From 3c42a8d68993c9d5447096c791633acb476fe11d Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:58:10 +0100 Subject: [PATCH 004/111] Tree panel width is now proportional to window. Changed icons (lighter and more neutral), including one for files. Show size of files. Fill "filename" box upon clicking on a file. Sort files alphabetically. --- .../WebFileManager/data/edit/index.htm | 169 ++++++++++++------ 1 file changed, 115 insertions(+), 54 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index f535601e7d..7e1124f62d 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -1,7 +1,7 @@ - SD Editor + File manager From 82d50b4147669d50bb08616b589ab76e334a20a7 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:59:36 +0100 Subject: [PATCH 005/111] Replaced by a lighter version --- .../examples/WebFileManager/data/pins.png | Bin 177869 -> 55578 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png b/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png index ac7fc0f9cb64d99f9f33b877798c61d02086d00b..f988bf45e3eff7380cf899affb33f7bb17e23cd5 100644 GIT binary patch literal 55578 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 literal 177869 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 z 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 Nla*4EtQ9v7`9ICfW Date: Sat, 14 Mar 2020 15:36:46 +0100 Subject: [PATCH 006/111] Return the filesystem time in the status object + Massive cleanup/merge/align with some code from the FSBrowser example and misc refactorings --- .../WebFileManager/WebFileManager.ino | 521 +++++++++++------- 1 file changed, 323 insertions(+), 198 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 06a74c534e..cdf5148f19 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -18,9 +18,18 @@ * - Filled filename box in header with the name of the currently selected file * * TODO: + * - Show file names as they come from server (with "/" for SPIFFS, without for others) and adapt the queries such as "delete" by only adding the leading "/" if not already present + * - Test fileSystem status in web page and indicate if ERROR + * - Show FS type in status + * - Cleanup (look for TODO below) * - Fix Editor (add Save/Discard buttons ?) * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? - * - Test on SPIFFS + * + * TEST: + * - Thoroughly on SPIFFS + SDFS + LittleFS : + * - List / Edit / View image / Download / Create file / Upload file / Mkdir at root + * - List / Edit / View image / Download / Create file / Upload file / Mkdir in subfolder + * (after PR): reported free space */ /* @@ -54,7 +63,7 @@ - for SDFS, copy that 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 - Once the data and sketch have been uploaded, access the editor by going to http://espfilemanager.local/edit + Once the data and sketch have been uploaded, access the editor by going to http://webfilemanager.local/edit Notes: - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line just before "FILESYSTEM.begin()", @@ -66,7 +75,7 @@ */ -////////////////// +//////////////////////////////// // Select the FileSystem by uncommenting one of the lines below @@ -74,7 +83,7 @@ //#define USE_SPIFFS //#define USE_LITTLEFS -////////////////// +//////////////////////////////// #include #include @@ -85,12 +94,15 @@ #if defined USE_SDFS #define FILESYSTEM SDFS #include + const char* fsName = "SDFS"; #elif defined USE_SPIFFS #define FILESYSTEM SPIFFS #include + const char* fsName = "SPIFFS"; #elif defined USE_LITTLEFS #define FILESYSTEM LittleFS #include + const char* fsName = "LittleFS"; #else #error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. #endif @@ -108,293 +120,406 @@ const char* host = "webfilemanager"; ESP8266WebServer server(80); -static bool hasSD = false; +static bool fsOK; File uploadFile; +//////////////////////////////// +// Utils to return HTTP codes and determine content-type + void returnOK() { server.send(200, "text/plain", ""); } +void returnNotFound(String msg) { + server.send(404, "text/plain", msg); +} + void returnFail(String msg) { + DBG_OUTPUT_PORT.println(msg); server.send(500, "text/plain", msg + "\r\n"); } -bool loadFromSdCard(String path) { - String dataType = "text/plain"; - if (path.endsWith("/")) { - path += "index.htm"; +String getContentType(String filename) { + if (filename.endsWith(".htm")) { + return "text/html"; + } else if (filename.endsWith(".html")) { + return "text/html"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".jpeg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; } + return "text/plain"; +} - 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"; - } +bool isDir(File file) { +#ifdef USE_SPIFFS + return false; +#else + return file.isDirectory(); +#endif +} - File dataFile = FILESYSTEM.open(path.c_str(), "r"); - if (dataFile.isDirectory()) { - path += "/index.htm"; - dataType = "text/html"; - dataFile = FILESYSTEM.open(path.c_str(), "r"); - } - if (!dataFile) { - return false; + +//////////////////////////////// +// Request handlers + +/* + * Return the FS type, status and size info + */ +void handleStatus() { + FSInfo fs_info; + String json = String("{\"fs\":\"") + fsName + "\", \"isOk\":"; + + if (fsOK) { + FILESYSTEM.info(fs_info); + json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; + } + else { + json += "\"false\""; } + json += "}"; + server.send(200, "application/json", json); +} - if (server.hasArg("download")) { - dataType = "application/octet-stream"; + +/* + * Return the list of files in the directory specified by the "dir" query string parameter + */ +void handleFileList() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); } - if (server.streamFile(dataFile, dataType) != dataFile.size()) { - DBG_OUTPUT_PORT.println("Sent less data than expected!"); + if (!server.hasArg("dir")) { + return returnFail("BAD ARGS"); } - dataFile.close(); - return true; -} + String path = server.arg("dir"); + if (path != "/" && !FILESYSTEM.exists(path)) { + return returnFail("BAD PATH"); + } -void handleFileUpload() { - if (server.uri() != "/edit") { - return; + /* TODO should we still perform a test equivalent to this one + File dir = FILESYSTEM.open(path, "r"); + path = String(); + if (!dir.isDirectory()) { + dir.close(); + return returnFail("NOT DIR"); } - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - if (FILESYSTEM.exists((char *)upload.filename.c_str())) { - FILESYSTEM.remove((char *)upload.filename.c_str()); + */ + + DBG_OUTPUT_PORT.println("handleFileList: " + path); + Dir dir = FILESYSTEM.openDir(path); + path = String(); + + String output = "["; + while (dir.next()) { + File entry = dir.openFile("r"); + if (output != "[") { + output += ','; } - uploadFile = FILESYSTEM.open(upload.filename.c_str(), "w"); - 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); + output += "{\"type\":\""; + if (isDir(entry)) { + output += "dir"; } - DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize); - } else if (upload.status == UPLOAD_FILE_END) { - if (uploadFile) { - uploadFile.close(); + else { + output += "file"; + output += "\",\"size\":\""; + output += entry.size(); + } + output += "\",\"name\":\""; + + // original code only had + // output += entry.name(); + // TODO check that the following works with other FS than SPIFFS + // The convention is that files listed at the root don't start with "/" + if (entry.name()[0] == '/') { + output += &(entry.name()[1]); + } + else { + output += entry.name(); } - DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); + output += "\"}"; + entry.close(); } + + output += "]"; + server.send(200, "text/json", output); } -void deleteRecursive(String path) { - File file = FILESYSTEM.open((char *)path.c_str(), "r"); - if (!file.isDirectory()) { - file.close(); - FILESYSTEM.remove((char *)path.c_str()); - return; - } - file.rewindDirectory(); - while (true) { - File entry = file.openNextFile(); - if (!entry) { - break; +/* + * Read the given file from the filesystem and stream it back to the client + */ +bool handleFileRead(String path) { + DBG_OUTPUT_PORT.println("handleFileRead: " + path); + if (path.endsWith("/")) { + path += "index.htm"; + } + + String contentType; + if (server.hasArg("download")) { + contentType = "application/octet-stream"; + } else { + contentType = getContentType(path); + } + + String pathWithGz = path + ".gz"; + if (FILESYSTEM.exists(pathWithGz) || FILESYSTEM.exists(path)) { + if (FILESYSTEM.exists(pathWithGz)) { + path += ".gz"; } - String entryPath = path + "/" + entry.name(); - if (entry.isDirectory()) { - entry.close(); - deleteRecursive(entryPath); - } else { - entry.close(); - FILESYSTEM.remove((char *)entryPath.c_str()); + File file = FILESYSTEM.open(path, "r"); + if (server.streamFile(file, contentType) != file.size()) { + DBG_OUTPUT_PORT.println("Sent less data than expected!"); } - yield(); + file.close(); + return true; } - - FILESYSTEM.rmdir((char *)path.c_str()); - file.close(); + return false; } -void handleDelete() { - if (server.args() == 0) { - return returnFail("BAD ARGS"); - } - String path = server.arg(0); - if (path == "/" || !FILESYSTEM.exists((char *)path.c_str())) { - returnFail("BAD PATH"); - return; - } - deleteRecursive(path); - returnOK(); -} -void handleCreate() { +/* + * Handle the creation of a new file + */ +void handleFileCreate() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); + } if (server.args() == 0) { return returnFail("BAD ARGS"); } String path = server.arg(0); - if (path == "/" || FILESYSTEM.exists((char *)path.c_str())) { - returnFail("BAD PATH"); - return; + DBG_OUTPUT_PORT.println("handleFileCreate: " + path); + if (path == "/") { + return returnFail("BAD PATH"); + } + if (FILESYSTEM.exists(path)) { + return returnFail("FILE EXISTS"); } + // TODO what ? create file if it has an extension otherwise create folder ? if (path.indexOf('.') > 0) { - File file = FILESYSTEM.open((char *)path.c_str(), "w"); + File file = FILESYSTEM.open(path, "w"); if (file) { file.write((const char *)0); file.close(); } } else { - FILESYSTEM.mkdir((char *)path.c_str()); + FILESYSTEM.mkdir(path); } - returnOK(); + return returnOK(); } -void handleList() { - if (!server.hasArg("dir")) { - return returnFail("BAD ARGS"); - } - String path = server.arg("dir"); - if (path != "/" && !FILESYSTEM.exists((char *)path.c_str())) { - return returnFail("BAD PATH"); - } - File dir = FILESYSTEM.open((char *)path.c_str(), "r"); - path = String(); - 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 = ','; - } +/* + * Delete the given directory, including all nested files and directories + */ +void deleteRecursive(String path) { + Dir dir = FILESYSTEM.openDir(path); + path = String(); - output += "{\"type\":\""; - if (entry.isDirectory()) { - output += "dir"; + while (dir.next()) { + File entry = dir.openFile("r"); + String entryPath = path + "/" + entry.name(); + if (isDir(entry)) { + entry.close(); + deleteRecursive(entryPath); } else { - output += "file"; - output += "\",\"size\":\""; - output += entry.size(); + entry.close(); + FILESYSTEM.remove(entryPath); } - 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(); + + FILESYSTEM.rmdir(path); } -void handleStatus() { - FSInfo fs_info; - FILESYSTEM.info(fs_info); - String json = String("{\"totalBytes\":\"") + fs_info.totalBytes - + "\", \"usedBytes\":\"" + fs_info.usedBytes - + "\"}"; +/* + * Handle a file deletion request + */ +void handleFileDelete() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); + } + if (server.args() == 0) { + return returnFail("BAD ARGS"); + } + String path = server.arg(0); + DBG_OUTPUT_PORT.println("handleFileDelete: " + path); + if (path == "/" || !FILESYSTEM.exists(path)) { + return returnFail("BAD PATH"); + } + deleteRecursive(path); + returnOK(); +} - server.send(200, "application/json", json); +/* + * Handle a file upload request + */ +void handleFileUpload() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); + } + 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.println("handleFileUpload Name: " + filename); + uploadFile = FILESYSTEM.open(filename, "w"); + DBG_OUTPUT_PORT.print("Upload: START, filename: " + filename); + } + else if (upload.status == UPLOAD_FILE_WRITE) { + if (uploadFile) { + uploadFile.write(upload.buf, upload.currentSize); + } + DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: " + upload.currentSize); + } + else if (upload.status == UPLOAD_FILE_END) { + if (uploadFile) { + uploadFile.close(); + } + DBG_OUTPUT_PORT.print("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 details + */ void handleNotFound() { - if (hasSD && loadFromSdCard(server.uri())) { - return; + if (!fsOK) { + return returnFail("FS INIT ERROR"); } - 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"; + if (!handleFileRead(server.uri())) { + String message = "Error: File not found\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"; + } + DBG_OUTPUT_PORT.print(message); + return returnNotFound(message); } - server.send(404, "text/plain", message); - DBG_OUTPUT_PORT.print(message); } + void setup(void) { + //////////////////////////////// + // SERIAL INIT DBG_OUTPUT_PORT.begin(115200); DBG_OUTPUT_PORT.setDebugOutput(true); DBG_OUTPUT_PORT.print("\n"); + + //////////////////////////////// + // FILESYSTEM INIT + + // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); + fsOK = FILESYSTEM.begin(); + DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "Filesystem init failed!"); + + { + // Debug: dump contents of root folder on console + Dir dir = FILESYSTEM.openDir("/"); + while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + ", size: " + dir.fileSize()); + DBG_OUTPUT_PORT.printf("\n"); + } + + //////////////////////////////// + // WI-FI INIT + DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); 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 + while (WiFi.status() != WL_CONNECTED) { 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("."); + } + DBG_OUTPUT_PORT.println(""); DBG_OUTPUT_PORT.print("Connected! IP address: "); DBG_OUTPUT_PORT.println(WiFi.localIP()); + //////////////////////////////// + // MDNS INIT 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("Open http://"); DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local"); + DBG_OUTPUT_PORT.println(".local/edit to open the file manager"); } + //////////////////////////////// + // WEB SERVER INIT + // Filesystem status server.on("/status",HTTP_GET, handleStatus); - server.on("/list", HTTP_GET, handleList); - server.on("/edit", HTTP_DELETE, handleDelete); - server.on("/edit", HTTP_PUT, handleCreate); - server.on("/edit", HTTP_POST, []() { + + // List directory + server.on("/list", HTTP_GET, handleFileList); + + // Load editor + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit/index.htm")) returnNotFound("FileNotFound"); + }); + + // 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, /*[]() { returnOK(); - }, handleFileUpload); + }*/returnOK, 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"); - - // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); - if (FILESYSTEM.begin()) { - DBG_OUTPUT_PORT.println("SD Card initialized."); - hasSD = true; - } } + void loop(void) { server.handleClient(); MDNS.update(); From 6aad505fb17905cad4704bf14e9f16b72b7b0656 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 15:40:26 +0100 Subject: [PATCH 007/111] Fixed folder handling --- .../examples/WebFileManager/data/edit/index.htm | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 7e1124f62d..922d196024 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -252,11 +252,8 @@ 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; - } + if (!path.value.startsWith("/")) path.value = "/" + path.value; + path.value = path.value.substring(0, path.value.lastIndexOf("/")+1) + filename; } } @@ -265,13 +262,13 @@ xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { var statusObj = JSON.parse(xmlHttp.responseText); - var statusTxt = statusObj.fs + ": "; + var statusText = statusObj.fs + " - "; if (statusObj.isOk) { - statusTxt += "Free space: " + readableSize(statusObj.totalBytes - statusObj.usedBytes) + statusText += "Free space: " + readableSize(statusObj.totalBytes - statusObj.usedBytes) + ". Used: " + readableSize(statusObj.usedBytes) + ". Total: " + readableSize(statusObj.totalBytes); } else { - statusTxt += "INIT ERROR !"; + statusText += "INIT ERROR !"; } document.getElementById("status").innerHTML = statusText; } @@ -529,6 +526,7 @@ switch(ext){ case "png": case "jpg": + case "jpeg": case "gif": case "ico": return true; From 26b772d9bc34b9660a5f05c1d99b3a6a25ca2104 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 15:48:23 +0100 Subject: [PATCH 008/111] Replaced the FILESYSTEM #define by a filesystem variable, and introduced FSConfig to prevent FS formating. Fixed recursive deletion. Got rid of specific isDir() for SPIFFS. --- .../WebFileManager/WebFileManager.ino | 145 ++++++++++-------- 1 file changed, 84 insertions(+), 61 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index cdf5148f19..8960d370a4 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -1,13 +1,13 @@ /* Changes : * - * Sketch: + * 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" * - * Web page: + * Web page improvements: * - Tree panel width is now proportional (20%) to see long names on big screens * - Added an icon for files, and indented them to the same level as folders * - Changed file/folder icon set to use lighter and more neutral ones from https://feathericons.com/ (passed the result through compresspng.com and base64-image.de) @@ -15,20 +15,42 @@ * - Added file size after each file name * - Added FS status information at the top right * - Replaced that FS status by operation status when upload/delete is in progress - * - Filled filename box in header with the name of the currently selected file + * - Filled filename box in header with the name of the last clicked file + * - Selecting a file for upload proposes to put it in the same file as the last clicked file + * - No limit to 8.3 lowercase filenames if not needed + * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) * * TODO: - * - Show file names as they come from server (with "/" for SPIFFS, without for others) and adapt the queries such as "delete" by only adding the leading "/" if not already present - * - Test fileSystem status in web page and indicate if ERROR - * - Show FS type in status + * - test creation of edit.txt/EDIT.TXT on LittleFS and SPIFFS (should succeed). (correclty fails on SDFS) + * - Check directory behaviour on SPIFFS (now that we call file.isDirectory() + * - Support Filenames without extension, Dirnames with extension ? * - Cleanup (look for TODO below) - * - Fix Editor (add Save/Discard buttons ?) + * - Reload tree (size) and status on Editor Save + * - Add Editor Save/Discard buttons ? * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? + * - After delete in subfolder, refresh folder, not full tree (with folders closed) * - * TEST: - * - Thoroughly on SPIFFS + SDFS + LittleFS : - * - List / Edit / View image / Download / Create file / Upload file / Mkdir at root - * - List / Edit / View image / Download / Create file / Upload file / Mkdir in subfolder + * TEST: (*) = failed vXXX = OK + * - On SPIFFS: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - with Long Filenames: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * + * - On LittleFS: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - with Long Filenames: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * + * - On SDFS: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - with Long Filenames: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder * (after PR): reported free space */ @@ -66,7 +88,7 @@ Once the data and sketch have been uploaded, access the editor by going to http://webfilemanager.local/edit Notes: - - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line just before "FILESYSTEM.begin()", + - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to - index.htm is the default index (works on subfolders as well) - Filesystem limitations apply. For example, SDFS is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . @@ -79,9 +101,9 @@ // Select the FileSystem by uncommenting one of the lines below +// #define USE_SPIFFS +// #define USE_LITTLEFS #define USE_SDFS -//#define USE_SPIFFS -//#define USE_LITTLEFS //////////////////////////////// @@ -91,22 +113,26 @@ #include #include -#if defined USE_SDFS - #define FILESYSTEM SDFS - #include - const char* fsName = "SDFS"; -#elif defined USE_SPIFFS - #define FILESYSTEM SPIFFS +#if defined USE_SPIFFS #include const char* fsName = "SPIFFS"; + FS* fileSystem = &SPIFFS; + SPIFFSConfig fileSystemConfig = SPIFFSConfig(); #elif defined USE_LITTLEFS - #define FILESYSTEM LittleFS #include const char* fsName = "LittleFS"; + FS* fileSystem = &LittleFS; + LitteFSConfig fileSystemConfig = LittleFSConfig(); +#elif defined USE_SDFS + #include + const char* fsName = "SDFS"; + FS* fileSystem = &SDFS; + SDFSConfig fileSystemConfig = SDFSConfig(); #else #error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. #endif + #define DBG_OUTPUT_PORT Serial #ifndef STASSID @@ -171,15 +197,6 @@ String getContentType(String filename) { return "text/plain"; } -bool isDir(File file) { -#ifdef USE_SPIFFS - return false; -#else - return file.isDirectory(); -#endif -} - - //////////////////////////////// // Request handlers @@ -189,10 +206,10 @@ bool isDir(File file) { */ void handleStatus() { FSInfo fs_info; - String json = String("{\"fs\":\"") + fsName + "\", \"isOk\":"; + String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; if (fsOK) { - FILESYSTEM.info(fs_info); + fileSystem->info(fs_info); json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; } else { @@ -216,12 +233,12 @@ void handleFileList() { } String path = server.arg("dir"); - if (path != "/" && !FILESYSTEM.exists(path)) { + if (path != "/" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } /* TODO should we still perform a test equivalent to this one - File dir = FILESYSTEM.open(path, "r"); + File dir = fileSystem->open(path, "r"); path = String(); if (!dir.isDirectory()) { dir.close(); @@ -230,7 +247,7 @@ void handleFileList() { */ DBG_OUTPUT_PORT.println("handleFileList: " + path); - Dir dir = FILESYSTEM.openDir(path); + Dir dir = fileSystem->openDir(path); path = String(); String output = "["; @@ -240,7 +257,7 @@ void handleFileList() { output += ','; } output += "{\"type\":\""; - if (isDir(entry)) { + if (entry.isDirectory()) { output += "dir"; } else { @@ -286,11 +303,11 @@ bool handleFileRead(String path) { } String pathWithGz = path + ".gz"; - if (FILESYSTEM.exists(pathWithGz) || FILESYSTEM.exists(path)) { - if (FILESYSTEM.exists(pathWithGz)) { + if (fileSystem->exists(pathWithGz) || fileSystem->exists(path)) { + if (fileSystem->exists(pathWithGz)) { path += ".gz"; } - File file = FILESYSTEM.open(path, "r"); + File file = fileSystem->open(path, "r"); if (server.streamFile(file, contentType) != file.size()) { DBG_OUTPUT_PORT.println("Sent less data than expected!"); } @@ -316,45 +333,50 @@ void handleFileCreate() { if (path == "/") { return returnFail("BAD PATH"); } - if (FILESYSTEM.exists(path)) { + if (fileSystem->exists(path)) { return returnFail("FILE EXISTS"); } // TODO what ? create file if it has an extension otherwise create folder ? if (path.indexOf('.') > 0) { - File file = FILESYSTEM.open(path, "w"); + File file = fileSystem->open(path, "w"); if (file) { file.write((const char *)0); file.close(); } } else { - FILESYSTEM.mkdir(path); + fileSystem->mkdir(path); } return returnOK(); } /* - * Delete the given directory, including all nested files and directories + * 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 */ void deleteRecursive(String path) { - Dir dir = FILESYSTEM.openDir(path); - path = String(); + 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()) { - File entry = dir.openFile("r"); - String entryPath = path + "/" + entry.name(); - if (isDir(entry)) { - entry.close(); - deleteRecursive(entryPath); - } - else { - entry.close(); - FILESYSTEM.remove(entryPath); - } + String entryPath = path + "/" + dir.fileName(); + deleteRecursive(entryPath); } - FILESYSTEM.rmdir(path); + // Then delete the folder itself + fileSystem->rmdir(path); } @@ -370,7 +392,7 @@ void handleFileDelete() { } String path = server.arg(0); DBG_OUTPUT_PORT.println("handleFileDelete: " + path); - if (path == "/" || !FILESYSTEM.exists(path)) { + if (path == "/" || !fileSystem->exists(path)) { return returnFail("BAD PATH"); } deleteRecursive(path); @@ -394,7 +416,7 @@ void handleFileUpload() { filename = "/" + filename; } DBG_OUTPUT_PORT.println("handleFileUpload Name: " + filename); - uploadFile = FILESYSTEM.open(filename, "w"); + uploadFile = fileSystem->open(filename, "w"); DBG_OUTPUT_PORT.print("Upload: START, filename: " + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { @@ -449,13 +471,14 @@ void setup(void) { //////////////////////////////// // FILESYSTEM INIT - // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); - fsOK = FILESYSTEM.begin(); + fileSystemConfig.setAutoFormat(false); + fileSystem->setConfig(fileSystemConfig); + fsOK = fileSystem->begin(); DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "Filesystem init failed!"); { // Debug: dump contents of root folder on console - Dir dir = FILESYSTEM.openDir("/"); + Dir dir = fileSystem->openDir("/"); while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + ", size: " + dir.fileSize()); DBG_OUTPUT_PORT.printf("\n"); } From c1947d465fb724b00d2ab47f58d70a4d62f238c4 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 15:54:49 +0100 Subject: [PATCH 009/111] Made 8.3 lowercase filenames formating optional (disabled by default). Refresh only part of the tree when possible. Selecting a file for upload defaults to the same folder as the last clicked file. Removed the Mkdir button on SPIFFS. --- .../WebFileManager/data/edit/index.htm | 102 +++++++++++------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 922d196024..6d47033b9d 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -163,6 +163,8 @@ } From 5217201eccdee91c506e69cb1f1958b59d722713 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 21:19:17 +0100 Subject: [PATCH 011/111] Removed limitation "files must have an extension, folders may not". Case insensivity of the extension for the editor and preview. --- .../WebFileManager/WebFileManager.ino | 50 ++++++------ .../WebFileManager/data/edit/index.htm | 79 ++++++++++++------- 2 files changed, 74 insertions(+), 55 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 8960d370a4..3caede9d14 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -7,36 +7,35 @@ * - LittleFS.open() second parametsr is mandatory > specified "r" where needed * - 'FILE_WRITE' was not declared in this scope > replaced by "w" * - * Web page improvements: - * - Tree panel width is now proportional (20%) to see long names on big screens + * UI improvements: + * - Tree panel width is now proportional (20%) to see long names on big screens * - Added an icon for files, and indented them to the same level as folders * - Changed file/folder icon set to use lighter and more neutral ones from https://feathericons.com/ (passed the result through compresspng.com and base64-image.de) * - 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 - * - Replaced that FS status by operation status when upload/delete is in progress + * - Replaced that FS status by operation status when async operations are in progress * - Filled filename box in header with the name of the last clicked file - * - Selecting a file for upload proposes to put it in the same file as the last clicked file - * - No limit to 8.3 lowercase filenames if not needed + * - Selecting a file for upload proposes to put it in the same folder as the last clicked file + * - Removed limitation to 8.3 lowercase filenames + * - Removed limitation "files must have an extension, folders may not" * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) * * TODO: - * - test creation of edit.txt/EDIT.TXT on LittleFS and SPIFFS (should succeed). (correclty fails on SDFS) - * - Check directory behaviour on SPIFFS (now that we call file.isDirectory() + * - test creation of edit.txt/EDIT.TXT on LittleFS (should succeed). (Correclty supported on SPIFFS but not on SDFS) * - Support Filenames without extension, Dirnames with extension ? * - Cleanup (look for TODO below) * - Reload tree (size) and status on Editor Save * - Add Editor Save/Discard buttons ? - * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? - * - After delete in subfolder, refresh folder, not full tree (with folders closed) + * - Can we query the fatType of the SDFS ? * * TEST: (*) = failed vXXX = OK * - On SPIFFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder * * - On LittleFS: * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root @@ -51,7 +50,6 @@ * - with Long Filenames: * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder - * (after PR): reported free space */ /* @@ -91,7 +89,7 @@ - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to - index.htm is the default index (works on subfolders as well) - - Filesystem limitations apply. For example, SDFS is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . + - Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . SPIFFS and LittleFS also have limitations. Plese see the 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 @@ -101,9 +99,9 @@ // Select the FileSystem by uncommenting one of the lines below -// #define USE_SPIFFS -// #define USE_LITTLEFS -#define USE_SDFS +#define USE_SPIFFS +//#define USE_LITTLEFS +//#define USE_SDFS //////////////////////////////// @@ -128,6 +126,7 @@ 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 @@ -267,10 +266,6 @@ void handleFileList() { } output += "\",\"name\":\""; - // original code only had - // output += entry.name(); - // TODO check that the following works with other FS than SPIFFS - // The convention is that files listed at the root don't start with "/" if (entry.name()[0] == '/') { output += &(entry.name()[1]); } @@ -337,15 +332,18 @@ void handleFileCreate() { return returnFail("FILE EXISTS"); } - // TODO what ? create file if it has an extension otherwise create folder ? - if (path.indexOf('.') > 0) { + if (path.endsWith("/")) { + // Create a folder + path.remove(path.length() - 1); + fileSystem->mkdir(path); + } + else { + // Create a file File file = fileSystem->open(path, "w"); if (file) { file.write((const char *)0); file.close(); } - } else { - fileSystem->mkdir(path); } return returnOK(); } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 064e6afa03..75aba571f8 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -203,31 +203,35 @@ refreshStatus(); } } - function createPath(p){ + function createPath(path){ xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = postCallback; + xmlHttp.onload = postCallback; var formData = new FormData(); - formData.append("path", p); + formData.append("path", path); document.body.style.cursor = "wait"; xmlHttp.open("PUT", "/edit"); xmlHttp.send(formData); } mkfile.onclick = function(e){ - var name = path.value - if(name.indexOf(".") === -1) return; - createPath(name); - editor.loadUrl(name); + var name = path.value.trim(); + if (name === "") alert("A filename must be given"); + else if (name.endsWith("/")) alert("Filenames cannot end with a '/' character"); + else { + if (!name.startsWith("/")) name = "/" + name; + createPath(name); + editor.loadUrl(name); + } }; mkdir.onclick = function(e){ - var name = path.value - if(name.length < 2) return; - if(name.indexOf(".") !== -1){ - if(name.lastIndexOf("/") === 0) return; - name = name.substring(0, name.lastIndexOf("/")); + var name = path.value.trim(); + if (name.isEmpty()) alert("A folder name must be given"); + else { + if (!name.endsWith("/")) name = name + "/"; + if (!name.startsWith("/")) name = "/" + name; + createPath(name); } - createPath(name); }; input.onchange = function(e){ // A file has been selected @@ -243,7 +247,7 @@ if(input.files.length === 0) return; setStatus("Uploading..."); xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = postCallback; + xmlHttp.onload = postCallback; var formData = new FormData(); formData.append("data", input.files[0], path.value); document.body.style.cursor = "wait"; @@ -281,9 +285,9 @@ + ". Used: " + readableSize(fsInfo.usedBytes) + ". Total: " + readableSize(fsInfo.totalBytes); } else { - statusText += "INIT ERROR !"; + statusText += "INIT ERROR !"; } - document.getElementById("status").innerHTML = statusText; + setStatus(statusText); if (fsInfo.type != "SPIFFS") { document.getElementById("mkdir").style.display = "inline"; } @@ -292,8 +296,25 @@ function refreshStatus(){ var xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = statusCallback; - document.getElementById("status").innerHTML = "(refreshing...)"; + xmlHttp.onload = function() { + if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText); + else { + fsInfo = JSON.parse(xmlHttp.responseText); + var statusText = fsInfo.type + " - "; + if (fsInfo.isOk) { + statusText += "Free space: " + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + + ". Used: " + readableSize(fsInfo.usedBytes) + + ". Total: " + readableSize(fsInfo.totalBytes); + } else { + statusText += "INIT ERROR !"; + } + setStatus(statusText); + if (fsInfo.type != "SPIFFS") { + document.getElementById("mkdir").style.display = "inline"; + } + } + }; + setStatus("(refreshing...)"); xmlHttp.open("GET", "/status", true); xmlHttp.send(null); } @@ -449,9 +470,9 @@ function createTreeLeaf(path, name, size){ var leaf = document.createElement("li"); - leaf.id = ((path == "/")?"":path)+"/"+name; // Removed toLowerCase() + leaf.id = ((path == "/")?"":path)+"/"+name; var label = document.createElement("span"); - label.innerHTML = name + " (" + readableSize(size) + ")"; // Removed toLowerCase() + label.innerHTML = name + " (" + readableSize(size) + ")"; leaf.appendChild(label); leaf.onclick = function(e){ if(isTextFile(leaf.id)){ @@ -474,12 +495,12 @@ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; - check.id = ((path == "/")?"":path)+"/"+name; // Removed toLowerCase() + check.id = ((path == "/")?"":path)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); var label = document.createElement("label"); label.for = check.id; - label.textContent = name; // Removed toLowerCase() + label.textContent = name; leaf.appendChild(label); check.onchange = function(e){ if(check.checked){ @@ -532,7 +553,7 @@ function isTextFile(path){ var ext = /(?:\.([^.]+))?$/.exec(path)[1]; if(typeof ext !== undefined){ - switch(ext){ + switch(ext.toLowerCase()){ case "txt": case "htm": case "html": @@ -552,7 +573,7 @@ function isImageFile(path){ var ext = /(?:\.([^.]+))?$/.exec(path)[1]; if(typeof ext !== undefined){ - switch(ext){ + switch(ext.toLowerCase()){ case "png": case "jpg": case "jpeg": @@ -600,7 +621,7 @@ function httpDelete(filename){ setStatus("Deleting..."); xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = deleteCallback(filename); + xmlHttp.onload = deleteCallback(filename); var formData = new FormData(); formData.append("path", filename); document.body.style.cursor = "wait"; @@ -617,7 +638,7 @@ function httpList(parent, path){ xmlHttp = new XMLHttpRequest(parent, path); - xmlHttp.onloadend = getCallback(parent, path); + xmlHttp.onload = getCallback(parent, path); document.body.style.cursor = "wait"; xmlHttp.open("GET", "/list?dir="+path, true); xmlHttp.send(null); @@ -665,7 +686,7 @@ var xmlHttp = null; var editor = ace.edit(element); - + //post function postCallback(){ document.body.style.cursor = "default"; @@ -673,7 +694,7 @@ } function httpPost(filename, data, type){ xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = postCallback; + xmlHttp.onload = postCallback; var formData = new FormData(); formData.append("data", new Blob([data], { type: type }), filename); document.body.style.cursor = "wait"; @@ -691,7 +712,7 @@ } function httpGet(theUrl){ xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = getCallback; + xmlHttp.onload = getCallback; document.body.style.cursor = "wait"; xmlHttp.open("GET", theUrl, true); xmlHttp.send(null); From df49379bf30a9e6374d394a796fd365e78b7413d Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 15 Mar 2020 11:01:48 +0100 Subject: [PATCH 012/111] Support Filenames without extension, Dirnames with extension. Added Save/Discard/Help buttons to Editor, discard confirmation on leave, and refresh tree/status upon save. Removed redundant Ctrl-Z + Ctrl-Shift-Z shortcut declarations. Small bug fixes. + some refactoring --- .../WebFileManager/WebFileManager.ino | 29 +- .../WebFileManager/data/edit/index.htm | 361 ++++++++++++------ 2 files changed, 258 insertions(+), 132 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 3caede9d14..5a7f218cb8 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -20,14 +20,15 @@ * - Removed limitation to 8.3 lowercase filenames * - Removed limitation "files must have an extension, folders may not" * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) + * - Support Filenames without extension, Dirnames with extension + * - Added Save/Discard/Help buttons to Editor, discard confirmation on leave, and refresh tree/status upon save * * TODO: - * - test creation of edit.txt/EDIT.TXT on LittleFS (should succeed). (Correclty supported on SPIFFS but not on SDFS) - * - Support Filenames without extension, Dirnames with extension ? - * - Cleanup (look for TODO below) - * - Reload tree (size) and status on Editor Save - * - Add Editor Save/Discard buttons ? - * - Can we query the fatType of the SDFS ? + * - Change lengthy FS status by a percentage graph, with numbers as tooltip + * - Bug: when deleting a file, we should refresh the parent node unless it has been deleted behind the scene, in which case recurse + * - Support Files not starting with '/' (SPIFFS) + * - Cleanup (look for TODO below and in HTML) + * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? * * TEST: (*) = failed vXXX = OK * - On SPIFFS: @@ -99,8 +100,8 @@ // Select the FileSystem by uncommenting one of the lines below -#define USE_SPIFFS -//#define USE_LITTLEFS +//#define USE_SPIFFS +#define USE_LITTLEFS //#define USE_SDFS //////////////////////////////// @@ -120,7 +121,7 @@ #include const char* fsName = "LittleFS"; FS* fileSystem = &LittleFS; - LitteFSConfig fileSystemConfig = LittleFSConfig(); + LittleFSConfig fileSystemConfig = LittleFSConfig(); #elif defined USE_SDFS #include const char* fsName = "SDFS"; @@ -232,7 +233,7 @@ void handleFileList() { } String path = server.arg("dir"); - if (path != "/" && !fileSystem->exists(path)) { + if (path != "" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } @@ -413,9 +414,9 @@ void handleFileUpload() { if (!filename.startsWith("/")) { filename = "/" + filename; } - DBG_OUTPUT_PORT.println("handleFileUpload Name: " + filename); + DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); - DBG_OUTPUT_PORT.print("Upload: START, filename: " + filename); + DBG_OUTPUT_PORT.print(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { @@ -476,8 +477,8 @@ void setup(void) { { // Debug: dump contents of root folder on console - Dir dir = fileSystem->openDir("/"); - while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + ", size: " + dir.fileSize()); + Dir dir = fileSystem->openDir(""); + while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + ")")); DBG_OUTPUT_PORT.printf("\n"); } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 75aba571f8..b192b836b9 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -124,7 +124,7 @@ } } - #uploader { + #header { position: absolute; top: 0; right: 0; @@ -154,7 +154,7 @@ background-color: #EEE; padding:5px; } - #uploader span { + #status { position: absolute; top: 4px; right: 10px; @@ -164,48 +164,131 @@ -

+
From 637cb4d2460c2c5b0ea2aad040075fabba6f2bc9 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 15 Mar 2020 22:41:21 +0100 Subject: [PATCH 013/111] Fixed tree refresh on delete in all cases by returning the remaining path as response to the delete request. Refactoring --- .../WebFileManager/WebFileManager.ino | 19 +++-- .../WebFileManager/data/edit/index.htm | 76 +++++++++---------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 5a7f218cb8..e3c2e2d363 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -25,11 +25,11 @@ * * TODO: * - Change lengthy FS status by a percentage graph, with numbers as tooltip - * - Bug: when deleting a file, we should refresh the parent node unless it has been deleted behind the scene, in which case recurse + * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k * - Support Files not starting with '/' (SPIFFS) * - Cleanup (look for TODO below and in HTML) * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? - * + * * TEST: (*) = failed vXXX = OK * - On SPIFFS: * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root @@ -267,6 +267,7 @@ void handleFileList() { } output += "\",\"name\":\""; + // TODO Is this correct if path does not start with "/" ? if (entry.name()[0] == '/') { output += &(entry.name()[1]); } @@ -370,8 +371,7 @@ void deleteRecursive(String path) { Dir dir = fileSystem->openDir(path); while (dir.next()) { - String entryPath = path + "/" + dir.fileName(); - deleteRecursive(entryPath); + deleteRecursive(path + "/" + dir.fileName()); } // Then delete the folder itself @@ -395,7 +395,15 @@ void handleFileDelete() { return returnFail("BAD PATH"); } deleteRecursive(path); - returnOK(); + + // As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, + // return the path of the closest still existing parent + while (path != "" && !fileSystem->exists(path)) { + path = path.substring(0, path.lastIndexOf("/")); + } + DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); + + server.send(200, "text/plain", path + "/"); } /* @@ -411,6 +419,7 @@ void handleFileUpload() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; + // TODO Is this correct if path does not start with "/" ? if (!filename.startsWith("/")) { filename = "/" + filename; } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index b192b836b9..6282c2496e 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -188,7 +188,7 @@ alert("ERROR: [" + request.status+"] " + request.responseText); } - function canLoadEditor() { + function canLoadContents() { return (document.getElementById("saveBtn").disabled || confirm("Changes to your document will be lost if you continue")); } @@ -209,6 +209,7 @@ path.id = "upload-path"; path.type = "text"; path.name = "path"; + // TODO Is this correct if path does not start with "/" ? path.defaultValue = "/"; header.appendChild(path); @@ -260,7 +261,7 @@ if (mustFormat83()) { filename = to83(filename); } - // TODO is this suitable ? + // TODO Is this correct if path does not start with "/" ? if (!path.value.startsWith("/")) path.value = "/" + path.value; path.value = path.value.substring(0, path.value.lastIndexOf("/")+1) + filename; } @@ -271,6 +272,7 @@ else { tree.refreshPath(path.value); refreshStatus(); + attemptLoad(name); } } @@ -319,13 +321,11 @@ mkfile.onclick = function(e){ var name = path.value.trim(); if (name === "") alert("A filename must be given"); - else if (name.endsWith("/")) alert("Filenames cannot end with a '/' character"); - else { + else if (name.endsWith("/")) alert("Filenames must not end with a '/' character"); + else { + // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); - if (canLoadEditor()) { - editor.loadUrl(name); - } } }; @@ -334,6 +334,7 @@ if (name === "") alert("A folder name must be given"); else { if (!name.endsWith("/")) name = name + "/"; + // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); } @@ -468,6 +469,7 @@ upload.onclick = function(e){ var pathEl = document.getElementById("upload-path"); if(pathEl){ + // TODO Is this correct if path does not start with "/" ? var subPath = pathEl.value; if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath; else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath; @@ -491,13 +493,13 @@ if(isTextFile(path)){ action.innerHTML = "Edit"; action.onclick = function(e){ - if (canLoadEditor()) editor.loadUrl(path); + if (canLoadContents()) editor.loadUrl(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; } else if(isImageFile(path)){ action.innerHTML = "Preview"; action.onclick = function(e){ - if (canLoadEditor()) loadPreview(path); + if (canLoadContents()) loadPreview(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; } @@ -538,23 +540,28 @@ } }; } + + function attemptLoad(path) { + if (canLoadContents()) { + if(isTextFile(path)){ + editor.loadUrl(path); + } else if(isImageFile(path)){ + loadPreview(path); + } else { + clearMainPanel(path); + } + } + } function createTreeLeaf(path, name, size){ var leaf = document.createElement("li"); + // TODO Is this correct if path does not start with "/" ? leaf.id = ((path == "/")?"":path)+"/"+name; var label = document.createElement("span"); label.innerHTML = name + " (" + readableSize(size) + ")"; leaf.appendChild(label); leaf.onclick = function(e){ - if (canLoadEditor()) { - if(isTextFile(leaf.id)){ - editor.loadUrl(leaf.id); - } else if(isImageFile(leaf.id)){ - loadPreview(leaf.id); - } else { - clearMainPanel(leaf.id); - } - } + attemptLoad(lead.id); }; leaf.oncontextmenu = function(e){ e.preventDefault(); @@ -568,6 +575,7 @@ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; + // TODO Is this correct if path does not start with "/" ? check.id = ((path == "/")?"":path)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); @@ -660,8 +668,8 @@ this.refreshPath = function(path){ if(path.lastIndexOf("/") < 1){ - // TODO is this what we want ? - path = '/'; + // TODO Is this correct if path does not start with "/" ? + path = "/"; treeRoot.removeChild(treeRoot.childNodes[0]); httpList(treeRoot, "/"); } else { @@ -675,6 +683,7 @@ else { // No parent node (SPIFFS), refresh full tree treeRoot.removeChild(treeRoot.childNodes[0]); + // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); } } @@ -685,7 +694,8 @@ document.body.style.cursor = "default"; if(xmlHttp.status != 200) showHttpError(xmlHttp); else { - refreshPath(path); + // Folder to refresh is returned + refreshPath(xmlHttp.responseText); } refreshStatus(); } @@ -705,27 +715,8 @@ function getCallback(parent, path){ return function(){ document.body.style.cursor = "default"; - if(xmlHttp.status == 200) addList(parent, path, JSON.parse(xmlHttp.responseText)); - else if(xmlHttp.status == 500) { - // With littleFS, it could be because we just deleted the last file of a folder, and the folder itself was silently removed. Let's refresh the parent -/* - var lastSlashPos = path.lastIndexOf("/"); - if (lastSlashPos == -1) { - showHttpError(xmlHttp); - } - else { - httpList(parent.parentElement, path.substring(0, lastSlashPos)); - } -*/ - path = path.substring(0, path.lastIndexOf("/")); - if (document.getElementById(path)) { - var leaf = document.getElementById(path).parentNode; - if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); - httpList(leaf, path); - } - else showHttpError(xmlHttp); - } - else showHttpError(xmlHttp); + if(xmlHttp.status != 200) showHttpError(xmlHttp); + else addList(parent, path, JSON.parse(xmlHttp.responseText)); } } @@ -737,6 +728,7 @@ xmlHttp.send(null); } + // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); return this; } From 36e28ccd98443be49fce604032519bb01f1f5ee4 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 18 Mar 2020 22:06:07 +0100 Subject: [PATCH 014/111] Changed FS status in text by a percentage graph, with numbers as tooltip. Unsupported files on SPIFFS (files at root not sarting with "/", files with double "/", files ending with "/") are now detected and reported in the page. --- .../WebFileManager/WebFileManager.ino | 134 +++++---- .../WebFileManager/data/edit/index.htm | 261 +++++++++--------- 2 files changed, 219 insertions(+), 176 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index e3c2e2d363..3af3d0dcc1 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -16,46 +16,52 @@ * - Added FS status information at the top right * - Replaced that FS status by operation status when async operations are in progress * - Filled filename box in header with the name of the last clicked file - * - Selecting a file for upload proposes to put it in the same folder as 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 - * - Removed limitation "files must have an extension, folders may not" - * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) * - Support Filenames without extension, Dirnames with extension - * - Added Save/Discard/Help buttons to Editor, discard confirmation on leave, and refresh tree/status upon save + * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) + * - Added Save/Discard/Help buttons to ACE editor, discard confirmation on leave, and refresh tree/status upon save * * TODO: - * - Change lengthy FS status by a percentage graph, with numbers as tooltip * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k - * - Support Files not starting with '/' (SPIFFS) * - Cleanup (look for TODO below and in HTML) * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? + * - Check/default editor to text viewer if ace.js is not reachable + * - Perform test suite again * - * TEST: (*) = failed vXXX = OK + * TEST: (#XXX = failed / vXXX = OK) * - On SPIFFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image + * - Create nested file and delete the file / Create nested file and delete top folder + * - Attempt creation of unsupported filenames * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image + * - Create nested file and delete the file / Create nested file and delete top folder * * - On LittleFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder * * - On SDFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder */ /* WebFileManager - A web-based File Manager for ESP8266 filesystems - (unified from former FSBrowser, FSWebServer and SDWebServer examples) + (unified from former FSWebServer, FSBrowser and SDWebServer examples) Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the ESP8266WebServer library for Arduino environment. @@ -93,6 +99,8 @@ - Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . SPIFFS and LittleFS also have limitations. Plese see the 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. */ @@ -147,6 +155,8 @@ const char* host = "webfilemanager"; ESP8266WebServer server(80); static bool fsOK; +String unsupportedFiles = String(); + File uploadFile; @@ -215,6 +225,7 @@ void handleStatus() { else { json += "\"false\""; } + json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; json += "}"; server.send(200, "application/json", json); } @@ -233,7 +244,7 @@ void handleFileList() { } String path = server.arg("dir"); - if (path != "" && !fileSystem->exists(path)) { + if (path != "/" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } @@ -246,36 +257,30 @@ void handleFileList() { } */ - DBG_OUTPUT_PORT.println("handleFileList: " + path); + DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = fileSystem->openDir(path); path = String(); String output = "["; while (dir.next()) { - File entry = dir.openFile("r"); - if (output != "[") { - output += ','; - } - output += "{\"type\":\""; - if (entry.isDirectory()) { - output += "dir"; + String error = getFileError(dir.fileName()); + if (error.length() > 0) { + DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); } else { - output += "file"; - output += "\",\"size\":\""; - output += entry.size(); - } - output += "\",\"name\":\""; - - // TODO Is this correct if path does not start with "/" ? - if (entry.name()[0] == '/') { - output += &(entry.name()[1]); - } - else { - output += entry.name(); + // Filename is supported + if (output != "[") output += ','; + output += "{\"type\":\""; + if (dir.isDirectory()) output += "dir"; + else output += String("file\",\"size\":\"") + dir.fileSize(); + + output += "\",\"name\":\""; + // Always return names without leading "/" + if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); + else output += dir.fileName(); + + output += "\"}"; } - output += "\"}"; - entry.close(); } output += "]"; @@ -287,7 +292,7 @@ void handleFileList() { * Read the given file from the filesystem and stream it back to the client */ bool handleFileRead(String path) { - DBG_OUTPUT_PORT.println("handleFileRead: " + path); + DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); if (path.endsWith("/")) { path += "index.htm"; } @@ -326,7 +331,7 @@ void handleFileCreate() { return returnFail("BAD ARGS"); } String path = server.arg(0); - DBG_OUTPUT_PORT.println("handleFileCreate: " + path); + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); if (path == "/") { return returnFail("BAD PATH"); } @@ -390,7 +395,7 @@ void handleFileDelete() { return returnFail("BAD ARGS"); } String path = server.arg(0); - DBG_OUTPUT_PORT.println("handleFileDelete: " + path); + DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); if (path == "/" || !fileSystem->exists(path)) { return returnFail("BAD PATH"); } @@ -399,7 +404,8 @@ void handleFileDelete() { // As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, // return the path of the closest still existing parent while (path != "" && !fileSystem->exists(path)) { - path = path.substring(0, path.lastIndexOf("/")); + 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); @@ -419,25 +425,25 @@ void handleFileUpload() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; - // TODO Is this correct if path does not start with "/" ? + // Make sure paths always start with "/" if (!filename.startsWith("/")) { filename = "/" + filename; } DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); - DBG_OUTPUT_PORT.print(String("Upload: START, filename: ") + filename); + DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { uploadFile.write(upload.buf, upload.currentSize); } - DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: " + upload.currentSize); + DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); } else if (upload.status == UPLOAD_FILE_END) { if (uploadFile) { uploadFile.close(); } - DBG_OUTPUT_PORT.print("Upload: END, Size: " + upload.totalSize); + DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); } } @@ -468,6 +474,18 @@ void handleNotFound() { } } +/* + * Checks filename for unsupported combinations + * Returns an empty String if supported, or detail of error(s) if unsupported + */ +String getFileError(String path) { + String error = String(); + if (!path.startsWith("/")) error += "!NO_LEADING_SLASH! "; + if (path.indexOf("//") != -1) error += "!DOUBLE_SLASH! "; + if (path.endsWith("/")) error += "!TRAILING_SLASH! "; + return error; +} + void setup(void) { //////////////////////////////// @@ -487,8 +505,18 @@ void setup(void) { { // Debug: dump contents of root folder on console Dir dir = fileSystem->openDir(""); - while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + ")")); - DBG_OUTPUT_PORT.printf("\n"); + DBG_OUTPUT_PORT.println("List of files at root of filesystem:"); + while (dir.next()) { + String error = getFileError(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.println(); + + // Keep the "unsupportedFiles" variable to show it, but clean it up + unsupportedFiles.replace("\n", "
"); + unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); } //////////////////////////////// diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 6282c2496e..4c15b28b65 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -1,4 +1,4 @@ - + File manager @@ -124,6 +124,47 @@ } } + #fsTotal { + display: inline-block; + width:100px; + height:10px; + border:1px solid #000; + overflow:hidden; + background: #E6E6E6; + } + #fsUsed { + display: inline-block; + height:100%; + border-right: 1px solid #26A0DA; + background: #26A0DA; + } + #warning { + height:100%; + background-color:orange; + color:black; + } + .tooltip { + position: absolute; + z-index: 2; /* must be above editor which is at 1 */ + right:0px; + top:20px; + visibility: hidden; + background-color: white; + color: black; + text-align: center; + border: 1px solid #000; + padding: 3px; + font-size: 10px; + } + + #fsTotal:hover .tooltip { + visibility: visible; + } + + #warning:hover .tooltip { + visibility: visible; + } + #header { position: absolute; top: 0; @@ -206,10 +247,9 @@ header.appendChild(fileSelector); var path = document.createElement("input"); - path.id = "upload-path"; + path.id = "filenameTxt"; path.type = "text"; path.name = "path"; - // TODO Is this correct if path does not start with "/" ? path.defaultValue = "/"; header.appendChild(path); @@ -261,7 +301,6 @@ if (mustFormat83()) { filename = to83(filename); } - // TODO Is this correct if path does not start with "/" ? if (!path.value.startsWith("/")) path.value = "/" + path.value; path.value = path.value.substring(0, path.value.lastIndexOf("/")+1) + filename; } @@ -272,7 +311,7 @@ else { tree.refreshPath(path.value); refreshStatus(); - attemptLoad(name); + tree.attemptLoad(path.value); } } @@ -300,7 +339,7 @@ function mustFormat83() { - return false; // TODO: if needed, test fsInfo.type + return false; // TODO: if needed, test fsInfo.type (and fatType ?) } function to83(filename) { @@ -323,7 +362,6 @@ if (name === "") alert("A filename must be given"); else if (name.endsWith("/")) alert("Filenames must not end with a '/' character"); else { - // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); } @@ -334,7 +372,6 @@ if (name === "") alert("A folder name must be given"); else { if (!name.endsWith("/")) name = name + "/"; - // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); } @@ -354,27 +391,7 @@ } - - - function statusCallback() { - if(xmlHttp.status != 200) showHttpError(xmlHttp); - else { - fsInfo = JSON.parse(xmlHttp.responseText); - var statusText = fsInfo.type + " - "; - if (fsInfo.isOk) { - statusText += "Free space: " + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) - + ". Used: " + readableSize(fsInfo.usedBytes) - + ". Total: " + readableSize(fsInfo.totalBytes); - } else { - statusText += "INIT ERROR !"; - } - setStatus(statusText); - if (fsInfo.type != "SPIFFS") { - document.getElementById("mkdir").style.display = "inline"; - } - } - } - + function refreshStatus(){ var xmlHttp = new XMLHttpRequest(); xmlHttp.onload = function() { @@ -383,10 +400,19 @@ fsInfo = JSON.parse(xmlHttp.responseText); var statusText = fsInfo.type + " - "; if (fsInfo.isOk) { - statusText += "Free space: " + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) - + ". Used: " + readableSize(fsInfo.usedBytes) - + ". Total: " + readableSize(fsInfo.totalBytes); - } else { + statusText += "" + + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + "\xa0free\xa0of\xa0" + readableSize(fsInfo.totalBytes) + + ""; + if (fsInfo.unsupportedFiles) { + statusText += " WARNING" + + "Filesystem\xa0contains\xa0unsupported\xa0filenames:
" + + fsInfo.unsupportedFiles + + "
"; + } + } + else { statusText += "INIT ERROR !"; } setStatus(statusText); @@ -415,7 +441,7 @@ } function loadPreview(path){ - document.getElementById("upload-path").value = path; + document.getElementById("filenameTxt").value = path; document.getElementById("editor").style.display = "none"; document.getElementById("editorButtons").style.display = "none"; preview.style.display = "block"; @@ -423,13 +449,48 @@ } function clearMainPanel(path) { - document.getElementById("upload-path").value = path; + document.getElementById("filenameTxt").value = path; document.getElementById("editor").style.display = "none"; document.getElementById("editorButtons").style.display = "none"; preview.style.display = "block"; preview.innerHTML = "(format not supported for viewing/editing)"; } + function isTextFile(path){ + var ext = /(?:\.([^.]+))?$/.exec(path)[1]; + if(ext !== undefined){ + switch(ext.toLowerCase()){ + case "txt": + case "htm": + case "html": + case "js": + case "json": + case "c": + case "h": + case "cpp": + case "css": + case "xml": + return true; + } + } + return false; + } + + function isImageFile(path){ + var ext = /(?:\.([^.]+))?$/.exec(path)[1]; + if(ext !== undefined){ + switch(ext.toLowerCase()){ + case "png": + case "jpg": + case "jpeg": + case "gif": + case "ico": + return true; + } + } + return false; + } + function fillFolderMenu(el, path){ var list = document.createElement("ul"); el.appendChild(list); @@ -453,7 +514,8 @@ httpList(leaf, path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; - } else { + } + else { expnd.innerHTML = "Expand"; expnd.onclick = function(e){ document.getElementById(path).checked = true; @@ -467,13 +529,10 @@ list.appendChild(upload); upload.innerHTML = "Upload"; upload.onclick = function(e){ - var pathEl = document.getElementById("upload-path"); - if(pathEl){ - // TODO Is this correct if path does not start with "/" ? - var subPath = pathEl.value; - if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath; - else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath; - } + var filenameTxt = document.getElementById("filenameTxt"); + var subPath = filenameTxt.value; + if(subPath.lastIndexOf("/") < 1) filenameTxt.value = path + subPath; + else filenameTxt.value = path.substring(subPath.lastIndexOf("/")) + subPath; if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; var delFolder = document.createElement("li"); @@ -496,7 +555,8 @@ if (canLoadContents()) editor.loadUrl(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; - } else if(isImageFile(path)){ + } + else if(isImageFile(path)){ action.innerHTML = "Preview"; action.onclick = function(e){ if (canLoadContents()) loadPreview(path); @@ -541,27 +601,22 @@ }; } - function attemptLoad(path) { + this.attemptLoad = function(path) { if (canLoadContents()) { - if(isTextFile(path)){ - editor.loadUrl(path); - } else if(isImageFile(path)){ - loadPreview(path); - } else { - clearMainPanel(path); - } + if(isTextFile(path)) editor.loadUrl(path); + else if(isImageFile(path)) loadPreview(path); + else clearMainPanel(path); } } - function createTreeLeaf(path, name, size){ + function createTreeLeaf(parentPath, name, size){ var leaf = document.createElement("li"); - // TODO Is this correct if path does not start with "/" ? - leaf.id = ((path == "/")?"":path)+"/"+name; + leaf.id = ((parentPath == "/")?"":parentPath)+"/"+name; var label = document.createElement("span"); label.innerHTML = name + " (" + readableSize(size) + ")"; leaf.appendChild(label); leaf.onclick = function(e){ - attemptLoad(lead.id); + attemptLoad(leaf.id); }; leaf.oncontextmenu = function(e){ e.preventDefault(); @@ -571,11 +626,10 @@ return leaf; } - function createTreeBranch(path, name, disabled){ + function createTreeBranch(parentPath, name, disabled){ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; - // TODO Is this correct if path does not start with "/" ? check.id = ((path == "/")?"":path)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); @@ -594,7 +648,8 @@ check.checked = true; if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); httpList(leaf, check.id); - } else { + } + else { check.checked = false; } }; @@ -606,7 +661,7 @@ return leaf; } - function addList(parent, path, items){ + function addFileNodes(parentEl, parentPath, items){ items.sort(function(a, b) { if (a.type == b.type) { return a.name.localeCompare(b.name); // a before z @@ -616,74 +671,51 @@ } }); var list = document.createElement("ul"); - parent.appendChild(list); + parentEl.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); - } + if(item.type === "file") itemEl = createTreeLeaf(parentPath, item.name, item.size); + else itemEl = createTreeBranch(parentPath, item.name); list.appendChild(itemEl); - } - + } } - function isTextFile(path){ - var ext = /(?:\.([^.]+))?$/.exec(path)[1]; - if(ext !== undefined){ - switch(ext.toLowerCase()){ - case "txt": - case "htm": - case "html": - case "js": - case "json": - case "c": - case "h": - case "cpp": - case "css": - case "xml": - return true; - } + function onListReceived(parentEl, parentPath){ + return function(){ + document.body.style.cursor = "default"; + if(xmlHttp.status != 200) showHttpError(xmlHttp); + else addFileNodes(parentEl, parentPath, JSON.parse(xmlHttp.responseText)); } - return false; } - function isImageFile(path){ - var ext = /(?:\.([^.]+))?$/.exec(path)[1]; - if(ext !== undefined){ - switch(ext.toLowerCase()){ - case "png": - case "jpg": - case "jpeg": - case "gif": - case "ico": - return true; - } - } - return false; + function httpList(parentEl, parentPath){ + xmlHttp = new XMLHttpRequest(parentEl, parentPath); + xmlHttp.onload = onListReceived(parentEl, parentPath); + document.body.style.cursor = "wait"; + xmlHttp.open("GET", "/list?dir="+parentPath, true); + xmlHttp.send(null); } this.refreshPath = function(path){ if(path.lastIndexOf("/") < 1){ - // TODO Is this correct if path does not start with "/" ? + // No "/" => reset the root path = "/"; treeRoot.removeChild(treeRoot.childNodes[0]); httpList(treeRoot, "/"); - } else { - path = path.substring(0, path.lastIndexOf("/")); - if (document.getElementById(path)) { + } + else { + var parentPath = path.substring(0, path.lastIndexOf("/")); + if (document.getElementById(parentPath)) { // We have a parent node (LittleFS or SDFS) - var leaf = document.getElementById(path).parentNode; + var leaf = document.getElementById(parentPath).parentNode; if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); - httpList(leaf, path); + httpList(leaf, parentPath); } else { // No parent node (SPIFFS), refresh full tree treeRoot.removeChild(treeRoot.childNodes[0]); - // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); } } @@ -712,23 +744,6 @@ xmlHttp.send(formData); } - function getCallback(parent, path){ - return function(){ - document.body.style.cursor = "default"; - if(xmlHttp.status != 200) showHttpError(xmlHttp); - else addList(parent, path, JSON.parse(xmlHttp.responseText)); - } - } - - function httpList(parent, path){ - xmlHttp = new XMLHttpRequest(parent, path); - xmlHttp.onload = getCallback(parent, path); - document.body.style.cursor = "wait"; - xmlHttp.open("GET", "/list?dir="+path, true); - xmlHttp.send(null); - } - - // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); return this; } @@ -838,7 +853,7 @@ editor.loadUrl = function(filename){ enableSaveDiscard(false); - document.getElementById("upload-path").value = filename; + document.getElementById("filenameTxt").value = filename; file = filename; lang = getLangFromFilename(file); type = "text/"+lang; From f76dfafb83dc2d0004be354c4e05a705dcada8f6 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 20 Mar 2020 22:56:13 +0100 Subject: [PATCH 015/111] Small fix + refactoring --- .../WebFileManager/data/edit/index.htm | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 4c15b28b65..75d3be9c1d 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -123,7 +123,7 @@ } } } - + #fsTotal { display: inline-block; width:100px; @@ -391,7 +391,7 @@ } - + function refreshStatus(){ var xmlHttp = new XMLHttpRequest(); xmlHttp.onload = function() { @@ -439,8 +439,8 @@ function loadDownload(path){ document.getElementById('download-frame').src = path+"?download=true"; } - - function loadPreview(path){ + + function loadImgPreview(path){ document.getElementById("filenameTxt").value = path; document.getElementById("editor").style.display = "none"; document.getElementById("editorButtons").style.display = "none"; @@ -455,7 +455,7 @@ preview.style.display = "block"; preview.innerHTML = "(format not supported for viewing/editing)"; } - + function isTextFile(path){ var ext = /(?:\.([^.]+))?$/.exec(path)[1]; if(ext !== undefined){ @@ -559,7 +559,7 @@ else if(isImageFile(path)){ action.innerHTML = "Preview"; action.onclick = function(e){ - if (canLoadContents()) loadPreview(path); + if (canLoadContents()) loadImgPreview(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; } @@ -604,7 +604,7 @@ this.attemptLoad = function(path) { if (canLoadContents()) { if(isTextFile(path)) editor.loadUrl(path); - else if(isImageFile(path)) loadPreview(path); + else if(isImageFile(path)) loadImgPreview(path); else clearMainPanel(path); } } @@ -630,7 +630,7 @@ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; - check.id = ((path == "/")?"":path)+"/"+name; + check.id = ((parentPath == "/")?"":parentPath)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); var label = document.createElement("label"); @@ -649,7 +649,7 @@ if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); httpList(leaf, check.id); } - else { + else { check.checked = false; } }; @@ -679,7 +679,7 @@ if(item.type === "file") itemEl = createTreeLeaf(parentPath, item.name, item.size); else itemEl = createTreeBranch(parentPath, item.name); list.appendChild(itemEl); - } + } } function onListReceived(parentEl, parentPath){ @@ -697,7 +697,7 @@ xmlHttp.open("GET", "/list?dir="+parentPath, true); xmlHttp.send(null); } - + this.refreshPath = function(path){ if(path.lastIndexOf("/") < 1){ // No "/" => reset the root @@ -894,7 +894,7 @@ function onBodyLoad(){ var vars = {}; - var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; }); + var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; }); var editor = createEditor("editor", vars.file, vars.lang, vars.theme); tree = createTree("tree", editor); createHeader("header", tree, editor); From f0b87ef89b5924a048dfb23633bcf71dcbb1b431 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 20 Mar 2020 22:57:33 +0100 Subject: [PATCH 016/111] Restrict filename support check to SPIFFS. --- .../WebFileManager/WebFileManager.ino | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 3af3d0dcc1..979721214f 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -25,8 +25,8 @@ * TODO: * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k * - Cleanup (look for TODO below and in HTML) - * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? - * - Check/default editor to text viewer if ace.js is not reachable + * - ? Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) + * - ? Check if ace.js is reachable and default to text viewer otherwise * - Perform test suite again * * TEST: (#XXX = failed / vXXX = OK) @@ -109,8 +109,8 @@ // Select the FileSystem by uncommenting one of the lines below //#define USE_SPIFFS -#define USE_LITTLEFS -//#define USE_SDFS +//#define USE_LITTLEFS +#define USE_SDFS //////////////////////////////// @@ -263,24 +263,24 @@ void handleFileList() { String output = "["; while (dir.next()) { +#ifdef USE_SPIFFS String error = getFileError(dir.fileName()); if (error.length() > 0) { DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); + continue } - else { - // Filename is supported - if (output != "[") output += ','; - output += "{\"type\":\""; - if (dir.isDirectory()) output += "dir"; - else output += String("file\",\"size\":\"") + dir.fileSize(); - - output += "\",\"name\":\""; - // Always return names without leading "/" - if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); - else output += dir.fileName(); - - output += "\"}"; - } +#endif + if (output != "[") output += ','; + output += "{\"type\":\""; + if (dir.isDirectory()) output += "dir"; + else output += String("file\",\"size\":\"") + dir.fileSize(); + + output += "\",\"name\":\""; + // Always return names without leading "/" + if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); + else output += dir.fileName(); + + output += "\"}"; } output += "]"; @@ -502,8 +502,8 @@ void setup(void) { fsOK = fileSystem->begin(); DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "Filesystem init failed!"); - { - // Debug: dump contents of root folder on console +#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("List of files at root of filesystem:"); while (dir.next()) { @@ -517,7 +517,7 @@ void setup(void) { // Keep the "unsupportedFiles" variable to show it, but clean it up unsupportedFiles.replace("\n", "
"); unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); - } +#endif //////////////////////////////// // WI-FI INIT From 53126c5ed20b93f42842399bfa928239b341f054 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 29 Mar 2020 19:22:56 +0200 Subject: [PATCH 017/111] Implemented Move/Rename. Added "loading" screen during async operations (dim with spinner and status). Fixed "discard" feature that kept prompting even after an image was loaded. Improved refresh of parts of the tree, with recursive listing. Moved the "path" id attribute to the "li" elements for folders (was already the case for files). Refactoring and cleanup. --- .../WebFileManager/WebFileManager.ino | 129 +++- .../WebFileManager/data/edit/index.htm | 591 ++++++++++++------ 2 files changed, 487 insertions(+), 233 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 979721214f..1a643cc5d7 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -14,19 +14,21 @@ * - 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 - * - Replaced that FS status by operation status when async operations are in progress + * - Make clear that an async operation is in progress by dimming screen and showing opertation 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 refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) - * - Added Save/Discard/Help buttons to ACE editor, discard confirmation on leave, and refresh tree/status upon save + * - Improved recursive refresh of parts of the tree (e.g. upon file delete, refresh subfolder, move to a closed folder) + * - Added Save/Discard/Help buttons to ACE editor, discard confirmation on leaveand refresh tree/status upon save + * - Added Rename/Move feature to context menu * * TODO: - * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k * - Cleanup (look for TODO below and in HTML) + * - ? Add a visible root node "/" (without a delete option) + add the FS type next to it, like LittleFS + move "Mkdir" and "MkFile" to context menu + implement drag/drop for move + make "rename" only a local rename operation (no move) + remove recursive opening ? * - ? Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) * - ? Check if ace.js is reachable and default to text viewer otherwise + * - ? Present SPIFFS as a hierarchical FS * - Perform test suite again * * TEST: (#XXX = failed / vXXX = OK) @@ -167,6 +169,10 @@ void returnOK() { server.send(200, "text/plain", ""); } +void returnOKWithMsg(String msg) { + server.send(200, "text/plain", msg); +} + void returnNotFound(String msg) { server.send(404, "text/plain", msg); } @@ -321,38 +327,85 @@ bool handleFileRead(String path) { /* - * Handle the creation of a new file + * 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 returnFail("FS INIT ERROR"); } - if (server.args() == 0) { + if (server.args() == 0 || server.args() > 2) { return returnFail("BAD ARGS"); } - String path = server.arg(0); - DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + + String path = server.arg("path"); + if (path == "") { // todo is this the right test in case of no arg + return returnFail("MISSING PATH ARG"); + } if (path == "/") { return returnFail("BAD PATH"); } if (fileSystem->exists(path)) { - return returnFail("FILE EXISTS"); + return returnFail("PATH FILE EXISTS"); } + + if (server.args() == 1) { + // One argument: create + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + if (path.endsWith("/")) { + // Create a folder + path.remove(path.length() - 1); + if (!fileSystem->mkdir(path)) { + return returnFail("MKDIR FAILED"); + } + } + else { + // Create a file + File file = fileSystem->open(path, "w"); + if (file) { + file.write((const char *)0); + file.close(); + } + else { + return returnFail("CREATE FAILED"); + } + } + if (path.lastIndexOf("/") > 0) path = path.substring(0, path.lastIndexOf("/")); + returnOKWithMsg(path); + } + else { + // Two arguments: rename + String src = server.arg("src"); + if (src == "") { // todo is this the right test in case of no arg + return returnFail("MISSING SRC ARG"); + } + if (src == "/") { + return returnFail("BAD SRC"); + } + if (!fileSystem->exists(src)) { + return returnFail("SRC FILE NOT FOUND"); + } + + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); - if (path.endsWith("/")) { - // Create a folder - path.remove(path.length() - 1); - fileSystem->mkdir(path); - } - else { - // Create a file - File file = fileSystem->open(path, "w"); - if (file) { - file.write((const char *)0); - file.close(); + if (path.endsWith("/")) { + path.remove(path.length() - 1); } + if (src.endsWith("/")) { + src.remove(src.length() - 1); + } + if (!fileSystem->rename(src, path)) { + return returnFail("RENAME FAILED"); + } + returnOKWithMsg(lastExistingParent(src) + "/"); } - return returnOK(); } @@ -384,8 +437,24 @@ void deleteRecursive(String path) { } +// TODO test delete and move on SPIFFS +// 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 != "" && !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 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) { @@ -400,16 +469,8 @@ void handleFileDelete() { return returnFail("BAD PATH"); } deleteRecursive(path); - - // As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, - // return the path of the closest still existing parent - while (path != "" && !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); - - server.send(200, "text/plain", path + "/"); + + returnOKWithMsg(lastExistingParent(path) + "/"); } /* @@ -451,13 +512,14 @@ void handleFileUpload() { /* * 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 details + * and if it fails, return a 404 page with debug information */ void handleNotFound() { if (!fsOK) { return returnFail("FS INIT ERROR"); } if (!handleFileRead(server.uri())) { + // Dump debug data String message = "Error: File not found\n\n"; message += "URI: "; message += server.uri(); @@ -467,8 +529,9 @@ void handleNotFound() { 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"; + message += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; } + message += String("path=") + server.arg("path") + "\n"; DBG_OUTPUT_PORT.print(message); return returnNotFound(message); } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 75d3be9c1d..149614d9ed 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -202,14 +202,79 @@ height: 28px; font: normal 14px Verdana, Arial, Sans-serif; } + + /* Spinner based on from https://github.com/jlong/css-spinners */ + #loading { + position:absolute; + // note: changing between block and none cancels the opacity anim, so we move it in and out of screen instead by changing the top value + // ? TODO: see https://www.impressivewebs.com/animate-display-block-none/ for another approach + display:block; + top: -100vh; + left: 0; + width: 100vw; + height: 100vh; + z-index: 100; + background-color: rgba(0, 0, 0, 0.5); + opacity: 0; + transition: opacity 500ms ease-in-out; + } + #loading.shown { + top: 0; + opacity: 1; + } + + #loading-msg { + display: inline-block; + position: relative; + top: 0px; + left: 50%; + transform: translateX(-50%); + color:white; + font: normal 32px Verdana, Arial, Sans-serif; + } + + @keyframes spinner-anim { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } + /* :not(:required) hides this rule from IE9 and below */ + .spinner-anim:not(:required) { + display: inline-block; + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: spinner-anim 1s infinite linear; + border: 16px solid #eee; + border-right-color: transparent; + border-radius: 32px; + box-sizing: border-box; + overflow: hidden; + text-indent: -9999px; + width: 64px; + height: 64px; + } - - - -
- - - - -
-
-
-
- - \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico b/libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico similarity index 100% rename from libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico rename to libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico From 83644bbc6062f3efb425824710eab2a4745d7f36 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 02:34:18 +0200 Subject: [PATCH 022/111] Renaming project --- .../examples/WebFileManager/{WebFileManager.ino => FSBrowser.ino} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libraries/ESP8266WebServer/examples/WebFileManager/{WebFileManager.ino => FSBrowser.ino} (100%) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino rename to libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino From 73490beafe9dca130deddc395a5a3271f66259a1 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 02:57:00 +0200 Subject: [PATCH 023/111] Renamed project --- .../{WebFileManager => FSBrowser}/FSBrowser.ino | 0 .../data/edit/index.htm | 0 .../data/edit/index.htm.gz | Bin .../{WebFileManager => FSBrowser}/data/favicon.ico | Bin .../{WebFileManager => FSBrowser}/data/index.htm | 0 .../{WebFileManager => FSBrowser}/data/pins.png | Bin .../{WebFileManager => FSBrowser}/feathericons.png | Bin .../{WebFileManager => FSBrowser}/readme.md | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/FSBrowser.ino (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/edit/index.htm (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/edit/index.htm.gz (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/favicon.ico (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/index.htm (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/pins.png (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/feathericons.png (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/readme.md (100%) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino rename to libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm rename to libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm.gz rename to libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico b/libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico rename to libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm rename to libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png b/libraries/ESP8266WebServer/examples/FSBrowser/data/pins.png similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png rename to libraries/ESP8266WebServer/examples/FSBrowser/data/pins.png diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/feathericons.png b/libraries/ESP8266WebServer/examples/FSBrowser/feathericons.png similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/feathericons.png rename to libraries/ESP8266WebServer/examples/FSBrowser/feathericons.png diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/readme.md rename to libraries/ESP8266WebServer/examples/FSBrowser/readme.md From 11635c5c73dec82fee9e01966c48d96a96b612f7 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:02:06 +0200 Subject: [PATCH 024/111] Renamed project --- .../ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 66c5882f7d..491cf9591f 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -1,7 +1,5 @@ - - /* - WebFileManager - A web-based File Manager for ESP8266 filesystems + 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. @@ -70,7 +68,7 @@ const char* ssid = STASSID; const char* password = STAPSK; -const char* host = "webfilemanager"; +const char* host = "fsbrowser"; ESP8266WebServer server(80); @@ -543,7 +541,7 @@ void setup(void) { MDNS.addService("http", "tcp", 80); DBG_OUTPUT_PORT.print("Open http://"); DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local/edit to open the file manager"); + DBG_OUTPUT_PORT.println(".local/edit to open the FileSystem Browser"); } //////////////////////////////// From 6354c9f792843fe5a0e94768acb4dd824940183c Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:07:58 +0200 Subject: [PATCH 025/111] Small changes --- .../examples/FSBrowser/readme.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 4a6216b8b1..e41a973ad2 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -2,7 +2,7 @@ ## What is this sketch about ? -This example is a FileSystem browser for the ESP8266 using http requests and a html/javascript frontend, +This example is a FileSystem Browser for the ESP8266 using http requests and a html/javascript frontend, working for both 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. @@ -10,11 +10,12 @@ This unified version is based on the previous examples named FSWebServer, FSBrow 1. Uncomment one of the "#define USE_xxx" directives in the sketch 2. Add the credentials of your WiFi network (search for "STASSID") -3. Copy the contents of the 'data' folder to the filesystem. To do so: +3. Compile and upload the sketch to your ESP8266 device +4. Copy the contents of the 'data' folder to the filesystem. To do so: - for SDFS, copy that 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 -4. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit -5. If you need to free as much space as possible, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total usage of less than 7KB +5. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit +6. If you need as much free space as possible on your filesystem, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total FS usage of less than 7KB ## Notes - See https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html for more information on FileSystems supported by the ESP8266. @@ -63,7 +64,7 @@ This unified version is based on the previous examples named FSWebServer, FSBrow - ? 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 @@ -100,7 +101,7 @@ This unified version is based on the previous examples named FSWebServer, FSBrow - Create nested file '/My folder/My test file.txt' and delete file 'My test file.txt' ## Credits -- Icons are from https://feathericons.com/ . The resulting PNG is passed first through https://compresspng.com/ before converting it using https://www.base64-image.de/ -- Spinner is based on https://github.com/jlong/css-spinners -- Minifiying of index.htm done by : https://css-minifier.com/ (css) + https://www.javascriptminifier.com/ (js) + https://www.willpeavy.com/tools/minifier/ (html) +- 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 by : https://css-minifier.com/ (css) + https://www.javascriptminifier.com/ (js) + https://www.willpeavy.com/tools/minifier/ (html) From b32f6c7baa3ee70ea879c26d090c17eb49143ed4 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:19:57 +0200 Subject: [PATCH 026/111] Add a note about the ace.js dependency --- libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index e41a973ad2..cc6a8d5e30 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -17,6 +17,9 @@ This unified version is based on the previous examples named FSWebServer, FSBrow 5. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit 6. If you need as much free space as possible on your filesystem, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total FS usage of less than 7KB +## Dependency +The html page uses the ace.js text editor which is hosted on a CDN, so Internet access from your web browser is required for the FSBrowser to work. + ## 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 the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to From 94c5ad750d16150d618eb4b82f78430fb4967d11 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:24:06 +0200 Subject: [PATCH 027/111] Minor changes --- .../ESP8266WebServer/examples/FSBrowser/readme.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index cc6a8d5e30..6da1e3f26b 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -22,14 +22,14 @@ The html page uses the ace.js text editor which is hosted on a CDN, so Internet ## 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 the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to +- For SDFS, if your card's CS pin is not connected the default pin (4), enable the "fileSystemConfig.setCSPin(chipSelectPin);" line, specifying the GPIO the CS pin is connected to - index.htm is the default index (works on subfolders as well) -- Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - 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 +- 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. -## Changeslog since original FSBrowser +## Changelog since original FSBrowser ### Fixes to work on LittleFS based on SDFS - #define logic to select FS @@ -57,8 +57,7 @@ The html page uses the ace.js text editor which is hosted on a CDN, so Internet - Added "Rename/Move" feature to context menu ## TODO (maybe) -- ? Is there a case where we need to limit files to 8.3 ? FAT16 maybe ? -- ? If so, how can we query the fatType of the SDFS (FAT16 or FAT32) +- ? 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) From 99ba01591148ec0901aa6e9591c28914c083034f Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 00:41:28 +0200 Subject: [PATCH 028/111] Define LittleFS by default. If both uncompressed and gz versions exist, use uncompressed version. Small fixes. --- libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 6da1e3f26b..18a7a2dfa6 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -39,14 +39,14 @@ The html page uses the ace.js text editor which is hosted on a CDN, so Internet - 'FILE_WRITE' was not declared in this scope > replaced by "w" ### UI/usability improvements -- Never format filesystem, just return "FS INIT ERROR" when FS could not be mounted +- 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 an icon for files, and indented them to the same level as folders -- Changed file/folder icon set to use lighter and more neutral ones, and added specific "text" and "image" icons for formats recognized as such +- 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 opertation status +- 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 From 6f48db446b41028c8072a28261f160c6391714e4 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 00:46:02 +0200 Subject: [PATCH 029/111] Define LittleFS by default. If both uncompressed and gz versions exist, use uncompressed version. Small fixes. --- .../examples/FSBrowser/FSBrowser.ino | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 491cf9591f..29cf398159 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -27,7 +27,7 @@ // Select the FileSystem by uncommenting one of the lines below //#define USE_SPIFFS -//#define USE_LITTLEFS +#define USE_LITTLEFS //#define USE_SDFS //////////////////////////////// @@ -240,12 +240,12 @@ bool handleFileRead(String path) { } else { contentType = getContentType(path); } - - String pathWithGz = path + ".gz"; - if (fileSystem->exists(pathWithGz) || fileSystem->exists(path)) { - if (fileSystem->exists(pathWithGz)) { - path += ".gz"; - } + + 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!"); @@ -253,6 +253,7 @@ bool handleFileRead(String path) { file.close(); return true; } + return false; } @@ -375,9 +376,10 @@ void deleteRecursive(String path) { } -// TODO test delete and move on SPIFFS -// 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 +/* + * 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 != "" && !fileSystem->exists(path)) { if (path.lastIndexOf("/") > 0) path = path.substring(0, path.lastIndexOf("/")); From 30eeeb6541662f55cf30b1d6b0e8897aa63e01e3 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 00:55:47 +0200 Subject: [PATCH 030/111] Restyled version --- .../examples/FSBrowser/FSBrowser.ino | 361 ++++++++++-------- 1 file changed, 204 insertions(+), 157 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 29cf398159..12f3368292 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -1,5 +1,5 @@ /* - FSBrowser - A web-based FileSystem Browser for ESP8266 filesystems + 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. @@ -39,23 +39,23 @@ #include #if defined USE_SPIFFS - #include - const char* fsName = "SPIFFS"; - FS* fileSystem = &SPIFFS; - SPIFFSConfig fileSystemConfig = SPIFFSConfig(); +#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(); +#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. +#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 @@ -99,62 +99,92 @@ void returnFail(String msg) { } String getContentType(String filename) { - if (filename.endsWith(".htm")) return "text/html"; - if (filename.endsWith(".html")) return "text/html"; - if (filename.endsWith(".css")) return "text/css"; - if (filename.endsWith(".js")) return "application/javascript"; - if (filename.endsWith(".png")) return "image/png"; - if (filename.endsWith(".gif")) return "image/gif"; - if (filename.endsWith(".jpg")) return "image/jpeg"; - if (filename.endsWith(".jpeg")) return "image/jpeg"; - if (filename.endsWith(".ico")) return "image/x-icon"; - if (filename.endsWith(".xml")) return "text/xml"; - if (filename.endsWith(".pdf")) return "application/x-pdf"; - if (filename.endsWith(".zip")) return "application/x-zip"; - if (filename.endsWith(".gz")) return "application/x-gzip"; + if (filename.endsWith(".htm")) { + return "text/html"; + } + if (filename.endsWith(".html")) { + return "text/html"; + } + if (filename.endsWith(".css")) { + return "text/css"; + } + if (filename.endsWith(".js")) { + return "application/javascript"; + } + if (filename.endsWith(".png")) { + return "image/png"; + } + if (filename.endsWith(".gif")) { + return "image/gif"; + } + if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } + if (filename.endsWith(".jpeg")) { + return "image/jpeg"; + } + if (filename.endsWith(".ico")) { + return "image/x-icon"; + } + if (filename.endsWith(".xml")) { + return "text/xml"; + } + if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } + if (filename.endsWith(".zip")) { + return "application/x-zip"; + } + if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } return "text/plain"; } unsigned char h2int(char c) { - if (c >= '0' && c <='9') return((unsigned char)c - '0'); - if (c >= 'a' && c <='f') return((unsigned char)c - 'a' + 10); - if (c >= 'A' && c <='F') return((unsigned char)c - 'A' + 10); - return(0); + if (c >= '0' && c <= '9') { + return ((unsigned char)c - '0'); + } + if (c >= 'a' && c <= 'f') { + return ((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') { + return ((unsigned char)c - 'A' + 10); + } + return (0); } -String urlDecode(String str) { - String decodedString=""; - char c; - char code0; - char code1; - for (int i =0; i < str.length(); i++) { - c=str.charAt(i); - if (c == '+') { - decodedString+=' '; - } - else if (c == '%') { - i++; - code0=str.charAt(i); - i++; - code1=str.charAt(i); - c = (h2int(code0) << 4) | h2int(code1); - decodedString+=c; - } - else { - decodedString+=c; - } +String urlDecode(String str) { + String decodedString = ""; + char c; + char code0; + char code1; + for (int i = 0; i < str.length(); i++) { + c = str.charAt(i); + if (c == '+') { + decodedString += ' '; + } else if (c == '%') { + i++; + code0 = str.charAt(i); + i++; + code1 = str.charAt(i); + c = (h2int(code0) << 4) | h2int(code1); + decodedString += c; + } else { + decodedString += c; } - - return decodedString; + } + + return decodedString; } //////////////////////////////// // Request handlers -/* - * Return the FS type, status and size info - */ +/* + Return the FS type, status and size info +*/ void handleStatus() { FSInfo fs_info; String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; @@ -162,8 +192,7 @@ void handleStatus() { if (fsOK) { fileSystem->info(fs_info); json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; - } - else { + } else { json += "\"false\""; } json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; @@ -172,9 +201,9 @@ void handleStatus() { } -/* - * Return the list of files in the directory specified by the "dir" query string parameter - */ +/* + Return the list of files in the directory specified by the "dir" query string parameter +*/ void handleFileList() { if (!fsOK) { return returnFail("FS INIT ERROR"); @@ -185,10 +214,10 @@ void handleFileList() { } String path = urlDecode(server.arg("dir")); - if (path != "/" && !fileSystem->exists(path)) { + if (path != "/" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } - + DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = fileSystem->openDir(path); path = String(); @@ -202,15 +231,23 @@ void handleFileList() { continue; } #endif - if (output != "[") output += ','; + if (output != "[") { + output += ','; + } output += "{\"type\":\""; - if (dir.isDirectory()) output += "dir"; - else output += String("file\",\"size\":\"") + dir.fileSize(); + if (dir.isDirectory()) { + output += "dir"; + } else { + output += String("file\",\"size\":\"") + dir.fileSize(); + } output += "\",\"name\":\""; // Always return names without leading "/" - if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); - else output += dir.fileName(); + if (dir.fileName()[0] == '/') { + output += &(dir.fileName()[1]); + } else { + output += dir.fileName(); + } output += "\"}"; } @@ -221,8 +258,8 @@ void handleFileList() { /* - * Read the given file from the filesystem and stream it back to the client - */ + 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) { @@ -233,7 +270,7 @@ bool handleFileRead(String path) { if (path.endsWith("/")) { path += "index.htm"; } - + String contentType; if (server.hasArg("download")) { contentType = "application/octet-stream"; @@ -244,8 +281,8 @@ bool handleFileRead(String path) { if (!fileSystem->exists(path)) { // File not found, try gzip version path = path + ".gz"; - } - if (fileSystem->exists(path)) { + } + 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!"); @@ -259,16 +296,16 @@ bool handleFileRead(String 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 - */ + 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 returnFail("FS INIT ERROR"); @@ -276,7 +313,7 @@ void handleFileCreate() { if (server.args() == 0 || server.args() > 2) { return returnFail("BAD ARGS"); } - + String path = server.arg("path"); if (path == "") { return returnFail("MISSING PATH ARG"); @@ -294,9 +331,9 @@ void handleFileCreate() { if (fileSystem->exists(path)) { return returnFail("PATH FILE EXISTS"); } - + if (server.args() == 1) { - // One argument: create + // One argument: create DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); if (path.endsWith("/")) { // Create a folder @@ -304,22 +341,21 @@ void handleFileCreate() { if (!fileSystem->mkdir(path)) { return returnFail("MKDIR FAILED"); } - } - else { + } else { // Create a file File file = fileSystem->open(path, "w"); if (file) { file.write((const char *)0); file.close(); - } - else { + } else { return returnFail("CREATE FAILED"); } } - if (path.lastIndexOf("/") > -1) path = path.substring(0, path.lastIndexOf("/")); + if (path.lastIndexOf("/") > -1) { + path = path.substring(0, path.lastIndexOf("/")); + } returnOKWithMsg(path); - } - else { + } else { // Two arguments: rename String src = server.arg("src"); if (src == "") { @@ -331,11 +367,11 @@ void handleFileCreate() { if (!fileSystem->exists(src)) { return returnFail("SRC FILE NOT FOUND"); } - + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); if (path.endsWith("/")) { - path.remove(path.length() - 1); + path.remove(path.length() - 1); } if (src.endsWith("/")) { src.remove(src.length() - 1); @@ -349,21 +385,21 @@ void handleFileCreate() { /* - * 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 - */ + 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 +*/ 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); @@ -376,26 +412,29 @@ void deleteRecursive(String path) { } -/* - * 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 - */ +/* + 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 != "" && !fileSystem->exists(path)) { - if (path.lastIndexOf("/") > 0) path = path.substring(0, path.lastIndexOf("/")); - else path = String(); // No slash => the top folder does not exist + 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 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 - */ + 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 returnFail("FS INIT ERROR"); @@ -414,8 +453,8 @@ void handleFileDelete() { } /* - * Handle a file upload request - */ + Handle a file upload request +*/ void handleFileUpload() { if (!fsOK) { return returnFail("FS INIT ERROR"); @@ -433,14 +472,12 @@ void handleFileUpload() { DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); - } - else if (upload.status == UPLOAD_FILE_WRITE) { + } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { uploadFile.write(upload.buf, upload.currentSize); } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); - } - else if (upload.status == UPLOAD_FILE_END) { + } else if (upload.status == UPLOAD_FILE_END) { if (uploadFile) { uploadFile.close(); } @@ -449,11 +486,11 @@ void handleFileUpload() { } -/* - * 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 - */ +/* + 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 returnFail("FS INIT ERROR"); @@ -479,14 +516,20 @@ void handleNotFound() { } /* - * Checks filename for unsupported combinations - * Returns an empty String if supported, or detail of error(s) if unsupported - */ + Checks filename for unsupported combinations + Returns an empty String if supported, or detail of error(s) if unsupported +*/ String getFileError(String path) { String error = String(); - if (!path.startsWith("/")) error += "!NO_LEADING_SLASH! "; - if (path.indexOf("//") != -1) error += "!DOUBLE_SLASH! "; - if (path.endsWith("/")) error += "!TRAILING_SLASH! "; + if (!path.startsWith("/")) { + error += "!NO_LEADING_SLASH! "; + } + if (path.indexOf("//") != -1) { + error += "!DOUBLE_SLASH! "; + } + if (path.endsWith("/")) { + error += "!TRAILING_SLASH! "; + } return error; } @@ -507,20 +550,22 @@ void setup(void) { DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "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("List of files at root of filesystem:"); - while (dir.next()) { - String error = getFileError(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.println(); - - // Keep the "unsupportedFiles" variable to show it, but clean it up - unsupportedFiles.replace("\n", "
"); - unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); + // Debug: dump on console contents of filessytem with no filter and check filenames validity + Dir dir = fileSystem->openDir(""); + DBG_OUTPUT_PORT.println("List of files at root of filesystem:"); + while (dir.next()) { + String error = getFileError(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.println(); + + // Keep the "unsupportedFiles" variable to show it, but clean it up + unsupportedFiles.replace("\n", "
"); + unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); #endif //////////////////////////////// @@ -532,7 +577,7 @@ void setup(void) { 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.println(WiFi.localIP()); @@ -550,22 +595,24 @@ void setup(void) { // WEB SERVER INIT // Filesystem status - server.on("/status",HTTP_GET, handleStatus); - + server.on("/status", HTTP_GET, handleStatus); + // List directory - server.on("/list", HTTP_GET, handleFileList); - + server.on("/list", HTTP_GET, handleFileList); + // Load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit/index.htm")) returnNotFound("FileNotFound"); + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit/index.htm")) { + returnNotFound("FileNotFound"); + } }); - + // 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 From 3889b961469ef1ec3e64d31883b1288692a73767 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 14:33:53 +0200 Subject: [PATCH 031/111] (dummy edit to retrigger broken CI) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 12f3368292..b63d8dde2d 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -19,7 +19,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA See readme.md for more information. - */ //////////////////////////////// From cc612201694cc54a301e2496d6c39b121ee05239 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 22:14:46 +0200 Subject: [PATCH 032/111] Using unsigned int for comparison with String.length() --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index b63d8dde2d..57012be850 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -158,7 +158,7 @@ String urlDecode(String str) { char c; char code0; char code1; - for (int i = 0; i < str.length(); i++) { + for (unsigned int i = 0; i < str.length(); i++) { c = str.charAt(i); if (c == '+') { decodedString += ' '; From 6fcbdf204b710b7532375a5e973b1a993263656b Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 23:35:40 +0200 Subject: [PATCH 033/111] Return an error when upload fails (e.g. filesystem full) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 57012be850..62b891ed1b 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -473,7 +473,10 @@ void handleFileUpload() { DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { - uploadFile.write(upload.buf, upload.currentSize); + size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize); + if (bytesWritten != upload.currentSize) { + return returnFail("WRITE FAILED"); + } } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); } else if (upload.status == UPLOAD_FILE_END) { From 534081a00c14c0f6b33810eb38c442a768bbb70c Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Mon, 6 Apr 2020 13:27:34 +0200 Subject: [PATCH 034/111] Trying to reorder functions to please CI --- .../examples/FSBrowser/FSBrowser.ino | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 62b891ed1b..39183a3929 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -25,8 +25,8 @@ // Select the FileSystem by uncommenting one of the lines below -//#define USE_SPIFFS -#define USE_LITTLEFS +#define USE_SPIFFS +//#define USE_LITTLEFS //#define USE_SDFS //////////////////////////////// @@ -294,6 +294,22 @@ bool handleFileRead(String path) { } +/* + 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 != "" && !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 @@ -411,22 +427,6 @@ void deleteRecursive(String path) { } -/* - 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 != "" && !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 a file deletion request Operation | req.responseText From b6ba07ed60a6585975aa0c12c1f9a725a71322d2 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Mon, 6 Apr 2020 19:18:09 +0200 Subject: [PATCH 035/111] Reordered functions to please CI. --- .../examples/FSBrowser/FSBrowser.ino | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 39183a3929..2dc8a07eed 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -178,6 +178,25 @@ String urlDecode(String str) { } +/* + Checks filename for unsupported combinations + Returns an empty String if supported, or detail of error(s) if unsupported +*/ +String getFileError(String path) { + String error = String(); + if (!path.startsWith("/")) { + error += "!NO_LEADING_SLASH! "; + } + if (path.indexOf("//") != -1) { + error += "!DOUBLE_SLASH! "; + } + if (path.endsWith("/")) { + error += "!TRAILING_SLASH! "; + } + return error; +} + + //////////////////////////////// // Request handlers @@ -185,6 +204,7 @@ String urlDecode(String str) { Return the FS type, status and size info */ void handleStatus() { + DBG_OUTPUT_PORT.println("handleStatus"); FSInfo fs_info; String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; @@ -517,24 +537,6 @@ void handleNotFound() { } } -/* - Checks filename for unsupported combinations - Returns an empty String if supported, or detail of error(s) if unsupported -*/ -String getFileError(String path) { - String error = String(); - if (!path.startsWith("/")) { - error += "!NO_LEADING_SLASH! "; - } - if (path.indexOf("//") != -1) { - error += "!DOUBLE_SLASH! "; - } - if (path.endsWith("/")) { - error += "!TRAILING_SLASH! "; - } - return error; -} - void setup(void) { //////////////////////////////// From 9a81b8ba0d4aac07d3efa96b7a6a64c13fecc466 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Tue, 7 Apr 2020 01:49:18 +0200 Subject: [PATCH 036/111] Moved file --- .../FSBrowser/{ => extras}/feathericons.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename libraries/ESP8266WebServer/examples/FSBrowser/{ => extras}/feathericons.png (100%) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/feathericons.png b/libraries/ESP8266WebServer/examples/FSBrowser/extras/feathericons.png similarity index 100% rename from libraries/ESP8266WebServer/examples/FSBrowser/feathericons.png rename to libraries/ESP8266WebServer/examples/FSBrowser/extras/feathericons.png From c953d969ba1decae5c245c5dded29324aa7a5151 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:26:27 +0100 Subject: [PATCH 037/111] Renamed "SDWebServer" to the more universal "WebFileManager" --- .../WebFileManager.ino} | 0 .../SdRoot => WebFileManager/data}/edit/index.htm | 0 .../SdRoot => WebFileManager/data}/index.htm | 0 .../SdRoot => WebFileManager/data}/pins.png | Bin 4 files changed, 0 insertions(+), 0 deletions(-) rename libraries/ESP8266WebServer/examples/{SDWebServer/SDWebServer.ino => WebFileManager/WebFileManager.ino} (100%) rename libraries/ESP8266WebServer/examples/{SDWebServer/SdRoot => WebFileManager/data}/edit/index.htm (100%) rename libraries/ESP8266WebServer/examples/{SDWebServer/SdRoot => WebFileManager/data}/index.htm (100%) rename libraries/ESP8266WebServer/examples/{SDWebServer/SdRoot => WebFileManager/data}/pins.png (100%) diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SDWebServer.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SDWebServer.ino rename to libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/edit/index.htm rename to libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/index.htm rename to libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm diff --git a/libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png b/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png similarity index 100% rename from libraries/ESP8266WebServer/examples/SDWebServer/SdRoot/pins.png rename to libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png From 6ca8da54c4c2c477518799632b49f85834deb38a Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:40:05 +0100 Subject: [PATCH 038/111] SD was replaced by SDFS, and sketch now works on either SDFS, SPIFFS or LittleFS based on a #define logic (required adding a second param to open() and replacing 'FILE_WRITE' by "w") + Added size information to file list and a /status request handler to return filesystem status --- .../WebFileManager/WebFileManager.ino | 145 ++++++++++++++---- 1 file changed, 113 insertions(+), 32 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 1679b5806a..06a74c534e 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -1,5 +1,31 @@ +/* Changes : + * + * Sketch: + * - #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" + * + * Web page: + * - Tree panel width is now proportional (20%) to see long names on big screens + * - Added an icon for files, and indented them to the same level as folders + * - Changed file/folder icon set to use lighter and more neutral ones from https://feathericons.com/ (passed the result through compresspng.com and base64-image.de) + * - 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 + * - Replaced that FS status by operation status when upload/delete is in progress + * - Filled filename box in header with the name of the currently selected file + * + * TODO: + * - Fix Editor (add Save/Discard buttons ?) + * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? + * - Test on SPIFFS + */ + /* - SDWebServer - Example WebServer with SD Card backend for esp8266 + WebFileManager - A web-based File Manager for ESP8266 filesystems + (unified from former FSBrowser, FSWebServer and SDWebServer examples) Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the ESP8266WebServer library for Arduino environment. @@ -18,21 +44,56 @@ 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 + + This example implements a file manager using http requests and a html/javascript frontend. + The filesystem itself can be SDFS, SPIFFS, LittleFS and has to be selected using one of the "#define USE_xxx" directives below. + See https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html for more information on FileSystems. + + This example requires that a copy of the 'data' folder be first copied to the filesystem. To do so: + - for SDFS, copy that 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 + + Once the data and sketch have been uploaded, access the editor by going to http://espfilemanager.local/edit + + Notes: + - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line just before "FILESYSTEM.begin()", + specifying the GPIO the CS pin is connected to + - index.htm is the default index (works on subfolders as well) + - Filesystem limitations apply. For example, SDFS is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . + SPIFFS and LittleFS also have limitations. Plese see the 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 */ + +////////////////// + +// Select the FileSystem by uncommenting one of the lines below + +#define USE_SDFS +//#define USE_SPIFFS +//#define USE_LITTLEFS + +////////////////// + #include #include #include #include #include -#include + +#if defined USE_SDFS + #define FILESYSTEM SDFS + #include +#elif defined USE_SPIFFS + #define FILESYSTEM SPIFFS + #include +#elif defined USE_LITTLEFS + #define FILESYSTEM LittleFS + #include +#else + #error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. +#endif #define DBG_OUTPUT_PORT Serial @@ -43,7 +104,7 @@ const char* ssid = STASSID; const char* password = STAPSK; -const char* host = "esp8266sd"; +const char* host = "webfilemanager"; ESP8266WebServer server(80); @@ -89,11 +150,11 @@ bool loadFromSdCard(String path) { dataType = "application/zip"; } - File dataFile = SD.open(path.c_str()); + File dataFile = FILESYSTEM.open(path.c_str(), "r"); if (dataFile.isDirectory()) { path += "/index.htm"; dataType = "text/html"; - dataFile = SD.open(path.c_str()); + dataFile = FILESYSTEM.open(path.c_str(), "r"); } if (!dataFile) { @@ -118,10 +179,10 @@ void handleFileUpload() { } 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()); + if (FILESYSTEM.exists((char *)upload.filename.c_str())) { + FILESYSTEM.remove((char *)upload.filename.c_str()); } - uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE); + uploadFile = FILESYSTEM.open(upload.filename.c_str(), "w"); DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { @@ -137,10 +198,10 @@ void handleFileUpload() { } void deleteRecursive(String path) { - File file = SD.open((char *)path.c_str()); + File file = FILESYSTEM.open((char *)path.c_str(), "r"); if (!file.isDirectory()) { file.close(); - SD.remove((char *)path.c_str()); + FILESYSTEM.remove((char *)path.c_str()); return; } @@ -156,12 +217,12 @@ void deleteRecursive(String path) { deleteRecursive(entryPath); } else { entry.close(); - SD.remove((char *)entryPath.c_str()); + FILESYSTEM.remove((char *)entryPath.c_str()); } yield(); } - SD.rmdir((char *)path.c_str()); + FILESYSTEM.rmdir((char *)path.c_str()); file.close(); } @@ -170,7 +231,7 @@ void handleDelete() { return returnFail("BAD ARGS"); } String path = server.arg(0); - if (path == "/" || !SD.exists((char *)path.c_str())) { + if (path == "/" || !FILESYSTEM.exists((char *)path.c_str())) { returnFail("BAD PATH"); return; } @@ -183,33 +244,33 @@ void handleCreate() { return returnFail("BAD ARGS"); } String path = server.arg(0); - if (path == "/" || SD.exists((char *)path.c_str())) { + if (path == "/" || FILESYSTEM.exists((char *)path.c_str())) { returnFail("BAD PATH"); return; } if (path.indexOf('.') > 0) { - File file = SD.open((char *)path.c_str(), FILE_WRITE); + File file = FILESYSTEM.open((char *)path.c_str(), "w"); if (file) { file.write((const char *)0); file.close(); } } else { - SD.mkdir((char *)path.c_str()); + FILESYSTEM.mkdir((char *)path.c_str()); } returnOK(); } -void printDirectory() { +void handleList() { if (!server.hasArg("dir")) { return returnFail("BAD ARGS"); } String path = server.arg("dir"); - if (path != "/" && !SD.exists((char *)path.c_str())) { + if (path != "/" && !FILESYSTEM.exists((char *)path.c_str())) { return returnFail("BAD PATH"); } - File dir = SD.open((char *)path.c_str()); - path.clear(); + File dir = FILESYSTEM.open((char *)path.c_str(), "r"); + path = String(); if (!dir.isDirectory()) { dir.close(); return returnFail("NOT DIR"); @@ -232,7 +293,14 @@ void printDirectory() { } output += "{\"type\":\""; - output += (entry.isDirectory()) ? "dir" : "file"; + if (entry.isDirectory()) { + output += "dir"; + } + else { + output += "file"; + output += "\",\"size\":\""; + output += entry.size(); + } output += "\",\"name\":\""; output += entry.name(); output += "\""; @@ -245,6 +313,17 @@ void printDirectory() { dir.close(); } +void handleStatus() { + FSInfo fs_info; + FILESYSTEM.info(fs_info); + + String json = String("{\"totalBytes\":\"") + fs_info.totalBytes + + "\", \"usedBytes\":\"" + fs_info.usedBytes + + "\"}"; + + server.send(200, "application/json", json); +} + void handleNotFound() { if (hasSD && loadFromSdCard(server.uri())) { return; @@ -297,10 +376,11 @@ void setup(void) { } - server.on("/list", HTTP_GET, printDirectory); - server.on("/edit", HTTP_DELETE, handleDelete); - server.on("/edit", HTTP_PUT, handleCreate); - server.on("/edit", HTTP_POST, []() { + server.on("/status",HTTP_GET, handleStatus); + server.on("/list", HTTP_GET, handleList); + server.on("/edit", HTTP_DELETE, handleDelete); + server.on("/edit", HTTP_PUT, handleCreate); + server.on("/edit", HTTP_POST, []() { returnOK(); }, handleFileUpload); server.onNotFound(handleNotFound); @@ -308,7 +388,8 @@ void setup(void) { server.begin(); DBG_OUTPUT_PORT.println("HTTP server started"); - if (SD.begin(SS)) { + // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); + if (FILESYSTEM.begin()) { DBG_OUTPUT_PORT.println("SD Card initialized."); hasSD = true; } From 6d501ab1e2baf87e0cbc29cff5c705e5412f229a Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:58:10 +0100 Subject: [PATCH 039/111] Tree panel width is now proportional to window. Changed icons (lighter and more neutral), including one for files. Show size of files. Fill "filename" box upon clicking on a file. Sort files alphabetically. --- .../WebFileManager/data/edit/index.htm | 169 ++++++++++++------ 1 file changed, 115 insertions(+), 54 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index f535601e7d..7e1124f62d 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -1,7 +1,7 @@ - SD Editor + File manager From 4ae345fd0ec313199c67d9e69b665f675746ef53 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 14:59:36 +0100 Subject: [PATCH 040/111] Replaced by a lighter version --- .../examples/WebFileManager/data/pins.png | Bin 177869 -> 55578 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png b/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png index ac7fc0f9cb64d99f9f33b877798c61d02086d00b..f988bf45e3eff7380cf899affb33f7bb17e23cd5 100644 GIT binary patch literal 55578 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
+
From da09f3636e38705cc0f6d45813c80d9676538f74 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 15 Mar 2020 22:41:21 +0100 Subject: [PATCH 048/111] Fixed tree refresh on delete in all cases by returning the remaining path as response to the delete request. Refactoring --- .../WebFileManager/WebFileManager.ino | 19 +++-- .../WebFileManager/data/edit/index.htm | 76 +++++++++---------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 5a7f218cb8..e3c2e2d363 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -25,11 +25,11 @@ * * TODO: * - Change lengthy FS status by a percentage graph, with numbers as tooltip - * - Bug: when deleting a file, we should refresh the parent node unless it has been deleted behind the scene, in which case recurse + * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k * - Support Files not starting with '/' (SPIFFS) * - Cleanup (look for TODO below and in HTML) * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? - * + * * TEST: (*) = failed vXXX = OK * - On SPIFFS: * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root @@ -267,6 +267,7 @@ void handleFileList() { } output += "\",\"name\":\""; + // TODO Is this correct if path does not start with "/" ? if (entry.name()[0] == '/') { output += &(entry.name()[1]); } @@ -370,8 +371,7 @@ void deleteRecursive(String path) { Dir dir = fileSystem->openDir(path); while (dir.next()) { - String entryPath = path + "/" + dir.fileName(); - deleteRecursive(entryPath); + deleteRecursive(path + "/" + dir.fileName()); } // Then delete the folder itself @@ -395,7 +395,15 @@ void handleFileDelete() { return returnFail("BAD PATH"); } deleteRecursive(path); - returnOK(); + + // As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, + // return the path of the closest still existing parent + while (path != "" && !fileSystem->exists(path)) { + path = path.substring(0, path.lastIndexOf("/")); + } + DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); + + server.send(200, "text/plain", path + "/"); } /* @@ -411,6 +419,7 @@ void handleFileUpload() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; + // TODO Is this correct if path does not start with "/" ? if (!filename.startsWith("/")) { filename = "/" + filename; } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index b192b836b9..6282c2496e 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -188,7 +188,7 @@ alert("ERROR: [" + request.status+"] " + request.responseText); } - function canLoadEditor() { + function canLoadContents() { return (document.getElementById("saveBtn").disabled || confirm("Changes to your document will be lost if you continue")); } @@ -209,6 +209,7 @@ path.id = "upload-path"; path.type = "text"; path.name = "path"; + // TODO Is this correct if path does not start with "/" ? path.defaultValue = "/"; header.appendChild(path); @@ -260,7 +261,7 @@ if (mustFormat83()) { filename = to83(filename); } - // TODO is this suitable ? + // TODO Is this correct if path does not start with "/" ? if (!path.value.startsWith("/")) path.value = "/" + path.value; path.value = path.value.substring(0, path.value.lastIndexOf("/")+1) + filename; } @@ -271,6 +272,7 @@ else { tree.refreshPath(path.value); refreshStatus(); + attemptLoad(name); } } @@ -319,13 +321,11 @@ mkfile.onclick = function(e){ var name = path.value.trim(); if (name === "") alert("A filename must be given"); - else if (name.endsWith("/")) alert("Filenames cannot end with a '/' character"); - else { + else if (name.endsWith("/")) alert("Filenames must not end with a '/' character"); + else { + // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); - if (canLoadEditor()) { - editor.loadUrl(name); - } } }; @@ -334,6 +334,7 @@ if (name === "") alert("A folder name must be given"); else { if (!name.endsWith("/")) name = name + "/"; + // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); } @@ -468,6 +469,7 @@ upload.onclick = function(e){ var pathEl = document.getElementById("upload-path"); if(pathEl){ + // TODO Is this correct if path does not start with "/" ? var subPath = pathEl.value; if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath; else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath; @@ -491,13 +493,13 @@ if(isTextFile(path)){ action.innerHTML = "Edit"; action.onclick = function(e){ - if (canLoadEditor()) editor.loadUrl(path); + if (canLoadContents()) editor.loadUrl(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; } else if(isImageFile(path)){ action.innerHTML = "Preview"; action.onclick = function(e){ - if (canLoadEditor()) loadPreview(path); + if (canLoadContents()) loadPreview(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; } @@ -538,23 +540,28 @@ } }; } + + function attemptLoad(path) { + if (canLoadContents()) { + if(isTextFile(path)){ + editor.loadUrl(path); + } else if(isImageFile(path)){ + loadPreview(path); + } else { + clearMainPanel(path); + } + } + } function createTreeLeaf(path, name, size){ var leaf = document.createElement("li"); + // TODO Is this correct if path does not start with "/" ? leaf.id = ((path == "/")?"":path)+"/"+name; var label = document.createElement("span"); label.innerHTML = name + " (" + readableSize(size) + ")"; leaf.appendChild(label); leaf.onclick = function(e){ - if (canLoadEditor()) { - if(isTextFile(leaf.id)){ - editor.loadUrl(leaf.id); - } else if(isImageFile(leaf.id)){ - loadPreview(leaf.id); - } else { - clearMainPanel(leaf.id); - } - } + attemptLoad(lead.id); }; leaf.oncontextmenu = function(e){ e.preventDefault(); @@ -568,6 +575,7 @@ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; + // TODO Is this correct if path does not start with "/" ? check.id = ((path == "/")?"":path)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); @@ -660,8 +668,8 @@ this.refreshPath = function(path){ if(path.lastIndexOf("/") < 1){ - // TODO is this what we want ? - path = '/'; + // TODO Is this correct if path does not start with "/" ? + path = "/"; treeRoot.removeChild(treeRoot.childNodes[0]); httpList(treeRoot, "/"); } else { @@ -675,6 +683,7 @@ else { // No parent node (SPIFFS), refresh full tree treeRoot.removeChild(treeRoot.childNodes[0]); + // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); } } @@ -685,7 +694,8 @@ document.body.style.cursor = "default"; if(xmlHttp.status != 200) showHttpError(xmlHttp); else { - refreshPath(path); + // Folder to refresh is returned + refreshPath(xmlHttp.responseText); } refreshStatus(); } @@ -705,27 +715,8 @@ function getCallback(parent, path){ return function(){ document.body.style.cursor = "default"; - if(xmlHttp.status == 200) addList(parent, path, JSON.parse(xmlHttp.responseText)); - else if(xmlHttp.status == 500) { - // With littleFS, it could be because we just deleted the last file of a folder, and the folder itself was silently removed. Let's refresh the parent -/* - var lastSlashPos = path.lastIndexOf("/"); - if (lastSlashPos == -1) { - showHttpError(xmlHttp); - } - else { - httpList(parent.parentElement, path.substring(0, lastSlashPos)); - } -*/ - path = path.substring(0, path.lastIndexOf("/")); - if (document.getElementById(path)) { - var leaf = document.getElementById(path).parentNode; - if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); - httpList(leaf, path); - } - else showHttpError(xmlHttp); - } - else showHttpError(xmlHttp); + if(xmlHttp.status != 200) showHttpError(xmlHttp); + else addList(parent, path, JSON.parse(xmlHttp.responseText)); } } @@ -737,6 +728,7 @@ xmlHttp.send(null); } + // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); return this; } From f0587c2b913283d97618b2cd74d2fbfe09b054e9 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 18 Mar 2020 22:06:07 +0100 Subject: [PATCH 049/111] Changed FS status in text by a percentage graph, with numbers as tooltip. Unsupported files on SPIFFS (files at root not sarting with "/", files with double "/", files ending with "/") are now detected and reported in the page. --- .../WebFileManager/WebFileManager.ino | 134 +++++---- .../WebFileManager/data/edit/index.htm | 261 +++++++++--------- 2 files changed, 219 insertions(+), 176 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index e3c2e2d363..3af3d0dcc1 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -16,46 +16,52 @@ * - Added FS status information at the top right * - Replaced that FS status by operation status when async operations are in progress * - Filled filename box in header with the name of the last clicked file - * - Selecting a file for upload proposes to put it in the same folder as 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 - * - Removed limitation "files must have an extension, folders may not" - * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) * - Support Filenames without extension, Dirnames with extension - * - Added Save/Discard/Help buttons to Editor, discard confirmation on leave, and refresh tree/status upon save + * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) + * - Added Save/Discard/Help buttons to ACE editor, discard confirmation on leave, and refresh tree/status upon save * * TODO: - * - Change lengthy FS status by a percentage graph, with numbers as tooltip * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k - * - Support Files not starting with '/' (SPIFFS) * - Cleanup (look for TODO below and in HTML) * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? + * - Check/default editor to text viewer if ace.js is not reachable + * - Perform test suite again * - * TEST: (*) = failed vXXX = OK + * TEST: (#XXX = failed / vXXX = OK) * - On SPIFFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image + * - Create nested file and delete the file / Create nested file and delete top folder + * - Attempt creation of unsupported filenames * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image + * - Create nested file and delete the file / Create nested file and delete top folder * * - On LittleFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder * * - On SDFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - At root : MkFile 'x.txt' / List / Edit / Download / Delete / Upload 'x.png' / View image / Mkdir + * - In subdir : MkFile 'y.txt' / List / Edit / Download / Delete / Upload 'y.png' / View image / Mkdir + * - Create nested file and delete the file / Create nested file and delete top folder */ /* WebFileManager - A web-based File Manager for ESP8266 filesystems - (unified from former FSBrowser, FSWebServer and SDWebServer examples) + (unified from former FSWebServer, FSBrowser and SDWebServer examples) Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the ESP8266WebServer library for Arduino environment. @@ -93,6 +99,8 @@ - Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . SPIFFS and LittleFS also have limitations. Plese see the 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. */ @@ -147,6 +155,8 @@ const char* host = "webfilemanager"; ESP8266WebServer server(80); static bool fsOK; +String unsupportedFiles = String(); + File uploadFile; @@ -215,6 +225,7 @@ void handleStatus() { else { json += "\"false\""; } + json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; json += "}"; server.send(200, "application/json", json); } @@ -233,7 +244,7 @@ void handleFileList() { } String path = server.arg("dir"); - if (path != "" && !fileSystem->exists(path)) { + if (path != "/" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } @@ -246,36 +257,30 @@ void handleFileList() { } */ - DBG_OUTPUT_PORT.println("handleFileList: " + path); + DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = fileSystem->openDir(path); path = String(); String output = "["; while (dir.next()) { - File entry = dir.openFile("r"); - if (output != "[") { - output += ','; - } - output += "{\"type\":\""; - if (entry.isDirectory()) { - output += "dir"; + String error = getFileError(dir.fileName()); + if (error.length() > 0) { + DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); } else { - output += "file"; - output += "\",\"size\":\""; - output += entry.size(); - } - output += "\",\"name\":\""; - - // TODO Is this correct if path does not start with "/" ? - if (entry.name()[0] == '/') { - output += &(entry.name()[1]); - } - else { - output += entry.name(); + // Filename is supported + if (output != "[") output += ','; + output += "{\"type\":\""; + if (dir.isDirectory()) output += "dir"; + else output += String("file\",\"size\":\"") + dir.fileSize(); + + output += "\",\"name\":\""; + // Always return names without leading "/" + if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); + else output += dir.fileName(); + + output += "\"}"; } - output += "\"}"; - entry.close(); } output += "]"; @@ -287,7 +292,7 @@ void handleFileList() { * Read the given file from the filesystem and stream it back to the client */ bool handleFileRead(String path) { - DBG_OUTPUT_PORT.println("handleFileRead: " + path); + DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); if (path.endsWith("/")) { path += "index.htm"; } @@ -326,7 +331,7 @@ void handleFileCreate() { return returnFail("BAD ARGS"); } String path = server.arg(0); - DBG_OUTPUT_PORT.println("handleFileCreate: " + path); + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); if (path == "/") { return returnFail("BAD PATH"); } @@ -390,7 +395,7 @@ void handleFileDelete() { return returnFail("BAD ARGS"); } String path = server.arg(0); - DBG_OUTPUT_PORT.println("handleFileDelete: " + path); + DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); if (path == "/" || !fileSystem->exists(path)) { return returnFail("BAD PATH"); } @@ -399,7 +404,8 @@ void handleFileDelete() { // As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, // return the path of the closest still existing parent while (path != "" && !fileSystem->exists(path)) { - path = path.substring(0, path.lastIndexOf("/")); + 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); @@ -419,25 +425,25 @@ void handleFileUpload() { HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; - // TODO Is this correct if path does not start with "/" ? + // Make sure paths always start with "/" if (!filename.startsWith("/")) { filename = "/" + filename; } DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); - DBG_OUTPUT_PORT.print(String("Upload: START, filename: ") + filename); + DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { uploadFile.write(upload.buf, upload.currentSize); } - DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: " + upload.currentSize); + DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); } else if (upload.status == UPLOAD_FILE_END) { if (uploadFile) { uploadFile.close(); } - DBG_OUTPUT_PORT.print("Upload: END, Size: " + upload.totalSize); + DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); } } @@ -468,6 +474,18 @@ void handleNotFound() { } } +/* + * Checks filename for unsupported combinations + * Returns an empty String if supported, or detail of error(s) if unsupported + */ +String getFileError(String path) { + String error = String(); + if (!path.startsWith("/")) error += "!NO_LEADING_SLASH! "; + if (path.indexOf("//") != -1) error += "!DOUBLE_SLASH! "; + if (path.endsWith("/")) error += "!TRAILING_SLASH! "; + return error; +} + void setup(void) { //////////////////////////////// @@ -487,8 +505,18 @@ void setup(void) { { // Debug: dump contents of root folder on console Dir dir = fileSystem->openDir(""); - while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + ")")); - DBG_OUTPUT_PORT.printf("\n"); + DBG_OUTPUT_PORT.println("List of files at root of filesystem:"); + while (dir.next()) { + String error = getFileError(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.println(); + + // Keep the "unsupportedFiles" variable to show it, but clean it up + unsupportedFiles.replace("\n", "
"); + unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); } //////////////////////////////// diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 6282c2496e..4c15b28b65 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -1,4 +1,4 @@ - + File manager @@ -124,6 +124,47 @@ } } + #fsTotal { + display: inline-block; + width:100px; + height:10px; + border:1px solid #000; + overflow:hidden; + background: #E6E6E6; + } + #fsUsed { + display: inline-block; + height:100%; + border-right: 1px solid #26A0DA; + background: #26A0DA; + } + #warning { + height:100%; + background-color:orange; + color:black; + } + .tooltip { + position: absolute; + z-index: 2; /* must be above editor which is at 1 */ + right:0px; + top:20px; + visibility: hidden; + background-color: white; + color: black; + text-align: center; + border: 1px solid #000; + padding: 3px; + font-size: 10px; + } + + #fsTotal:hover .tooltip { + visibility: visible; + } + + #warning:hover .tooltip { + visibility: visible; + } + #header { position: absolute; top: 0; @@ -206,10 +247,9 @@ header.appendChild(fileSelector); var path = document.createElement("input"); - path.id = "upload-path"; + path.id = "filenameTxt"; path.type = "text"; path.name = "path"; - // TODO Is this correct if path does not start with "/" ? path.defaultValue = "/"; header.appendChild(path); @@ -261,7 +301,6 @@ if (mustFormat83()) { filename = to83(filename); } - // TODO Is this correct if path does not start with "/" ? if (!path.value.startsWith("/")) path.value = "/" + path.value; path.value = path.value.substring(0, path.value.lastIndexOf("/")+1) + filename; } @@ -272,7 +311,7 @@ else { tree.refreshPath(path.value); refreshStatus(); - attemptLoad(name); + tree.attemptLoad(path.value); } } @@ -300,7 +339,7 @@ function mustFormat83() { - return false; // TODO: if needed, test fsInfo.type + return false; // TODO: if needed, test fsInfo.type (and fatType ?) } function to83(filename) { @@ -323,7 +362,6 @@ if (name === "") alert("A filename must be given"); else if (name.endsWith("/")) alert("Filenames must not end with a '/' character"); else { - // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); } @@ -334,7 +372,6 @@ if (name === "") alert("A folder name must be given"); else { if (!name.endsWith("/")) name = name + "/"; - // TODO Is this correct if path does not start with "/" ? if (!name.startsWith("/")) name = "/" + name; createPath(name); } @@ -354,27 +391,7 @@ } - - - function statusCallback() { - if(xmlHttp.status != 200) showHttpError(xmlHttp); - else { - fsInfo = JSON.parse(xmlHttp.responseText); - var statusText = fsInfo.type + " - "; - if (fsInfo.isOk) { - statusText += "Free space: " + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) - + ". Used: " + readableSize(fsInfo.usedBytes) - + ". Total: " + readableSize(fsInfo.totalBytes); - } else { - statusText += "INIT ERROR !"; - } - setStatus(statusText); - if (fsInfo.type != "SPIFFS") { - document.getElementById("mkdir").style.display = "inline"; - } - } - } - + function refreshStatus(){ var xmlHttp = new XMLHttpRequest(); xmlHttp.onload = function() { @@ -383,10 +400,19 @@ fsInfo = JSON.parse(xmlHttp.responseText); var statusText = fsInfo.type + " - "; if (fsInfo.isOk) { - statusText += "Free space: " + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) - + ". Used: " + readableSize(fsInfo.usedBytes) - + ". Total: " + readableSize(fsInfo.totalBytes); - } else { + statusText += "" + + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + "\xa0free\xa0of\xa0" + readableSize(fsInfo.totalBytes) + + ""; + if (fsInfo.unsupportedFiles) { + statusText += " WARNING" + + "Filesystem\xa0contains\xa0unsupported\xa0filenames:
" + + fsInfo.unsupportedFiles + + "
"; + } + } + else { statusText += "INIT ERROR !"; } setStatus(statusText); @@ -415,7 +441,7 @@ } function loadPreview(path){ - document.getElementById("upload-path").value = path; + document.getElementById("filenameTxt").value = path; document.getElementById("editor").style.display = "none"; document.getElementById("editorButtons").style.display = "none"; preview.style.display = "block"; @@ -423,13 +449,48 @@ } function clearMainPanel(path) { - document.getElementById("upload-path").value = path; + document.getElementById("filenameTxt").value = path; document.getElementById("editor").style.display = "none"; document.getElementById("editorButtons").style.display = "none"; preview.style.display = "block"; preview.innerHTML = "(format not supported for viewing/editing)"; } + function isTextFile(path){ + var ext = /(?:\.([^.]+))?$/.exec(path)[1]; + if(ext !== undefined){ + switch(ext.toLowerCase()){ + case "txt": + case "htm": + case "html": + case "js": + case "json": + case "c": + case "h": + case "cpp": + case "css": + case "xml": + return true; + } + } + return false; + } + + function isImageFile(path){ + var ext = /(?:\.([^.]+))?$/.exec(path)[1]; + if(ext !== undefined){ + switch(ext.toLowerCase()){ + case "png": + case "jpg": + case "jpeg": + case "gif": + case "ico": + return true; + } + } + return false; + } + function fillFolderMenu(el, path){ var list = document.createElement("ul"); el.appendChild(list); @@ -453,7 +514,8 @@ httpList(leaf, path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; - } else { + } + else { expnd.innerHTML = "Expand"; expnd.onclick = function(e){ document.getElementById(path).checked = true; @@ -467,13 +529,10 @@ list.appendChild(upload); upload.innerHTML = "Upload"; upload.onclick = function(e){ - var pathEl = document.getElementById("upload-path"); - if(pathEl){ - // TODO Is this correct if path does not start with "/" ? - var subPath = pathEl.value; - if(subPath.lastIndexOf("/") < 1) pathEl.value = path+subPath; - else pathEl.value = path.substring(subPath.lastIndexOf("/"))+subPath; - } + var filenameTxt = document.getElementById("filenameTxt"); + var subPath = filenameTxt.value; + if(subPath.lastIndexOf("/") < 1) filenameTxt.value = path + subPath; + else filenameTxt.value = path.substring(subPath.lastIndexOf("/")) + subPath; if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; var delFolder = document.createElement("li"); @@ -496,7 +555,8 @@ if (canLoadContents()) editor.loadUrl(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; - } else if(isImageFile(path)){ + } + else if(isImageFile(path)){ action.innerHTML = "Preview"; action.onclick = function(e){ if (canLoadContents()) loadPreview(path); @@ -541,27 +601,22 @@ }; } - function attemptLoad(path) { + this.attemptLoad = function(path) { if (canLoadContents()) { - if(isTextFile(path)){ - editor.loadUrl(path); - } else if(isImageFile(path)){ - loadPreview(path); - } else { - clearMainPanel(path); - } + if(isTextFile(path)) editor.loadUrl(path); + else if(isImageFile(path)) loadPreview(path); + else clearMainPanel(path); } } - function createTreeLeaf(path, name, size){ + function createTreeLeaf(parentPath, name, size){ var leaf = document.createElement("li"); - // TODO Is this correct if path does not start with "/" ? - leaf.id = ((path == "/")?"":path)+"/"+name; + leaf.id = ((parentPath == "/")?"":parentPath)+"/"+name; var label = document.createElement("span"); label.innerHTML = name + " (" + readableSize(size) + ")"; leaf.appendChild(label); leaf.onclick = function(e){ - attemptLoad(lead.id); + attemptLoad(leaf.id); }; leaf.oncontextmenu = function(e){ e.preventDefault(); @@ -571,11 +626,10 @@ return leaf; } - function createTreeBranch(path, name, disabled){ + function createTreeBranch(parentPath, name, disabled){ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; - // TODO Is this correct if path does not start with "/" ? check.id = ((path == "/")?"":path)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); @@ -594,7 +648,8 @@ check.checked = true; if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); httpList(leaf, check.id); - } else { + } + else { check.checked = false; } }; @@ -606,7 +661,7 @@ return leaf; } - function addList(parent, path, items){ + function addFileNodes(parentEl, parentPath, items){ items.sort(function(a, b) { if (a.type == b.type) { return a.name.localeCompare(b.name); // a before z @@ -616,74 +671,51 @@ } }); var list = document.createElement("ul"); - parent.appendChild(list); + parentEl.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); - } + if(item.type === "file") itemEl = createTreeLeaf(parentPath, item.name, item.size); + else itemEl = createTreeBranch(parentPath, item.name); list.appendChild(itemEl); - } - + } } - function isTextFile(path){ - var ext = /(?:\.([^.]+))?$/.exec(path)[1]; - if(ext !== undefined){ - switch(ext.toLowerCase()){ - case "txt": - case "htm": - case "html": - case "js": - case "json": - case "c": - case "h": - case "cpp": - case "css": - case "xml": - return true; - } + function onListReceived(parentEl, parentPath){ + return function(){ + document.body.style.cursor = "default"; + if(xmlHttp.status != 200) showHttpError(xmlHttp); + else addFileNodes(parentEl, parentPath, JSON.parse(xmlHttp.responseText)); } - return false; } - function isImageFile(path){ - var ext = /(?:\.([^.]+))?$/.exec(path)[1]; - if(ext !== undefined){ - switch(ext.toLowerCase()){ - case "png": - case "jpg": - case "jpeg": - case "gif": - case "ico": - return true; - } - } - return false; + function httpList(parentEl, parentPath){ + xmlHttp = new XMLHttpRequest(parentEl, parentPath); + xmlHttp.onload = onListReceived(parentEl, parentPath); + document.body.style.cursor = "wait"; + xmlHttp.open("GET", "/list?dir="+parentPath, true); + xmlHttp.send(null); } this.refreshPath = function(path){ if(path.lastIndexOf("/") < 1){ - // TODO Is this correct if path does not start with "/" ? + // No "/" => reset the root path = "/"; treeRoot.removeChild(treeRoot.childNodes[0]); httpList(treeRoot, "/"); - } else { - path = path.substring(0, path.lastIndexOf("/")); - if (document.getElementById(path)) { + } + else { + var parentPath = path.substring(0, path.lastIndexOf("/")); + if (document.getElementById(parentPath)) { // We have a parent node (LittleFS or SDFS) - var leaf = document.getElementById(path).parentNode; + var leaf = document.getElementById(parentPath).parentNode; if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); - httpList(leaf, path); + httpList(leaf, parentPath); } else { // No parent node (SPIFFS), refresh full tree treeRoot.removeChild(treeRoot.childNodes[0]); - // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); } } @@ -712,23 +744,6 @@ xmlHttp.send(formData); } - function getCallback(parent, path){ - return function(){ - document.body.style.cursor = "default"; - if(xmlHttp.status != 200) showHttpError(xmlHttp); - else addList(parent, path, JSON.parse(xmlHttp.responseText)); - } - } - - function httpList(parent, path){ - xmlHttp = new XMLHttpRequest(parent, path); - xmlHttp.onload = getCallback(parent, path); - document.body.style.cursor = "wait"; - xmlHttp.open("GET", "/list?dir="+path, true); - xmlHttp.send(null); - } - - // TODO Is this correct if path does not start with "/" ? httpList(treeRoot, "/"); return this; } @@ -838,7 +853,7 @@ editor.loadUrl = function(filename){ enableSaveDiscard(false); - document.getElementById("upload-path").value = filename; + document.getElementById("filenameTxt").value = filename; file = filename; lang = getLangFromFilename(file); type = "text/"+lang; From c6948fcd3d89696794427c882267afade180d7d1 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 20 Mar 2020 22:56:13 +0100 Subject: [PATCH 050/111] Small fix + refactoring --- .../WebFileManager/data/edit/index.htm | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 4c15b28b65..75d3be9c1d 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -123,7 +123,7 @@ } } } - + #fsTotal { display: inline-block; width:100px; @@ -391,7 +391,7 @@ } - + function refreshStatus(){ var xmlHttp = new XMLHttpRequest(); xmlHttp.onload = function() { @@ -439,8 +439,8 @@ function loadDownload(path){ document.getElementById('download-frame').src = path+"?download=true"; } - - function loadPreview(path){ + + function loadImgPreview(path){ document.getElementById("filenameTxt").value = path; document.getElementById("editor").style.display = "none"; document.getElementById("editorButtons").style.display = "none"; @@ -455,7 +455,7 @@ preview.style.display = "block"; preview.innerHTML = "(format not supported for viewing/editing)"; } - + function isTextFile(path){ var ext = /(?:\.([^.]+))?$/.exec(path)[1]; if(ext !== undefined){ @@ -559,7 +559,7 @@ else if(isImageFile(path)){ action.innerHTML = "Preview"; action.onclick = function(e){ - if (canLoadContents()) loadPreview(path); + if (canLoadContents()) loadImgPreview(path); if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el); }; } @@ -604,7 +604,7 @@ this.attemptLoad = function(path) { if (canLoadContents()) { if(isTextFile(path)) editor.loadUrl(path); - else if(isImageFile(path)) loadPreview(path); + else if(isImageFile(path)) loadImgPreview(path); else clearMainPanel(path); } } @@ -630,7 +630,7 @@ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; - check.id = ((path == "/")?"":path)+"/"+name; + check.id = ((parentPath == "/")?"":parentPath)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); var label = document.createElement("label"); @@ -649,7 +649,7 @@ if(leaf.childNodes.length == 3) leaf.removeChild(leaf.childNodes[2]); httpList(leaf, check.id); } - else { + else { check.checked = false; } }; @@ -679,7 +679,7 @@ if(item.type === "file") itemEl = createTreeLeaf(parentPath, item.name, item.size); else itemEl = createTreeBranch(parentPath, item.name); list.appendChild(itemEl); - } + } } function onListReceived(parentEl, parentPath){ @@ -697,7 +697,7 @@ xmlHttp.open("GET", "/list?dir="+parentPath, true); xmlHttp.send(null); } - + this.refreshPath = function(path){ if(path.lastIndexOf("/") < 1){ // No "/" => reset the root @@ -894,7 +894,7 @@ function onBodyLoad(){ var vars = {}; - var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; }); + var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) { vars[key] = value; }); var editor = createEditor("editor", vars.file, vars.lang, vars.theme); tree = createTree("tree", editor); createHeader("header", tree, editor); From 16acb245b8e310618e1549ed60fe34e6e708ebeb Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 20 Mar 2020 22:57:33 +0100 Subject: [PATCH 051/111] Restrict filename support check to SPIFFS. --- .../WebFileManager/WebFileManager.ino | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 3af3d0dcc1..979721214f 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -25,8 +25,8 @@ * TODO: * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k * - Cleanup (look for TODO below and in HTML) - * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? - * - Check/default editor to text viewer if ace.js is not reachable + * - ? Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) + * - ? Check if ace.js is reachable and default to text viewer otherwise * - Perform test suite again * * TEST: (#XXX = failed / vXXX = OK) @@ -109,8 +109,8 @@ // Select the FileSystem by uncommenting one of the lines below //#define USE_SPIFFS -#define USE_LITTLEFS -//#define USE_SDFS +//#define USE_LITTLEFS +#define USE_SDFS //////////////////////////////// @@ -263,24 +263,24 @@ void handleFileList() { String output = "["; while (dir.next()) { +#ifdef USE_SPIFFS String error = getFileError(dir.fileName()); if (error.length() > 0) { DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); + continue } - else { - // Filename is supported - if (output != "[") output += ','; - output += "{\"type\":\""; - if (dir.isDirectory()) output += "dir"; - else output += String("file\",\"size\":\"") + dir.fileSize(); - - output += "\",\"name\":\""; - // Always return names without leading "/" - if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); - else output += dir.fileName(); - - output += "\"}"; - } +#endif + if (output != "[") output += ','; + output += "{\"type\":\""; + if (dir.isDirectory()) output += "dir"; + else output += String("file\",\"size\":\"") + dir.fileSize(); + + output += "\",\"name\":\""; + // Always return names without leading "/" + if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); + else output += dir.fileName(); + + output += "\"}"; } output += "]"; @@ -502,8 +502,8 @@ void setup(void) { fsOK = fileSystem->begin(); DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "Filesystem init failed!"); - { - // Debug: dump contents of root folder on console +#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("List of files at root of filesystem:"); while (dir.next()) { @@ -517,7 +517,7 @@ void setup(void) { // Keep the "unsupportedFiles" variable to show it, but clean it up unsupportedFiles.replace("\n", "
"); unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); - } +#endif //////////////////////////////// // WI-FI INIT From 9bba9dd739b1ae5e57e6f6792b72a360bfb55c2a Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 29 Mar 2020 19:22:56 +0200 Subject: [PATCH 052/111] Implemented Move/Rename. Added "loading" screen during async operations (dim with spinner and status). Fixed "discard" feature that kept prompting even after an image was loaded. Improved refresh of parts of the tree, with recursive listing. Moved the "path" id attribute to the "li" elements for folders (was already the case for files). Refactoring and cleanup. --- .../WebFileManager/WebFileManager.ino | 129 +++- .../WebFileManager/data/edit/index.htm | 591 ++++++++++++------ 2 files changed, 487 insertions(+), 233 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 979721214f..1a643cc5d7 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -14,19 +14,21 @@ * - 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 - * - Replaced that FS status by operation status when async operations are in progress + * - Make clear that an async operation is in progress by dimming screen and showing opertation 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 refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) - * - Added Save/Discard/Help buttons to ACE editor, discard confirmation on leave, and refresh tree/status upon save + * - Improved recursive refresh of parts of the tree (e.g. upon file delete, refresh subfolder, move to a closed folder) + * - Added Save/Discard/Help buttons to ACE editor, discard confirmation on leaveand refresh tree/status upon save + * - Added Rename/Move feature to context menu * * TODO: - * - When creating file /d/f/g/h/k, tree should open nodes recursively to show k * - Cleanup (look for TODO below and in HTML) + * - ? Add a visible root node "/" (without a delete option) + add the FS type next to it, like LittleFS + move "Mkdir" and "MkFile" to context menu + implement drag/drop for move + make "rename" only a local rename operation (no move) + remove recursive opening ? * - ? Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) * - ? Check if ace.js is reachable and default to text viewer otherwise + * - ? Present SPIFFS as a hierarchical FS * - Perform test suite again * * TEST: (#XXX = failed / vXXX = OK) @@ -167,6 +169,10 @@ void returnOK() { server.send(200, "text/plain", ""); } +void returnOKWithMsg(String msg) { + server.send(200, "text/plain", msg); +} + void returnNotFound(String msg) { server.send(404, "text/plain", msg); } @@ -321,38 +327,85 @@ bool handleFileRead(String path) { /* - * Handle the creation of a new file + * 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 returnFail("FS INIT ERROR"); } - if (server.args() == 0) { + if (server.args() == 0 || server.args() > 2) { return returnFail("BAD ARGS"); } - String path = server.arg(0); - DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + + String path = server.arg("path"); + if (path == "") { // todo is this the right test in case of no arg + return returnFail("MISSING PATH ARG"); + } if (path == "/") { return returnFail("BAD PATH"); } if (fileSystem->exists(path)) { - return returnFail("FILE EXISTS"); + return returnFail("PATH FILE EXISTS"); } + + if (server.args() == 1) { + // One argument: create + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + if (path.endsWith("/")) { + // Create a folder + path.remove(path.length() - 1); + if (!fileSystem->mkdir(path)) { + return returnFail("MKDIR FAILED"); + } + } + else { + // Create a file + File file = fileSystem->open(path, "w"); + if (file) { + file.write((const char *)0); + file.close(); + } + else { + return returnFail("CREATE FAILED"); + } + } + if (path.lastIndexOf("/") > 0) path = path.substring(0, path.lastIndexOf("/")); + returnOKWithMsg(path); + } + else { + // Two arguments: rename + String src = server.arg("src"); + if (src == "") { // todo is this the right test in case of no arg + return returnFail("MISSING SRC ARG"); + } + if (src == "/") { + return returnFail("BAD SRC"); + } + if (!fileSystem->exists(src)) { + return returnFail("SRC FILE NOT FOUND"); + } + + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); - if (path.endsWith("/")) { - // Create a folder - path.remove(path.length() - 1); - fileSystem->mkdir(path); - } - else { - // Create a file - File file = fileSystem->open(path, "w"); - if (file) { - file.write((const char *)0); - file.close(); + if (path.endsWith("/")) { + path.remove(path.length() - 1); } + if (src.endsWith("/")) { + src.remove(src.length() - 1); + } + if (!fileSystem->rename(src, path)) { + return returnFail("RENAME FAILED"); + } + returnOKWithMsg(lastExistingParent(src) + "/"); } - return returnOK(); } @@ -384,8 +437,24 @@ void deleteRecursive(String path) { } +// TODO test delete and move on SPIFFS +// 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 != "" && !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 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) { @@ -400,16 +469,8 @@ void handleFileDelete() { return returnFail("BAD PATH"); } deleteRecursive(path); - - // As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed, - // return the path of the closest still existing parent - while (path != "" && !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); - - server.send(200, "text/plain", path + "/"); + + returnOKWithMsg(lastExistingParent(path) + "/"); } /* @@ -451,13 +512,14 @@ void handleFileUpload() { /* * 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 details + * and if it fails, return a 404 page with debug information */ void handleNotFound() { if (!fsOK) { return returnFail("FS INIT ERROR"); } if (!handleFileRead(server.uri())) { + // Dump debug data String message = "Error: File not found\n\n"; message += "URI: "; message += server.uri(); @@ -467,8 +529,9 @@ void handleNotFound() { 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"; + message += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; } + message += String("path=") + server.arg("path") + "\n"; DBG_OUTPUT_PORT.print(message); return returnNotFound(message); } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 75d3be9c1d..149614d9ed 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -202,14 +202,79 @@ height: 28px; font: normal 14px Verdana, Arial, Sans-serif; } + + /* Spinner based on from https://github.com/jlong/css-spinners */ + #loading { + position:absolute; + // note: changing between block and none cancels the opacity anim, so we move it in and out of screen instead by changing the top value + // ? TODO: see https://www.impressivewebs.com/animate-display-block-none/ for another approach + display:block; + top: -100vh; + left: 0; + width: 100vw; + height: 100vh; + z-index: 100; + background-color: rgba(0, 0, 0, 0.5); + opacity: 0; + transition: opacity 500ms ease-in-out; + } + #loading.shown { + top: 0; + opacity: 1; + } + + #loading-msg { + display: inline-block; + position: relative; + top: 0px; + left: 50%; + transform: translateX(-50%); + color:white; + font: normal 32px Verdana, Arial, Sans-serif; + } + + @keyframes spinner-anim { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } + /* :not(:required) hides this rule from IE9 and below */ + .spinner-anim:not(:required) { + display: inline-block; + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: spinner-anim 1s infinite linear; + border: 16px solid #eee; + border-right-color: transparent; + border-radius: 32px; + box-sizing: border-box; + overflow: hidden; + text-indent: -9999px; + width: 64px; + height: 64px; + } - - - -
- - - - -
-
-
-
- - \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico b/libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico similarity index 100% rename from libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico rename to libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico From f541b6d0866a1e366d8210388251c9d4672ea70a Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 02:34:18 +0200 Subject: [PATCH 057/111] Renaming project --- .../examples/WebFileManager/{WebFileManager.ino => FSBrowser.ino} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libraries/ESP8266WebServer/examples/WebFileManager/{WebFileManager.ino => FSBrowser.ino} (100%) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino rename to libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino From 917dacdef372a9960bbdfb95febb0a9ce6999274 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 02:57:00 +0200 Subject: [PATCH 058/111] Renamed project --- .../examples/FSBrowser/FSBrowser.ino | 688 ++++++++++++------ ...o~f541b6d0866a1e366d8210388251c9d4672ea70a | 311 ++++++++ .../data/edit/index.htm | 0 .../data/edit/index.htm.gz | Bin .../data/favicon.ico | Bin .../data/index.htm | 0 .../data/pins.png | Bin .../feathericons.png | Bin .../{WebFileManager => FSBrowser}/readme.md | 0 .../examples/WebFileManager/FSBrowser.ino | 587 --------------- 10 files changed, 793 insertions(+), 793 deletions(-) create mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/edit/index.htm (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/edit/index.htm.gz (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/favicon.ico (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/index.htm (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/data/pins.png (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/feathericons.png (100%) rename libraries/ESP8266WebServer/examples/{WebFileManager => FSBrowser}/readme.md (100%) delete mode 100644 libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index bd25fad582..66c5882f7d 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -1,5 +1,8 @@ + + /* - FSWebServer - Example WebServer with SPIFFS backend for esp8266 + WebFileManager - A web-based File Manager for ESP8266 filesystems + Copyright (c) 2015 Hristo Gochkov. All rights reserved. This file is part of the ESP8266WebServer library for Arduino environment. @@ -7,30 +10,56 @@ 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 + See readme.md for more information. - access the sample web page at http://esp8266fs.local - edit the page by going to http://esp8266fs.local/edit */ + +//////////////////////////////// + +// Select the FileSystem by uncommenting one of the lines below + +//#define USE_SPIFFS +//#define USE_LITTLEFS +//#define USE_SDFS + +//////////////////////////////// + #include #include #include #include -#include -#include +#include + +#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,270 +70,517 @@ FS* filesystem = &LittleFS; const char* ssid = STASSID; const char* password = STAPSK; -const char* host = "esp8266fs"; +const char* host = "webfilemanager"; 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; + + +//////////////////////////////// +// Utils to return HTTP codes, determine content-type and URLdecode params of GET method + +void returnOK() { + server.send(200, "text/plain", ""); +} + +void returnOKWithMsg(String msg) { + server.send(200, "text/plain", msg); +} + +void returnNotFound(String msg) { + server.send(404, "text/plain", msg); +} + +void returnFail(String msg) { + DBG_OUTPUT_PORT.println(msg); + server.send(500, "text/plain", msg + "\r\n"); } String getContentType(String filename) { - if (server.hasArg("download")) { - return "application/octet-stream"; - } else if (filename.endsWith(".htm")) { - return "text/html"; - } else if (filename.endsWith(".html")) { - return "text/html"; - } else if (filename.endsWith(".css")) { - return "text/css"; - } else if (filename.endsWith(".js")) { - return "application/javascript"; - } else if (filename.endsWith(".png")) { - return "image/png"; - } else if (filename.endsWith(".gif")) { - return "image/gif"; - } else if (filename.endsWith(".jpg")) { - return "image/jpeg"; - } else if (filename.endsWith(".ico")) { - return "image/x-icon"; - } else if (filename.endsWith(".xml")) { - return "text/xml"; - } else if (filename.endsWith(".pdf")) { - return "application/x-pdf"; - } else if (filename.endsWith(".zip")) { - return "application/x-zip"; - } else if (filename.endsWith(".gz")) { - return "application/x-gzip"; - } + if (filename.endsWith(".htm")) return "text/html"; + if (filename.endsWith(".html")) return "text/html"; + if (filename.endsWith(".css")) return "text/css"; + if (filename.endsWith(".js")) return "application/javascript"; + if (filename.endsWith(".png")) return "image/png"; + if (filename.endsWith(".gif")) return "image/gif"; + if (filename.endsWith(".jpg")) return "image/jpeg"; + if (filename.endsWith(".jpeg")) return "image/jpeg"; + if (filename.endsWith(".ico")) return "image/x-icon"; + if (filename.endsWith(".xml")) return "text/xml"; + if (filename.endsWith(".pdf")) return "application/x-pdf"; + if (filename.endsWith(".zip")) return "application/x-zip"; + if (filename.endsWith(".gz")) return "application/x-gzip"; return "text/plain"; } +unsigned char h2int(char c) { + if (c >= '0' && c <='9') return((unsigned char)c - '0'); + if (c >= 'a' && c <='f') return((unsigned char)c - 'a' + 10); + if (c >= 'A' && c <='F') return((unsigned char)c - 'A' + 10); + return(0); +} + +String urlDecode(String str) { + String decodedString=""; + char c; + char code0; + char code1; + for (int i =0; i < str.length(); i++) { + c=str.charAt(i); + if (c == '+') { + decodedString+=' '; + } + else if (c == '%') { + i++; + code0=str.charAt(i); + i++; + code1=str.charAt(i); + c = (h2int(code0) << 4) | h2int(code1); + decodedString+=c; + } + else { + decodedString+=c; + } + } + + return decodedString; +} + + +//////////////////////////////// +// Request handlers + +/* + * Return the FS type, status and size info + */ +void handleStatus() { + FSInfo fs_info; + String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; + + if (fsOK) { + fileSystem->info(fs_info); + json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; + } + else { + json += "\"false\""; + } + json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; + json += "}"; + server.send(200, "application/json", json); +} + + +/* + * Return the list of files in the directory specified by the "dir" query string parameter + */ +void handleFileList() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); + } + + if (!server.hasArg("dir")) { + return returnFail("BAD ARGS"); + } + + String path = urlDecode(server.arg("dir")); + if (path != "/" && !fileSystem->exists(path)) { + return returnFail("BAD PATH"); + } + + DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); + Dir dir = fileSystem->openDir(path); + path = String(); + + String output = "["; + while (dir.next()) { +#ifdef USE_SPIFFS + String error = getFileError(dir.fileName()); + if (error.length() > 0) { + DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); + continue; + } +#endif + if (output != "[") output += ','; + output += "{\"type\":\""; + if (dir.isDirectory()) output += "dir"; + else output += String("file\",\"size\":\"") + dir.fileSize(); + + output += "\",\"name\":\""; + // Always return names without leading "/" + if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); + else output += dir.fileName(); + + output += "\"}"; + } + + output += "]"; + server.send(200, "text/json", output); +} + + +/* + * Read the given file from the filesystem and stream it back to the client + */ bool handleFileRead(String path) { - DBG_OUTPUT_PORT.println("handleFileRead: " + path); + DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); + if (!fsOK) { + returnFail("FS INIT ERROR"); + return true; + } + if (path.endsWith("/")) { path += "index.htm"; } - String contentType = getContentType(path); + + String contentType; + if (server.hasArg("download")) { + contentType = "application/octet-stream"; + } else { + contentType = getContentType(path); + } + String pathWithGz = path + ".gz"; - if (filesystem->exists(pathWithGz) || filesystem->exists(path)) { - if (filesystem->exists(pathWithGz)) { + if (fileSystem->exists(pathWithGz) || fileSystem->exists(path)) { + if (fileSystem->exists(pathWithGz)) { path += ".gz"; } - File file = filesystem->open(path, "r"); - server.streamFile(file, contentType); + 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; } -void handleFileUpload() { - if (server.uri() != "/edit") { - return; + +/* + * 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 returnFail("FS INIT ERROR"); } - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - String filename = upload.filename; - if (!filename.startsWith("/")) { - filename = "/" + filename; + if (server.args() == 0 || server.args() > 2) { + return returnFail("BAD ARGS"); + } + + String path = server.arg("path"); + if (path == "") { + return returnFail("MISSING PATH ARG"); + } + +#ifdef USE_SPIFFS + if (getFileError(path).length() > 0) { + return returnFail("INVALID FILENAME"); + } +#endif + + if (path == "/") { + return returnFail("BAD PATH"); + } + if (fileSystem->exists(path)) { + return returnFail("PATH FILE EXISTS"); + } + + if (server.args() == 1) { + // One argument: create + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); + if (path.endsWith("/")) { + // Create a folder + path.remove(path.length() - 1); + if (!fileSystem->mkdir(path)) { + return returnFail("MKDIR FAILED"); + } + } + else { + // Create a file + File file = fileSystem->open(path, "w"); + if (file) { + file.write((const char *)0); + file.close(); + } + else { + return returnFail("CREATE FAILED"); + } + } + if (path.lastIndexOf("/") > -1) path = path.substring(0, path.lastIndexOf("/")); + returnOKWithMsg(path); + } + else { + // Two arguments: rename + String src = server.arg("src"); + if (src == "") { + return returnFail("MISSING SRC ARG"); } - 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); + if (src == "/") { + return returnFail("BAD SRC"); } - } else if (upload.status == UPLOAD_FILE_END) { - if (fsUploadFile) { - fsUploadFile.close(); + if (!fileSystem->exists(src)) { + return returnFail("SRC FILE NOT FOUND"); } - DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); + + 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 returnFail("RENAME FAILED"); + } + returnOKWithMsg(lastExistingParent(src)); } } -void handleFileDelete() { - if (server.args() == 0) { - return server.send(500, "text/plain", "BAD ARGS"); + +/* + * 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 + */ +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; } - String path = server.arg(0); - DBG_OUTPUT_PORT.println("handleFileDelete: " + path); - if (path == "/") { - return server.send(500, "text/plain", "BAD PATH"); + + // Otherwise delete its contents first + Dir dir = fileSystem->openDir(path); + + while (dir.next()) { + deleteRecursive(path + "/" + dir.fileName()); } - if (!filesystem->exists(path)) { - return server.send(404, "text/plain", "FileNotFound"); + + // Then delete the folder itself + fileSystem->rmdir(path); +} + + +// TODO test delete and move on SPIFFS +// 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 != "" && !fileSystem->exists(path)) { + if (path.lastIndexOf("/") > 0) path = path.substring(0, path.lastIndexOf("/")); + else path = String(); // No slash => the top folder does not exist } - filesystem->remove(path); - server.send(200, "text/plain", ""); - path.clear(); + DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path); + return path; } -void handleFileCreate() { +/* + * 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 returnFail("FS INIT ERROR"); + } if (server.args() == 0) { - return server.send(500, "text/plain", "BAD ARGS"); + return returnFail("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(); - } else { - return server.send(500, "text/plain", "CREATE FAILED"); + DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); + if (path == "/" || !fileSystem->exists(path)) { + return returnFail("BAD PATH"); } - server.send(200, "text/plain", ""); - path.clear(); + deleteRecursive(path); + + returnOKWithMsg(lastExistingParent(path)); } -void handleFileList() { - if (!server.hasArg("dir")) { - server.send(500, "text/plain", "BAD ARGS"); - return; +/* + * Handle a file upload request + */ +void handleFileUpload() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); } - - String path = server.arg("dir"); - DBG_OUTPUT_PORT.println("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")); + if (server.uri() != "/edit") { return; } - - // use the same string for every line - String output; - output.reserve(64); - while (dir.next()) { - - if (output.length()) { - // send string from previous iteration - // as an HTTP chunk - server.sendContent(output); - output = ','; - } else { - output = '['; + 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"); + DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); + } + else if (upload.status == UPLOAD_FILE_WRITE) { + if (uploadFile) { + uploadFile.write(upload.buf, upload.currentSize); } + 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); + } +} - File entry = dir.openFile("r"); - bool isDir = false; - output += "{\"type\":\""; - output += (isDir) ? "dir" : "file"; - output += "\",\"name\":\""; - if (entry.name()[0] == '/') { - output += &(entry.name()[1]); - } else { - output += entry.name(); + +/* + * 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 returnFail("FS INIT ERROR"); + } + String uri = urlDecode(server.uri()); + if (!handleFileRead(uri)) { + // Dump debug data + String message = "Error: File not found\n\n"; + message += "URI: "; + message += 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 += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; } - output += "\"}"; - entry.close(); + message += String("path=") + server.arg("path") + "\n"; + DBG_OUTPUT_PORT.print(message); + return returnNotFound(message); } +} - // send last string - output += "]"; - server.sendContent(output); - server.chunkedResponseFinalize(); +/* + * Checks filename for unsupported combinations + * Returns an empty String if supported, or detail of error(s) if unsupported + */ +String getFileError(String path) { + String error = String(); + if (!path.startsWith("/")) error += "!NO_LEADING_SLASH! "; + if (path.indexOf("//") != -1) error += "!DOUBLE_SLASH! "; + if (path.endsWith("/")) error += "!TRAILING_SLASH! "; + return error; } + 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.printf("\n"); - } + DBG_OUTPUT_PORT.print("\n"); + //////////////////////////////// + // FILESYSTEM INIT - //WIFI INIT - DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); - if (String(WiFi.SSID()) != String(ssid)) { - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - } + fileSystemConfig.setAutoFormat(false); + fileSystem->setConfig(fileSystemConfig); + fsOK = fileSystem->begin(); + DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "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("List of files at root of filesystem:"); + while (dir.next()) { + String error = getFileError(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.println(); + + // Keep the "unsupportedFiles" variable to show it, but clean it up + unsupportedFiles.replace("\n", "
"); + unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); +#endif + + //////////////////////////////// + // WI-FI INIT + DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); + 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.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"); - - - //SERVER INIT - //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"); - } - }); + //////////////////////////////// + // MDNS INIT + if (MDNS.begin(host)) { + MDNS.addService("http", "tcp", 80); + DBG_OUTPUT_PORT.print("Open http://"); + DBG_OUTPUT_PORT.print(host); + DBG_OUTPUT_PORT.println(".local/edit to open the file manager"); + } - //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(); + //////////////////////////////// + // WEB SERVER INIT + + // Filesystem status + server.on("/status",HTTP_GET, handleStatus); + + // List directory + server.on("/list", HTTP_GET, handleFileList); + + // Load editor + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit/index.htm")) returnNotFound("FileNotFound"); }); + + // 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, returnOK, 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/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a new file mode 100644 index 0000000000..bd25fad582 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a @@ -0,0 +1,311 @@ +/* + FSWebServer - Example WebServer with SPIFFS 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 + + 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 +*/ +#include +#include +#include +#include +#include +#include + +//FS* filesystem = &SPIFFS; +FS* filesystem = &LittleFS; + +#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 = "esp8266fs"; + +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"; + } +} + +String getContentType(String filename) { + if (server.hasArg("download")) { + return "application/octet-stream"; + } else if (filename.endsWith(".htm")) { + return "text/html"; + } else if (filename.endsWith(".html")) { + return "text/html"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } + return "text/plain"; +} + +bool handleFileRead(String path) { + DBG_OUTPUT_PORT.println("handleFileRead: " + path); + if (path.endsWith("/")) { + path += "index.htm"; + } + 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; + } + 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); + } +} + +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(); + } else { + return server.send(500, "text/plain", "CREATE FAILED"); + } + server.send(200, "text/plain", ""); + path.clear(); +} + +void handleFileList() { + if (!server.hasArg("dir")) { + server.send(500, "text/plain", "BAD ARGS"); + return; + } + + String path = server.arg("dir"); + DBG_OUTPUT_PORT.println("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")); + return; + } + + // use the same string for every line + String output; + output.reserve(64); + while (dir.next()) { + + if (output.length()) { + // send string from previous iteration + // as an HTTP chunk + server.sendContent(output); + output = ','; + } else { + 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]); + } else { + output += entry.name(); + } + output += "\"}"; + entry.close(); + } + + // send last string + output += "]"; + server.sendContent(output); + server.chunkedResponseFinalize(); +} + +void setup(void) { + 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.printf("\n"); + } + + + //WIFI INIT + DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); + if (String(WiFi.SSID()) != String(ssid)) { + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + } + + 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.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"); + + + //SERVER INIT + //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(); + }); + server.begin(); + DBG_OUTPUT_PORT.println("HTTP server started"); + +} + +void loop(void) { + server.handleClient(); + MDNS.update(); +} diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm rename to libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm.gz rename to libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico b/libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/favicon.ico rename to libraries/ESP8266WebServer/examples/FSBrowser/data/favicon.ico diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/index.htm rename to libraries/ESP8266WebServer/examples/FSBrowser/data/index.htm diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png b/libraries/ESP8266WebServer/examples/FSBrowser/data/pins.png similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/data/pins.png rename to libraries/ESP8266WebServer/examples/FSBrowser/data/pins.png diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/feathericons.png b/libraries/ESP8266WebServer/examples/FSBrowser/feathericons.png similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/feathericons.png rename to libraries/ESP8266WebServer/examples/FSBrowser/feathericons.png diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md similarity index 100% rename from libraries/ESP8266WebServer/examples/WebFileManager/readme.md rename to libraries/ESP8266WebServer/examples/FSBrowser/readme.md diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino b/libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino deleted file mode 100644 index 66c5882f7d..0000000000 --- a/libraries/ESP8266WebServer/examples/WebFileManager/FSBrowser.ino +++ /dev/null @@ -1,587 +0,0 @@ - - -/* - WebFileManager - A web-based File Manager for ESP8266 filesystems - - 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 - - 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 - -//////////////////////////////// - -#include -#include -#include -#include -#include - -#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 - - -#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 = "webfilemanager"; - -ESP8266WebServer server(80); - -static bool fsOK; -String unsupportedFiles = String(); - -File uploadFile; - - -//////////////////////////////// -// Utils to return HTTP codes, determine content-type and URLdecode params of GET method - -void returnOK() { - server.send(200, "text/plain", ""); -} - -void returnOKWithMsg(String msg) { - server.send(200, "text/plain", msg); -} - -void returnNotFound(String msg) { - server.send(404, "text/plain", msg); -} - -void returnFail(String msg) { - DBG_OUTPUT_PORT.println(msg); - server.send(500, "text/plain", msg + "\r\n"); -} - -String getContentType(String filename) { - if (filename.endsWith(".htm")) return "text/html"; - if (filename.endsWith(".html")) return "text/html"; - if (filename.endsWith(".css")) return "text/css"; - if (filename.endsWith(".js")) return "application/javascript"; - if (filename.endsWith(".png")) return "image/png"; - if (filename.endsWith(".gif")) return "image/gif"; - if (filename.endsWith(".jpg")) return "image/jpeg"; - if (filename.endsWith(".jpeg")) return "image/jpeg"; - if (filename.endsWith(".ico")) return "image/x-icon"; - if (filename.endsWith(".xml")) return "text/xml"; - if (filename.endsWith(".pdf")) return "application/x-pdf"; - if (filename.endsWith(".zip")) return "application/x-zip"; - if (filename.endsWith(".gz")) return "application/x-gzip"; - return "text/plain"; -} - -unsigned char h2int(char c) { - if (c >= '0' && c <='9') return((unsigned char)c - '0'); - if (c >= 'a' && c <='f') return((unsigned char)c - 'a' + 10); - if (c >= 'A' && c <='F') return((unsigned char)c - 'A' + 10); - return(0); -} - -String urlDecode(String str) { - String decodedString=""; - char c; - char code0; - char code1; - for (int i =0; i < str.length(); i++) { - c=str.charAt(i); - if (c == '+') { - decodedString+=' '; - } - else if (c == '%') { - i++; - code0=str.charAt(i); - i++; - code1=str.charAt(i); - c = (h2int(code0) << 4) | h2int(code1); - decodedString+=c; - } - else { - decodedString+=c; - } - } - - return decodedString; -} - - -//////////////////////////////// -// Request handlers - -/* - * Return the FS type, status and size info - */ -void handleStatus() { - FSInfo fs_info; - String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; - - if (fsOK) { - fileSystem->info(fs_info); - json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; - } - else { - json += "\"false\""; - } - json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; - json += "}"; - server.send(200, "application/json", json); -} - - -/* - * Return the list of files in the directory specified by the "dir" query string parameter - */ -void handleFileList() { - if (!fsOK) { - return returnFail("FS INIT ERROR"); - } - - if (!server.hasArg("dir")) { - return returnFail("BAD ARGS"); - } - - String path = urlDecode(server.arg("dir")); - if (path != "/" && !fileSystem->exists(path)) { - return returnFail("BAD PATH"); - } - - DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); - Dir dir = fileSystem->openDir(path); - path = String(); - - String output = "["; - while (dir.next()) { -#ifdef USE_SPIFFS - String error = getFileError(dir.fileName()); - if (error.length() > 0) { - DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); - continue; - } -#endif - if (output != "[") output += ','; - output += "{\"type\":\""; - if (dir.isDirectory()) output += "dir"; - else output += String("file\",\"size\":\"") + dir.fileSize(); - - output += "\",\"name\":\""; - // Always return names without leading "/" - if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); - else output += dir.fileName(); - - output += "\"}"; - } - - output += "]"; - server.send(200, "text/json", output); -} - - -/* - * 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) { - returnFail("FS INIT ERROR"); - return true; - } - - if (path.endsWith("/")) { - path += "index.htm"; - } - - String contentType; - if (server.hasArg("download")) { - contentType = "application/octet-stream"; - } else { - 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"); - if (server.streamFile(file, contentType) != file.size()) { - DBG_OUTPUT_PORT.println("Sent less data than expected!"); - } - file.close(); - return true; - } - return false; -} - - -/* - * 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 returnFail("FS INIT ERROR"); - } - if (server.args() == 0 || server.args() > 2) { - return returnFail("BAD ARGS"); - } - - String path = server.arg("path"); - if (path == "") { - return returnFail("MISSING PATH ARG"); - } - -#ifdef USE_SPIFFS - if (getFileError(path).length() > 0) { - return returnFail("INVALID FILENAME"); - } -#endif - - if (path == "/") { - return returnFail("BAD PATH"); - } - if (fileSystem->exists(path)) { - return returnFail("PATH FILE EXISTS"); - } - - if (server.args() == 1) { - // One argument: create - DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); - if (path.endsWith("/")) { - // Create a folder - path.remove(path.length() - 1); - if (!fileSystem->mkdir(path)) { - return returnFail("MKDIR FAILED"); - } - } - else { - // Create a file - File file = fileSystem->open(path, "w"); - if (file) { - file.write((const char *)0); - file.close(); - } - else { - return returnFail("CREATE FAILED"); - } - } - if (path.lastIndexOf("/") > -1) path = path.substring(0, path.lastIndexOf("/")); - returnOKWithMsg(path); - } - else { - // Two arguments: rename - String src = server.arg("src"); - if (src == "") { - return returnFail("MISSING SRC ARG"); - } - if (src == "/") { - return returnFail("BAD SRC"); - } - if (!fileSystem->exists(src)) { - return returnFail("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 returnFail("RENAME FAILED"); - } - returnOKWithMsg(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 - */ -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); -} - - -// TODO test delete and move on SPIFFS -// 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 != "" && !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 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 returnFail("FS INIT ERROR"); - } - if (server.args() == 0) { - return returnFail("BAD ARGS"); - } - String path = server.arg(0); - DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); - if (path == "/" || !fileSystem->exists(path)) { - return returnFail("BAD PATH"); - } - deleteRecursive(path); - - returnOKWithMsg(lastExistingParent(path)); -} - -/* - * Handle a file upload request - */ -void handleFileUpload() { - if (!fsOK) { - return returnFail("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"); - DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); - } - else if (upload.status == UPLOAD_FILE_WRITE) { - if (uploadFile) { - uploadFile.write(upload.buf, upload.currentSize); - } - 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 returnFail("FS INIT ERROR"); - } - String uri = urlDecode(server.uri()); - if (!handleFileRead(uri)) { - // Dump debug data - String message = "Error: File not found\n\n"; - message += "URI: "; - message += 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 += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; - } - message += String("path=") + server.arg("path") + "\n"; - DBG_OUTPUT_PORT.print(message); - return returnNotFound(message); - } -} - -/* - * Checks filename for unsupported combinations - * Returns an empty String if supported, or detail of error(s) if unsupported - */ -String getFileError(String path) { - String error = String(); - if (!path.startsWith("/")) error += "!NO_LEADING_SLASH! "; - if (path.indexOf("//") != -1) error += "!DOUBLE_SLASH! "; - if (path.endsWith("/")) error += "!TRAILING_SLASH! "; - return error; -} - - -void setup(void) { - //////////////////////////////// - // SERIAL INIT - DBG_OUTPUT_PORT.begin(115200); - DBG_OUTPUT_PORT.setDebugOutput(true); - DBG_OUTPUT_PORT.print("\n"); - - //////////////////////////////// - // FILESYSTEM INIT - - fileSystemConfig.setAutoFormat(false); - fileSystem->setConfig(fileSystemConfig); - fsOK = fileSystem->begin(); - DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "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("List of files at root of filesystem:"); - while (dir.next()) { - String error = getFileError(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.println(); - - // Keep the "unsupportedFiles" variable to show it, but clean it up - unsupportedFiles.replace("\n", "
"); - unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); -#endif - - //////////////////////////////// - // WI-FI INIT - DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); - 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.println(WiFi.localIP()); - - //////////////////////////////// - // MDNS INIT - if (MDNS.begin(host)) { - MDNS.addService("http", "tcp", 80); - DBG_OUTPUT_PORT.print("Open http://"); - DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local/edit to open the file manager"); - } - - //////////////////////////////// - // WEB SERVER INIT - - // Filesystem status - server.on("/status",HTTP_GET, handleStatus); - - // List directory - server.on("/list", HTTP_GET, handleFileList); - - // Load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit/index.htm")) returnNotFound("FileNotFound"); - }); - - // 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, returnOK, 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(); -} From 86dfa58d61e9f014c26e89bd3fa80276caf1f171 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:02:06 +0200 Subject: [PATCH 059/111] Renamed project --- .../ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 66c5882f7d..491cf9591f 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -1,7 +1,5 @@ - - /* - WebFileManager - A web-based File Manager for ESP8266 filesystems + 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. @@ -70,7 +68,7 @@ const char* ssid = STASSID; const char* password = STAPSK; -const char* host = "webfilemanager"; +const char* host = "fsbrowser"; ESP8266WebServer server(80); @@ -543,7 +541,7 @@ void setup(void) { MDNS.addService("http", "tcp", 80); DBG_OUTPUT_PORT.print("Open http://"); DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local/edit to open the file manager"); + DBG_OUTPUT_PORT.println(".local/edit to open the FileSystem Browser"); } //////////////////////////////// From 86b54a41a2788fc5960b78f0fce5846a70626bc9 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:07:58 +0200 Subject: [PATCH 060/111] Small changes --- .../examples/FSBrowser/readme.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 4a6216b8b1..e41a973ad2 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -2,7 +2,7 @@ ## What is this sketch about ? -This example is a FileSystem browser for the ESP8266 using http requests and a html/javascript frontend, +This example is a FileSystem Browser for the ESP8266 using http requests and a html/javascript frontend, working for both 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. @@ -10,11 +10,12 @@ This unified version is based on the previous examples named FSWebServer, FSBrow 1. Uncomment one of the "#define USE_xxx" directives in the sketch 2. Add the credentials of your WiFi network (search for "STASSID") -3. Copy the contents of the 'data' folder to the filesystem. To do so: +3. Compile and upload the sketch to your ESP8266 device +4. Copy the contents of the 'data' folder to the filesystem. To do so: - for SDFS, copy that 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 -4. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit -5. If you need to free as much space as possible, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total usage of less than 7KB +5. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit +6. If you need as much free space as possible on your filesystem, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total FS usage of less than 7KB ## Notes - See https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html for more information on FileSystems supported by the ESP8266. @@ -63,7 +64,7 @@ This unified version is based on the previous examples named FSWebServer, FSBrow - ? 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 @@ -100,7 +101,7 @@ This unified version is based on the previous examples named FSWebServer, FSBrow - Create nested file '/My folder/My test file.txt' and delete file 'My test file.txt' ## Credits -- Icons are from https://feathericons.com/ . The resulting PNG is passed first through https://compresspng.com/ before converting it using https://www.base64-image.de/ -- Spinner is based on https://github.com/jlong/css-spinners -- Minifiying of index.htm done by : https://css-minifier.com/ (css) + https://www.javascriptminifier.com/ (js) + https://www.willpeavy.com/tools/minifier/ (html) +- 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 by : https://css-minifier.com/ (css) + https://www.javascriptminifier.com/ (js) + https://www.willpeavy.com/tools/minifier/ (html) From 1c5d10f68dbae17cd14c9332c7ed6806464d022c Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:19:57 +0200 Subject: [PATCH 061/111] Add a note about the ace.js dependency --- libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index e41a973ad2..cc6a8d5e30 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -17,6 +17,9 @@ This unified version is based on the previous examples named FSWebServer, FSBrow 5. Once the data and sketch have been uploaded, access the editor by pointing your browser to http://fsbrowser.local/edit 6. If you need as much free space as possible on your filesystem, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total FS usage of less than 7KB +## Dependency +The html page uses the ace.js text editor which is hosted on a CDN, so Internet access from your web browser is required for the FSBrowser to work. + ## 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 the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to From 170f3fba3461bfc062d85a947fb8b203f06a8073 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 2 Apr 2020 03:24:06 +0200 Subject: [PATCH 062/111] Minor changes --- .../ESP8266WebServer/examples/FSBrowser/readme.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index cc6a8d5e30..6da1e3f26b 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -22,14 +22,14 @@ The html page uses the ace.js text editor which is hosted on a CDN, so Internet ## 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 the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to +- For SDFS, if your card's CS pin is not connected the default pin (4), enable the "fileSystemConfig.setCSPin(chipSelectPin);" line, specifying the GPIO the CS pin is connected to - index.htm is the default index (works on subfolders as well) -- Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - 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 +- 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. -## Changeslog since original FSBrowser +## Changelog since original FSBrowser ### Fixes to work on LittleFS based on SDFS - #define logic to select FS @@ -57,8 +57,7 @@ The html page uses the ace.js text editor which is hosted on a CDN, so Internet - Added "Rename/Move" feature to context menu ## TODO (maybe) -- ? Is there a case where we need to limit files to 8.3 ? FAT16 maybe ? -- ? If so, how can we query the fatType of the SDFS (FAT16 or FAT32) +- ? 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) From ef26c497481da608aa43b0c802ff77cc070a3c1b Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 00:41:28 +0200 Subject: [PATCH 063/111] Define LittleFS by default. If both uncompressed and gz versions exist, use uncompressed version. Small fixes. --- libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 6da1e3f26b..18a7a2dfa6 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -39,14 +39,14 @@ The html page uses the ace.js text editor which is hosted on a CDN, so Internet - 'FILE_WRITE' was not declared in this scope > replaced by "w" ### UI/usability improvements -- Never format filesystem, just return "FS INIT ERROR" when FS could not be mounted +- 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 an icon for files, and indented them to the same level as folders -- Changed file/folder icon set to use lighter and more neutral ones, and added specific "text" and "image" icons for formats recognized as such +- 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 opertation status +- 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 From 780db417b3130a2d9e8a700286a90cb943ca8e11 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 00:46:02 +0200 Subject: [PATCH 064/111] Define LittleFS by default. If both uncompressed and gz versions exist, use uncompressed version. Small fixes. --- .../examples/FSBrowser/FSBrowser.ino | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 491cf9591f..29cf398159 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -27,7 +27,7 @@ // Select the FileSystem by uncommenting one of the lines below //#define USE_SPIFFS -//#define USE_LITTLEFS +#define USE_LITTLEFS //#define USE_SDFS //////////////////////////////// @@ -240,12 +240,12 @@ bool handleFileRead(String path) { } else { contentType = getContentType(path); } - - String pathWithGz = path + ".gz"; - if (fileSystem->exists(pathWithGz) || fileSystem->exists(path)) { - if (fileSystem->exists(pathWithGz)) { - path += ".gz"; - } + + 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!"); @@ -253,6 +253,7 @@ bool handleFileRead(String path) { file.close(); return true; } + return false; } @@ -375,9 +376,10 @@ void deleteRecursive(String path) { } -// TODO test delete and move on SPIFFS -// 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 +/* + * 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 != "" && !fileSystem->exists(path)) { if (path.lastIndexOf("/") > 0) path = path.substring(0, path.lastIndexOf("/")); From 697f133515365da141239d039b283b8f455420b8 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 00:55:47 +0200 Subject: [PATCH 065/111] Restyled version --- .../examples/FSBrowser/FSBrowser.ino | 361 ++++++++++-------- 1 file changed, 204 insertions(+), 157 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 29cf398159..12f3368292 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -1,5 +1,5 @@ /* - FSBrowser - A web-based FileSystem Browser for ESP8266 filesystems + 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. @@ -39,23 +39,23 @@ #include #if defined USE_SPIFFS - #include - const char* fsName = "SPIFFS"; - FS* fileSystem = &SPIFFS; - SPIFFSConfig fileSystemConfig = SPIFFSConfig(); +#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(); +#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. +#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 @@ -99,62 +99,92 @@ void returnFail(String msg) { } String getContentType(String filename) { - if (filename.endsWith(".htm")) return "text/html"; - if (filename.endsWith(".html")) return "text/html"; - if (filename.endsWith(".css")) return "text/css"; - if (filename.endsWith(".js")) return "application/javascript"; - if (filename.endsWith(".png")) return "image/png"; - if (filename.endsWith(".gif")) return "image/gif"; - if (filename.endsWith(".jpg")) return "image/jpeg"; - if (filename.endsWith(".jpeg")) return "image/jpeg"; - if (filename.endsWith(".ico")) return "image/x-icon"; - if (filename.endsWith(".xml")) return "text/xml"; - if (filename.endsWith(".pdf")) return "application/x-pdf"; - if (filename.endsWith(".zip")) return "application/x-zip"; - if (filename.endsWith(".gz")) return "application/x-gzip"; + if (filename.endsWith(".htm")) { + return "text/html"; + } + if (filename.endsWith(".html")) { + return "text/html"; + } + if (filename.endsWith(".css")) { + return "text/css"; + } + if (filename.endsWith(".js")) { + return "application/javascript"; + } + if (filename.endsWith(".png")) { + return "image/png"; + } + if (filename.endsWith(".gif")) { + return "image/gif"; + } + if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } + if (filename.endsWith(".jpeg")) { + return "image/jpeg"; + } + if (filename.endsWith(".ico")) { + return "image/x-icon"; + } + if (filename.endsWith(".xml")) { + return "text/xml"; + } + if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } + if (filename.endsWith(".zip")) { + return "application/x-zip"; + } + if (filename.endsWith(".gz")) { + return "application/x-gzip"; + } return "text/plain"; } unsigned char h2int(char c) { - if (c >= '0' && c <='9') return((unsigned char)c - '0'); - if (c >= 'a' && c <='f') return((unsigned char)c - 'a' + 10); - if (c >= 'A' && c <='F') return((unsigned char)c - 'A' + 10); - return(0); + if (c >= '0' && c <= '9') { + return ((unsigned char)c - '0'); + } + if (c >= 'a' && c <= 'f') { + return ((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <= 'F') { + return ((unsigned char)c - 'A' + 10); + } + return (0); } -String urlDecode(String str) { - String decodedString=""; - char c; - char code0; - char code1; - for (int i =0; i < str.length(); i++) { - c=str.charAt(i); - if (c == '+') { - decodedString+=' '; - } - else if (c == '%') { - i++; - code0=str.charAt(i); - i++; - code1=str.charAt(i); - c = (h2int(code0) << 4) | h2int(code1); - decodedString+=c; - } - else { - decodedString+=c; - } +String urlDecode(String str) { + String decodedString = ""; + char c; + char code0; + char code1; + for (int i = 0; i < str.length(); i++) { + c = str.charAt(i); + if (c == '+') { + decodedString += ' '; + } else if (c == '%') { + i++; + code0 = str.charAt(i); + i++; + code1 = str.charAt(i); + c = (h2int(code0) << 4) | h2int(code1); + decodedString += c; + } else { + decodedString += c; } - - return decodedString; + } + + return decodedString; } //////////////////////////////// // Request handlers -/* - * Return the FS type, status and size info - */ +/* + Return the FS type, status and size info +*/ void handleStatus() { FSInfo fs_info; String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; @@ -162,8 +192,7 @@ void handleStatus() { if (fsOK) { fileSystem->info(fs_info); json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; - } - else { + } else { json += "\"false\""; } json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; @@ -172,9 +201,9 @@ void handleStatus() { } -/* - * Return the list of files in the directory specified by the "dir" query string parameter - */ +/* + Return the list of files in the directory specified by the "dir" query string parameter +*/ void handleFileList() { if (!fsOK) { return returnFail("FS INIT ERROR"); @@ -185,10 +214,10 @@ void handleFileList() { } String path = urlDecode(server.arg("dir")); - if (path != "/" && !fileSystem->exists(path)) { + if (path != "/" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } - + DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = fileSystem->openDir(path); path = String(); @@ -202,15 +231,23 @@ void handleFileList() { continue; } #endif - if (output != "[") output += ','; + if (output != "[") { + output += ','; + } output += "{\"type\":\""; - if (dir.isDirectory()) output += "dir"; - else output += String("file\",\"size\":\"") + dir.fileSize(); + if (dir.isDirectory()) { + output += "dir"; + } else { + output += String("file\",\"size\":\"") + dir.fileSize(); + } output += "\",\"name\":\""; // Always return names without leading "/" - if (dir.fileName()[0] == '/') output += &(dir.fileName()[1]); - else output += dir.fileName(); + if (dir.fileName()[0] == '/') { + output += &(dir.fileName()[1]); + } else { + output += dir.fileName(); + } output += "\"}"; } @@ -221,8 +258,8 @@ void handleFileList() { /* - * Read the given file from the filesystem and stream it back to the client - */ + 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) { @@ -233,7 +270,7 @@ bool handleFileRead(String path) { if (path.endsWith("/")) { path += "index.htm"; } - + String contentType; if (server.hasArg("download")) { contentType = "application/octet-stream"; @@ -244,8 +281,8 @@ bool handleFileRead(String path) { if (!fileSystem->exists(path)) { // File not found, try gzip version path = path + ".gz"; - } - if (fileSystem->exists(path)) { + } + 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!"); @@ -259,16 +296,16 @@ bool handleFileRead(String 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 - */ + 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 returnFail("FS INIT ERROR"); @@ -276,7 +313,7 @@ void handleFileCreate() { if (server.args() == 0 || server.args() > 2) { return returnFail("BAD ARGS"); } - + String path = server.arg("path"); if (path == "") { return returnFail("MISSING PATH ARG"); @@ -294,9 +331,9 @@ void handleFileCreate() { if (fileSystem->exists(path)) { return returnFail("PATH FILE EXISTS"); } - + if (server.args() == 1) { - // One argument: create + // One argument: create DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); if (path.endsWith("/")) { // Create a folder @@ -304,22 +341,21 @@ void handleFileCreate() { if (!fileSystem->mkdir(path)) { return returnFail("MKDIR FAILED"); } - } - else { + } else { // Create a file File file = fileSystem->open(path, "w"); if (file) { file.write((const char *)0); file.close(); - } - else { + } else { return returnFail("CREATE FAILED"); } } - if (path.lastIndexOf("/") > -1) path = path.substring(0, path.lastIndexOf("/")); + if (path.lastIndexOf("/") > -1) { + path = path.substring(0, path.lastIndexOf("/")); + } returnOKWithMsg(path); - } - else { + } else { // Two arguments: rename String src = server.arg("src"); if (src == "") { @@ -331,11 +367,11 @@ void handleFileCreate() { if (!fileSystem->exists(src)) { return returnFail("SRC FILE NOT FOUND"); } - + DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); if (path.endsWith("/")) { - path.remove(path.length() - 1); + path.remove(path.length() - 1); } if (src.endsWith("/")) { src.remove(src.length() - 1); @@ -349,21 +385,21 @@ void handleFileCreate() { /* - * 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 - */ + 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 +*/ 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); @@ -376,26 +412,29 @@ void deleteRecursive(String path) { } -/* - * 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 - */ +/* + 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 != "" && !fileSystem->exists(path)) { - if (path.lastIndexOf("/") > 0) path = path.substring(0, path.lastIndexOf("/")); - else path = String(); // No slash => the top folder does not exist + 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 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 - */ + 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 returnFail("FS INIT ERROR"); @@ -414,8 +453,8 @@ void handleFileDelete() { } /* - * Handle a file upload request - */ + Handle a file upload request +*/ void handleFileUpload() { if (!fsOK) { return returnFail("FS INIT ERROR"); @@ -433,14 +472,12 @@ void handleFileUpload() { DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); - } - else if (upload.status == UPLOAD_FILE_WRITE) { + } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { uploadFile.write(upload.buf, upload.currentSize); } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); - } - else if (upload.status == UPLOAD_FILE_END) { + } else if (upload.status == UPLOAD_FILE_END) { if (uploadFile) { uploadFile.close(); } @@ -449,11 +486,11 @@ void handleFileUpload() { } -/* - * 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 - */ +/* + 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 returnFail("FS INIT ERROR"); @@ -479,14 +516,20 @@ void handleNotFound() { } /* - * Checks filename for unsupported combinations - * Returns an empty String if supported, or detail of error(s) if unsupported - */ + Checks filename for unsupported combinations + Returns an empty String if supported, or detail of error(s) if unsupported +*/ String getFileError(String path) { String error = String(); - if (!path.startsWith("/")) error += "!NO_LEADING_SLASH! "; - if (path.indexOf("//") != -1) error += "!DOUBLE_SLASH! "; - if (path.endsWith("/")) error += "!TRAILING_SLASH! "; + if (!path.startsWith("/")) { + error += "!NO_LEADING_SLASH! "; + } + if (path.indexOf("//") != -1) { + error += "!DOUBLE_SLASH! "; + } + if (path.endsWith("/")) { + error += "!TRAILING_SLASH! "; + } return error; } @@ -507,20 +550,22 @@ void setup(void) { DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "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("List of files at root of filesystem:"); - while (dir.next()) { - String error = getFileError(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.println(); - - // Keep the "unsupportedFiles" variable to show it, but clean it up - unsupportedFiles.replace("\n", "
"); - unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); + // Debug: dump on console contents of filessytem with no filter and check filenames validity + Dir dir = fileSystem->openDir(""); + DBG_OUTPUT_PORT.println("List of files at root of filesystem:"); + while (dir.next()) { + String error = getFileError(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.println(); + + // Keep the "unsupportedFiles" variable to show it, but clean it up + unsupportedFiles.replace("\n", "
"); + unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); #endif //////////////////////////////// @@ -532,7 +577,7 @@ void setup(void) { 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.println(WiFi.localIP()); @@ -550,22 +595,24 @@ void setup(void) { // WEB SERVER INIT // Filesystem status - server.on("/status",HTTP_GET, handleStatus); - + server.on("/status", HTTP_GET, handleStatus); + // List directory - server.on("/list", HTTP_GET, handleFileList); - + server.on("/list", HTTP_GET, handleFileList); + // Load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit/index.htm")) returnNotFound("FileNotFound"); + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit/index.htm")) { + returnNotFound("FileNotFound"); + } }); - + // 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 From 612b8caadbf6d718d4a1514a7027de0267bb61d3 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 14:33:53 +0200 Subject: [PATCH 066/111] (dummy edit to retrigger broken CI) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 12f3368292..b63d8dde2d 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -19,7 +19,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA See readme.md for more information. - */ //////////////////////////////// From 180705a87998b6b6ef6c4c3fdaad1a694f44ebae Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 22:14:46 +0200 Subject: [PATCH 067/111] Using unsigned int for comparison with String.length() --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index b63d8dde2d..57012be850 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -158,7 +158,7 @@ String urlDecode(String str) { char c; char code0; char code1; - for (int i = 0; i < str.length(); i++) { + for (unsigned int i = 0; i < str.length(); i++) { c = str.charAt(i); if (c == '+') { decodedString += ' '; From c4b73698e91227901ed28ce1b4c0538e74375c66 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 3 Apr 2020 23:35:40 +0200 Subject: [PATCH 068/111] Return an error when upload fails (e.g. filesystem full) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 57012be850..62b891ed1b 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -473,7 +473,10 @@ void handleFileUpload() { DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { - uploadFile.write(upload.buf, upload.currentSize); + size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize); + if (bytesWritten != upload.currentSize) { + return returnFail("WRITE FAILED"); + } } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); } else if (upload.status == UPLOAD_FILE_END) { From b993004ff52a26aa1a001533b47b67d3d9d8de35 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Mon, 6 Apr 2020 13:27:34 +0200 Subject: [PATCH 069/111] Trying to reorder functions to please CI --- .../examples/FSBrowser/FSBrowser.ino | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 62b891ed1b..39183a3929 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -25,8 +25,8 @@ // Select the FileSystem by uncommenting one of the lines below -//#define USE_SPIFFS -#define USE_LITTLEFS +#define USE_SPIFFS +//#define USE_LITTLEFS //#define USE_SDFS //////////////////////////////// @@ -294,6 +294,22 @@ bool handleFileRead(String path) { } +/* + 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 != "" && !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 @@ -411,22 +427,6 @@ void deleteRecursive(String path) { } -/* - 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 != "" && !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 a file deletion request Operation | req.responseText From 109ca0cbc512ec293f92db4fc282cfbaa62084c5 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Mon, 6 Apr 2020 19:18:09 +0200 Subject: [PATCH 070/111] Reordered functions to please CI. --- .../examples/FSBrowser/FSBrowser.ino | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 39183a3929..2dc8a07eed 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -178,6 +178,25 @@ String urlDecode(String str) { } +/* + Checks filename for unsupported combinations + Returns an empty String if supported, or detail of error(s) if unsupported +*/ +String getFileError(String path) { + String error = String(); + if (!path.startsWith("/")) { + error += "!NO_LEADING_SLASH! "; + } + if (path.indexOf("//") != -1) { + error += "!DOUBLE_SLASH! "; + } + if (path.endsWith("/")) { + error += "!TRAILING_SLASH! "; + } + return error; +} + + //////////////////////////////// // Request handlers @@ -185,6 +204,7 @@ String urlDecode(String str) { Return the FS type, status and size info */ void handleStatus() { + DBG_OUTPUT_PORT.println("handleStatus"); FSInfo fs_info; String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; @@ -517,24 +537,6 @@ void handleNotFound() { } } -/* - Checks filename for unsupported combinations - Returns an empty String if supported, or detail of error(s) if unsupported -*/ -String getFileError(String path) { - String error = String(); - if (!path.startsWith("/")) { - error += "!NO_LEADING_SLASH! "; - } - if (path.indexOf("//") != -1) { - error += "!DOUBLE_SLASH! "; - } - if (path.endsWith("/")) { - error += "!TRAILING_SLASH! "; - } - return error; -} - void setup(void) { //////////////////////////////// From d6a835009e19856c441722333227b4f37af06e6e Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 8 Apr 2020 01:42:54 +0200 Subject: [PATCH 071/111] Update to use chunked response API --- .../examples/FSBrowser/FSBrowser.ino | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 2dc8a07eed..ef2db76a6e 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -221,7 +221,8 @@ void handleStatus() { /* - Return the list of files in the directory specified by the "dir" query string parameter + 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) { @@ -241,7 +242,15 @@ void handleFileList() { Dir dir = fileSystem->openDir(path); path = String(); - String output = "["; + // 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")); + return; + } + + // use the same string for every line + String output; + output.reserve(64); while (dir.next()) { #ifdef USE_SPIFFS String error = getFileError(dir.fileName()); @@ -250,9 +259,15 @@ void handleFileList() { continue; } #endif - if (output != "[") { - output += ','; + if (output.length()) { + // send string from previous iteration + // as an HTTP chunk + server.sendContent(output); + output = ','; + } else { + output = '['; } + output += "{\"type\":\""; if (dir.isDirectory()) { output += "dir"; @@ -271,8 +286,10 @@ void handleFileList() { output += "\"}"; } + // send last string output += "]"; - server.send(200, "text/json", output); + server.sendContent(output); + server.chunkedResponseFinalize(); } From c3a33e685b0c6c172b62658510d4169944e59ad3 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 8 Apr 2020 01:45:49 +0200 Subject: [PATCH 072/111] Removed temp files commited by mistake --- .../examples/FSBrowser/FSBrowser.ino.orig | 638 ------------------ ...o~f541b6d0866a1e366d8210388251c9d4672ea70a | 311 --------- 2 files changed, 949 deletions(-) delete mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino.orig delete mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino.orig b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino.orig deleted file mode 100644 index 2dc8a07eed..0000000000 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino.orig +++ /dev/null @@ -1,638 +0,0 @@ -/* - 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. - - 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 - - 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 - -//////////////////////////////// - -#include -#include -#include -#include -#include - -#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 - - -#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 = "fsbrowser"; - -ESP8266WebServer server(80); - -static bool fsOK; -String unsupportedFiles = String(); - -File uploadFile; - - -//////////////////////////////// -// Utils to return HTTP codes, determine content-type and URLdecode params of GET method - -void returnOK() { - server.send(200, "text/plain", ""); -} - -void returnOKWithMsg(String msg) { - server.send(200, "text/plain", msg); -} - -void returnNotFound(String msg) { - server.send(404, "text/plain", msg); -} - -void returnFail(String msg) { - DBG_OUTPUT_PORT.println(msg); - server.send(500, "text/plain", msg + "\r\n"); -} - -String getContentType(String filename) { - if (filename.endsWith(".htm")) { - return "text/html"; - } - if (filename.endsWith(".html")) { - return "text/html"; - } - if (filename.endsWith(".css")) { - return "text/css"; - } - if (filename.endsWith(".js")) { - return "application/javascript"; - } - if (filename.endsWith(".png")) { - return "image/png"; - } - if (filename.endsWith(".gif")) { - return "image/gif"; - } - if (filename.endsWith(".jpg")) { - return "image/jpeg"; - } - if (filename.endsWith(".jpeg")) { - return "image/jpeg"; - } - if (filename.endsWith(".ico")) { - return "image/x-icon"; - } - if (filename.endsWith(".xml")) { - return "text/xml"; - } - if (filename.endsWith(".pdf")) { - return "application/x-pdf"; - } - if (filename.endsWith(".zip")) { - return "application/x-zip"; - } - if (filename.endsWith(".gz")) { - return "application/x-gzip"; - } - return "text/plain"; -} - -unsigned char h2int(char c) { - if (c >= '0' && c <= '9') { - return ((unsigned char)c - '0'); - } - if (c >= 'a' && c <= 'f') { - return ((unsigned char)c - 'a' + 10); - } - if (c >= 'A' && c <= 'F') { - return ((unsigned char)c - 'A' + 10); - } - return (0); -} - -String urlDecode(String str) { - String decodedString = ""; - char c; - char code0; - char code1; - for (unsigned int i = 0; i < str.length(); i++) { - c = str.charAt(i); - if (c == '+') { - decodedString += ' '; - } else if (c == '%') { - i++; - code0 = str.charAt(i); - i++; - code1 = str.charAt(i); - c = (h2int(code0) << 4) | h2int(code1); - decodedString += c; - } else { - decodedString += c; - } - } - - return decodedString; -} - - -/* - Checks filename for unsupported combinations - Returns an empty String if supported, or detail of error(s) if unsupported -*/ -String getFileError(String path) { - String error = String(); - if (!path.startsWith("/")) { - error += "!NO_LEADING_SLASH! "; - } - if (path.indexOf("//") != -1) { - error += "!DOUBLE_SLASH! "; - } - if (path.endsWith("/")) { - error += "!TRAILING_SLASH! "; - } - return error; -} - - -//////////////////////////////// -// Request handlers - -/* - Return the FS type, status and size info -*/ -void handleStatus() { - DBG_OUTPUT_PORT.println("handleStatus"); - FSInfo fs_info; - String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; - - if (fsOK) { - fileSystem->info(fs_info); - json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; - } else { - json += "\"false\""; - } - json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; - json += "}"; - server.send(200, "application/json", json); -} - - -/* - Return the list of files in the directory specified by the "dir" query string parameter -*/ -void handleFileList() { - if (!fsOK) { - return returnFail("FS INIT ERROR"); - } - - if (!server.hasArg("dir")) { - return returnFail("BAD ARGS"); - } - - String path = urlDecode(server.arg("dir")); - if (path != "/" && !fileSystem->exists(path)) { - return returnFail("BAD PATH"); - } - - DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); - Dir dir = fileSystem->openDir(path); - path = String(); - - String output = "["; - while (dir.next()) { -#ifdef USE_SPIFFS - String error = getFileError(dir.fileName()); - if (error.length() > 0) { - DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); - continue; - } -#endif - if (output != "[") { - output += ','; - } - output += "{\"type\":\""; - if (dir.isDirectory()) { - output += "dir"; - } else { - output += String("file\",\"size\":\"") + dir.fileSize(); - } - - output += "\",\"name\":\""; - // Always return names without leading "/" - if (dir.fileName()[0] == '/') { - output += &(dir.fileName()[1]); - } else { - output += dir.fileName(); - } - - output += "\"}"; - } - - output += "]"; - server.send(200, "text/json", output); -} - - -/* - 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) { - returnFail("FS INIT ERROR"); - return true; - } - - if (path.endsWith("/")) { - path += "index.htm"; - } - - String contentType; - if (server.hasArg("download")) { - contentType = "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 != "" && !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 returnFail("FS INIT ERROR"); - } - if (server.args() == 0 || server.args() > 2) { - return returnFail("BAD ARGS"); - } - - String path = server.arg("path"); - if (path == "") { - return returnFail("MISSING PATH ARG"); - } - -#ifdef USE_SPIFFS - if (getFileError(path).length() > 0) { - return returnFail("INVALID FILENAME"); - } -#endif - - if (path == "/") { - return returnFail("BAD PATH"); - } - if (fileSystem->exists(path)) { - return returnFail("PATH FILE EXISTS"); - } - - if (server.args() == 1) { - // One argument: create - DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); - if (path.endsWith("/")) { - // Create a folder - path.remove(path.length() - 1); - if (!fileSystem->mkdir(path)) { - return returnFail("MKDIR FAILED"); - } - } else { - // Create a file - File file = fileSystem->open(path, "w"); - if (file) { - file.write((const char *)0); - file.close(); - } else { - return returnFail("CREATE FAILED"); - } - } - if (path.lastIndexOf("/") > -1) { - path = path.substring(0, path.lastIndexOf("/")); - } - returnOKWithMsg(path); - } else { - // Two arguments: rename - String src = server.arg("src"); - if (src == "") { - return returnFail("MISSING SRC ARG"); - } - if (src == "/") { - return returnFail("BAD SRC"); - } - if (!fileSystem->exists(src)) { - return returnFail("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 returnFail("RENAME FAILED"); - } - returnOKWithMsg(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 -*/ -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 returnFail("FS INIT ERROR"); - } - if (server.args() == 0) { - return returnFail("BAD ARGS"); - } - String path = server.arg(0); - DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); - if (path == "/" || !fileSystem->exists(path)) { - return returnFail("BAD PATH"); - } - deleteRecursive(path); - - returnOKWithMsg(lastExistingParent(path)); -} - -/* - Handle a file upload request -*/ -void handleFileUpload() { - if (!fsOK) { - return returnFail("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"); - 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 returnFail("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 returnFail("FS INIT ERROR"); - } - String uri = urlDecode(server.uri()); - if (!handleFileRead(uri)) { - // Dump debug data - String message = "Error: File not found\n\n"; - message += "URI: "; - message += 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 += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; - } - message += String("path=") + server.arg("path") + "\n"; - DBG_OUTPUT_PORT.print(message); - return returnNotFound(message); - } -} - - -void setup(void) { - //////////////////////////////// - // SERIAL INIT - DBG_OUTPUT_PORT.begin(115200); - DBG_OUTPUT_PORT.setDebugOutput(true); - DBG_OUTPUT_PORT.print("\n"); - - //////////////////////////////// - // FILESYSTEM INIT - - fileSystemConfig.setAutoFormat(false); - fileSystem->setConfig(fileSystemConfig); - fsOK = fileSystem->begin(); - DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "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("List of files at root of filesystem:"); - while (dir.next()) { - String error = getFileError(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.println(); - - // Keep the "unsupportedFiles" variable to show it, but clean it up - unsupportedFiles.replace("\n", "
"); - unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5); -#endif - - //////////////////////////////// - // WI-FI INIT - DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); - 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.println(WiFi.localIP()); - - //////////////////////////////// - // MDNS INIT - if (MDNS.begin(host)) { - MDNS.addService("http", "tcp", 80); - DBG_OUTPUT_PORT.print("Open http://"); - DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local/edit to open the FileSystem Browser"); - } - - //////////////////////////////// - // WEB SERVER INIT - - // Filesystem status - server.on("/status", HTTP_GET, handleStatus); - - // List directory - server.on("/list", HTTP_GET, handleFileList); - - // Load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit/index.htm")) { - returnNotFound("FileNotFound"); - } - }); - - // 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, returnOK, 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/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a deleted file mode 100644 index bd25fad582..0000000000 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino~f541b6d0866a1e366d8210388251c9d4672ea70a +++ /dev/null @@ -1,311 +0,0 @@ -/* - FSWebServer - Example WebServer with SPIFFS 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 - - 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 -*/ -#include -#include -#include -#include -#include -#include - -//FS* filesystem = &SPIFFS; -FS* filesystem = &LittleFS; - -#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 = "esp8266fs"; - -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"; - } -} - -String getContentType(String filename) { - if (server.hasArg("download")) { - return "application/octet-stream"; - } else if (filename.endsWith(".htm")) { - return "text/html"; - } else if (filename.endsWith(".html")) { - return "text/html"; - } else if (filename.endsWith(".css")) { - return "text/css"; - } else if (filename.endsWith(".js")) { - return "application/javascript"; - } else if (filename.endsWith(".png")) { - return "image/png"; - } else if (filename.endsWith(".gif")) { - return "image/gif"; - } else if (filename.endsWith(".jpg")) { - return "image/jpeg"; - } else if (filename.endsWith(".ico")) { - return "image/x-icon"; - } else if (filename.endsWith(".xml")) { - return "text/xml"; - } else if (filename.endsWith(".pdf")) { - return "application/x-pdf"; - } else if (filename.endsWith(".zip")) { - return "application/x-zip"; - } else if (filename.endsWith(".gz")) { - return "application/x-gzip"; - } - return "text/plain"; -} - -bool handleFileRead(String path) { - DBG_OUTPUT_PORT.println("handleFileRead: " + path); - if (path.endsWith("/")) { - path += "index.htm"; - } - 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; - } - 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); - } -} - -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(); - } else { - return server.send(500, "text/plain", "CREATE FAILED"); - } - server.send(200, "text/plain", ""); - path.clear(); -} - -void handleFileList() { - if (!server.hasArg("dir")) { - server.send(500, "text/plain", "BAD ARGS"); - return; - } - - String path = server.arg("dir"); - DBG_OUTPUT_PORT.println("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")); - return; - } - - // use the same string for every line - String output; - output.reserve(64); - while (dir.next()) { - - if (output.length()) { - // send string from previous iteration - // as an HTTP chunk - server.sendContent(output); - output = ','; - } else { - 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]); - } else { - output += entry.name(); - } - output += "\"}"; - entry.close(); - } - - // send last string - output += "]"; - server.sendContent(output); - server.chunkedResponseFinalize(); -} - -void setup(void) { - 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.printf("\n"); - } - - - //WIFI INIT - DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); - if (String(WiFi.SSID()) != String(ssid)) { - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - } - - 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.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"); - - - //SERVER INIT - //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(); - }); - server.begin(); - DBG_OUTPUT_PORT.println("HTTP server started"); - -} - -void loop(void) { - server.handleClient(); - MDNS.update(); -} From 16b932972ecce2763d2b8bad62c5b677fdde2ae4 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 9 Apr 2020 02:54:35 +0200 Subject: [PATCH 073/111] Avoid using args() as requested --- .../examples/FSBrowser/FSBrowser.ino | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index ef2db76a6e..6aaa7eb3fc 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -362,9 +362,6 @@ void handleFileCreate() { if (!fsOK) { return returnFail("FS INIT ERROR"); } - if (server.args() == 0 || server.args() > 2) { - return returnFail("BAD ARGS"); - } String path = server.arg("path"); if (path == "") { @@ -384,8 +381,9 @@ void handleFileCreate() { return returnFail("PATH FILE EXISTS"); } - if (server.args() == 1) { - // One argument: create + String src = server.arg("src"); + if (src == "") { + // No source specified: creation DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); if (path.endsWith("/")) { // Create a folder @@ -408,11 +406,7 @@ void handleFileCreate() { } returnOKWithMsg(path); } else { - // Two arguments: rename - String src = server.arg("src"); - if (src == "") { - return returnFail("MISSING SRC ARG"); - } + // Source specified: rename if (src == "/") { return returnFail("BAD SRC"); } @@ -475,10 +469,12 @@ void handleFileDelete() { if (!fsOK) { return returnFail("FS INIT ERROR"); } - if (server.args() == 0) { + + String path = server.arg(0); + if (path == "") { return returnFail("BAD ARGS"); } - String path = server.arg(0); + DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); if (path == "/" || !fileSystem->exists(path)) { return returnFail("BAD PATH"); From b0fa0967e536f7cdc76679bc0d48274d6ab600b7 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 9 Apr 2020 02:59:20 +0200 Subject: [PATCH 074/111] Use html entity for non-breaking space to avoid losing char when minifying --- .../ESP8266WebServer/examples/FSBrowser/data/edit/index.htm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm index a47934a08a..2edd603142 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm @@ -302,7 +302,7 @@ status += "" - + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + "\xa0free\xa0of\xa0" + readableSize(fsInfo.totalBytes) + + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + " free of " + readableSize(fsInfo.totalBytes) + ""; if (fsInfo.unsupportedFiles) { status += " WARNING" From 50f2a73fe1b43efded155b0c36d277c551b218fe Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Thu, 9 Apr 2020 03:14:46 +0200 Subject: [PATCH 075/111] Script to preprocess index.htm --- .../examples/FSBrowser/extras/reduce_index.sh | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh 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..7ee9f8ddff --- /dev/null +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -0,0 +1,57 @@ +#/bin/sh + +# Requires xdd which is part of the VIM package +# Requires html-minifier +# sudo npm install html-minifier -g +# Requires npm +# sudo apt install npm +# ln -s /usr/bin/nodejs /usr/bin/node + +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 + +xxd index.htm.gz > index_htm.h +if [ $? -ne 0 ] +then + echo "Error creating include file for index.htm" + exit -1 +fi + +echo Reduce complete. +echo index.htm.gz can be used instead of data/edit/index.htm to save FileSystem space +echo Alternately, you can leave the filesystem untouched by embedding index.htm +echo inside the code. To do so, uncomment the line +echo " #define BUILT_IN_INDEX_HTM" +echo in FSBrowser.ino From 88968dfd83647d265dd90c18259353f5fbca207d Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 11 Apr 2020 01:09:52 +0200 Subject: [PATCH 076/111] (reformated code) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 6aaa7eb3fc..cfe8c1c04e 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -474,7 +474,7 @@ void handleFileDelete() { if (path == "") { return returnFail("BAD ARGS"); } - + DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); if (path == "/" || !fileSystem->exists(path)) { return returnFail("BAD PATH"); From 1e0424006dcc9b8f36da61e79c9dc066c6d7d4a1 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 11 Apr 2020 01:33:00 +0200 Subject: [PATCH 077/111] (comments) --- .../examples/FSBrowser/extras/reduce_index.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh index 7ee9f8ddff..65d7d013c2 100755 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -1,11 +1,11 @@ #/bin/sh # Requires xdd which is part of the VIM package -# Requires html-minifier -# sudo npm install html-minifier -g # 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 \ @@ -53,5 +53,5 @@ echo Reduce complete. echo index.htm.gz can be used instead of data/edit/index.htm to save FileSystem space echo Alternately, you can leave the filesystem untouched by embedding index.htm echo inside the code. To do so, uncomment the line -echo " #define BUILT_IN_INDEX_HTM" +echo " #define INCLUDE_FALLBACK_INDEX_HTM" echo in FSBrowser.ino From dd3fe6da52bfea08f5e2d2e0b7b7b67ea71a78fa Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 11 Apr 2020 01:37:12 +0200 Subject: [PATCH 078/111] Preprocessed files --- .../examples/FSBrowser/extras/index.htm.gz | Bin 0 -> 6012 bytes .../examples/FSBrowser/extras/index_htm.h | 376 ++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz create mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h 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 0000000000000000000000000000000000000000..07f3212b16c4e076ca5ff847d768b5b24f24c953 GIT binary patch literal 6012 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 qvQ_m}0*RUNHq}xHk<....U*5.eI.Q.. +00000090: 4aa5 e170 a80f 9775 c7eb 966e af4b 2306 J..p...u...n.K#. +000000a0: abc2 1687 cd22 4dad d44d 6aca 5b9b 7c3f ....."M..Mj.[.|? +000000b0: 0bd9 5d03 db5b 9b94 500b 6f1d 100b 4b03 ..]..[..P.o...K. +000000c0: 64a3 2ef6 364b a26f d3a7 63d6 8b4d 820c d...6K.o..c..M.. +000000d0: bfed 6198 de72 ccf1 6bc7 b169 d127 2fb8 ..a..r..k..i.'/. +000000e0: 56a9 baa3 3aff eca0 01b1 c6b5 3bec 9900 V...:.......;... +000000f0: 45db f108 b2b4 1bd8 bbe8 638f 7426 7a1b E.........c.t&z. +00000100: 66e1 113d c376 f0fa 5224 b689 47b5 e572 f..=.v..R$..G..r +00000110: b9ee 3a02 bd1a 6af9 8e15 505c b770 87d6 ..:...j...P\.p.. +00000120: 5601 70cb f14c ecd5 2aee 4882 2162 4a1f V.p..L..*.H.!bJ. +00000130: 5656 56ea 2dd4 ee77 3d27 b0cd 62db b11c VVV.-..w='..b... +00000140: aff6 a1b3 cafe d64d e2bb 161a d76c c7c6 .......M.....l.. +00000150: b076 54f4 7bc8 7486 b5b2 5496 2a65 00e2 .vT.{.t...T.*e.. +00000160: 755b 4829 6bec afbe a2d6 5364 5422 3286 u[H)k.....SdT"2. +00000170: 9874 7bb4 b65e 2e67 3096 02eb d522 3e4c .t{..^.g0....">L +00000180: 672c 113b 50c7 ad95 05b2 e5fa 0079 5d62 g,.;P........y]b +00000190: 43c3 45a6 49ec 6e6d 6abd 455e 6342 3d6c C.E.I.nmj.E^cB=l +000001a0: 214a 9e71 7d40 ece2 9098 b457 5b03 f4ea !J.q}@.....W[... +000001b0: edc0 f381 1cd7 21b0 cecb aef7 5d64 bf86 ......!.....]d.. +000001c0: e432 2644 b412 db22 362e b62c a7dd 8ff7 .2&D..."6..,.... +000001d0: 5e73 47d3 bbd7 7ace 33f6 5e13 de71 30f9 ^sG...z.3.^..q0. +000001e0: b332 9b61 8c61 96ef 8322 61fc 4cf0 10a6 .2.a.a..."a.L... +000001f0: 69d9 0ee0 4d4c 75c2 8829 764d 0121 b61b i...MLu..)vM.!.. +00000200: d0d7 59d9 3b2e 6a13 3ae6 ec4b cd7f 9d92 ..Y.;.j.:..K.... +00000210: 5571 e0bc 1403 d02c d02e 0bb7 a990 0848 Uq.....,.......H +00000220: afd5 2774 7660 ba63 0a99 34bd e572 6756 ..'tv`.c..4..rgV +00000230: 10d3 9343 6e32 ce15 4ddc 763c c4a9 00b6 ...Cn2..M.v<.... +00000240: 628f c923 8fd8 8285 5ad8 2a00 b322 1649 b..#....Z.*..".I +00000250: ec6f b5ca 8535 33fd 7f60 625a a1a7 85c0 .o...53..`bZ.... +00000260: 8069 397d b55a 0b77 1c0f bfbe 4904 dfa2 .i9}.Z.w....I... +00000270: 06f0 51cb c2a6 402d 5a61 e20e 0a2c 1a8b ..Q...@-Za...,.. +00000280: 425f cb5d dcee e176 1f9b 801c 5562 486a B_.]...v....UbHj +00000290: 1a6d ae94 3f8b b796 239a 94d6 069e a598 .m..?...#....... +000002a0: 88a2 1a19 80ab 2ab9 7617 fc81 8fd7 5634 ......*.v.....V4 +000002b0: 72b7 7b71 3d2c 9f1c 769d 1df8 737e d3ec r.{q=,..v...s~.. +000002c0: 359a 5d68 edb2 cfbd eede ce19 6b8c 5747 5.]h........k.WG +000002d0: c301 6bb4 2abb 6777 8dbb a376 637c 3f58 ..k.*.gw...vc|?X +000002e0: 5dbd bfaf 36d6 ae9a e6ce 5563 97a0 c3ca ]...6.....Uc.... +000002f0: 133a 5c2f 954a 1bcf e74f cb27 2f67 eb7b .:\/.J...O.'/g.{ +00000300: a3b3 d660 35e8 9cbd b44b 8f55 f3db 889e ...`5....K.U.... +00000310: a243 e2ac 6e74 1fef 3f3d 7d3e b8be 3bf8 .C..nt..?=}>..;. +00000320: fce9 b651 3a7d 6917 9efd d6b9 f970 b7be ...Q:}i......p.. +00000330: 52b8 ec3d 9b47 96df baaf f6dd b56f ebcf R..=.G.......o.. +00000340: a72f e38d e7b3 e0fc f6a5 b3fc b1d0 73d6 ./............s. +00000350: 9ae3 8be6 de41 eff1 be69 adda cfeb 8592 .....A...i...... +00000360: bb86 fb57 a474 78cd 70dc 79b8 beb9 b5ce ...W.tx.p.y..... +00000370: 76ae 2e5a 8f9f 192d 37d7 9fae 1b07 cd9b v..Z...-7....... +00000380: b30e ed7f a4e3 21d9 b9ea 39cd 9dde ddce ......!...9..... +00000390: f093 fb6d fffa 5b07 3d3f 6fbc 58c1 f949 ...m..[.=?o.X..I +000003a0: 3ff0 1a4f dee3 7201 afaf 7c22 2fa7 76f5 ?..O..r...|"/.v. +000003b0: be32 203b 2723 b777 b17b 869b 8fdf d682 .2 ;'#.w.{...... +000003c0: a3d2 cef2 ceda daa3 7bbd 7bfd edd3 6774 ........{.{...gt +000003d0: 76bc 4186 fe5d 69cf 3c5b dba9 8e9e 56ed v.A..]i.<[....V. +000003e0: e3a6 7f52 2e91 dbe6 a577 783e 289f 35f7 ...R.....wx>(.5. +000003f0: 8ffb f8b0 f469 7525 b820 7dbf f5b1 f7d0 .....iu%. }..... +00000400: 5b5d bdec 1d1f 3f75 4eba e6a7 dba3 9bc6 []....?uN....... +00000410: b85a 3ad8 2faf 3d0c 1c73 e3fc 62e8 79c3 .Z:./.=..s..b.y. +00000420: 83a6 7f35 78f8 7cf4 b9d7 c3fd a35d b23b ...5x.|......].; +00000430: 7e3c 76d0 f1a7 9da7 cb8d e6cd 3dd9 dbd9 ~..gk... +000004c0: c3fe a57f 853e a1c1 b17b f5b2 e78d 87bd .....>...{...... +000004d0: fd0a b9ad 3ef4 fdd3 bda1 5f3a fefc bcf1 ....>....._:.... +000004e0: d9e9 acd3 eaea a3d5 e762 ba69 de5d 5c9f .........b.i.]\. +000004f0: acee 3d1e 1f1b aa64 3b45 0fbb 18d1 3f41 ..=....d;E....?A +00000500: f773 ba62 0bcf 8d01 3d11 c02a 1003 eabc .s.b....=..*.... +00000510: 3bdd 014e 8b92 36b2 8ac8 225d bb36 20a6 ;..N..6..."].6 . +00000520: 69e5 7a96 94c9 1563 275d d980 185a cef3 i.z....c']...Z.. +00000530: 9d73 6717 5736 66dc 1b5b a1d3 117d 63d5 .sg.W6f..[...}c. +00000540: da4a fe2a 32e8 beb1 6aa3 3cb3 6aca 25b2 .J.*2...j.<.j.%. +00000550: 6067 d39a 2cd7 45d8 e54c 89dd 3173 c5cc `g..,.E..L..1s.. +00000560: 27e7 b3a9 9eb7 7159 2a2e cf3a f08c 947e '.....qY*..:...~ +00000570: 70d3 b77c 6c21 4bc6 1c44 18dc c9af 3c5b p..|l!K..D....<[ +00000580: 9344 b626 21db 9494 282a b26c c304 e86d .D.&!...(*.l...m +00000590: 5c74 c908 5b45 1eb5 6a65 f535 1b6a a3f9 \t..[E..je.5.j.. +000005a0: c806 1fcb 8147 1de6 136a 0329 4508 a403 .....G...j.)E... +000005b0: f62b 22aa e315 5b41 b743 4680 7487 d884 .+"...[A.CF.t... +000005c0: 62a9 e24f 7e8d c0f4 f1b8 e3a1 01f6 a51f b..O~........... +000005d0: 04f3 daf1 9c41 9251 4ca8 93fa 984c 3e74 .....A.QL....L>t +000005e0: fc5b 8722 2b5f fd43 1697 593e 1569 7e39 .[."+_.C..Y>.i~9 +000005f0: 3795 2c43 eec9 a278 c782 14b1 0722 c676 7.,C...x.....".v +00000600: 3d9d 1ee1 35f6 976d d7f4 b1f9 b6b1 95cb =...5..m........ +00000610: bf84 3b14 3dd1 93ec 535d 4365 1365 408b ..;.=...S]Ce.e@. +00000620: aec9 8721 f26c 20eb 3503 663a bb85 e4c2 ...!.l .5.f:.... +00000630: eee2 7a9c 9e80 a650 c7b1 2871 73d2 a728 ..z....P..(qs..( +00000640: b1ae d605 1e65 9ea5 5619 079e 894f 5ac4 .....e..V....OZ. +00000650: 6211 7d86 da38 91ee 7452 1bd5 797e 236c b.}..8..tR..y~#l +00000660: 8009 0c7b 73d8 1889 6739 caa2 45a2 c6cc ...{s...g9..E... +00000670: 3192 5698 59c6 a8a7 b0e1 4d70 4311 3b7e 1.V.Y.....MpC.;~ +00000680: 646a 0f23 c023 8701 2229 8f88 0f93 f390 dj.#.#..")...... +00000690: bfd5 8d29 9f58 0537 1321 5fe4 7385 aecc ...).X.7.!_.s... +000006a0: f085 65dc a97c f803 b395 399b f34d 5a0e ..e..|....9..MZ. +000006b0: a5ce 2041 4068 6515 c41b b18a b9c5 0f60 .. A@he........` +000006c0: aaa0 f6da 07d7 1396 f706 c488 a22c 6480 .............,d. +000006d0: 3889 57cf a20d b8c6 1bae b20d 7d8a 68e0 8.W.........}.h. +000006e0: cfd9 6725 dea6 92b2 1ebe 7b4a a8cc 337f ..g%......{J..3. +000006f0: b01c c480 ce01 5404 457e ee65 6967 5dc3 ......T.E~.eig]. +00000700: 94bd c078 a4ab f035 cbf2 74a9 b6aa 2665 ...x...5..t...&e +00000710: 413d a967 6b61 a7a4 affa 1286 2410 e015 A=.gka......$... +00000720: 9d80 c6e8 e97e cf19 daaf 4225 2208 9578 .....~....B%"..x +00000730: b838 f0bb f966 3d4f ad44 610a 52e4 4880 .8...f=O.Da.R.H. +00000740: 2f1e d478 0bca 39fc a014 6144 ada7 2c29 /..x..9...aD..,) +00000750: e11a 8f11 bf26 9ed0 7789 6d83 b360 2ef6 .....&..w.m..`.. +00000760: b5fc cb6b 02cf 0363 a158 29ab 13e6 0d66 ...k...c.X)....f +00000770: 0796 d7ca 26ee aa93 899e 8621 9270 0f7f ....&......!.p.. +00000780: 0b88 0749 f83b 54c5 f527 a38a 91f3 165d ...I.;T..'.....] +00000790: 9c2c 4dd0 9644 84f4 e6e0 ed13 cfcf f643 .,M..D.........C +000007a0: 899f 584b 1c05 53c6 b48f 0c05 cd37 7291 ..XK..S......7r. +000007b0: 07fe 251e 06f1 043e 679a 28e3 c90b d3e0 ..%....>g.(..... +000007c0: 7014 7a66 fc36 7754 4c9b 20ce 163f c21f p.zf.6wTL. ..?.. +000007d0: 5819 16d6 2b89 2af3 ac62 b3c4 6bd2 ad4d X...+.*..b..k..M +000007e0: 0891 c4a5 5bcf c893 9839 6b1d ffd8 ee38 ....[....9k....8 +000007f0: f54e 60b7 1989 928f e9a9 5014 056b 547d .N`.......P..kT} +00000800: a58b 8b10 ca81 18ac 5b4e 57a1 aa66 3aed ........[NW..f:. +00000810: 8005 30bd 8b69 43c4 b2dd f1b1 a9c8 2905 ..0..iC.......). +00000820: 9355 9d73 8a9d e418 f4fb 7759 d6f0 f67b .U.s......wY...{ +00000830: 0b61 51db 42be 7f0a 25b4 0ef6 abc8 5c93 .aQ.B...%.....\. +00000840: 65b5 f653 2b3d 3c00 46c5 8b13 7cd9 218e e..S+=<.F...|.!. +00000850: ced9 a08b 9acf c0db f210 112a d7e4 b0fa ...........*.... +00000860: 9327 3123 3cf0 b5ac b8bb 014d 56b0 fa4a .'1#<......MV..J +00000870: 3a0a deac 94ab 2baa 8769 e0d9 122e c8d2 :.....+..i...... +00000880: ae0c eaee 299c 9b46 b152 a785 82c6 e66c ....)..F.R.....l +00000890: 2ab8 64f0 c975 b51e cd07 d77e 0019 88a9 *.d..u.....~.... +000008a0: 54d5 c217 593a 21bb b226 4b67 e2e7 50fc T...Y:!..&Kg..P. +000008b0: dcf2 9f4b f8f7 eb17 fa35 8d4d c7c3 7eef ...K.....5.M..~. +000008c0: 867b 3205 547d 1e47 84af cbf0 5f56 c2d5 .{2.T}.G...._V.. +000008d0: cc39 e8ba 2ad7 05be 3624 5c0f 67a7 4794 .9..*...6$\.g.G. +000008e0: bad7 6043 d8a7 75aa 3b36 e3a9 116d ac70 ..`C..u.;6...m.p +000008f0: c2ab e5f2 8241 7501 5b65 8c65 8b1a 9e07 .....Au.[e.e.... +00000900: b453 b58e 2d1f bf32 98d8 5084 3e19 9f6e .S..-..2..P.>..n +00000910: 2ece 75d0 6f1f 2b4c 22be 0b3a 846f 415f ..u.o.+L"..:.oA_ +00000920: 5555 a763 97f1 ae28 01f3 f86c 9df8 17fd UU.c...(...l.... +00000930: 6d05 170c 7993 2591 1231 8da5 3082 2e6d m...y.%..1..0..m +00000940: a5bb 584e b224 7119 1a4b 42d5 e582 5229 ..XN.$q..KB...R) +00000950: 9c21 dad3 b923 553e 7efc cf10 6a00 9377 .!...#U>~...j..w +00000960: c714 fba5 b083 3280 bc47 550b f22f 00ba ......2..GU../.. +00000970: c460 873b 700d 3296 c2f8 bbb4 2517 322a .`.;p.2.....%.2* +00000980: 3003 a338 bd0d c05c b45b be5b 0766 63d1 0..8...\.[.[.fc. +00000990: 723a e2f7 3d58 b034 c245 fcc8 5a04 ddf6 r:..=X.4.E..Z... +000009a0: 03d7 753c 8a4d 7658 e92f 2e72 3e49 0957 ..u<.MvX./.r>I.W +000009b0: c2fc 6169 eb7e e7fa fcf8 fc30 9f1a be78 ..ai.~.....0...x +000009c0: ec53 3cf8 fd37 969b 2362 fbbf ff96 82fe .S<..7..#b...... +000009d0: fb6f 1d98 6233 475d db6c 7925 a07f 0e0a .o..b3G].ly%.... +000009e0: 33b8 aa6a 2d91 5d28 9cd9 d086 cd30 4a0c 3..j-.](.....0J. +000009f0: 7be0 3233 e78e 2dc7 3297 b68e cf8f 6fa5 {.23..-.2.....o. +00000a00: c6f5 f5c5 b5b4 10b3 e167 141d 6bf2 cde5 .........g..k... +00000a10: f1c1 c18d bc60 442c 065d 0396 cd85 32e8 .....`D,.]....2. +00000a20: 9bc4 0320 c22d 8401 c490 4504 9121 e068 ... .-....E..!.h +00000a30: 6012 2eb6 15f9 b071 0b96 590a b7d5 16ca `......q..Y..... +00000a40: 2a0c f918 34ce 0e2c 4b4d 4c35 6b1e e039 *...4..,KML5k..9 +00000a50: 9005 8595 2273 ca6a d217 b980 434b 2ac8 ...."s.j....CK*. +00000a60: 5f25 f695 b18f 0450 1bd9 cc15 9fe3 e19e _%.....P........ +00000a70: 28a6 98e1 0b67 b2b0 309f 2fe8 19ef 5270 (....g..0./...Rp +00000a80: 7c7a 743a f5fd fbc2 0288 bc43 bc81 22ef |zt:.......C..". +00000a90: f558 5eed 4bd4 91c6 4ee0 4911 1c69 482c .X^.K...N.I..iH, +00000aa0: 4b6a 4128 737c 2a91 0e1b 9598 a210 3b00 KjA(s|*.......;. +00000ab0: 4630 b5b3 b9fe 02f8 7de2 b791 67c2 2ebe F0......}...g... +00000ac0: b250 5119 2f12 acf3 a7e1 b75c d60c c6c6 .PQ./......\.... +00000ad0: 029e 2f78 3306 9b5d 91a0 002b 2e79 683d ../x3..]...+.yh= +00000ae0: 00bd c25c 06b1 0f06 ce7b d4bf 27b4 a7c8 ...\.....{..'... +00000af0: 2559 fdfe 5dc1 0634 0a58 d5b0 0ed2 4c86 %Y..]..4.X....L. +00000b00: 18c9 062c b0a0 7484 9cac 5851 d914 3f68 ...,..t...XQ..?h +00000b10: f9d4 63e1 b10c 5f60 64f4 98a5 7317 1dbe ..c..._`d...s... +00000b20: 262d 3db0 788a 8f78 c60e a1d4 66c1 94f9 &-=.x..x....f... +00000b30: 48c7 9847 19a0 8092 41b1 3e1c 5764 5e19 H..G....A.>.Wd^. +00000b40: cb90 8c70 9536 6466 acb2 86f4 41c0 ec1b ...p.6df....A... +00000b50: 2c6e a102 5fcc 7c0d 999d 14ca 9aa3 2317 ,n.._.|.......#. +00000b60: 34d7 dceb 11cb 5490 cabd bef9 ee06 a60e 4.....T......... +00000b70: 4e45 76c1 a11e f31e cd0c 7764 d906 fb12 NEv.......wd.... +00000b80: 7bb0 09ec 2b0c 9c77 c80a 3823 a7b6 35c5 {...+..w..8#..5. +00000b90: b6d6 dc6d 5b01 64f6 20c9 ba95 8e58 4d97 ...m[.d. ....XM. +00000ba0: 45a1 6960 9600 e6bf 0fcc e744 08e3 d6fc E.i`.......D.... +00000bb0: 69eb 6607 cbac 3bb5 e159 7f9f 4dcd eee7 i.f...;..Y..M... +00000bc0: 8bfd c8fb fb91 2cac 032e 9c2c 3022 80b5 ......,....,0".. +00000bd0: e702 630e 0f40 b539 eaa2 46da e5f0 c1db ..c..@.9..F..... +00000be0: b4f3 49c8 eed0 163b 78ef a3eb f13d 22bb ..I....;x....=". +00000bf0: d3bc 34f6 cc72 5997 d850 1cd8 9c42 a66c ..4..rY..P...B.l +00000c00: c8cb 501f b191 c4e6 ca80 581a 034f 6030 ..P.......X..O`0 +00000c10: 781f 8301 c720 65ca da20 8d44 e83a 58ef x.... e.. .D.:X. +00000c20: fcdd 0662 b7e0 fddd 02be 5b0f 5b2e df2a ...b......[.[..* +00000c30: 486f b52d 4f81 0d04 58f7 3d41 b982 8961 Ho.-O...X.=A...a +00000c40: 3c70 3309 5798 97aa d322 72c1 c421 c56a " +00000cc0: 0d02 0849 109a ba50 bdb1 7280 661d 75b4 ...I...P..r.f.u. +00000cd0: e220 ca5f c402 280e 2118 9910 d868 4f42 . ._..(.!....hOB +00000ce0: d252 6949 02be 7ba8 4d31 44fc 9a40 7d8f .RiI..{.M1D..@}. +00000cf0: 8b16 92d8 09f8 833f 8e29 67a4 948b ac32 .......?.)g....2 +00000d00: 852d 4887 1698 6054 6d0a 05c0 c1cb c501 .-H...`Tm....... +00000d10: f20c 304a 05c6 0773 c643 4362 5382 7920 ..0J...s.CCbS.y +00000d20: 202d b9e9 4116 d70e 5822 3199 8e54 b790 -..A...X"1..T.. +00000d30: b442 9cf2 6392 e7c5 dff0 e085 e13f d744 .B..c........?.D +00000d40: 4cf2 0c16 12ef 40de 8cff 49ac 5143 0d7d L.....@...I.QC.} +00000d50: 23fa 0bc7 389b b109 87f8 ceb2 c89f ce59 #...8..........Y +00000d60: 4da7 bbf9 0902 eb4f d9f6 2619 7425 df6b M......O..&.t%.k +00000d70: 1b4b 9034 14e4 b81a 19a0 5131 3ef4 f9a5 .K.4......Q1>... +00000d80: 2eb1 eff4 09a7 149e 7da3 803a 7529 7359 ........}..:u)sY +00000d90: 585f 9220 dd4e 0924 51bc 92b2 5dfb 2f5d X_. .N.$Q...]./] +00000da0: f9f2 dffa d782 aa6e ff47 49c7 23cc c6bf .......n.GI.#... +00000db0: 54be d6c1 6708 4f00 9649 551f 14bd dd03 T...g.O..IU..... +00000dc0: 75a3 cea9 33c4 de1e 82e2 4b55 5fdb f02b u...3.....KU_..+ +00000dd0: 5388 dd35 deea d141 d2b2 c2e6 931f 37c0 S..5...A......7. +00000de0: 618a 663b 9a16 7dbb 6ed4 f2a3 e923 0621 a.f;..}.n....#.! +00000df0: ccad 2a29 95f2 fe7c 0a5c 28f3 431c dda4 ..*)...|.\(.C... +00000e00: 85a3 6697 74c2 1669 3bb9 380d 145b 73d4 ..f.t..i;.8..[s. +00000e10: b04e 9da7 b881 057a 6b67 7c36 56eb 5316 .N.....zkg|6V.S. +00000e20: 31b5 c622 b006 67d6 5055 6b2b 8eba ad64 1.."..g.PUk+...d +00000e30: 5587 d734 0d50 c5b8 bca1 f9ae 7633 7b68 U..4.P......v3{h +00000e40: 9168 b3bf 3bde 6395 dd39 b81a 454e 3d63 .h..;.c..9..EN=c +00000e50: 009d 8e9c 7276 ad38 0e11 68d9 8056 4e51 ....rv.8..h..VNQ +00000e60: b1b8 081e 1d3a 9b9e 0548 4fd4 9a07 3f90 .....:...HO...?. +00000e70: fee6 207f 298c ff9f 863f e178 7339 cd4f .. .)....?.xs9.O +00000e80: a173 e484 583c 9e21 72df 19f2 5390 984a .s..X<.!r...S..J +00000e90: 343f 34d4 ff42 5aa9 e1bc 5106 8548 16f9 4?4..BZ...Q..H.. +00000ea0: e12a 7369 e093 6841 de8e 060c ea41 e536 .*si..hA.....A.6 +00000eb0: 7927 edcf 618a c952 8519 a65c f3d8 5a3a y'..a..R...\..Z: +00000ec0: 0304 93e2 fcdf 2cfd d010 5dcf 19b8 80bb ......,...]..... +00000ed0: 4009 4a68 a720 4341 0bc9 16d8 2e14 e30b @.Jh. CA........ +00000ee0: 065d 5ca4 0b86 b3b8 d851 1c28 bc26 ef14 .]\......Q.(.&.. +00000ef0: 2139 4cb0 54cd cad1 0c6c 619a d03f 2751 !9L.T....la..?'Q +00000f00: faeb 3462 ccf4 3c71 6916 3ba3 d5ec 99c2 ..4b...../..{{P.%... +00000f70: 16d3 9e6b cad8 1d55 6fb3 99e7 8e29 aa80 ...k...Uo....).. +00000f80: f086 5c7b c7ae d999 f5b4 6923 c8c4 6615 ..\{......i#..f. +00000f90: 7b0f 188f 5cff ef33 edb9 8e6e 1eed c642 {...\..3...n...B +00000fa0: e54f 33eb 6b71 3efe f7d9 758f db35 3b47 .O3.kq>...u..5;G +00000fb0: 9772 84d3 18c1 bfe6 ff27 d194 0545 ef1c .r.......'...E.. +00000fc0: d7e4 c8c7 57b3 e732 f363 4f7e b9f6 4f8d ....W..2.cO~..O. +00000fd0: 3df3 cf90 7298 4054 8dbc 1b7b f20b ebbf =...r.@T...{.... +00000fe0: 3cf6 a89c a8a9 4bb5 34f2 4e74 68e6 e84e <.....K.4.Nth..N +00000ff0: a7e3 637a cf8a 22ad 1d7f 1ff1 a208 bcb8 ..cz.."......... +00001000: 630f 9cc0 c74e 4033 2428 b1e7 dfb4 be7f c....N@3$(...... +00001010: 8f3f b6ac 0249 3e1f 37fd d4c7 965f 6843 .?...I>.7...._hC +00001020: 16f8 5711 9f89 b9c1 8fc5 5c2e 5687 1d11 ..W.......\.V... +00001030: b1aa df60 578d 722d 3a63 7927 510d 0f98 ...`W.r-:cy'Q... +00001040: c21a a60d f161 1b4d 5f8d b282 8e25 ea14 .....a.M_....%.. +00001050: e89e 1984 1255 ce66 b690 184a 9b64 4b99 .....U.f...J.dK. +00001060: ba88 b201 2175 b344 b666 8f8b 3527 57c1 ....!u.D.f..5'W. +00001070: 10a5 180c 80e5 e00a 234f 9d88 8982 a580 ........#O...... +00001080: 7f90 998e 7576 5c00 54ed 8b73 6285 9fa1 ....uv\.T..sb... +00001090: 43cc bdf4 1c17 7591 b863 d458 22c3 a0f1 C.....u..c.X"... +000010a0: ab04 cd49 b8ed fef5 dc9e 3e59 e74e ace5 ...I......>Y.N.. +000010b0: 403e 1097 a6f6 e222 fca7 a0e4 3052 8e5a @>....."....0R.Z +000010c0: b2fa b307 edfc 555b 225e 5367 8c0b 8b19 ......U["^Sg.... +000010d0: 83ce 9c9f cf3b 3044 91b7 5d5c ec45 92c8 .....;0D..]\.E.. +000010e0: 8f0c f1cc 6d94 0a9e 3505 4df9 6b06 e3cf ....m...5.M.k... +000010f0: 1667 252b 4e87 6555 2c05 0989 cf5e 33a7 .g%+N.eU,....^3. +00001100: de1e b06b 257e eb6c e7df 3adb 995b e7f9 ...k%~.l..:..[.. +00001110: 018a a740 d151 84b8 af09 2770 fed6 c159 ...@.Q....'p...Y +00001120: cbcd 53d0 18aa 53d4 65ee 6171 1167 8c1f ..S...S.e.aq.g.. +00001130: 3cf8 42c2 02fe 2442 f71d 8f2a d9ce e492 <.B...$B...*.... +00001140: 9f29 1103 07bf db98 9fd7 4271 dd46 16de .)........Bq.F.. +00001150: 83c8 813c 7621 ce3a d59a 983b 33c8 3aa3 ......;C..D6 +000012d0: b6d2 6f9a fec1 d70b 2679 8eae 1466 1e22 ..o.....&y...f." +000012e0: 2f6d 29cc 31f3 abad 0e7b 5b23 399e c41e /m).1....{[#9... +000012f0: 2e22 71dd 15bf d381 9c0e e06c c913 c188 ."q........l.... +00001300: 9493 c884 f3f4 cb3e 7947 4c62 9ac2 e42e .......>yGLb.... +00001310: 399d acc6 fcd4 fd4d fe31 adc2 cefd b793 9......M.1...... +00001320: c366 cc32 58e8 61f7 43b5 acb4 1496 8a70 .f.2X.a.C......p +00001330: e453 de6c fac6 347a ee63 a49f fba8 19aa .S.l..4z.c...... +00001340: aee3 a76f ec06 5050 226c 3b76 d673 e7a7 ...o..PP"l;v.s.. +00001350: a92f 560c 23e7 bd47 0ad4 6bf2 12f0 cb57 ./V.#..G..k....W +00001360: cd36 70bd 5861 f9cb f49a c5c5 b98f 7720 .6p.Xa........w +00001370: 3aa8 5477 03bf c774 d8ce 8b1e ecea d1de :.Tw...t........ +00001380: 0e27 3170 b564 0137 a790 6bc9 bdac 914d .'1p.d.7..k....M +00001390: 54a6 bc83 9813 92fb 377a 42f1 6625 2748 T.......7zB.f%'H +000013a0: 5c5e dcbc e5fa 126a c53d 6a46 45a6 68e5 \^.....j.=jFE.h. +000013b0: 3366 24fb e69b a03f 422a fe63 8ef2 0d17 3f$....?B*.c.... +000013c0: cfae a705 6e9c dce9 abdb 06f7 3ea2 5212 ....n.......>.R. +000013d0: e975 5eb8 94c1 e910 1bbc e98f 5fc9 d9d1 .u^........._... +000013e0: 959c 9dbe 468c 41d5 5bb0 7dbf 9e5c 2bc2 ....F.A.[.}..\+. +000013f0: 08bf 544c 0fb0 ab45 e87f 42cf 48bc 3dce ..TL...E..B.H.=. +00001400: 8cb6 d397 8b30 adfd 2fd6 cacc 882f 1bfd .....0../..../.. +00001410: a4e9 f6dc bc4b ccf8 ee92 df4b 8200 a2e0 .....K.....K.... +00001420: 4227 8226 766d cf2e b640 bcfc 15be ced0 B'.&vm...@...... +00001430: 8ed2 5183 177a 0ae4 dc4a 92a3 42a7 039d ..Q..z...J..B... +00001440: 8e78 ff04 8e16 a7e7 2356 158a 21d0 175b .x......#V..!..[ +00001450: 0bf1 0f21 4523 825b 6af4 14ea 7f3b b9b6 ...!E#.[j....;.. +00001460: deb4 6128 fc57 b668 42c9 1a08 ec69 cb16 ..a(.W.hB....i.. +00001470: aab6 2f93 b66a 93d2 3d55 6822 2140 9813 ../..j..=Uh"!@.. +00001480: 4b24 a843 88ff beef 1c3b c431 6155 fb42 K$.C.....;.1aU.B +00001490: 2ebe c43e 3e97 cfc7 e740 88c6 afa2 799a ...>>....@....y. +000014a0: 8d68 b1c9 656c 9c94 77b9 b681 9c42 43ce .h..el..w....BC. +000014b0: c1a0 8b39 0538 0276 d254 92ae ad35 6832 ...9.8.v.T...5h2 +000014c0: 36ec 33ce 9acf 3ef8 fcc9 ffab ace3 7933 6.3...>.......y3 +000014d0: 6dff 5e6b 5575 9026 1128 3a11 e8da ad20 m.^kUu.&.(:.... +000014e0: 332a 1ecd 1516 76d6 9020 e6cc 2bb5 55bd 3*....v.. ..+.U. +000014f0: 18d5 08c5 4a84 b5ad 9275 c66d a3e5 5697 ....J....u.m..V. +00001500: b60a c615 3dea c36b f446 4ade e616 0e2b ....=..k.FJ....+ +00001510: 042c 4cec ab78 58cb 9cf2 4654 44ab 38ab .,L..xX...FTD.8. +00001520: 2a9e 044d f81e bb1e d701 4f05 05ee 8819 *..M......O..... +00001530: 69ba 2878 5867 852e a9e9 1645 928a de31 i.(xXg.....E...1 +00001540: f563 3e72 c190 a349 30f6 cfbb fd55 65b1 .c>r...I0....Ue. +00001550: 5cd6 0ff3 0444 197b 3d35 50c4 aea4 0ffa \....D.{=5P..... +00001560: 7b5f f3d5 5a90 87ef 26a5 6c8b ef58 22dd {_..Z...&.l..X". +00001570: 1285 3198 f7e7 362f eb7b 0e86 6076 c3a2 ..1...6/.{..`v.. +00001580: c8a2 9897 8b8a 9c57 77ea de3d d0ce 32e4 .......Ww..=..2. +00001590: 1835 c74f 20ac dfb2 7d78 78ca cbd0 b9ab .5.O ...}xx..... +000015a0: b762 183b 7e31 4ff1 a0ea e3f9 e893 160b .b.;~1O......... +000015b0: bbee 8a26 7086 bc5e 3f4a b10f df4e 8ecf ...&p..^?J...N.. +000015c0: 7d12 63c4 c712 0966 3805 ccf4 8ee1 46d4 }.c....f8.....F. +000015d0: c3b5 350e f5ae 7f2c 7604 8e22 0ad3 12dc ..5....,v..".... +000015e0: e03a cacb e3f8 9d66 fdfc 39f6 b8b5 0652 .:.....f..9....R +000015f0: 1da3 f722 9c76 39a8 97d5 60c4 31a9 867e ...".v9...`.1..~ +00001600: 7b39 136e 7570 15ad 8409 c12f 7fd9 c42b {9.nup...../...+ +00001610: b42f b764 0c4d 5e28 62f9 67e5 fae8 d863 ./.d.M^(b.g....c +00001620: 6941 0f2a bc15 3271 1feb 997f 2020 1996 iA.*..2q.... .. +00001630: a071 e69d e4d3 4222 5a46 a577 c4e6 9369 .q....B"ZF.w...i +00001640: a194 8e77 e538 b0c1 3467 1d96 654e bb05 ...w.8..4g..eN.. +00001650: bff5 7f94 0f13 cc64 17b3 0b32 1c1c cfbd .......d...2.... +00001660: e2be 40f4 9dd0 64a7 75fa 03de 05af 8246 ..@...d.u......F +00001670: bf0b 3e15 ed72 2125 2ca9 6396 5e46 075f ..>..r!%,.c.^F._ +00001680: 92b8 d0e8 0c9c 21cb 5bb9 d8b3 93a1 71c5 ......!.[.....q. +00001690: 1ec0 e391 0b61 58c8 27f6 6151 c5d1 1a86 .....aX.'.aQ.... +000016a0: 064a 176c 9266 6ef0 783d 985d 0167 44b8 .J.l.fn.x=.].gD. +000016b0: 7811 6e06 b3f7 5eb0 cacf 9658 82e8 1191 x.n...^....X.... +000016c0: bb03 681a c3e1 4b8e 2cc4 85fe be05 1756 ..h...K.,......V +000016d0: 64b0 a79d 186b 4765 473a 6c10 2323 a8cd d....kGeG:l.##.. +000016e0: a117 0cb8 b273 43f8 25d0 1951 3a33 8a43 .....sC.%..Q:3.C +000016f0: bb08 4e56 6110 a48b 7243 3643 ee16 4b01 ..NVa...rC6C..K. +00001700: 934a 6a23 986f e67f 0391 2755 4014 9f8c .Jj#.o....'U@... +00001710: 26a3 4f74 37da 541c 5f08 6e8d 76f5 72f8 &.Ot7.T._.n.v.r. +00001720: 71da 764d 270a 6f1a 8f88 41ca 29ef f6b0 q.vM'.o...A.)... +00001730: b955 439f aa5d 5bf3 9286 6dbd 5204 b15e .UC..][...m.R..^ +00001740: 6ab3 ac77 8de6 bf67 5815 35d2 6f33 6a8c j..w...gX.5.o3j. +00001750: 8cad 532e 4cb2 55f5 5502 8999 f136 d552 ..S.L.U.U....6.R +00001760: 0891 d3fd aadf 9c23 50a8 bf6e 4c4a ff80 .......#P..nLJ.. +00001770: 54ed e93f c17d 6ba8 0848 0000 T..?.}k..H.. From 54f8d79f731a898e403b150837d0b6c676ccdf6d Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 11 Apr 2020 02:05:54 +0200 Subject: [PATCH 079/111] Fixed dump to create an actual include file --- .../examples/FSBrowser/extras/index.htm.gz | Bin 6012 -> 6012 bytes .../examples/FSBrowser/extras/index_htm.h | 880 ++++++++++-------- .../examples/FSBrowser/extras/reduce_index.sh | 2 +- 3 files changed, 505 insertions(+), 377 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz index 07f3212b16c4e076ca5ff847d768b5b24f24c953..5080fbadae476e25d8f92aa54dd4ee31e243c3cb 100644 GIT binary patch delta 15 WcmeyP_eYOSzMF%ij&mbhnK%G07zE$| delta 15 WcmeyP_eYOSzMF&N9n(g(GI0Pfl?6!v diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h index 2c19c9d4ac..9f93bb0ab6 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h @@ -1,376 +1,504 @@ -00000000: 1f8b 0808 ee02 915e 0003 696e 6465 782e .......^..index. -00000010: 6874 6d00 d43b 8976 dbb6 96bf 4233 539b htm..;.v....B3S. -00000020: 1c51 d4e2 3592 69d7 8bbc c4fb 222f c964 .Q..5.i....."/.d -00000030: de81 4448 8245 910c 095a 921d cdb7 f45b ..DH.E...Z.....[ -00000040: fa65 7301 7095 283b e9b4 af9d a427 02b1 .es.p.(;.....'.. -00000050: 5cdc 7d01 d0cd 85fd 8bbd dbc7 cb86 d4a3 \.}............. -00000060: 034b ba6c ee9e 1eef 4972 b154 ba5f de2b .K.l....Ir.T._.+ -00000070: 95f6 6ff7 a587 a3db b353 a9a2 97a5 5b0f ..o......S....[. -00000080: d93e a1c4 b191 552a 35ce 6549 ee51 ead6 .>....U*5.eI.Q.. -00000090: 4aa5 e170 a80f 9775 c7eb 966e af4b 2306 J..p...u...n.K#. -000000a0: abc2 1687 cd22 4dad d44d 6aca 5b9b 7c3f ....."M..Mj.[.|? -000000b0: 0bd9 5d03 db5b 9b94 500b 6f1d 100b 4b03 ..]..[..P.o...K. -000000c0: 64a3 2ef6 364b a26f d3a7 63d6 8b4d 820c d...6K.o..c..M.. -000000d0: bfed 6198 de72 ccf1 6bc7 b169 d127 2fb8 ..a..r..k..i.'/. -000000e0: 56a9 baa3 3aff eca0 01b1 c6b5 3bec 9900 V...:.......;... -000000f0: 45db f108 b2b4 1bd8 bbe8 638f 7426 7a1b E.........c.t&z. -00000100: 66e1 113d c376 f0fa 5224 b689 47b5 e572 f..=.v..R$..G..r -00000110: b9ee 3a02 bd1a 6af9 8e15 505c b770 87d6 ..:...j...P\.p.. -00000120: 5601 70cb f14c ecd5 2aee 4882 2162 4a1f V.p..L..*.H.!bJ. -00000130: 5656 56ea 2dd4 ee77 3d27 b0cd 62db b11c VVV.-..w='..b... -00000140: aff6 a1b3 cafe d64d e2bb 161a d76c c7c6 .......M.....l.. -00000150: b076 54f4 7bc8 7486 b5b2 5496 2a65 00e2 .vT.{.t...T.*e.. -00000160: 755b 4829 6bec afbe a2d6 5364 5422 3286 u[H)k.....SdT"2. -00000170: 9874 7bb4 b65e 2e67 3096 02eb d522 3e4c .t{..^.g0....">L -00000180: 672c 113b 50c7 ad95 05b2 e5fa 0079 5d62 g,.;P........y]b -00000190: 43c3 45a6 49ec 6e6d 6abd 455e 6342 3d6c C.E.I.nmj.E^cB=l -000001a0: 214a 9e71 7d40 ece2 9098 b457 5b03 f4ea !J.q}@.....W[... -000001b0: edc0 f381 1cd7 21b0 cecb aef7 5d64 bf86 ......!.....]d.. -000001c0: e432 2644 b412 db22 362e b62c a7dd 8ff7 .2&D..."6..,.... -000001d0: 5e73 47d3 bbd7 7ace 33f6 5e13 de71 30f9 ^sG...z.3.^..q0. -000001e0: b332 9b61 8c61 96ef 8322 61fc 4cf0 10a6 .2.a.a..."a.L... -000001f0: 69d9 0ee0 4d4c 75c2 8829 764d 0121 b61b i...MLu..)vM.!.. -00000200: d0d7 59d9 3b2e 6a13 3ae6 ec4b cd7f 9d92 ..Y.;.j.:..K.... -00000210: 5571 e0bc 1403 d02c d02e 0bb7 a990 0848 Uq.....,.......H -00000220: afd5 2774 7660 ba63 0a99 34bd e572 6756 ..'tv`.c..4..rgV -00000230: 10d3 9343 6e32 ce15 4ddc 763c c4a9 00b6 ...Cn2..M.v<.... -00000240: 628f c923 8fd8 8285 5ad8 2a00 b322 1649 b..#....Z.*..".I -00000250: ec6f b5ca 8535 33fd 7f60 625a a1a7 85c0 .o...53..`bZ.... -00000260: 8069 397d b55a 0b77 1c0f bfbe 4904 dfa2 .i9}.Z.w....I... -00000270: 06f0 51cb c2a6 402d 5a61 e20e 0a2c 1a8b ..Q...@-Za...,.. -00000280: 425f cb5d dcee e176 1f9b 801c 5562 486a B_.]...v....UbHj -00000290: 1a6d ae94 3f8b b796 239a 94d6 069e a598 .m..?...#....... -000002a0: 88a2 1a19 80ab 2ab9 7617 fc81 8fd7 5634 ......*.v.....V4 -000002b0: 72b7 7b71 3d2c 9f1c 769d 1df8 737e d3ec r.{q=,..v...s~.. -000002c0: 359a 5d68 edb2 cfbd eede ce19 6b8c 5747 5.]h........k.WG -000002d0: c301 6bb4 2abb 6777 8dbb a376 637c 3f58 ..k.*.gw...vc|?X -000002e0: 5dbd bfaf 36d6 ae9a e6ce 5563 97a0 c3ca ]...6.....Uc.... -000002f0: 133a 5c2f 954a 1bcf e74f cb27 2f67 eb7b .:\/.J...O.'/g.{ -00000300: a3b3 d660 35e8 9cbd b44b 8f55 f3db 889e ...`5....K.U.... -00000310: a243 e2ac 6e74 1fef 3f3d 7d3e b8be 3bf8 .C..nt..?=}>..;. -00000320: fce9 b651 3a7d 6917 9efd d6b9 f970 b7be ...Q:}i......p.. -00000330: 52b8 ec3d 9b47 96df baaf f6dd b56f ebcf R..=.G.......o.. -00000340: a72f e38d e7b3 e0fc f6a5 b3fc b1d0 73d6 ./............s. -00000350: 9ae3 8be6 de41 eff1 be69 adda cfeb 8592 .....A...i...... -00000360: bb86 fb57 a474 78cd 70dc 79b8 beb9 b5ce ...W.tx.p.y..... -00000370: 76ae 2e5a 8f9f 192d 37d7 9fae 1b07 cd9b v..Z...-7....... -00000380: b30e ed7f a4e3 21d9 b9ea 39cd 9dde ddce ......!...9..... -00000390: f093 fb6d fffa 5b07 3d3f 6fbc 58c1 f949 ...m..[.=?o.X..I -000003a0: 3ff0 1a4f dee3 7201 afaf 7c22 2fa7 76f5 ?..O..r...|"/.v. -000003b0: be32 203b 2723 b777 b17b 869b 8fdf d682 .2 ;'#.w.{...... -000003c0: a3d2 cef2 ceda daa3 7bbd 7bfd edd3 6774 ........{.{...gt -000003d0: 76bc 4186 fe5d 69cf 3c5b dba9 8e9e 56ed v.A..]i.<[....V. -000003e0: e3a6 7f52 2e91 dbe6 a577 783e 289f 35f7 ...R.....wx>(.5. -000003f0: 8ffb f8b0 f469 7525 b820 7dbf f5b1 f7d0 .....iu%. }..... -00000400: 5b5d bdec 1d1f 3f75 4eba e6a7 dba3 9bc6 []....?uN....... -00000410: b85a 3ad8 2faf 3d0c 1c73 e3fc 62e8 79c3 .Z:./.=..s..b.y. -00000420: 83a6 7f35 78f8 7cf4 b9d7 c3fd a35d b23b ...5x.|......].; -00000430: 7e3c 76d0 f1a7 9da7 cb8d e6cd 3dd9 dbd9 ~..gk... -000004c0: c3fe a57f 853e a1c1 b17b f5b2 e78d 87bd .....>...{...... -000004d0: fd0a b9ad 3ef4 fdd3 bda1 5f3a fefc bcf1 ....>....._:.... -000004e0: d9e9 acd3 eaea a3d5 e762 ba69 de5d 5c9f .........b.i.]\. -000004f0: acee 3d1e 1f1b aa64 3b45 0fbb 18d1 3f41 ..=....d;E....?A -00000500: f773 ba62 0bcf 8d01 3d11 c02a 1003 eabc .s.b....=..*.... -00000510: 3bdd 014e 8b92 36b2 8ac8 225d bb36 20a6 ;..N..6..."].6 . -00000520: 69e5 7a96 94c9 1563 275d d980 185a cef3 i.z....c']...Z.. -00000530: 9d73 6717 5736 66dc 1b5b a1d3 117d 63d5 .sg.W6f..[...}c. -00000540: da4a fe2a 32e8 beb1 6aa3 3cb3 6aca 25b2 .J.*2...j.<.j.%. -00000550: 6067 d39a 2cd7 45d8 e54c 89dd 3173 c5cc `g..,.E..L..1s.. -00000560: 27e7 b3a9 9eb7 7159 2a2e cf3a f08c 947e '.....qY*..:...~ -00000570: 70d3 b77c 6c21 4bc6 1c44 18dc c9af 3c5b p..|l!K..D....<[ -00000580: 9344 b626 21db 9494 282a b26c c304 e86d .D.&!...(*.l...m -00000590: 5c74 c908 5b45 1eb5 6a65 f535 1b6a a3f9 \t..[E..je.5.j.. -000005a0: c806 1fcb 8147 1de6 136a 0329 4508 a403 .....G...j.)E... -000005b0: f62b 22aa e315 5b41 b743 4680 7487 d884 .+"...[A.CF.t... -000005c0: 62a9 e24f 7e8d c0f4 f1b8 e3a1 01f6 a51f b..O~........... -000005d0: 04f3 daf1 9c41 9251 4ca8 93fa 984c 3e74 .....A.QL....L>t -000005e0: fc5b 8722 2b5f fd43 1697 593e 1569 7e39 .[."+_.C..Y>.i~9 -000005f0: 3795 2c43 eec9 a278 c782 14b1 0722 c676 7.,C...x.....".v -00000600: 3d9d 1ee1 35f6 976d d7f4 b1f9 b6b1 95cb =...5..m........ -00000610: bf84 3b14 3dd1 93ec 535d 4365 1365 408b ..;.=...S]Ce.e@. -00000620: aec9 8721 f26c 20eb 3503 663a bb85 e4c2 ...!.l .5.f:.... -00000630: eee2 7a9c 9e80 a650 c7b1 2871 73d2 a728 ..z....P..(qs..( -00000640: b1ae d605 1e65 9ea5 5619 079e 894f 5ac4 .....e..V....OZ. -00000650: 6211 7d86 da38 91ee 7452 1bd5 797e 236c b.}..8..tR..y~#l -00000660: 8009 0c7b 73d8 1889 6739 caa2 45a2 c6cc ...{s...g9..E... -00000670: 3192 5698 59c6 a8a7 b0e1 4d70 4311 3b7e 1.V.Y.....MpC.;~ -00000680: 646a 0f23 c023 8701 2229 8f88 0f93 f390 dj.#.#..")...... -00000690: bfd5 8d29 9f58 0537 1321 5fe4 7385 aecc ...).X.7.!_.s... -000006a0: f085 65dc a97c f803 b395 399b f34d 5a0e ..e..|....9..MZ. -000006b0: a5ce 2041 4068 6515 c41b b18a b9c5 0f60 .. A@he........` -000006c0: aaa0 f6da 07d7 1396 f706 c488 a22c 6480 .............,d. -000006d0: 3889 57cf a20d b8c6 1bae b20d 7d8a 68e0 8.W.........}.h. -000006e0: cfd9 6725 dea6 92b2 1ebe 7b4a a8cc 337f ..g%......{J..3. -000006f0: b01c c480 ce01 5404 457e ee65 6967 5dc3 ......T.E~.eig]. -00000700: 94bd c078 a4ab f035 cbf2 74a9 b6aa 2665 ...x...5..t...&e -00000710: 413d a967 6b61 a7a4 affa 1286 2410 e015 A=.gka......$... -00000720: 9d80 c6e8 e97e cf19 daaf 4225 2208 9578 .....~....B%"..x -00000730: b838 f0bb f966 3d4f ad44 610a 52e4 4880 .8...f=O.Da.R.H. -00000740: 2f1e d478 0bca 39fc a014 6144 ada7 2c29 /..x..9...aD..,) -00000750: e11a 8f11 bf26 9ed0 7789 6d83 b360 2ef6 .....&..w.m..`.. -00000760: b5fc cb6b 02cf 0363 a158 29ab 13e6 0d66 ...k...c.X)....f -00000770: 0796 d7ca 26ee aa93 899e 8621 9270 0f7f ....&......!.p.. -00000780: 0b88 0749 f83b 54c5 f527 a38a 91f3 165d ...I.;T..'.....] -00000790: 9c2c 4dd0 9644 84f4 e6e0 ed13 cfcf f643 .,M..D.........C -000007a0: 899f 584b 1c05 53c6 b48f 0c05 cd37 7291 ..XK..S......7r. -000007b0: 07fe 251e 06f1 043e 679a 28e3 c90b d3e0 ..%....>g.(..... -000007c0: 7014 7a66 fc36 7754 4c9b 20ce 163f c21f p.zf.6wTL. ..?.. -000007d0: 5819 16d6 2b89 2af3 ac62 b3c4 6bd2 ad4d X...+.*..b..k..M -000007e0: 0891 c4a5 5bcf c893 9839 6b1d ffd8 ee38 ....[....9k....8 -000007f0: f54e 60b7 1989 928f e9a9 5014 056b 547d .N`.......P..kT} -00000800: a58b 8b10 ca81 18ac 5b4e 57a1 aa66 3aed ........[NW..f:. -00000810: 8005 30bd 8b69 43c4 b2dd f1b1 a9c8 2905 ..0..iC.......). -00000820: 9355 9d73 8a9d e418 f4fb 7759 d6f0 f67b .U.s......wY...{ -00000830: 0b61 51db 42be 7f0a 25b4 0ef6 abc8 5c93 .aQ.B...%.....\. -00000840: 65b5 f653 2b3d 3c00 46c5 8b13 7cd9 218e e..S+=<.F...|.!. -00000850: ced9 a08b 9acf c0db f210 112a d7e4 b0fa ...........*.... -00000860: 9327 3123 3cf0 b5ac b8bb 014d 56b0 fa4a .'1#<......MV..J -00000870: 3a0a deac 94ab 2baa 8769 e0d9 122e c8d2 :.....+..i...... -00000880: ae0c eaee 299c 9b46 b152 a785 82c6 e66c ....)..F.R.....l -00000890: 2ab8 64f0 c975 b51e cd07 d77e 0019 88a9 *.d..u.....~.... -000008a0: 54d5 c217 593a 21bb b226 4b67 e2e7 50fc T...Y:!..&Kg..P. -000008b0: dcf2 9f4b f8f7 eb17 fa35 8d4d c7c3 7eef ...K.....5.M..~. -000008c0: 867b 3205 547d 1e47 84af cbf0 5f56 c2d5 .{2.T}.G...._V.. -000008d0: cc39 e8ba 2ad7 05be 3624 5c0f 67a7 4794 .9..*...6$\.g.G. -000008e0: bad7 6043 d8a7 75aa 3b36 e3a9 116d ac70 ..`C..u.;6...m.p -000008f0: c2ab e5f2 8241 7501 5b65 8c65 8b1a 9e07 .....Au.[e.e.... -00000900: b453 b58e 2d1f bf32 98d8 5084 3e19 9f6e .S..-..2..P.>..n -00000910: 2ece 75d0 6f1f 2b4c 22be 0b3a 846f 415f ..u.o.+L"..:.oA_ -00000920: 5555 a763 97f1 ae28 01f3 f86c 9df8 17fd UU.c...(...l.... -00000930: 6d05 170c 7993 2591 1231 8da5 3082 2e6d m...y.%..1..0..m -00000940: a5bb 584e b224 7119 1a4b 42d5 e582 5229 ..XN.$q..KB...R) -00000950: 9c21 dad3 b923 553e 7efc cf10 6a00 9377 .!...#U>~...j..w -00000960: c714 fba5 b083 3280 bc47 550b f22f 00ba ......2..GU../.. -00000970: c460 873b 700d 3296 c2f8 bbb4 2517 322a .`.;p.2.....%.2* -00000980: 3003 a338 bd0d c05c b45b be5b 0766 63d1 0..8...\.[.[.fc. -00000990: 723a e2f7 3d58 b034 c245 fcc8 5a04 ddf6 r:..=X.4.E..Z... -000009a0: 03d7 753c 8a4d 7658 e92f 2e72 3e49 0957 ..u<.MvX./.r>I.W -000009b0: c2fc 6169 eb7e e7fa fcf8 fc30 9f1a be78 ..ai.~.....0...x -000009c0: ec53 3cf8 fd37 969b 2362 fbbf ff96 82fe .S<..7..#b...... -000009d0: fb6f 1d98 6233 475d db6c 7925 a07f 0e0a .o..b3G].ly%.... -000009e0: 33b8 aa6a 2d91 5d28 9cd9 d086 cd30 4a0c 3..j-.](.....0J. -000009f0: 7be0 3233 e78e 2dc7 3297 b68e cf8f 6fa5 {.23..-.2.....o. -00000a00: c6f5 f5c5 b5b4 10b3 e167 141d 6bf2 cde5 .........g..k... -00000a10: f1c1 c18d bc60 442c 065d 0396 cd85 32e8 .....`D,.]....2. -00000a20: 9bc4 0320 c22d 8401 c490 4504 9121 e068 ... .-....E..!.h -00000a30: 6012 2eb6 15f9 b071 0b96 590a b7d5 16ca `......q..Y..... -00000a40: 2a0c f918 34ce 0e2c 4b4d 4c35 6b1e e039 *...4..,KML5k..9 -00000a50: 9005 8595 2273 ca6a d217 b980 434b 2ac8 ...."s.j....CK*. -00000a60: 5f25 f695 b18f 0450 1bd9 cc15 9fe3 e19e _%.....P........ -00000a70: 28a6 98e1 0b67 b2b0 309f 2fe8 19ef 5270 (....g..0./...Rp -00000a80: 7c7a 743a f5fd fbc2 0288 bc43 bc81 22ef |zt:.......C..". -00000a90: f558 5eed 4bd4 91c6 4ee0 4911 1c69 482c .X^.K...N.I..iH, -00000aa0: 4b6a 4128 737c 2a91 0e1b 9598 a210 3b00 KjA(s|*.......;. -00000ab0: 4630 b5b3 b9fe 02f8 7de2 b791 67c2 2ebe F0......}...g... -00000ac0: b250 5119 2f12 acf3 a7e1 b75c d60c c6c6 .PQ./......\.... -00000ad0: 029e 2f78 3306 9b5d 91a0 002b 2e79 683d ../x3..]...+.yh= -00000ae0: 00bd c25c 06b1 0f06 ce7b d4bf 27b4 a7c8 ...\.....{..'... -00000af0: 2559 fdfe 5dc1 0634 0a58 d5b0 0ed2 4c86 %Y..]..4.X....L. -00000b00: 18c9 062c b0a0 7484 9cac 5851 d914 3f68 ...,..t...XQ..?h -00000b10: f9d4 63e1 b10c 5f60 64f4 98a5 7317 1dbe ..c..._`d...s... -00000b20: 262d 3db0 788a 8f78 c60e a1d4 66c1 94f9 &-=.x..x....f... -00000b30: 48c7 9847 19a0 8092 41b1 3e1c 5764 5e19 H..G....A.>.Wd^. -00000b40: cb90 8c70 9536 6466 acb2 86f4 41c0 ec1b ...p.6df....A... -00000b50: 2c6e a102 5fcc 7c0d 999d 14ca 9aa3 2317 ,n.._.|.......#. -00000b60: 34d7 dceb 11cb 5490 cabd bef9 ee06 a60e 4.....T......... -00000b70: 4e45 76c1 a11e f31e cd0c 7764 d906 fb12 NEv.......wd.... -00000b80: 7bb0 09ec 2b0c 9c77 c80a 3823 a7b6 35c5 {...+..w..8#..5. -00000b90: b6d6 dc6d 5b01 64f6 20c9 ba95 8e58 4d97 ...m[.d. ....XM. -00000ba0: 45a1 6960 9600 e6bf 0fcc e744 08e3 d6fc E.i`.......D.... -00000bb0: 69eb 6607 cbac 3bb5 e159 7f9f 4dcd eee7 i.f...;..Y..M... -00000bc0: 8bfd c8fb fb91 2cac 032e 9c2c 3022 80b5 ......,....,0".. -00000bd0: e702 630e 0f40 b539 eaa2 46da e5f0 c1db ..c..@.9..F..... -00000be0: b4f3 49c8 eed0 163b 78ef a3eb f13d 22bb ..I....;x....=". -00000bf0: d3bc 34f6 cc72 5997 d850 1cd8 9c42 a66c ..4..rY..P...B.l -00000c00: c8cb 501f b191 c4e6 ca80 581a 034f 6030 ..P.......X..O`0 -00000c10: 781f 8301 c720 65ca da20 8d44 e83a 58ef x.... e.. .D.:X. -00000c20: fcdd 0662 b7e0 fddd 02be 5b0f 5b2e df2a ...b......[.[..* -00000c30: 486f b52d 4f81 0d04 58f7 3d41 b982 8961 Ho.-O...X.=A...a -00000c40: 3c70 3309 5798 97aa d322 72c1 c421 c56a " -00000cc0: 0d02 0849 109a ba50 bdb1 7280 661d 75b4 ...I...P..r.f.u. -00000cd0: e220 ca5f c402 280e 2118 9910 d868 4f42 . ._..(.!....hOB -00000ce0: d252 6949 02be 7ba8 4d31 44fc 9a40 7d8f .RiI..{.M1D..@}. -00000cf0: 8b16 92d8 09f8 833f 8e29 67a4 948b ac32 .......?.)g....2 -00000d00: 852d 4887 1698 6054 6d0a 05c0 c1cb c501 .-H...`Tm....... -00000d10: f20c 304a 05c6 0773 c643 4362 5382 7920 ..0J...s.CCbS.y -00000d20: 202d b9e9 4116 d70e 5822 3199 8e54 b790 -..A...X"1..T.. -00000d30: b442 9cf2 6392 e7c5 dff0 e085 e13f d744 .B..c........?.D -00000d40: 4cf2 0c16 12ef 40de 8cff 49ac 5143 0d7d L.....@...I.QC.} -00000d50: 23fa 0bc7 389b b109 87f8 ceb2 c89f ce59 #...8..........Y -00000d60: 4da7 bbf9 0902 eb4f d9f6 2619 7425 df6b M......O..&.t%.k -00000d70: 1b4b 9034 14e4 b81a 19a0 5131 3ef4 f9a5 .K.4......Q1>... -00000d80: 2eb1 eff4 09a7 149e 7da3 803a 7529 7359 ........}..:u)sY -00000d90: 585f 9220 dd4e 0924 51bc 92b2 5dfb 2f5d X_. .N.$Q...]./] -00000da0: f9f2 dffa d782 aa6e ff47 49c7 23cc c6bf .......n.GI.#... -00000db0: 54be d6c1 6708 4f00 9649 551f 14bd dd03 T...g.O..IU..... -00000dc0: 75a3 cea9 33c4 de1e 82e2 4b55 5fdb f02b u...3.....KU_..+ -00000dd0: 5388 dd35 deea d141 d2b2 c2e6 931f 37c0 S..5...A......7. -00000de0: 618a 663b 9a16 7dbb 6ed4 f2a3 e923 0621 a.f;..}.n....#.! -00000df0: ccad 2a29 95f2 fe7c 0a5c 28f3 431c dda4 ..*)...|.\(.C... -00000e00: 85a3 6697 74c2 1669 3bb9 380d 145b 73d4 ..f.t..i;.8..[s. -00000e10: b04e 9da7 b881 057a 6b67 7c36 56eb 5316 .N.....zkg|6V.S. -00000e20: 31b5 c622 b006 67d6 5055 6b2b 8eba ad64 1.."..g.PUk+...d -00000e30: 5587 d734 0d50 c5b8 bca1 f9ae 7633 7b68 U..4.P......v3{h -00000e40: 9168 b3bf 3bde 6395 dd39 b81a 454e 3d63 .h..;.c..9..EN=c -00000e50: 009d 8e9c 7276 ad38 0e11 68d9 8056 4e51 ....rv.8..h..VNQ -00000e60: b1b8 081e 1d3a 9b9e 0548 4fd4 9a07 3f90 .....:...HO...?. -00000e70: fee6 207f 298c ff9f 863f e178 7339 cd4f .. .)....?.xs9.O -00000e80: a173 e484 583c 9e21 72df 19f2 5390 984a .s..X<.!r...S..J -00000e90: 343f 34d4 ff42 5aa9 e1bc 5106 8548 16f9 4?4..BZ...Q..H.. -00000ea0: e12a 7369 e093 6841 de8e 060c ea41 e536 .*si..hA.....A.6 -00000eb0: 7927 edcf 618a c952 8519 a65c f3d8 5a3a y'..a..R...\..Z: -00000ec0: 0304 93e2 fcdf 2cfd d010 5dcf 19b8 80bb ......,...]..... -00000ed0: 4009 4a68 a720 4341 0bc9 16d8 2e14 e30b @.Jh. CA........ -00000ee0: 065d 5ca4 0b86 b3b8 d851 1c28 bc26 ef14 .]\......Q.(.&.. -00000ef0: 2139 4cb0 54cd cad1 0c6c 619a d03f 2751 !9L.T....la..?'Q -00000f00: faeb 3462 ccf4 3c71 6916 3ba3 d5ec 99c2 ..4b...../..{{P.%... -00000f70: 16d3 9e6b cad8 1d55 6fb3 99e7 8e29 aa80 ...k...Uo....).. -00000f80: f086 5c7b c7ae d999 f5b4 6923 c8c4 6615 ..\{......i#..f. -00000f90: 7b0f 188f 5cff ef33 edb9 8e6e 1eed c642 {...\..3...n...B -00000fa0: e54f 33eb 6b71 3efe f7d9 758f db35 3b47 .O3.kq>...u..5;G -00000fb0: 9772 84d3 18c1 bfe6 ff27 d194 0545 ef1c .r.......'...E.. -00000fc0: d7e4 c8c7 57b3 e732 f363 4f7e b9f6 4f8d ....W..2.cO~..O. -00000fd0: 3df3 cf90 7298 4054 8dbc 1b7b f20b ebbf =...r.@T...{.... -00000fe0: 3cf6 a89c a8a9 4bb5 34f2 4e74 68e6 e84e <.....K.4.Nth..N -00000ff0: a7e3 637a cf8a 22ad 1d7f 1ff1 a208 bcb8 ..cz.."......... -00001000: 630f 9cc0 c74e 4033 2428 b1e7 dfb4 be7f c....N@3$(...... -00001010: 8f3f b6ac 0249 3e1f 37fd d4c7 965f 6843 .?...I>.7...._hC -00001020: 16f8 5711 9f89 b9c1 8fc5 5c2e 5687 1d11 ..W.......\.V... -00001030: b1aa df60 578d 722d 3a63 7927 510d 0f98 ...`W.r-:cy'Q... -00001040: c21a a60d f161 1b4d 5f8d b282 8e25 ea14 .....a.M_....%.. -00001050: e89e 1984 1255 ce66 b690 184a 9b64 4b99 .....U.f...J.dK. -00001060: ba88 b201 2175 b344 b666 8f8b 3527 57c1 ....!u.D.f..5'W. -00001070: 10a5 180c 80e5 e00a 234f 9d88 8982 a580 ........#O...... -00001080: 7f90 998e 7576 5c00 54ed 8b73 6285 9fa1 ....uv\.T..sb... -00001090: 43cc bdf4 1c17 7591 b863 d458 22c3 a0f1 C.....u..c.X"... -000010a0: ab04 cd49 b8ed fef5 dc9e 3e59 e74e ace5 ...I......>Y.N.. -000010b0: 403e 1097 a6f6 e222 fca7 a0e4 3052 8e5a @>....."....0R.Z -000010c0: b2fa b307 edfc 555b 225e 5367 8c0b 8b19 ......U["^Sg.... -000010d0: 83ce 9c9f cf3b 3044 91b7 5d5c ec45 92c8 .....;0D..]\.E.. -000010e0: 8f0c f1cc 6d94 0a9e 3505 4df9 6b06 e3cf ....m...5.M.k... -000010f0: 1667 252b 4e87 6555 2c05 0989 cf5e 33a7 .g%+N.eU,....^3. -00001100: de1e b06b 257e eb6c e7df 3adb 995b e7f9 ...k%~.l..:..[.. -00001110: 018a a740 d151 84b8 af09 2770 fed6 c159 ...@.Q....'p...Y -00001120: cbcd 53d0 18aa 53d4 65ee 6171 1167 8c1f ..S...S.e.aq.g.. -00001130: 3cf8 42c2 02fe 2442 f71d 8f2a d9ce e492 <.B...$B...*.... -00001140: 9f29 1103 07bf db98 9fd7 4271 dd46 16de .)........Bq.F.. -00001150: 83c8 813c 7621 ce3a d59a 983b 33c8 3aa3 ......;C..D6 -000012d0: b6d2 6f9a fec1 d70b 2679 8eae 1466 1e22 ..o.....&y...f." -000012e0: 2f6d 29cc 31f3 abad 0e7b 5b23 399e c41e /m).1....{[#9... -000012f0: 2e22 71dd 15bf d381 9c0e e06c c913 c188 ."q........l.... -00001300: 9493 c884 f3f4 cb3e 7947 4c62 9ac2 e42e .......>yGLb.... -00001310: 399d acc6 fcd4 fd4d fe31 adc2 cefd b793 9......M.1...... -00001320: c366 cc32 58e8 61f7 43b5 acb4 1496 8a70 .f.2X.a.C......p -00001330: e453 de6c fac6 347a ee63 a49f fba8 19aa .S.l..4z.c...... -00001340: aee3 a76f ec06 5050 226c 3b76 d673 e7a7 ...o..PP"l;v.s.. -00001350: a92f 560c 23e7 bd47 0ad4 6bf2 12f0 cb57 ./V.#..G..k....W -00001360: cd36 70bd 5861 f9cb f49a c5c5 b98f 7720 .6p.Xa........w -00001370: 3aa8 5477 03bf c774 d8ce 8b1e ecea d1de :.Tw...t........ -00001380: 0e27 3170 b564 0137 a790 6bc9 bdac 914d .'1p.d.7..k....M -00001390: 54a6 bc83 9813 92fb 377a 42f1 6625 2748 T.......7zB.f%'H -000013a0: 5c5e dcbc e5fa 126a c53d 6a46 45a6 68e5 \^.....j.=jFE.h. -000013b0: 3366 24fb e69b a03f 422a fe63 8ef2 0d17 3f$....?B*.c.... -000013c0: cfae a705 6e9c dce9 abdb 06f7 3ea2 5212 ....n.......>.R. -000013d0: e975 5eb8 94c1 e910 1bbc e98f 5fc9 d9d1 .u^........._... -000013e0: 959c 9dbe 468c 41d5 5bb0 7dbf 9e5c 2bc2 ....F.A.[.}..\+. -000013f0: 08bf 544c 0fb0 ab45 e87f 42cf 48bc 3dce ..TL...E..B.H.=. -00001400: 8cb6 d397 8b30 adfd 2fd6 cacc 882f 1bfd .....0../..../.. -00001410: a4e9 f6dc bc4b ccf8 ee92 df4b 8200 a2e0 .....K.....K.... -00001420: 4227 8226 766d cf2e b640 bcfc 15be ced0 B'.&vm...@...... -00001430: 8ed2 5183 177a 0ae4 dc4a 92a3 42a7 039d ..Q..z...J..B... -00001440: 8e78 ff04 8e16 a7e7 2356 158a 21d0 175b .x......#V..!..[ -00001450: 0bf1 0f21 4523 825b 6af4 14ea 7f3b b9b6 ...!E#.[j....;.. -00001460: deb4 6128 fc57 b668 42c9 1a08 ec69 cb16 ..a(.W.hB....i.. -00001470: aab6 2f93 b66a 93d2 3d55 6822 2140 9813 ../..j..=Uh"!@.. -00001480: 4b24 a843 88ff beef 1c3b c431 6155 fb42 K$.C.....;.1aU.B -00001490: 2ebe c43e 3e97 cfc7 e740 88c6 afa2 799a ...>>....@....y. -000014a0: 8d68 b1c9 656c 9c94 77b9 b681 9c42 43ce .h..el..w....BC. -000014b0: c1a0 8b39 0538 0276 d254 92ae ad35 6832 ...9.8.v.T...5h2 -000014c0: 36ec 33ce 9acf 3ef8 fcc9 ffab ace3 7933 6.3...>.......y3 -000014d0: 6dff 5e6b 5575 9026 1128 3a11 e8da ad20 m.^kUu.&.(:.... -000014e0: 332a 1ecd 1516 76d6 9020 e6cc 2bb5 55bd 3*....v.. ..+.U. -000014f0: 18d5 08c5 4a84 b5ad 9275 c66d a3e5 5697 ....J....u.m..V. -00001500: b60a c615 3dea c36b f446 4ade e616 0e2b ....=..k.FJ....+ -00001510: 042c 4cec ab78 58cb 9cf2 4654 44ab 38ab .,L..xX...FTD.8. -00001520: 2a9e 044d f81e bb1e d701 4f05 05ee 8819 *..M......O..... -00001530: 69ba 2878 5867 852e a9e9 1645 928a de31 i.(xXg.....E...1 -00001540: f563 3e72 c190 a349 30f6 cfbb fd55 65b1 .c>r...I0....Ue. -00001550: 5cd6 0ff3 0444 197b 3d35 50c4 aea4 0ffa \....D.{=5P..... -00001560: 7b5f f3d5 5a90 87ef 26a5 6c8b ef58 22dd {_..Z...&.l..X". -00001570: 1285 3198 f7e7 362f eb7b 0e86 6076 c3a2 ..1...6/.{..`v.. -00001580: c8a2 9897 8b8a 9c57 77ea de3d d0ce 32e4 .......Ww..=..2. -00001590: 1835 c74f 20ac dfb2 7d78 78ca cbd0 b9ab .5.O ...}xx..... -000015a0: b762 183b 7e31 4ff1 a0ea e3f9 e893 160b .b.;~1O......... -000015b0: bbee 8a26 7086 bc5e 3f4a b10f df4e 8ecf ...&p..^?J...N.. -000015c0: 7d12 63c4 c712 0966 3805 ccf4 8ee1 46d4 }.c....f8.....F. -000015d0: c3b5 350e f5ae 7f2c 7604 8e22 0ad3 12dc ..5....,v..".... -000015e0: e03a cacb e3f8 9d66 fdfc 39f6 b8b5 0652 .:.....f..9....R -000015f0: 1da3 f722 9c76 39a8 97d5 60c4 31a9 867e ...".v9...`.1..~ -00001600: 7b39 136e 7570 15ad 8409 c12f 7fd9 c42b {9.nup...../...+ -00001610: b42f b764 0c4d 5e28 62f9 67e5 fae8 d863 ./.d.M^(b.g....c -00001620: 6941 0f2a bc15 3271 1feb 997f 2020 1996 iA.*..2q.... .. -00001630: a071 e69d e4d3 4222 5a46 a577 c4e6 9369 .q....B"ZF.w...i -00001640: a194 8e77 e538 b0c1 3467 1d96 654e bb05 ...w.8..4g..eN.. -00001650: bff5 7f94 0f13 cc64 17b3 0b32 1c1c cfbd .......d...2.... -00001660: e2be 40f4 9dd0 64a7 75fa 03de 05af 8246 ..@...d.u......F -00001670: bf0b 3e15 ed72 2125 2ca9 6396 5e46 075f ..>..r!%,.c.^F._ -00001680: 92b8 d0e8 0c9c 21cb 5bb9 d8b3 93a1 71c5 ......!.[.....q. -00001690: 1ec0 e391 0b61 58c8 27f6 6151 c5d1 1a86 .....aX.'.aQ.... -000016a0: 064a 176c 9266 6ef0 783d 985d 0167 44b8 .J.l.fn.x=.].gD. -000016b0: 7811 6e06 b3f7 5eb0 cacf 9658 82e8 1191 x.n...^....X.... -000016c0: bb03 681a c3e1 4b8e 2cc4 85fe be05 1756 ..h...K.,......V -000016d0: 64b0 a79d 186b 4765 473a 6c10 2323 a8cd d....kGeG:l.##.. -000016e0: a117 0cb8 b273 43f8 25d0 1951 3a33 8a43 .....sC.%..Q:3.C -000016f0: bb08 4e56 6110 a48b 7243 3643 ee16 4b01 ..NVa...rC6C..K. -00001700: 934a 6a23 986f e67f 0391 2755 4014 9f8c .Jj#.o....'U@... -00001710: 26a3 4f74 37da 541c 5f08 6e8d 76f5 72f8 &.Ot7.T._.n.v.r. -00001720: 71da 764d 270a 6f1a 8f88 41ca 29ef f6b0 q.vM'.o...A.)... -00001730: b955 439f aa5d 5bf3 9286 6dbd 5204 b15e .UC..][...m.R..^ -00001740: 6ab3 ac77 8de6 bf67 5815 35d2 6f33 6a8c j..w...gX.5.o3j. -00001750: 8cad 532e 4cb2 55f5 5502 8999 f136 d552 ..S.L.U.U....6.R -00001760: 0891 d3fd aadf 9c23 50a8 bf6e 4c4a ff80 .......#P..nLJ.. -00001770: 54ed e93f c17d 6ba8 0848 0000 T..?.}k..H.. +unsigned char index_htm_gz[] = { + 0x1f, 0x8b, 0x08, 0x08, 0x7e, 0x09, 0x91, 0x5e, 0x00, 0x03, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xd4, 0x3b, 0x89, 0x76, + 0xdb, 0xb6, 0x96, 0xbf, 0x42, 0x33, 0x53, 0x9b, 0x1c, 0x51, 0xd4, 0xe2, + 0x35, 0x92, 0x69, 0xd7, 0x8b, 0xbc, 0xc4, 0xfb, 0x22, 0x2f, 0xc9, 0x64, + 0xde, 0x81, 0x44, 0x48, 0x82, 0x45, 0x91, 0x0c, 0x09, 0x5a, 0x92, 0x1d, + 0xcd, 0xb7, 0xf4, 0x5b, 0xfa, 0x65, 0x73, 0x01, 0x70, 0x95, 0x28, 0x3b, + 0xe9, 0xb4, 0xaf, 0x9d, 0xa4, 0x27, 0x02, 0xb1, 0x5c, 0xdc, 0x7d, 0x01, + 0xd0, 0xcd, 0x85, 0xfd, 0x8b, 0xbd, 0xdb, 0xc7, 0xcb, 0x86, 0xd4, 0xa3, + 0x03, 0x4b, 0xba, 0x6c, 0xee, 0x9e, 0x1e, 0xef, 0x49, 0x72, 0xb1, 0x54, + 0xba, 0x5f, 0xde, 0x2b, 0x95, 0xf6, 0x6f, 0xf7, 0xa5, 0x87, 0xa3, 0xdb, + 0xb3, 0x53, 0xa9, 0xa2, 0x97, 0xa5, 0x5b, 0x0f, 0xd9, 0x3e, 0xa1, 0xc4, + 0xb1, 0x91, 0x55, 0x2a, 0x35, 0xce, 0x65, 0x49, 0xee, 0x51, 0xea, 0xd6, + 0x4a, 0xa5, 0xe1, 0x70, 0xa8, 0x0f, 0x97, 0x75, 0xc7, 0xeb, 0x96, 0x6e, + 0xaf, 0x4b, 0x23, 0x06, 0xab, 0xc2, 0x16, 0x87, 0xcd, 0x22, 0x4d, 0xad, + 0xd4, 0x4d, 0x6a, 0xca, 0x5b, 0x9b, 0x7c, 0x3f, 0x0b, 0xd9, 0x5d, 0x03, + 0xdb, 0x5b, 0x9b, 0x94, 0x50, 0x0b, 0x6f, 0x1d, 0x10, 0x0b, 0x4b, 0x03, + 0x64, 0xa3, 0x2e, 0xf6, 0x36, 0x4b, 0xa2, 0x6f, 0xd3, 0xa7, 0x63, 0xd6, + 0x8b, 0x4d, 0x82, 0x0c, 0xbf, 0xed, 0x61, 0x98, 0xde, 0x72, 0xcc, 0xf1, + 0x6b, 0xc7, 0xb1, 0x69, 0xd1, 0x27, 0x2f, 0xb8, 0x56, 0xa9, 0xba, 0xa3, + 0x3a, 0xff, 0xec, 0xa0, 0x01, 0xb1, 0xc6, 0xb5, 0x3b, 0xec, 0x99, 0x00, + 0x45, 0xdb, 0xf1, 0x08, 0xb2, 0xb4, 0x1b, 0xd8, 0xbb, 0xe8, 0x63, 0x8f, + 0x74, 0x26, 0x7a, 0x1b, 0x66, 0xe1, 0x11, 0x3d, 0xc3, 0x76, 0xf0, 0xfa, + 0x52, 0x24, 0xb6, 0x89, 0x47, 0xb5, 0xe5, 0x72, 0xb9, 0xee, 0x3a, 0x02, + 0xbd, 0x1a, 0x6a, 0xf9, 0x8e, 0x15, 0x50, 0x5c, 0xb7, 0x70, 0x87, 0xd6, + 0x56, 0x01, 0x70, 0xcb, 0xf1, 0x4c, 0xec, 0xd5, 0x2a, 0xee, 0x48, 0x82, + 0x21, 0x62, 0x4a, 0x1f, 0x56, 0x56, 0x56, 0xea, 0x2d, 0xd4, 0xee, 0x77, + 0x3d, 0x27, 0xb0, 0xcd, 0x62, 0xdb, 0xb1, 0x1c, 0xaf, 0xf6, 0xa1, 0xb3, + 0xca, 0xfe, 0xd6, 0x4d, 0xe2, 0xbb, 0x16, 0x1a, 0xd7, 0x6c, 0xc7, 0xc6, + 0xb0, 0x76, 0x54, 0xf4, 0x7b, 0xc8, 0x74, 0x86, 0xb5, 0xb2, 0x54, 0x96, + 0x2a, 0x65, 0x00, 0xe2, 0x75, 0x5b, 0x48, 0x29, 0x6b, 0xec, 0xaf, 0xbe, + 0xa2, 0xd6, 0x53, 0x64, 0x54, 0x22, 0x32, 0x86, 0x98, 0x74, 0x7b, 0xb4, + 0xb6, 0x5e, 0x2e, 0x67, 0x30, 0x96, 0x02, 0xeb, 0xd5, 0x22, 0x3e, 0x4c, + 0x67, 0x2c, 0x11, 0x3b, 0x50, 0xc7, 0xad, 0x95, 0x05, 0xb2, 0xe5, 0xfa, + 0x00, 0x79, 0x5d, 0x62, 0x43, 0xc3, 0x45, 0xa6, 0x49, 0xec, 0x6e, 0x6d, + 0x6a, 0xbd, 0x45, 0x5e, 0x63, 0x42, 0x3d, 0x6c, 0x21, 0x4a, 0x9e, 0x71, + 0x7d, 0x40, 0xec, 0xe2, 0x90, 0x98, 0xb4, 0x57, 0x5b, 0x03, 0xf4, 0xea, + 0xed, 0xc0, 0xf3, 0x81, 0x1c, 0xd7, 0x21, 0xb0, 0xce, 0xcb, 0xae, 0xf7, + 0x5d, 0x64, 0xbf, 0x86, 0xe4, 0x32, 0x26, 0x44, 0xb4, 0x12, 0xdb, 0x22, + 0x36, 0x2e, 0xb6, 0x2c, 0xa7, 0xdd, 0x8f, 0xf7, 0x5e, 0x73, 0x47, 0xd3, + 0xbb, 0xd7, 0x7a, 0xce, 0x33, 0xf6, 0x5e, 0x13, 0xde, 0x71, 0x30, 0xf9, + 0xb3, 0x32, 0x9b, 0x61, 0x8c, 0x61, 0x96, 0xef, 0x83, 0x22, 0x61, 0xfc, + 0x4c, 0xf0, 0x10, 0xa6, 0x69, 0xd9, 0x0e, 0xe0, 0x4d, 0x4c, 0x75, 0xc2, + 0x88, 0x29, 0x76, 0x4d, 0x01, 0x21, 0xb6, 0x1b, 0xd0, 0xd7, 0x59, 0xd9, + 0x3b, 0x2e, 0x6a, 0x13, 0x3a, 0xe6, 0xec, 0x4b, 0xcd, 0x7f, 0x9d, 0x92, + 0x55, 0x71, 0xe0, 0xbc, 0x14, 0x03, 0xd0, 0x2c, 0xd0, 0x2e, 0x0b, 0xb7, + 0xa9, 0x90, 0x08, 0x48, 0xaf, 0xd5, 0x27, 0x74, 0x76, 0x60, 0xba, 0x63, + 0x0a, 0x99, 0x34, 0xbd, 0xe5, 0x72, 0x67, 0x56, 0x10, 0xd3, 0x93, 0x43, + 0x6e, 0x32, 0xce, 0x15, 0x4d, 0xdc, 0x76, 0x3c, 0xc4, 0xa9, 0x00, 0xb6, + 0x62, 0x8f, 0xc9, 0x23, 0x8f, 0xd8, 0x82, 0x85, 0x5a, 0xd8, 0x2a, 0x00, + 0xb3, 0x22, 0x16, 0x49, 0xec, 0x6f, 0xb5, 0xca, 0x85, 0x35, 0x33, 0xfd, + 0x7f, 0x60, 0x62, 0x5a, 0xa1, 0xa7, 0x85, 0xc0, 0x80, 0x69, 0x39, 0x7d, + 0xb5, 0x5a, 0x0b, 0x77, 0x1c, 0x0f, 0xbf, 0xbe, 0x49, 0x04, 0xdf, 0xa2, + 0x06, 0xf0, 0x51, 0xcb, 0xc2, 0xa6, 0x40, 0x2d, 0x5a, 0x61, 0xe2, 0x0e, + 0x0a, 0x2c, 0x1a, 0x8b, 0x42, 0x5f, 0xcb, 0x5d, 0xdc, 0xee, 0xe1, 0x76, + 0x1f, 0x9b, 0x80, 0x1c, 0x55, 0x62, 0x48, 0x6a, 0x1a, 0x6d, 0xae, 0x94, + 0x3f, 0x8b, 0xb7, 0x96, 0x23, 0x9a, 0x94, 0xd6, 0x06, 0x9e, 0xa5, 0x98, + 0x88, 0xa2, 0x1a, 0x19, 0x80, 0xab, 0x2a, 0xb9, 0x76, 0x17, 0xfc, 0x81, + 0x8f, 0xd7, 0x56, 0x34, 0x72, 0xb7, 0x7b, 0x71, 0x3d, 0x2c, 0x9f, 0x1c, + 0x76, 0x9d, 0x1d, 0xf8, 0x73, 0x7e, 0xd3, 0xec, 0x35, 0x9a, 0x5d, 0x68, + 0xed, 0xb2, 0xcf, 0xbd, 0xee, 0xde, 0xce, 0x19, 0x6b, 0x8c, 0x57, 0x47, + 0xc3, 0x01, 0x6b, 0xb4, 0x2a, 0xbb, 0x67, 0x77, 0x8d, 0xbb, 0xa3, 0x76, + 0x63, 0x7c, 0x3f, 0x58, 0x5d, 0xbd, 0xbf, 0xaf, 0x36, 0xd6, 0xae, 0x9a, + 0xe6, 0xce, 0x55, 0x63, 0x97, 0xa0, 0xc3, 0xca, 0x13, 0x3a, 0x5c, 0x2f, + 0x95, 0x4a, 0x1b, 0xcf, 0xe7, 0x4f, 0xcb, 0x27, 0x2f, 0x67, 0xeb, 0x7b, + 0xa3, 0xb3, 0xd6, 0x60, 0x35, 0xe8, 0x9c, 0xbd, 0xb4, 0x4b, 0x8f, 0x55, + 0xf3, 0xdb, 0x88, 0x9e, 0xa2, 0x43, 0xe2, 0xac, 0x6e, 0x74, 0x1f, 0xef, + 0x3f, 0x3d, 0x7d, 0x3e, 0xb8, 0xbe, 0x3b, 0xf8, 0xfc, 0xe9, 0xb6, 0x51, + 0x3a, 0x7d, 0x69, 0x17, 0x9e, 0xfd, 0xd6, 0xb9, 0xf9, 0x70, 0xb7, 0xbe, + 0x52, 0xb8, 0xec, 0x3d, 0x9b, 0x47, 0x96, 0xdf, 0xba, 0xaf, 0xf6, 0xdd, + 0xb5, 0x6f, 0xeb, 0xcf, 0xa7, 0x2f, 0xe3, 0x8d, 0xe7, 0xb3, 0xe0, 0xfc, + 0xf6, 0xa5, 0xb3, 0xfc, 0xb1, 0xd0, 0x73, 0xd6, 0x9a, 0xe3, 0x8b, 0xe6, + 0xde, 0x41, 0xef, 0xf1, 0xbe, 0x69, 0xad, 0xda, 0xcf, 0xeb, 0x85, 0x92, + 0xbb, 0x86, 0xfb, 0x57, 0xa4, 0x74, 0x78, 0xcd, 0x70, 0xdc, 0x79, 0xb8, + 0xbe, 0xb9, 0xb5, 0xce, 0x76, 0xae, 0x2e, 0x5a, 0x8f, 0x9f, 0x19, 0x2d, + 0x37, 0xd7, 0x9f, 0xae, 0x1b, 0x07, 0xcd, 0x9b, 0xb3, 0x0e, 0xed, 0x7f, + 0xa4, 0xe3, 0x21, 0xd9, 0xb9, 0xea, 0x39, 0xcd, 0x9d, 0xde, 0xdd, 0xce, + 0xf0, 0x93, 0xfb, 0x6d, 0xff, 0xfa, 0x5b, 0x07, 0x3d, 0x3f, 0x6f, 0xbc, + 0x58, 0xc1, 0xf9, 0x49, 0x3f, 0xf0, 0x1a, 0x4f, 0xde, 0xe3, 0x72, 0x01, + 0xaf, 0xaf, 0x7c, 0x22, 0x2f, 0xa7, 0x76, 0xf5, 0xbe, 0x32, 0x20, 0x3b, + 0x27, 0x23, 0xb7, 0x77, 0xb1, 0x7b, 0x86, 0x9b, 0x8f, 0xdf, 0xd6, 0x82, + 0xa3, 0xd2, 0xce, 0xf2, 0xce, 0xda, 0xda, 0xa3, 0x7b, 0xbd, 0x7b, 0xfd, + 0xed, 0xd3, 0x67, 0x74, 0x76, 0xbc, 0x41, 0x86, 0xfe, 0x5d, 0x69, 0xcf, + 0x3c, 0x5b, 0xdb, 0xa9, 0x8e, 0x9e, 0x56, 0xed, 0xe3, 0xa6, 0x7f, 0x52, + 0x2e, 0x91, 0xdb, 0xe6, 0xa5, 0x77, 0x78, 0x3e, 0x28, 0x9f, 0x35, 0xf7, + 0x8f, 0xfb, 0xf8, 0xb0, 0xf4, 0x69, 0x75, 0x25, 0xb8, 0x20, 0x7d, 0xbf, + 0xf5, 0xb1, 0xf7, 0xd0, 0x5b, 0x5d, 0xbd, 0xec, 0x1d, 0x1f, 0x3f, 0x75, + 0x4e, 0xba, 0xe6, 0xa7, 0xdb, 0xa3, 0x9b, 0xc6, 0xb8, 0x5a, 0x3a, 0xd8, + 0x2f, 0xaf, 0x3d, 0x0c, 0x1c, 0x73, 0xe3, 0xfc, 0x62, 0xe8, 0x79, 0xc3, + 0x83, 0xa6, 0x7f, 0x35, 0x78, 0xf8, 0x7c, 0xf4, 0xb9, 0xd7, 0xc3, 0xfd, + 0xa3, 0x5d, 0xb2, 0x3b, 0x7e, 0x3c, 0x76, 0xd0, 0xf1, 0xa7, 0x9d, 0xa7, + 0xcb, 0x8d, 0xe6, 0xcd, 0x3d, 0xd9, 0xdb, 0xd9, 0x38, 0xe9, 0x35, 0xee, + 0x37, 0x9a, 0x87, 0xb7, 0xeb, 0x27, 0x97, 0xe8, 0x73, 0x63, 0xe4, 0x5f, + 0xb4, 0x8e, 0xc6, 0x5e, 0xb3, 0x7b, 0xdb, 0x7f, 0xba, 0x7d, 0xd9, 0xb0, + 0xc8, 0xe5, 0xc3, 0xf0, 0x65, 0xd8, 0xd8, 0x2d, 0x5c, 0x5c, 0x1e, 0xdc, + 0x8d, 0x8e, 0x1a, 0x1b, 0x0f, 0xd5, 0x76, 0xff, 0x7a, 0x77, 0x7c, 0x8a, + 0xee, 0xc6, 0xbd, 0xbb, 0x93, 0xd1, 0x65, 0x75, 0xfd, 0xe4, 0xbc, 0x60, + 0xef, 0xd0, 0xa3, 0xf5, 0xeb, 0xe7, 0xc0, 0xab, 0xee, 0x7b, 0x6b, 0xd5, + 0x0a, 0xc5, 0xfd, 0x33, 0xec, 0x17, 0xee, 0xc9, 0xe1, 0xc6, 0xda, 0x91, + 0xb7, 0xf6, 0x74, 0xf2, 0xf8, 0x54, 0x58, 0xbf, 0xaa, 0x9c, 0x98, 0xe5, + 0x4b, 0x77, 0x74, 0xbc, 0xba, 0x71, 0xee, 0x5f, 0x99, 0xe7, 0xa5, 0xea, + 0xea, 0x8b, 0x75, 0xb5, 0x7f, 0x65, 0x9e, 0xb4, 0x3e, 0xee, 0xd8, 0x67, + 0x6b, 0x9d, 0xa3, 0x9b, 0xc3, 0xfe, 0xa5, 0x7f, 0x85, 0x3e, 0xa1, 0xc1, + 0xb1, 0x7b, 0xf5, 0xb2, 0xe7, 0x8d, 0x87, 0xbd, 0xfd, 0x0a, 0xb9, 0xad, + 0x3e, 0xf4, 0xfd, 0xd3, 0xbd, 0xa1, 0x5f, 0x3a, 0xfe, 0xfc, 0xbc, 0xf1, + 0xd9, 0xe9, 0xac, 0xd3, 0xea, 0xea, 0xa3, 0xd5, 0xe7, 0x62, 0xba, 0x69, + 0xde, 0x5d, 0x5c, 0x9f, 0xac, 0xee, 0x3d, 0x1e, 0x1f, 0x1b, 0xaa, 0x64, + 0x3b, 0x45, 0x0f, 0xbb, 0x18, 0xd1, 0x3f, 0x41, 0xf7, 0x73, 0xba, 0x62, + 0x0b, 0xcf, 0x8d, 0x01, 0x3d, 0x11, 0xc0, 0x2a, 0x10, 0x03, 0xea, 0xbc, + 0x3b, 0xdd, 0x01, 0x4e, 0x8b, 0x92, 0x36, 0xb2, 0x8a, 0xc8, 0x22, 0x5d, + 0xbb, 0x36, 0x20, 0xa6, 0x69, 0xe5, 0x7a, 0x96, 0x94, 0xc9, 0x15, 0x63, + 0x27, 0x5d, 0xd9, 0x80, 0x18, 0x5a, 0xce, 0xf3, 0x9d, 0x73, 0x67, 0x17, + 0x57, 0x36, 0x66, 0xdc, 0x1b, 0x5b, 0xa1, 0xd3, 0x11, 0x7d, 0x63, 0xd5, + 0xda, 0x4a, 0xfe, 0x2a, 0x32, 0xe8, 0xbe, 0xb1, 0x6a, 0xa3, 0x3c, 0xb3, + 0x6a, 0xca, 0x25, 0xb2, 0x60, 0x67, 0xd3, 0x9a, 0x2c, 0xd7, 0x45, 0xd8, + 0xe5, 0x4c, 0x89, 0xdd, 0x31, 0x73, 0xc5, 0xcc, 0x27, 0xe7, 0xb3, 0xa9, + 0x9e, 0xb7, 0x71, 0x59, 0x2a, 0x2e, 0xcf, 0x3a, 0xf0, 0x8c, 0x94, 0x7e, + 0x70, 0xd3, 0xb7, 0x7c, 0x6c, 0x21, 0x4b, 0xc6, 0x1c, 0x44, 0x18, 0xdc, + 0xc9, 0xaf, 0x3c, 0x5b, 0x93, 0x44, 0xb6, 0x26, 0x21, 0xdb, 0x94, 0x94, + 0x28, 0x2a, 0xb2, 0x6c, 0xc3, 0x04, 0xe8, 0x6d, 0x5c, 0x74, 0xc9, 0x08, + 0x5b, 0x45, 0x1e, 0xb5, 0x6a, 0x65, 0xf5, 0x35, 0x1b, 0x6a, 0xa3, 0xf9, + 0xc8, 0x06, 0x1f, 0xcb, 0x81, 0x47, 0x1d, 0xe6, 0x13, 0x6a, 0x03, 0x29, + 0x45, 0x08, 0xa4, 0x03, 0xf6, 0x2b, 0x22, 0xaa, 0xe3, 0x15, 0x5b, 0x41, + 0xb7, 0x43, 0x46, 0x80, 0x74, 0x87, 0xd8, 0x84, 0x62, 0xa9, 0xe2, 0x4f, + 0x7e, 0x8d, 0xc0, 0xf4, 0xf1, 0xb8, 0xe3, 0xa1, 0x01, 0xf6, 0xa5, 0x1f, + 0x04, 0xf3, 0xda, 0xf1, 0x9c, 0x41, 0x92, 0x51, 0x4c, 0xa8, 0x93, 0xfa, + 0x98, 0x4c, 0x3e, 0x74, 0xfc, 0x5b, 0x87, 0x22, 0x2b, 0x5f, 0xfd, 0x43, + 0x16, 0x97, 0x59, 0x3e, 0x15, 0x69, 0x7e, 0x39, 0x37, 0x95, 0x2c, 0x43, + 0xee, 0xc9, 0xa2, 0x78, 0xc7, 0x82, 0x14, 0xb1, 0x07, 0x22, 0xc6, 0x76, + 0x3d, 0x9d, 0x1e, 0xe1, 0x35, 0xf6, 0x97, 0x6d, 0xd7, 0xf4, 0xb1, 0xf9, + 0xb6, 0xb1, 0x95, 0xcb, 0xbf, 0x84, 0x3b, 0x14, 0x3d, 0xd1, 0x93, 0xec, + 0x53, 0x5d, 0x43, 0x65, 0x13, 0x65, 0x40, 0x8b, 0xae, 0xc9, 0x87, 0x21, + 0xf2, 0x6c, 0x20, 0xeb, 0x35, 0x03, 0x66, 0x3a, 0xbb, 0x85, 0xe4, 0xc2, + 0xee, 0xe2, 0x7a, 0x9c, 0x9e, 0x80, 0xa6, 0x50, 0xc7, 0xb1, 0x28, 0x71, + 0x73, 0xd2, 0xa7, 0x28, 0xb1, 0xae, 0xd6, 0x05, 0x1e, 0x65, 0x9e, 0xa5, + 0x56, 0x19, 0x07, 0x9e, 0x89, 0x4f, 0x5a, 0xc4, 0x62, 0x11, 0x7d, 0x86, + 0xda, 0x38, 0x91, 0xee, 0x74, 0x52, 0x1b, 0xd5, 0x79, 0x7e, 0x23, 0x6c, + 0x80, 0x09, 0x0c, 0x7b, 0x73, 0xd8, 0x18, 0x89, 0x67, 0x39, 0xca, 0xa2, + 0x45, 0xa2, 0xc6, 0xcc, 0x31, 0x92, 0x56, 0x98, 0x59, 0xc6, 0xa8, 0xa7, + 0xb0, 0xe1, 0x4d, 0x70, 0x43, 0x11, 0x3b, 0x7e, 0x64, 0x6a, 0x0f, 0x23, + 0xc0, 0x23, 0x87, 0x01, 0x22, 0x29, 0x8f, 0x88, 0x0f, 0x93, 0xf3, 0x90, + 0xbf, 0xd5, 0x8d, 0x29, 0x9f, 0x58, 0x05, 0x37, 0x13, 0x21, 0x5f, 0xe4, + 0x73, 0x85, 0xae, 0xcc, 0xf0, 0x85, 0x65, 0xdc, 0xa9, 0x7c, 0xf8, 0x03, + 0xb3, 0x95, 0x39, 0x9b, 0xf3, 0x4d, 0x5a, 0x0e, 0xa5, 0xce, 0x20, 0x41, + 0x40, 0x68, 0x65, 0x15, 0xc4, 0x1b, 0xb1, 0x8a, 0xb9, 0xc5, 0x0f, 0x60, + 0xaa, 0xa0, 0xf6, 0xda, 0x07, 0xd7, 0x13, 0x96, 0xf7, 0x06, 0xc4, 0x88, + 0xa2, 0x2c, 0x64, 0x80, 0x38, 0x89, 0x57, 0xcf, 0xa2, 0x0d, 0xb8, 0xc6, + 0x1b, 0xae, 0xb2, 0x0d, 0x7d, 0x8a, 0x68, 0xe0, 0xcf, 0xd9, 0x67, 0x25, + 0xde, 0xa6, 0x92, 0xb2, 0x1e, 0xbe, 0x7b, 0x4a, 0xa8, 0xcc, 0x33, 0x7f, + 0xb0, 0x1c, 0xc4, 0x80, 0xce, 0x01, 0x54, 0x04, 0x45, 0x7e, 0xee, 0x65, + 0x69, 0x67, 0x5d, 0xc3, 0x94, 0xbd, 0xc0, 0x78, 0xa4, 0xab, 0xf0, 0x35, + 0xcb, 0xf2, 0x74, 0xa9, 0xb6, 0xaa, 0x26, 0x65, 0x41, 0x3d, 0xa9, 0x67, + 0x6b, 0x61, 0xa7, 0xa4, 0xaf, 0xfa, 0x12, 0x86, 0x24, 0x10, 0xe0, 0x15, + 0x9d, 0x80, 0xc6, 0xe8, 0xe9, 0x7e, 0xcf, 0x19, 0xda, 0xaf, 0x42, 0x25, + 0x22, 0x08, 0x95, 0x78, 0xb8, 0x38, 0xf0, 0xbb, 0xf9, 0x66, 0x3d, 0x4f, + 0xad, 0x44, 0x61, 0x0a, 0x52, 0xe4, 0x48, 0x80, 0x2f, 0x1e, 0xd4, 0x78, + 0x0b, 0xca, 0x39, 0xfc, 0xa0, 0x14, 0x61, 0x44, 0xad, 0xa7, 0x2c, 0x29, + 0xe1, 0x1a, 0x8f, 0x11, 0xbf, 0x26, 0x9e, 0xd0, 0x77, 0x89, 0x6d, 0x83, + 0xb3, 0x60, 0x2e, 0xf6, 0xb5, 0xfc, 0xcb, 0x6b, 0x02, 0xcf, 0x03, 0x63, + 0xa1, 0x58, 0x29, 0xab, 0x13, 0xe6, 0x0d, 0x66, 0x07, 0x96, 0xd7, 0xca, + 0x26, 0xee, 0xaa, 0x93, 0x89, 0x9e, 0x86, 0x21, 0x92, 0x70, 0x0f, 0x7f, + 0x0b, 0x88, 0x07, 0x49, 0xf8, 0x3b, 0x54, 0xc5, 0xf5, 0x27, 0xa3, 0x8a, + 0x91, 0xf3, 0x16, 0x5d, 0x9c, 0x2c, 0x4d, 0xd0, 0x96, 0x44, 0x84, 0xf4, + 0xe6, 0xe0, 0xed, 0x13, 0xcf, 0xcf, 0xf6, 0x43, 0x89, 0x9f, 0x58, 0x4b, + 0x1c, 0x05, 0x53, 0xc6, 0xb4, 0x8f, 0x0c, 0x05, 0xcd, 0x37, 0x72, 0x91, + 0x07, 0xfe, 0x25, 0x1e, 0x06, 0xf1, 0x04, 0x3e, 0x67, 0x9a, 0x28, 0xe3, + 0xc9, 0x0b, 0xd3, 0xe0, 0x70, 0x14, 0x7a, 0x66, 0xfc, 0x36, 0x77, 0x54, + 0x4c, 0x9b, 0x20, 0xce, 0x16, 0x3f, 0xc2, 0x1f, 0x58, 0x19, 0x16, 0xd6, + 0x2b, 0x89, 0x2a, 0xf3, 0xac, 0x62, 0xb3, 0xc4, 0x6b, 0xd2, 0xad, 0x4d, + 0x08, 0x91, 0xc4, 0xa5, 0x5b, 0xcf, 0xc8, 0x93, 0x98, 0x39, 0x6b, 0x1d, + 0xff, 0xd8, 0xee, 0x38, 0xf5, 0x4e, 0x60, 0xb7, 0x19, 0x89, 0x92, 0x8f, + 0xe9, 0xa9, 0x50, 0x14, 0x05, 0x6b, 0x54, 0x7d, 0xa5, 0x8b, 0x8b, 0x10, + 0xca, 0x81, 0x18, 0xac, 0x5b, 0x4e, 0x57, 0xa1, 0xaa, 0x66, 0x3a, 0xed, + 0x80, 0x05, 0x30, 0xbd, 0x8b, 0x69, 0x43, 0xc4, 0xb2, 0xdd, 0xf1, 0xb1, + 0xa9, 0xc8, 0x29, 0x05, 0x93, 0x55, 0x9d, 0x73, 0x8a, 0x9d, 0xe4, 0x18, + 0xf4, 0xfb, 0x77, 0x59, 0xd6, 0xf0, 0xf6, 0x7b, 0x0b, 0x61, 0x51, 0xdb, + 0x42, 0xbe, 0x7f, 0x0a, 0x25, 0xb4, 0x0e, 0xf6, 0xab, 0xc8, 0x5c, 0x93, + 0x65, 0xb5, 0xf6, 0x53, 0x2b, 0x3d, 0x3c, 0x00, 0x46, 0xc5, 0x8b, 0x13, + 0x7c, 0xd9, 0x21, 0x8e, 0xce, 0xd9, 0xa0, 0x8b, 0x9a, 0xcf, 0xc0, 0xdb, + 0xf2, 0x10, 0x11, 0x2a, 0xd7, 0xe4, 0xb0, 0xfa, 0x93, 0x27, 0x31, 0x23, + 0x3c, 0xf0, 0xb5, 0xac, 0xb8, 0xbb, 0x01, 0x4d, 0x56, 0xb0, 0xfa, 0x4a, + 0x3a, 0x0a, 0xde, 0xac, 0x94, 0xab, 0x2b, 0xaa, 0x87, 0x69, 0xe0, 0xd9, + 0x12, 0x2e, 0xc8, 0xd2, 0xae, 0x0c, 0xea, 0xee, 0x29, 0x9c, 0x9b, 0x46, + 0xb1, 0x52, 0xa7, 0x85, 0x82, 0xc6, 0xe6, 0x6c, 0x2a, 0xb8, 0x64, 0xf0, + 0xc9, 0x75, 0xb5, 0x1e, 0xcd, 0x07, 0xd7, 0x7e, 0x00, 0x19, 0x88, 0xa9, + 0x54, 0xd5, 0xc2, 0x17, 0x59, 0x3a, 0x21, 0xbb, 0xb2, 0x26, 0x4b, 0x67, + 0xe2, 0xe7, 0x50, 0xfc, 0xdc, 0xf2, 0x9f, 0x4b, 0xf8, 0xf7, 0xeb, 0x17, + 0xfa, 0x35, 0x8d, 0x4d, 0xc7, 0xc3, 0x7e, 0xef, 0x86, 0x7b, 0x32, 0x05, + 0x54, 0x7d, 0x1e, 0x47, 0x84, 0xaf, 0xcb, 0xf0, 0x5f, 0x56, 0xc2, 0xd5, + 0xcc, 0x39, 0xe8, 0xba, 0x2a, 0xd7, 0x05, 0xbe, 0x36, 0x24, 0x5c, 0x0f, + 0x67, 0xa7, 0x47, 0x94, 0xba, 0xd7, 0x60, 0x43, 0xd8, 0xa7, 0x75, 0xaa, + 0x3b, 0x36, 0xe3, 0xa9, 0x11, 0x6d, 0xac, 0x70, 0xc2, 0xab, 0xe5, 0xf2, + 0x82, 0x41, 0x75, 0x01, 0x5b, 0x65, 0x8c, 0x65, 0x8b, 0x1a, 0x9e, 0x07, + 0xb4, 0x53, 0xb5, 0x8e, 0x2d, 0x1f, 0xbf, 0x32, 0x98, 0xd8, 0x50, 0x84, + 0x3e, 0x19, 0x9f, 0x6e, 0x2e, 0xce, 0x75, 0xd0, 0x6f, 0x1f, 0x2b, 0x4c, + 0x22, 0xbe, 0x0b, 0x3a, 0x84, 0x6f, 0x41, 0x5f, 0x55, 0x55, 0xa7, 0x63, + 0x97, 0xf1, 0xae, 0x28, 0x01, 0xf3, 0xf8, 0x6c, 0x9d, 0xf8, 0x17, 0xfd, + 0x6d, 0x05, 0x17, 0x0c, 0x79, 0x93, 0x25, 0x91, 0x12, 0x31, 0x8d, 0xa5, + 0x30, 0x82, 0x2e, 0x6d, 0xa5, 0xbb, 0x58, 0x4e, 0xb2, 0x24, 0x71, 0x19, + 0x1a, 0x4b, 0x42, 0xd5, 0xe5, 0x82, 0x52, 0x29, 0x9c, 0x21, 0xda, 0xd3, + 0xb9, 0x23, 0x55, 0x3e, 0x7e, 0xfc, 0xcf, 0x10, 0x6a, 0x00, 0x93, 0x77, + 0xc7, 0x14, 0xfb, 0xa5, 0xb0, 0x83, 0x32, 0x80, 0xbc, 0x47, 0x55, 0x0b, + 0xf2, 0x2f, 0x00, 0xba, 0xc4, 0x60, 0x87, 0x3b, 0x70, 0x0d, 0x32, 0x96, + 0xc2, 0xf8, 0xbb, 0xb4, 0x25, 0x17, 0x32, 0x2a, 0x30, 0x03, 0xa3, 0x38, + 0xbd, 0x0d, 0xc0, 0x5c, 0xb4, 0x5b, 0xbe, 0x5b, 0x07, 0x66, 0x63, 0xd1, + 0x72, 0x3a, 0xe2, 0xf7, 0x3d, 0x58, 0xb0, 0x34, 0xc2, 0x45, 0xfc, 0xc8, + 0x5a, 0x04, 0xdd, 0xf6, 0x03, 0xd7, 0x75, 0x3c, 0x8a, 0x4d, 0x76, 0x58, + 0xe9, 0x2f, 0x2e, 0x72, 0x3e, 0x49, 0x09, 0x57, 0xc2, 0xfc, 0x61, 0x69, + 0xeb, 0x7e, 0xe7, 0xfa, 0xfc, 0xf8, 0xfc, 0x30, 0x9f, 0x1a, 0xbe, 0x78, + 0xec, 0x53, 0x3c, 0xf8, 0xfd, 0x37, 0x96, 0x9b, 0x23, 0x62, 0xfb, 0xbf, + 0xff, 0x96, 0x82, 0xfe, 0xfb, 0x6f, 0x1d, 0x98, 0x62, 0x33, 0x47, 0x5d, + 0xdb, 0x6c, 0x79, 0x25, 0xa0, 0x7f, 0x0e, 0x0a, 0x33, 0xb8, 0xaa, 0x6a, + 0x2d, 0x91, 0x5d, 0x28, 0x9c, 0xd9, 0xd0, 0x86, 0xcd, 0x30, 0x4a, 0x0c, + 0x7b, 0xe0, 0x32, 0x33, 0xe7, 0x8e, 0x2d, 0xc7, 0x32, 0x97, 0xb6, 0x8e, + 0xcf, 0x8f, 0x6f, 0xa5, 0xc6, 0xf5, 0xf5, 0xc5, 0xb5, 0xb4, 0x10, 0xb3, + 0xe1, 0x67, 0x14, 0x1d, 0x6b, 0xf2, 0xcd, 0xe5, 0xf1, 0xc1, 0xc1, 0x8d, + 0xbc, 0x60, 0x44, 0x2c, 0x06, 0x5d, 0x03, 0x96, 0xcd, 0x85, 0x32, 0xe8, + 0x9b, 0xc4, 0x03, 0x20, 0xc2, 0x2d, 0x84, 0x01, 0xc4, 0x90, 0x45, 0x04, + 0x91, 0x21, 0xe0, 0x68, 0x60, 0x12, 0x2e, 0xb6, 0x15, 0xf9, 0xb0, 0x71, + 0x0b, 0x96, 0x59, 0x0a, 0xb7, 0xd5, 0x16, 0xca, 0x2a, 0x0c, 0xf9, 0x18, + 0x34, 0xce, 0x0e, 0x2c, 0x4b, 0x4d, 0x4c, 0x35, 0x6b, 0x1e, 0xe0, 0x39, + 0x90, 0x05, 0x85, 0x95, 0x22, 0x73, 0xca, 0x6a, 0xd2, 0x17, 0xb9, 0x80, + 0x43, 0x4b, 0x2a, 0xc8, 0x5f, 0x25, 0xf6, 0x95, 0xb1, 0x8f, 0x04, 0x50, + 0x1b, 0xd9, 0xcc, 0x15, 0x9f, 0xe3, 0xe1, 0x9e, 0x28, 0xa6, 0x98, 0xe1, + 0x0b, 0x67, 0xb2, 0xb0, 0x30, 0x9f, 0x2f, 0xe8, 0x19, 0xef, 0x52, 0x70, + 0x7c, 0x7a, 0x74, 0x3a, 0xf5, 0xfd, 0xfb, 0xc2, 0x02, 0x88, 0xbc, 0x43, + 0xbc, 0x81, 0x22, 0xef, 0xf5, 0x58, 0x5e, 0xed, 0x4b, 0xd4, 0x91, 0xc6, + 0x4e, 0xe0, 0x49, 0x11, 0x1c, 0x69, 0x48, 0x2c, 0x4b, 0x6a, 0x41, 0x28, + 0x73, 0x7c, 0x2a, 0x91, 0x0e, 0x1b, 0x95, 0x98, 0xa2, 0x10, 0x3b, 0x00, + 0x46, 0x30, 0xb5, 0xb3, 0xb9, 0xfe, 0x02, 0xf8, 0x7d, 0xe2, 0xb7, 0x91, + 0x67, 0xc2, 0x2e, 0xbe, 0xb2, 0x50, 0x51, 0x19, 0x2f, 0x12, 0xac, 0xf3, + 0xa7, 0xe1, 0xb7, 0x5c, 0xd6, 0x0c, 0xc6, 0xc6, 0x02, 0x9e, 0x2f, 0x78, + 0x33, 0x06, 0x9b, 0x5d, 0x91, 0xa0, 0x00, 0x2b, 0x2e, 0x79, 0x68, 0x3d, + 0x00, 0xbd, 0xc2, 0x5c, 0x06, 0xb1, 0x0f, 0x06, 0xce, 0x7b, 0xd4, 0xbf, + 0x27, 0xb4, 0xa7, 0xc8, 0x25, 0x59, 0xfd, 0xfe, 0x5d, 0xc1, 0x06, 0x34, + 0x0a, 0x58, 0xd5, 0xb0, 0x0e, 0xd2, 0x4c, 0x86, 0x18, 0xc9, 0x06, 0x2c, + 0xb0, 0xa0, 0x74, 0x84, 0x9c, 0xac, 0x58, 0x51, 0xd9, 0x14, 0x3f, 0x68, + 0xf9, 0xd4, 0x63, 0xe1, 0xb1, 0x0c, 0x5f, 0x60, 0x64, 0xf4, 0x98, 0xa5, + 0x73, 0x17, 0x1d, 0xbe, 0x26, 0x2d, 0x3d, 0xb0, 0x78, 0x8a, 0x8f, 0x78, + 0xc6, 0x0e, 0xa1, 0xd4, 0x66, 0xc1, 0x94, 0xf9, 0x48, 0xc7, 0x98, 0x47, + 0x19, 0xa0, 0x80, 0x92, 0x41, 0xb1, 0x3e, 0x1c, 0x57, 0x64, 0x5e, 0x19, + 0xcb, 0x90, 0x8c, 0x70, 0x95, 0x36, 0x64, 0x66, 0xac, 0xb2, 0x86, 0xf4, + 0x41, 0xc0, 0xec, 0x1b, 0x2c, 0x6e, 0xa1, 0x02, 0x5f, 0xcc, 0x7c, 0x0d, + 0x99, 0x9d, 0x14, 0xca, 0x9a, 0xa3, 0x23, 0x17, 0x34, 0xd7, 0xdc, 0xeb, + 0x11, 0xcb, 0x54, 0x90, 0xca, 0xbd, 0xbe, 0xf9, 0xee, 0x06, 0xa6, 0x0e, + 0x4e, 0x45, 0x76, 0xc1, 0xa1, 0x1e, 0xf3, 0x1e, 0xcd, 0x0c, 0x77, 0x64, + 0xd9, 0x06, 0xfb, 0x12, 0x7b, 0xb0, 0x09, 0xec, 0x2b, 0x0c, 0x9c, 0x77, + 0xc8, 0x0a, 0x38, 0x23, 0xa7, 0xb6, 0x35, 0xc5, 0xb6, 0xd6, 0xdc, 0x6d, + 0x5b, 0x01, 0x64, 0xf6, 0x20, 0xc9, 0xba, 0x95, 0x8e, 0x58, 0x4d, 0x97, + 0x45, 0xa1, 0x69, 0x60, 0x96, 0x00, 0xe6, 0xbf, 0x0f, 0xcc, 0xe7, 0x44, + 0x08, 0xe3, 0xd6, 0xfc, 0x69, 0xeb, 0x66, 0x07, 0xcb, 0xac, 0x3b, 0xb5, + 0xe1, 0x59, 0x7f, 0x9f, 0x4d, 0xcd, 0xee, 0xe7, 0x8b, 0xfd, 0xc8, 0xfb, + 0xfb, 0x91, 0x2c, 0xac, 0x03, 0x2e, 0x9c, 0x2c, 0x30, 0x22, 0x80, 0xb5, + 0xe7, 0x02, 0x63, 0x0e, 0x0f, 0x40, 0xb5, 0x39, 0xea, 0xa2, 0x46, 0xda, + 0xe5, 0xf0, 0xc1, 0xdb, 0xb4, 0xf3, 0x49, 0xc8, 0xee, 0xd0, 0x16, 0x3b, + 0x78, 0xef, 0xa3, 0xeb, 0xf1, 0x3d, 0x22, 0xbb, 0xd3, 0xbc, 0x34, 0xf6, + 0xcc, 0x72, 0x59, 0x97, 0xd8, 0x50, 0x1c, 0xd8, 0x9c, 0x42, 0xa6, 0x6c, + 0xc8, 0xcb, 0x50, 0x1f, 0xb1, 0x91, 0xc4, 0xe6, 0xca, 0x80, 0x58, 0x1a, + 0x03, 0x4f, 0x60, 0x30, 0x78, 0x1f, 0x83, 0x01, 0xc7, 0x20, 0x65, 0xca, + 0xda, 0x20, 0x8d, 0x44, 0xe8, 0x3a, 0x58, 0xef, 0xfc, 0xdd, 0x06, 0x62, + 0xb7, 0xe0, 0xfd, 0xdd, 0x02, 0xbe, 0x5b, 0x0f, 0x5b, 0x2e, 0xdf, 0x2a, + 0x48, 0x6f, 0xb5, 0x2d, 0x4f, 0x81, 0x0d, 0x04, 0x58, 0xf7, 0x3d, 0x41, + 0xb9, 0x82, 0x89, 0x61, 0x3c, 0x70, 0x33, 0x09, 0x57, 0x98, 0x97, 0xaa, + 0xd3, 0x22, 0x72, 0xc1, 0xc4, 0x21, 0xc5, 0x6a, 0x73, 0x27, 0x9c, 0x24, + 0x59, 0x22, 0xbd, 0x84, 0x1c, 0xcb, 0x40, 0x3a, 0x33, 0x6c, 0x5f, 0x87, + 0x48, 0xdc, 0xa5, 0x3d, 0xe1, 0x31, 0x68, 0xd4, 0xfb, 0xa5, 0xfc, 0x95, + 0x5b, 0x5f, 0xfd, 0xd9, 0x81, 0xd2, 0xa2, 0x0c, 0xd6, 0xf7, 0xcc, 0xcc, + 0x2e, 0xc7, 0xa7, 0x85, 0x23, 0xdc, 0xb3, 0x85, 0x6d, 0x35, 0x9a, 0x6e, + 0x4c, 0xbb, 0xc7, 0x68, 0x42, 0x81, 0xcd, 0xa6, 0x10, 0xf2, 0x2c, 0x86, + 0x22, 0xb8, 0xbd, 0x7e, 0x06, 0xc3, 0x59, 0xf4, 0x16, 0x17, 0x6d, 0x9d, + 0xdd, 0xc7, 0x0a, 0x6b, 0x55, 0x12, 0x2c, 0xa3, 0xad, 0xd4, 0x89, 0x46, + 0x72, 0x61, 0x09, 0xb2, 0x22, 0xfc, 0xc1, 0x9f, 0x0e, 0x14, 0xb5, 0x2e, + 0xcb, 0x86, 0x61, 0xd0, 0xed, 0x30, 0x5c, 0xee, 0x48, 0x51, 0x3e, 0x22, + 0x0d, 0x02, 0x08, 0x49, 0x10, 0x9a, 0xba, 0x50, 0xbd, 0xb1, 0x72, 0x80, + 0x66, 0x1d, 0x75, 0xb4, 0xe2, 0x20, 0xca, 0x5f, 0xc4, 0x02, 0x28, 0x0e, + 0x21, 0x18, 0x99, 0x10, 0xd8, 0x68, 0x4f, 0x42, 0xd2, 0x52, 0x69, 0x49, + 0x02, 0xbe, 0x7b, 0xa8, 0x4d, 0x31, 0x44, 0xfc, 0x9a, 0x40, 0x7d, 0x8f, + 0x8b, 0x16, 0x92, 0xd8, 0x09, 0xf8, 0x83, 0x3f, 0x8e, 0x29, 0x67, 0xa4, + 0x94, 0x8b, 0xac, 0x32, 0x85, 0x2d, 0x48, 0x87, 0x16, 0x98, 0x60, 0x54, + 0x6d, 0x0a, 0x05, 0xc0, 0xc1, 0xcb, 0xc5, 0x01, 0xf2, 0x0c, 0x30, 0x4a, + 0x05, 0xc6, 0x07, 0x73, 0xc6, 0x43, 0x43, 0x62, 0x53, 0x82, 0x79, 0x20, + 0x20, 0x2d, 0xb9, 0xe9, 0x41, 0x16, 0xd7, 0x0e, 0x58, 0x22, 0x31, 0x99, + 0x8e, 0x54, 0xb7, 0x90, 0xb4, 0x42, 0x9c, 0xf2, 0x63, 0x92, 0xe7, 0xc5, + 0xdf, 0xf0, 0xe0, 0x85, 0xe1, 0x3f, 0xd7, 0x44, 0x4c, 0xf2, 0x0c, 0x16, + 0x12, 0xef, 0x40, 0xde, 0x8c, 0xff, 0x49, 0xac, 0x51, 0x43, 0x0d, 0x7d, + 0x23, 0xfa, 0x0b, 0xc7, 0x38, 0x9b, 0xb1, 0x09, 0x87, 0xf8, 0xce, 0xb2, + 0xc8, 0x9f, 0xce, 0x59, 0x4d, 0xa7, 0xbb, 0xf9, 0x09, 0x02, 0xeb, 0x4f, + 0xd9, 0xf6, 0x26, 0x19, 0x74, 0x25, 0xdf, 0x6b, 0x1b, 0x4b, 0x90, 0x34, + 0x14, 0xe4, 0xb8, 0x1a, 0x19, 0xa0, 0x51, 0x31, 0x3e, 0xf4, 0xf9, 0xa5, + 0x2e, 0xb1, 0xef, 0xf4, 0x09, 0xa7, 0x14, 0x9e, 0x7d, 0xa3, 0x80, 0x3a, + 0x75, 0x29, 0x73, 0x59, 0x58, 0x5f, 0x92, 0x20, 0xdd, 0x4e, 0x09, 0x24, + 0x51, 0xbc, 0x92, 0xb2, 0x5d, 0xfb, 0x2f, 0x5d, 0xf9, 0xf2, 0xdf, 0xfa, + 0xd7, 0x82, 0xaa, 0x6e, 0xff, 0x47, 0x49, 0xc7, 0x23, 0xcc, 0xc6, 0xbf, + 0x54, 0xbe, 0xd6, 0xc1, 0x67, 0x08, 0x4f, 0x00, 0x96, 0x49, 0x55, 0x1f, + 0x14, 0xbd, 0xdd, 0x03, 0x75, 0xa3, 0xce, 0xa9, 0x33, 0xc4, 0xde, 0x1e, + 0x82, 0xe2, 0x4b, 0x55, 0x5f, 0xdb, 0xf0, 0x2b, 0x53, 0x88, 0xdd, 0x35, + 0xde, 0xea, 0xd1, 0x41, 0xd2, 0xb2, 0xc2, 0xe6, 0x93, 0x1f, 0x37, 0xc0, + 0x61, 0x8a, 0x66, 0x3b, 0x9a, 0x16, 0x7d, 0xbb, 0x6e, 0xd4, 0xf2, 0xa3, + 0xe9, 0x23, 0x06, 0x21, 0xcc, 0xad, 0x2a, 0x29, 0x95, 0xf2, 0xfe, 0x7c, + 0x0a, 0x5c, 0x28, 0xf3, 0x43, 0x1c, 0xdd, 0xa4, 0x85, 0xa3, 0x66, 0x97, + 0x74, 0xc2, 0x16, 0x69, 0x3b, 0xb9, 0x38, 0x0d, 0x14, 0x5b, 0x73, 0xd4, + 0xb0, 0x4e, 0x9d, 0xa7, 0xb8, 0x81, 0x05, 0x7a, 0x6b, 0x67, 0x7c, 0x36, + 0x56, 0xeb, 0x53, 0x16, 0x31, 0xb5, 0xc6, 0x22, 0xb0, 0x06, 0x67, 0xd6, + 0x50, 0x55, 0x6b, 0x2b, 0x8e, 0xba, 0xad, 0x64, 0x55, 0x87, 0xd7, 0x34, + 0x0d, 0x50, 0xc5, 0xb8, 0xbc, 0xa1, 0xf9, 0xae, 0x76, 0x33, 0x7b, 0x68, + 0x91, 0x68, 0xb3, 0xbf, 0x3b, 0xde, 0x63, 0x95, 0xdd, 0x39, 0xb8, 0x1a, + 0x45, 0x4e, 0x3d, 0x63, 0x00, 0x9d, 0x8e, 0x9c, 0x72, 0x76, 0xad, 0x38, + 0x0e, 0x11, 0x68, 0xd9, 0x80, 0x56, 0x4e, 0x51, 0xb1, 0xb8, 0x08, 0x1e, + 0x1d, 0x3a, 0x9b, 0x9e, 0x05, 0x48, 0x4f, 0xd4, 0x9a, 0x07, 0x3f, 0x90, + 0xfe, 0xe6, 0x20, 0x7f, 0x29, 0x8c, 0xff, 0x9f, 0x86, 0x3f, 0xe1, 0x78, + 0x73, 0x39, 0xcd, 0x4f, 0xa1, 0x73, 0xe4, 0x84, 0x58, 0x3c, 0x9e, 0x21, + 0x72, 0xdf, 0x19, 0xf2, 0x53, 0x90, 0x98, 0x4a, 0x34, 0x3f, 0x34, 0xd4, + 0xff, 0x42, 0x5a, 0xa9, 0xe1, 0xbc, 0x51, 0x06, 0x85, 0x48, 0x16, 0xf9, + 0xe1, 0x2a, 0x73, 0x69, 0xe0, 0x93, 0x68, 0x41, 0xde, 0x8e, 0x06, 0x0c, + 0xea, 0x41, 0xe5, 0x36, 0x79, 0x27, 0xed, 0xcf, 0x61, 0x8a, 0xc9, 0x52, + 0x85, 0x19, 0xa6, 0x5c, 0xf3, 0xd8, 0x5a, 0x3a, 0x03, 0x04, 0x93, 0xe2, + 0xfc, 0xdf, 0x2c, 0xfd, 0xd0, 0x10, 0x5d, 0xcf, 0x19, 0xb8, 0x80, 0xbb, + 0x40, 0x09, 0x4a, 0x68, 0xa7, 0x20, 0x43, 0x41, 0x0b, 0xc9, 0x16, 0xd8, + 0x2e, 0x14, 0xe3, 0x0b, 0x06, 0x5d, 0x5c, 0xa4, 0x0b, 0x86, 0xb3, 0xb8, + 0xd8, 0x51, 0x1c, 0x28, 0xbc, 0x26, 0xef, 0x14, 0x21, 0x39, 0x4c, 0xb0, + 0x54, 0xcd, 0xca, 0xd1, 0x0c, 0x6c, 0x61, 0x9a, 0xd0, 0x3f, 0x27, 0x51, + 0xfa, 0xeb, 0x34, 0x62, 0xcc, 0xf4, 0x3c, 0x71, 0x69, 0x16, 0x3b, 0xa3, + 0xd5, 0xec, 0x99, 0xc2, 0x32, 0x2f, 0x1a, 0xa7, 0x6b, 0x4b, 0x71, 0x12, + 0xda, 0xf6, 0x1c, 0xcb, 0xba, 0x75, 0xdc, 0xed, 0x39, 0xfd, 0xc9, 0xb1, + 0x6b, 0xd4, 0x08, 0x21, 0x26, 0x53, 0x34, 0x33, 0x17, 0x28, 0xab, 0x17, + 0x72, 0xa1, 0xb2, 0x81, 0xf7, 0xc0, 0xb2, 0x39, 0x9a, 0x05, 0x95, 0x37, + 0x70, 0x16, 0x7a, 0x1f, 0x0a, 0xa6, 0xe6, 0xc7, 0x5f, 0x8f, 0x05, 0x54, + 0x77, 0xc4, 0x61, 0xef, 0x39, 0xaf, 0x43, 0xd3, 0x4c, 0x84, 0x64, 0x3b, + 0x3f, 0x80, 0x47, 0xfd, 0xec, 0xc4, 0xdf, 0xb0, 0x0a, 0x32, 0xab, 0x63, + 0xa2, 0x3e, 0xea, 0xb8, 0x86, 0x2f, 0xba, 0x14, 0x7b, 0x7b, 0x50, 0x8b, + 0x25, 0xf9, 0xef, 0x8c, 0x16, 0xd3, 0x9e, 0x6b, 0xca, 0xd8, 0x1d, 0x55, + 0x6f, 0xb3, 0x99, 0xe7, 0x8e, 0x29, 0xaa, 0x80, 0xf0, 0x86, 0x5c, 0x7b, + 0xc7, 0xae, 0xd9, 0x99, 0xf5, 0xb4, 0x69, 0x23, 0xc8, 0xc4, 0x66, 0x15, + 0x7b, 0x0f, 0x18, 0x8f, 0x5c, 0xff, 0xef, 0x33, 0xed, 0xb9, 0x8e, 0x6e, + 0x1e, 0xed, 0xc6, 0x42, 0xe5, 0x4f, 0x33, 0xeb, 0x6b, 0x71, 0x3e, 0xfe, + 0xf7, 0xd9, 0x75, 0x8f, 0xdb, 0x35, 0x3b, 0x47, 0x97, 0x72, 0x84, 0xd3, + 0x18, 0xc1, 0xbf, 0xe6, 0xff, 0x27, 0xd1, 0x94, 0x05, 0x45, 0xef, 0x1c, + 0xd7, 0xe4, 0xc8, 0xc7, 0x57, 0xb3, 0xe7, 0x32, 0xf3, 0x63, 0x4f, 0x7e, + 0xb9, 0xf6, 0x4f, 0x8d, 0x3d, 0xf3, 0xcf, 0x90, 0x72, 0x98, 0x40, 0x54, + 0x8d, 0xbc, 0x1b, 0x7b, 0xf2, 0x0b, 0xeb, 0xbf, 0x3c, 0xf6, 0xa8, 0x9c, + 0xa8, 0xa9, 0x4b, 0xb5, 0x34, 0xf2, 0x4e, 0x74, 0x68, 0xe6, 0xe8, 0x4e, + 0xa7, 0xe3, 0x63, 0x7a, 0xcf, 0x8a, 0x22, 0xad, 0x1d, 0x7f, 0x1f, 0xf1, + 0xa2, 0x08, 0xbc, 0xb8, 0x63, 0x0f, 0x9c, 0xc0, 0xc7, 0x4e, 0x40, 0x33, + 0x24, 0x28, 0xb1, 0xe7, 0xdf, 0xb4, 0xbe, 0x7f, 0x8f, 0x3f, 0xb6, 0xac, + 0x02, 0x49, 0x3e, 0x1f, 0x37, 0xfd, 0xd4, 0xc7, 0x96, 0x5f, 0x68, 0x43, + 0x16, 0xf8, 0x57, 0x11, 0x9f, 0x89, 0xb9, 0xc1, 0x8f, 0xc5, 0x5c, 0x2e, + 0x56, 0x87, 0x1d, 0x11, 0xb1, 0xaa, 0xdf, 0x60, 0x57, 0x8d, 0x72, 0x2d, + 0x3a, 0x63, 0x79, 0x27, 0x51, 0x0d, 0x0f, 0x98, 0xc2, 0x1a, 0xa6, 0x0d, + 0xf1, 0x61, 0x1b, 0x4d, 0x5f, 0x8d, 0xb2, 0x82, 0x8e, 0x25, 0xea, 0x14, + 0xe8, 0x9e, 0x19, 0x84, 0x12, 0x55, 0xce, 0x66, 0xb6, 0x90, 0x18, 0x4a, + 0x9b, 0x64, 0x4b, 0x99, 0xba, 0x88, 0xb2, 0x01, 0x21, 0x75, 0xb3, 0x44, + 0xb6, 0x66, 0x8f, 0x8b, 0x35, 0x27, 0x57, 0xc1, 0x10, 0xa5, 0x18, 0x0c, + 0x80, 0xe5, 0xe0, 0x0a, 0x23, 0x4f, 0x9d, 0x88, 0x89, 0x82, 0xa5, 0x80, + 0x7f, 0x90, 0x99, 0x8e, 0x75, 0x76, 0x5c, 0x00, 0x54, 0xed, 0x8b, 0x73, + 0x62, 0x85, 0x9f, 0xa1, 0x43, 0xcc, 0xbd, 0xf4, 0x1c, 0x17, 0x75, 0x91, + 0xb8, 0x63, 0xd4, 0x58, 0x22, 0xc3, 0xa0, 0xf1, 0xab, 0x04, 0xcd, 0x49, + 0xb8, 0xed, 0xfe, 0xf5, 0xdc, 0x9e, 0x3e, 0x59, 0xe7, 0x4e, 0xac, 0xe5, + 0x40, 0x3e, 0x10, 0x97, 0xa6, 0xf6, 0xe2, 0x22, 0xfc, 0xa7, 0xa0, 0xe4, + 0x30, 0x52, 0x8e, 0x5a, 0xb2, 0xfa, 0xb3, 0x07, 0xed, 0xfc, 0x55, 0x5b, + 0x22, 0x5e, 0x53, 0x67, 0x8c, 0x0b, 0x8b, 0x19, 0x83, 0xce, 0x9c, 0x9f, + 0xcf, 0x3b, 0x30, 0x44, 0x91, 0xb7, 0x5d, 0x5c, 0xec, 0x45, 0x92, 0xc8, + 0x8f, 0x0c, 0xf1, 0xcc, 0x6d, 0x94, 0x0a, 0x9e, 0x35, 0x05, 0x4d, 0xf9, + 0x6b, 0x06, 0xe3, 0xcf, 0x16, 0x67, 0x25, 0x2b, 0x4e, 0x87, 0x65, 0x55, + 0x2c, 0x05, 0x09, 0x89, 0xcf, 0x5e, 0x33, 0xa7, 0xde, 0x1e, 0xb0, 0x6b, + 0x25, 0x7e, 0xeb, 0x6c, 0xe7, 0xdf, 0x3a, 0xdb, 0x99, 0x5b, 0xe7, 0xf9, + 0x01, 0x8a, 0xa7, 0x40, 0xd1, 0x51, 0x84, 0xb8, 0xaf, 0x09, 0x27, 0x70, + 0xfe, 0xd6, 0xc1, 0x59, 0xcb, 0xcd, 0x53, 0xd0, 0x18, 0xaa, 0x53, 0xd4, + 0x65, 0xee, 0x61, 0x71, 0x11, 0x67, 0x8c, 0x1f, 0x3c, 0xf8, 0x42, 0xc2, + 0x02, 0xfe, 0x24, 0x42, 0xf7, 0x1d, 0x8f, 0x2a, 0xd9, 0xce, 0xe4, 0x92, + 0x9f, 0x29, 0x11, 0x03, 0x07, 0xbf, 0xdb, 0x98, 0x9f, 0xd7, 0x42, 0x71, + 0xdd, 0x46, 0x16, 0xde, 0x83, 0xc8, 0x81, 0x3c, 0x76, 0x21, 0xce, 0x3a, + 0xd5, 0x9a, 0x98, 0x3b, 0x33, 0xc8, 0x3a, 0xa3, 0x62, 0x76, 0xfe, 0x09, + 0x1b, 0x4f, 0x3d, 0xe7, 0xdf, 0x24, 0x65, 0x94, 0xc8, 0x56, 0xe3, 0x57, + 0x0a, 0x0e, 0x60, 0x26, 0x1c, 0x1e, 0xd4, 0x03, 0xe5, 0x3a, 0xda, 0x74, + 0xea, 0xa8, 0x50, 0x10, 0x0c, 0x32, 0x21, 0xeb, 0xa6, 0x5f, 0xd0, 0xd7, + 0xba, 0x19, 0x5e, 0x2f, 0x19, 0x86, 0x61, 0x09, 0x3a, 0x98, 0xd3, 0xb3, + 0x38, 0xda, 0xf0, 0xc3, 0xde, 0xf6, 0xa8, 0x35, 0x37, 0xee, 0x62, 0x07, + 0x99, 0x59, 0x9d, 0x9d, 0x4c, 0x20, 0x4a, 0xa4, 0x9e, 0x00, 0xd8, 0x53, + 0x4f, 0x00, 0x12, 0xcb, 0xfa, 0xd9, 0xd4, 0xe2, 0x8d, 0xbb, 0xdc, 0x37, + 0xb2, 0x11, 0x30, 0x22, 0xe6, 0x25, 0x63, 0x4f, 0x0f, 0x46, 0x3a, 0x49, + 0xb9, 0x74, 0xa4, 0x70, 0x17, 0xf3, 0xc3, 0x3a, 0xf9, 0xf6, 0x4b, 0x08, + 0x09, 0x56, 0xd9, 0x51, 0xe5, 0x30, 0x7d, 0xd6, 0x0e, 0xa2, 0xc8, 0xbe, + 0x87, 0x80, 0x64, 0x21, 0xf3, 0xbd, 0x60, 0x80, 0x06, 0x86, 0xef, 0x35, + 0x2e, 0x11, 0xed, 0x4d, 0x3f, 0x9f, 0xd0, 0xd2, 0x63, 0xec, 0xc6, 0x30, + 0xe5, 0x8f, 0x6d, 0x55, 0xa4, 0x90, 0x6f, 0x2c, 0xaf, 0x4f, 0x3d, 0x24, + 0x49, 0x71, 0xa1, 0x27, 0x14, 0x39, 0x4d, 0x70, 0x59, 0x93, 0x59, 0x50, + 0x81, 0xb6, 0x14, 0x9e, 0x76, 0xea, 0xba, 0x0e, 0x1e, 0x6f, 0x34, 0xb0, + 0x18, 0xd9, 0x39, 0x2f, 0x48, 0xa2, 0xa1, 0xe8, 0x1d, 0x89, 0xa3, 0x84, + 0x1d, 0x1a, 0x03, 0x9e, 0x8c, 0xa6, 0xaf, 0xd4, 0xd9, 0xff, 0x32, 0xb3, + 0x6d, 0x12, 0xcf, 0x80, 0x2d, 0xf8, 0xbd, 0x7a, 0x34, 0x2b, 0xef, 0x76, + 0xdd, 0x4c, 0x9b, 0xb3, 0xeb, 0xb8, 0x8a, 0xca, 0x6c, 0x18, 0xe8, 0xd4, + 0x70, 0x6a, 0x56, 0x27, 0x97, 0x16, 0x9e, 0xb3, 0xa5, 0x88, 0x61, 0x97, + 0xe0, 0xd0, 0xa4, 0x31, 0x5d, 0x34, 0xe7, 0x2a, 0x85, 0xf2, 0x4b, 0x14, + 0xfa, 0x33, 0x44, 0xa3, 0x98, 0x68, 0x1a, 0x99, 0x31, 0x5b, 0x74, 0xe0, + 0x78, 0x83, 0x7d, 0x44, 0x51, 0x5c, 0x31, 0x2a, 0xe1, 0x2d, 0x2a, 0x4d, + 0x2c, 0x08, 0x82, 0xbf, 0xd7, 0x96, 0x81, 0x96, 0x29, 0x56, 0x5d, 0x36, + 0x39, 0xab, 0xd8, 0x31, 0xb6, 0x3c, 0xcd, 0xa0, 0x14, 0xdd, 0x63, 0xc6, + 0x9d, 0x29, 0xaa, 0x79, 0xde, 0xf8, 0x7f, 0x10, 0x61, 0x4c, 0x4d, 0x94, + 0x02, 0x67, 0x68, 0xa1, 0x53, 0xb4, 0xcc, 0x60, 0xbe, 0xdf, 0x38, 0x6d, + 0xdc, 0x36, 0xe6, 0x21, 0x0f, 0xde, 0x35, 0x34, 0x3b, 0x3b, 0x53, 0xd5, + 0xa7, 0x5e, 0x66, 0xcb, 0xc0, 0x1c, 0x76, 0xd3, 0x56, 0x9a, 0x7f, 0xbc, + 0x3f, 0xe3, 0xef, 0x34, 0xda, 0x23, 0x3e, 0x00, 0xc4, 0xc8, 0x3b, 0x43, + 0xc4, 0xbe, 0x44, 0x36, 0xb6, 0xd2, 0x6f, 0x9a, 0xfe, 0xc1, 0xd7, 0x0b, + 0x26, 0x79, 0x8e, 0xae, 0x14, 0x66, 0x1e, 0x22, 0x2f, 0x6d, 0x29, 0xcc, + 0x31, 0xf3, 0xab, 0xad, 0x0e, 0x7b, 0x5b, 0x23, 0x39, 0x9e, 0xc4, 0x1e, + 0x2e, 0x22, 0x71, 0xdd, 0x15, 0xbf, 0xd3, 0x81, 0x9c, 0x0e, 0xe0, 0x6c, + 0xc9, 0x13, 0xc1, 0x88, 0x94, 0x93, 0xc8, 0x84, 0xf3, 0xf4, 0xcb, 0x3e, + 0x79, 0x47, 0x4c, 0x62, 0x9a, 0xc2, 0xe4, 0x2e, 0x39, 0x9d, 0xac, 0xc6, + 0xfc, 0xd4, 0xfd, 0x4d, 0xfe, 0x31, 0xad, 0xc2, 0xce, 0xfd, 0xb7, 0x93, + 0xc3, 0x66, 0xcc, 0x32, 0x58, 0xe8, 0x61, 0xf7, 0x43, 0xb5, 0xac, 0xb4, + 0x14, 0x96, 0x8a, 0x70, 0xe4, 0x53, 0xde, 0x6c, 0xfa, 0xc6, 0x34, 0x7a, + 0xee, 0x63, 0xa4, 0x9f, 0xfb, 0xa8, 0x19, 0xaa, 0xae, 0xe3, 0xa7, 0x6f, + 0xec, 0x06, 0x50, 0x50, 0x22, 0x6c, 0x3b, 0x76, 0xd6, 0x73, 0xe7, 0xa7, + 0xa9, 0x2f, 0x56, 0x0c, 0x23, 0xe7, 0xbd, 0x47, 0x0a, 0xd4, 0x6b, 0xf2, + 0x12, 0xf0, 0xcb, 0x57, 0xcd, 0x36, 0x70, 0xbd, 0x58, 0x61, 0xf9, 0xcb, + 0xf4, 0x9a, 0xc5, 0xc5, 0xb9, 0x8f, 0x77, 0x20, 0x3a, 0xa8, 0x54, 0x77, + 0x03, 0xbf, 0xc7, 0x74, 0xd8, 0xce, 0x8b, 0x1e, 0xec, 0xea, 0xd1, 0xde, + 0x0e, 0x27, 0x31, 0x70, 0xb5, 0x64, 0x01, 0x37, 0xa7, 0x90, 0x6b, 0xc9, + 0xbd, 0xac, 0x91, 0x4d, 0x54, 0xa6, 0xbc, 0x83, 0x98, 0x13, 0x92, 0xfb, + 0x37, 0x7a, 0x42, 0xf1, 0x66, 0x25, 0x27, 0x48, 0x5c, 0x5e, 0xdc, 0xbc, + 0xe5, 0xfa, 0x12, 0x6a, 0xc5, 0x3d, 0x6a, 0x46, 0x45, 0xa6, 0x68, 0xe5, + 0x33, 0x66, 0x24, 0xfb, 0xe6, 0x9b, 0xa0, 0x3f, 0x42, 0x2a, 0xfe, 0x63, + 0x8e, 0xf2, 0x0d, 0x17, 0xcf, 0xae, 0xa7, 0x05, 0x6e, 0x9c, 0xdc, 0xe9, + 0xab, 0xdb, 0x06, 0xf7, 0x3e, 0xa2, 0x52, 0x12, 0xe9, 0x75, 0x5e, 0xb8, + 0x94, 0xc1, 0xe9, 0x10, 0x1b, 0xbc, 0xe9, 0x8f, 0x5f, 0xc9, 0xd9, 0xd1, + 0x95, 0x9c, 0x9d, 0xbe, 0x46, 0x8c, 0x41, 0xd5, 0x5b, 0xb0, 0x7d, 0xbf, + 0x9e, 0x5c, 0x2b, 0xc2, 0x08, 0xbf, 0x54, 0x4c, 0x0f, 0xb0, 0xab, 0x45, + 0xe8, 0x7f, 0x42, 0xcf, 0x48, 0xbc, 0x3d, 0xce, 0x8c, 0xb6, 0xd3, 0x97, + 0x8b, 0x30, 0xad, 0xfd, 0x2f, 0xd6, 0xca, 0xcc, 0x88, 0x2f, 0x1b, 0xfd, + 0xa4, 0xe9, 0xf6, 0xdc, 0xbc, 0x4b, 0xcc, 0xf8, 0xee, 0x92, 0xdf, 0x4b, + 0x82, 0x00, 0xa2, 0xe0, 0x42, 0x27, 0x82, 0x26, 0x76, 0x6d, 0xcf, 0x2e, + 0xb6, 0x40, 0xbc, 0xfc, 0x15, 0xbe, 0xce, 0xd0, 0x8e, 0xd2, 0x51, 0x83, + 0x17, 0x7a, 0x0a, 0xe4, 0xdc, 0x4a, 0x92, 0xa3, 0x42, 0xa7, 0x03, 0x9d, + 0x8e, 0x78, 0xff, 0x04, 0x8e, 0x16, 0xa7, 0xe7, 0x23, 0x56, 0x15, 0x8a, + 0x21, 0xd0, 0x17, 0x5b, 0x0b, 0xf1, 0x0f, 0x21, 0x45, 0x23, 0x82, 0x5b, + 0x6a, 0xf4, 0x14, 0xea, 0x7f, 0x3b, 0xb9, 0xb6, 0xde, 0xb4, 0x61, 0x28, + 0xfc, 0x57, 0xb6, 0x68, 0x42, 0xc9, 0x1a, 0x08, 0xec, 0x69, 0xcb, 0x16, + 0xaa, 0xb6, 0x2f, 0x93, 0xb6, 0x6a, 0x93, 0xd2, 0x3d, 0x55, 0x68, 0x22, + 0x21, 0x40, 0x98, 0x13, 0x4b, 0x24, 0xa8, 0x43, 0x88, 0xff, 0xbe, 0xef, + 0x1c, 0x3b, 0xc4, 0x31, 0x61, 0x55, 0xfb, 0x42, 0x2e, 0xbe, 0xc4, 0x3e, + 0x3e, 0x97, 0xcf, 0xc7, 0xe7, 0x40, 0x88, 0xc6, 0xaf, 0xa2, 0x79, 0x9a, + 0x8d, 0x68, 0xb1, 0xc9, 0x65, 0x6c, 0x9c, 0x94, 0x77, 0xb9, 0xb6, 0x81, + 0x9c, 0x42, 0x43, 0xce, 0xc1, 0xa0, 0x8b, 0x39, 0x05, 0x38, 0x02, 0x76, + 0xd2, 0x54, 0x92, 0xae, 0xad, 0x35, 0x68, 0x32, 0x36, 0xec, 0x33, 0xce, + 0x9a, 0xcf, 0x3e, 0xf8, 0xfc, 0xc9, 0xff, 0xab, 0xac, 0xe3, 0x79, 0x33, + 0x6d, 0xff, 0x5e, 0x6b, 0x55, 0x75, 0x90, 0x26, 0x11, 0x28, 0x3a, 0x11, + 0xe8, 0xda, 0xad, 0x20, 0x33, 0x2a, 0x1e, 0xcd, 0x15, 0x16, 0x76, 0xd6, + 0x90, 0x20, 0xe6, 0xcc, 0x2b, 0xb5, 0x55, 0xbd, 0x18, 0xd5, 0x08, 0xc5, + 0x4a, 0x84, 0xb5, 0xad, 0x92, 0x75, 0xc6, 0x6d, 0xa3, 0xe5, 0x56, 0x97, + 0xb6, 0x0a, 0xc6, 0x15, 0x3d, 0xea, 0xc3, 0x6b, 0xf4, 0x46, 0x4a, 0xde, + 0xe6, 0x16, 0x0e, 0x2b, 0x04, 0x2c, 0x4c, 0xec, 0xab, 0x78, 0x58, 0xcb, + 0x9c, 0xf2, 0x46, 0x54, 0x44, 0xab, 0x38, 0xab, 0x2a, 0x9e, 0x04, 0x4d, + 0xf8, 0x1e, 0xbb, 0x1e, 0xd7, 0x01, 0x4f, 0x05, 0x05, 0xee, 0x88, 0x19, + 0x69, 0xba, 0x28, 0x78, 0x58, 0x67, 0x85, 0x2e, 0xa9, 0xe9, 0x16, 0x45, + 0x92, 0x8a, 0xde, 0x31, 0xf5, 0x63, 0x3e, 0x72, 0xc1, 0x90, 0xa3, 0x49, + 0x30, 0xf6, 0xcf, 0xbb, 0xfd, 0x55, 0x65, 0xb1, 0x5c, 0xd6, 0x0f, 0xf3, + 0x04, 0x44, 0x19, 0x7b, 0x3d, 0x35, 0x50, 0xc4, 0xae, 0xa4, 0x0f, 0xfa, + 0x7b, 0x5f, 0xf3, 0xd5, 0x5a, 0x90, 0x87, 0xef, 0x26, 0xa5, 0x6c, 0x8b, + 0xef, 0x58, 0x22, 0xdd, 0x12, 0x85, 0x31, 0x98, 0xf7, 0xe7, 0x36, 0x2f, + 0xeb, 0x7b, 0x0e, 0x86, 0x60, 0x76, 0xc3, 0xa2, 0xc8, 0xa2, 0x98, 0x97, + 0x8b, 0x8a, 0x9c, 0x57, 0x77, 0xea, 0xde, 0x3d, 0xd0, 0xce, 0x32, 0xe4, + 0x18, 0x35, 0xc7, 0x4f, 0x20, 0xac, 0xdf, 0xb2, 0x7d, 0x78, 0x78, 0xca, + 0xcb, 0xd0, 0xb9, 0xab, 0xb7, 0x62, 0x18, 0x3b, 0x7e, 0x31, 0x4f, 0xf1, + 0xa0, 0xea, 0xe3, 0xf9, 0xe8, 0x93, 0x16, 0x0b, 0xbb, 0xee, 0x8a, 0x26, + 0x70, 0x86, 0xbc, 0x5e, 0x3f, 0x4a, 0xb1, 0x0f, 0xdf, 0x4e, 0x8e, 0xcf, + 0x7d, 0x12, 0x63, 0xc4, 0xc7, 0x12, 0x09, 0x66, 0x38, 0x05, 0xcc, 0xf4, + 0x8e, 0xe1, 0x46, 0xd4, 0xc3, 0xb5, 0x35, 0x0e, 0xf5, 0xae, 0x7f, 0x2c, + 0x76, 0x04, 0x8e, 0x22, 0x0a, 0xd3, 0x12, 0xdc, 0xe0, 0x3a, 0xca, 0xcb, + 0xe3, 0xf8, 0x9d, 0x66, 0xfd, 0xfc, 0x39, 0xf6, 0xb8, 0xb5, 0x06, 0x52, + 0x1d, 0xa3, 0xf7, 0x22, 0x9c, 0x76, 0x39, 0xa8, 0x97, 0xd5, 0x60, 0xc4, + 0x31, 0xa9, 0x86, 0x7e, 0x7b, 0x39, 0x13, 0x6e, 0x75, 0x70, 0x15, 0xad, + 0x84, 0x09, 0xc1, 0x2f, 0x7f, 0xd9, 0xc4, 0x2b, 0xb4, 0x2f, 0xb7, 0x64, + 0x0c, 0x4d, 0x5e, 0x28, 0x62, 0xf9, 0x67, 0xe5, 0xfa, 0xe8, 0xd8, 0x63, + 0x69, 0x41, 0x0f, 0x2a, 0xbc, 0x15, 0x32, 0x71, 0x1f, 0xeb, 0x99, 0x7f, + 0x20, 0x20, 0x19, 0x96, 0xa0, 0x71, 0xe6, 0x9d, 0xe4, 0xd3, 0x42, 0x22, + 0x5a, 0x46, 0xa5, 0x77, 0xc4, 0xe6, 0x93, 0x69, 0xa1, 0x94, 0x8e, 0x77, + 0xe5, 0x38, 0xb0, 0xc1, 0x34, 0x67, 0x1d, 0x96, 0x65, 0x4e, 0xbb, 0x05, + 0xbf, 0xf5, 0x7f, 0x94, 0x0f, 0x13, 0xcc, 0x64, 0x17, 0xb3, 0x0b, 0x32, + 0x1c, 0x1c, 0xcf, 0xbd, 0xe2, 0xbe, 0x40, 0xf4, 0x9d, 0xd0, 0x64, 0xa7, + 0x75, 0xfa, 0x03, 0xde, 0x05, 0xaf, 0x82, 0x46, 0xbf, 0x0b, 0x3e, 0x15, + 0xed, 0x72, 0x21, 0x25, 0x2c, 0xa9, 0x63, 0x96, 0x5e, 0x46, 0x07, 0x5f, + 0x92, 0xb8, 0xd0, 0xe8, 0x0c, 0x9c, 0x21, 0xcb, 0x5b, 0xb9, 0xd8, 0xb3, + 0x93, 0xa1, 0x71, 0xc5, 0x1e, 0xc0, 0xe3, 0x91, 0x0b, 0x61, 0x58, 0xc8, + 0x27, 0xf6, 0x61, 0x51, 0xc5, 0xd1, 0x1a, 0x86, 0x06, 0x4a, 0x17, 0x6c, + 0x92, 0x66, 0x6e, 0xf0, 0x78, 0x3d, 0x98, 0x5d, 0x01, 0x67, 0x44, 0xb8, + 0x78, 0x11, 0x6e, 0x06, 0xb3, 0xf7, 0x5e, 0xb0, 0xca, 0xcf, 0x96, 0x58, + 0x82, 0xe8, 0x11, 0x91, 0xbb, 0x03, 0x68, 0x1a, 0xc3, 0xe1, 0x4b, 0x8e, + 0x2c, 0xc4, 0x85, 0xfe, 0xbe, 0x05, 0x17, 0x56, 0x64, 0xb0, 0xa7, 0x9d, + 0x18, 0x6b, 0x47, 0x65, 0x47, 0x3a, 0x6c, 0x10, 0x23, 0x23, 0xa8, 0xcd, + 0xa1, 0x17, 0x0c, 0xb8, 0xb2, 0x73, 0x43, 0xf8, 0x25, 0xd0, 0x19, 0x51, + 0x3a, 0x33, 0x8a, 0x43, 0xbb, 0x08, 0x4e, 0x56, 0x61, 0x10, 0xa4, 0x8b, + 0x72, 0x43, 0x36, 0x43, 0xee, 0x16, 0x4b, 0x01, 0x93, 0x4a, 0x6a, 0x23, + 0x98, 0x6f, 0xe6, 0x7f, 0x03, 0x91, 0x27, 0x55, 0x40, 0x14, 0x9f, 0x8c, + 0x26, 0xa3, 0x4f, 0x74, 0x37, 0xda, 0x54, 0x1c, 0x5f, 0x08, 0x6e, 0x8d, + 0x76, 0xf5, 0x72, 0xf8, 0x71, 0xda, 0x76, 0x4d, 0x27, 0x0a, 0x6f, 0x1a, + 0x8f, 0x88, 0x41, 0xca, 0x29, 0xef, 0xf6, 0xb0, 0xb9, 0x55, 0x43, 0x9f, + 0xaa, 0x5d, 0x5b, 0xf3, 0x92, 0x86, 0x6d, 0xbd, 0x52, 0x04, 0xb1, 0x5e, + 0x6a, 0xb3, 0xac, 0x77, 0x8d, 0xe6, 0xbf, 0x67, 0x58, 0x15, 0x35, 0xd2, + 0x6f, 0x33, 0x6a, 0x8c, 0x8c, 0xad, 0x53, 0x2e, 0x4c, 0xb2, 0x55, 0xf5, + 0x55, 0x02, 0x89, 0x99, 0xf1, 0x36, 0xd5, 0x52, 0x08, 0x91, 0xd3, 0xfd, + 0xaa, 0xdf, 0x9c, 0x23, 0x50, 0xa8, 0xbf, 0x6e, 0x4c, 0x4a, 0xff, 0x80, + 0x54, 0xed, 0xe9, 0x3f, 0xc1, 0x7d, 0x6b, 0xa8, 0x08, 0x48, 0x00, 0x00 +}; +unsigned int index_htm_gz_len = 6012; diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh index 65d7d013c2..f463e22bb5 100755 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -42,7 +42,7 @@ then exit -1 fi -xxd index.htm.gz > index_htm.h +xxd -i index.htm.gz > index_htm.h if [ $? -ne 0 ] then echo "Error creating include file for index.htm" From 5728526c9bdc3b60fb0345aeb80949999728e640 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 11 Apr 2020 03:36:56 +0200 Subject: [PATCH 080/111] Optionally embed index.htm in code. (+ documentation and preprocessing script) --- .../examples/FSBrowser/FSBrowser.ino | 32 ++++++++++++++++--- .../examples/FSBrowser/extras/reduce_index.sh | 9 +++--- .../examples/FSBrowser/readme.md | 13 ++++++-- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index cfe8c1c04e..2aee72bcf6 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -29,6 +29,12 @@ //#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 @@ -37,6 +43,10 @@ #include #include +#ifdef INCLUDE_FALLBACK_INDEX_HTM +#include "extras/index_htm.h" +#endif + #if defined USE_SPIFFS #include const char* fsName = "SPIFFS"; @@ -550,6 +560,22 @@ void handleNotFound() { } } +/* + 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("/edit/index.htm")) { +#ifdef INCLUDE_FALLBACK_INDEX_HTM + server.sendHeader("Content-Encoding", "gzip"); + server.send(200, "text/html", index_htm_gz, index_htm_gz_len); +#else + returnNotFound("FileNotFound"); +#endif + } +} void setup(void) { //////////////////////////////// @@ -618,11 +644,7 @@ void setup(void) { server.on("/list", HTTP_GET, handleFileList); // Load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit/index.htm")) { - returnNotFound("FileNotFound"); - } - }); + server.on("/edit", HTTP_GET, handleGetEdit); // Create file server.on("/edit", HTTP_PUT, handleFileCreate); diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh index f463e22bb5..d768f5ec54 100755 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -1,5 +1,8 @@ #/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 @@ -50,8 +53,4 @@ then fi echo Reduce complete. -echo index.htm.gz can be used instead of data/edit/index.htm to save FileSystem space -echo Alternately, you can leave the filesystem untouched by embedding index.htm -echo inside the code. To do so, uncomment the line -echo " #define INCLUDE_FALLBACK_INDEX_HTM" -echo in FSBrowser.ino + diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 18a7a2dfa6..3b20eb9d32 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -11,11 +11,16 @@ This unified version is based on the previous examples named FSWebServer, FSBrow 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. Copy the contents of the 'data' folder to the filesystem. To do so: +4. For normal use, copy the contents of the 'data' folder to the filesystem. To do so: - for SDFS, copy that 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 -6. If you need as much free space as possible on your filesystem, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total FS usage of less than 7KB + +## Options +If you need to free some space on your filesystem, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total FS usage of less than 7KB. +If you want to use the browser on a an existing filesystem and cannot thus execute step 4 above, you can uncomment the line +"#define INCLUDE_FALLBACK_INDEX_HTM", which will embed a version of the gzipped index in the source code and return it if the index.htm file is not found on the filesystem. +The files required for those two options can be regenerated using the script "reduce_index.sh" located in the "extras" subfolder. ## Dependency The html page uses the ace.js text editor which is hosted on a CDN, so Internet access from your web browser is required for the FSBrowser to work. @@ -55,6 +60,7 @@ The html page uses the ace.js text editor which is hosted on a CDN, so Internet - 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 ? @@ -105,5 +111,6 @@ These tests are a checklist of operations to verify the FSBrowser behaviour. ## Credits - 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 by : https://css-minifier.com/ (css) + https://www.javascriptminifier.com/ (js) + https://www.willpeavy.com/tools/minifier/ (html) +- 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 From 00523361448e32c2ce3a223d816da0c5dd901082 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 11 Apr 2020 03:40:27 +0200 Subject: [PATCH 081/111] (reformated) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 2aee72bcf6..89607a1c2e 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -29,7 +29,7 @@ //#define USE_LITTLEFS //#define USE_SDFS -// Uncomment the following line to embed a version of the web page in the code +// 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" @@ -562,7 +562,7 @@ void handleNotFound() { /* 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 + 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 */ From d7e6d27d765167ebd28e87d23df367992c5b2299 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 11 Apr 2020 19:22:57 +0200 Subject: [PATCH 082/111] If editor cannot be loaded from the web, try a local version, or default to a text viewer if not present --- .../examples/FSBrowser/data/edit/index.htm | 149 +++++++++++------- .../examples/FSBrowser/readme.md | 16 +- 2 files changed, 107 insertions(+), 58 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm index 2edd603142..a9bb8e6a21 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm @@ -294,7 +294,7 @@ document.getElementById("status").innerHTML = "(refreshing...)"; var xmlHttp = new XMLHttpRequest(); xmlHttp.onload = function() { - if(xmlHttp.status != 200) showHttpError(xmlHttp); + if (xmlHttp.status != 200) showHttpError(xmlHttp); else { fsInfo = JSON.parse(xmlHttp.responseText); var status = fsInfo.type + " - "; @@ -426,7 +426,7 @@ header.appendChild(status); fileSelector.onchange = function(e){ // A file has been selected - if(fileSelector.files.length === 0) return; + if (fileSelector.files.length === 0) return; var filename = fileSelector.files[0].name; if (mustFormat83()) { filename = to83(filename); @@ -436,7 +436,7 @@ } upload.onclick = function(e) { - if(fileSelector.files.length === 0) return; + if (fileSelector.files.length === 0) return; tree.httpUpload(fileSelector.files[0], pathInput.value); } @@ -447,13 +447,13 @@ function to83(filename) { var ext = /(?:\.([^.]+))?$/.exec(filename)[1]; var name = /(.*)\.[^.]+$/.exec(filename)[1]; - if(name !== undefined){ - if(name.length > 8) name = name.substring(0, 8); + if (name !== undefined){ + if (name.length > 8) name = name.substring(0, 8); filename = name; } - if(ext !== undefined){ - if(ext === "html") ext = "htm"; - else if(ext === "jpeg") ext = "jpg"; + if (ext !== undefined){ + if (ext === "html") ext = "htm"; + else if (ext === "jpeg") ext = "jpg"; filename = filename + "." + ext; } return filename; @@ -512,6 +512,21 @@ preview.innerHTML = ""; } + // Fall back code used when ace.js is not available (neither online or local) + function loadTxtPreview(path){ + document.getElementById("pathInput").value = path; + document.getElementById("editor").style.display = "none"; + document.getElementById("editorButtons").style.display = "none"; + preview.style.display = "block"; + preview.innerHTML = "Ace editor could not be loaded from the internet nor from /edit/ace.js . Defaulting to text viewer...
";
+          var xhr = new XMLHttpRequest();
+          xhr.onload = function () {
+            document.getElementById('txtContents').textContent = this.responseText;
+          };
+          xhr.open('GET', path);
+          xhr.send();
+        }
+        
         this.clearMainPanel = function() {
           document.getElementById("editor").style.display = "none";
           document.getElementById("editorButtons").style.display = "none";
@@ -521,7 +536,7 @@
     
         function isTextFile(path){
           var ext = /(?:\.([^.]+))?$/.exec(path)[1];
-          if(ext !== undefined){
+          if (ext !== undefined){
             switch(ext.toLowerCase()){
               case "txt":
               case "htm":
@@ -541,7 +556,7 @@
   
         function isImageFile(path){
           var ext = /(?:\.([^.]+))?$/.exec(path)[1];
-          if(ext !== undefined){
+          if (ext !== undefined){
             switch(ext.toLowerCase()){
               case "png":
               case "jpg":
@@ -562,24 +577,24 @@
           var isChecked = document.getElementById(path).childNodes[0].checked;
           var expnd = document.createElement("li");
           list.appendChild(expnd);
-          if(isChecked){
+          if (isChecked){
             expnd.innerHTML = "Collapse";
             expnd.onclick = function(e){
-              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+              if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
               document.getElementById(path).childNodes[0].checked = false;
             };
             var refrsh = document.createElement("li");
             list.appendChild(refrsh);
             refrsh.innerHTML = "Refresh";
             refrsh.onclick = function(e){
-              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+              if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
               httpList(path);
             };
           } 
           else {
             expnd.innerHTML = "Expand";
             expnd.onclick = function(e){
-              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+              if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
               document.getElementById(path).childNodes[0].checked = true;
               httpList(path);
             };
@@ -588,7 +603,7 @@
           list.appendChild(renFolder);
           renFolder.innerHTML = "Rename/Move";
           renFolder.onclick = function(e){
-            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+            if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
             var newPath = prompt("Rename " + path + " to", path);
             if (newPath != null && newPath != path) {
               httpRename(path, newPath);
@@ -598,7 +613,7 @@
           list.appendChild(delFolder);
           delFolder.innerHTML = "Delete";
           delFolder.onclick = function(e){
-            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+            if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
             httpDelete(path);
           };
         }
@@ -608,17 +623,27 @@
           el.appendChild(list);
           var action = document.createElement("li");
           list.appendChild(action);
-          if(isTextFile(path)){
-            action.innerHTML = "Edit";
-            action.onclick = function(e){
-              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
-              if (canLoadNewContents()) editor.loadUrl(path);
-            };
+          if (isTextFile(path)){
+            if (typeof ace == "undefined") {
+              // Could not load editor
+              action.innerHTML = "View";
+              action.onclick = function(e){
+                if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+                if (canLoadNewContents()) loadTxtPreview(path);
+              };
+            }
+            else {
+              action.innerHTML = "Edit";
+              action.onclick = function(e){
+                if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+                if (canLoadNewContents()) editor.loadUrl(path);
+              };
+            }
           } 
-          else if(isImageFile(path)){
+          else if (isImageFile(path)){
             action.innerHTML = "Preview";
             action.onclick = function(e){
-              if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+              if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
               if (canLoadNewContents()) loadImgPreview(path);
             };
           }
@@ -626,14 +651,14 @@
           list.appendChild(download);
           download.innerHTML = "Download";
           download.onclick = function(e){
-            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+            if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
             loadDownload(path);
           };
           var renFile = document.createElement("li");
           list.appendChild(renFile);
           renFile.innerHTML = "Rename/Move";
           renFile.onclick = function(e){
-            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+            if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
             var newPath = prompt("Rename " + path + " to", path);
             if (newPath != null && newPath != path) {
               httpRename(path, newPath);
@@ -643,7 +668,7 @@
           list.appendChild(delFile);
           delFile.innerHTML = "Delete";
           delFile.onclick = function(e){
-            if(document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
+            if (document.body.getElementsByClassName('contextMenu').length > 0) document.body.removeChild(el);
             httpDelete(path);
           };
         }
@@ -658,14 +683,14 @@
           divContext.style.display = 'block';
           divContext.style.left = left + 'px';
           divContext.style.top = top + 'px';
-          if(isfile) fillFileMenu(divContext, path);
+          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);
+            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);
             }
           };
         }
@@ -674,8 +699,8 @@
           var leaf = document.createElement("li");
           leaf.id = ((parentPath == "/")?"":parentPath)+"/"+name;
           var span = document.createElement("span");
-          if(isTextFile(name)) span.classList.add("txt");
-          else if(isImageFile(name)) span.classList.add("img");
+          if (isTextFile(name)) span.classList.add("txt");
+          else if (isImageFile(name)) span.classList.add("img");
           span.innerHTML = name + " (" + readableSize(size) + ")";
           leaf.appendChild(span);
           leaf.onclick = function(e){
@@ -694,18 +719,18 @@
           leaf.id = ((parentPath == "/")?"":parentPath)+"/"+name;          
           var check = document.createElement("input");
           check.type = "checkbox";
-          if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled";
+          if (typeof disabled !== "undefined" && disabled) check.disabled = "disabled";
           leaf.appendChild(check);
           var label = document.createElement("label");
           label.textContent = name;
           leaf.appendChild(label);
           check.onchange = function(e){
-            if(check.checked){
+            if (check.checked){
               httpList(leaf.id);
             }
           };
           label.onclick = function(e){
-            if(!check.checked){
+            if (!check.checked){
               check.checked = true;
               httpList(leaf.id);
             } 
@@ -737,7 +762,7 @@
           for(var i = 0; i < ll; i++){
             var item = items[i];
             var itemEl;
-            if(item.type === "file") itemEl = createTreeLeaf(parentPath, item.name, item.size);
+            if (item.type === "file") itemEl = createTreeLeaf(parentPath, item.name, item.size);
             else itemEl = createTreeBranch(parentPath, item.name);
             list.appendChild(itemEl);
           }   
@@ -747,8 +772,15 @@
           console.log("Attempting load of '" + path  + "'...");
           document.getElementById("pathInput").value = path;
           if (canLoadNewContents()) {
-            if(isTextFile(path)) editor.loadUrl(path);
-            else if(isImageFile(path)) loadImgPreview(path);
+            if (isTextFile(path)) {
+              if (typeof ace == "undefined") {
+                loadTxtPreview(path);
+              }
+              else {
+                editor.loadUrl(path);              
+              }
+            }
+            else if (isImageFile(path)) loadImgPreview(path);
             else clearMainPanel();
           }
         }
@@ -768,7 +800,7 @@
           }
           else {
             console.log("Refreshing '" + path + "'...");
-            if(path.lastIndexOf("/") == -1){
+            if (path.lastIndexOf("/") == -1){
               // No "/" => reset the root
               httpList("/");
             } 
@@ -800,7 +832,7 @@
         function onListReceived(req, parentPath, remainingPaths) {
           return function(){
             setLoading(false);
-            if(req.status != 200) showHttpError(req);
+            if (req.status != 200) showHttpError(req);
             else {
                // Remove previous child list, if any
               var parentEl = document.getElementById(parentPath);
@@ -840,7 +872,7 @@
         function onOperationComplete(req, path){
           return function(){
             setLoading(false);
-            if(req.status != 200) showHttpError(req);
+            if (req.status != 200) showHttpError(req);
             else {
               if (path) {                
                 var parentPath = getParentFolder(path);
@@ -941,7 +973,7 @@
         function getLangFromFilename(filename){
           var lang = "plain";
           var ext = /(?:\.([^.]+))?$/.exec(filename)[1];
-          if(ext !== undefined){
+          if (ext !== undefined){
             switch(ext){
               case "txt": lang = "plain"; break;
               case "htm": lang = "html"; break;
@@ -960,17 +992,17 @@
           return lang;
         }
   
-        if(typeof file === "undefined") file = "/index.htm";
+        if (typeof file === "undefined") file = "/index.htm";
   
-        if(typeof lang === "undefined"){
+        if (typeof lang === "undefined"){
           lang = getLangFromFilename(file);
         }
   
-        if(typeof theme === "undefined") theme = "textmate";
+        if (typeof theme === "undefined") theme = "textmate";
   
-        if(typeof type === "undefined"){
+        if (typeof type === "undefined"){
           type = "text/"+lang;
-          if(lang === "c_cpp") type = "text/plain";
+          if (lang === "c_cpp") type = "text/plain";
         }
   
         var xmlHttp = null;
@@ -978,7 +1010,7 @@
         
         function filePosted(){
           setLoading(false);
-          if(xmlHttp.status != 200) showHttpError(xmlHttp);
+          if (xmlHttp.status != 200) showHttpError(xmlHttp);
           tree.refreshPath(getParentFolder(file)); // to update size in tree
           refreshStatus();
         }
@@ -997,7 +1029,7 @@
           document.getElementById("preview").style.display = "none";
           document.getElementById("editor").style.display = "block";
           document.getElementById("editorButtons").style.display = "inline";
-          if(xmlHttp.status == 200) {
+          if (xmlHttp.status == 200) {
             editor.setValue(xmlHttp.responseText);
             editor.clearSelection();
             enableSaveDiscardBtns(false);
@@ -1012,7 +1044,7 @@
           xmlHttp.send(null);
         }
   
-        if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
+        if (lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
         editor.setTheme("ace/theme/"+theme);
         editor.$blockScrolling = Infinity;
         editor.getSession().setUseSoftTabs(true);
@@ -1045,7 +1077,7 @@
           file = path;
           lang = getLangFromFilename(file);
           type = "text/"+lang;
-          if(lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
+          if (lang !== "plain") editor.getSession().setMode("ace/mode/"+lang);
           loadFile(file);
         }
         
@@ -1077,13 +1109,24 @@
       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);
+        if (typeof ace != "undefined") {
+          var editor = createEditor("editor", vars.file, vars.lang, vars.theme);
+        }
         tree = createTree("tree", editor);
         createHeader("header", tree, editor);
         refreshStatus();
       };
     
-    
+       
+    
   
   
     
diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md
index 3b20eb9d32..3ca06dc446 100644
--- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md
+++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md
@@ -7,7 +7,6 @@ working for both 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 ?
-
 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
@@ -18,12 +17,19 @@ This unified version is based on the previous examples named FSWebServer, FSBrow
 
 ## Options
 If you need to free some space on your filesystem, you can delete the sample files at the root but also the /edit/index.htm file, because the compressed version /edit/index.htm.gz will be used instead, for a total FS usage of less than 7KB.
-If you want to use the browser on a an existing filesystem and cannot thus execute step 4 above, you can uncomment the line 
-"#define INCLUDE_FALLBACK_INDEX_HTM", which will embed a version of the gzipped index in the source code and return it if the index.htm file is not found on the filesystem.
-The files required for those two options can be regenerated using the script "reduce_index.sh" located in the "extras" subfolder.
+If you want to use the browser on a an existing filesystem and cannot thus execute 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 gzipped index in the source code by uncommenting the line in the code and rebuilding
+`#define INCLUDE_FALLBACK_INDEX_HTM`
+That version will be return if no "/edit/index.htm" or "/edit/index.htm.gz" file can be found on the filesystem.
+
+The files required for the previous options can be regenerated using the script "reduce_index.sh" located in the "extras" subfolder.
 
 ## Dependency
-The html page uses the ace.js text editor which is hosted on a CDN, so Internet access from your web browser is required for the FSBrowser to work.
+The html page uses the ace.js text editor which is loaded from a CDN, so 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 upload ace.js (and dependancies) to the edit subfolder ESP filesystem.
+If the ace.js cannot be found on the local 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.

From d2fe22e25a77aad30fe0527d9d8827208f1097db Mon Sep 17 00:00:00 2001
From: vdeconinck 
Date: Sat, 11 Apr 2020 19:29:57 +0200
Subject: [PATCH 083/111] (removed a TODO item :-))

---
 libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md
index 3ca06dc446..dcd095ba24 100644
--- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md
+++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md
@@ -74,7 +74,6 @@ If the ace.js cannot be found on the local filesystem either, the page will defa
 - ? 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
-- ? Check if internet access is available and and replace ace editor by a text viewer otherwise
 - ? Optionally mount several filesystems at the same time (SPIFFS + SDFS or LittleFS + SDFS)
 
 ## Test suite

From 461a7f10d04a9b27d7d5603627fa1f192dfa3444 Mon Sep 17 00:00:00 2001
From: vdeconinck 
Date: Sun, 12 Apr 2020 09:59:14 +0200
Subject: [PATCH 084/111] (forgot to reprocess files after last commit)

---
 .../examples/FSBrowser/extras/index.htm.gz    | Bin 6012 -> 6012 bytes
 .../examples/FSBrowser/extras/index_htm.h     |   2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz
index 5080fbadae476e25d8f92aa54dd4ee31e243c3cb..bc04ef6164cfb3b40cbfb844b1c89c9f4a12944f 100644
GIT binary patch
delta 16
XcmeyP_eYOizMF$X{M4k4?4{xWHc$o4

delta 16
XcmeyP_eYOizMF%ij&tHh_EK>GG?oQ&

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h
index 9f93bb0ab6..422ffd99dc 100644
--- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h
+++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h
@@ -1,5 +1,5 @@
 unsigned char index_htm_gz[] = {
-  0x1f, 0x8b, 0x08, 0x08, 0x7e, 0x09, 0x91, 0x5e, 0x00, 0x03, 0x69, 0x6e,
+  0x1f, 0x8b, 0x08, 0x08, 0x17, 0xca, 0x92, 0x5e, 0x00, 0x03, 0x69, 0x6e,
   0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xd4, 0x3b, 0x89, 0x76,
   0xdb, 0xb6, 0x96, 0xbf, 0x42, 0x33, 0x53, 0x9b, 0x1c, 0x51, 0xd4, 0xe2,
   0x35, 0x92, 0x69, 0xd7, 0x8b, 0xbc, 0xc4, 0xfb, 0x22, 0x2f, 0xc9, 0x64,

From cb98a52c00beacac69fb8df3def1d05ab4a0d58b Mon Sep 17 00:00:00 2001
From: vdeconinck 
Date: Sun, 12 Apr 2020 10:29:22 +0200
Subject: [PATCH 085/111] (reprocess should be ok this time)

---
 .../examples/FSBrowser/data/edit/index.htm.gz |  Bin 6145 -> 6251 bytes
 .../examples/FSBrowser/extras/index.htm.gz    |  Bin 6012 -> 0 bytes
 .../examples/FSBrowser/extras/index_htm.h     | 1024 +++++++++--------
 .../examples/FSBrowser/extras/reduce_index.sh |    7 +
 4 files changed, 529 insertions(+), 502 deletions(-)
 delete mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz
index e4d445366a471dd28f761cfab6c915b84a6c7418..193bf7bedebf1d9afc34dced1d835f354bca1dd4 100644
GIT binary patch
literal 6251
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
VYlC*WCnUW`{{ZQ&0X_^&007zcIKuz{

literal 6145
zcmV+c82;xUiwFo|8--p30BLSyWq2-VbZr3D90^;iM)0rjXsgf=LQ?hFM6@39sOR&1
z_WU@OKtg~A#6(m3+dH$%nGmhL`)p;|V|I?+nc11ejdy$JyVv&@UCxe&E_ZRWb8@uH
z@%2XI?yKEKWAA#8`*?VLdcrkjg}V+lFLGkX_cXWB=$`Q$Z^v=aZZyW@u{{1t_QPJ|
z`m!-WSWO(96zg$eq-@3}zqvu@b2YE0^2Fmexec3WCOl#%cFE?x;}UMDd0LNz8x2|q
zZBhIP)$wCMR6Zt?xM4&Q&kc#`Xeu8WAwfvGZ$3^f-;3*!^F-RsRxs(%S4$f@?qmCw
zgeCx%wnImAr7J+J9+A+oo@E1i0n}6Cjiyg^$1};K{gtA00-xdD*7V4CM=|NR#ERRi
z09f}!lZ5SNFyWxdF}crHR#rN?W(<0vKl04F;k$m={%n60e>$cU1+Mnk_I!`P*hxLI
zHPaur6;9!r3IGXvx+W+R{>Uq0r$9zCC1XsSo*lQ}D@qAtZsbl~CxY(CBsN3r2W^ES
z3y+}|_8boifo7VH*K5y=cb#cQXh>Wwb{(9W5#xSO#uXqQ5XR^f$zY$usl8r
zG;hktMJ8mlI-cuzq^`TZG05g#3nsI(w{8D{gwvdN6ijum(q!Fk7kWieJ%$DF;E*w}
zLMl}tZOP0hSw!7i#C
z@+)>tMLmx=11MG0wc9$e{E$p%NtvId4e&MHC1#3XI>scHHge-kB=TD2aKk3XfS7<@
zEM&06GU{}B{tn!cw=a=XT)(5x6?o3nVy*2AL6bECuLrszB5Ny>bGvhXIaZDjdj2;2
zJG;8EyEi>3?cnQfZ+H6?3y-Ul@em7obLaH7dwXbfAMb{%t9N&;?%IbNbNfSg$I%X&
zeeK|VqtRG@IO~6P{B-($cXFx^S4Ykp@+v+F18tFLRezjpI@ezUu8
z-{0N1tKP%=S|eB^gAY#Q;Bp)PeZ0K7c2BoIoa^`BAuU&z-(Gh2Z>~ee&t)|>BMW@HHXgj@g%U%cTUO8
z{in6jVPpHN?X|W0;Bx2k)3@Jgr$_6~IJ#}@nx|{qtx12?JGzOEm4u#b-V)^X4L_WJOu``Bvi?p}neY29@$K8~Np-JRO`#s2N&u)F@TWehHN9#6E}NBj18a?yH!d{**F9g*&RoXqwgNpzw@p4aclM79Z)e{-Q1pE
z9Jd0K1R%HDPnbW-*F*;orb
zplVtGGw)4m#T;faafEV23&v36)PtGRM{W$iA83M_wY$;!4tXfCjz*dD-
zFMSq-bmzSQF{P*s86iGrqYG%~xMmZs;)GGG#iMA!JS)jGj0-u_%0gX1IIgcD&L_*qhQdlEG+Z&Gli_tKR1e)>(3JRlZ7=G%O76y%>XPK2Ow
z@uYVJ%U96EsbdM+MpJ36h#`qbA@~|Kp4;I&4_e6S>i0Z%?CkIo&z-Wz0ef82#|8ZU
zQ8Ltevt#<4)=&|SWYlV%vF*43ev<<3q>#ZB?1RT*3N5jwe{cL)#L5auWM5IeCWzC8
zoMD=;BnVzeMx$nsWs%{osUA3BA5TvXAr^NDyN5(^N0WUId8?)q3zPvZMR})cGJ`E5
zgK98+CRjOy}cUd7y$WsV9|U1lqtuL;fCsTSKZ)uzJNUw@fE9YN=v
z$CyNobYcWZYeccee*vHy2s@d9E+=(4G0DrDe66$|=3vxkrvaEhOf&`fP4MYk`1=JY
z>7WFQ_+1-4?TCwg?$IBG860lxxGtxI
zA@ie{b1ZD-aIHAr2>7ri5Ra~5fWGHMh8CJINhG{$ipYufyJn7dWl!Y88*o$imeHce~PHYQ&gBO4KMbK0z)HIYQh%;|mT7o7R;KFed
zJhOUJq#_^bQ5>SRRNxKvT6}~~-MK}_6vYP{KPHFNi$}{U8R9f+3^Gv`S@#JsWxBE<
zY_)%oh3|iof2@h(*5?hGOo##P-#5XmK(qX%_=8NFOKqt^dGNvc0M)G;K!zd1&hEwb
z<~qKpMu~`ZBuh0!DI20gFCMckysDxtSMdcbW^CpQK&jX*__31cuDAHK>h17s|sCsxeS0@0#!i`U%=FKnkis)`9>ewhIQ;
z6f9a%
zcF`{11ZZn#f!Pe0^ExmGiAc1UfXaUb
zXF_brb&4VlN*K5dgW>BK3=xB(s*;99BofsKwJ*#zWV3m%&*stDY~luVDz7-z7C!^l
zV_^N)1m#npsloG0Ze`+yi?br~VT*$!X;3pW<4U4xv1HuWQKF9$WtY}|ulz_iUk9`!
zRWYC7^dZ6?e8A0Dp}CV5s=t$R)?J-70%?aD_SV6&+lVMA_dyZQc_f#XbQzP@Y|9Ec
zu2Kr|S?bS&Vze1*ian^?p&)j6RG`LI;uCChsnf*`M^TIhxCbXxJOCMGW0a2APn|S2
z01m($^y9|RkJGs2#xe)I4>bcKSpbA#zDva`7=Wnr#}TDm(Ehpm|u`oa`j*;Z^rQzw62dH`N%Sr}R8)iLb#gXD8g;Re8}Zfiyck5!Gn`
z1D(XsKx4O+HmGq3&QU&(^&+_3;)ux#UF_o|>JfTYsl^wEOI6|HHV-oIa5iA>!KRo#u40sz$=&bh?|2;KH1_675>Ntb+45axxtKtxp0i@z!;-6
z#$;p2LI+TXpbhps^G&Ahlk|^dy*Ldr&X{rbBm(=mv|#Chao2ZUEr>{dn66S5<@ivJ
zWVG|Ri>K!^*zW>gxJJT@sRykrEtN(mu_r`}I=F8oT?Jt=$Mr3o?tz(`C;pPfdNSI3
z|1j-nuAiSVB*aySraOV(=Kn}A3L}X5_ivGoElPHUbg12KoB~oA6YPJM3CWfYAomym
z>I@?eyrCKo5dj_)MF)c!?^F}uyu))|B
zt{eM;hKF*>l(*ZbBEmMzL+fV`-@h
z_Y0Pp`;Vb-A==5|_AOHCi&DR0)h7O4Mr8jVCX)K@>~#GWY6hY-_$$Ur@0WO~Sb3lCgTa$nU$?mtUD=pm!1hX&}Qy{?H&=YfZ{pq4>HIuCrFYd_dp
zP@kvL(n10;?;=^1Krt$^wWwh+*u0O_gZL>h7HU7%EXmH^?rQ|(WTb3VOKgvb0Q$(stv5gkn;*?a$7*^#An(s;VGa$D#=ol@s=fA
zyg0{1I``s(7`>mDOWKl=iHXfs!AYDupH+qdN$OrNEqzLalAi=BJ8qG}QK>DEqJN%%
z_^SomEDP34?nTIv523K6npWm%B_p89v8CDr?XQ1Ne(dy;q>Bm>P(}ynk0j>;
z54es3*blQRQJOiE7B!F0HbcgsawIsvazZnRzvqs@%b6ve#t7gBP_ln8FPC3R8eF$a+;?
z3JCKMA*drifB?IWDo~fS;?;pFT9|uWAO>7yEb19udPQnPSE)gH3(-MFw4SITI%ps6
z#qB}DH9O!7Np2K?=T#6Yh6`V_th%_NtirG~Kf@rSKzA%?b5x^?{%1s(QIp(0D$}Bu
zX_?1-uY1zH?p7mDmn>t@;%GlR7sJ389s1m$mfYduWp$ofC7OEvor>Rt6Nc?X01)mZ
zp2BUP3(X^L(YpSJ^A5n2G}kb9H+y+gKnq2)UKZXh;`|T_A>7WSb=f^7@Dl+JWE&VG
z3fiXR85Hk1^4qi%U2?F10JK$JdT&bXREH%5%z13(u@w!xVyv;SRF)QG0utm4YO#Pk
z)$-|eC$)IpNi0#7#DwWaY9y3=LM4%F{RREY)YqG;T5;W_0BVZPMshV0uc&C9dK1jT
z+?b`Mch#(<9*d~U!6>3OV$KXGkDy3Ih
zs;tb#`PB=$God!mtrn=Aqm}lUljsXqq5c@D+8@)ji){poHCe2b+}Vt7muem6X-Xz$
zgvzN{h7BLMu&F@J{Ql&ektXMiVq!4^)zM-4K_^c%vKvxvsi7ay`o8v{u^ZQ=R)aPg
zKSR2)R1fd-Ei>uJC@%(fP?hHNy*zV?;Ed{TtiUb^HQ7C$VnGjnE3VdIYL>%#Z?kJ1zsW7~L!
zRS&?ccUX_DIkSNBV4g>>8~i_HI_a=^qofB|%$;uLXZMO8b|LVgW`U37f?|VyZX?6i
zfQc$Q8uUIS9()#bpeHwWHE;+TXAvB{W1Rkl2?%3LZ2!_5G=&zXJ;d~)A`*ZxQG>`R
zOhHS1!9>ll%bPYz{)$8q6$y&(6i$i)55pQmDB*k{JZ!kO$uMb&p@>Z;HhoUl(iIKM
zf&kQJLy_je!rl5S--@r{`yT<=Jl_MGXe6$c%y$UEQ5XJ?w+;NR<^-4mBa_A}&>j~d
ze4&3zFNDCVp-apk4j~I1VeD_0m4zur6Wa8)9vmt_X2;~QJsra(5`H%h-TD>WlG6-$
zVg2F#Swh9#E(LkA3q%++Jons3yp;Uflz<~lru(q8v#UduIBdId-7e$7>MC)Wzl%hf
z!gwh|C!R*efZT#lzF@N{A`VVn#>~vW`Kr<43*0)ivP~cr*YF=$g)X(&HWh{XI%zO>
zU(j)(r8)`1pu9v7Mtb3^2qxtMPP33699-sq5d>~FdKtb)VF3{%Hz5P*hOgC7_^?TS
z9@11qS*-Z27t8LKS57Wyn4My07eZm)4g~2@`N90`^PA~2FkBeJO#YY#0@5%Iu1y(ff+_X?@t_Ik>Xg7uc4W+|k*3+ogsHRDne9&wu6*f__RRLq
zzF^D0Y$|<7mwr}*AK}nJ$rn9*@#8~&6DeU|busYCZ--u1M4;vCi|LuVgc^PU#ReeP
TkX=xLp5*3l=p>p^=1Bkm(1iS+

diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz
deleted file mode 100644
index bc04ef6164cfb3b40cbfb844b1c89c9f4a12944f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 6012
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 qvQ_m}0*RUNHq}xHk<D73 diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h index 422ffd99dc..a190a69276 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h @@ -1,504 +1,524 @@ unsigned char index_htm_gz[] = { - 0x1f, 0x8b, 0x08, 0x08, 0x17, 0xca, 0x92, 0x5e, 0x00, 0x03, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xd4, 0x3b, 0x89, 0x76, - 0xdb, 0xb6, 0x96, 0xbf, 0x42, 0x33, 0x53, 0x9b, 0x1c, 0x51, 0xd4, 0xe2, - 0x35, 0x92, 0x69, 0xd7, 0x8b, 0xbc, 0xc4, 0xfb, 0x22, 0x2f, 0xc9, 0x64, - 0xde, 0x81, 0x44, 0x48, 0x82, 0x45, 0x91, 0x0c, 0x09, 0x5a, 0x92, 0x1d, - 0xcd, 0xb7, 0xf4, 0x5b, 0xfa, 0x65, 0x73, 0x01, 0x70, 0x95, 0x28, 0x3b, - 0xe9, 0xb4, 0xaf, 0x9d, 0xa4, 0x27, 0x02, 0xb1, 0x5c, 0xdc, 0x7d, 0x01, - 0xd0, 0xcd, 0x85, 0xfd, 0x8b, 0xbd, 0xdb, 0xc7, 0xcb, 0x86, 0xd4, 0xa3, - 0x03, 0x4b, 0xba, 0x6c, 0xee, 0x9e, 0x1e, 0xef, 0x49, 0x72, 0xb1, 0x54, - 0xba, 0x5f, 0xde, 0x2b, 0x95, 0xf6, 0x6f, 0xf7, 0xa5, 0x87, 0xa3, 0xdb, - 0xb3, 0x53, 0xa9, 0xa2, 0x97, 0xa5, 0x5b, 0x0f, 0xd9, 0x3e, 0xa1, 0xc4, - 0xb1, 0x91, 0x55, 0x2a, 0x35, 0xce, 0x65, 0x49, 0xee, 0x51, 0xea, 0xd6, - 0x4a, 0xa5, 0xe1, 0x70, 0xa8, 0x0f, 0x97, 0x75, 0xc7, 0xeb, 0x96, 0x6e, - 0xaf, 0x4b, 0x23, 0x06, 0xab, 0xc2, 0x16, 0x87, 0xcd, 0x22, 0x4d, 0xad, - 0xd4, 0x4d, 0x6a, 0xca, 0x5b, 0x9b, 0x7c, 0x3f, 0x0b, 0xd9, 0x5d, 0x03, - 0xdb, 0x5b, 0x9b, 0x94, 0x50, 0x0b, 0x6f, 0x1d, 0x10, 0x0b, 0x4b, 0x03, - 0x64, 0xa3, 0x2e, 0xf6, 0x36, 0x4b, 0xa2, 0x6f, 0xd3, 0xa7, 0x63, 0xd6, - 0x8b, 0x4d, 0x82, 0x0c, 0xbf, 0xed, 0x61, 0x98, 0xde, 0x72, 0xcc, 0xf1, - 0x6b, 0xc7, 0xb1, 0x69, 0xd1, 0x27, 0x2f, 0xb8, 0x56, 0xa9, 0xba, 0xa3, - 0x3a, 0xff, 0xec, 0xa0, 0x01, 0xb1, 0xc6, 0xb5, 0x3b, 0xec, 0x99, 0x00, - 0x45, 0xdb, 0xf1, 0x08, 0xb2, 0xb4, 0x1b, 0xd8, 0xbb, 0xe8, 0x63, 0x8f, - 0x74, 0x26, 0x7a, 0x1b, 0x66, 0xe1, 0x11, 0x3d, 0xc3, 0x76, 0xf0, 0xfa, - 0x52, 0x24, 0xb6, 0x89, 0x47, 0xb5, 0xe5, 0x72, 0xb9, 0xee, 0x3a, 0x02, - 0xbd, 0x1a, 0x6a, 0xf9, 0x8e, 0x15, 0x50, 0x5c, 0xb7, 0x70, 0x87, 0xd6, - 0x56, 0x01, 0x70, 0xcb, 0xf1, 0x4c, 0xec, 0xd5, 0x2a, 0xee, 0x48, 0x82, - 0x21, 0x62, 0x4a, 0x1f, 0x56, 0x56, 0x56, 0xea, 0x2d, 0xd4, 0xee, 0x77, - 0x3d, 0x27, 0xb0, 0xcd, 0x62, 0xdb, 0xb1, 0x1c, 0xaf, 0xf6, 0xa1, 0xb3, - 0xca, 0xfe, 0xd6, 0x4d, 0xe2, 0xbb, 0x16, 0x1a, 0xd7, 0x6c, 0xc7, 0xc6, - 0xb0, 0x76, 0x54, 0xf4, 0x7b, 0xc8, 0x74, 0x86, 0xb5, 0xb2, 0x54, 0x96, - 0x2a, 0x65, 0x00, 0xe2, 0x75, 0x5b, 0x48, 0x29, 0x6b, 0xec, 0xaf, 0xbe, - 0xa2, 0xd6, 0x53, 0x64, 0x54, 0x22, 0x32, 0x86, 0x98, 0x74, 0x7b, 0xb4, - 0xb6, 0x5e, 0x2e, 0x67, 0x30, 0x96, 0x02, 0xeb, 0xd5, 0x22, 0x3e, 0x4c, - 0x67, 0x2c, 0x11, 0x3b, 0x50, 0xc7, 0xad, 0x95, 0x05, 0xb2, 0xe5, 0xfa, - 0x00, 0x79, 0x5d, 0x62, 0x43, 0xc3, 0x45, 0xa6, 0x49, 0xec, 0x6e, 0x6d, - 0x6a, 0xbd, 0x45, 0x5e, 0x63, 0x42, 0x3d, 0x6c, 0x21, 0x4a, 0x9e, 0x71, - 0x7d, 0x40, 0xec, 0xe2, 0x90, 0x98, 0xb4, 0x57, 0x5b, 0x03, 0xf4, 0xea, - 0xed, 0xc0, 0xf3, 0x81, 0x1c, 0xd7, 0x21, 0xb0, 0xce, 0xcb, 0xae, 0xf7, - 0x5d, 0x64, 0xbf, 0x86, 0xe4, 0x32, 0x26, 0x44, 0xb4, 0x12, 0xdb, 0x22, - 0x36, 0x2e, 0xb6, 0x2c, 0xa7, 0xdd, 0x8f, 0xf7, 0x5e, 0x73, 0x47, 0xd3, - 0xbb, 0xd7, 0x7a, 0xce, 0x33, 0xf6, 0x5e, 0x13, 0xde, 0x71, 0x30, 0xf9, - 0xb3, 0x32, 0x9b, 0x61, 0x8c, 0x61, 0x96, 0xef, 0x83, 0x22, 0x61, 0xfc, - 0x4c, 0xf0, 0x10, 0xa6, 0x69, 0xd9, 0x0e, 0xe0, 0x4d, 0x4c, 0x75, 0xc2, - 0x88, 0x29, 0x76, 0x4d, 0x01, 0x21, 0xb6, 0x1b, 0xd0, 0xd7, 0x59, 0xd9, - 0x3b, 0x2e, 0x6a, 0x13, 0x3a, 0xe6, 0xec, 0x4b, 0xcd, 0x7f, 0x9d, 0x92, - 0x55, 0x71, 0xe0, 0xbc, 0x14, 0x03, 0xd0, 0x2c, 0xd0, 0x2e, 0x0b, 0xb7, - 0xa9, 0x90, 0x08, 0x48, 0xaf, 0xd5, 0x27, 0x74, 0x76, 0x60, 0xba, 0x63, - 0x0a, 0x99, 0x34, 0xbd, 0xe5, 0x72, 0x67, 0x56, 0x10, 0xd3, 0x93, 0x43, - 0x6e, 0x32, 0xce, 0x15, 0x4d, 0xdc, 0x76, 0x3c, 0xc4, 0xa9, 0x00, 0xb6, - 0x62, 0x8f, 0xc9, 0x23, 0x8f, 0xd8, 0x82, 0x85, 0x5a, 0xd8, 0x2a, 0x00, - 0xb3, 0x22, 0x16, 0x49, 0xec, 0x6f, 0xb5, 0xca, 0x85, 0x35, 0x33, 0xfd, - 0x7f, 0x60, 0x62, 0x5a, 0xa1, 0xa7, 0x85, 0xc0, 0x80, 0x69, 0x39, 0x7d, - 0xb5, 0x5a, 0x0b, 0x77, 0x1c, 0x0f, 0xbf, 0xbe, 0x49, 0x04, 0xdf, 0xa2, - 0x06, 0xf0, 0x51, 0xcb, 0xc2, 0xa6, 0x40, 0x2d, 0x5a, 0x61, 0xe2, 0x0e, - 0x0a, 0x2c, 0x1a, 0x8b, 0x42, 0x5f, 0xcb, 0x5d, 0xdc, 0xee, 0xe1, 0x76, - 0x1f, 0x9b, 0x80, 0x1c, 0x55, 0x62, 0x48, 0x6a, 0x1a, 0x6d, 0xae, 0x94, - 0x3f, 0x8b, 0xb7, 0x96, 0x23, 0x9a, 0x94, 0xd6, 0x06, 0x9e, 0xa5, 0x98, - 0x88, 0xa2, 0x1a, 0x19, 0x80, 0xab, 0x2a, 0xb9, 0x76, 0x17, 0xfc, 0x81, - 0x8f, 0xd7, 0x56, 0x34, 0x72, 0xb7, 0x7b, 0x71, 0x3d, 0x2c, 0x9f, 0x1c, - 0x76, 0x9d, 0x1d, 0xf8, 0x73, 0x7e, 0xd3, 0xec, 0x35, 0x9a, 0x5d, 0x68, - 0xed, 0xb2, 0xcf, 0xbd, 0xee, 0xde, 0xce, 0x19, 0x6b, 0x8c, 0x57, 0x47, - 0xc3, 0x01, 0x6b, 0xb4, 0x2a, 0xbb, 0x67, 0x77, 0x8d, 0xbb, 0xa3, 0x76, - 0x63, 0x7c, 0x3f, 0x58, 0x5d, 0xbd, 0xbf, 0xaf, 0x36, 0xd6, 0xae, 0x9a, - 0xe6, 0xce, 0x55, 0x63, 0x97, 0xa0, 0xc3, 0xca, 0x13, 0x3a, 0x5c, 0x2f, - 0x95, 0x4a, 0x1b, 0xcf, 0xe7, 0x4f, 0xcb, 0x27, 0x2f, 0x67, 0xeb, 0x7b, - 0xa3, 0xb3, 0xd6, 0x60, 0x35, 0xe8, 0x9c, 0xbd, 0xb4, 0x4b, 0x8f, 0x55, - 0xf3, 0xdb, 0x88, 0x9e, 0xa2, 0x43, 0xe2, 0xac, 0x6e, 0x74, 0x1f, 0xef, - 0x3f, 0x3d, 0x7d, 0x3e, 0xb8, 0xbe, 0x3b, 0xf8, 0xfc, 0xe9, 0xb6, 0x51, - 0x3a, 0x7d, 0x69, 0x17, 0x9e, 0xfd, 0xd6, 0xb9, 0xf9, 0x70, 0xb7, 0xbe, - 0x52, 0xb8, 0xec, 0x3d, 0x9b, 0x47, 0x96, 0xdf, 0xba, 0xaf, 0xf6, 0xdd, - 0xb5, 0x6f, 0xeb, 0xcf, 0xa7, 0x2f, 0xe3, 0x8d, 0xe7, 0xb3, 0xe0, 0xfc, - 0xf6, 0xa5, 0xb3, 0xfc, 0xb1, 0xd0, 0x73, 0xd6, 0x9a, 0xe3, 0x8b, 0xe6, - 0xde, 0x41, 0xef, 0xf1, 0xbe, 0x69, 0xad, 0xda, 0xcf, 0xeb, 0x85, 0x92, - 0xbb, 0x86, 0xfb, 0x57, 0xa4, 0x74, 0x78, 0xcd, 0x70, 0xdc, 0x79, 0xb8, - 0xbe, 0xb9, 0xb5, 0xce, 0x76, 0xae, 0x2e, 0x5a, 0x8f, 0x9f, 0x19, 0x2d, - 0x37, 0xd7, 0x9f, 0xae, 0x1b, 0x07, 0xcd, 0x9b, 0xb3, 0x0e, 0xed, 0x7f, - 0xa4, 0xe3, 0x21, 0xd9, 0xb9, 0xea, 0x39, 0xcd, 0x9d, 0xde, 0xdd, 0xce, - 0xf0, 0x93, 0xfb, 0x6d, 0xff, 0xfa, 0x5b, 0x07, 0x3d, 0x3f, 0x6f, 0xbc, - 0x58, 0xc1, 0xf9, 0x49, 0x3f, 0xf0, 0x1a, 0x4f, 0xde, 0xe3, 0x72, 0x01, - 0xaf, 0xaf, 0x7c, 0x22, 0x2f, 0xa7, 0x76, 0xf5, 0xbe, 0x32, 0x20, 0x3b, - 0x27, 0x23, 0xb7, 0x77, 0xb1, 0x7b, 0x86, 0x9b, 0x8f, 0xdf, 0xd6, 0x82, - 0xa3, 0xd2, 0xce, 0xf2, 0xce, 0xda, 0xda, 0xa3, 0x7b, 0xbd, 0x7b, 0xfd, - 0xed, 0xd3, 0x67, 0x74, 0x76, 0xbc, 0x41, 0x86, 0xfe, 0x5d, 0x69, 0xcf, - 0x3c, 0x5b, 0xdb, 0xa9, 0x8e, 0x9e, 0x56, 0xed, 0xe3, 0xa6, 0x7f, 0x52, - 0x2e, 0x91, 0xdb, 0xe6, 0xa5, 0x77, 0x78, 0x3e, 0x28, 0x9f, 0x35, 0xf7, - 0x8f, 0xfb, 0xf8, 0xb0, 0xf4, 0x69, 0x75, 0x25, 0xb8, 0x20, 0x7d, 0xbf, - 0xf5, 0xb1, 0xf7, 0xd0, 0x5b, 0x5d, 0xbd, 0xec, 0x1d, 0x1f, 0x3f, 0x75, - 0x4e, 0xba, 0xe6, 0xa7, 0xdb, 0xa3, 0x9b, 0xc6, 0xb8, 0x5a, 0x3a, 0xd8, - 0x2f, 0xaf, 0x3d, 0x0c, 0x1c, 0x73, 0xe3, 0xfc, 0x62, 0xe8, 0x79, 0xc3, - 0x83, 0xa6, 0x7f, 0x35, 0x78, 0xf8, 0x7c, 0xf4, 0xb9, 0xd7, 0xc3, 0xfd, - 0xa3, 0x5d, 0xb2, 0x3b, 0x7e, 0x3c, 0x76, 0xd0, 0xf1, 0xa7, 0x9d, 0xa7, - 0xcb, 0x8d, 0xe6, 0xcd, 0x3d, 0xd9, 0xdb, 0xd9, 0x38, 0xe9, 0x35, 0xee, - 0x37, 0x9a, 0x87, 0xb7, 0xeb, 0x27, 0x97, 0xe8, 0x73, 0x63, 0xe4, 0x5f, - 0xb4, 0x8e, 0xc6, 0x5e, 0xb3, 0x7b, 0xdb, 0x7f, 0xba, 0x7d, 0xd9, 0xb0, - 0xc8, 0xe5, 0xc3, 0xf0, 0x65, 0xd8, 0xd8, 0x2d, 0x5c, 0x5c, 0x1e, 0xdc, - 0x8d, 0x8e, 0x1a, 0x1b, 0x0f, 0xd5, 0x76, 0xff, 0x7a, 0x77, 0x7c, 0x8a, - 0xee, 0xc6, 0xbd, 0xbb, 0x93, 0xd1, 0x65, 0x75, 0xfd, 0xe4, 0xbc, 0x60, - 0xef, 0xd0, 0xa3, 0xf5, 0xeb, 0xe7, 0xc0, 0xab, 0xee, 0x7b, 0x6b, 0xd5, - 0x0a, 0xc5, 0xfd, 0x33, 0xec, 0x17, 0xee, 0xc9, 0xe1, 0xc6, 0xda, 0x91, - 0xb7, 0xf6, 0x74, 0xf2, 0xf8, 0x54, 0x58, 0xbf, 0xaa, 0x9c, 0x98, 0xe5, - 0x4b, 0x77, 0x74, 0xbc, 0xba, 0x71, 0xee, 0x5f, 0x99, 0xe7, 0xa5, 0xea, - 0xea, 0x8b, 0x75, 0xb5, 0x7f, 0x65, 0x9e, 0xb4, 0x3e, 0xee, 0xd8, 0x67, - 0x6b, 0x9d, 0xa3, 0x9b, 0xc3, 0xfe, 0xa5, 0x7f, 0x85, 0x3e, 0xa1, 0xc1, - 0xb1, 0x7b, 0xf5, 0xb2, 0xe7, 0x8d, 0x87, 0xbd, 0xfd, 0x0a, 0xb9, 0xad, - 0x3e, 0xf4, 0xfd, 0xd3, 0xbd, 0xa1, 0x5f, 0x3a, 0xfe, 0xfc, 0xbc, 0xf1, - 0xd9, 0xe9, 0xac, 0xd3, 0xea, 0xea, 0xa3, 0xd5, 0xe7, 0x62, 0xba, 0x69, - 0xde, 0x5d, 0x5c, 0x9f, 0xac, 0xee, 0x3d, 0x1e, 0x1f, 0x1b, 0xaa, 0x64, - 0x3b, 0x45, 0x0f, 0xbb, 0x18, 0xd1, 0x3f, 0x41, 0xf7, 0x73, 0xba, 0x62, - 0x0b, 0xcf, 0x8d, 0x01, 0x3d, 0x11, 0xc0, 0x2a, 0x10, 0x03, 0xea, 0xbc, - 0x3b, 0xdd, 0x01, 0x4e, 0x8b, 0x92, 0x36, 0xb2, 0x8a, 0xc8, 0x22, 0x5d, - 0xbb, 0x36, 0x20, 0xa6, 0x69, 0xe5, 0x7a, 0x96, 0x94, 0xc9, 0x15, 0x63, - 0x27, 0x5d, 0xd9, 0x80, 0x18, 0x5a, 0xce, 0xf3, 0x9d, 0x73, 0x67, 0x17, - 0x57, 0x36, 0x66, 0xdc, 0x1b, 0x5b, 0xa1, 0xd3, 0x11, 0x7d, 0x63, 0xd5, - 0xda, 0x4a, 0xfe, 0x2a, 0x32, 0xe8, 0xbe, 0xb1, 0x6a, 0xa3, 0x3c, 0xb3, - 0x6a, 0xca, 0x25, 0xb2, 0x60, 0x67, 0xd3, 0x9a, 0x2c, 0xd7, 0x45, 0xd8, - 0xe5, 0x4c, 0x89, 0xdd, 0x31, 0x73, 0xc5, 0xcc, 0x27, 0xe7, 0xb3, 0xa9, - 0x9e, 0xb7, 0x71, 0x59, 0x2a, 0x2e, 0xcf, 0x3a, 0xf0, 0x8c, 0x94, 0x7e, - 0x70, 0xd3, 0xb7, 0x7c, 0x6c, 0x21, 0x4b, 0xc6, 0x1c, 0x44, 0x18, 0xdc, - 0xc9, 0xaf, 0x3c, 0x5b, 0x93, 0x44, 0xb6, 0x26, 0x21, 0xdb, 0x94, 0x94, - 0x28, 0x2a, 0xb2, 0x6c, 0xc3, 0x04, 0xe8, 0x6d, 0x5c, 0x74, 0xc9, 0x08, - 0x5b, 0x45, 0x1e, 0xb5, 0x6a, 0x65, 0xf5, 0x35, 0x1b, 0x6a, 0xa3, 0xf9, - 0xc8, 0x06, 0x1f, 0xcb, 0x81, 0x47, 0x1d, 0xe6, 0x13, 0x6a, 0x03, 0x29, - 0x45, 0x08, 0xa4, 0x03, 0xf6, 0x2b, 0x22, 0xaa, 0xe3, 0x15, 0x5b, 0x41, - 0xb7, 0x43, 0x46, 0x80, 0x74, 0x87, 0xd8, 0x84, 0x62, 0xa9, 0xe2, 0x4f, - 0x7e, 0x8d, 0xc0, 0xf4, 0xf1, 0xb8, 0xe3, 0xa1, 0x01, 0xf6, 0xa5, 0x1f, - 0x04, 0xf3, 0xda, 0xf1, 0x9c, 0x41, 0x92, 0x51, 0x4c, 0xa8, 0x93, 0xfa, - 0x98, 0x4c, 0x3e, 0x74, 0xfc, 0x5b, 0x87, 0x22, 0x2b, 0x5f, 0xfd, 0x43, - 0x16, 0x97, 0x59, 0x3e, 0x15, 0x69, 0x7e, 0x39, 0x37, 0x95, 0x2c, 0x43, - 0xee, 0xc9, 0xa2, 0x78, 0xc7, 0x82, 0x14, 0xb1, 0x07, 0x22, 0xc6, 0x76, - 0x3d, 0x9d, 0x1e, 0xe1, 0x35, 0xf6, 0x97, 0x6d, 0xd7, 0xf4, 0xb1, 0xf9, - 0xb6, 0xb1, 0x95, 0xcb, 0xbf, 0x84, 0x3b, 0x14, 0x3d, 0xd1, 0x93, 0xec, - 0x53, 0x5d, 0x43, 0x65, 0x13, 0x65, 0x40, 0x8b, 0xae, 0xc9, 0x87, 0x21, - 0xf2, 0x6c, 0x20, 0xeb, 0x35, 0x03, 0x66, 0x3a, 0xbb, 0x85, 0xe4, 0xc2, - 0xee, 0xe2, 0x7a, 0x9c, 0x9e, 0x80, 0xa6, 0x50, 0xc7, 0xb1, 0x28, 0x71, - 0x73, 0xd2, 0xa7, 0x28, 0xb1, 0xae, 0xd6, 0x05, 0x1e, 0x65, 0x9e, 0xa5, - 0x56, 0x19, 0x07, 0x9e, 0x89, 0x4f, 0x5a, 0xc4, 0x62, 0x11, 0x7d, 0x86, - 0xda, 0x38, 0x91, 0xee, 0x74, 0x52, 0x1b, 0xd5, 0x79, 0x7e, 0x23, 0x6c, - 0x80, 0x09, 0x0c, 0x7b, 0x73, 0xd8, 0x18, 0x89, 0x67, 0x39, 0xca, 0xa2, - 0x45, 0xa2, 0xc6, 0xcc, 0x31, 0x92, 0x56, 0x98, 0x59, 0xc6, 0xa8, 0xa7, - 0xb0, 0xe1, 0x4d, 0x70, 0x43, 0x11, 0x3b, 0x7e, 0x64, 0x6a, 0x0f, 0x23, - 0xc0, 0x23, 0x87, 0x01, 0x22, 0x29, 0x8f, 0x88, 0x0f, 0x93, 0xf3, 0x90, - 0xbf, 0xd5, 0x8d, 0x29, 0x9f, 0x58, 0x05, 0x37, 0x13, 0x21, 0x5f, 0xe4, - 0x73, 0x85, 0xae, 0xcc, 0xf0, 0x85, 0x65, 0xdc, 0xa9, 0x7c, 0xf8, 0x03, - 0xb3, 0x95, 0x39, 0x9b, 0xf3, 0x4d, 0x5a, 0x0e, 0xa5, 0xce, 0x20, 0x41, - 0x40, 0x68, 0x65, 0x15, 0xc4, 0x1b, 0xb1, 0x8a, 0xb9, 0xc5, 0x0f, 0x60, - 0xaa, 0xa0, 0xf6, 0xda, 0x07, 0xd7, 0x13, 0x96, 0xf7, 0x06, 0xc4, 0x88, - 0xa2, 0x2c, 0x64, 0x80, 0x38, 0x89, 0x57, 0xcf, 0xa2, 0x0d, 0xb8, 0xc6, - 0x1b, 0xae, 0xb2, 0x0d, 0x7d, 0x8a, 0x68, 0xe0, 0xcf, 0xd9, 0x67, 0x25, - 0xde, 0xa6, 0x92, 0xb2, 0x1e, 0xbe, 0x7b, 0x4a, 0xa8, 0xcc, 0x33, 0x7f, - 0xb0, 0x1c, 0xc4, 0x80, 0xce, 0x01, 0x54, 0x04, 0x45, 0x7e, 0xee, 0x65, - 0x69, 0x67, 0x5d, 0xc3, 0x94, 0xbd, 0xc0, 0x78, 0xa4, 0xab, 0xf0, 0x35, - 0xcb, 0xf2, 0x74, 0xa9, 0xb6, 0xaa, 0x26, 0x65, 0x41, 0x3d, 0xa9, 0x67, - 0x6b, 0x61, 0xa7, 0xa4, 0xaf, 0xfa, 0x12, 0x86, 0x24, 0x10, 0xe0, 0x15, - 0x9d, 0x80, 0xc6, 0xe8, 0xe9, 0x7e, 0xcf, 0x19, 0xda, 0xaf, 0x42, 0x25, - 0x22, 0x08, 0x95, 0x78, 0xb8, 0x38, 0xf0, 0xbb, 0xf9, 0x66, 0x3d, 0x4f, - 0xad, 0x44, 0x61, 0x0a, 0x52, 0xe4, 0x48, 0x80, 0x2f, 0x1e, 0xd4, 0x78, - 0x0b, 0xca, 0x39, 0xfc, 0xa0, 0x14, 0x61, 0x44, 0xad, 0xa7, 0x2c, 0x29, - 0xe1, 0x1a, 0x8f, 0x11, 0xbf, 0x26, 0x9e, 0xd0, 0x77, 0x89, 0x6d, 0x83, - 0xb3, 0x60, 0x2e, 0xf6, 0xb5, 0xfc, 0xcb, 0x6b, 0x02, 0xcf, 0x03, 0x63, - 0xa1, 0x58, 0x29, 0xab, 0x13, 0xe6, 0x0d, 0x66, 0x07, 0x96, 0xd7, 0xca, - 0x26, 0xee, 0xaa, 0x93, 0x89, 0x9e, 0x86, 0x21, 0x92, 0x70, 0x0f, 0x7f, - 0x0b, 0x88, 0x07, 0x49, 0xf8, 0x3b, 0x54, 0xc5, 0xf5, 0x27, 0xa3, 0x8a, - 0x91, 0xf3, 0x16, 0x5d, 0x9c, 0x2c, 0x4d, 0xd0, 0x96, 0x44, 0x84, 0xf4, - 0xe6, 0xe0, 0xed, 0x13, 0xcf, 0xcf, 0xf6, 0x43, 0x89, 0x9f, 0x58, 0x4b, - 0x1c, 0x05, 0x53, 0xc6, 0xb4, 0x8f, 0x0c, 0x05, 0xcd, 0x37, 0x72, 0x91, - 0x07, 0xfe, 0x25, 0x1e, 0x06, 0xf1, 0x04, 0x3e, 0x67, 0x9a, 0x28, 0xe3, - 0xc9, 0x0b, 0xd3, 0xe0, 0x70, 0x14, 0x7a, 0x66, 0xfc, 0x36, 0x77, 0x54, - 0x4c, 0x9b, 0x20, 0xce, 0x16, 0x3f, 0xc2, 0x1f, 0x58, 0x19, 0x16, 0xd6, - 0x2b, 0x89, 0x2a, 0xf3, 0xac, 0x62, 0xb3, 0xc4, 0x6b, 0xd2, 0xad, 0x4d, - 0x08, 0x91, 0xc4, 0xa5, 0x5b, 0xcf, 0xc8, 0x93, 0x98, 0x39, 0x6b, 0x1d, - 0xff, 0xd8, 0xee, 0x38, 0xf5, 0x4e, 0x60, 0xb7, 0x19, 0x89, 0x92, 0x8f, - 0xe9, 0xa9, 0x50, 0x14, 0x05, 0x6b, 0x54, 0x7d, 0xa5, 0x8b, 0x8b, 0x10, - 0xca, 0x81, 0x18, 0xac, 0x5b, 0x4e, 0x57, 0xa1, 0xaa, 0x66, 0x3a, 0xed, - 0x80, 0x05, 0x30, 0xbd, 0x8b, 0x69, 0x43, 0xc4, 0xb2, 0xdd, 0xf1, 0xb1, - 0xa9, 0xc8, 0x29, 0x05, 0x93, 0x55, 0x9d, 0x73, 0x8a, 0x9d, 0xe4, 0x18, - 0xf4, 0xfb, 0x77, 0x59, 0xd6, 0xf0, 0xf6, 0x7b, 0x0b, 0x61, 0x51, 0xdb, - 0x42, 0xbe, 0x7f, 0x0a, 0x25, 0xb4, 0x0e, 0xf6, 0xab, 0xc8, 0x5c, 0x93, - 0x65, 0xb5, 0xf6, 0x53, 0x2b, 0x3d, 0x3c, 0x00, 0x46, 0xc5, 0x8b, 0x13, - 0x7c, 0xd9, 0x21, 0x8e, 0xce, 0xd9, 0xa0, 0x8b, 0x9a, 0xcf, 0xc0, 0xdb, - 0xf2, 0x10, 0x11, 0x2a, 0xd7, 0xe4, 0xb0, 0xfa, 0x93, 0x27, 0x31, 0x23, - 0x3c, 0xf0, 0xb5, 0xac, 0xb8, 0xbb, 0x01, 0x4d, 0x56, 0xb0, 0xfa, 0x4a, - 0x3a, 0x0a, 0xde, 0xac, 0x94, 0xab, 0x2b, 0xaa, 0x87, 0x69, 0xe0, 0xd9, - 0x12, 0x2e, 0xc8, 0xd2, 0xae, 0x0c, 0xea, 0xee, 0x29, 0x9c, 0x9b, 0x46, - 0xb1, 0x52, 0xa7, 0x85, 0x82, 0xc6, 0xe6, 0x6c, 0x2a, 0xb8, 0x64, 0xf0, - 0xc9, 0x75, 0xb5, 0x1e, 0xcd, 0x07, 0xd7, 0x7e, 0x00, 0x19, 0x88, 0xa9, - 0x54, 0xd5, 0xc2, 0x17, 0x59, 0x3a, 0x21, 0xbb, 0xb2, 0x26, 0x4b, 0x67, - 0xe2, 0xe7, 0x50, 0xfc, 0xdc, 0xf2, 0x9f, 0x4b, 0xf8, 0xf7, 0xeb, 0x17, - 0xfa, 0x35, 0x8d, 0x4d, 0xc7, 0xc3, 0x7e, 0xef, 0x86, 0x7b, 0x32, 0x05, - 0x54, 0x7d, 0x1e, 0x47, 0x84, 0xaf, 0xcb, 0xf0, 0x5f, 0x56, 0xc2, 0xd5, - 0xcc, 0x39, 0xe8, 0xba, 0x2a, 0xd7, 0x05, 0xbe, 0x36, 0x24, 0x5c, 0x0f, - 0x67, 0xa7, 0x47, 0x94, 0xba, 0xd7, 0x60, 0x43, 0xd8, 0xa7, 0x75, 0xaa, - 0x3b, 0x36, 0xe3, 0xa9, 0x11, 0x6d, 0xac, 0x70, 0xc2, 0xab, 0xe5, 0xf2, - 0x82, 0x41, 0x75, 0x01, 0x5b, 0x65, 0x8c, 0x65, 0x8b, 0x1a, 0x9e, 0x07, - 0xb4, 0x53, 0xb5, 0x8e, 0x2d, 0x1f, 0xbf, 0x32, 0x98, 0xd8, 0x50, 0x84, - 0x3e, 0x19, 0x9f, 0x6e, 0x2e, 0xce, 0x75, 0xd0, 0x6f, 0x1f, 0x2b, 0x4c, - 0x22, 0xbe, 0x0b, 0x3a, 0x84, 0x6f, 0x41, 0x5f, 0x55, 0x55, 0xa7, 0x63, - 0x97, 0xf1, 0xae, 0x28, 0x01, 0xf3, 0xf8, 0x6c, 0x9d, 0xf8, 0x17, 0xfd, - 0x6d, 0x05, 0x17, 0x0c, 0x79, 0x93, 0x25, 0x91, 0x12, 0x31, 0x8d, 0xa5, - 0x30, 0x82, 0x2e, 0x6d, 0xa5, 0xbb, 0x58, 0x4e, 0xb2, 0x24, 0x71, 0x19, - 0x1a, 0x4b, 0x42, 0xd5, 0xe5, 0x82, 0x52, 0x29, 0x9c, 0x21, 0xda, 0xd3, - 0xb9, 0x23, 0x55, 0x3e, 0x7e, 0xfc, 0xcf, 0x10, 0x6a, 0x00, 0x93, 0x77, - 0xc7, 0x14, 0xfb, 0xa5, 0xb0, 0x83, 0x32, 0x80, 0xbc, 0x47, 0x55, 0x0b, - 0xf2, 0x2f, 0x00, 0xba, 0xc4, 0x60, 0x87, 0x3b, 0x70, 0x0d, 0x32, 0x96, - 0xc2, 0xf8, 0xbb, 0xb4, 0x25, 0x17, 0x32, 0x2a, 0x30, 0x03, 0xa3, 0x38, - 0xbd, 0x0d, 0xc0, 0x5c, 0xb4, 0x5b, 0xbe, 0x5b, 0x07, 0x66, 0x63, 0xd1, - 0x72, 0x3a, 0xe2, 0xf7, 0x3d, 0x58, 0xb0, 0x34, 0xc2, 0x45, 0xfc, 0xc8, - 0x5a, 0x04, 0xdd, 0xf6, 0x03, 0xd7, 0x75, 0x3c, 0x8a, 0x4d, 0x76, 0x58, - 0xe9, 0x2f, 0x2e, 0x72, 0x3e, 0x49, 0x09, 0x57, 0xc2, 0xfc, 0x61, 0x69, - 0xeb, 0x7e, 0xe7, 0xfa, 0xfc, 0xf8, 0xfc, 0x30, 0x9f, 0x1a, 0xbe, 0x78, - 0xec, 0x53, 0x3c, 0xf8, 0xfd, 0x37, 0x96, 0x9b, 0x23, 0x62, 0xfb, 0xbf, - 0xff, 0x96, 0x82, 0xfe, 0xfb, 0x6f, 0x1d, 0x98, 0x62, 0x33, 0x47, 0x5d, - 0xdb, 0x6c, 0x79, 0x25, 0xa0, 0x7f, 0x0e, 0x0a, 0x33, 0xb8, 0xaa, 0x6a, - 0x2d, 0x91, 0x5d, 0x28, 0x9c, 0xd9, 0xd0, 0x86, 0xcd, 0x30, 0x4a, 0x0c, - 0x7b, 0xe0, 0x32, 0x33, 0xe7, 0x8e, 0x2d, 0xc7, 0x32, 0x97, 0xb6, 0x8e, - 0xcf, 0x8f, 0x6f, 0xa5, 0xc6, 0xf5, 0xf5, 0xc5, 0xb5, 0xb4, 0x10, 0xb3, - 0xe1, 0x67, 0x14, 0x1d, 0x6b, 0xf2, 0xcd, 0xe5, 0xf1, 0xc1, 0xc1, 0x8d, - 0xbc, 0x60, 0x44, 0x2c, 0x06, 0x5d, 0x03, 0x96, 0xcd, 0x85, 0x32, 0xe8, - 0x9b, 0xc4, 0x03, 0x20, 0xc2, 0x2d, 0x84, 0x01, 0xc4, 0x90, 0x45, 0x04, - 0x91, 0x21, 0xe0, 0x68, 0x60, 0x12, 0x2e, 0xb6, 0x15, 0xf9, 0xb0, 0x71, - 0x0b, 0x96, 0x59, 0x0a, 0xb7, 0xd5, 0x16, 0xca, 0x2a, 0x0c, 0xf9, 0x18, - 0x34, 0xce, 0x0e, 0x2c, 0x4b, 0x4d, 0x4c, 0x35, 0x6b, 0x1e, 0xe0, 0x39, - 0x90, 0x05, 0x85, 0x95, 0x22, 0x73, 0xca, 0x6a, 0xd2, 0x17, 0xb9, 0x80, - 0x43, 0x4b, 0x2a, 0xc8, 0x5f, 0x25, 0xf6, 0x95, 0xb1, 0x8f, 0x04, 0x50, - 0x1b, 0xd9, 0xcc, 0x15, 0x9f, 0xe3, 0xe1, 0x9e, 0x28, 0xa6, 0x98, 0xe1, - 0x0b, 0x67, 0xb2, 0xb0, 0x30, 0x9f, 0x2f, 0xe8, 0x19, 0xef, 0x52, 0x70, - 0x7c, 0x7a, 0x74, 0x3a, 0xf5, 0xfd, 0xfb, 0xc2, 0x02, 0x88, 0xbc, 0x43, - 0xbc, 0x81, 0x22, 0xef, 0xf5, 0x58, 0x5e, 0xed, 0x4b, 0xd4, 0x91, 0xc6, - 0x4e, 0xe0, 0x49, 0x11, 0x1c, 0x69, 0x48, 0x2c, 0x4b, 0x6a, 0x41, 0x28, - 0x73, 0x7c, 0x2a, 0x91, 0x0e, 0x1b, 0x95, 0x98, 0xa2, 0x10, 0x3b, 0x00, - 0x46, 0x30, 0xb5, 0xb3, 0xb9, 0xfe, 0x02, 0xf8, 0x7d, 0xe2, 0xb7, 0x91, - 0x67, 0xc2, 0x2e, 0xbe, 0xb2, 0x50, 0x51, 0x19, 0x2f, 0x12, 0xac, 0xf3, - 0xa7, 0xe1, 0xb7, 0x5c, 0xd6, 0x0c, 0xc6, 0xc6, 0x02, 0x9e, 0x2f, 0x78, - 0x33, 0x06, 0x9b, 0x5d, 0x91, 0xa0, 0x00, 0x2b, 0x2e, 0x79, 0x68, 0x3d, - 0x00, 0xbd, 0xc2, 0x5c, 0x06, 0xb1, 0x0f, 0x06, 0xce, 0x7b, 0xd4, 0xbf, - 0x27, 0xb4, 0xa7, 0xc8, 0x25, 0x59, 0xfd, 0xfe, 0x5d, 0xc1, 0x06, 0x34, - 0x0a, 0x58, 0xd5, 0xb0, 0x0e, 0xd2, 0x4c, 0x86, 0x18, 0xc9, 0x06, 0x2c, - 0xb0, 0xa0, 0x74, 0x84, 0x9c, 0xac, 0x58, 0x51, 0xd9, 0x14, 0x3f, 0x68, - 0xf9, 0xd4, 0x63, 0xe1, 0xb1, 0x0c, 0x5f, 0x60, 0x64, 0xf4, 0x98, 0xa5, - 0x73, 0x17, 0x1d, 0xbe, 0x26, 0x2d, 0x3d, 0xb0, 0x78, 0x8a, 0x8f, 0x78, - 0xc6, 0x0e, 0xa1, 0xd4, 0x66, 0xc1, 0x94, 0xf9, 0x48, 0xc7, 0x98, 0x47, - 0x19, 0xa0, 0x80, 0x92, 0x41, 0xb1, 0x3e, 0x1c, 0x57, 0x64, 0x5e, 0x19, - 0xcb, 0x90, 0x8c, 0x70, 0x95, 0x36, 0x64, 0x66, 0xac, 0xb2, 0x86, 0xf4, - 0x41, 0xc0, 0xec, 0x1b, 0x2c, 0x6e, 0xa1, 0x02, 0x5f, 0xcc, 0x7c, 0x0d, - 0x99, 0x9d, 0x14, 0xca, 0x9a, 0xa3, 0x23, 0x17, 0x34, 0xd7, 0xdc, 0xeb, - 0x11, 0xcb, 0x54, 0x90, 0xca, 0xbd, 0xbe, 0xf9, 0xee, 0x06, 0xa6, 0x0e, - 0x4e, 0x45, 0x76, 0xc1, 0xa1, 0x1e, 0xf3, 0x1e, 0xcd, 0x0c, 0x77, 0x64, - 0xd9, 0x06, 0xfb, 0x12, 0x7b, 0xb0, 0x09, 0xec, 0x2b, 0x0c, 0x9c, 0x77, - 0xc8, 0x0a, 0x38, 0x23, 0xa7, 0xb6, 0x35, 0xc5, 0xb6, 0xd6, 0xdc, 0x6d, - 0x5b, 0x01, 0x64, 0xf6, 0x20, 0xc9, 0xba, 0x95, 0x8e, 0x58, 0x4d, 0x97, - 0x45, 0xa1, 0x69, 0x60, 0x96, 0x00, 0xe6, 0xbf, 0x0f, 0xcc, 0xe7, 0x44, - 0x08, 0xe3, 0xd6, 0xfc, 0x69, 0xeb, 0x66, 0x07, 0xcb, 0xac, 0x3b, 0xb5, - 0xe1, 0x59, 0x7f, 0x9f, 0x4d, 0xcd, 0xee, 0xe7, 0x8b, 0xfd, 0xc8, 0xfb, - 0xfb, 0x91, 0x2c, 0xac, 0x03, 0x2e, 0x9c, 0x2c, 0x30, 0x22, 0x80, 0xb5, - 0xe7, 0x02, 0x63, 0x0e, 0x0f, 0x40, 0xb5, 0x39, 0xea, 0xa2, 0x46, 0xda, - 0xe5, 0xf0, 0xc1, 0xdb, 0xb4, 0xf3, 0x49, 0xc8, 0xee, 0xd0, 0x16, 0x3b, - 0x78, 0xef, 0xa3, 0xeb, 0xf1, 0x3d, 0x22, 0xbb, 0xd3, 0xbc, 0x34, 0xf6, - 0xcc, 0x72, 0x59, 0x97, 0xd8, 0x50, 0x1c, 0xd8, 0x9c, 0x42, 0xa6, 0x6c, - 0xc8, 0xcb, 0x50, 0x1f, 0xb1, 0x91, 0xc4, 0xe6, 0xca, 0x80, 0x58, 0x1a, - 0x03, 0x4f, 0x60, 0x30, 0x78, 0x1f, 0x83, 0x01, 0xc7, 0x20, 0x65, 0xca, - 0xda, 0x20, 0x8d, 0x44, 0xe8, 0x3a, 0x58, 0xef, 0xfc, 0xdd, 0x06, 0x62, - 0xb7, 0xe0, 0xfd, 0xdd, 0x02, 0xbe, 0x5b, 0x0f, 0x5b, 0x2e, 0xdf, 0x2a, - 0x48, 0x6f, 0xb5, 0x2d, 0x4f, 0x81, 0x0d, 0x04, 0x58, 0xf7, 0x3d, 0x41, - 0xb9, 0x82, 0x89, 0x61, 0x3c, 0x70, 0x33, 0x09, 0x57, 0x98, 0x97, 0xaa, - 0xd3, 0x22, 0x72, 0xc1, 0xc4, 0x21, 0xc5, 0x6a, 0x73, 0x27, 0x9c, 0x24, - 0x59, 0x22, 0xbd, 0x84, 0x1c, 0xcb, 0x40, 0x3a, 0x33, 0x6c, 0x5f, 0x87, - 0x48, 0xdc, 0xa5, 0x3d, 0xe1, 0x31, 0x68, 0xd4, 0xfb, 0xa5, 0xfc, 0x95, - 0x5b, 0x5f, 0xfd, 0xd9, 0x81, 0xd2, 0xa2, 0x0c, 0xd6, 0xf7, 0xcc, 0xcc, - 0x2e, 0xc7, 0xa7, 0x85, 0x23, 0xdc, 0xb3, 0x85, 0x6d, 0x35, 0x9a, 0x6e, - 0x4c, 0xbb, 0xc7, 0x68, 0x42, 0x81, 0xcd, 0xa6, 0x10, 0xf2, 0x2c, 0x86, - 0x22, 0xb8, 0xbd, 0x7e, 0x06, 0xc3, 0x59, 0xf4, 0x16, 0x17, 0x6d, 0x9d, - 0xdd, 0xc7, 0x0a, 0x6b, 0x55, 0x12, 0x2c, 0xa3, 0xad, 0xd4, 0x89, 0x46, - 0x72, 0x61, 0x09, 0xb2, 0x22, 0xfc, 0xc1, 0x9f, 0x0e, 0x14, 0xb5, 0x2e, - 0xcb, 0x86, 0x61, 0xd0, 0xed, 0x30, 0x5c, 0xee, 0x48, 0x51, 0x3e, 0x22, - 0x0d, 0x02, 0x08, 0x49, 0x10, 0x9a, 0xba, 0x50, 0xbd, 0xb1, 0x72, 0x80, - 0x66, 0x1d, 0x75, 0xb4, 0xe2, 0x20, 0xca, 0x5f, 0xc4, 0x02, 0x28, 0x0e, - 0x21, 0x18, 0x99, 0x10, 0xd8, 0x68, 0x4f, 0x42, 0xd2, 0x52, 0x69, 0x49, - 0x02, 0xbe, 0x7b, 0xa8, 0x4d, 0x31, 0x44, 0xfc, 0x9a, 0x40, 0x7d, 0x8f, - 0x8b, 0x16, 0x92, 0xd8, 0x09, 0xf8, 0x83, 0x3f, 0x8e, 0x29, 0x67, 0xa4, - 0x94, 0x8b, 0xac, 0x32, 0x85, 0x2d, 0x48, 0x87, 0x16, 0x98, 0x60, 0x54, - 0x6d, 0x0a, 0x05, 0xc0, 0xc1, 0xcb, 0xc5, 0x01, 0xf2, 0x0c, 0x30, 0x4a, - 0x05, 0xc6, 0x07, 0x73, 0xc6, 0x43, 0x43, 0x62, 0x53, 0x82, 0x79, 0x20, - 0x20, 0x2d, 0xb9, 0xe9, 0x41, 0x16, 0xd7, 0x0e, 0x58, 0x22, 0x31, 0x99, - 0x8e, 0x54, 0xb7, 0x90, 0xb4, 0x42, 0x9c, 0xf2, 0x63, 0x92, 0xe7, 0xc5, - 0xdf, 0xf0, 0xe0, 0x85, 0xe1, 0x3f, 0xd7, 0x44, 0x4c, 0xf2, 0x0c, 0x16, - 0x12, 0xef, 0x40, 0xde, 0x8c, 0xff, 0x49, 0xac, 0x51, 0x43, 0x0d, 0x7d, - 0x23, 0xfa, 0x0b, 0xc7, 0x38, 0x9b, 0xb1, 0x09, 0x87, 0xf8, 0xce, 0xb2, - 0xc8, 0x9f, 0xce, 0x59, 0x4d, 0xa7, 0xbb, 0xf9, 0x09, 0x02, 0xeb, 0x4f, - 0xd9, 0xf6, 0x26, 0x19, 0x74, 0x25, 0xdf, 0x6b, 0x1b, 0x4b, 0x90, 0x34, - 0x14, 0xe4, 0xb8, 0x1a, 0x19, 0xa0, 0x51, 0x31, 0x3e, 0xf4, 0xf9, 0xa5, - 0x2e, 0xb1, 0xef, 0xf4, 0x09, 0xa7, 0x14, 0x9e, 0x7d, 0xa3, 0x80, 0x3a, - 0x75, 0x29, 0x73, 0x59, 0x58, 0x5f, 0x92, 0x20, 0xdd, 0x4e, 0x09, 0x24, - 0x51, 0xbc, 0x92, 0xb2, 0x5d, 0xfb, 0x2f, 0x5d, 0xf9, 0xf2, 0xdf, 0xfa, - 0xd7, 0x82, 0xaa, 0x6e, 0xff, 0x47, 0x49, 0xc7, 0x23, 0xcc, 0xc6, 0xbf, - 0x54, 0xbe, 0xd6, 0xc1, 0x67, 0x08, 0x4f, 0x00, 0x96, 0x49, 0x55, 0x1f, - 0x14, 0xbd, 0xdd, 0x03, 0x75, 0xa3, 0xce, 0xa9, 0x33, 0xc4, 0xde, 0x1e, - 0x82, 0xe2, 0x4b, 0x55, 0x5f, 0xdb, 0xf0, 0x2b, 0x53, 0x88, 0xdd, 0x35, - 0xde, 0xea, 0xd1, 0x41, 0xd2, 0xb2, 0xc2, 0xe6, 0x93, 0x1f, 0x37, 0xc0, - 0x61, 0x8a, 0x66, 0x3b, 0x9a, 0x16, 0x7d, 0xbb, 0x6e, 0xd4, 0xf2, 0xa3, - 0xe9, 0x23, 0x06, 0x21, 0xcc, 0xad, 0x2a, 0x29, 0x95, 0xf2, 0xfe, 0x7c, - 0x0a, 0x5c, 0x28, 0xf3, 0x43, 0x1c, 0xdd, 0xa4, 0x85, 0xa3, 0x66, 0x97, - 0x74, 0xc2, 0x16, 0x69, 0x3b, 0xb9, 0x38, 0x0d, 0x14, 0x5b, 0x73, 0xd4, - 0xb0, 0x4e, 0x9d, 0xa7, 0xb8, 0x81, 0x05, 0x7a, 0x6b, 0x67, 0x7c, 0x36, - 0x56, 0xeb, 0x53, 0x16, 0x31, 0xb5, 0xc6, 0x22, 0xb0, 0x06, 0x67, 0xd6, - 0x50, 0x55, 0x6b, 0x2b, 0x8e, 0xba, 0xad, 0x64, 0x55, 0x87, 0xd7, 0x34, - 0x0d, 0x50, 0xc5, 0xb8, 0xbc, 0xa1, 0xf9, 0xae, 0x76, 0x33, 0x7b, 0x68, - 0x91, 0x68, 0xb3, 0xbf, 0x3b, 0xde, 0x63, 0x95, 0xdd, 0x39, 0xb8, 0x1a, - 0x45, 0x4e, 0x3d, 0x63, 0x00, 0x9d, 0x8e, 0x9c, 0x72, 0x76, 0xad, 0x38, - 0x0e, 0x11, 0x68, 0xd9, 0x80, 0x56, 0x4e, 0x51, 0xb1, 0xb8, 0x08, 0x1e, - 0x1d, 0x3a, 0x9b, 0x9e, 0x05, 0x48, 0x4f, 0xd4, 0x9a, 0x07, 0x3f, 0x90, - 0xfe, 0xe6, 0x20, 0x7f, 0x29, 0x8c, 0xff, 0x9f, 0x86, 0x3f, 0xe1, 0x78, - 0x73, 0x39, 0xcd, 0x4f, 0xa1, 0x73, 0xe4, 0x84, 0x58, 0x3c, 0x9e, 0x21, - 0x72, 0xdf, 0x19, 0xf2, 0x53, 0x90, 0x98, 0x4a, 0x34, 0x3f, 0x34, 0xd4, - 0xff, 0x42, 0x5a, 0xa9, 0xe1, 0xbc, 0x51, 0x06, 0x85, 0x48, 0x16, 0xf9, - 0xe1, 0x2a, 0x73, 0x69, 0xe0, 0x93, 0x68, 0x41, 0xde, 0x8e, 0x06, 0x0c, - 0xea, 0x41, 0xe5, 0x36, 0x79, 0x27, 0xed, 0xcf, 0x61, 0x8a, 0xc9, 0x52, - 0x85, 0x19, 0xa6, 0x5c, 0xf3, 0xd8, 0x5a, 0x3a, 0x03, 0x04, 0x93, 0xe2, - 0xfc, 0xdf, 0x2c, 0xfd, 0xd0, 0x10, 0x5d, 0xcf, 0x19, 0xb8, 0x80, 0xbb, - 0x40, 0x09, 0x4a, 0x68, 0xa7, 0x20, 0x43, 0x41, 0x0b, 0xc9, 0x16, 0xd8, - 0x2e, 0x14, 0xe3, 0x0b, 0x06, 0x5d, 0x5c, 0xa4, 0x0b, 0x86, 0xb3, 0xb8, - 0xd8, 0x51, 0x1c, 0x28, 0xbc, 0x26, 0xef, 0x14, 0x21, 0x39, 0x4c, 0xb0, - 0x54, 0xcd, 0xca, 0xd1, 0x0c, 0x6c, 0x61, 0x9a, 0xd0, 0x3f, 0x27, 0x51, - 0xfa, 0xeb, 0x34, 0x62, 0xcc, 0xf4, 0x3c, 0x71, 0x69, 0x16, 0x3b, 0xa3, - 0xd5, 0xec, 0x99, 0xc2, 0x32, 0x2f, 0x1a, 0xa7, 0x6b, 0x4b, 0x71, 0x12, - 0xda, 0xf6, 0x1c, 0xcb, 0xba, 0x75, 0xdc, 0xed, 0x39, 0xfd, 0xc9, 0xb1, - 0x6b, 0xd4, 0x08, 0x21, 0x26, 0x53, 0x34, 0x33, 0x17, 0x28, 0xab, 0x17, - 0x72, 0xa1, 0xb2, 0x81, 0xf7, 0xc0, 0xb2, 0x39, 0x9a, 0x05, 0x95, 0x37, - 0x70, 0x16, 0x7a, 0x1f, 0x0a, 0xa6, 0xe6, 0xc7, 0x5f, 0x8f, 0x05, 0x54, - 0x77, 0xc4, 0x61, 0xef, 0x39, 0xaf, 0x43, 0xd3, 0x4c, 0x84, 0x64, 0x3b, - 0x3f, 0x80, 0x47, 0xfd, 0xec, 0xc4, 0xdf, 0xb0, 0x0a, 0x32, 0xab, 0x63, - 0xa2, 0x3e, 0xea, 0xb8, 0x86, 0x2f, 0xba, 0x14, 0x7b, 0x7b, 0x50, 0x8b, - 0x25, 0xf9, 0xef, 0x8c, 0x16, 0xd3, 0x9e, 0x6b, 0xca, 0xd8, 0x1d, 0x55, - 0x6f, 0xb3, 0x99, 0xe7, 0x8e, 0x29, 0xaa, 0x80, 0xf0, 0x86, 0x5c, 0x7b, - 0xc7, 0xae, 0xd9, 0x99, 0xf5, 0xb4, 0x69, 0x23, 0xc8, 0xc4, 0x66, 0x15, - 0x7b, 0x0f, 0x18, 0x8f, 0x5c, 0xff, 0xef, 0x33, 0xed, 0xb9, 0x8e, 0x6e, - 0x1e, 0xed, 0xc6, 0x42, 0xe5, 0x4f, 0x33, 0xeb, 0x6b, 0x71, 0x3e, 0xfe, - 0xf7, 0xd9, 0x75, 0x8f, 0xdb, 0x35, 0x3b, 0x47, 0x97, 0x72, 0x84, 0xd3, - 0x18, 0xc1, 0xbf, 0xe6, 0xff, 0x27, 0xd1, 0x94, 0x05, 0x45, 0xef, 0x1c, - 0xd7, 0xe4, 0xc8, 0xc7, 0x57, 0xb3, 0xe7, 0x32, 0xf3, 0x63, 0x4f, 0x7e, - 0xb9, 0xf6, 0x4f, 0x8d, 0x3d, 0xf3, 0xcf, 0x90, 0x72, 0x98, 0x40, 0x54, - 0x8d, 0xbc, 0x1b, 0x7b, 0xf2, 0x0b, 0xeb, 0xbf, 0x3c, 0xf6, 0xa8, 0x9c, - 0xa8, 0xa9, 0x4b, 0xb5, 0x34, 0xf2, 0x4e, 0x74, 0x68, 0xe6, 0xe8, 0x4e, - 0xa7, 0xe3, 0x63, 0x7a, 0xcf, 0x8a, 0x22, 0xad, 0x1d, 0x7f, 0x1f, 0xf1, - 0xa2, 0x08, 0xbc, 0xb8, 0x63, 0x0f, 0x9c, 0xc0, 0xc7, 0x4e, 0x40, 0x33, - 0x24, 0x28, 0xb1, 0xe7, 0xdf, 0xb4, 0xbe, 0x7f, 0x8f, 0x3f, 0xb6, 0xac, - 0x02, 0x49, 0x3e, 0x1f, 0x37, 0xfd, 0xd4, 0xc7, 0x96, 0x5f, 0x68, 0x43, - 0x16, 0xf8, 0x57, 0x11, 0x9f, 0x89, 0xb9, 0xc1, 0x8f, 0xc5, 0x5c, 0x2e, - 0x56, 0x87, 0x1d, 0x11, 0xb1, 0xaa, 0xdf, 0x60, 0x57, 0x8d, 0x72, 0x2d, - 0x3a, 0x63, 0x79, 0x27, 0x51, 0x0d, 0x0f, 0x98, 0xc2, 0x1a, 0xa6, 0x0d, - 0xf1, 0x61, 0x1b, 0x4d, 0x5f, 0x8d, 0xb2, 0x82, 0x8e, 0x25, 0xea, 0x14, - 0xe8, 0x9e, 0x19, 0x84, 0x12, 0x55, 0xce, 0x66, 0xb6, 0x90, 0x18, 0x4a, - 0x9b, 0x64, 0x4b, 0x99, 0xba, 0x88, 0xb2, 0x01, 0x21, 0x75, 0xb3, 0x44, - 0xb6, 0x66, 0x8f, 0x8b, 0x35, 0x27, 0x57, 0xc1, 0x10, 0xa5, 0x18, 0x0c, - 0x80, 0xe5, 0xe0, 0x0a, 0x23, 0x4f, 0x9d, 0x88, 0x89, 0x82, 0xa5, 0x80, - 0x7f, 0x90, 0x99, 0x8e, 0x75, 0x76, 0x5c, 0x00, 0x54, 0xed, 0x8b, 0x73, - 0x62, 0x85, 0x9f, 0xa1, 0x43, 0xcc, 0xbd, 0xf4, 0x1c, 0x17, 0x75, 0x91, - 0xb8, 0x63, 0xd4, 0x58, 0x22, 0xc3, 0xa0, 0xf1, 0xab, 0x04, 0xcd, 0x49, - 0xb8, 0xed, 0xfe, 0xf5, 0xdc, 0x9e, 0x3e, 0x59, 0xe7, 0x4e, 0xac, 0xe5, - 0x40, 0x3e, 0x10, 0x97, 0xa6, 0xf6, 0xe2, 0x22, 0xfc, 0xa7, 0xa0, 0xe4, - 0x30, 0x52, 0x8e, 0x5a, 0xb2, 0xfa, 0xb3, 0x07, 0xed, 0xfc, 0x55, 0x5b, - 0x22, 0x5e, 0x53, 0x67, 0x8c, 0x0b, 0x8b, 0x19, 0x83, 0xce, 0x9c, 0x9f, - 0xcf, 0x3b, 0x30, 0x44, 0x91, 0xb7, 0x5d, 0x5c, 0xec, 0x45, 0x92, 0xc8, - 0x8f, 0x0c, 0xf1, 0xcc, 0x6d, 0x94, 0x0a, 0x9e, 0x35, 0x05, 0x4d, 0xf9, - 0x6b, 0x06, 0xe3, 0xcf, 0x16, 0x67, 0x25, 0x2b, 0x4e, 0x87, 0x65, 0x55, - 0x2c, 0x05, 0x09, 0x89, 0xcf, 0x5e, 0x33, 0xa7, 0xde, 0x1e, 0xb0, 0x6b, - 0x25, 0x7e, 0xeb, 0x6c, 0xe7, 0xdf, 0x3a, 0xdb, 0x99, 0x5b, 0xe7, 0xf9, - 0x01, 0x8a, 0xa7, 0x40, 0xd1, 0x51, 0x84, 0xb8, 0xaf, 0x09, 0x27, 0x70, - 0xfe, 0xd6, 0xc1, 0x59, 0xcb, 0xcd, 0x53, 0xd0, 0x18, 0xaa, 0x53, 0xd4, - 0x65, 0xee, 0x61, 0x71, 0x11, 0x67, 0x8c, 0x1f, 0x3c, 0xf8, 0x42, 0xc2, - 0x02, 0xfe, 0x24, 0x42, 0xf7, 0x1d, 0x8f, 0x2a, 0xd9, 0xce, 0xe4, 0x92, - 0x9f, 0x29, 0x11, 0x03, 0x07, 0xbf, 0xdb, 0x98, 0x9f, 0xd7, 0x42, 0x71, - 0xdd, 0x46, 0x16, 0xde, 0x83, 0xc8, 0x81, 0x3c, 0x76, 0x21, 0xce, 0x3a, - 0xd5, 0x9a, 0x98, 0x3b, 0x33, 0xc8, 0x3a, 0xa3, 0x62, 0x76, 0xfe, 0x09, - 0x1b, 0x4f, 0x3d, 0xe7, 0xdf, 0x24, 0x65, 0x94, 0xc8, 0x56, 0xe3, 0x57, - 0x0a, 0x0e, 0x60, 0x26, 0x1c, 0x1e, 0xd4, 0x03, 0xe5, 0x3a, 0xda, 0x74, - 0xea, 0xa8, 0x50, 0x10, 0x0c, 0x32, 0x21, 0xeb, 0xa6, 0x5f, 0xd0, 0xd7, - 0xba, 0x19, 0x5e, 0x2f, 0x19, 0x86, 0x61, 0x09, 0x3a, 0x98, 0xd3, 0xb3, - 0x38, 0xda, 0xf0, 0xc3, 0xde, 0xf6, 0xa8, 0x35, 0x37, 0xee, 0x62, 0x07, - 0x99, 0x59, 0x9d, 0x9d, 0x4c, 0x20, 0x4a, 0xa4, 0x9e, 0x00, 0xd8, 0x53, - 0x4f, 0x00, 0x12, 0xcb, 0xfa, 0xd9, 0xd4, 0xe2, 0x8d, 0xbb, 0xdc, 0x37, - 0xb2, 0x11, 0x30, 0x22, 0xe6, 0x25, 0x63, 0x4f, 0x0f, 0x46, 0x3a, 0x49, - 0xb9, 0x74, 0xa4, 0x70, 0x17, 0xf3, 0xc3, 0x3a, 0xf9, 0xf6, 0x4b, 0x08, - 0x09, 0x56, 0xd9, 0x51, 0xe5, 0x30, 0x7d, 0xd6, 0x0e, 0xa2, 0xc8, 0xbe, - 0x87, 0x80, 0x64, 0x21, 0xf3, 0xbd, 0x60, 0x80, 0x06, 0x86, 0xef, 0x35, - 0x2e, 0x11, 0xed, 0x4d, 0x3f, 0x9f, 0xd0, 0xd2, 0x63, 0xec, 0xc6, 0x30, - 0xe5, 0x8f, 0x6d, 0x55, 0xa4, 0x90, 0x6f, 0x2c, 0xaf, 0x4f, 0x3d, 0x24, - 0x49, 0x71, 0xa1, 0x27, 0x14, 0x39, 0x4d, 0x70, 0x59, 0x93, 0x59, 0x50, - 0x81, 0xb6, 0x14, 0x9e, 0x76, 0xea, 0xba, 0x0e, 0x1e, 0x6f, 0x34, 0xb0, - 0x18, 0xd9, 0x39, 0x2f, 0x48, 0xa2, 0xa1, 0xe8, 0x1d, 0x89, 0xa3, 0x84, - 0x1d, 0x1a, 0x03, 0x9e, 0x8c, 0xa6, 0xaf, 0xd4, 0xd9, 0xff, 0x32, 0xb3, - 0x6d, 0x12, 0xcf, 0x80, 0x2d, 0xf8, 0xbd, 0x7a, 0x34, 0x2b, 0xef, 0x76, - 0xdd, 0x4c, 0x9b, 0xb3, 0xeb, 0xb8, 0x8a, 0xca, 0x6c, 0x18, 0xe8, 0xd4, - 0x70, 0x6a, 0x56, 0x27, 0x97, 0x16, 0x9e, 0xb3, 0xa5, 0x88, 0x61, 0x97, - 0xe0, 0xd0, 0xa4, 0x31, 0x5d, 0x34, 0xe7, 0x2a, 0x85, 0xf2, 0x4b, 0x14, - 0xfa, 0x33, 0x44, 0xa3, 0x98, 0x68, 0x1a, 0x99, 0x31, 0x5b, 0x74, 0xe0, - 0x78, 0x83, 0x7d, 0x44, 0x51, 0x5c, 0x31, 0x2a, 0xe1, 0x2d, 0x2a, 0x4d, - 0x2c, 0x08, 0x82, 0xbf, 0xd7, 0x96, 0x81, 0x96, 0x29, 0x56, 0x5d, 0x36, - 0x39, 0xab, 0xd8, 0x31, 0xb6, 0x3c, 0xcd, 0xa0, 0x14, 0xdd, 0x63, 0xc6, - 0x9d, 0x29, 0xaa, 0x79, 0xde, 0xf8, 0x7f, 0x10, 0x61, 0x4c, 0x4d, 0x94, - 0x02, 0x67, 0x68, 0xa1, 0x53, 0xb4, 0xcc, 0x60, 0xbe, 0xdf, 0x38, 0x6d, - 0xdc, 0x36, 0xe6, 0x21, 0x0f, 0xde, 0x35, 0x34, 0x3b, 0x3b, 0x53, 0xd5, - 0xa7, 0x5e, 0x66, 0xcb, 0xc0, 0x1c, 0x76, 0xd3, 0x56, 0x9a, 0x7f, 0xbc, - 0x3f, 0xe3, 0xef, 0x34, 0xda, 0x23, 0x3e, 0x00, 0xc4, 0xc8, 0x3b, 0x43, - 0xc4, 0xbe, 0x44, 0x36, 0xb6, 0xd2, 0x6f, 0x9a, 0xfe, 0xc1, 0xd7, 0x0b, - 0x26, 0x79, 0x8e, 0xae, 0x14, 0x66, 0x1e, 0x22, 0x2f, 0x6d, 0x29, 0xcc, - 0x31, 0xf3, 0xab, 0xad, 0x0e, 0x7b, 0x5b, 0x23, 0x39, 0x9e, 0xc4, 0x1e, - 0x2e, 0x22, 0x71, 0xdd, 0x15, 0xbf, 0xd3, 0x81, 0x9c, 0x0e, 0xe0, 0x6c, - 0xc9, 0x13, 0xc1, 0x88, 0x94, 0x93, 0xc8, 0x84, 0xf3, 0xf4, 0xcb, 0x3e, - 0x79, 0x47, 0x4c, 0x62, 0x9a, 0xc2, 0xe4, 0x2e, 0x39, 0x9d, 0xac, 0xc6, - 0xfc, 0xd4, 0xfd, 0x4d, 0xfe, 0x31, 0xad, 0xc2, 0xce, 0xfd, 0xb7, 0x93, - 0xc3, 0x66, 0xcc, 0x32, 0x58, 0xe8, 0x61, 0xf7, 0x43, 0xb5, 0xac, 0xb4, - 0x14, 0x96, 0x8a, 0x70, 0xe4, 0x53, 0xde, 0x6c, 0xfa, 0xc6, 0x34, 0x7a, - 0xee, 0x63, 0xa4, 0x9f, 0xfb, 0xa8, 0x19, 0xaa, 0xae, 0xe3, 0xa7, 0x6f, - 0xec, 0x06, 0x50, 0x50, 0x22, 0x6c, 0x3b, 0x76, 0xd6, 0x73, 0xe7, 0xa7, - 0xa9, 0x2f, 0x56, 0x0c, 0x23, 0xe7, 0xbd, 0x47, 0x0a, 0xd4, 0x6b, 0xf2, - 0x12, 0xf0, 0xcb, 0x57, 0xcd, 0x36, 0x70, 0xbd, 0x58, 0x61, 0xf9, 0xcb, - 0xf4, 0x9a, 0xc5, 0xc5, 0xb9, 0x8f, 0x77, 0x20, 0x3a, 0xa8, 0x54, 0x77, - 0x03, 0xbf, 0xc7, 0x74, 0xd8, 0xce, 0x8b, 0x1e, 0xec, 0xea, 0xd1, 0xde, - 0x0e, 0x27, 0x31, 0x70, 0xb5, 0x64, 0x01, 0x37, 0xa7, 0x90, 0x6b, 0xc9, - 0xbd, 0xac, 0x91, 0x4d, 0x54, 0xa6, 0xbc, 0x83, 0x98, 0x13, 0x92, 0xfb, - 0x37, 0x7a, 0x42, 0xf1, 0x66, 0x25, 0x27, 0x48, 0x5c, 0x5e, 0xdc, 0xbc, - 0xe5, 0xfa, 0x12, 0x6a, 0xc5, 0x3d, 0x6a, 0x46, 0x45, 0xa6, 0x68, 0xe5, - 0x33, 0x66, 0x24, 0xfb, 0xe6, 0x9b, 0xa0, 0x3f, 0x42, 0x2a, 0xfe, 0x63, - 0x8e, 0xf2, 0x0d, 0x17, 0xcf, 0xae, 0xa7, 0x05, 0x6e, 0x9c, 0xdc, 0xe9, - 0xab, 0xdb, 0x06, 0xf7, 0x3e, 0xa2, 0x52, 0x12, 0xe9, 0x75, 0x5e, 0xb8, - 0x94, 0xc1, 0xe9, 0x10, 0x1b, 0xbc, 0xe9, 0x8f, 0x5f, 0xc9, 0xd9, 0xd1, - 0x95, 0x9c, 0x9d, 0xbe, 0x46, 0x8c, 0x41, 0xd5, 0x5b, 0xb0, 0x7d, 0xbf, - 0x9e, 0x5c, 0x2b, 0xc2, 0x08, 0xbf, 0x54, 0x4c, 0x0f, 0xb0, 0xab, 0x45, - 0xe8, 0x7f, 0x42, 0xcf, 0x48, 0xbc, 0x3d, 0xce, 0x8c, 0xb6, 0xd3, 0x97, - 0x8b, 0x30, 0xad, 0xfd, 0x2f, 0xd6, 0xca, 0xcc, 0x88, 0x2f, 0x1b, 0xfd, - 0xa4, 0xe9, 0xf6, 0xdc, 0xbc, 0x4b, 0xcc, 0xf8, 0xee, 0x92, 0xdf, 0x4b, - 0x82, 0x00, 0xa2, 0xe0, 0x42, 0x27, 0x82, 0x26, 0x76, 0x6d, 0xcf, 0x2e, - 0xb6, 0x40, 0xbc, 0xfc, 0x15, 0xbe, 0xce, 0xd0, 0x8e, 0xd2, 0x51, 0x83, - 0x17, 0x7a, 0x0a, 0xe4, 0xdc, 0x4a, 0x92, 0xa3, 0x42, 0xa7, 0x03, 0x9d, - 0x8e, 0x78, 0xff, 0x04, 0x8e, 0x16, 0xa7, 0xe7, 0x23, 0x56, 0x15, 0x8a, - 0x21, 0xd0, 0x17, 0x5b, 0x0b, 0xf1, 0x0f, 0x21, 0x45, 0x23, 0x82, 0x5b, - 0x6a, 0xf4, 0x14, 0xea, 0x7f, 0x3b, 0xb9, 0xb6, 0xde, 0xb4, 0x61, 0x28, - 0xfc, 0x57, 0xb6, 0x68, 0x42, 0xc9, 0x1a, 0x08, 0xec, 0x69, 0xcb, 0x16, - 0xaa, 0xb6, 0x2f, 0x93, 0xb6, 0x6a, 0x93, 0xd2, 0x3d, 0x55, 0x68, 0x22, - 0x21, 0x40, 0x98, 0x13, 0x4b, 0x24, 0xa8, 0x43, 0x88, 0xff, 0xbe, 0xef, - 0x1c, 0x3b, 0xc4, 0x31, 0x61, 0x55, 0xfb, 0x42, 0x2e, 0xbe, 0xc4, 0x3e, - 0x3e, 0x97, 0xcf, 0xc7, 0xe7, 0x40, 0x88, 0xc6, 0xaf, 0xa2, 0x79, 0x9a, - 0x8d, 0x68, 0xb1, 0xc9, 0x65, 0x6c, 0x9c, 0x94, 0x77, 0xb9, 0xb6, 0x81, - 0x9c, 0x42, 0x43, 0xce, 0xc1, 0xa0, 0x8b, 0x39, 0x05, 0x38, 0x02, 0x76, - 0xd2, 0x54, 0x92, 0xae, 0xad, 0x35, 0x68, 0x32, 0x36, 0xec, 0x33, 0xce, - 0x9a, 0xcf, 0x3e, 0xf8, 0xfc, 0xc9, 0xff, 0xab, 0xac, 0xe3, 0x79, 0x33, - 0x6d, 0xff, 0x5e, 0x6b, 0x55, 0x75, 0x90, 0x26, 0x11, 0x28, 0x3a, 0x11, - 0xe8, 0xda, 0xad, 0x20, 0x33, 0x2a, 0x1e, 0xcd, 0x15, 0x16, 0x76, 0xd6, - 0x90, 0x20, 0xe6, 0xcc, 0x2b, 0xb5, 0x55, 0xbd, 0x18, 0xd5, 0x08, 0xc5, - 0x4a, 0x84, 0xb5, 0xad, 0x92, 0x75, 0xc6, 0x6d, 0xa3, 0xe5, 0x56, 0x97, - 0xb6, 0x0a, 0xc6, 0x15, 0x3d, 0xea, 0xc3, 0x6b, 0xf4, 0x46, 0x4a, 0xde, - 0xe6, 0x16, 0x0e, 0x2b, 0x04, 0x2c, 0x4c, 0xec, 0xab, 0x78, 0x58, 0xcb, - 0x9c, 0xf2, 0x46, 0x54, 0x44, 0xab, 0x38, 0xab, 0x2a, 0x9e, 0x04, 0x4d, - 0xf8, 0x1e, 0xbb, 0x1e, 0xd7, 0x01, 0x4f, 0x05, 0x05, 0xee, 0x88, 0x19, - 0x69, 0xba, 0x28, 0x78, 0x58, 0x67, 0x85, 0x2e, 0xa9, 0xe9, 0x16, 0x45, - 0x92, 0x8a, 0xde, 0x31, 0xf5, 0x63, 0x3e, 0x72, 0xc1, 0x90, 0xa3, 0x49, - 0x30, 0xf6, 0xcf, 0xbb, 0xfd, 0x55, 0x65, 0xb1, 0x5c, 0xd6, 0x0f, 0xf3, - 0x04, 0x44, 0x19, 0x7b, 0x3d, 0x35, 0x50, 0xc4, 0xae, 0xa4, 0x0f, 0xfa, - 0x7b, 0x5f, 0xf3, 0xd5, 0x5a, 0x90, 0x87, 0xef, 0x26, 0xa5, 0x6c, 0x8b, - 0xef, 0x58, 0x22, 0xdd, 0x12, 0x85, 0x31, 0x98, 0xf7, 0xe7, 0x36, 0x2f, - 0xeb, 0x7b, 0x0e, 0x86, 0x60, 0x76, 0xc3, 0xa2, 0xc8, 0xa2, 0x98, 0x97, - 0x8b, 0x8a, 0x9c, 0x57, 0x77, 0xea, 0xde, 0x3d, 0xd0, 0xce, 0x32, 0xe4, - 0x18, 0x35, 0xc7, 0x4f, 0x20, 0xac, 0xdf, 0xb2, 0x7d, 0x78, 0x78, 0xca, - 0xcb, 0xd0, 0xb9, 0xab, 0xb7, 0x62, 0x18, 0x3b, 0x7e, 0x31, 0x4f, 0xf1, - 0xa0, 0xea, 0xe3, 0xf9, 0xe8, 0x93, 0x16, 0x0b, 0xbb, 0xee, 0x8a, 0x26, - 0x70, 0x86, 0xbc, 0x5e, 0x3f, 0x4a, 0xb1, 0x0f, 0xdf, 0x4e, 0x8e, 0xcf, - 0x7d, 0x12, 0x63, 0xc4, 0xc7, 0x12, 0x09, 0x66, 0x38, 0x05, 0xcc, 0xf4, - 0x8e, 0xe1, 0x46, 0xd4, 0xc3, 0xb5, 0x35, 0x0e, 0xf5, 0xae, 0x7f, 0x2c, - 0x76, 0x04, 0x8e, 0x22, 0x0a, 0xd3, 0x12, 0xdc, 0xe0, 0x3a, 0xca, 0xcb, - 0xe3, 0xf8, 0x9d, 0x66, 0xfd, 0xfc, 0x39, 0xf6, 0xb8, 0xb5, 0x06, 0x52, - 0x1d, 0xa3, 0xf7, 0x22, 0x9c, 0x76, 0x39, 0xa8, 0x97, 0xd5, 0x60, 0xc4, - 0x31, 0xa9, 0x86, 0x7e, 0x7b, 0x39, 0x13, 0x6e, 0x75, 0x70, 0x15, 0xad, - 0x84, 0x09, 0xc1, 0x2f, 0x7f, 0xd9, 0xc4, 0x2b, 0xb4, 0x2f, 0xb7, 0x64, - 0x0c, 0x4d, 0x5e, 0x28, 0x62, 0xf9, 0x67, 0xe5, 0xfa, 0xe8, 0xd8, 0x63, - 0x69, 0x41, 0x0f, 0x2a, 0xbc, 0x15, 0x32, 0x71, 0x1f, 0xeb, 0x99, 0x7f, - 0x20, 0x20, 0x19, 0x96, 0xa0, 0x71, 0xe6, 0x9d, 0xe4, 0xd3, 0x42, 0x22, - 0x5a, 0x46, 0xa5, 0x77, 0xc4, 0xe6, 0x93, 0x69, 0xa1, 0x94, 0x8e, 0x77, - 0xe5, 0x38, 0xb0, 0xc1, 0x34, 0x67, 0x1d, 0x96, 0x65, 0x4e, 0xbb, 0x05, - 0xbf, 0xf5, 0x7f, 0x94, 0x0f, 0x13, 0xcc, 0x64, 0x17, 0xb3, 0x0b, 0x32, - 0x1c, 0x1c, 0xcf, 0xbd, 0xe2, 0xbe, 0x40, 0xf4, 0x9d, 0xd0, 0x64, 0xa7, - 0x75, 0xfa, 0x03, 0xde, 0x05, 0xaf, 0x82, 0x46, 0xbf, 0x0b, 0x3e, 0x15, - 0xed, 0x72, 0x21, 0x25, 0x2c, 0xa9, 0x63, 0x96, 0x5e, 0x46, 0x07, 0x5f, - 0x92, 0xb8, 0xd0, 0xe8, 0x0c, 0x9c, 0x21, 0xcb, 0x5b, 0xb9, 0xd8, 0xb3, - 0x93, 0xa1, 0x71, 0xc5, 0x1e, 0xc0, 0xe3, 0x91, 0x0b, 0x61, 0x58, 0xc8, - 0x27, 0xf6, 0x61, 0x51, 0xc5, 0xd1, 0x1a, 0x86, 0x06, 0x4a, 0x17, 0x6c, - 0x92, 0x66, 0x6e, 0xf0, 0x78, 0x3d, 0x98, 0x5d, 0x01, 0x67, 0x44, 0xb8, - 0x78, 0x11, 0x6e, 0x06, 0xb3, 0xf7, 0x5e, 0xb0, 0xca, 0xcf, 0x96, 0x58, - 0x82, 0xe8, 0x11, 0x91, 0xbb, 0x03, 0x68, 0x1a, 0xc3, 0xe1, 0x4b, 0x8e, - 0x2c, 0xc4, 0x85, 0xfe, 0xbe, 0x05, 0x17, 0x56, 0x64, 0xb0, 0xa7, 0x9d, - 0x18, 0x6b, 0x47, 0x65, 0x47, 0x3a, 0x6c, 0x10, 0x23, 0x23, 0xa8, 0xcd, - 0xa1, 0x17, 0x0c, 0xb8, 0xb2, 0x73, 0x43, 0xf8, 0x25, 0xd0, 0x19, 0x51, - 0x3a, 0x33, 0x8a, 0x43, 0xbb, 0x08, 0x4e, 0x56, 0x61, 0x10, 0xa4, 0x8b, - 0x72, 0x43, 0x36, 0x43, 0xee, 0x16, 0x4b, 0x01, 0x93, 0x4a, 0x6a, 0x23, - 0x98, 0x6f, 0xe6, 0x7f, 0x03, 0x91, 0x27, 0x55, 0x40, 0x14, 0x9f, 0x8c, - 0x26, 0xa3, 0x4f, 0x74, 0x37, 0xda, 0x54, 0x1c, 0x5f, 0x08, 0x6e, 0x8d, - 0x76, 0xf5, 0x72, 0xf8, 0x71, 0xda, 0x76, 0x4d, 0x27, 0x0a, 0x6f, 0x1a, - 0x8f, 0x88, 0x41, 0xca, 0x29, 0xef, 0xf6, 0xb0, 0xb9, 0x55, 0x43, 0x9f, - 0xaa, 0x5d, 0x5b, 0xf3, 0x92, 0x86, 0x6d, 0xbd, 0x52, 0x04, 0xb1, 0x5e, - 0x6a, 0xb3, 0xac, 0x77, 0x8d, 0xe6, 0xbf, 0x67, 0x58, 0x15, 0x35, 0xd2, - 0x6f, 0x33, 0x6a, 0x8c, 0x8c, 0xad, 0x53, 0x2e, 0x4c, 0xb2, 0x55, 0xf5, - 0x55, 0x02, 0x89, 0x99, 0xf1, 0x36, 0xd5, 0x52, 0x08, 0x91, 0xd3, 0xfd, - 0xaa, 0xdf, 0x9c, 0x23, 0x50, 0xa8, 0xbf, 0x6e, 0x4c, 0x4a, 0xff, 0x80, - 0x54, 0xed, 0xe9, 0x3f, 0xc1, 0x7d, 0x6b, 0xa8, 0x08, 0x48, 0x00, 0x00 + 0x1f, 0x8b, 0x08, 0x08, 0x4d, 0xcf, 0x92, 0x5e, 0x00, 0x03, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xdc, 0x3b, 0x89, 0x76, + 0xdb, 0xb6, 0x96, 0xbf, 0x42, 0x33, 0x53, 0x9b, 0x1c, 0x51, 0xd4, 0x12, + 0x6f, 0x91, 0x4c, 0xbb, 0x5e, 0xe4, 0x25, 0xde, 0x77, 0x3b, 0x99, 0xcc, + 0x39, 0x90, 0x08, 0x49, 0xb4, 0x29, 0x92, 0x21, 0x41, 0x4b, 0xb2, 0xe3, + 0xf9, 0x96, 0x7e, 0x4b, 0xbf, 0x6c, 0xee, 0x05, 0xb8, 0x4a, 0x94, 0xdd, + 0xcc, 0xb4, 0xaf, 0x9d, 0x69, 0x7a, 0x22, 0x12, 0x04, 0x2e, 0xee, 0xbe, + 0x01, 0x59, 0x9b, 0xdb, 0x39, 0xdd, 0xbe, 0xba, 0x3f, 0x6b, 0x49, 0x7d, + 0x36, 0xb0, 0xa5, 0xb3, 0xeb, 0xad, 0xa3, 0x83, 0x6d, 0x49, 0x2e, 0x57, + 0x2a, 0xb7, 0x1f, 0xb7, 0x2b, 0x95, 0x9d, 0xab, 0x1d, 0xe9, 0x6e, 0xff, + 0xea, 0xf8, 0x48, 0xaa, 0xe9, 0x55, 0xe9, 0xca, 0x27, 0x4e, 0x60, 0x31, + 0xcb, 0x75, 0x88, 0x5d, 0xa9, 0xb4, 0x4e, 0x64, 0x49, 0xee, 0x33, 0xe6, + 0x35, 0x2a, 0x95, 0xe1, 0x70, 0xa8, 0x0f, 0x3f, 0xea, 0xae, 0xdf, 0xab, + 0x5c, 0x5d, 0x54, 0x46, 0x08, 0xab, 0x86, 0x8b, 0xa3, 0xc7, 0x32, 0xcb, + 0xac, 0xd4, 0x4d, 0x66, 0xca, 0xeb, 0x6b, 0x7c, 0x3f, 0x9b, 0x38, 0x3d, + 0x83, 0x3a, 0xeb, 0x6b, 0xcc, 0x62, 0x36, 0x5d, 0xdf, 0xb5, 0x6c, 0x2a, + 0x0d, 0x88, 0x43, 0x7a, 0xd4, 0x5f, 0xab, 0x88, 0xb1, 0xb5, 0x80, 0x8d, + 0x71, 0x94, 0x9a, 0x16, 0x31, 0x82, 0x8e, 0x4f, 0x61, 0x7a, 0xdb, 0x35, + 0xc7, 0x2f, 0x5d, 0xd7, 0x61, 0xe5, 0xc0, 0x7a, 0xa6, 0x8d, 0x5a, 0xdd, + 0x1b, 0x35, 0xf9, 0x6b, 0x97, 0x0c, 0x2c, 0x7b, 0xdc, 0xb8, 0xa1, 0xbe, + 0x09, 0x50, 0xb4, 0x4d, 0xdf, 0x22, 0xb6, 0x76, 0x09, 0x7b, 0x97, 0x03, + 0xea, 0x5b, 0xdd, 0x57, 0xbd, 0x03, 0xb3, 0xe8, 0x88, 0x1d, 0x53, 0x27, + 0x7c, 0x79, 0x2e, 0x5b, 0x8e, 0x49, 0x47, 0x8d, 0x8f, 0xd5, 0x6a, 0xd3, + 0x73, 0x05, 0x7a, 0x0d, 0xd2, 0x0e, 0x5c, 0x3b, 0x64, 0xb4, 0x69, 0xd3, + 0x2e, 0x6b, 0x2c, 0x01, 0xe0, 0xb6, 0xeb, 0x9b, 0xd4, 0x6f, 0xd4, 0xbc, + 0x91, 0x04, 0x9f, 0x2c, 0x53, 0xfa, 0xb0, 0xb8, 0xb8, 0xd8, 0x6c, 0x93, + 0xce, 0x63, 0xcf, 0x77, 0x43, 0xc7, 0x2c, 0x77, 0x5c, 0xdb, 0xf5, 0x1b, + 0x1f, 0xba, 0x4b, 0xf8, 0xa7, 0x69, 0x5a, 0x81, 0x67, 0x93, 0x71, 0xc3, + 0x71, 0x1d, 0x0a, 0x6b, 0x47, 0xe5, 0xa0, 0x4f, 0x4c, 0x77, 0xd8, 0xa8, + 0x4a, 0x55, 0xa9, 0x56, 0x05, 0x20, 0x7e, 0xaf, 0x4d, 0x94, 0xaa, 0x86, + 0x7f, 0xf4, 0x45, 0xb5, 0x99, 0x21, 0xa3, 0x16, 0x93, 0x31, 0xa4, 0x56, + 0xaf, 0xcf, 0x1a, 0x2b, 0xd5, 0x6a, 0x0e, 0x63, 0x29, 0xb4, 0x5f, 0x6c, + 0x2b, 0x80, 0xe9, 0xc8, 0x12, 0xb1, 0x03, 0x73, 0xbd, 0x46, 0x55, 0x20, + 0x5b, 0x6d, 0x0e, 0x88, 0xdf, 0xb3, 0x1c, 0x78, 0xf0, 0x88, 0x69, 0x5a, + 0x4e, 0xaf, 0x31, 0xb1, 0xde, 0xb6, 0x5e, 0x12, 0x42, 0x7d, 0x6a, 0x13, + 0x66, 0x3d, 0xd1, 0xe6, 0xc0, 0x72, 0xca, 0x43, 0xcb, 0x64, 0xfd, 0xc6, + 0x32, 0xa0, 0xd7, 0xec, 0x84, 0x7e, 0x00, 0xe4, 0x78, 0xae, 0x05, 0xeb, + 0xfc, 0xfc, 0xfa, 0xc0, 0x23, 0xce, 0x4b, 0x44, 0x2e, 0x32, 0x21, 0xa6, + 0xd5, 0x72, 0x6c, 0xcb, 0xa1, 0xe5, 0xb6, 0xed, 0x76, 0x1e, 0x93, 0xbd, + 0x97, 0xbd, 0xd1, 0xe4, 0xee, 0x8d, 0xbe, 0xfb, 0x44, 0xfd, 0x97, 0x94, + 0x77, 0x1c, 0x4c, 0xf1, 0xac, 0xdc, 0x66, 0x94, 0x52, 0x98, 0x15, 0x04, + 0xa0, 0x48, 0x94, 0x3e, 0x59, 0x74, 0x08, 0xd3, 0xb4, 0xfc, 0x00, 0xf0, + 0x26, 0xa1, 0x3a, 0x65, 0xc4, 0x04, 0xbb, 0x26, 0x80, 0x58, 0x8e, 0x17, + 0xb2, 0x97, 0x69, 0xd9, 0xbb, 0x1e, 0xe9, 0x58, 0x6c, 0xcc, 0xd9, 0x97, + 0x99, 0xff, 0x32, 0x21, 0xab, 0xf2, 0xc0, 0x7d, 0x2e, 0x87, 0xa0, 0x59, + 0xa0, 0x5d, 0x36, 0xed, 0x30, 0x21, 0x11, 0x90, 0x5e, 0xfb, 0xd1, 0x62, + 0xd3, 0x1f, 0x26, 0x07, 0x26, 0x90, 0xc9, 0xd2, 0x5b, 0xad, 0x76, 0xa7, + 0x05, 0x31, 0x39, 0x39, 0xe2, 0x26, 0x72, 0xae, 0x6c, 0xd2, 0x8e, 0xeb, + 0x13, 0x4e, 0x05, 0xb0, 0x95, 0xfa, 0x28, 0x8f, 0x22, 0x62, 0x4b, 0x36, + 0x69, 0x53, 0xbb, 0x04, 0xcc, 0x8a, 0x59, 0x24, 0xe1, 0x9f, 0x7a, 0x9d, + 0x0b, 0x6b, 0x6a, 0xfa, 0x7f, 0xc1, 0xc4, 0xac, 0x42, 0x4f, 0x0a, 0x01, + 0x81, 0x69, 0x05, 0x63, 0x8d, 0x46, 0x9b, 0x76, 0x5d, 0x9f, 0xbe, 0xbc, + 0x49, 0x04, 0xdf, 0xa2, 0x01, 0xf0, 0x49, 0xdb, 0xa6, 0xa6, 0x40, 0x2d, + 0x5e, 0x61, 0xd2, 0x2e, 0x09, 0x6d, 0x96, 0x88, 0x42, 0x5f, 0x2e, 0x5c, + 0xdc, 0xe9, 0xd3, 0xce, 0x23, 0x35, 0x01, 0x39, 0xa6, 0x24, 0x90, 0xd4, + 0x2c, 0xda, 0x5c, 0x29, 0x7f, 0x16, 0x6f, 0xad, 0x40, 0x34, 0x19, 0xad, + 0x0d, 0x7d, 0x5b, 0x31, 0x09, 0x23, 0x0d, 0x6b, 0x00, 0xae, 0xaa, 0xe2, + 0x39, 0x3d, 0xf0, 0x07, 0x01, 0x5d, 0x5e, 0xd4, 0xac, 0x9b, 0xad, 0xd3, + 0x8b, 0x61, 0xf5, 0x70, 0xaf, 0xe7, 0x6e, 0xc2, 0x7f, 0x27, 0x97, 0xd7, + 0xfd, 0xd6, 0x75, 0x0f, 0x9e, 0xb6, 0xf0, 0x75, 0xbb, 0xb7, 0xbd, 0x79, + 0x8c, 0x0f, 0xe3, 0xa5, 0xd1, 0x70, 0x80, 0x0f, 0xed, 0xda, 0xd6, 0xf1, + 0x4d, 0xeb, 0x66, 0xbf, 0xd3, 0x1a, 0xdf, 0x0e, 0x96, 0x96, 0x6e, 0x6f, + 0xeb, 0xad, 0xe5, 0xf3, 0x6b, 0x73, 0xf3, 0xbc, 0xb5, 0x65, 0x91, 0xbd, + 0xda, 0x03, 0xd9, 0x5b, 0xa9, 0x54, 0x2a, 0xab, 0x4f, 0x27, 0x0f, 0x1f, + 0x0f, 0x9f, 0x8f, 0x57, 0xb6, 0x47, 0xc7, 0xed, 0xc1, 0x52, 0xd8, 0x3d, + 0x7e, 0xee, 0x54, 0xee, 0xeb, 0xe6, 0xf7, 0x11, 0x3b, 0x22, 0x7b, 0x96, + 0xbb, 0xb4, 0xda, 0xbb, 0xbf, 0xfd, 0xfc, 0xf0, 0x65, 0xf7, 0xe2, 0x66, + 0xf7, 0xcb, 0xe7, 0xab, 0x56, 0xe5, 0xe8, 0xb9, 0x53, 0x7a, 0x0a, 0xda, + 0x27, 0xe6, 0xdd, 0xcd, 0xca, 0x62, 0xe9, 0xac, 0xff, 0x64, 0xee, 0xdb, + 0x41, 0xfb, 0xb6, 0xfe, 0xe8, 0x2d, 0x7f, 0x5f, 0x79, 0x3a, 0x7a, 0x1e, + 0xaf, 0x3e, 0x1d, 0x87, 0x27, 0x57, 0xcf, 0xdd, 0x8f, 0x9f, 0x4a, 0x7d, + 0x77, 0xf9, 0x7a, 0x7c, 0x7a, 0xbd, 0xbd, 0xdb, 0xbf, 0xbf, 0xbd, 0xb6, + 0x97, 0x9c, 0xa7, 0x95, 0x52, 0xc5, 0x5b, 0xa6, 0x8f, 0xe7, 0x56, 0x65, + 0xef, 0x02, 0x71, 0xdc, 0xbc, 0xbb, 0xb8, 0xbc, 0xb2, 0x8f, 0x37, 0xcf, + 0x4f, 0xdb, 0xf7, 0x5f, 0x90, 0x96, 0xcb, 0x8b, 0xcf, 0x17, 0xad, 0xdd, + 0xeb, 0xcb, 0xe3, 0x2e, 0x7b, 0xfc, 0xc4, 0xc6, 0x43, 0x6b, 0xf3, 0xbc, + 0xef, 0x5e, 0x6f, 0xf6, 0x6f, 0x36, 0x87, 0x9f, 0xbd, 0xef, 0x3b, 0x17, + 0xdf, 0xbb, 0xe4, 0xe9, 0x69, 0xf5, 0xd9, 0x0e, 0x4f, 0x0e, 0x1f, 0x43, + 0xbf, 0xf5, 0xe0, 0xdf, 0x7f, 0x2c, 0xd1, 0x95, 0xc5, 0xcf, 0xd6, 0xf3, + 0x91, 0x53, 0xbf, 0xad, 0x0d, 0xac, 0xcd, 0xc3, 0x91, 0xd7, 0x3f, 0xdd, + 0x3a, 0xa6, 0xd7, 0xf7, 0xdf, 0x97, 0xc3, 0xfd, 0xca, 0xe6, 0xc7, 0xcd, + 0xe5, 0xe5, 0x7b, 0xef, 0x62, 0xeb, 0xe2, 0xfb, 0xe7, 0x2f, 0xe4, 0xf8, + 0x60, 0xd5, 0x1a, 0x06, 0x37, 0x95, 0x6d, 0xf3, 0x78, 0x79, 0xb3, 0x3e, + 0x7a, 0x58, 0x72, 0x0e, 0xae, 0x83, 0xc3, 0x6a, 0xc5, 0xba, 0xba, 0x3e, + 0xf3, 0xf7, 0x4e, 0x06, 0xd5, 0xe3, 0xeb, 0x9d, 0x83, 0x47, 0xba, 0x57, + 0xf9, 0xbc, 0xb4, 0x18, 0x9e, 0x5a, 0x8f, 0x41, 0xfb, 0x53, 0xff, 0xae, + 0xbf, 0xb4, 0x74, 0xd6, 0x3f, 0x38, 0x78, 0xe8, 0x1e, 0xf6, 0xcc, 0xcf, + 0x57, 0xfb, 0x97, 0xad, 0x71, 0xbd, 0xb2, 0xbb, 0x53, 0x5d, 0xbe, 0x1b, + 0xb8, 0xe6, 0xea, 0xc9, 0xe9, 0xd0, 0xf7, 0x87, 0xbb, 0xd7, 0xc1, 0xf9, + 0xe0, 0xee, 0xcb, 0xfe, 0x97, 0x7e, 0x9f, 0x3e, 0xee, 0x6f, 0x59, 0x5b, + 0xe3, 0xfb, 0x03, 0x97, 0x1c, 0x7c, 0xde, 0x7c, 0x38, 0x5b, 0xbd, 0xbe, + 0xbc, 0xb5, 0xb6, 0x37, 0x57, 0x0f, 0xfb, 0xad, 0xdb, 0xd5, 0xeb, 0xbd, + 0xab, 0x95, 0xc3, 0x33, 0xf2, 0xa5, 0x35, 0x0a, 0x4e, 0xdb, 0xfb, 0x63, + 0xff, 0xba, 0x77, 0xf5, 0xf8, 0x70, 0xf5, 0xbc, 0x6a, 0x5b, 0x67, 0x77, + 0xc3, 0xe7, 0x61, 0x6b, 0xab, 0x74, 0x7a, 0xb6, 0x7b, 0x33, 0xda, 0x6f, + 0xad, 0xde, 0xd5, 0x3b, 0x8f, 0x17, 0x5b, 0xe3, 0x23, 0x72, 0x33, 0xee, + 0xdf, 0x1c, 0x8e, 0xce, 0xea, 0x2b, 0x87, 0x27, 0x25, 0x67, 0x93, 0xed, + 0xaf, 0x5c, 0x3c, 0x85, 0x7e, 0x7d, 0xc7, 0x5f, 0xae, 0xd7, 0x18, 0x7d, + 0x3c, 0xa6, 0x41, 0xe9, 0xd6, 0xda, 0x5b, 0x5d, 0xde, 0xf7, 0x97, 0x1f, + 0x0e, 0xef, 0x1f, 0x4a, 0x2b, 0xe7, 0xb5, 0x43, 0xb3, 0x7a, 0xe6, 0x8d, + 0x0e, 0x96, 0x56, 0x4f, 0x82, 0x73, 0xf3, 0xa4, 0x52, 0x5f, 0x7a, 0xb6, + 0xcf, 0x77, 0xce, 0xcd, 0xc3, 0xf6, 0xa7, 0x4d, 0xe7, 0x78, 0xb9, 0xbb, + 0x7f, 0xb9, 0xf7, 0x78, 0x16, 0x9c, 0x93, 0xcf, 0x64, 0x70, 0xe0, 0x9d, + 0x3f, 0x6f, 0xfb, 0xe3, 0x61, 0x7f, 0xa7, 0x66, 0x5d, 0xd5, 0xef, 0x1e, + 0x83, 0xa3, 0xed, 0x61, 0x50, 0x39, 0xf8, 0xf2, 0xb4, 0xfa, 0xc5, 0xed, + 0xae, 0xb0, 0xfa, 0xd2, 0xbd, 0xfd, 0xc8, 0xc5, 0x74, 0x79, 0x7d, 0x73, + 0x7a, 0x71, 0xb8, 0xb4, 0x7d, 0x7f, 0x70, 0x60, 0xa8, 0x92, 0xe3, 0x96, + 0x7d, 0xea, 0x51, 0xc2, 0xfe, 0x04, 0xdd, 0x2f, 0x18, 0x4a, 0x2c, 0xbc, + 0x30, 0x06, 0xf4, 0x45, 0x00, 0xab, 0x41, 0x0c, 0x68, 0xf2, 0xe1, 0xec, + 0x00, 0x38, 0x2d, 0x66, 0x75, 0x88, 0x5d, 0x26, 0xb6, 0xd5, 0x73, 0x1a, + 0x03, 0xcb, 0x34, 0xed, 0x42, 0xcf, 0x92, 0x31, 0xb9, 0x72, 0xe2, 0xa4, + 0x6b, 0xab, 0x10, 0x43, 0xab, 0x45, 0xbe, 0x73, 0xe6, 0xec, 0xf2, 0xe2, + 0xea, 0x94, 0x7b, 0xc3, 0x15, 0x3a, 0x1b, 0xb1, 0x37, 0x56, 0x2d, 0x2f, + 0x16, 0xaf, 0xb2, 0x06, 0xbd, 0x37, 0x56, 0xad, 0x56, 0xa7, 0x56, 0x4d, + 0xb8, 0x44, 0x0c, 0x76, 0x0e, 0x6b, 0xc8, 0x72, 0x53, 0x84, 0x5d, 0xce, + 0x94, 0xc4, 0x1d, 0xa3, 0x2b, 0x46, 0x9f, 0x5c, 0xcc, 0xa6, 0x66, 0xd1, + 0xc6, 0x55, 0xa9, 0xfc, 0x71, 0xda, 0x81, 0xe7, 0xa4, 0xf4, 0x07, 0x37, + 0x7d, 0xcb, 0xc7, 0x96, 0xf2, 0x64, 0xcc, 0x40, 0x04, 0xe1, 0xbe, 0xfe, + 0xca, 0xb3, 0x35, 0x49, 0x64, 0x6b, 0x12, 0x71, 0x4c, 0x49, 0x89, 0xa3, + 0x22, 0x66, 0x1b, 0x26, 0x40, 0xef, 0xd0, 0xb2, 0x67, 0x8d, 0xa8, 0x5d, + 0xe6, 0x51, 0xab, 0x51, 0x55, 0x5f, 0xf2, 0xa1, 0x36, 0x9e, 0x4f, 0x1c, + 0xf0, 0xb1, 0x1c, 0x78, 0x3c, 0x60, 0x3e, 0x90, 0x0e, 0x90, 0x52, 0x86, + 0x40, 0x3a, 0xc0, 0x5f, 0x11, 0x51, 0x5d, 0xbf, 0xdc, 0x0e, 0x7b, 0x5d, + 0x6b, 0x04, 0x48, 0x77, 0x2d, 0xc7, 0x62, 0x54, 0xaa, 0x05, 0xaf, 0xbf, + 0xc6, 0x60, 0x1e, 0xe9, 0xb8, 0xeb, 0x93, 0x01, 0x0d, 0xa4, 0x3f, 0x08, + 0xe6, 0xa5, 0xeb, 0xbb, 0x83, 0x34, 0xa3, 0x78, 0x65, 0x6e, 0xe6, 0xe5, + 0xf5, 0xf5, 0x43, 0x37, 0xb8, 0x72, 0x19, 0xb1, 0x8b, 0xd5, 0x3f, 0x62, + 0x71, 0x15, 0xf3, 0xa9, 0x58, 0xf3, 0xab, 0x85, 0xa9, 0x64, 0x15, 0x72, + 0x4f, 0x8c, 0xe2, 0x5d, 0x1b, 0x52, 0xc4, 0x3e, 0x88, 0x98, 0x3a, 0xcd, + 0x6c, 0x7a, 0x44, 0x97, 0xf1, 0x0f, 0x6e, 0x77, 0x1d, 0x50, 0xf3, 0x6d, + 0x63, 0xab, 0x56, 0x7f, 0x89, 0x76, 0x28, 0xfb, 0x62, 0x24, 0xdd, 0xa7, + 0xbe, 0x4c, 0xaa, 0x26, 0xc9, 0x81, 0x16, 0x43, 0xaf, 0x1f, 0x86, 0xc4, + 0x77, 0x80, 0xac, 0x97, 0x1c, 0x98, 0xc9, 0xec, 0x16, 0x92, 0x0b, 0xa7, + 0x47, 0x9b, 0x49, 0x7a, 0x02, 0x9a, 0xc2, 0x5c, 0xd7, 0x66, 0x96, 0x57, + 0x90, 0x3e, 0xc5, 0x89, 0x75, 0xbd, 0x29, 0xf0, 0xa8, 0xf2, 0x2c, 0xb5, + 0x8e, 0x1c, 0x78, 0xb2, 0x02, 0xab, 0x6d, 0xd9, 0x18, 0xd1, 0xa7, 0xa8, + 0x4d, 0x12, 0xe9, 0x6e, 0x37, 0xb3, 0x51, 0x93, 0xe7, 0x37, 0xc2, 0x06, + 0x50, 0x60, 0xd4, 0x9f, 0xc1, 0xc6, 0x58, 0x3c, 0x1f, 0xe3, 0x2c, 0x5a, + 0x24, 0x6a, 0x68, 0x8e, 0xb1, 0xb4, 0xa2, 0xcc, 0x32, 0x41, 0x3d, 0x83, + 0x0d, 0x7f, 0x04, 0x37, 0x14, 0xb3, 0xe3, 0x8f, 0x4c, 0xed, 0x53, 0x02, + 0x78, 0x14, 0x30, 0x40, 0x24, 0xe5, 0x31, 0xf1, 0x51, 0x72, 0x1e, 0xf1, + 0xb7, 0xbe, 0x3a, 0xe1, 0x13, 0xeb, 0xe0, 0x66, 0x62, 0xe4, 0xcb, 0x7c, + 0xae, 0xd0, 0x95, 0x29, 0xbe, 0x60, 0xc6, 0x9d, 0xc9, 0x87, 0x3f, 0xa0, + 0xad, 0xcc, 0xd8, 0x9c, 0x6f, 0xd2, 0x76, 0x19, 0x73, 0x07, 0x29, 0x02, + 0x42, 0x2b, 0xeb, 0x20, 0xde, 0x98, 0x55, 0xe8, 0x16, 0x3f, 0x80, 0xa9, + 0x82, 0xda, 0x6b, 0x1f, 0x3c, 0x5f, 0x58, 0xde, 0x1b, 0x10, 0x63, 0x8a, + 0xf2, 0x90, 0x01, 0xe2, 0x6b, 0xb2, 0x7a, 0x1a, 0x6d, 0xc0, 0x35, 0xd9, + 0x70, 0x09, 0x37, 0x0c, 0x18, 0x61, 0x61, 0x30, 0x63, 0x9f, 0xc5, 0x64, + 0x9b, 0x5a, 0xc6, 0x7a, 0xf8, 0xee, 0x19, 0xa1, 0xa2, 0x67, 0xfe, 0x60, + 0xbb, 0x04, 0x81, 0xce, 0x00, 0x54, 0x06, 0x45, 0x7e, 0xea, 0xe7, 0x69, + 0xc7, 0xa1, 0x61, 0xc6, 0x5e, 0xe0, 0x7b, 0xac, 0xab, 0xf0, 0x36, 0xcd, + 0xf2, 0x6c, 0xa9, 0xb6, 0xa4, 0xa6, 0x65, 0x41, 0x33, 0xad, 0x67, 0x1b, + 0xd1, 0xa0, 0xa4, 0x2f, 0x05, 0x12, 0x85, 0x24, 0x10, 0xe0, 0x95, 0xdd, + 0x90, 0x25, 0xe8, 0xe9, 0x41, 0xdf, 0x1d, 0x3a, 0x2f, 0x42, 0x25, 0x62, + 0x08, 0xb5, 0xe4, 0x73, 0x79, 0x10, 0xf4, 0x8a, 0xcd, 0x7a, 0x96, 0x5a, + 0x89, 0xc2, 0x14, 0xa4, 0xc8, 0x91, 0x00, 0x5f, 0x3c, 0x68, 0xf0, 0x27, + 0x28, 0xe7, 0xe8, 0x9d, 0x52, 0x86, 0x2f, 0x6a, 0x33, 0x63, 0x49, 0x29, + 0xd7, 0x78, 0x8c, 0xf8, 0x35, 0xf5, 0x84, 0x81, 0x67, 0x39, 0x0e, 0x38, + 0x0b, 0x74, 0xb1, 0x2f, 0xd5, 0x5f, 0x5e, 0x52, 0x78, 0x3e, 0x18, 0x0b, + 0xa3, 0x4a, 0x55, 0x7d, 0x45, 0x6f, 0x30, 0xfd, 0xe1, 0xe3, 0x72, 0xd5, + 0xa4, 0x3d, 0xf5, 0xf5, 0x55, 0xcf, 0xc2, 0x10, 0x49, 0xb8, 0x4f, 0xbf, + 0x87, 0x96, 0x0f, 0x49, 0xf8, 0x3b, 0x54, 0x25, 0xf5, 0x27, 0x52, 0x85, + 0xe4, 0xbc, 0x45, 0x17, 0x27, 0x4b, 0x13, 0xb4, 0xa5, 0x11, 0x21, 0xbb, + 0x39, 0x78, 0xfb, 0xd4, 0xf3, 0xe3, 0x7e, 0x24, 0xf5, 0x13, 0xcb, 0xa9, + 0xa3, 0x40, 0x65, 0xcc, 0xfa, 0xc8, 0x48, 0xd0, 0x7c, 0x23, 0x8f, 0xf8, + 0xe0, 0x5f, 0x92, 0xcf, 0x20, 0x9e, 0x30, 0xe0, 0x4c, 0x13, 0x65, 0xbc, + 0xf5, 0x8c, 0x1a, 0x1c, 0x7d, 0x85, 0x91, 0x29, 0xbf, 0xcd, 0x1d, 0x15, + 0x6a, 0x13, 0xc4, 0xd9, 0xf2, 0x27, 0xf8, 0x0f, 0x56, 0x46, 0x85, 0xf5, + 0x62, 0xaa, 0xca, 0x3c, 0xab, 0x58, 0xab, 0xf0, 0x9a, 0x74, 0x7d, 0x0d, + 0x42, 0xa4, 0xe5, 0xb1, 0xf5, 0x27, 0xe2, 0x4b, 0x68, 0xce, 0x5a, 0x37, + 0x38, 0x70, 0xba, 0x6e, 0xb3, 0x1b, 0x3a, 0x1d, 0x24, 0x51, 0x0a, 0x28, + 0x3b, 0x12, 0x8a, 0xa2, 0x50, 0x8d, 0xa9, 0x2f, 0x6c, 0x7e, 0x1e, 0x42, + 0x39, 0x10, 0x43, 0x75, 0xdb, 0xed, 0x29, 0x4c, 0xd5, 0x4c, 0xb7, 0x13, + 0x62, 0x00, 0xd3, 0x7b, 0x94, 0xb5, 0x44, 0x2c, 0xdb, 0x1a, 0x1f, 0x98, + 0x8a, 0x9c, 0x51, 0x30, 0x59, 0xd5, 0x39, 0xa7, 0xb0, 0x93, 0x63, 0xb0, + 0x1f, 0x3f, 0x64, 0x59, 0xa3, 0x1b, 0xef, 0x2d, 0x84, 0x45, 0x1d, 0x9b, + 0x04, 0xc1, 0x11, 0x94, 0xd0, 0x3a, 0xd8, 0xaf, 0x22, 0x73, 0x4d, 0x96, + 0xd5, 0xc6, 0x4f, 0xad, 0xf4, 0xe9, 0x00, 0x18, 0x95, 0x2c, 0x4e, 0xf1, + 0xc5, 0x26, 0x8e, 0xce, 0xd9, 0xa0, 0x8b, 0x9a, 0xcf, 0xa0, 0x1b, 0xf2, + 0x90, 0x58, 0x4c, 0x6e, 0xc8, 0x51, 0xf5, 0x27, 0xbf, 0x26, 0x8c, 0xf0, + 0xc1, 0xd7, 0x62, 0x71, 0x77, 0x09, 0x9a, 0xac, 0x50, 0xf5, 0xc5, 0xea, + 0x2a, 0x74, 0xad, 0x56, 0xad, 0x2f, 0xaa, 0x3e, 0x65, 0xa1, 0xef, 0x48, + 0xb4, 0x24, 0x4b, 0x5b, 0x32, 0xa8, 0xbb, 0xaf, 0x70, 0x6e, 0x1a, 0xe5, + 0x5a, 0x93, 0x95, 0x4a, 0x1a, 0xce, 0x59, 0x53, 0x68, 0xc5, 0xe0, 0x93, + 0x9b, 0x6a, 0x33, 0x9e, 0x0f, 0xae, 0x7d, 0x17, 0x32, 0x10, 0x53, 0xa9, + 0xab, 0xa5, 0xaf, 0xb2, 0x74, 0x68, 0x6d, 0xc9, 0x9a, 0x2c, 0x1d, 0x8b, + 0x9f, 0x3d, 0xf1, 0x73, 0xc5, 0x7f, 0xce, 0xe0, 0xef, 0x6f, 0x5f, 0xd9, + 0xb7, 0x2c, 0x36, 0x5d, 0x9f, 0x06, 0xfd, 0x4b, 0xee, 0xc9, 0x14, 0x50, + 0xf5, 0x59, 0x1c, 0x11, 0xbe, 0x2e, 0xc7, 0x7f, 0x59, 0x89, 0x56, 0xa3, + 0x73, 0xd0, 0x75, 0x55, 0x6e, 0x0a, 0x7c, 0x1d, 0x48, 0xb8, 0xee, 0x8e, + 0x8f, 0xf6, 0x19, 0xf3, 0x2e, 0xc0, 0x86, 0x68, 0xc0, 0x9a, 0x4c, 0x77, + 0x1d, 0xe4, 0xa9, 0x11, 0x6f, 0xac, 0x70, 0xc2, 0xeb, 0xd5, 0xea, 0x9c, + 0xc1, 0x74, 0x01, 0x5b, 0x45, 0xc6, 0xe2, 0xa2, 0x96, 0xef, 0x03, 0xed, + 0x4c, 0x6d, 0x52, 0x3b, 0xa0, 0x2f, 0x08, 0x93, 0x1a, 0x8a, 0xd0, 0x27, + 0xe3, 0xf3, 0xe5, 0xe9, 0x89, 0x0e, 0xfa, 0x1d, 0x50, 0x05, 0x25, 0x12, + 0x78, 0xa0, 0x43, 0xf4, 0x0a, 0xf4, 0x55, 0x55, 0x75, 0x36, 0xf6, 0x90, + 0x77, 0x65, 0x09, 0x98, 0xc7, 0x67, 0xeb, 0x56, 0x70, 0xfa, 0xb8, 0xa1, + 0xd0, 0x92, 0x21, 0xaf, 0x61, 0x12, 0x29, 0x59, 0xa6, 0xb1, 0x10, 0x45, + 0xd0, 0x85, 0xf5, 0xec, 0x10, 0xe6, 0x24, 0x0b, 0x12, 0x97, 0xa1, 0xb1, + 0x20, 0x54, 0x5d, 0x2e, 0x29, 0xb5, 0xd2, 0x31, 0x61, 0x7d, 0x9d, 0x3b, + 0x52, 0xe5, 0xd3, 0xa7, 0x7f, 0x8f, 0xa0, 0x86, 0x30, 0x79, 0x6b, 0xcc, + 0x68, 0x50, 0x89, 0x06, 0x18, 0x02, 0xe4, 0x23, 0xaa, 0x5a, 0x92, 0x7f, + 0x01, 0xd0, 0x15, 0x84, 0x1d, 0xed, 0xc0, 0x35, 0xc8, 0x58, 0x88, 0xe2, + 0xef, 0xc2, 0xba, 0x5c, 0xca, 0xa9, 0xc0, 0x14, 0x8c, 0xf2, 0xe4, 0x36, + 0x00, 0x73, 0xde, 0x69, 0x07, 0x5e, 0x13, 0x98, 0x4d, 0xc5, 0x93, 0xdb, + 0x15, 0xbf, 0xef, 0xc1, 0x82, 0xa5, 0x31, 0x2e, 0xe2, 0x47, 0xd6, 0x62, + 0xe8, 0x4e, 0x10, 0x7a, 0x9e, 0xeb, 0x33, 0x6a, 0x62, 0xb3, 0x32, 0x98, + 0x9f, 0xe7, 0x7c, 0x92, 0x52, 0xae, 0x44, 0xf9, 0xc3, 0xc2, 0xfa, 0xed, + 0xe6, 0xc5, 0xc9, 0xc1, 0xc9, 0x5e, 0x31, 0x35, 0x7c, 0xf1, 0x38, 0x60, + 0x74, 0xf0, 0xfb, 0x6f, 0x98, 0x9b, 0x13, 0xcb, 0x09, 0x7e, 0xff, 0x2d, + 0x03, 0xfd, 0xf7, 0xdf, 0xba, 0x30, 0xc5, 0x41, 0x47, 0xdd, 0x58, 0x6b, + 0xfb, 0x15, 0xa0, 0x7f, 0x06, 0x0a, 0x53, 0xb8, 0xaa, 0x6a, 0x23, 0x95, + 0x5d, 0x24, 0x9c, 0xe9, 0xd0, 0x46, 0xcd, 0x28, 0x4a, 0x0c, 0xfb, 0xe0, + 0x32, 0x73, 0x7d, 0xc7, 0xb6, 0x6b, 0x9b, 0x0b, 0xeb, 0x07, 0x27, 0x07, + 0x57, 0x52, 0xeb, 0xe2, 0xe2, 0xf4, 0x42, 0x9a, 0x4b, 0xd8, 0xf0, 0x33, + 0x8a, 0x4e, 0x35, 0xf9, 0xf2, 0xec, 0x60, 0x77, 0xf7, 0x52, 0x9e, 0x33, + 0x62, 0x16, 0x83, 0xae, 0x01, 0xcb, 0x66, 0x42, 0x19, 0x3c, 0x9a, 0x96, + 0x0f, 0x40, 0x84, 0x5b, 0x88, 0x02, 0x88, 0x21, 0x8b, 0x08, 0x22, 0x43, + 0xc0, 0xd1, 0xc0, 0x24, 0x3c, 0xea, 0x28, 0xf2, 0x5e, 0xeb, 0x0a, 0x2c, + 0xb3, 0x12, 0x6d, 0xab, 0xcd, 0x55, 0x55, 0xf8, 0x14, 0x50, 0xd0, 0x38, + 0x27, 0xb4, 0x6d, 0x35, 0x35, 0xd5, 0xbc, 0x79, 0x80, 0xe7, 0x20, 0x36, + 0x14, 0x56, 0x8a, 0xcc, 0x29, 0x6b, 0x48, 0x5f, 0xe5, 0x12, 0x8d, 0x2c, + 0xa9, 0x24, 0x7f, 0x93, 0xf0, 0x2d, 0x67, 0x1f, 0x29, 0xa0, 0x0e, 0x71, + 0xd0, 0x15, 0x9f, 0xd0, 0xe1, 0xb6, 0x28, 0xa6, 0xd0, 0xf0, 0x85, 0x33, + 0x99, 0x9b, 0x9b, 0xcd, 0x17, 0xf2, 0x44, 0xb7, 0x18, 0x38, 0x3e, 0x3d, + 0xee, 0x4e, 0xfd, 0xf8, 0x31, 0x37, 0x07, 0x22, 0xef, 0x5a, 0xfe, 0x40, + 0x91, 0xb7, 0xfb, 0x98, 0x57, 0x07, 0x12, 0x73, 0xa5, 0xb1, 0x1b, 0xfa, + 0x52, 0x0c, 0x47, 0x1a, 0x5a, 0xb6, 0x2d, 0xb5, 0x21, 0x94, 0xb9, 0x01, + 0x93, 0xac, 0x2e, 0x7e, 0x95, 0x50, 0x51, 0x2c, 0x27, 0x04, 0x46, 0xa0, + 0xda, 0x39, 0x5c, 0x7f, 0x01, 0xfc, 0x8e, 0x15, 0x74, 0x88, 0x6f, 0xc2, + 0x2e, 0x81, 0x32, 0x57, 0x53, 0x91, 0x17, 0x29, 0xd6, 0xc5, 0xd3, 0xe8, + 0x5b, 0x2e, 0x6b, 0x0a, 0x63, 0x63, 0x8e, 0xce, 0x16, 0xbc, 0x99, 0x80, + 0xcd, 0xaf, 0x48, 0x51, 0x80, 0x15, 0x67, 0x3c, 0xb4, 0xee, 0x82, 0x5e, + 0x51, 0x2e, 0x83, 0xc4, 0x07, 0x03, 0xe7, 0x7d, 0x16, 0xdc, 0x5a, 0xac, + 0xaf, 0xc8, 0x15, 0x59, 0xfd, 0xf1, 0x43, 0xa1, 0x06, 0x3c, 0x94, 0xa8, + 0xaa, 0x51, 0x1d, 0xa4, 0x99, 0x7e, 0x42, 0x92, 0x0d, 0x58, 0x60, 0x43, + 0xe9, 0x08, 0x39, 0x59, 0xb9, 0xa6, 0xe2, 0x94, 0x20, 0x6c, 0x07, 0xcc, + 0xc7, 0xf0, 0x58, 0x85, 0x37, 0x30, 0x32, 0x76, 0x80, 0xe9, 0xdc, 0x69, + 0x97, 0xaf, 0xc9, 0x4a, 0x0f, 0x2c, 0x9e, 0xd1, 0x7d, 0x9e, 0xb1, 0x43, + 0x28, 0x75, 0x30, 0x98, 0xa2, 0x8f, 0x74, 0x8d, 0x59, 0x94, 0x01, 0x0a, + 0x24, 0xfd, 0x28, 0xd6, 0x47, 0xdf, 0x15, 0x99, 0x57, 0xc6, 0x32, 0x24, + 0x23, 0x5c, 0xa5, 0x0d, 0x19, 0x8d, 0x55, 0xd6, 0x88, 0x3e, 0x08, 0xd1, + 0xbe, 0xc1, 0xe2, 0xe6, 0x6a, 0xf0, 0x86, 0xe6, 0x6b, 0xc8, 0xd8, 0x29, + 0x94, 0x35, 0x57, 0x27, 0x1e, 0x68, 0xae, 0xb9, 0xdd, 0xb7, 0x6c, 0x53, + 0x21, 0x2a, 0xf7, 0xfa, 0xe6, 0xbb, 0x1b, 0x98, 0x3a, 0x38, 0x15, 0xd9, + 0x03, 0x87, 0x7a, 0xc0, 0x47, 0x34, 0x33, 0xda, 0x11, 0xb3, 0x0d, 0x7c, + 0x13, 0x7b, 0xe0, 0x04, 0x7c, 0x8b, 0x02, 0xe7, 0x0d, 0xb1, 0x43, 0xce, + 0xc8, 0x89, 0x6d, 0x4d, 0xb1, 0xad, 0x3d, 0x73, 0xdb, 0x76, 0x08, 0x99, + 0x3d, 0x48, 0xb2, 0x69, 0x67, 0x23, 0xd6, 0xb5, 0x87, 0x51, 0x68, 0x12, + 0x98, 0x2d, 0x80, 0x05, 0xef, 0x03, 0x0b, 0x38, 0x11, 0xc2, 0xb8, 0xb5, + 0x60, 0xd2, 0xba, 0xb1, 0xb1, 0x8c, 0xc3, 0x99, 0x0d, 0x8f, 0x1f, 0x77, + 0x70, 0x6a, 0x7e, 0xbf, 0x40, 0xec, 0x67, 0xbd, 0xbf, 0x9f, 0x95, 0x87, + 0xb5, 0xcb, 0x85, 0x93, 0x07, 0x66, 0x09, 0x60, 0x9d, 0x99, 0xc0, 0xd0, + 0xe1, 0x01, 0xa8, 0x0e, 0x47, 0x5d, 0xd4, 0x48, 0x5b, 0x1c, 0x3e, 0x78, + 0x9b, 0x4e, 0x31, 0x09, 0xf9, 0x1d, 0x3a, 0x62, 0x07, 0xff, 0x7d, 0x74, + 0x7d, 0xbe, 0x47, 0x6c, 0x77, 0x9a, 0x9f, 0xc5, 0x1e, 0x2d, 0x17, 0x87, + 0xc4, 0x86, 0xa2, 0x61, 0x73, 0x04, 0x99, 0xb2, 0x21, 0x7f, 0x84, 0xfa, + 0x08, 0xbf, 0xa4, 0x36, 0x57, 0x05, 0xc4, 0xb2, 0x18, 0xf8, 0x02, 0x83, + 0xc1, 0xfb, 0x18, 0x0c, 0x38, 0x06, 0x19, 0x53, 0xd6, 0x06, 0x59, 0x24, + 0x22, 0xd7, 0x81, 0xa3, 0xb3, 0x77, 0x1b, 0x88, 0xdd, 0xc2, 0xf7, 0x77, + 0x0b, 0xf9, 0x6e, 0x7d, 0x6a, 0x7b, 0x7c, 0xab, 0x30, 0xbb, 0xd5, 0x86, + 0x3c, 0x01, 0x36, 0x14, 0x60, 0xbd, 0xf7, 0x04, 0xe5, 0x09, 0x26, 0x46, + 0xf1, 0xc0, 0xcb, 0x25, 0x5c, 0x51, 0x5e, 0xaa, 0x4e, 0x8a, 0xc8, 0x03, + 0x13, 0x87, 0x14, 0xab, 0xc3, 0x9d, 0x70, 0x9a, 0x64, 0x89, 0xf4, 0x12, + 0x72, 0x2c, 0x83, 0xe8, 0x68, 0xd8, 0x81, 0x0e, 0x91, 0xb8, 0xc7, 0xfa, + 0xc2, 0x63, 0xb0, 0x78, 0xf4, 0x6b, 0xf5, 0x1b, 0xb7, 0xbe, 0xe6, 0x93, + 0x0b, 0xa5, 0x45, 0x15, 0xac, 0xef, 0x09, 0xcd, 0xae, 0xc0, 0xa7, 0x45, + 0x5f, 0xb8, 0x67, 0x8b, 0x9e, 0xd5, 0x78, 0xba, 0x31, 0xe9, 0x1e, 0xe3, + 0x09, 0x25, 0x9c, 0xcd, 0x20, 0xe4, 0xd9, 0x88, 0x22, 0xb8, 0xbd, 0xc7, + 0x1c, 0x86, 0xd3, 0xe8, 0xcd, 0xcf, 0x3b, 0x3a, 0x9e, 0xc7, 0x0a, 0x6b, + 0x55, 0x52, 0x2c, 0xe3, 0xad, 0xd4, 0x57, 0xcd, 0x2a, 0x84, 0x25, 0xc8, + 0x8a, 0xf1, 0x07, 0x7f, 0x3a, 0x50, 0xd4, 0xa6, 0x2c, 0x1b, 0x86, 0xc1, + 0x36, 0xa2, 0x70, 0xb9, 0x29, 0xc5, 0xf9, 0x88, 0x34, 0x08, 0x21, 0x24, + 0x41, 0x68, 0xea, 0x41, 0xf5, 0x86, 0xe5, 0x00, 0xcb, 0x3b, 0xea, 0x78, + 0xc5, 0x6e, 0x9c, 0xbf, 0x88, 0x05, 0x50, 0x1c, 0x42, 0x30, 0x32, 0x21, + 0xb0, 0xb1, 0xbe, 0x44, 0xa4, 0x85, 0xca, 0x82, 0x04, 0x7c, 0xf7, 0x49, + 0x87, 0x51, 0x88, 0xf8, 0x0d, 0x81, 0xfa, 0x36, 0x17, 0x2d, 0x24, 0xb1, + 0xaf, 0xe0, 0x0f, 0xfe, 0xe7, 0x98, 0x72, 0x46, 0x4a, 0x85, 0xc8, 0x2a, + 0x13, 0xd8, 0x82, 0x74, 0x58, 0x09, 0x05, 0xa3, 0x6a, 0x13, 0x28, 0x00, + 0x0e, 0x7e, 0x21, 0x0e, 0x90, 0x67, 0x80, 0x51, 0x2a, 0xf0, 0x7d, 0x30, + 0xe3, 0x7b, 0x64, 0x48, 0x38, 0x25, 0x9c, 0x05, 0x02, 0xd2, 0x92, 0xcb, + 0x3e, 0x64, 0x71, 0x9d, 0x10, 0x13, 0x89, 0xd7, 0xc9, 0x48, 0x75, 0x05, + 0x49, 0x2b, 0xc4, 0xa9, 0x40, 0x90, 0xec, 0xcc, 0x8c, 0x52, 0x72, 0xd4, + 0x78, 0x01, 0xfc, 0xd9, 0x4c, 0x13, 0x31, 0xad, 0x27, 0xb0, 0x90, 0x64, + 0x07, 0xeb, 0xcd, 0xf8, 0x9f, 0xc6, 0x1a, 0x35, 0xd2, 0xd0, 0x37, 0xa2, + 0xbf, 0x70, 0x8c, 0xd3, 0x19, 0x9b, 0x70, 0x88, 0xef, 0x2c, 0x8b, 0xfd, + 0xe9, 0x8c, 0xd5, 0xce, 0xe4, 0x30, 0xef, 0x20, 0xe0, 0x78, 0xc6, 0xb6, + 0xd7, 0xac, 0x41, 0x4f, 0x0a, 0xfc, 0x8e, 0xb1, 0x00, 0x49, 0x43, 0x49, + 0x4e, 0xaa, 0x91, 0x01, 0x19, 0x95, 0x93, 0xa6, 0xcf, 0x2f, 0x4d, 0x09, + 0xdf, 0xb3, 0x1d, 0x4e, 0x29, 0xea, 0x7d, 0x93, 0x90, 0xb9, 0x4d, 0x29, + 0x77, 0x58, 0xd8, 0x5c, 0x90, 0x20, 0xdd, 0xce, 0x08, 0xe4, 0xff, 0x13, + 0xbb, 0xb2, 0x45, 0x41, 0x5a, 0x09, 0x2c, 0xac, 0x6f, 0x76, 0xa8, 0x24, + 0xb6, 0x81, 0x34, 0x33, 0xb4, 0x4d, 0x6e, 0xaf, 0x3c, 0xff, 0x84, 0x8c, + 0xc9, 0x94, 0xb0, 0x1b, 0x2e, 0xb1, 0x3e, 0x95, 0xf8, 0x39, 0xae, 0x43, + 0xd1, 0x9e, 0x7d, 0x31, 0x5a, 0xc1, 0x65, 0x15, 0xd2, 0xa1, 0xfa, 0x43, + 0x20, 0xe9, 0xd2, 0x8e, 0xc8, 0x40, 0xc0, 0xdd, 0x62, 0x52, 0x8b, 0x59, + 0x8a, 0x84, 0xfa, 0x49, 0x7d, 0xa8, 0x75, 0xe3, 0x42, 0x05, 0x74, 0x96, + 0xd7, 0x4a, 0x6c, 0xc4, 0xe2, 0x5c, 0x3a, 0x11, 0x5c, 0xd2, 0x55, 0x91, + 0xb8, 0x68, 0xb0, 0x2a, 0x84, 0xe9, 0xeb, 0x3f, 0x59, 0x25, 0xcf, 0x64, + 0x65, 0x66, 0x4b, 0x60, 0x24, 0xa2, 0x17, 0xbd, 0x1a, 0xac, 0x6f, 0x05, + 0xb9, 0xdc, 0x7f, 0xa2, 0xd8, 0xa0, 0x49, 0x85, 0x91, 0x49, 0x2b, 0xfd, + 0xd4, 0x29, 0x55, 0x94, 0x8d, 0xc6, 0x7f, 0xe8, 0xca, 0xd7, 0xff, 0xd4, + 0xbf, 0x95, 0x54, 0x75, 0xe3, 0xdf, 0x2a, 0x3a, 0x1d, 0x51, 0xd4, 0x9d, + 0xaf, 0xb5, 0x6f, 0x4d, 0x88, 0x27, 0x22, 0x4a, 0x80, 0xd7, 0x66, 0x6a, + 0x00, 0x4e, 0xb0, 0xd3, 0x07, 0x57, 0xc4, 0xdc, 0x23, 0x17, 0x58, 0xb3, + 0x4d, 0xa0, 0x30, 0x57, 0xd5, 0x97, 0x0e, 0xfc, 0x22, 0x86, 0x72, 0x83, + 0x3f, 0xf5, 0xd9, 0x20, 0x7d, 0xb2, 0xa3, 0xc7, 0x87, 0x20, 0x79, 0x80, + 0x60, 0x2a, 0x1e, 0x3b, 0xf1, 0xb4, 0xf8, 0xdd, 0xf3, 0xe2, 0xa7, 0x20, + 0x9e, 0x3e, 0x42, 0x08, 0x51, 0xde, 0x5d, 0xcb, 0xb8, 0x9b, 0xc1, 0x9f, + 0x4f, 0x81, 0xe7, 0xf4, 0x62, 0x1c, 0xbd, 0xf4, 0x89, 0xc6, 0x8f, 0x3d, + 0xab, 0x1b, 0x3d, 0x59, 0x1d, 0xb7, 0x10, 0xa7, 0x50, 0x71, 0x34, 0x57, + 0x8d, 0x7a, 0x18, 0xb3, 0x9c, 0x5a, 0x68, 0x83, 0x4f, 0x73, 0x72, 0xf1, + 0x9c, 0xaa, 0x91, 0x96, 0xcc, 0x5a, 0x63, 0x5b, 0xb0, 0x86, 0xe6, 0xd6, + 0x30, 0x55, 0xf3, 0x15, 0x57, 0xdd, 0x90, 0xf1, 0xde, 0x44, 0x17, 0xca, + 0x4c, 0x13, 0xa2, 0x09, 0xa6, 0xd8, 0x6e, 0x57, 0x02, 0xa5, 0xde, 0x00, + 0x1a, 0x27, 0xed, 0x67, 0xfd, 0x06, 0x14, 0x3a, 0x29, 0x89, 0x59, 0x71, + 0x78, 0x5e, 0xcb, 0x37, 0xba, 0x52, 0x3d, 0x0c, 0xb6, 0xc6, 0xdb, 0xd8, + 0x0d, 0x38, 0x81, 0xf0, 0xa4, 0xc8, 0x99, 0xab, 0x2f, 0xa0, 0x8f, 0x71, + 0x20, 0xcf, 0xaf, 0x15, 0x2d, 0x34, 0x81, 0xae, 0xa3, 0x6a, 0x45, 0x85, + 0xe8, 0xfc, 0x7c, 0x07, 0x88, 0x78, 0xe5, 0xf1, 0x6d, 0x0a, 0xdd, 0x16, + 0x18, 0xe8, 0x3f, 0x0d, 0x5d, 0x48, 0x5a, 0x60, 0xf0, 0xda, 0xb7, 0x05, + 0xda, 0x03, 0xf8, 0x81, 0x0a, 0xaf, 0x00, 0xf9, 0x33, 0x11, 0xdf, 0xfe, + 0x69, 0xf8, 0x5b, 0x1c, 0x6f, 0xae, 0x6e, 0xb3, 0xab, 0xc4, 0x02, 0x75, + 0x23, 0x98, 0x72, 0x4e, 0x11, 0xb9, 0xe3, 0x0e, 0xb9, 0x0b, 0x4b, 0xa8, + 0x24, 0xb3, 0xb3, 0x9f, 0xe6, 0x5f, 0x48, 0x2b, 0x33, 0xdc, 0x37, 0x2a, + 0xfd, 0x08, 0xc9, 0x32, 0x3f, 0x3f, 0xc0, 0x30, 0x04, 0x61, 0x97, 0x95, + 0xe4, 0x8d, 0xf8, 0x83, 0xc1, 0xfc, 0x90, 0xca, 0xaf, 0xef, 0x54, 0xb6, + 0x05, 0x4c, 0x31, 0x31, 0x1b, 0x9e, 0x62, 0xca, 0x05, 0x4f, 0x1f, 0x2b, + 0xc7, 0x80, 0x60, 0xda, 0x7f, 0xfa, 0x17, 0x4b, 0x3f, 0xf2, 0x27, 0x1e, + 0x04, 0x3a, 0x0f, 0x70, 0x17, 0x28, 0x49, 0x72, 0xc9, 0x2d, 0xc9, 0x10, + 0xde, 0xa0, 0x9e, 0x00, 0x17, 0x14, 0xda, 0xf6, 0x9c, 0xc1, 0xe6, 0xe7, + 0xd9, 0x9c, 0xe1, 0xce, 0xcf, 0x8f, 0x15, 0x57, 0x83, 0xfc, 0xf5, 0x9d, + 0x3a, 0xbb, 0x80, 0x09, 0xb6, 0xaa, 0xd9, 0x05, 0x9a, 0x41, 0x6d, 0xca, + 0x52, 0xfa, 0x67, 0xd4, 0x02, 0x7f, 0x9d, 0x46, 0xf4, 0x50, 0xcf, 0x53, + 0xcf, 0x6c, 0xe3, 0x31, 0x84, 0xe6, 0x4c, 0xf5, 0x4e, 0x8a, 0x12, 0xce, + 0x6c, 0xfb, 0x44, 0x34, 0xfb, 0x3b, 0xbe, 0x6b, 0xdb, 0x57, 0xae, 0xb7, + 0x31, 0x63, 0x3c, 0x3d, 0x59, 0x88, 0x1f, 0x22, 0x88, 0xe9, 0x14, 0xcd, + 0x2c, 0x04, 0x8a, 0x25, 0x71, 0x21, 0x54, 0xfc, 0xf0, 0x1e, 0x58, 0x9c, + 0xa3, 0xd9, 0x06, 0xd5, 0x81, 0xb3, 0x30, 0x7a, 0x57, 0x32, 0xb5, 0x20, + 0x79, 0xbb, 0x2f, 0x91, 0xa6, 0x2b, 0xce, 0x33, 0x4e, 0x78, 0xab, 0x25, + 0xcb, 0x44, 0xa8, 0x27, 0x8b, 0x93, 0xae, 0x78, 0x1c, 0x0f, 0xb5, 0x0c, + 0xbb, 0x24, 0x63, 0xa9, 0x1e, 0x8f, 0x31, 0xd7, 0x33, 0x02, 0x31, 0xa4, + 0x38, 0x1b, 0x61, 0x23, 0x91, 0xe4, 0xbf, 0x32, 0xe8, 0x4d, 0x7a, 0xae, + 0x09, 0x63, 0x77, 0x55, 0xbd, 0x83, 0x33, 0x4f, 0x5c, 0x53, 0x14, 0xba, + 0xd1, 0x25, 0x10, 0xed, 0x1d, 0xbb, 0xc6, 0x63, 0x99, 0x49, 0xd3, 0x26, + 0x90, 0x8e, 0x4d, 0x2b, 0xf6, 0x36, 0x30, 0x9e, 0x78, 0xc1, 0xdf, 0x67, + 0xda, 0x33, 0x1d, 0xdd, 0x2c, 0xda, 0x8d, 0xb9, 0xda, 0x9f, 0x66, 0xd6, + 0x17, 0xe2, 0x08, 0xe8, 0xef, 0xb3, 0xeb, 0x3e, 0xb7, 0x6b, 0x3c, 0x2a, + 0x92, 0x0a, 0x84, 0xd3, 0x1a, 0xc1, 0xdf, 0xe6, 0xff, 0x25, 0xd1, 0x54, + 0x05, 0x45, 0xef, 0x74, 0x24, 0x0b, 0xe4, 0x13, 0xa8, 0xf9, 0xd6, 0xe3, + 0xec, 0xd8, 0x53, 0xdc, 0x91, 0xf8, 0xa7, 0xc6, 0x9e, 0xd9, 0x6d, 0xd2, + 0x02, 0x26, 0x58, 0xaa, 0x66, 0xbd, 0x1b, 0x7b, 0x8a, 0x7b, 0x47, 0x7f, + 0x79, 0xec, 0x51, 0x39, 0x51, 0x13, 0xe7, 0xc6, 0x59, 0xe4, 0xdd, 0xb8, + 0x2f, 0xec, 0xea, 0x6e, 0xb7, 0x1b, 0x50, 0x76, 0x8b, 0x75, 0xbf, 0xd6, + 0x49, 0xde, 0xf7, 0x79, 0xdd, 0x0f, 0x5e, 0xdc, 0x75, 0x06, 0x6e, 0x18, + 0x50, 0x37, 0x64, 0x39, 0x12, 0x94, 0xc4, 0xf3, 0xaf, 0xd9, 0x3f, 0x7e, + 0x24, 0x2f, 0xeb, 0x76, 0xc9, 0x4a, 0x5f, 0xef, 0xd7, 0x82, 0xcc, 0xcb, + 0x7a, 0x50, 0xea, 0x40, 0x16, 0xf8, 0x57, 0x11, 0x9f, 0x8b, 0xb9, 0xde, + 0x1f, 0x8b, 0xb9, 0x5c, 0xac, 0x2e, 0x76, 0x41, 0xb1, 0xb1, 0x65, 0xe0, + 0x69, 0xba, 0xdc, 0x88, 0xdb, 0x88, 0xef, 0x24, 0xaa, 0x51, 0x0f, 0x35, + 0x2a, 0xc5, 0xf0, 0x14, 0x79, 0x83, 0x4c, 0x9e, 0xfe, 0x63, 0x5d, 0x8a, + 0x89, 0x3a, 0x03, 0xba, 0xa7, 0x3e, 0x5a, 0x83, 0x9e, 0x9c, 0xcf, 0x6c, + 0x21, 0x31, 0x94, 0xd6, 0xac, 0x75, 0x65, 0xe2, 0xac, 0xd5, 0x01, 0x84, + 0xd4, 0xb5, 0x8a, 0xb5, 0x3e, 0x7d, 0x22, 0xa2, 0xb9, 0x85, 0x0a, 0x46, + 0x18, 0xa3, 0x60, 0x00, 0x98, 0x83, 0x2b, 0x48, 0x9e, 0xfa, 0x2a, 0x26, + 0x0a, 0x96, 0x02, 0xfe, 0x61, 0x6e, 0x3a, 0xd5, 0xb1, 0x23, 0x06, 0x54, + 0x45, 0x8d, 0x08, 0x85, 0x1f, 0x13, 0x41, 0xcc, 0x3d, 0xf3, 0x5d, 0x8f, + 0xf4, 0x88, 0x68, 0x10, 0x68, 0x98, 0xc8, 0x20, 0x34, 0x7e, 0x5a, 0xa6, + 0xb9, 0x29, 0xb7, 0xbb, 0x7f, 0x3d, 0xb7, 0x27, 0x0f, 0x8f, 0xb8, 0x13, + 0x6b, 0xbb, 0x90, 0x0f, 0x24, 0x15, 0xb6, 0x33, 0x3f, 0x0f, 0xff, 0x2b, + 0x24, 0xed, 0xb7, 0xcb, 0xf1, 0x93, 0xac, 0xfe, 0xec, 0x59, 0x12, 0xbf, + 0xb8, 0x99, 0x8a, 0xd7, 0xcc, 0xb7, 0x3d, 0xa6, 0x8e, 0x88, 0x66, 0xf5, + 0xc4, 0x49, 0xec, 0x6d, 0xe7, 0xe7, 0xfb, 0xb1, 0x24, 0x8a, 0x23, 0x43, + 0x32, 0x73, 0x83, 0x64, 0x82, 0x67, 0x43, 0x21, 0x13, 0xfe, 0x1a, 0x61, + 0xfc, 0xd9, 0xe2, 0xac, 0xe5, 0xc5, 0xe9, 0x62, 0x56, 0x85, 0x29, 0x48, + 0x44, 0x7c, 0xfe, 0x26, 0x45, 0xe6, 0x7a, 0x0d, 0x9e, 0x9c, 0xf2, 0x8b, + 0x15, 0x4e, 0xf1, 0xc5, 0x0a, 0x27, 0x77, 0xb1, 0x62, 0x76, 0x80, 0xe2, + 0x29, 0x50, 0xdc, 0x51, 0x11, 0x47, 0x92, 0xd1, 0x04, 0xce, 0xdf, 0x26, + 0x38, 0x6b, 0xf9, 0xfa, 0x08, 0x7b, 0x0c, 0x3a, 0x23, 0x3d, 0x74, 0x0f, + 0xf3, 0xf3, 0x34, 0x67, 0xfc, 0xe0, 0xc1, 0xe7, 0x52, 0x16, 0xf0, 0x5b, + 0x3f, 0x7a, 0xe0, 0xfa, 0x4c, 0xc9, 0x0f, 0xa6, 0xf7, 0x58, 0x50, 0x89, + 0x10, 0x1c, 0xfc, 0x6e, 0x50, 0x7e, 0x24, 0x01, 0xc5, 0x75, 0x87, 0xd8, + 0x74, 0x1b, 0x22, 0x07, 0xf1, 0xf1, 0xce, 0x07, 0x0e, 0xaa, 0x0d, 0x31, + 0x77, 0xea, 0x23, 0x0e, 0xc6, 0xc5, 0xac, 0xf3, 0x76, 0xea, 0x39, 0xfb, + 0xb0, 0x34, 0xa7, 0x44, 0x8e, 0x9a, 0x5c, 0xc4, 0x71, 0x01, 0x33, 0xe1, + 0xf0, 0xa0, 0x1e, 0xa8, 0x36, 0xc9, 0x9a, 0xdb, 0x24, 0xa5, 0x92, 0x60, + 0x90, 0x09, 0x59, 0x37, 0xfb, 0x4a, 0xbe, 0x35, 0xcd, 0xe8, 0x04, 0xd5, + 0x30, 0x0c, 0x5b, 0xd0, 0x81, 0x4e, 0xcf, 0xe6, 0x68, 0xc3, 0x0f, 0x5e, + 0x5f, 0x53, 0x1b, 0xdd, 0x64, 0x08, 0x7b, 0xf5, 0x79, 0x9d, 0x7d, 0x7d, + 0x85, 0x28, 0x91, 0xb9, 0xe5, 0xe2, 0x4c, 0xdc, 0x72, 0x49, 0x2d, 0xeb, + 0x67, 0x53, 0x8b, 0x37, 0xae, 0x2b, 0xbc, 0x91, 0x8d, 0x80, 0x11, 0xa1, + 0x97, 0x4c, 0x3c, 0x3d, 0x18, 0xe9, 0x6b, 0xc6, 0xa5, 0x13, 0x85, 0xbb, + 0x98, 0x3f, 0xac, 0x93, 0x6f, 0x5f, 0xf6, 0x91, 0x60, 0x95, 0x13, 0x57, + 0x0e, 0x93, 0xc7, 0x49, 0x20, 0x8a, 0xfc, 0x95, 0x1f, 0x48, 0x16, 0x72, + 0xef, 0x73, 0x06, 0x68, 0x60, 0x74, 0x25, 0xe9, 0x8c, 0xb0, 0xfe, 0xe4, + 0x0d, 0x21, 0x2d, 0xfb, 0x0d, 0x0f, 0xc5, 0x33, 0xfe, 0xd8, 0x51, 0x45, + 0x0a, 0xf9, 0xc6, 0xf2, 0xe6, 0xc4, 0x5d, 0xa9, 0x0c, 0x17, 0xfa, 0x42, + 0x91, 0xb3, 0x04, 0x57, 0x35, 0x19, 0x83, 0x0a, 0x76, 0x8f, 0xa3, 0x86, + 0xbe, 0xae, 0xeb, 0xe0, 0xf1, 0x46, 0x03, 0x1b, 0xc9, 0x2e, 0x68, 0xff, + 0xc6, 0x9f, 0xe2, 0x26, 0xb0, 0xab, 0x44, 0x03, 0x1a, 0x02, 0x4f, 0xbf, + 0x66, 0x6f, 0x8d, 0xe0, 0xbf, 0x0a, 0xdb, 0x30, 0x2d, 0xdf, 0x80, 0x2d, + 0xf8, 0xd5, 0x91, 0x78, 0x56, 0xd1, 0x05, 0x12, 0x33, 0x6b, 0xce, 0x9e, + 0xeb, 0x29, 0x2a, 0xda, 0x30, 0xd0, 0xa9, 0xd1, 0xcc, 0xac, 0x71, 0x21, + 0x2d, 0x3c, 0x67, 0xcb, 0x10, 0x83, 0x2d, 0x71, 0x78, 0x64, 0x09, 0x5d, + 0xac, 0xe0, 0xb4, 0x90, 0xf1, 0x73, 0x42, 0xf6, 0x33, 0x44, 0x93, 0x84, + 0x68, 0x16, 0x9b, 0x31, 0x2e, 0xda, 0x75, 0xfd, 0xc1, 0x0e, 0x61, 0x24, + 0xa9, 0x18, 0x95, 0xe8, 0xa2, 0x00, 0x4b, 0x2d, 0x08, 0x82, 0xbf, 0xdf, + 0xe1, 0xdd, 0xed, 0x3c, 0xab, 0xce, 0xae, 0x39, 0xab, 0xb0, 0xb9, 0x2f, + 0x4f, 0x32, 0x28, 0x43, 0x77, 0x0f, 0xb9, 0x33, 0x41, 0x35, 0xcf, 0x1b, + 0xff, 0x17, 0x22, 0x4c, 0xa8, 0x51, 0x33, 0x4d, 0xff, 0x84, 0x16, 0x36, + 0x41, 0xcb, 0x14, 0xe6, 0x3b, 0xad, 0xa3, 0xd6, 0x55, 0x6b, 0x16, 0xf2, + 0xe0, 0x5d, 0x23, 0xb3, 0x63, 0xb9, 0xaa, 0x3e, 0xf3, 0x8f, 0x0f, 0xb0, + 0xc9, 0x88, 0x87, 0xc9, 0x95, 0xd9, 0x47, 0x32, 0x13, 0xfe, 0x0e, 0xf8, + 0xc9, 0x8f, 0x0f, 0x3a, 0x36, 0x25, 0xfe, 0x31, 0xb1, 0x9c, 0x33, 0xe2, + 0x50, 0xfb, 0x0f, 0x1d, 0x48, 0xfc, 0xfd, 0x47, 0x42, 0xa6, 0xf5, 0x14, + 0x1f, 0xbe, 0x4c, 0xdd, 0xb5, 0x5f, 0x58, 0x57, 0xd0, 0x31, 0xf3, 0xd3, + 0xa0, 0x2e, 0x5e, 0x1f, 0x93, 0xf0, 0xd8, 0x07, 0x44, 0x41, 0xc4, 0x89, + 0x6e, 0x72, 0x15, 0x0d, 0x72, 0x3a, 0x80, 0xb3, 0x2e, 0xbf, 0x0a, 0x46, + 0x64, 0x9c, 0x44, 0x2e, 0x9c, 0x67, 0x2f, 0xaf, 0xca, 0x9b, 0x62, 0x12, + 0x6a, 0x0a, 0xca, 0x5d, 0x72, 0xbb, 0x79, 0x8d, 0xf9, 0xa9, 0x33, 0xb7, + 0xe2, 0x36, 0xad, 0x82, 0x07, 0x34, 0x33, 0x7b, 0xfb, 0x78, 0xb6, 0xd1, + 0x48, 0x3b, 0xd1, 0x14, 0xd3, 0x5b, 0x98, 0x8e, 0xe7, 0xa3, 0x8d, 0xbc, + 0x28, 0x15, 0xcc, 0x53, 0xa2, 0x13, 0xa2, 0xc4, 0xd5, 0x4d, 0xde, 0x18, + 0x88, 0xaf, 0xbb, 0x19, 0xd9, 0xeb, 0x6e, 0x6a, 0x8e, 0xe4, 0x8b, 0xe4, + 0xea, 0x27, 0x9e, 0x80, 0x0b, 0x32, 0x85, 0xe1, 0x27, 0x9e, 0x7c, 0xe6, + 0xfc, 0x2c, 0x6b, 0xca, 0x35, 0xc3, 0x28, 0xb8, 0xef, 0x94, 0x01, 0xf5, + 0x92, 0xde, 0x84, 0xfd, 0xfa, 0x4d, 0x73, 0x0c, 0xda, 0x2c, 0xd7, 0x30, + 0xb9, 0x99, 0x5c, 0x33, 0x3f, 0x3f, 0xf3, 0xf2, 0x1a, 0x84, 0x0e, 0x95, + 0xe9, 0x5e, 0x18, 0xf4, 0xb1, 0x1a, 0x73, 0x8a, 0x42, 0x0b, 0x1e, 0xbd, + 0x3b, 0x1b, 0xd1, 0x24, 0x04, 0xd7, 0x48, 0x17, 0x70, 0x5b, 0x8b, 0xb8, + 0x96, 0xde, 0x4b, 0x30, 0xf2, 0x59, 0xcc, 0x84, 0xeb, 0x10, 0x73, 0x22, + 0x72, 0xff, 0x46, 0x37, 0x29, 0xee, 0x6c, 0x15, 0x44, 0x90, 0xb3, 0xd3, + 0xcb, 0xb7, 0xfc, 0x62, 0x4a, 0xad, 0xb8, 0x47, 0x90, 0x53, 0x91, 0xff, + 0xee, 0xe4, 0x6a, 0x7b, 0xda, 0x86, 0x81, 0xf0, 0x5f, 0x81, 0x68, 0xaa, + 0x92, 0x11, 0x1a, 0x98, 0xf6, 0x61, 0x6b, 0x97, 0x22, 0x40, 0x93, 0x26, + 0x6d, 0x68, 0x93, 0xca, 0x3e, 0x21, 0x84, 0x92, 0x34, 0xd0, 0x40, 0x1a, + 0xa3, 0x26, 0x8c, 0x55, 0x55, 0xff, 0xfb, 0x9e, 0xbb, 0x73, 0x52, 0xc7, + 0x4d, 0x61, 0xf4, 0x4b, 0x9b, 0xc6, 0x8e, 0x63, 0x9f, 0xcf, 0xf7, 0xfa, + 0x5c, 0xad, 0xb5, 0x72, 0x8f, 0x8d, 0x9d, 0x7d, 0x11, 0x13, 0xb7, 0xcb, + 0x52, 0xd3, 0xdd, 0xa4, 0xe8, 0x0b, 0xf2, 0x9f, 0xe0, 0x19, 0x32, 0x37, + 0x5e, 0xae, 0x0d, 0x5d, 0xf8, 0xca, 0xa2, 0x49, 0xdc, 0x28, 0xb1, 0xbd, + 0xbb, 0x74, 0xa9, 0x03, 0x89, 0x94, 0x15, 0x90, 0x45, 0xff, 0x9f, 0x76, + 0x2c, 0xea, 0xb4, 0x63, 0x61, 0xa6, 0x4a, 0x9b, 0xa1, 0x86, 0x31, 0x5e, + 0xff, 0x30, 0x5c, 0xa7, 0x4e, 0xd1, 0xc2, 0x89, 0x53, 0xb3, 0x81, 0xd2, + 0xa7, 0xb8, 0x7f, 0x1f, 0xfd, 0x89, 0x04, 0x7b, 0xdf, 0x6a, 0x4d, 0xcc, + 0x04, 0x2a, 0xba, 0x25, 0x37, 0x74, 0xd5, 0xea, 0xd1, 0x24, 0x54, 0xcb, + 0xf5, 0xe5, 0xe3, 0xf4, 0xb1, 0x2b, 0x51, 0xdb, 0xe4, 0x67, 0x39, 0xf7, + 0x8a, 0x0d, 0x68, 0x34, 0xcf, 0x4a, 0xd6, 0x44, 0xb0, 0x15, 0xca, 0x7a, + 0x61, 0x7b, 0xb9, 0x0a, 0xa5, 0x4f, 0xd3, 0xae, 0x6d, 0xd5, 0x90, 0xbd, + 0x40, 0x17, 0x06, 0xb9, 0xbb, 0x36, 0x60, 0x71, 0x53, 0xe1, 0xa6, 0x12, + 0xfc, 0x1f, 0xa4, 0x70, 0x6a, 0xf6, 0x8f, 0xc8, 0x65, 0x94, 0x26, 0xf0, + 0x4b, 0xe1, 0xeb, 0xf9, 0xeb, 0x91, 0xea, 0x16, 0xa1, 0x96, 0x57, 0x43, + 0x01, 0xc9, 0xdc, 0xf1, 0xcb, 0x90, 0x12, 0xf8, 0xb4, 0xd9, 0x14, 0x4f, + 0x36, 0x90, 0x22, 0x6d, 0xae, 0xad, 0xed, 0xd1, 0x5c, 0xdb, 0xa3, 0xbd, + 0x5e, 0xdb, 0x20, 0xcd, 0xc1, 0x11, 0x50, 0xa2, 0xa6, 0x90, 0x74, 0x6d, + 0xa9, 0x41, 0x8b, 0xb1, 0x6d, 0x42, 0x03, 0x6b, 0xb1, 0xf1, 0xc2, 0xd7, + 0x91, 0x2f, 0x3b, 0xa9, 0xce, 0xcd, 0xc7, 0xb4, 0x72, 0xdc, 0x55, 0xe5, + 0x6a, 0x90, 0x32, 0x11, 0x28, 0x6c, 0x08, 0x74, 0xe2, 0x96, 0x38, 0x33, + 0x82, 0xc7, 0x74, 0x73, 0xcb, 0xb0, 0xd6, 0xf6, 0xc2, 0x98, 0x2b, 0x0f, + 0xc5, 0x8f, 0xdd, 0x8a, 0xea, 0x85, 0x60, 0x25, 0xc2, 0xda, 0x5a, 0xc9, + 0x42, 0x22, 0xd8, 0xa6, 0xf4, 0x5a, 0x96, 0xae, 0x05, 0x8c, 0x9b, 0x77, + 0x88, 0x0f, 0xaf, 0x96, 0x1b, 0x09, 0x85, 0xa2, 0x0d, 0xd0, 0x03, 0x9b, + 0xc7, 0xb9, 0x69, 0x18, 0x0b, 0x0f, 0xeb, 0x33, 0x27, 0xa1, 0x8a, 0x92, + 0x68, 0x35, 0x4e, 0xcb, 0x92, 0x17, 0x41, 0x0b, 0xbe, 0x80, 0x4b, 0xe4, + 0x3a, 0xe0, 0xa9, 0x60, 0x86, 0x2b, 0x62, 0x46, 0x5a, 0x2e, 0x1a, 0x2e, + 0xa7, 0xe9, 0x4c, 0xb7, 0x54, 0x74, 0x89, 0x26, 0x45, 0x4d, 0xef, 0x98, + 0xfa, 0x63, 0xce, 0xc7, 0x60, 0xca, 0xe1, 0x71, 0x70, 0xe4, 0x6f, 0x0e, + 0xfb, 0xbb, 0x4c, 0xc7, 0xea, 0xb6, 0xba, 0x8c, 0x62, 0x10, 0xe5, 0xc8, + 0xeb, 0xe8, 0x81, 0x26, 0x8e, 0x33, 0x7d, 0xd0, 0xef, 0xfb, 0x96, 0xdd, + 0x4d, 0x73, 0x0a, 0xff, 0x9d, 0x26, 0x54, 0x6d, 0xf4, 0x03, 0x5b, 0xa4, + 0x9f, 0x44, 0xe3, 0x18, 0xcc, 0xfb, 0x6b, 0x9e, 0x15, 0xd5, 0x05, 0x83, + 0x81, 0x98, 0xdd, 0xb0, 0x29, 0x6a, 0x36, 0x8b, 0x8a, 0x49, 0x49, 0x91, + 0xad, 0x73, 0xb9, 0x76, 0x97, 0xe4, 0x76, 0x0e, 0x18, 0xa3, 0xe9, 0xf8, + 0x31, 0x0e, 0xeb, 0xf7, 0x74, 0x31, 0x58, 0x3e, 0x67, 0xc5, 0xc0, 0x39, + 0xaf, 0xe6, 0xf9, 0xe1, 0xd8, 0xf1, 0x67, 0x51, 0x82, 0x1f, 0xd2, 0x1f, + 0xbf, 0x57, 0x3e, 0x49, 0xb1, 0x41, 0x3b, 0x96, 0x51, 0x03, 0xc7, 0x28, + 0x24, 0xf6, 0xb3, 0xc8, 0x17, 0x83, 0xfd, 0xe3, 0xd5, 0x6b, 0xaf, 0xc4, + 0x1c, 0xf1, 0xb2, 0x58, 0x81, 0x19, 0x1a, 0xc0, 0x58, 0xe7, 0x1c, 0x4e, + 0xf3, 0xea, 0x70, 0x6a, 0xcd, 0x43, 0xee, 0x75, 0xcf, 0xc5, 0x46, 0xa0, + 0x09, 0x51, 0x98, 0x96, 0xe0, 0x06, 0xd7, 0x91, 0x10, 0x90, 0xe3, 0xb7, + 0x1e, 0xeb, 0xe6, 0xcf, 0x23, 0x8f, 0x9f, 0xd6, 0x86, 0x54, 0x4b, 0xe9, + 0xbd, 0xc9, 0x88, 0xdb, 0x0e, 0x6a, 0x67, 0x31, 0x18, 0x32, 0x26, 0xdb, + 0x90, 0x6f, 0x6f, 0x67, 0xc2, 0xb9, 0x06, 0x17, 0xd2, 0x4e, 0x98, 0xf6, + 0xf9, 0xf6, 0x37, 0x9b, 0xf6, 0x0a, 0x39, 0xed, 0xd6, 0x19, 0xc3, 0x23, + 0x6f, 0x3c, 0x62, 0xd9, 0x50, 0xe2, 0x22, 0x2d, 0x7d, 0xac, 0x2c, 0xd3, + 0x83, 0x1a, 0xcf, 0x72, 0x15, 0xbb, 0x57, 0xd5, 0xb5, 0xbf, 0x24, 0x43, + 0x72, 0x50, 0x80, 0xc6, 0xa9, 0xd7, 0x9c, 0x4f, 0xcb, 0x12, 0xd1, 0x67, + 0x54, 0x79, 0x2b, 0x78, 0xa6, 0x4c, 0x0b, 0x11, 0x3a, 0xde, 0x81, 0xe3, + 0x40, 0x07, 0xd3, 0x9a, 0x35, 0x2c, 0xd1, 0x5c, 0xf6, 0xda, 0xf8, 0xad, + 0x5e, 0x10, 0x3e, 0x4c, 0x30, 0x93, 0x5d, 0xcc, 0x21, 0x48, 0x71, 0x70, + 0x3d, 0xc3, 0x1d, 0x8f, 0x05, 0xa2, 0x3f, 0xe5, 0x9a, 0xec, 0xb4, 0x4f, + 0x0f, 0xe0, 0x5d, 0xf0, 0x2a, 0x68, 0x74, 0x33, 0xe3, 0x94, 0x69, 0x9b, + 0x0b, 0xa9, 0x60, 0x4f, 0x72, 0x30, 0x9d, 0x8c, 0x0e, 0xbe, 0xa4, 0xe3, + 0x42, 0xb3, 0x33, 0xec, 0x0c, 0x55, 0x9c, 0xa9, 0xc9, 0x82, 0x23, 0x10, + 0x75, 0x9c, 0x76, 0xb9, 0x1a, 0xe2, 0x24, 0x4c, 0xd4, 0x33, 0x47, 0xb7, + 0xa8, 0x57, 0x7f, 0x0a, 0x2d, 0x03, 0x89, 0x0b, 0x1e, 0x49, 0x52, 0x37, + 0xb8, 0x3a, 0xe9, 0x5d, 0x1f, 0xc0, 0xc8, 0x08, 0xf1, 0xe5, 0x85, 0xb8, + 0xe8, 0x5d, 0xbf, 0xf7, 0x82, 0xbb, 0x6c, 0x63, 0x7f, 0x15, 0x28, 0x0e, + 0x55, 0xcd, 0x81, 0x3c, 0xc3, 0x71, 0xd8, 0x37, 0x1c, 0x07, 0x4f, 0x42, + 0x2d, 0x2d, 0x73, 0xa7, 0x56, 0x2b, 0xbe, 0x62, 0xdc, 0x2d, 0xbe, 0xe8, + 0xcf, 0x8d, 0xf0, 0xc5, 0x62, 0xce, 0x1b, 0xb6, 0x0a, 0x10, 0x1c, 0x29, + 0x1d, 0x76, 0x58, 0x5b, 0x86, 0x06, 0xe2, 0xd3, 0xa1, 0x1b, 0x6c, 0x8d, + 0xa5, 0x9b, 0x5a, 0xf2, 0x4b, 0xa0, 0xcb, 0x05, 0x75, 0xd9, 0x20, 0xe3, + 0x1e, 0xc9, 0xd6, 0x2c, 0x07, 0x41, 0x90, 0x4c, 0x8a, 0x7b, 0x52, 0x28, + 0xea, 0x69, 0x72, 0x9b, 0x43, 0xdf, 0x92, 0x4c, 0x09, 0xa2, 0xfb, 0xe8, + 0x6f, 0x90, 0x67, 0x71, 0x49, 0xf8, 0xbc, 0xe0, 0xb8, 0xff, 0xb1, 0xff, + 0xb9, 0x46, 0xea, 0x11, 0xf8, 0x16, 0xac, 0x1c, 0x3e, 0x55, 0xb7, 0x87, + 0x9f, 0x46, 0xf6, 0xd0, 0xa3, 0xf6, 0xe2, 0x4d, 0xaf, 0xc9, 0x72, 0xe2, + 0xce, 0xa3, 0x82, 0x1c, 0x41, 0x76, 0xe0, 0xf4, 0xd0, 0x0d, 0x58, 0xf0, + 0x39, 0x8d, 0xfd, 0xbd, 0x6a, 0xbe, 0x10, 0x0f, 0x2f, 0x89, 0xf2, 0xbd, + 0x44, 0x3d, 0x2e, 0x1c, 0x5d, 0x37, 0xc0, 0x2f, 0xda, 0x9e, 0x92, 0x10, + 0xeb, 0xcc, 0x1b, 0xca, 0x05, 0x83, 0x4d, 0x1c, 0x13, 0x6b, 0xe8, 0xf8, + 0xba, 0x25, 0x2a, 0x17, 0x45, 0x42, 0x05, 0x17, 0xcd, 0x50, 0x44, 0xdd, + 0x76, 0x92, 0x8f, 0x7b, 0x9a, 0x14, 0xa4, 0x94, 0xcb, 0x5e, 0x1d, 0x32, + 0x32, 0xd8, 0x69, 0xc4, 0xee, 0x30, 0xbc, 0x7f, 0xd9, 0xa1, 0x91, 0xb8, + 0xb5, 0xf5, 0x4d, 0xda, 0x1d, 0xeb, 0x96, 0x6c, 0xbb, 0x75, 0x53, 0x9b, + 0x26, 0xda, 0xad, 0x36, 0xff, 0x41, 0xc7, 0xea, 0xa8, 0xbd, 0x9d, 0x75, + 0x55, 0x9d, 0x51, 0xb5, 0xd9, 0xd4, 0xc3, 0xc5, 0x73, 0xe9, 0x2f, 0x45, + 0x64, 0x66, 0xd5, 0xeb, 0x48, 0x4b, 0x22, 0xc6, 0x56, 0xf2, 0xb8, 0xf2, + 0x99, 0x31, 0x44, 0x87, 0xc6, 0x6b, 0x83, 0x76, 0xba, 0x27, 0x24, 0xbd, + 0x47, 0xff, 0x00, 0xe9, 0xad, 0x01, 0x3e, 0x0c, 0x4c, 0x00, 0x00 }; -unsigned int index_htm_gz_len = 6012; +unsigned int index_htm_gz_len = 6251; diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh index d768f5ec54..541154a788 100755 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -52,5 +52,12 @@ then exit -1 fi +mv index.htm.gz ../data/edit +if [ $? -ne 0 ] +then + echo "Error moving gzipped version back to data/edit" + exit -1 +fi + echo Reduce complete. From bc092076ae769cf0afb2003983892ab4ae8c8474 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 12 Apr 2020 15:24:24 +0200 Subject: [PATCH 086/111] Return error 500 when upload fails immediately (e.g. filesystem full) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 89607a1c2e..5447bf5eb6 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -513,6 +513,9 @@ void handleFileUpload() { } DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); + if (!uploadFile) { + return returnFail("CREATE FAILED"); + } DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { From 941a7ec71e2b7118ad6d8b78f730850eeb527bca Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 12 Apr 2020 15:26:35 +0200 Subject: [PATCH 087/111] Use standard tag for filesystem use --- .../examples/FSBrowser/data/edit/index.htm | 93 ++++++++----------- 1 file changed, 38 insertions(+), 55 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm index a9bb8e6a21..532834c5ab 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm @@ -131,61 +131,19 @@ } } - #fsTotal { - display: inline-block; - width:100px; - height:10px; - border:1px solid #000; - overflow:hidden; - background: #E6E6E6; - } - #fsUsed { - display: inline-block; - height:100%; - border-right: 1px solid #26A0DA; - background: #26A0DA; - } - #warning { - height:100%; - background-color:orange; - color:black; - } - .tooltip { - position: absolute; - z-index: 2; /* must be above editor which is at 1 */ - right:0px; - top:20px; - visibility: hidden; - background-color: white; - color: black; - text-align: center; - border: 1px solid #000; - padding: 3px; - font-size: 10px; - } - - #fsTotal:hover .tooltip { - visibility: visible; - } - - #warning:hover .tooltip { - visibility: visible; - } - #header { position: absolute; top: 0; right: 0; left: 0; - height:28px; - line-height: 24px; - padding-left: 10px; + height:24px; + padding: 1px 1px 0px 10px; background-color: #444; color:#EEE; } #tree { position: absolute; - top: 28px; + top: 25px; bottom: 0; left: 0; width: 20%; @@ -193,7 +151,7 @@ } #editor, #preview { position: absolute; - top: 28px; + top: 25px; right: 0; bottom: 0; left: 20%; @@ -202,14 +160,41 @@ background-color: #EEE; padding:5px; } + #status { position: absolute; - top: 4px; + top: 3px; right: 10px; - height: 28px; - font-size: 14px; + font-size: 15px; } + #fsMeter { + width:100px; + padding-bottom:2px; + } + + #warning { + height:100%; + background-color:orange; + color:black; + } + .tooltip { + position: absolute; + z-index: 2; /* must be above editor which is at 1 */ + right:0px; + top:20px; + visibility: hidden; + background-color: white; + color: black; + text-align: center; + border: 1px solid #000; + padding: 3px; + font-size: 10px; + } + #warning:hover .tooltip { + visibility: visible; + } + #loading { position:absolute; // note: changing between block and none cancels the opacity anim, so we move it in and out of screen instead by changing the top value @@ -299,14 +284,12 @@ fsInfo = JSON.parse(xmlHttp.responseText); var status = fsInfo.type + " - "; if (fsInfo.isOk) { - status += "" - + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + " free of " + readableSize(fsInfo.totalBytes) - + ""; + var percent = (1+Math.round(99*fsInfo.usedBytes/fsInfo.totalBytes)); // fake to see the "used" bar in any case + var text = readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + " free of " + readableSize(fsInfo.totalBytes); + status += "" + text + "" if (fsInfo.unsupportedFiles) { status += " WARNING" - + "Filesystem\xa0contains\xa0unsupported\xa0filenames:
" + + "Filesystem contains unsupported filenames:
" + fsInfo.unsupportedFiles + "
"; } From 3fc2416b4b0532230d6170dbd767f44cea58f0ad Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 12 Apr 2020 15:30:51 +0200 Subject: [PATCH 088/111] (updated following changes to index.htm) --- .../examples/FSBrowser/data/edit/index.htm.gz | Bin 6251 -> 6261 bytes .../examples/FSBrowser/extras/index_htm.h | 1045 +++++++++-------- 2 files changed, 523 insertions(+), 522 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz index 193bf7bedebf1d9afc34dced1d835f354bca1dd4..de5abd1062cb121766b15497d6552c491bcea021 100644 GIT binary patch literal 6261 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 VYlC*WCnUW`{{ZQ&0X_^&007zcIKuz{ diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h index a190a69276..ef53bb4652 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h @@ -1,524 +1,525 @@ unsigned char index_htm_gz[] = { - 0x1f, 0x8b, 0x08, 0x08, 0x4d, 0xcf, 0x92, 0x5e, 0x00, 0x03, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xdc, 0x3b, 0x89, 0x76, - 0xdb, 0xb6, 0x96, 0xbf, 0x42, 0x33, 0x53, 0x9b, 0x1c, 0x51, 0xd4, 0x12, - 0x6f, 0x91, 0x4c, 0xbb, 0x5e, 0xe4, 0x25, 0xde, 0x77, 0x3b, 0x99, 0xcc, - 0x39, 0x90, 0x08, 0x49, 0xb4, 0x29, 0x92, 0x21, 0x41, 0x4b, 0xb2, 0xe3, - 0xf9, 0x96, 0x7e, 0x4b, 0xbf, 0x6c, 0xee, 0x05, 0xb8, 0x4a, 0x94, 0xdd, - 0xcc, 0xb4, 0xaf, 0x9d, 0x69, 0x7a, 0x22, 0x12, 0x04, 0x2e, 0xee, 0xbe, - 0x01, 0x59, 0x9b, 0xdb, 0x39, 0xdd, 0xbe, 0xba, 0x3f, 0x6b, 0x49, 0x7d, - 0x36, 0xb0, 0xa5, 0xb3, 0xeb, 0xad, 0xa3, 0x83, 0x6d, 0x49, 0x2e, 0x57, - 0x2a, 0xb7, 0x1f, 0xb7, 0x2b, 0x95, 0x9d, 0xab, 0x1d, 0xe9, 0x6e, 0xff, - 0xea, 0xf8, 0x48, 0xaa, 0xe9, 0x55, 0xe9, 0xca, 0x27, 0x4e, 0x60, 0x31, - 0xcb, 0x75, 0x88, 0x5d, 0xa9, 0xb4, 0x4e, 0x64, 0x49, 0xee, 0x33, 0xe6, - 0x35, 0x2a, 0x95, 0xe1, 0x70, 0xa8, 0x0f, 0x3f, 0xea, 0xae, 0xdf, 0xab, - 0x5c, 0x5d, 0x54, 0x46, 0x08, 0xab, 0x86, 0x8b, 0xa3, 0xc7, 0x32, 0xcb, - 0xac, 0xd4, 0x4d, 0x66, 0xca, 0xeb, 0x6b, 0x7c, 0x3f, 0x9b, 0x38, 0x3d, - 0x83, 0x3a, 0xeb, 0x6b, 0xcc, 0x62, 0x36, 0x5d, 0xdf, 0xb5, 0x6c, 0x2a, - 0x0d, 0x88, 0x43, 0x7a, 0xd4, 0x5f, 0xab, 0x88, 0xb1, 0xb5, 0x80, 0x8d, - 0x71, 0x94, 0x9a, 0x16, 0x31, 0x82, 0x8e, 0x4f, 0x61, 0x7a, 0xdb, 0x35, - 0xc7, 0x2f, 0x5d, 0xd7, 0x61, 0xe5, 0xc0, 0x7a, 0xa6, 0x8d, 0x5a, 0xdd, - 0x1b, 0x35, 0xf9, 0x6b, 0x97, 0x0c, 0x2c, 0x7b, 0xdc, 0xb8, 0xa1, 0xbe, - 0x09, 0x50, 0xb4, 0x4d, 0xdf, 0x22, 0xb6, 0x76, 0x09, 0x7b, 0x97, 0x03, - 0xea, 0x5b, 0xdd, 0x57, 0xbd, 0x03, 0xb3, 0xe8, 0x88, 0x1d, 0x53, 0x27, - 0x7c, 0x79, 0x2e, 0x5b, 0x8e, 0x49, 0x47, 0x8d, 0x8f, 0xd5, 0x6a, 0xd3, - 0x73, 0x05, 0x7a, 0x0d, 0xd2, 0x0e, 0x5c, 0x3b, 0x64, 0xb4, 0x69, 0xd3, - 0x2e, 0x6b, 0x2c, 0x01, 0xe0, 0xb6, 0xeb, 0x9b, 0xd4, 0x6f, 0xd4, 0xbc, - 0x91, 0x04, 0x9f, 0x2c, 0x53, 0xfa, 0xb0, 0xb8, 0xb8, 0xd8, 0x6c, 0x93, - 0xce, 0x63, 0xcf, 0x77, 0x43, 0xc7, 0x2c, 0x77, 0x5c, 0xdb, 0xf5, 0x1b, - 0x1f, 0xba, 0x4b, 0xf8, 0xa7, 0x69, 0x5a, 0x81, 0x67, 0x93, 0x71, 0xc3, - 0x71, 0x1d, 0x0a, 0x6b, 0x47, 0xe5, 0xa0, 0x4f, 0x4c, 0x77, 0xd8, 0xa8, - 0x4a, 0x55, 0xa9, 0x56, 0x05, 0x20, 0x7e, 0xaf, 0x4d, 0x94, 0xaa, 0x86, - 0x7f, 0xf4, 0x45, 0xb5, 0x99, 0x21, 0xa3, 0x16, 0x93, 0x31, 0xa4, 0x56, - 0xaf, 0xcf, 0x1a, 0x2b, 0xd5, 0x6a, 0x0e, 0x63, 0x29, 0xb4, 0x5f, 0x6c, - 0x2b, 0x80, 0xe9, 0xc8, 0x12, 0xb1, 0x03, 0x73, 0xbd, 0x46, 0x55, 0x20, - 0x5b, 0x6d, 0x0e, 0x88, 0xdf, 0xb3, 0x1c, 0x78, 0xf0, 0x88, 0x69, 0x5a, - 0x4e, 0xaf, 0x31, 0xb1, 0xde, 0xb6, 0x5e, 0x12, 0x42, 0x7d, 0x6a, 0x13, - 0x66, 0x3d, 0xd1, 0xe6, 0xc0, 0x72, 0xca, 0x43, 0xcb, 0x64, 0xfd, 0xc6, - 0x32, 0xa0, 0xd7, 0xec, 0x84, 0x7e, 0x00, 0xe4, 0x78, 0xae, 0x05, 0xeb, - 0xfc, 0xfc, 0xfa, 0xc0, 0x23, 0xce, 0x4b, 0x44, 0x2e, 0x32, 0x21, 0xa6, - 0xd5, 0x72, 0x6c, 0xcb, 0xa1, 0xe5, 0xb6, 0xed, 0x76, 0x1e, 0x93, 0xbd, - 0x97, 0xbd, 0xd1, 0xe4, 0xee, 0x8d, 0xbe, 0xfb, 0x44, 0xfd, 0x97, 0x94, - 0x77, 0x1c, 0x4c, 0xf1, 0xac, 0xdc, 0x66, 0x94, 0x52, 0x98, 0x15, 0x04, - 0xa0, 0x48, 0x94, 0x3e, 0x59, 0x74, 0x08, 0xd3, 0xb4, 0xfc, 0x00, 0xf0, - 0x26, 0xa1, 0x3a, 0x65, 0xc4, 0x04, 0xbb, 0x26, 0x80, 0x58, 0x8e, 0x17, - 0xb2, 0x97, 0x69, 0xd9, 0xbb, 0x1e, 0xe9, 0x58, 0x6c, 0xcc, 0xd9, 0x97, - 0x99, 0xff, 0x32, 0x21, 0xab, 0xf2, 0xc0, 0x7d, 0x2e, 0x87, 0xa0, 0x59, - 0xa0, 0x5d, 0x36, 0xed, 0x30, 0x21, 0x11, 0x90, 0x5e, 0xfb, 0xd1, 0x62, - 0xd3, 0x1f, 0x26, 0x07, 0x26, 0x90, 0xc9, 0xd2, 0x5b, 0xad, 0x76, 0xa7, - 0x05, 0x31, 0x39, 0x39, 0xe2, 0x26, 0x72, 0xae, 0x6c, 0xd2, 0x8e, 0xeb, - 0x13, 0x4e, 0x05, 0xb0, 0x95, 0xfa, 0x28, 0x8f, 0x22, 0x62, 0x4b, 0x36, - 0x69, 0x53, 0xbb, 0x04, 0xcc, 0x8a, 0x59, 0x24, 0xe1, 0x9f, 0x7a, 0x9d, - 0x0b, 0x6b, 0x6a, 0xfa, 0x7f, 0xc1, 0xc4, 0xac, 0x42, 0x4f, 0x0a, 0x01, - 0x81, 0x69, 0x05, 0x63, 0x8d, 0x46, 0x9b, 0x76, 0x5d, 0x9f, 0xbe, 0xbc, - 0x49, 0x04, 0xdf, 0xa2, 0x01, 0xf0, 0x49, 0xdb, 0xa6, 0xa6, 0x40, 0x2d, - 0x5e, 0x61, 0xd2, 0x2e, 0x09, 0x6d, 0x96, 0x88, 0x42, 0x5f, 0x2e, 0x5c, - 0xdc, 0xe9, 0xd3, 0xce, 0x23, 0x35, 0x01, 0x39, 0xa6, 0x24, 0x90, 0xd4, - 0x2c, 0xda, 0x5c, 0x29, 0x7f, 0x16, 0x6f, 0xad, 0x40, 0x34, 0x19, 0xad, - 0x0d, 0x7d, 0x5b, 0x31, 0x09, 0x23, 0x0d, 0x6b, 0x00, 0xae, 0xaa, 0xe2, - 0x39, 0x3d, 0xf0, 0x07, 0x01, 0x5d, 0x5e, 0xd4, 0xac, 0x9b, 0xad, 0xd3, - 0x8b, 0x61, 0xf5, 0x70, 0xaf, 0xe7, 0x6e, 0xc2, 0x7f, 0x27, 0x97, 0xd7, - 0xfd, 0xd6, 0x75, 0x0f, 0x9e, 0xb6, 0xf0, 0x75, 0xbb, 0xb7, 0xbd, 0x79, - 0x8c, 0x0f, 0xe3, 0xa5, 0xd1, 0x70, 0x80, 0x0f, 0xed, 0xda, 0xd6, 0xf1, - 0x4d, 0xeb, 0x66, 0xbf, 0xd3, 0x1a, 0xdf, 0x0e, 0x96, 0x96, 0x6e, 0x6f, - 0xeb, 0xad, 0xe5, 0xf3, 0x6b, 0x73, 0xf3, 0xbc, 0xb5, 0x65, 0x91, 0xbd, - 0xda, 0x03, 0xd9, 0x5b, 0xa9, 0x54, 0x2a, 0xab, 0x4f, 0x27, 0x0f, 0x1f, - 0x0f, 0x9f, 0x8f, 0x57, 0xb6, 0x47, 0xc7, 0xed, 0xc1, 0x52, 0xd8, 0x3d, - 0x7e, 0xee, 0x54, 0xee, 0xeb, 0xe6, 0xf7, 0x11, 0x3b, 0x22, 0x7b, 0x96, - 0xbb, 0xb4, 0xda, 0xbb, 0xbf, 0xfd, 0xfc, 0xf0, 0x65, 0xf7, 0xe2, 0x66, - 0xf7, 0xcb, 0xe7, 0xab, 0x56, 0xe5, 0xe8, 0xb9, 0x53, 0x7a, 0x0a, 0xda, - 0x27, 0xe6, 0xdd, 0xcd, 0xca, 0x62, 0xe9, 0xac, 0xff, 0x64, 0xee, 0xdb, - 0x41, 0xfb, 0xb6, 0xfe, 0xe8, 0x2d, 0x7f, 0x5f, 0x79, 0x3a, 0x7a, 0x1e, - 0xaf, 0x3e, 0x1d, 0x87, 0x27, 0x57, 0xcf, 0xdd, 0x8f, 0x9f, 0x4a, 0x7d, - 0x77, 0xf9, 0x7a, 0x7c, 0x7a, 0xbd, 0xbd, 0xdb, 0xbf, 0xbf, 0xbd, 0xb6, - 0x97, 0x9c, 0xa7, 0x95, 0x52, 0xc5, 0x5b, 0xa6, 0x8f, 0xe7, 0x56, 0x65, - 0xef, 0x02, 0x71, 0xdc, 0xbc, 0xbb, 0xb8, 0xbc, 0xb2, 0x8f, 0x37, 0xcf, - 0x4f, 0xdb, 0xf7, 0x5f, 0x90, 0x96, 0xcb, 0x8b, 0xcf, 0x17, 0xad, 0xdd, - 0xeb, 0xcb, 0xe3, 0x2e, 0x7b, 0xfc, 0xc4, 0xc6, 0x43, 0x6b, 0xf3, 0xbc, - 0xef, 0x5e, 0x6f, 0xf6, 0x6f, 0x36, 0x87, 0x9f, 0xbd, 0xef, 0x3b, 0x17, - 0xdf, 0xbb, 0xe4, 0xe9, 0x69, 0xf5, 0xd9, 0x0e, 0x4f, 0x0e, 0x1f, 0x43, - 0xbf, 0xf5, 0xe0, 0xdf, 0x7f, 0x2c, 0xd1, 0x95, 0xc5, 0xcf, 0xd6, 0xf3, - 0x91, 0x53, 0xbf, 0xad, 0x0d, 0xac, 0xcd, 0xc3, 0x91, 0xd7, 0x3f, 0xdd, - 0x3a, 0xa6, 0xd7, 0xf7, 0xdf, 0x97, 0xc3, 0xfd, 0xca, 0xe6, 0xc7, 0xcd, - 0xe5, 0xe5, 0x7b, 0xef, 0x62, 0xeb, 0xe2, 0xfb, 0xe7, 0x2f, 0xe4, 0xf8, - 0x60, 0xd5, 0x1a, 0x06, 0x37, 0x95, 0x6d, 0xf3, 0x78, 0x79, 0xb3, 0x3e, - 0x7a, 0x58, 0x72, 0x0e, 0xae, 0x83, 0xc3, 0x6a, 0xc5, 0xba, 0xba, 0x3e, - 0xf3, 0xf7, 0x4e, 0x06, 0xd5, 0xe3, 0xeb, 0x9d, 0x83, 0x47, 0xba, 0x57, - 0xf9, 0xbc, 0xb4, 0x18, 0x9e, 0x5a, 0x8f, 0x41, 0xfb, 0x53, 0xff, 0xae, - 0xbf, 0xb4, 0x74, 0xd6, 0x3f, 0x38, 0x78, 0xe8, 0x1e, 0xf6, 0xcc, 0xcf, - 0x57, 0xfb, 0x97, 0xad, 0x71, 0xbd, 0xb2, 0xbb, 0x53, 0x5d, 0xbe, 0x1b, - 0xb8, 0xe6, 0xea, 0xc9, 0xe9, 0xd0, 0xf7, 0x87, 0xbb, 0xd7, 0xc1, 0xf9, - 0xe0, 0xee, 0xcb, 0xfe, 0x97, 0x7e, 0x9f, 0x3e, 0xee, 0x6f, 0x59, 0x5b, - 0xe3, 0xfb, 0x03, 0x97, 0x1c, 0x7c, 0xde, 0x7c, 0x38, 0x5b, 0xbd, 0xbe, - 0xbc, 0xb5, 0xb6, 0x37, 0x57, 0x0f, 0xfb, 0xad, 0xdb, 0xd5, 0xeb, 0xbd, - 0xab, 0x95, 0xc3, 0x33, 0xf2, 0xa5, 0x35, 0x0a, 0x4e, 0xdb, 0xfb, 0x63, - 0xff, 0xba, 0x77, 0xf5, 0xf8, 0x70, 0xf5, 0xbc, 0x6a, 0x5b, 0x67, 0x77, - 0xc3, 0xe7, 0x61, 0x6b, 0xab, 0x74, 0x7a, 0xb6, 0x7b, 0x33, 0xda, 0x6f, - 0xad, 0xde, 0xd5, 0x3b, 0x8f, 0x17, 0x5b, 0xe3, 0x23, 0x72, 0x33, 0xee, - 0xdf, 0x1c, 0x8e, 0xce, 0xea, 0x2b, 0x87, 0x27, 0x25, 0x67, 0x93, 0xed, - 0xaf, 0x5c, 0x3c, 0x85, 0x7e, 0x7d, 0xc7, 0x5f, 0xae, 0xd7, 0x18, 0x7d, - 0x3c, 0xa6, 0x41, 0xe9, 0xd6, 0xda, 0x5b, 0x5d, 0xde, 0xf7, 0x97, 0x1f, - 0x0e, 0xef, 0x1f, 0x4a, 0x2b, 0xe7, 0xb5, 0x43, 0xb3, 0x7a, 0xe6, 0x8d, - 0x0e, 0x96, 0x56, 0x4f, 0x82, 0x73, 0xf3, 0xa4, 0x52, 0x5f, 0x7a, 0xb6, - 0xcf, 0x77, 0xce, 0xcd, 0xc3, 0xf6, 0xa7, 0x4d, 0xe7, 0x78, 0xb9, 0xbb, - 0x7f, 0xb9, 0xf7, 0x78, 0x16, 0x9c, 0x93, 0xcf, 0x64, 0x70, 0xe0, 0x9d, - 0x3f, 0x6f, 0xfb, 0xe3, 0x61, 0x7f, 0xa7, 0x66, 0x5d, 0xd5, 0xef, 0x1e, - 0x83, 0xa3, 0xed, 0x61, 0x50, 0x39, 0xf8, 0xf2, 0xb4, 0xfa, 0xc5, 0xed, - 0xae, 0xb0, 0xfa, 0xd2, 0xbd, 0xfd, 0xc8, 0xc5, 0x74, 0x79, 0x7d, 0x73, - 0x7a, 0x71, 0xb8, 0xb4, 0x7d, 0x7f, 0x70, 0x60, 0xa8, 0x92, 0xe3, 0x96, - 0x7d, 0xea, 0x51, 0xc2, 0xfe, 0x04, 0xdd, 0x2f, 0x18, 0x4a, 0x2c, 0xbc, - 0x30, 0x06, 0xf4, 0x45, 0x00, 0xab, 0x41, 0x0c, 0x68, 0xf2, 0xe1, 0xec, - 0x00, 0x38, 0x2d, 0x66, 0x75, 0x88, 0x5d, 0x26, 0xb6, 0xd5, 0x73, 0x1a, - 0x03, 0xcb, 0x34, 0xed, 0x42, 0xcf, 0x92, 0x31, 0xb9, 0x72, 0xe2, 0xa4, - 0x6b, 0xab, 0x10, 0x43, 0xab, 0x45, 0xbe, 0x73, 0xe6, 0xec, 0xf2, 0xe2, - 0xea, 0x94, 0x7b, 0xc3, 0x15, 0x3a, 0x1b, 0xb1, 0x37, 0x56, 0x2d, 0x2f, - 0x16, 0xaf, 0xb2, 0x06, 0xbd, 0x37, 0x56, 0xad, 0x56, 0xa7, 0x56, 0x4d, - 0xb8, 0x44, 0x0c, 0x76, 0x0e, 0x6b, 0xc8, 0x72, 0x53, 0x84, 0x5d, 0xce, - 0x94, 0xc4, 0x1d, 0xa3, 0x2b, 0x46, 0x9f, 0x5c, 0xcc, 0xa6, 0x66, 0xd1, - 0xc6, 0x55, 0xa9, 0xfc, 0x71, 0xda, 0x81, 0xe7, 0xa4, 0xf4, 0x07, 0x37, - 0x7d, 0xcb, 0xc7, 0x96, 0xf2, 0x64, 0xcc, 0x40, 0x04, 0xe1, 0xbe, 0xfe, - 0xca, 0xb3, 0x35, 0x49, 0x64, 0x6b, 0x12, 0x71, 0x4c, 0x49, 0x89, 0xa3, - 0x22, 0x66, 0x1b, 0x26, 0x40, 0xef, 0xd0, 0xb2, 0x67, 0x8d, 0xa8, 0x5d, - 0xe6, 0x51, 0xab, 0x51, 0x55, 0x5f, 0xf2, 0xa1, 0x36, 0x9e, 0x4f, 0x1c, - 0xf0, 0xb1, 0x1c, 0x78, 0x3c, 0x60, 0x3e, 0x90, 0x0e, 0x90, 0x52, 0x86, - 0x40, 0x3a, 0xc0, 0x5f, 0x11, 0x51, 0x5d, 0xbf, 0xdc, 0x0e, 0x7b, 0x5d, - 0x6b, 0x04, 0x48, 0x77, 0x2d, 0xc7, 0x62, 0x54, 0xaa, 0x05, 0xaf, 0xbf, - 0xc6, 0x60, 0x1e, 0xe9, 0xb8, 0xeb, 0x93, 0x01, 0x0d, 0xa4, 0x3f, 0x08, - 0xe6, 0xa5, 0xeb, 0xbb, 0x83, 0x34, 0xa3, 0x78, 0x65, 0x6e, 0xe6, 0xe5, - 0xf5, 0xf5, 0x43, 0x37, 0xb8, 0x72, 0x19, 0xb1, 0x8b, 0xd5, 0x3f, 0x62, - 0x71, 0x15, 0xf3, 0xa9, 0x58, 0xf3, 0xab, 0x85, 0xa9, 0x64, 0x15, 0x72, - 0x4f, 0x8c, 0xe2, 0x5d, 0x1b, 0x52, 0xc4, 0x3e, 0x88, 0x98, 0x3a, 0xcd, - 0x6c, 0x7a, 0x44, 0x97, 0xf1, 0x0f, 0x6e, 0x77, 0x1d, 0x50, 0xf3, 0x6d, - 0x63, 0xab, 0x56, 0x7f, 0x89, 0x76, 0x28, 0xfb, 0x62, 0x24, 0xdd, 0xa7, - 0xbe, 0x4c, 0xaa, 0x26, 0xc9, 0x81, 0x16, 0x43, 0xaf, 0x1f, 0x86, 0xc4, - 0x77, 0x80, 0xac, 0x97, 0x1c, 0x98, 0xc9, 0xec, 0x16, 0x92, 0x0b, 0xa7, - 0x47, 0x9b, 0x49, 0x7a, 0x02, 0x9a, 0xc2, 0x5c, 0xd7, 0x66, 0x96, 0x57, - 0x90, 0x3e, 0xc5, 0x89, 0x75, 0xbd, 0x29, 0xf0, 0xa8, 0xf2, 0x2c, 0xb5, - 0x8e, 0x1c, 0x78, 0xb2, 0x02, 0xab, 0x6d, 0xd9, 0x18, 0xd1, 0xa7, 0xa8, - 0x4d, 0x12, 0xe9, 0x6e, 0x37, 0xb3, 0x51, 0x93, 0xe7, 0x37, 0xc2, 0x06, - 0x50, 0x60, 0xd4, 0x9f, 0xc1, 0xc6, 0x58, 0x3c, 0x1f, 0xe3, 0x2c, 0x5a, - 0x24, 0x6a, 0x68, 0x8e, 0xb1, 0xb4, 0xa2, 0xcc, 0x32, 0x41, 0x3d, 0x83, - 0x0d, 0x7f, 0x04, 0x37, 0x14, 0xb3, 0xe3, 0x8f, 0x4c, 0xed, 0x53, 0x02, - 0x78, 0x14, 0x30, 0x40, 0x24, 0xe5, 0x31, 0xf1, 0x51, 0x72, 0x1e, 0xf1, - 0xb7, 0xbe, 0x3a, 0xe1, 0x13, 0xeb, 0xe0, 0x66, 0x62, 0xe4, 0xcb, 0x7c, - 0xae, 0xd0, 0x95, 0x29, 0xbe, 0x60, 0xc6, 0x9d, 0xc9, 0x87, 0x3f, 0xa0, - 0xad, 0xcc, 0xd8, 0x9c, 0x6f, 0xd2, 0x76, 0x19, 0x73, 0x07, 0x29, 0x02, - 0x42, 0x2b, 0xeb, 0x20, 0xde, 0x98, 0x55, 0xe8, 0x16, 0x3f, 0x80, 0xa9, - 0x82, 0xda, 0x6b, 0x1f, 0x3c, 0x5f, 0x58, 0xde, 0x1b, 0x10, 0x63, 0x8a, - 0xf2, 0x90, 0x01, 0xe2, 0x6b, 0xb2, 0x7a, 0x1a, 0x6d, 0xc0, 0x35, 0xd9, - 0x70, 0x09, 0x37, 0x0c, 0x18, 0x61, 0x61, 0x30, 0x63, 0x9f, 0xc5, 0x64, - 0x9b, 0x5a, 0xc6, 0x7a, 0xf8, 0xee, 0x19, 0xa1, 0xa2, 0x67, 0xfe, 0x60, - 0xbb, 0x04, 0x81, 0xce, 0x00, 0x54, 0x06, 0x45, 0x7e, 0xea, 0xe7, 0x69, - 0xc7, 0xa1, 0x61, 0xc6, 0x5e, 0xe0, 0x7b, 0xac, 0xab, 0xf0, 0x36, 0xcd, - 0xf2, 0x6c, 0xa9, 0xb6, 0xa4, 0xa6, 0x65, 0x41, 0x33, 0xad, 0x67, 0x1b, - 0xd1, 0xa0, 0xa4, 0x2f, 0x05, 0x12, 0x85, 0x24, 0x10, 0xe0, 0x95, 0xdd, - 0x90, 0x25, 0xe8, 0xe9, 0x41, 0xdf, 0x1d, 0x3a, 0x2f, 0x42, 0x25, 0x62, - 0x08, 0xb5, 0xe4, 0x73, 0x79, 0x10, 0xf4, 0x8a, 0xcd, 0x7a, 0x96, 0x5a, - 0x89, 0xc2, 0x14, 0xa4, 0xc8, 0x91, 0x00, 0x5f, 0x3c, 0x68, 0xf0, 0x27, - 0x28, 0xe7, 0xe8, 0x9d, 0x52, 0x86, 0x2f, 0x6a, 0x33, 0x63, 0x49, 0x29, - 0xd7, 0x78, 0x8c, 0xf8, 0x35, 0xf5, 0x84, 0x81, 0x67, 0x39, 0x0e, 0x38, - 0x0b, 0x74, 0xb1, 0x2f, 0xd5, 0x5f, 0x5e, 0x52, 0x78, 0x3e, 0x18, 0x0b, - 0xa3, 0x4a, 0x55, 0x7d, 0x45, 0x6f, 0x30, 0xfd, 0xe1, 0xe3, 0x72, 0xd5, - 0xa4, 0x3d, 0xf5, 0xf5, 0x55, 0xcf, 0xc2, 0x10, 0x49, 0xb8, 0x4f, 0xbf, - 0x87, 0x96, 0x0f, 0x49, 0xf8, 0x3b, 0x54, 0x25, 0xf5, 0x27, 0x52, 0x85, - 0xe4, 0xbc, 0x45, 0x17, 0x27, 0x4b, 0x13, 0xb4, 0xa5, 0x11, 0x21, 0xbb, - 0x39, 0x78, 0xfb, 0xd4, 0xf3, 0xe3, 0x7e, 0x24, 0xf5, 0x13, 0xcb, 0xa9, - 0xa3, 0x40, 0x65, 0xcc, 0xfa, 0xc8, 0x48, 0xd0, 0x7c, 0x23, 0x8f, 0xf8, - 0xe0, 0x5f, 0x92, 0xcf, 0x20, 0x9e, 0x30, 0xe0, 0x4c, 0x13, 0x65, 0xbc, - 0xf5, 0x8c, 0x1a, 0x1c, 0x7d, 0x85, 0x91, 0x29, 0xbf, 0xcd, 0x1d, 0x15, - 0x6a, 0x13, 0xc4, 0xd9, 0xf2, 0x27, 0xf8, 0x0f, 0x56, 0x46, 0x85, 0xf5, - 0x62, 0xaa, 0xca, 0x3c, 0xab, 0x58, 0xab, 0xf0, 0x9a, 0x74, 0x7d, 0x0d, - 0x42, 0xa4, 0xe5, 0xb1, 0xf5, 0x27, 0xe2, 0x4b, 0x68, 0xce, 0x5a, 0x37, - 0x38, 0x70, 0xba, 0x6e, 0xb3, 0x1b, 0x3a, 0x1d, 0x24, 0x51, 0x0a, 0x28, - 0x3b, 0x12, 0x8a, 0xa2, 0x50, 0x8d, 0xa9, 0x2f, 0x6c, 0x7e, 0x1e, 0x42, - 0x39, 0x10, 0x43, 0x75, 0xdb, 0xed, 0x29, 0x4c, 0xd5, 0x4c, 0xb7, 0x13, - 0x62, 0x00, 0xd3, 0x7b, 0x94, 0xb5, 0x44, 0x2c, 0xdb, 0x1a, 0x1f, 0x98, - 0x8a, 0x9c, 0x51, 0x30, 0x59, 0xd5, 0x39, 0xa7, 0xb0, 0x93, 0x63, 0xb0, - 0x1f, 0x3f, 0x64, 0x59, 0xa3, 0x1b, 0xef, 0x2d, 0x84, 0x45, 0x1d, 0x9b, - 0x04, 0xc1, 0x11, 0x94, 0xd0, 0x3a, 0xd8, 0xaf, 0x22, 0x73, 0x4d, 0x96, - 0xd5, 0xc6, 0x4f, 0xad, 0xf4, 0xe9, 0x00, 0x18, 0x95, 0x2c, 0x4e, 0xf1, - 0xc5, 0x26, 0x8e, 0xce, 0xd9, 0xa0, 0x8b, 0x9a, 0xcf, 0xa0, 0x1b, 0xf2, - 0x90, 0x58, 0x4c, 0x6e, 0xc8, 0x51, 0xf5, 0x27, 0xbf, 0x26, 0x8c, 0xf0, - 0xc1, 0xd7, 0x62, 0x71, 0x77, 0x09, 0x9a, 0xac, 0x50, 0xf5, 0xc5, 0xea, - 0x2a, 0x74, 0xad, 0x56, 0xad, 0x2f, 0xaa, 0x3e, 0x65, 0xa1, 0xef, 0x48, - 0xb4, 0x24, 0x4b, 0x5b, 0x32, 0xa8, 0xbb, 0xaf, 0x70, 0x6e, 0x1a, 0xe5, - 0x5a, 0x93, 0x95, 0x4a, 0x1a, 0xce, 0x59, 0x53, 0x68, 0xc5, 0xe0, 0x93, - 0x9b, 0x6a, 0x33, 0x9e, 0x0f, 0xae, 0x7d, 0x17, 0x32, 0x10, 0x53, 0xa9, - 0xab, 0xa5, 0xaf, 0xb2, 0x74, 0x68, 0x6d, 0xc9, 0x9a, 0x2c, 0x1d, 0x8b, - 0x9f, 0x3d, 0xf1, 0x73, 0xc5, 0x7f, 0xce, 0xe0, 0xef, 0x6f, 0x5f, 0xd9, - 0xb7, 0x2c, 0x36, 0x5d, 0x9f, 0x06, 0xfd, 0x4b, 0xee, 0xc9, 0x14, 0x50, - 0xf5, 0x59, 0x1c, 0x11, 0xbe, 0x2e, 0xc7, 0x7f, 0x59, 0x89, 0x56, 0xa3, - 0x73, 0xd0, 0x75, 0x55, 0x6e, 0x0a, 0x7c, 0x1d, 0x48, 0xb8, 0xee, 0x8e, - 0x8f, 0xf6, 0x19, 0xf3, 0x2e, 0xc0, 0x86, 0x68, 0xc0, 0x9a, 0x4c, 0x77, - 0x1d, 0xe4, 0xa9, 0x11, 0x6f, 0xac, 0x70, 0xc2, 0xeb, 0xd5, 0xea, 0x9c, - 0xc1, 0x74, 0x01, 0x5b, 0x45, 0xc6, 0xe2, 0xa2, 0x96, 0xef, 0x03, 0xed, - 0x4c, 0x6d, 0x52, 0x3b, 0xa0, 0x2f, 0x08, 0x93, 0x1a, 0x8a, 0xd0, 0x27, - 0xe3, 0xf3, 0xe5, 0xe9, 0x89, 0x0e, 0xfa, 0x1d, 0x50, 0x05, 0x25, 0x12, - 0x78, 0xa0, 0x43, 0xf4, 0x0a, 0xf4, 0x55, 0x55, 0x75, 0x36, 0xf6, 0x90, - 0x77, 0x65, 0x09, 0x98, 0xc7, 0x67, 0xeb, 0x56, 0x70, 0xfa, 0xb8, 0xa1, - 0xd0, 0x92, 0x21, 0xaf, 0x61, 0x12, 0x29, 0x59, 0xa6, 0xb1, 0x10, 0x45, - 0xd0, 0x85, 0xf5, 0xec, 0x10, 0xe6, 0x24, 0x0b, 0x12, 0x97, 0xa1, 0xb1, - 0x20, 0x54, 0x5d, 0x2e, 0x29, 0xb5, 0xd2, 0x31, 0x61, 0x7d, 0x9d, 0x3b, - 0x52, 0xe5, 0xd3, 0xa7, 0x7f, 0x8f, 0xa0, 0x86, 0x30, 0x79, 0x6b, 0xcc, - 0x68, 0x50, 0x89, 0x06, 0x18, 0x02, 0xe4, 0x23, 0xaa, 0x5a, 0x92, 0x7f, - 0x01, 0xd0, 0x15, 0x84, 0x1d, 0xed, 0xc0, 0x35, 0xc8, 0x58, 0x88, 0xe2, - 0xef, 0xc2, 0xba, 0x5c, 0xca, 0xa9, 0xc0, 0x14, 0x8c, 0xf2, 0xe4, 0x36, - 0x00, 0x73, 0xde, 0x69, 0x07, 0x5e, 0x13, 0x98, 0x4d, 0xc5, 0x93, 0xdb, - 0x15, 0xbf, 0xef, 0xc1, 0x82, 0xa5, 0x31, 0x2e, 0xe2, 0x47, 0xd6, 0x62, - 0xe8, 0x4e, 0x10, 0x7a, 0x9e, 0xeb, 0x33, 0x6a, 0x62, 0xb3, 0x32, 0x98, - 0x9f, 0xe7, 0x7c, 0x92, 0x52, 0xae, 0x44, 0xf9, 0xc3, 0xc2, 0xfa, 0xed, - 0xe6, 0xc5, 0xc9, 0xc1, 0xc9, 0x5e, 0x31, 0x35, 0x7c, 0xf1, 0x38, 0x60, - 0x74, 0xf0, 0xfb, 0x6f, 0x98, 0x9b, 0x13, 0xcb, 0x09, 0x7e, 0xff, 0x2d, - 0x03, 0xfd, 0xf7, 0xdf, 0xba, 0x30, 0xc5, 0x41, 0x47, 0xdd, 0x58, 0x6b, - 0xfb, 0x15, 0xa0, 0x7f, 0x06, 0x0a, 0x53, 0xb8, 0xaa, 0x6a, 0x23, 0x95, - 0x5d, 0x24, 0x9c, 0xe9, 0xd0, 0x46, 0xcd, 0x28, 0x4a, 0x0c, 0xfb, 0xe0, - 0x32, 0x73, 0x7d, 0xc7, 0xb6, 0x6b, 0x9b, 0x0b, 0xeb, 0x07, 0x27, 0x07, - 0x57, 0x52, 0xeb, 0xe2, 0xe2, 0xf4, 0x42, 0x9a, 0x4b, 0xd8, 0xf0, 0x33, - 0x8a, 0x4e, 0x35, 0xf9, 0xf2, 0xec, 0x60, 0x77, 0xf7, 0x52, 0x9e, 0x33, - 0x62, 0x16, 0x83, 0xae, 0x01, 0xcb, 0x66, 0x42, 0x19, 0x3c, 0x9a, 0x96, - 0x0f, 0x40, 0x84, 0x5b, 0x88, 0x02, 0x88, 0x21, 0x8b, 0x08, 0x22, 0x43, - 0xc0, 0xd1, 0xc0, 0x24, 0x3c, 0xea, 0x28, 0xf2, 0x5e, 0xeb, 0x0a, 0x2c, - 0xb3, 0x12, 0x6d, 0xab, 0xcd, 0x55, 0x55, 0xf8, 0x14, 0x50, 0xd0, 0x38, - 0x27, 0xb4, 0x6d, 0x35, 0x35, 0xd5, 0xbc, 0x79, 0x80, 0xe7, 0x20, 0x36, - 0x14, 0x56, 0x8a, 0xcc, 0x29, 0x6b, 0x48, 0x5f, 0xe5, 0x12, 0x8d, 0x2c, - 0xa9, 0x24, 0x7f, 0x93, 0xf0, 0x2d, 0x67, 0x1f, 0x29, 0xa0, 0x0e, 0x71, - 0xd0, 0x15, 0x9f, 0xd0, 0xe1, 0xb6, 0x28, 0xa6, 0xd0, 0xf0, 0x85, 0x33, - 0x99, 0x9b, 0x9b, 0xcd, 0x17, 0xf2, 0x44, 0xb7, 0x18, 0x38, 0x3e, 0x3d, - 0xee, 0x4e, 0xfd, 0xf8, 0x31, 0x37, 0x07, 0x22, 0xef, 0x5a, 0xfe, 0x40, - 0x91, 0xb7, 0xfb, 0x98, 0x57, 0x07, 0x12, 0x73, 0xa5, 0xb1, 0x1b, 0xfa, - 0x52, 0x0c, 0x47, 0x1a, 0x5a, 0xb6, 0x2d, 0xb5, 0x21, 0x94, 0xb9, 0x01, - 0x93, 0xac, 0x2e, 0x7e, 0x95, 0x50, 0x51, 0x2c, 0x27, 0x04, 0x46, 0xa0, - 0xda, 0x39, 0x5c, 0x7f, 0x01, 0xfc, 0x8e, 0x15, 0x74, 0x88, 0x6f, 0xc2, - 0x2e, 0x81, 0x32, 0x57, 0x53, 0x91, 0x17, 0x29, 0xd6, 0xc5, 0xd3, 0xe8, - 0x5b, 0x2e, 0x6b, 0x0a, 0x63, 0x63, 0x8e, 0xce, 0x16, 0xbc, 0x99, 0x80, - 0xcd, 0xaf, 0x48, 0x51, 0x80, 0x15, 0x67, 0x3c, 0xb4, 0xee, 0x82, 0x5e, - 0x51, 0x2e, 0x83, 0xc4, 0x07, 0x03, 0xe7, 0x7d, 0x16, 0xdc, 0x5a, 0xac, - 0xaf, 0xc8, 0x15, 0x59, 0xfd, 0xf1, 0x43, 0xa1, 0x06, 0x3c, 0x94, 0xa8, - 0xaa, 0x51, 0x1d, 0xa4, 0x99, 0x7e, 0x42, 0x92, 0x0d, 0x58, 0x60, 0x43, - 0xe9, 0x08, 0x39, 0x59, 0xb9, 0xa6, 0xe2, 0x94, 0x20, 0x6c, 0x07, 0xcc, - 0xc7, 0xf0, 0x58, 0x85, 0x37, 0x30, 0x32, 0x76, 0x80, 0xe9, 0xdc, 0x69, - 0x97, 0xaf, 0xc9, 0x4a, 0x0f, 0x2c, 0x9e, 0xd1, 0x7d, 0x9e, 0xb1, 0x43, - 0x28, 0x75, 0x30, 0x98, 0xa2, 0x8f, 0x74, 0x8d, 0x59, 0x94, 0x01, 0x0a, - 0x24, 0xfd, 0x28, 0xd6, 0x47, 0xdf, 0x15, 0x99, 0x57, 0xc6, 0x32, 0x24, - 0x23, 0x5c, 0xa5, 0x0d, 0x19, 0x8d, 0x55, 0xd6, 0x88, 0x3e, 0x08, 0xd1, - 0xbe, 0xc1, 0xe2, 0xe6, 0x6a, 0xf0, 0x86, 0xe6, 0x6b, 0xc8, 0xd8, 0x29, - 0x94, 0x35, 0x57, 0x27, 0x1e, 0x68, 0xae, 0xb9, 0xdd, 0xb7, 0x6c, 0x53, - 0x21, 0x2a, 0xf7, 0xfa, 0xe6, 0xbb, 0x1b, 0x98, 0x3a, 0x38, 0x15, 0xd9, - 0x03, 0x87, 0x7a, 0xc0, 0x47, 0x34, 0x33, 0xda, 0x11, 0xb3, 0x0d, 0x7c, - 0x13, 0x7b, 0xe0, 0x04, 0x7c, 0x8b, 0x02, 0xe7, 0x0d, 0xb1, 0x43, 0xce, - 0xc8, 0x89, 0x6d, 0x4d, 0xb1, 0xad, 0x3d, 0x73, 0xdb, 0x76, 0x08, 0x99, - 0x3d, 0x48, 0xb2, 0x69, 0x67, 0x23, 0xd6, 0xb5, 0x87, 0x51, 0x68, 0x12, - 0x98, 0x2d, 0x80, 0x05, 0xef, 0x03, 0x0b, 0x38, 0x11, 0xc2, 0xb8, 0xb5, - 0x60, 0xd2, 0xba, 0xb1, 0xb1, 0x8c, 0xc3, 0x99, 0x0d, 0x8f, 0x1f, 0x77, - 0x70, 0x6a, 0x7e, 0xbf, 0x40, 0xec, 0x67, 0xbd, 0xbf, 0x9f, 0x95, 0x87, - 0xb5, 0xcb, 0x85, 0x93, 0x07, 0x66, 0x09, 0x60, 0x9d, 0x99, 0xc0, 0xd0, - 0xe1, 0x01, 0xa8, 0x0e, 0x47, 0x5d, 0xd4, 0x48, 0x5b, 0x1c, 0x3e, 0x78, - 0x9b, 0x4e, 0x31, 0x09, 0xf9, 0x1d, 0x3a, 0x62, 0x07, 0xff, 0x7d, 0x74, - 0x7d, 0xbe, 0x47, 0x6c, 0x77, 0x9a, 0x9f, 0xc5, 0x1e, 0x2d, 0x17, 0x87, - 0xc4, 0x86, 0xa2, 0x61, 0x73, 0x04, 0x99, 0xb2, 0x21, 0x7f, 0x84, 0xfa, - 0x08, 0xbf, 0xa4, 0x36, 0x57, 0x05, 0xc4, 0xb2, 0x18, 0xf8, 0x02, 0x83, - 0xc1, 0xfb, 0x18, 0x0c, 0x38, 0x06, 0x19, 0x53, 0xd6, 0x06, 0x59, 0x24, - 0x22, 0xd7, 0x81, 0xa3, 0xb3, 0x77, 0x1b, 0x88, 0xdd, 0xc2, 0xf7, 0x77, - 0x0b, 0xf9, 0x6e, 0x7d, 0x6a, 0x7b, 0x7c, 0xab, 0x30, 0xbb, 0xd5, 0x86, - 0x3c, 0x01, 0x36, 0x14, 0x60, 0xbd, 0xf7, 0x04, 0xe5, 0x09, 0x26, 0x46, - 0xf1, 0xc0, 0xcb, 0x25, 0x5c, 0x51, 0x5e, 0xaa, 0x4e, 0x8a, 0xc8, 0x03, - 0x13, 0x87, 0x14, 0xab, 0xc3, 0x9d, 0x70, 0x9a, 0x64, 0x89, 0xf4, 0x12, - 0x72, 0x2c, 0x83, 0xe8, 0x68, 0xd8, 0x81, 0x0e, 0x91, 0xb8, 0xc7, 0xfa, - 0xc2, 0x63, 0xb0, 0x78, 0xf4, 0x6b, 0xf5, 0x1b, 0xb7, 0xbe, 0xe6, 0x93, - 0x0b, 0xa5, 0x45, 0x15, 0xac, 0xef, 0x09, 0xcd, 0xae, 0xc0, 0xa7, 0x45, - 0x5f, 0xb8, 0x67, 0x8b, 0x9e, 0xd5, 0x78, 0xba, 0x31, 0xe9, 0x1e, 0xe3, - 0x09, 0x25, 0x9c, 0xcd, 0x20, 0xe4, 0xd9, 0x88, 0x22, 0xb8, 0xbd, 0xc7, - 0x1c, 0x86, 0xd3, 0xe8, 0xcd, 0xcf, 0x3b, 0x3a, 0x9e, 0xc7, 0x0a, 0x6b, - 0x55, 0x52, 0x2c, 0xe3, 0xad, 0xd4, 0x57, 0xcd, 0x2a, 0x84, 0x25, 0xc8, - 0x8a, 0xf1, 0x07, 0x7f, 0x3a, 0x50, 0xd4, 0xa6, 0x2c, 0x1b, 0x86, 0xc1, - 0x36, 0xa2, 0x70, 0xb9, 0x29, 0xc5, 0xf9, 0x88, 0x34, 0x08, 0x21, 0x24, - 0x41, 0x68, 0xea, 0x41, 0xf5, 0x86, 0xe5, 0x00, 0xcb, 0x3b, 0xea, 0x78, - 0xc5, 0x6e, 0x9c, 0xbf, 0x88, 0x05, 0x50, 0x1c, 0x42, 0x30, 0x32, 0x21, - 0xb0, 0xb1, 0xbe, 0x44, 0xa4, 0x85, 0xca, 0x82, 0x04, 0x7c, 0xf7, 0x49, - 0x87, 0x51, 0x88, 0xf8, 0x0d, 0x81, 0xfa, 0x36, 0x17, 0x2d, 0x24, 0xb1, - 0xaf, 0xe0, 0x0f, 0xfe, 0xe7, 0x98, 0x72, 0x46, 0x4a, 0x85, 0xc8, 0x2a, - 0x13, 0xd8, 0x82, 0x74, 0x58, 0x09, 0x05, 0xa3, 0x6a, 0x13, 0x28, 0x00, - 0x0e, 0x7e, 0x21, 0x0e, 0x90, 0x67, 0x80, 0x51, 0x2a, 0xf0, 0x7d, 0x30, - 0xe3, 0x7b, 0x64, 0x48, 0x38, 0x25, 0x9c, 0x05, 0x02, 0xd2, 0x92, 0xcb, - 0x3e, 0x64, 0x71, 0x9d, 0x10, 0x13, 0x89, 0xd7, 0xc9, 0x48, 0x75, 0x05, - 0x49, 0x2b, 0xc4, 0xa9, 0x40, 0x90, 0xec, 0xcc, 0x8c, 0x52, 0x72, 0xd4, - 0x78, 0x01, 0xfc, 0xd9, 0x4c, 0x13, 0x31, 0xad, 0x27, 0xb0, 0x90, 0x64, - 0x07, 0xeb, 0xcd, 0xf8, 0x9f, 0xc6, 0x1a, 0x35, 0xd2, 0xd0, 0x37, 0xa2, - 0xbf, 0x70, 0x8c, 0xd3, 0x19, 0x9b, 0x70, 0x88, 0xef, 0x2c, 0x8b, 0xfd, - 0xe9, 0x8c, 0xd5, 0xce, 0xe4, 0x30, 0xef, 0x20, 0xe0, 0x78, 0xc6, 0xb6, - 0xd7, 0xac, 0x41, 0x4f, 0x0a, 0xfc, 0x8e, 0xb1, 0x00, 0x49, 0x43, 0x49, - 0x4e, 0xaa, 0x91, 0x01, 0x19, 0x95, 0x93, 0xa6, 0xcf, 0x2f, 0x4d, 0x09, - 0xdf, 0xb3, 0x1d, 0x4e, 0x29, 0xea, 0x7d, 0x93, 0x90, 0xb9, 0x4d, 0x29, - 0x77, 0x58, 0xd8, 0x5c, 0x90, 0x20, 0xdd, 0xce, 0x08, 0xe4, 0xff, 0x13, - 0xbb, 0xb2, 0x45, 0x41, 0x5a, 0x09, 0x2c, 0xac, 0x6f, 0x76, 0xa8, 0x24, - 0xb6, 0x81, 0x34, 0x33, 0xb4, 0x4d, 0x6e, 0xaf, 0x3c, 0xff, 0x84, 0x8c, - 0xc9, 0x94, 0xb0, 0x1b, 0x2e, 0xb1, 0x3e, 0x95, 0xf8, 0x39, 0xae, 0x43, - 0xd1, 0x9e, 0x7d, 0x31, 0x5a, 0xc1, 0x65, 0x15, 0xd2, 0xa1, 0xfa, 0x43, - 0x20, 0xe9, 0xd2, 0x8e, 0xc8, 0x40, 0xc0, 0xdd, 0x62, 0x52, 0x8b, 0x59, - 0x8a, 0x84, 0xfa, 0x49, 0x7d, 0xa8, 0x75, 0xe3, 0x42, 0x05, 0x74, 0x96, - 0xd7, 0x4a, 0x6c, 0xc4, 0xe2, 0x5c, 0x3a, 0x11, 0x5c, 0xd2, 0x55, 0x91, - 0xb8, 0x68, 0xb0, 0x2a, 0x84, 0xe9, 0xeb, 0x3f, 0x59, 0x25, 0xcf, 0x64, - 0x65, 0x66, 0x4b, 0x60, 0x24, 0xa2, 0x17, 0xbd, 0x1a, 0xac, 0x6f, 0x05, - 0xb9, 0xdc, 0x7f, 0xa2, 0xd8, 0xa0, 0x49, 0x85, 0x91, 0x49, 0x2b, 0xfd, - 0xd4, 0x29, 0x55, 0x94, 0x8d, 0xc6, 0x7f, 0xe8, 0xca, 0xd7, 0xff, 0xd4, - 0xbf, 0x95, 0x54, 0x75, 0xe3, 0xdf, 0x2a, 0x3a, 0x1d, 0x51, 0xd4, 0x9d, - 0xaf, 0xb5, 0x6f, 0x4d, 0x88, 0x27, 0x22, 0x4a, 0x80, 0xd7, 0x66, 0x6a, - 0x00, 0x4e, 0xb0, 0xd3, 0x07, 0x57, 0xc4, 0xdc, 0x23, 0x17, 0x58, 0xb3, - 0x4d, 0xa0, 0x30, 0x57, 0xd5, 0x97, 0x0e, 0xfc, 0x22, 0x86, 0x72, 0x83, - 0x3f, 0xf5, 0xd9, 0x20, 0x7d, 0xb2, 0xa3, 0xc7, 0x87, 0x20, 0x79, 0x80, - 0x60, 0x2a, 0x1e, 0x3b, 0xf1, 0xb4, 0xf8, 0xdd, 0xf3, 0xe2, 0xa7, 0x20, - 0x9e, 0x3e, 0x42, 0x08, 0x51, 0xde, 0x5d, 0xcb, 0xb8, 0x9b, 0xc1, 0x9f, - 0x4f, 0x81, 0xe7, 0xf4, 0x62, 0x1c, 0xbd, 0xf4, 0x89, 0xc6, 0x8f, 0x3d, - 0xab, 0x1b, 0x3d, 0x59, 0x1d, 0xb7, 0x10, 0xa7, 0x50, 0x71, 0x34, 0x57, - 0x8d, 0x7a, 0x18, 0xb3, 0x9c, 0x5a, 0x68, 0x83, 0x4f, 0x73, 0x72, 0xf1, - 0x9c, 0xaa, 0x91, 0x96, 0xcc, 0x5a, 0x63, 0x5b, 0xb0, 0x86, 0xe6, 0xd6, - 0x30, 0x55, 0xf3, 0x15, 0x57, 0xdd, 0x90, 0xf1, 0xde, 0x44, 0x17, 0xca, - 0x4c, 0x13, 0xa2, 0x09, 0xa6, 0xd8, 0x6e, 0x57, 0x02, 0xa5, 0xde, 0x00, - 0x1a, 0x27, 0xed, 0x67, 0xfd, 0x06, 0x14, 0x3a, 0x29, 0x89, 0x59, 0x71, - 0x78, 0x5e, 0xcb, 0x37, 0xba, 0x52, 0x3d, 0x0c, 0xb6, 0xc6, 0xdb, 0xd8, - 0x0d, 0x38, 0x81, 0xf0, 0xa4, 0xc8, 0x99, 0xab, 0x2f, 0xa0, 0x8f, 0x71, - 0x20, 0xcf, 0xaf, 0x15, 0x2d, 0x34, 0x81, 0xae, 0xa3, 0x6a, 0x45, 0x85, - 0xe8, 0xfc, 0x7c, 0x07, 0x88, 0x78, 0xe5, 0xf1, 0x6d, 0x0a, 0xdd, 0x16, - 0x18, 0xe8, 0x3f, 0x0d, 0x5d, 0x48, 0x5a, 0x60, 0xf0, 0xda, 0xb7, 0x05, - 0xda, 0x03, 0xf8, 0x81, 0x0a, 0xaf, 0x00, 0xf9, 0x33, 0x11, 0xdf, 0xfe, - 0x69, 0xf8, 0x5b, 0x1c, 0x6f, 0xae, 0x6e, 0xb3, 0xab, 0xc4, 0x02, 0x75, - 0x23, 0x98, 0x72, 0x4e, 0x11, 0xb9, 0xe3, 0x0e, 0xb9, 0x0b, 0x4b, 0xa8, - 0x24, 0xb3, 0xb3, 0x9f, 0xe6, 0x5f, 0x48, 0x2b, 0x33, 0xdc, 0x37, 0x2a, - 0xfd, 0x08, 0xc9, 0x32, 0x3f, 0x3f, 0xc0, 0x30, 0x04, 0x61, 0x97, 0x95, - 0xe4, 0x8d, 0xf8, 0x83, 0xc1, 0xfc, 0x90, 0xca, 0xaf, 0xef, 0x54, 0xb6, - 0x05, 0x4c, 0x31, 0x31, 0x1b, 0x9e, 0x62, 0xca, 0x05, 0x4f, 0x1f, 0x2b, - 0xc7, 0x80, 0x60, 0xda, 0x7f, 0xfa, 0x17, 0x4b, 0x3f, 0xf2, 0x27, 0x1e, - 0x04, 0x3a, 0x0f, 0x70, 0x17, 0x28, 0x49, 0x72, 0xc9, 0x2d, 0xc9, 0x10, - 0xde, 0xa0, 0x9e, 0x00, 0x17, 0x14, 0xda, 0xf6, 0x9c, 0xc1, 0xe6, 0xe7, - 0xd9, 0x9c, 0xe1, 0xce, 0xcf, 0x8f, 0x15, 0x57, 0x83, 0xfc, 0xf5, 0x9d, - 0x3a, 0xbb, 0x80, 0x09, 0xb6, 0xaa, 0xd9, 0x05, 0x9a, 0x41, 0x6d, 0xca, - 0x52, 0xfa, 0x67, 0xd4, 0x02, 0x7f, 0x9d, 0x46, 0xf4, 0x50, 0xcf, 0x53, - 0xcf, 0x6c, 0xe3, 0x31, 0x84, 0xe6, 0x4c, 0xf5, 0x4e, 0x8a, 0x12, 0xce, - 0x6c, 0xfb, 0x44, 0x34, 0xfb, 0x3b, 0xbe, 0x6b, 0xdb, 0x57, 0xae, 0xb7, - 0x31, 0x63, 0x3c, 0x3d, 0x59, 0x88, 0x1f, 0x22, 0x88, 0xe9, 0x14, 0xcd, - 0x2c, 0x04, 0x8a, 0x25, 0x71, 0x21, 0x54, 0xfc, 0xf0, 0x1e, 0x58, 0x9c, - 0xa3, 0xd9, 0x06, 0xd5, 0x81, 0xb3, 0x30, 0x7a, 0x57, 0x32, 0xb5, 0x20, - 0x79, 0xbb, 0x2f, 0x91, 0xa6, 0x2b, 0xce, 0x33, 0x4e, 0x78, 0xab, 0x25, - 0xcb, 0x44, 0xa8, 0x27, 0x8b, 0x93, 0xae, 0x78, 0x1c, 0x0f, 0xb5, 0x0c, - 0xbb, 0x24, 0x63, 0xa9, 0x1e, 0x8f, 0x31, 0xd7, 0x33, 0x02, 0x31, 0xa4, - 0x38, 0x1b, 0x61, 0x23, 0x91, 0xe4, 0xbf, 0x32, 0xe8, 0x4d, 0x7a, 0xae, - 0x09, 0x63, 0x77, 0x55, 0xbd, 0x83, 0x33, 0x4f, 0x5c, 0x53, 0x14, 0xba, - 0xd1, 0x25, 0x10, 0xed, 0x1d, 0xbb, 0xc6, 0x63, 0x99, 0x49, 0xd3, 0x26, - 0x90, 0x8e, 0x4d, 0x2b, 0xf6, 0x36, 0x30, 0x9e, 0x78, 0xc1, 0xdf, 0x67, - 0xda, 0x33, 0x1d, 0xdd, 0x2c, 0xda, 0x8d, 0xb9, 0xda, 0x9f, 0x66, 0xd6, - 0x17, 0xe2, 0x08, 0xe8, 0xef, 0xb3, 0xeb, 0x3e, 0xb7, 0x6b, 0x3c, 0x2a, - 0x92, 0x0a, 0x84, 0xd3, 0x1a, 0xc1, 0xdf, 0xe6, 0xff, 0x25, 0xd1, 0x54, - 0x05, 0x45, 0xef, 0x74, 0x24, 0x0b, 0xe4, 0x13, 0xa8, 0xf9, 0xd6, 0xe3, - 0xec, 0xd8, 0x53, 0xdc, 0x91, 0xf8, 0xa7, 0xc6, 0x9e, 0xd9, 0x6d, 0xd2, - 0x02, 0x26, 0x58, 0xaa, 0x66, 0xbd, 0x1b, 0x7b, 0x8a, 0x7b, 0x47, 0x7f, - 0x79, 0xec, 0x51, 0x39, 0x51, 0x13, 0xe7, 0xc6, 0x59, 0xe4, 0xdd, 0xb8, - 0x2f, 0xec, 0xea, 0x6e, 0xb7, 0x1b, 0x50, 0x76, 0x8b, 0x75, 0xbf, 0xd6, - 0x49, 0xde, 0xf7, 0x79, 0xdd, 0x0f, 0x5e, 0xdc, 0x75, 0x06, 0x6e, 0x18, - 0x50, 0x37, 0x64, 0x39, 0x12, 0x94, 0xc4, 0xf3, 0xaf, 0xd9, 0x3f, 0x7e, - 0x24, 0x2f, 0xeb, 0x76, 0xc9, 0x4a, 0x5f, 0xef, 0xd7, 0x82, 0xcc, 0xcb, - 0x7a, 0x50, 0xea, 0x40, 0x16, 0xf8, 0x57, 0x11, 0x9f, 0x8b, 0xb9, 0xde, - 0x1f, 0x8b, 0xb9, 0x5c, 0xac, 0x2e, 0x76, 0x41, 0xb1, 0xb1, 0x65, 0xe0, - 0x69, 0xba, 0xdc, 0x88, 0xdb, 0x88, 0xef, 0x24, 0xaa, 0x51, 0x0f, 0x35, - 0x2a, 0xc5, 0xf0, 0x14, 0x79, 0x83, 0x4c, 0x9e, 0xfe, 0x63, 0x5d, 0x8a, - 0x89, 0x3a, 0x03, 0xba, 0xa7, 0x3e, 0x5a, 0x83, 0x9e, 0x9c, 0xcf, 0x6c, - 0x21, 0x31, 0x94, 0xd6, 0xac, 0x75, 0x65, 0xe2, 0xac, 0xd5, 0x01, 0x84, - 0xd4, 0xb5, 0x8a, 0xb5, 0x3e, 0x7d, 0x22, 0xa2, 0xb9, 0x85, 0x0a, 0x46, - 0x18, 0xa3, 0x60, 0x00, 0x98, 0x83, 0x2b, 0x48, 0x9e, 0xfa, 0x2a, 0x26, - 0x0a, 0x96, 0x02, 0xfe, 0x61, 0x6e, 0x3a, 0xd5, 0xb1, 0x23, 0x06, 0x54, - 0x45, 0x8d, 0x08, 0x85, 0x1f, 0x13, 0x41, 0xcc, 0x3d, 0xf3, 0x5d, 0x8f, - 0xf4, 0x88, 0x68, 0x10, 0x68, 0x98, 0xc8, 0x20, 0x34, 0x7e, 0x5a, 0xa6, - 0xb9, 0x29, 0xb7, 0xbb, 0x7f, 0x3d, 0xb7, 0x27, 0x0f, 0x8f, 0xb8, 0x13, - 0x6b, 0xbb, 0x90, 0x0f, 0x24, 0x15, 0xb6, 0x33, 0x3f, 0x0f, 0xff, 0x2b, - 0x24, 0xed, 0xb7, 0xcb, 0xf1, 0x93, 0xac, 0xfe, 0xec, 0x59, 0x12, 0xbf, - 0xb8, 0x99, 0x8a, 0xd7, 0xcc, 0xb7, 0x3d, 0xa6, 0x8e, 0x88, 0x66, 0xf5, - 0xc4, 0x49, 0xec, 0x6d, 0xe7, 0xe7, 0xfb, 0xb1, 0x24, 0x8a, 0x23, 0x43, - 0x32, 0x73, 0x83, 0x64, 0x82, 0x67, 0x43, 0x21, 0x13, 0xfe, 0x1a, 0x61, - 0xfc, 0xd9, 0xe2, 0xac, 0xe5, 0xc5, 0xe9, 0x62, 0x56, 0x85, 0x29, 0x48, - 0x44, 0x7c, 0xfe, 0x26, 0x45, 0xe6, 0x7a, 0x0d, 0x9e, 0x9c, 0xf2, 0x8b, - 0x15, 0x4e, 0xf1, 0xc5, 0x0a, 0x27, 0x77, 0xb1, 0x62, 0x76, 0x80, 0xe2, - 0x29, 0x50, 0xdc, 0x51, 0x11, 0x47, 0x92, 0xd1, 0x04, 0xce, 0xdf, 0x26, - 0x38, 0x6b, 0xf9, 0xfa, 0x08, 0x7b, 0x0c, 0x3a, 0x23, 0x3d, 0x74, 0x0f, - 0xf3, 0xf3, 0x34, 0x67, 0xfc, 0xe0, 0xc1, 0xe7, 0x52, 0x16, 0xf0, 0x5b, - 0x3f, 0x7a, 0xe0, 0xfa, 0x4c, 0xc9, 0x0f, 0xa6, 0xf7, 0x58, 0x50, 0x89, - 0x10, 0x1c, 0xfc, 0x6e, 0x50, 0x7e, 0x24, 0x01, 0xc5, 0x75, 0x87, 0xd8, - 0x74, 0x1b, 0x22, 0x07, 0xf1, 0xf1, 0xce, 0x07, 0x0e, 0xaa, 0x0d, 0x31, - 0x77, 0xea, 0x23, 0x0e, 0xc6, 0xc5, 0xac, 0xf3, 0x76, 0xea, 0x39, 0xfb, - 0xb0, 0x34, 0xa7, 0x44, 0x8e, 0x9a, 0x5c, 0xc4, 0x71, 0x01, 0x33, 0xe1, - 0xf0, 0xa0, 0x1e, 0xa8, 0x36, 0xc9, 0x9a, 0xdb, 0x24, 0xa5, 0x92, 0x60, - 0x90, 0x09, 0x59, 0x37, 0xfb, 0x4a, 0xbe, 0x35, 0xcd, 0xe8, 0x04, 0xd5, - 0x30, 0x0c, 0x5b, 0xd0, 0x81, 0x4e, 0xcf, 0xe6, 0x68, 0xc3, 0x0f, 0x5e, - 0x5f, 0x53, 0x1b, 0xdd, 0x64, 0x08, 0x7b, 0xf5, 0x79, 0x9d, 0x7d, 0x7d, - 0x85, 0x28, 0x91, 0xb9, 0xe5, 0xe2, 0x4c, 0xdc, 0x72, 0x49, 0x2d, 0xeb, - 0x67, 0x53, 0x8b, 0x37, 0xae, 0x2b, 0xbc, 0x91, 0x8d, 0x80, 0x11, 0xa1, - 0x97, 0x4c, 0x3c, 0x3d, 0x18, 0xe9, 0x6b, 0xc6, 0xa5, 0x13, 0x85, 0xbb, - 0x98, 0x3f, 0xac, 0x93, 0x6f, 0x5f, 0xf6, 0x91, 0x60, 0x95, 0x13, 0x57, - 0x0e, 0x93, 0xc7, 0x49, 0x20, 0x8a, 0xfc, 0x95, 0x1f, 0x48, 0x16, 0x72, - 0xef, 0x73, 0x06, 0x68, 0x60, 0x74, 0x25, 0xe9, 0x8c, 0xb0, 0xfe, 0xe4, - 0x0d, 0x21, 0x2d, 0xfb, 0x0d, 0x0f, 0xc5, 0x33, 0xfe, 0xd8, 0x51, 0x45, - 0x0a, 0xf9, 0xc6, 0xf2, 0xe6, 0xc4, 0x5d, 0xa9, 0x0c, 0x17, 0xfa, 0x42, - 0x91, 0xb3, 0x04, 0x57, 0x35, 0x19, 0x83, 0x0a, 0x76, 0x8f, 0xa3, 0x86, - 0xbe, 0xae, 0xeb, 0xe0, 0xf1, 0x46, 0x03, 0x1b, 0xc9, 0x2e, 0x68, 0xff, - 0xc6, 0x9f, 0xe2, 0x26, 0xb0, 0xab, 0x44, 0x03, 0x1a, 0x02, 0x4f, 0xbf, - 0x66, 0x6f, 0x8d, 0xe0, 0xbf, 0x0a, 0xdb, 0x30, 0x2d, 0xdf, 0x80, 0x2d, - 0xf8, 0xd5, 0x91, 0x78, 0x56, 0xd1, 0x05, 0x12, 0x33, 0x6b, 0xce, 0x9e, - 0xeb, 0x29, 0x2a, 0xda, 0x30, 0xd0, 0xa9, 0xd1, 0xcc, 0xac, 0x71, 0x21, - 0x2d, 0x3c, 0x67, 0xcb, 0x10, 0x83, 0x2d, 0x71, 0x78, 0x64, 0x09, 0x5d, - 0xac, 0xe0, 0xb4, 0x90, 0xf1, 0x73, 0x42, 0xf6, 0x33, 0x44, 0x93, 0x84, - 0x68, 0x16, 0x9b, 0x31, 0x2e, 0xda, 0x75, 0xfd, 0xc1, 0x0e, 0x61, 0x24, - 0xa9, 0x18, 0x95, 0xe8, 0xa2, 0x00, 0x4b, 0x2d, 0x08, 0x82, 0xbf, 0xdf, - 0xe1, 0xdd, 0xed, 0x3c, 0xab, 0xce, 0xae, 0x39, 0xab, 0xb0, 0xb9, 0x2f, - 0x4f, 0x32, 0x28, 0x43, 0x77, 0x0f, 0xb9, 0x33, 0x41, 0x35, 0xcf, 0x1b, - 0xff, 0x17, 0x22, 0x4c, 0xa8, 0x51, 0x33, 0x4d, 0xff, 0x84, 0x16, 0x36, - 0x41, 0xcb, 0x14, 0xe6, 0x3b, 0xad, 0xa3, 0xd6, 0x55, 0x6b, 0x16, 0xf2, - 0xe0, 0x5d, 0x23, 0xb3, 0x63, 0xb9, 0xaa, 0x3e, 0xf3, 0x8f, 0x0f, 0xb0, - 0xc9, 0x88, 0x87, 0xc9, 0x95, 0xd9, 0x47, 0x32, 0x13, 0xfe, 0x0e, 0xf8, - 0xc9, 0x8f, 0x0f, 0x3a, 0x36, 0x25, 0xfe, 0x31, 0xb1, 0x9c, 0x33, 0xe2, - 0x50, 0xfb, 0x0f, 0x1d, 0x48, 0xfc, 0xfd, 0x47, 0x42, 0xa6, 0xf5, 0x14, - 0x1f, 0xbe, 0x4c, 0xdd, 0xb5, 0x5f, 0x58, 0x57, 0xd0, 0x31, 0xf3, 0xd3, - 0xa0, 0x2e, 0x5e, 0x1f, 0x93, 0xf0, 0xd8, 0x07, 0x44, 0x41, 0xc4, 0x89, - 0x6e, 0x72, 0x15, 0x0d, 0x72, 0x3a, 0x80, 0xb3, 0x2e, 0xbf, 0x0a, 0x46, - 0x64, 0x9c, 0x44, 0x2e, 0x9c, 0x67, 0x2f, 0xaf, 0xca, 0x9b, 0x62, 0x12, - 0x6a, 0x0a, 0xca, 0x5d, 0x72, 0xbb, 0x79, 0x8d, 0xf9, 0xa9, 0x33, 0xb7, - 0xe2, 0x36, 0xad, 0x82, 0x07, 0x34, 0x33, 0x7b, 0xfb, 0x78, 0xb6, 0xd1, - 0x48, 0x3b, 0xd1, 0x14, 0xd3, 0x5b, 0x98, 0x8e, 0xe7, 0xa3, 0x8d, 0xbc, - 0x28, 0x15, 0xcc, 0x53, 0xa2, 0x13, 0xa2, 0xc4, 0xd5, 0x4d, 0xde, 0x18, - 0x88, 0xaf, 0xbb, 0x19, 0xd9, 0xeb, 0x6e, 0x6a, 0x8e, 0xe4, 0x8b, 0xe4, - 0xea, 0x27, 0x9e, 0x80, 0x0b, 0x32, 0x85, 0xe1, 0x27, 0x9e, 0x7c, 0xe6, - 0xfc, 0x2c, 0x6b, 0xca, 0x35, 0xc3, 0x28, 0xb8, 0xef, 0x94, 0x01, 0xf5, - 0x92, 0xde, 0x84, 0xfd, 0xfa, 0x4d, 0x73, 0x0c, 0xda, 0x2c, 0xd7, 0x30, - 0xb9, 0x99, 0x5c, 0x33, 0x3f, 0x3f, 0xf3, 0xf2, 0x1a, 0x84, 0x0e, 0x95, - 0xe9, 0x5e, 0x18, 0xf4, 0xb1, 0x1a, 0x73, 0x8a, 0x42, 0x0b, 0x1e, 0xbd, - 0x3b, 0x1b, 0xd1, 0x24, 0x04, 0xd7, 0x48, 0x17, 0x70, 0x5b, 0x8b, 0xb8, - 0x96, 0xde, 0x4b, 0x30, 0xf2, 0x59, 0xcc, 0x84, 0xeb, 0x10, 0x73, 0x22, - 0x72, 0xff, 0x46, 0x37, 0x29, 0xee, 0x6c, 0x15, 0x44, 0x90, 0xb3, 0xd3, - 0xcb, 0xb7, 0xfc, 0x62, 0x4a, 0xad, 0xb8, 0x47, 0x90, 0x53, 0x91, 0xff, - 0xee, 0xe4, 0x6a, 0x7b, 0xda, 0x86, 0x81, 0xf0, 0x5f, 0x81, 0x68, 0xaa, - 0x92, 0x11, 0x1a, 0x98, 0xf6, 0x61, 0x6b, 0x97, 0x22, 0x40, 0x93, 0x26, - 0x6d, 0x68, 0x93, 0xca, 0x3e, 0x21, 0x84, 0x92, 0x34, 0xd0, 0x40, 0x1a, - 0xa3, 0x26, 0x8c, 0x55, 0x55, 0xff, 0xfb, 0x9e, 0xbb, 0x73, 0x52, 0xc7, - 0x4d, 0x61, 0xf4, 0x4b, 0x9b, 0xc6, 0x8e, 0x63, 0x9f, 0xcf, 0xf7, 0xfa, - 0x5c, 0xad, 0xb5, 0x72, 0x8f, 0x8d, 0x9d, 0x7d, 0x11, 0x13, 0xb7, 0xcb, - 0x52, 0xd3, 0xdd, 0xa4, 0xe8, 0x0b, 0xf2, 0x9f, 0xe0, 0x19, 0x32, 0x37, - 0x5e, 0xae, 0x0d, 0x5d, 0xf8, 0xca, 0xa2, 0x49, 0xdc, 0x28, 0xb1, 0xbd, - 0xbb, 0x74, 0xa9, 0x03, 0x89, 0x94, 0x15, 0x90, 0x45, 0xff, 0x9f, 0x76, - 0x2c, 0xea, 0xb4, 0x63, 0x61, 0xa6, 0x4a, 0x9b, 0xa1, 0x86, 0x31, 0x5e, - 0xff, 0x30, 0x5c, 0xa7, 0x4e, 0xd1, 0xc2, 0x89, 0x53, 0xb3, 0x81, 0xd2, - 0xa7, 0xb8, 0x7f, 0x1f, 0xfd, 0x89, 0x04, 0x7b, 0xdf, 0x6a, 0x4d, 0xcc, - 0x04, 0x2a, 0xba, 0x25, 0x37, 0x74, 0xd5, 0xea, 0xd1, 0x24, 0x54, 0xcb, - 0xf5, 0xe5, 0xe3, 0xf4, 0xb1, 0x2b, 0x51, 0xdb, 0xe4, 0x67, 0x39, 0xf7, - 0x8a, 0x0d, 0x68, 0x34, 0xcf, 0x4a, 0xd6, 0x44, 0xb0, 0x15, 0xca, 0x7a, - 0x61, 0x7b, 0xb9, 0x0a, 0xa5, 0x4f, 0xd3, 0xae, 0x6d, 0xd5, 0x90, 0xbd, - 0x40, 0x17, 0x06, 0xb9, 0xbb, 0x36, 0x60, 0x71, 0x53, 0xe1, 0xa6, 0x12, - 0xfc, 0x1f, 0xa4, 0x70, 0x6a, 0xf6, 0x8f, 0xc8, 0x65, 0x94, 0x26, 0xf0, - 0x4b, 0xe1, 0xeb, 0xf9, 0xeb, 0x91, 0xea, 0x16, 0xa1, 0x96, 0x57, 0x43, - 0x01, 0xc9, 0xdc, 0xf1, 0xcb, 0x90, 0x12, 0xf8, 0xb4, 0xd9, 0x14, 0x4f, - 0x36, 0x90, 0x22, 0x6d, 0xae, 0xad, 0xed, 0xd1, 0x5c, 0xdb, 0xa3, 0xbd, - 0x5e, 0xdb, 0x20, 0xcd, 0xc1, 0x11, 0x50, 0xa2, 0xa6, 0x90, 0x74, 0x6d, - 0xa9, 0x41, 0x8b, 0xb1, 0x6d, 0x42, 0x03, 0x6b, 0xb1, 0xf1, 0xc2, 0xd7, - 0x91, 0x2f, 0x3b, 0xa9, 0xce, 0xcd, 0xc7, 0xb4, 0x72, 0xdc, 0x55, 0xe5, - 0x6a, 0x90, 0x32, 0x11, 0x28, 0x6c, 0x08, 0x74, 0xe2, 0x96, 0x38, 0x33, - 0x82, 0xc7, 0x74, 0x73, 0xcb, 0xb0, 0xd6, 0xf6, 0xc2, 0x98, 0x2b, 0x0f, - 0xc5, 0x8f, 0xdd, 0x8a, 0xea, 0x85, 0x60, 0x25, 0xc2, 0xda, 0x5a, 0xc9, - 0x42, 0x22, 0xd8, 0xa6, 0xf4, 0x5a, 0x96, 0xae, 0x05, 0x8c, 0x9b, 0x77, - 0x88, 0x0f, 0xaf, 0x96, 0x1b, 0x09, 0x85, 0xa2, 0x0d, 0xd0, 0x03, 0x9b, - 0xc7, 0xb9, 0x69, 0x18, 0x0b, 0x0f, 0xeb, 0x33, 0x27, 0xa1, 0x8a, 0x92, - 0x68, 0x35, 0x4e, 0xcb, 0x92, 0x17, 0x41, 0x0b, 0xbe, 0x80, 0x4b, 0xe4, - 0x3a, 0xe0, 0xa9, 0x60, 0x86, 0x2b, 0x62, 0x46, 0x5a, 0x2e, 0x1a, 0x2e, - 0xa7, 0xe9, 0x4c, 0xb7, 0x54, 0x74, 0x89, 0x26, 0x45, 0x4d, 0xef, 0x98, - 0xfa, 0x63, 0xce, 0xc7, 0x60, 0xca, 0xe1, 0x71, 0x70, 0xe4, 0x6f, 0x0e, - 0xfb, 0xbb, 0x4c, 0xc7, 0xea, 0xb6, 0xba, 0x8c, 0x62, 0x10, 0xe5, 0xc8, - 0xeb, 0xe8, 0x81, 0x26, 0x8e, 0x33, 0x7d, 0xd0, 0xef, 0xfb, 0x96, 0xdd, - 0x4d, 0x73, 0x0a, 0xff, 0x9d, 0x26, 0x54, 0x6d, 0xf4, 0x03, 0x5b, 0xa4, - 0x9f, 0x44, 0xe3, 0x18, 0xcc, 0xfb, 0x6b, 0x9e, 0x15, 0xd5, 0x05, 0x83, - 0x81, 0x98, 0xdd, 0xb0, 0x29, 0x6a, 0x36, 0x8b, 0x8a, 0x49, 0x49, 0x91, - 0xad, 0x73, 0xb9, 0x76, 0x97, 0xe4, 0x76, 0x0e, 0x18, 0xa3, 0xe9, 0xf8, - 0x31, 0x0e, 0xeb, 0xf7, 0x74, 0x31, 0x58, 0x3e, 0x67, 0xc5, 0xc0, 0x39, - 0xaf, 0xe6, 0xf9, 0xe1, 0xd8, 0xf1, 0x67, 0x51, 0x82, 0x1f, 0xd2, 0x1f, - 0xbf, 0x57, 0x3e, 0x49, 0xb1, 0x41, 0x3b, 0x96, 0x51, 0x03, 0xc7, 0x28, - 0x24, 0xf6, 0xb3, 0xc8, 0x17, 0x83, 0xfd, 0xe3, 0xd5, 0x6b, 0xaf, 0xc4, - 0x1c, 0xf1, 0xb2, 0x58, 0x81, 0x19, 0x1a, 0xc0, 0x58, 0xe7, 0x1c, 0x4e, - 0xf3, 0xea, 0x70, 0x6a, 0xcd, 0x43, 0xee, 0x75, 0xcf, 0xc5, 0x46, 0xa0, - 0x09, 0x51, 0x98, 0x96, 0xe0, 0x06, 0xd7, 0x91, 0x10, 0x90, 0xe3, 0xb7, - 0x1e, 0xeb, 0xe6, 0xcf, 0x23, 0x8f, 0x9f, 0xd6, 0x86, 0x54, 0x4b, 0xe9, - 0xbd, 0xc9, 0x88, 0xdb, 0x0e, 0x6a, 0x67, 0x31, 0x18, 0x32, 0x26, 0xdb, - 0x90, 0x6f, 0x6f, 0x67, 0xc2, 0xb9, 0x06, 0x17, 0xd2, 0x4e, 0x98, 0xf6, - 0xf9, 0xf6, 0x37, 0x9b, 0xf6, 0x0a, 0x39, 0xed, 0xd6, 0x19, 0xc3, 0x23, - 0x6f, 0x3c, 0x62, 0xd9, 0x50, 0xe2, 0x22, 0x2d, 0x7d, 0xac, 0x2c, 0xd3, - 0x83, 0x1a, 0xcf, 0x72, 0x15, 0xbb, 0x57, 0xd5, 0xb5, 0xbf, 0x24, 0x43, - 0x72, 0x50, 0x80, 0xc6, 0xa9, 0xd7, 0x9c, 0x4f, 0xcb, 0x12, 0xd1, 0x67, - 0x54, 0x79, 0x2b, 0x78, 0xa6, 0x4c, 0x0b, 0x11, 0x3a, 0xde, 0x81, 0xe3, - 0x40, 0x07, 0xd3, 0x9a, 0x35, 0x2c, 0xd1, 0x5c, 0xf6, 0xda, 0xf8, 0xad, - 0x5e, 0x10, 0x3e, 0x4c, 0x30, 0x93, 0x5d, 0xcc, 0x21, 0x48, 0x71, 0x70, - 0x3d, 0xc3, 0x1d, 0x8f, 0x05, 0xa2, 0x3f, 0xe5, 0x9a, 0xec, 0xb4, 0x4f, - 0x0f, 0xe0, 0x5d, 0xf0, 0x2a, 0x68, 0x74, 0x33, 0xe3, 0x94, 0x69, 0x9b, - 0x0b, 0xa9, 0x60, 0x4f, 0x72, 0x30, 0x9d, 0x8c, 0x0e, 0xbe, 0xa4, 0xe3, - 0x42, 0xb3, 0x33, 0xec, 0x0c, 0x55, 0x9c, 0xa9, 0xc9, 0x82, 0x23, 0x10, - 0x75, 0x9c, 0x76, 0xb9, 0x1a, 0xe2, 0x24, 0x4c, 0xd4, 0x33, 0x47, 0xb7, - 0xa8, 0x57, 0x7f, 0x0a, 0x2d, 0x03, 0x89, 0x0b, 0x1e, 0x49, 0x52, 0x37, - 0xb8, 0x3a, 0xe9, 0x5d, 0x1f, 0xc0, 0xc8, 0x08, 0xf1, 0xe5, 0x85, 0xb8, - 0xe8, 0x5d, 0xbf, 0xf7, 0x82, 0xbb, 0x6c, 0x63, 0x7f, 0x15, 0x28, 0x0e, - 0x55, 0xcd, 0x81, 0x3c, 0xc3, 0x71, 0xd8, 0x37, 0x1c, 0x07, 0x4f, 0x42, - 0x2d, 0x2d, 0x73, 0xa7, 0x56, 0x2b, 0xbe, 0x62, 0xdc, 0x2d, 0xbe, 0xe8, - 0xcf, 0x8d, 0xf0, 0xc5, 0x62, 0xce, 0x1b, 0xb6, 0x0a, 0x10, 0x1c, 0x29, - 0x1d, 0x76, 0x58, 0x5b, 0x86, 0x06, 0xe2, 0xd3, 0xa1, 0x1b, 0x6c, 0x8d, - 0xa5, 0x9b, 0x5a, 0xf2, 0x4b, 0xa0, 0xcb, 0x05, 0x75, 0xd9, 0x20, 0xe3, - 0x1e, 0xc9, 0xd6, 0x2c, 0x07, 0x41, 0x90, 0x4c, 0x8a, 0x7b, 0x52, 0x28, - 0xea, 0x69, 0x72, 0x9b, 0x43, 0xdf, 0x92, 0x4c, 0x09, 0xa2, 0xfb, 0xe8, - 0x6f, 0x90, 0x67, 0x71, 0x49, 0xf8, 0xbc, 0xe0, 0xb8, 0xff, 0xb1, 0xff, - 0xb9, 0x46, 0xea, 0x11, 0xf8, 0x16, 0xac, 0x1c, 0x3e, 0x55, 0xb7, 0x87, - 0x9f, 0x46, 0xf6, 0xd0, 0xa3, 0xf6, 0xe2, 0x4d, 0xaf, 0xc9, 0x72, 0xe2, - 0xce, 0xa3, 0x82, 0x1c, 0x41, 0x76, 0xe0, 0xf4, 0xd0, 0x0d, 0x58, 0xf0, - 0x39, 0x8d, 0xfd, 0xbd, 0x6a, 0xbe, 0x10, 0x0f, 0x2f, 0x89, 0xf2, 0xbd, - 0x44, 0x3d, 0x2e, 0x1c, 0x5d, 0x37, 0xc0, 0x2f, 0xda, 0x9e, 0x92, 0x10, - 0xeb, 0xcc, 0x1b, 0xca, 0x05, 0x83, 0x4d, 0x1c, 0x13, 0x6b, 0xe8, 0xf8, - 0xba, 0x25, 0x2a, 0x17, 0x45, 0x42, 0x05, 0x17, 0xcd, 0x50, 0x44, 0xdd, - 0x76, 0x92, 0x8f, 0x7b, 0x9a, 0x14, 0xa4, 0x94, 0xcb, 0x5e, 0x1d, 0x32, - 0x32, 0xd8, 0x69, 0xc4, 0xee, 0x30, 0xbc, 0x7f, 0xd9, 0xa1, 0x91, 0xb8, - 0xb5, 0xf5, 0x4d, 0xda, 0x1d, 0xeb, 0x96, 0x6c, 0xbb, 0x75, 0x53, 0x9b, - 0x26, 0xda, 0xad, 0x36, 0xff, 0x41, 0xc7, 0xea, 0xa8, 0xbd, 0x9d, 0x75, - 0x55, 0x9d, 0x51, 0xb5, 0xd9, 0xd4, 0xc3, 0xc5, 0x73, 0xe9, 0x2f, 0x45, - 0x64, 0x66, 0xd5, 0xeb, 0x48, 0x4b, 0x22, 0xc6, 0x56, 0xf2, 0xb8, 0xf2, - 0x99, 0x31, 0x44, 0x87, 0xc6, 0x6b, 0x83, 0x76, 0xba, 0x27, 0x24, 0xbd, - 0x47, 0xff, 0x00, 0xe9, 0xad, 0x01, 0x3e, 0x0c, 0x4c, 0x00, 0x00 + 0x1f, 0x8b, 0x08, 0x08, 0x9a, 0x17, 0x93, 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 = 6251; +unsigned int index_htm_gz_len = 6261; From c35b72e7835426c15b833d35f09f2279a3699b2b Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Fri, 24 Apr 2020 10:58:10 +0200 Subject: [PATCH 089/111] Do not include gzipped version in the data folder by default. Leave it in the extras folder and change readme accordingly (plus some reformatingi of the readme file) --- .../examples/FSBrowser/data/edit/index.htm.gz | Bin 6261 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm.gz deleted file mode 100644 index de5abd1062cb121766b15497d6552c491bcea021..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6261 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 fM1-xRarv_#hz^ctf_C~R#J!_`xuIwdI7 Date: Fri, 24 Apr 2020 11:00:11 +0200 Subject: [PATCH 090/111] Gzipped index file not included in data/edit by default. It is now left in the extras folder. Readme file was updated accordingly (+ some reformating) --- .../examples/FSBrowser/extras/index.htm.gz | Bin 0 -> 6261 bytes .../examples/FSBrowser/extras/index_htm.h | 2 +- .../examples/FSBrowser/extras/reduce_index.sh | 7 ---- .../examples/FSBrowser/readme.md | 30 +++++++++--------- 4 files changed, 16 insertions(+), 23 deletions(-) create mode 100644 libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz 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 0000000000000000000000000000000000000000..734a8475495971ae1ab58bd6ab04c3e4272e145b GIT binary patch literal 6261 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 fM1-xRarv_#hz^ctf_C~R#J!_`xuIwdI7 Date: Sun, 26 Apr 2020 14:31:11 +0200 Subject: [PATCH 091/111] Reduce String clutter by reserving and concatenating elements one by one. --- .../examples/FSBrowser/FSBrowser.ino | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 5447bf5eb6..346dfa63c6 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -216,16 +216,26 @@ String getFileError(String path) { void handleStatus() { DBG_OUTPUT_PORT.println("handleStatus"); FSInfo fs_info; - String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; - + String json; + json.reserve(128); + + json = "{\"type\":\""; + json += fsName; + json += "\", \"isOk\":"; if (fsOK) { fileSystem->info(fs_info); - json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; + json += "\"true\", \"totalBytes\":\""; + json += fs_info.totalBytes; + json += "\", \"usedBytes\":\""; + json += fs_info.usedBytes; + json += "\""; } else { json += "\"false\""; } - json += String(",\"unsupportedFiles\":\"") + unsupportedFiles + "\""; - json += "}"; + json += ",\"unsupportedFiles\":\""; + json += unsupportedFiles; + json += "\"}"; + server.send(200, "application/json", json); } @@ -282,7 +292,8 @@ void handleFileList() { if (dir.isDirectory()) { output += "dir"; } else { - output += String("file\",\"size\":\"") + dir.fileSize(); + output += "file\",\"size\":\""; + output += dir.fileSize(); } output += "\",\"name\":\""; From cb8938f7e2210bd47eba0c2194e550776c143a88 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 15:27:02 +0200 Subject: [PATCH 092/111] Use clear() to reset String. --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 346dfa63c6..7f5e36ec48 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -260,7 +260,7 @@ void handleFileList() { DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = fileSystem->openDir(path); - path = String(); + path.clear(); // use HTTP/1.1 Chunked response to avoid building a huge temporary string if (!server.chunkedResponseModeStart(200, "text/json")) { From 83fcdc99593b2bd7132b4c1516ecbce49f3f7c7e Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 15:59:45 +0200 Subject: [PATCH 093/111] Avoid comparisons against empty String. --- .../ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 7f5e36ec48..b8630a4f37 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -357,7 +357,7 @@ bool handleFileRead(String path) { return the path of the closest parent still existing */ String lastExistingParent(String path) { - while (path != "" && !fileSystem->exists(path)) { + while (!path.isEmpty() && !fileSystem->exists(path)) { if (path.lastIndexOf("/") > 0) { path = path.substring(0, path.lastIndexOf("/")); } else { @@ -385,7 +385,7 @@ void handleFileCreate() { } String path = server.arg("path"); - if (path == "") { + if (path.isEmpty()) { return returnFail("MISSING PATH ARG"); } @@ -403,7 +403,7 @@ void handleFileCreate() { } String src = server.arg("src"); - if (src == "") { + if (src.isEmpty()) { // No source specified: creation DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path); if (path.endsWith("/")) { @@ -492,7 +492,7 @@ void handleFileDelete() { } String path = server.arg(0); - if (path == "") { + if(path.isEmpty()) { return returnFail("BAD ARGS"); } From fcfca4c5a5457ecd0548a5259d1634957474cd64 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 16:13:14 +0200 Subject: [PATCH 094/111] Use char instead of single-char String where possible. --- .../examples/FSBrowser/FSBrowser.ino | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index b8630a4f37..6008673d52 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -358,8 +358,8 @@ bool handleFileRead(String path) { */ String lastExistingParent(String path) { while (!path.isEmpty() && !fileSystem->exists(path)) { - if (path.lastIndexOf("/") > 0) { - path = path.substring(0, path.lastIndexOf("/")); + if (path.lastIndexOf('/') > 0) { + path = path.substring(0, path.lastIndexOf('/')); } else { path = String(); // No slash => the top folder does not exist } @@ -422,8 +422,8 @@ void handleFileCreate() { return returnFail("CREATE FAILED"); } } - if (path.lastIndexOf("/") > -1) { - path = path.substring(0, path.lastIndexOf("/")); + if (path.lastIndexOf('/') > -1) { + path = path.substring(0, path.lastIndexOf('/')); } returnOKWithMsg(path); } else { @@ -471,7 +471,7 @@ void deleteRecursive(String path) { Dir dir = fileSystem->openDir(path); while (dir.next()) { - deleteRecursive(path + "/" + dir.fileName()); + deleteRecursive(path + '/' + dir.fileName()); } // Then delete the folder itself @@ -564,11 +564,11 @@ void handleNotFound() { message += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); - message += "\n"; + message += '\n'; for (uint8_t i = 0; i < server.args(); i++) { - message += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n"; + message += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + '\n'; } - message += String("path=") + server.arg("path") + "\n"; + message += String("path=") + server.arg("path") + '\n'; DBG_OUTPUT_PORT.print(message); return returnNotFound(message); } @@ -596,7 +596,7 @@ void setup(void) { // SERIAL INIT DBG_OUTPUT_PORT.begin(115200); DBG_OUTPUT_PORT.setDebugOutput(true); - DBG_OUTPUT_PORT.print("\n"); + DBG_OUTPUT_PORT.print('\n'); //////////////////////////////// // FILESYSTEM INIT @@ -615,7 +615,7 @@ void setup(void) { 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"; + unsupportedFiles += error + fileInfo + '\n'; } } DBG_OUTPUT_PORT.println(); From 1115837904c3f1f84dc683d6cee620a6647d189e Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 16:43:04 +0200 Subject: [PATCH 095/111] Prefer direct logic over inverted. --- .../examples/FSBrowser/FSBrowser.ino | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 6008673d52..04ed4f8824 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -554,24 +554,29 @@ void handleNotFound() { if (!fsOK) { return returnFail("FS INIT ERROR"); } + String uri = urlDecode(server.uri()); - if (!handleFileRead(uri)) { - // Dump debug data - String message = "Error: File not found\n\n"; - message += "URI: "; - message += 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 += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + '\n'; - } - message += String("path=") + server.arg("path") + '\n'; - DBG_OUTPUT_PORT.print(message); - return returnNotFound(message); + + if (handleFileRead(uri)) { + return; + } + + // Dump debug data + String message = "Error: File not found\n\n"; + message += "URI: "; + message += 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 += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + '\n'; } + message += String("path=") + server.arg("path") + '\n'; + DBG_OUTPUT_PORT.print(message); + + return returnNotFound(message); } /* @@ -581,14 +586,17 @@ void handleNotFound() { Otherwise, fails with a 404 page with debug information */ void handleGetEdit() { - if (!handleFileRead("/edit/index.htm")) { + if (handleFileRead("/edit/index.htm")) { + return; + } + #ifdef INCLUDE_FALLBACK_INDEX_HTM - server.sendHeader("Content-Encoding", "gzip"); - server.send(200, "text/html", index_htm_gz, index_htm_gz_len); + server.sendHeader("Content-Encoding", "gzip"); + server.send(200, "text/html", index_htm_gz, index_htm_gz_len); #else - returnNotFound("FileNotFound"); + returnNotFound("FileNotFound"); #endif - } + } void setup(void) { From 14463dc84700d2adf1180bb83c31be283f825f7e Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 16:49:11 +0200 Subject: [PATCH 096/111] Rename returnBlah to replyBlah. --- .../examples/FSBrowser/FSBrowser.ino | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 04ed4f8824..46651843c1 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -90,19 +90,19 @@ File uploadFile; //////////////////////////////// // Utils to return HTTP codes, determine content-type and URLdecode params of GET method -void returnOK() { +void replyOK() { server.send(200, "text/plain", ""); } -void returnOKWithMsg(String msg) { +void replyOKWithMsg(String msg) { server.send(200, "text/plain", msg); } -void returnNotFound(String msg) { +void replyNotFound(String msg) { server.send(404, "text/plain", msg); } -void returnFail(String msg) { +void replyFail(String msg) { DBG_OUTPUT_PORT.println(msg); server.send(500, "text/plain", msg + "\r\n"); } @@ -246,16 +246,16 @@ void handleStatus() { */ void handleFileList() { if (!fsOK) { - return returnFail("FS INIT ERROR"); + return replyFail("FS INIT ERROR"); } if (!server.hasArg("dir")) { - return returnFail("BAD ARGS"); + return replyFail("BAD ARGS"); } String path = urlDecode(server.arg("dir")); if (path != "/" && !fileSystem->exists(path)) { - return returnFail("BAD PATH"); + return replyFail("BAD PATH"); } DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); @@ -320,7 +320,7 @@ void handleFileList() { bool handleFileRead(String path) { DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); if (!fsOK) { - returnFail("FS INIT ERROR"); + replyFail("FS INIT ERROR"); return true; } @@ -381,25 +381,25 @@ String lastExistingParent(String path) { */ void handleFileCreate() { if (!fsOK) { - return returnFail("FS INIT ERROR"); + return replyFail("FS INIT ERROR"); } String path = server.arg("path"); if (path.isEmpty()) { - return returnFail("MISSING PATH ARG"); + return replyFail("MISSING PATH ARG"); } #ifdef USE_SPIFFS if (getFileError(path).length() > 0) { - return returnFail("INVALID FILENAME"); + return replyFail("INVALID FILENAME"); } #endif if (path == "/") { - return returnFail("BAD PATH"); + return replyFail("BAD PATH"); } if (fileSystem->exists(path)) { - return returnFail("PATH FILE EXISTS"); + return replyFail("PATH FILE EXISTS"); } String src = server.arg("src"); @@ -410,7 +410,7 @@ void handleFileCreate() { // Create a folder path.remove(path.length() - 1); if (!fileSystem->mkdir(path)) { - return returnFail("MKDIR FAILED"); + return replyFail("MKDIR FAILED"); } } else { // Create a file @@ -419,20 +419,20 @@ void handleFileCreate() { file.write((const char *)0); file.close(); } else { - return returnFail("CREATE FAILED"); + return replyFail("CREATE FAILED"); } } if (path.lastIndexOf('/') > -1) { path = path.substring(0, path.lastIndexOf('/')); } - returnOKWithMsg(path); + replyOKWithMsg(path); } else { // Source specified: rename if (src == "/") { - return returnFail("BAD SRC"); + return replyFail("BAD SRC"); } if (!fileSystem->exists(src)) { - return returnFail("SRC FILE NOT FOUND"); + return replyFail("SRC FILE NOT FOUND"); } DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); @@ -444,9 +444,9 @@ void handleFileCreate() { src.remove(src.length() - 1); } if (!fileSystem->rename(src, path)) { - return returnFail("RENAME FAILED"); + return replyFail("RENAME FAILED"); } - returnOKWithMsg(lastExistingParent(src)); + replyOKWithMsg(lastExistingParent(src)); } } @@ -488,21 +488,21 @@ void deleteRecursive(String path) { */ void handleFileDelete() { if (!fsOK) { - return returnFail("FS INIT ERROR"); + return replyFail("FS INIT ERROR"); } String path = server.arg(0); if(path.isEmpty()) { - return returnFail("BAD ARGS"); + return replyFail("BAD ARGS"); } DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); if (path == "/" || !fileSystem->exists(path)) { - return returnFail("BAD PATH"); + return replyFail("BAD PATH"); } deleteRecursive(path); - returnOKWithMsg(lastExistingParent(path)); + replyOKWithMsg(lastExistingParent(path)); } /* @@ -510,7 +510,7 @@ void handleFileDelete() { */ void handleFileUpload() { if (!fsOK) { - return returnFail("FS INIT ERROR"); + return replyFail("FS INIT ERROR"); } if (server.uri() != "/edit") { return; @@ -525,14 +525,14 @@ void handleFileUpload() { DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); if (!uploadFile) { - return returnFail("CREATE FAILED"); + return replyFail("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 returnFail("WRITE FAILED"); + return replyFail("WRITE FAILED"); } } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); @@ -552,7 +552,7 @@ void handleFileUpload() { */ void handleNotFound() { if (!fsOK) { - return returnFail("FS INIT ERROR"); + return replyFail("FS INIT ERROR"); } String uri = urlDecode(server.uri()); @@ -576,7 +576,7 @@ void handleNotFound() { message += String("path=") + server.arg("path") + '\n'; DBG_OUTPUT_PORT.print(message); - return returnNotFound(message); + return replyNotFound(message); } /* @@ -594,7 +594,7 @@ void handleGetEdit() { server.sendHeader("Content-Encoding", "gzip"); server.send(200, "text/html", index_htm_gz, index_htm_gz_len); #else - returnNotFound("FileNotFound"); + replyNotFound("FileNotFound"); #endif } @@ -677,7 +677,7 @@ void setup(void) { // 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, returnOK, handleFileUpload); + server.on("/edit", HTTP_POST, replyOK, handleFileUpload); // Default handler for all URIs not defined above // Use it to read files from filesystem From cffeb7699091016562396c7bd78b8250531c6b49 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 16:51:10 +0200 Subject: [PATCH 097/111] Renamed h2int to hexDigitToInt --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 46651843c1..cff3a99152 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -150,7 +150,7 @@ String getContentType(String filename) { return "text/plain"; } -unsigned char h2int(char c) { +unsigned char hexDigitToInt(char c) { if (c >= '0' && c <= '9') { return ((unsigned char)c - '0'); } @@ -177,7 +177,7 @@ String urlDecode(String str) { code0 = str.charAt(i); i++; code1 = str.charAt(i); - c = (h2int(code0) << 4) | h2int(code1); + c = (hexDigitToInt(code0) << 4) | hexDigitToInt(code1); decodedString += c; } else { decodedString += c; From c8a2181bc9bffc9fbd350a32afd3abbb2e4dc837 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 17:03:17 +0200 Subject: [PATCH 098/111] Renamed getFileError() to checkForUnsupportedPath(), to avoid confusion with a getter. --- .../examples/FSBrowser/FSBrowser.ino | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index cff3a99152..6a1430f654 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -187,24 +187,25 @@ String urlDecode(String str) { return decodedString; } - +#ifdef USE_SPIFFS /* - Checks filename for unsupported combinations + 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 getFileError(String path) { +String checkForUnsupportedPath(String filename) { String error = String(); - if (!path.startsWith("/")) { + if (!filename.startsWith("/")) { error += "!NO_LEADING_SLASH! "; } - if (path.indexOf("//") != -1) { + if (filename.indexOf("//") != -1) { error += "!DOUBLE_SLASH! "; } - if (path.endsWith("/")) { + if (filename.endsWith("/")) { error += "!TRAILING_SLASH! "; } return error; } +#endif //////////////////////////////// @@ -273,7 +274,7 @@ void handleFileList() { output.reserve(64); while (dir.next()) { #ifdef USE_SPIFFS - String error = getFileError(dir.fileName()); + String error = checkForUnsupportedPath(dir.fileName()); if (error.length() > 0) { DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName()); continue; @@ -390,7 +391,7 @@ void handleFileCreate() { } #ifdef USE_SPIFFS - if (getFileError(path).length() > 0) { + if (checkForUnsupportedPath(path).length() > 0) { return replyFail("INVALID FILENAME"); } #endif @@ -619,7 +620,7 @@ void setup(void) { Dir dir = fileSystem->openDir(""); DBG_OUTPUT_PORT.println("List of files at root of filesystem:"); while (dir.next()) { - String error = getFileError(dir.fileName()); + 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) { From a740ec204ab527a5da49d5f5da83897bdeab3264 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 17:18:55 +0200 Subject: [PATCH 099/111] Misc improvements. --- libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 5868c7532a..369b773ea2 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -24,16 +24,16 @@ If you want to use the browser on a an existing filesystem or don't want to perf `#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 cost of a larger compiled file. -To regenerate the files required for the previous options or understand how they are build, see the `reduce_index.sh` script located in the `extras` subfolder. +To regenerate the files required for the previous options or understand how they are built, see the `reduce_index.sh` script located in the `extras` subfolder. ## Dependency -The html page uses the [ace.js](https://ace.c9.io/) 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 (and dependencies) to the `edit` subfolder of the ESP filesystem. +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, as well as optional themes, plugins etc. according to your needs (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 the default pin (4), enable the `fileSystemConfig.setCSPin(chipSelectPin);` line, specifying the GPIO the CS pin is connected to +- 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. @@ -114,6 +114,7 @@ These tests are a checklist of operations to verify the FSBrowser behaviour. - 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/ From d6eb21dd0681a903b540b50b80e32e9f52ff7f66 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 20:40:51 +0200 Subject: [PATCH 100/111] Added comments about mandatory rebuilding gz and h files in case of update to index.htm. --- .../ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 3 +++ .../examples/FSBrowser/data/edit/index.htm | 6 ++++++ .../ESP8266WebServer/examples/FSBrowser/extras/index_htm.h | 3 +++ .../examples/FSBrowser/extras/reduce_index.sh | 7 +++++-- libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 2 +- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 6a1430f654..841b534fc4 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -43,6 +43,9 @@ #include #include +// Important : 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. #ifdef INCLUDE_FALLBACK_INDEX_HTM #include "extras/index_htm.h" #endif diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm index 532834c5ab..31c9613a0f 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/FSBrowser/data/edit/index.htm @@ -1,4 +1,10 @@ + File manager diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h index ea8406d1e3..a4d2d5b4c7 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h @@ -1,3 +1,6 @@ +// This file is an embeddable version of the gzipped index.htm file. +// Please rerun the `reduce_index.sh` script located in the `extras` subfolder and recompile +// the sketch after each change to the `index.html` file. unsigned char index_htm_gz[] = { 0x1f, 0x8b, 0x08, 0x08, 0x04, 0xa3, 0xa2, 0x5e, 0x00, 0x03, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x00, 0xdc, 0x3b, 0x89, 0x7b, diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh index d768f5ec54..7d21bb911f 100755 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -45,10 +45,13 @@ then exit -1 fi -xxd -i index.htm.gz > index_htm.h +echo 'This file is an embeddable version of the gzipped index.htm file.' > index_htm.h +echo 'Please rerun the `reduce_index.sh` script located in the `extras` subfolder and recompile' >> index_htm.h +echo '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 for index.htm" + echo "Error creating include file from index.htm.gz" exit -1 fi diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 369b773ea2..fb208857c6 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -24,7 +24,7 @@ If you want to use the browser on a an existing filesystem or don't want to perf `#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 cost of a larger compiled file. -To regenerate the files required for the previous options or understand how they are built, see the `reduce_index.sh` script located in the `extras` subfolder. +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. From cb7b0d1b666d8a6664b5469117add51c3d063b46 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 21:25:36 +0200 Subject: [PATCH 101/111] Addressed a few comments. --- libraries/ESP8266WebServer/examples/FSBrowser/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index fb208857c6..0d8c0882a4 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -3,10 +3,10 @@ ## What is this sketch about ? This example is a FileSystem Browser for the ESP8266 using http requests and a html/javascript frontend, -working for both SPIFFS, LittleFS and SDFS. +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 ? +## 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 @@ -22,7 +22,7 @@ If you want to use the browser on a an existing filesystem or don't want to perf `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 cost of a larger compiled file. +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. From c4b4ec0b22917bedf84748d4e4729b51934ab976 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 23:07:06 +0200 Subject: [PATCH 102/111] Improve replies: bad requests vs server error --- .../examples/FSBrowser/FSBrowser.ino | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 841b534fc4..558bac100b 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -43,9 +43,6 @@ #include #include -// Important : 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. #ifdef INCLUDE_FALLBACK_INDEX_HTM #include "extras/index_htm.h" #endif @@ -105,7 +102,12 @@ void replyNotFound(String msg) { server.send(404, "text/plain", msg); } -void replyFail(String msg) { +void replyBadRequest(String msg) { + DBG_OUTPUT_PORT.println(msg); + server.send(400, "text/plain", msg + "\r\n"); +} + +void replyServerError(String msg) { DBG_OUTPUT_PORT.println(msg); server.send(500, "text/plain", msg + "\r\n"); } @@ -250,16 +252,16 @@ void handleStatus() { */ void handleFileList() { if (!fsOK) { - return replyFail("FS INIT ERROR"); + return replyServerError("FS INIT ERROR"); } if (!server.hasArg("dir")) { - return replyFail("BAD ARGS"); + return replyBadRequest("BAD ARGS"); } String path = urlDecode(server.arg("dir")); if (path != "/" && !fileSystem->exists(path)) { - return replyFail("BAD PATH"); + return replyBadRequest("BAD PATH"); } DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); @@ -324,7 +326,7 @@ void handleFileList() { bool handleFileRead(String path) { DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); if (!fsOK) { - replyFail("FS INIT ERROR"); + replyServerError("FS INIT ERROR"); return true; } @@ -385,25 +387,25 @@ String lastExistingParent(String path) { */ void handleFileCreate() { if (!fsOK) { - return replyFail("FS INIT ERROR"); + return replyServerError("FS INIT ERROR"); } String path = server.arg("path"); if (path.isEmpty()) { - return replyFail("MISSING PATH ARG"); + return replyBadRequest("MISSING PATH ARG"); } #ifdef USE_SPIFFS if (checkForUnsupportedPath(path).length() > 0) { - return replyFail("INVALID FILENAME"); + return replyServerError("INVALID FILENAME"); } #endif if (path == "/") { - return replyFail("BAD PATH"); + return replyBadRequest("BAD PATH"); } if (fileSystem->exists(path)) { - return replyFail("PATH FILE EXISTS"); + return replyBadRequest("PATH FILE EXISTS"); } String src = server.arg("src"); @@ -414,7 +416,7 @@ void handleFileCreate() { // Create a folder path.remove(path.length() - 1); if (!fileSystem->mkdir(path)) { - return replyFail("MKDIR FAILED"); + return replyServerError("MKDIR FAILED"); } } else { // Create a file @@ -423,7 +425,7 @@ void handleFileCreate() { file.write((const char *)0); file.close(); } else { - return replyFail("CREATE FAILED"); + return replyServerError("CREATE FAILED"); } } if (path.lastIndexOf('/') > -1) { @@ -433,10 +435,10 @@ void handleFileCreate() { } else { // Source specified: rename if (src == "/") { - return replyFail("BAD SRC"); + return replyBadRequest("BAD SRC"); } if (!fileSystem->exists(src)) { - return replyFail("SRC FILE NOT FOUND"); + return replyBadRequest("SRC FILE NOT FOUND"); } DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); @@ -448,7 +450,7 @@ void handleFileCreate() { src.remove(src.length() - 1); } if (!fileSystem->rename(src, path)) { - return replyFail("RENAME FAILED"); + return replyServerError("RENAME FAILED"); } replyOKWithMsg(lastExistingParent(src)); } @@ -492,17 +494,17 @@ void deleteRecursive(String path) { */ void handleFileDelete() { if (!fsOK) { - return replyFail("FS INIT ERROR"); + return replyServerError("FS INIT ERROR"); } String path = server.arg(0); - if(path.isEmpty()) { - return replyFail("BAD ARGS"); + if(path.isEmpty() || path == "/") { + return replyBadRequest("BAD PATH"); } DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); - if (path == "/" || !fileSystem->exists(path)) { - return replyFail("BAD PATH"); + if (!fileSystem->exists(path)) { + return replyNotFound("FileNotFound"); } deleteRecursive(path); @@ -514,7 +516,7 @@ void handleFileDelete() { */ void handleFileUpload() { if (!fsOK) { - return replyFail("FS INIT ERROR"); + return replyServerError("FS INIT ERROR"); } if (server.uri() != "/edit") { return; @@ -529,14 +531,14 @@ void handleFileUpload() { DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); if (!uploadFile) { - return replyFail("CREATE FAILED"); + return replyServerError("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 replyFail("WRITE FAILED"); + return replyServerError("WRITE FAILED"); } } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); @@ -556,7 +558,7 @@ void handleFileUpload() { */ void handleNotFound() { if (!fsOK) { - return replyFail("FS INIT ERROR"); + return replyServerError("FS INIT ERROR"); } String uri = urlDecode(server.uri()); From 6f6ed1d72d89a8895074a3b8b212d531d7cb0ce7 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 23:13:02 +0200 Subject: [PATCH 103/111] (reformated) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 558bac100b..1b92ca14db 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -224,7 +224,7 @@ void handleStatus() { FSInfo fs_info; String json; json.reserve(128); - + json = "{\"type\":\""; json += fsName; json += "\", \"isOk\":"; @@ -241,7 +241,7 @@ void handleStatus() { json += ",\"unsupportedFiles\":\""; json += unsupportedFiles; json += "\"}"; - + server.send(200, "application/json", json); } @@ -498,7 +498,7 @@ void handleFileDelete() { } String path = server.arg(0); - if(path.isEmpty() || path == "/") { + if (path.isEmpty() || path == "/") { return replyBadRequest("BAD PATH"); } From 998de0190afddda72333b0286edd41c5f4a17cf2 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 26 Apr 2020 23:28:40 +0200 Subject: [PATCH 104/111] Reduce clutter by reserving String size beforehand. --- .../examples/FSBrowser/FSBrowser.ino | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 1b92ca14db..0888618fe3 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -568,7 +568,9 @@ void handleNotFound() { } // Dump debug data - String message = "Error: File not found\n\n"; + String message; + message.reserve(100); + message = "Error: File not found\n\n"; message += "URI: "; message += uri; message += "\nMethod: "; @@ -577,9 +579,15 @@ void handleNotFound() { message += server.args(); message += '\n'; for (uint8_t i = 0; i < server.args(); i++) { - message += String(" NAME:") + server.argName(i) + "\n VALUE:" + server.arg(i) + '\n'; - } - message += String("path=") + server.arg("path") + '\n'; + message += " NAME:"; + message += server.argName(i); + message += "\n VALUE:"; + message += server.arg(i); + message += '\n'; + } + message += "path="; + message += server.arg("path"); + message += '\n'; DBG_OUTPUT_PORT.print(message); return replyNotFound(message); From b8b7d85a66c04417e5682389a558009dd9e1137a Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Mon, 27 Apr 2020 02:22:20 +0200 Subject: [PATCH 105/111] Moved most Strings of more than 10 chars to flash. --- .../examples/FSBrowser/FSBrowser.ino | 96 ++++++++++--------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 0888618fe3..7c12301506 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -86,30 +86,33 @@ 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, determine content-type and URLdecode params of GET method void replyOK() { - server.send(200, "text/plain", ""); + server.send(200, FPSTR(TEXT_PLAIN), ""); } void replyOKWithMsg(String msg) { - server.send(200, "text/plain", msg); + server.send(200, FPSTR(TEXT_PLAIN), msg); } void replyNotFound(String msg) { - server.send(404, "text/plain", msg); + server.send(404, FPSTR(TEXT_PLAIN), msg); } void replyBadRequest(String msg) { DBG_OUTPUT_PORT.println(msg); - server.send(400, "text/plain", msg + "\r\n"); + server.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n"); } void replyServerError(String msg) { DBG_OUTPUT_PORT.println(msg); - server.send(500, "text/plain", msg + "\r\n"); + server.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n"); } String getContentType(String filename) { @@ -152,7 +155,7 @@ String getContentType(String filename) { if (filename.endsWith(".gz")) { return "application/x-gzip"; } - return "text/plain"; + return FPSTR(TEXT_PLAIN); } unsigned char hexDigitToInt(char c) { @@ -200,13 +203,13 @@ String urlDecode(String str) { String checkForUnsupportedPath(String filename) { String error = String(); if (!filename.startsWith("/")) { - error += "!NO_LEADING_SLASH! "; + error += F("!NO_LEADING_SLASH! "); } if (filename.indexOf("//") != -1) { - error += "!DOUBLE_SLASH! "; + error += F("!DOUBLE_SLASH! "); } if (filename.endsWith("/")) { - error += "!TRAILING_SLASH! "; + error += F("!TRAILING_SLASH! "); } return error; } @@ -230,15 +233,15 @@ void handleStatus() { json += "\", \"isOk\":"; if (fsOK) { fileSystem->info(fs_info); - json += "\"true\", \"totalBytes\":\""; + json += F("\"true\", \"totalBytes\":\""); json += fs_info.totalBytes; - json += "\", \"usedBytes\":\""; + json += F("\", \"usedBytes\":\""); json += fs_info.usedBytes; json += "\""; } else { json += "\"false\""; } - json += ",\"unsupportedFiles\":\""; + json += F(",\"unsupportedFiles\":\""); json += unsupportedFiles; json += "\"}"; @@ -252,11 +255,11 @@ void handleStatus() { */ void handleFileList() { if (!fsOK) { - return replyServerError("FS INIT ERROR"); + return replyServerError(FPSTR(FS_INIT_ERROR)); } if (!server.hasArg("dir")) { - return replyBadRequest("BAD ARGS"); + return replyBadRequest(F("DIR ARG MISSING")); } String path = urlDecode(server.arg("dir")); @@ -270,7 +273,7 @@ void handleFileList() { // 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; } @@ -298,11 +301,11 @@ void handleFileList() { if (dir.isDirectory()) { output += "dir"; } else { - output += "file\",\"size\":\""; + output += F("file\",\"size\":\""); output += dir.fileSize(); } - output += "\",\"name\":\""; + output += F("\",\"name\":\""); // Always return names without leading "/" if (dir.fileName()[0] == '/') { output += &(dir.fileName()[1]); @@ -326,7 +329,7 @@ void handleFileList() { bool handleFileRead(String path) { DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); if (!fsOK) { - replyServerError("FS INIT ERROR"); + replyServerError(FPSTR(FS_INIT_ERROR)); return true; } @@ -336,7 +339,7 @@ bool handleFileRead(String path) { String contentType; if (server.hasArg("download")) { - contentType = "application/octet-stream"; + contentType = F("application/octet-stream"); } else { contentType = getContentType(path); } @@ -387,17 +390,17 @@ String lastExistingParent(String path) { */ void handleFileCreate() { if (!fsOK) { - return replyServerError("FS INIT ERROR"); + return replyServerError(FPSTR(FS_INIT_ERROR)); } String path = server.arg("path"); if (path.isEmpty()) { - return replyBadRequest("MISSING PATH ARG"); + return replyBadRequest(F("PATH ARG MISSING")); } #ifdef USE_SPIFFS if (checkForUnsupportedPath(path).length() > 0) { - return replyServerError("INVALID FILENAME"); + return replyServerError(F("INVALID FILENAME")); } #endif @@ -405,7 +408,7 @@ void handleFileCreate() { return replyBadRequest("BAD PATH"); } if (fileSystem->exists(path)) { - return replyBadRequest("PATH FILE EXISTS"); + return replyBadRequest(F("PATH FILE EXISTS")); } String src = server.arg("src"); @@ -416,7 +419,7 @@ void handleFileCreate() { // Create a folder path.remove(path.length() - 1); if (!fileSystem->mkdir(path)) { - return replyServerError("MKDIR FAILED"); + return replyServerError(F("MKDIR FAILED")); } } else { // Create a file @@ -425,7 +428,7 @@ void handleFileCreate() { file.write((const char *)0); file.close(); } else { - return replyServerError("CREATE FAILED"); + return replyServerError(F("CREATE FAILED")); } } if (path.lastIndexOf('/') > -1) { @@ -438,7 +441,7 @@ void handleFileCreate() { return replyBadRequest("BAD SRC"); } if (!fileSystem->exists(src)) { - return replyBadRequest("SRC FILE NOT FOUND"); + return replyBadRequest(F("SRC FILE NOT FOUND")); } DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); @@ -450,7 +453,7 @@ void handleFileCreate() { src.remove(src.length() - 1); } if (!fileSystem->rename(src, path)) { - return replyServerError("RENAME FAILED"); + return replyServerError(F("RENAME FAILED")); } replyOKWithMsg(lastExistingParent(src)); } @@ -494,7 +497,7 @@ void deleteRecursive(String path) { */ void handleFileDelete() { if (!fsOK) { - return replyServerError("FS INIT ERROR"); + return replyServerError(FPSTR(FS_INIT_ERROR)); } String path = server.arg(0); @@ -504,7 +507,7 @@ void handleFileDelete() { DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); if (!fileSystem->exists(path)) { - return replyNotFound("FileNotFound"); + return replyNotFound(FPSTR(FILE_NOT_FOUND)); } deleteRecursive(path); @@ -516,7 +519,7 @@ void handleFileDelete() { */ void handleFileUpload() { if (!fsOK) { - return replyServerError("FS INIT ERROR"); + return replyServerError(FPSTR(FS_INIT_ERROR)); } if (server.uri() != "/edit") { return; @@ -531,14 +534,14 @@ void handleFileUpload() { DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); if (!uploadFile) { - return replyServerError("CREATE FAILED"); + 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("WRITE FAILED"); + return replyServerError(F("WRITE FAILED")); } } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); @@ -558,7 +561,7 @@ void handleFileUpload() { */ void handleNotFound() { if (!fsOK) { - return replyServerError("FS INIT ERROR"); + return replyServerError(FPSTR(FS_INIT_ERROR)); } String uri = urlDecode(server.uri()); @@ -570,18 +573,17 @@ void handleNotFound() { // Dump debug data String message; message.reserve(100); - message = "Error: File not found\n\n"; - message += "URI: "; + message = F("Error: File not found\n\nURI: "); message += uri; - message += "\nMethod: "; + message += F("\nMethod: "); message += (server.method() == HTTP_GET) ? "GET" : "POST"; - message += "\nArguments: "; + message += F("\nArguments: "); message += server.args(); message += '\n'; for (uint8_t i = 0; i < server.args(); i++) { - message += " NAME:"; + message += F(" NAME:"); message += server.argName(i); - message += "\n VALUE:"; + message += F("\n VALUE:"); message += server.arg(i); message += '\n'; } @@ -600,15 +602,15 @@ void handleNotFound() { Otherwise, fails with a 404 page with debug information */ void handleGetEdit() { - if (handleFileRead("/edit/index.htm")) { + if (handleFileRead(F("/edit/index.htm"))) { return; } #ifdef INCLUDE_FALLBACK_INDEX_HTM - server.sendHeader("Content-Encoding", "gzip"); + server.sendHeader(F("Content-Encoding"), "gzip"); server.send(200, "text/html", index_htm_gz, index_htm_gz_len); #else - replyNotFound("FileNotFound"); + replyNotFound(FPSTR(FILE_NOT_FOUND)); #endif } @@ -626,12 +628,12 @@ void setup(void) { fileSystemConfig.setAutoFormat(false); fileSystem->setConfig(fileSystemConfig); fsOK = fileSystem->begin(); - DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "Filesystem init failed!"); + 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("List of files at root of filesystem:"); + 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)"); @@ -658,16 +660,16 @@ void setup(void) { 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 INIT if (MDNS.begin(host)) { MDNS.addService("http", "tcp", 80); - DBG_OUTPUT_PORT.print("Open http://"); + DBG_OUTPUT_PORT.print(F("Open http://")); DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local/edit to open the FileSystem Browser"); + DBG_OUTPUT_PORT.println(F(".local/edit to open the FileSystem Browser")); } //////////////////////////////// From 2aabe7084eb5e7fbd1ee0bc01e95559484b7848d Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 29 Apr 2020 01:40:41 +0200 Subject: [PATCH 106/111] Use lib version of urlDecode() instead of a local one, and only call it when required. --- .../examples/FSBrowser/FSBrowser.ino | 43 ++----------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 7c12301506..e50130d052 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -91,7 +91,7 @@ static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR"; static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound"; //////////////////////////////// -// Utils to return HTTP codes, determine content-type and URLdecode params of GET method +// Utils to return HTTP codes, and determine content-type void replyOK() { server.send(200, FPSTR(TEXT_PLAIN), ""); @@ -158,43 +158,6 @@ String getContentType(String filename) { return FPSTR(TEXT_PLAIN); } -unsigned char hexDigitToInt(char c) { - if (c >= '0' && c <= '9') { - return ((unsigned char)c - '0'); - } - if (c >= 'a' && c <= 'f') { - return ((unsigned char)c - 'a' + 10); - } - if (c >= 'A' && c <= 'F') { - return ((unsigned char)c - 'A' + 10); - } - return (0); -} - -String urlDecode(String str) { - String decodedString = ""; - char c; - char code0; - char code1; - for (unsigned int i = 0; i < str.length(); i++) { - c = str.charAt(i); - if (c == '+') { - decodedString += ' '; - } else if (c == '%') { - i++; - code0 = str.charAt(i); - i++; - code1 = str.charAt(i); - c = (hexDigitToInt(code0) << 4) | hexDigitToInt(code1); - decodedString += c; - } else { - decodedString += c; - } - } - - return decodedString; -} - #ifdef USE_SPIFFS /* Checks filename for character combinations that are not supported by FSBrowser (alhtough valid on SPIFFS). @@ -262,7 +225,7 @@ void handleFileList() { return replyBadRequest(F("DIR ARG MISSING")); } - String path = urlDecode(server.arg("dir")); + String path = server.arg("dir"); if (path != "/" && !fileSystem->exists(path)) { return replyBadRequest("BAD PATH"); } @@ -564,7 +527,7 @@ void handleNotFound() { return replyServerError(FPSTR(FS_INIT_ERROR)); } - String uri = urlDecode(server.uri()); + String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks if (handleFileRead(uri)) { return; From 5c86ac0d402c4366d2fb0197b93bdd48cf491683 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 29 Apr 2020 01:46:47 +0200 Subject: [PATCH 107/111] Added a comment about the dangers of recursion on embedded devices. --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index e50130d052..6a94744a80 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -427,6 +427,10 @@ void handleFileCreate() { 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"); From 50ea73581fcdfcf3fbfaeb12365e21ebf31124e2 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 29 Apr 2020 01:52:32 +0200 Subject: [PATCH 108/111] Added a more explicit warning in the .h header comment. --- .../examples/FSBrowser/extras/reduce_index.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh index 7d21bb911f..e133a84180 100755 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -45,9 +45,10 @@ then exit -1 fi -echo 'This file is an embeddable version of the gzipped index.htm file.' > index_htm.h -echo 'Please rerun the `reduce_index.sh` script located in the `extras` subfolder and recompile' >> index_htm.h -echo 'the sketch after each change to the `index.html` file.' >> index_htm.h +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 From e53c6348b74ea1805705d602e7927e11e88f3673 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 29 Apr 2020 02:09:45 +0200 Subject: [PATCH 109/111] Added a typical set of required files to load ace editor from the ESP. --- .../examples/FSBrowser/readme.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md index 0d8c0882a4..c3ebb82a3e 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/readme.md +++ b/libraries/ESP8266WebServer/examples/FSBrowser/readme.md @@ -28,7 +28,23 @@ If you use the gzipped or `INCLUDE_FALLBACK_INDEX_HTM` options, please remember ## 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, as well as optional themes, plugins etc. according to your needs (see https://github.com/ajaxorg/ace-builds for a full list). + +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 From cc32ac8067f428977c407ca79d42bcb7adc00b27 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 29 Apr 2020 02:23:56 +0200 Subject: [PATCH 110/111] (reformated) --- libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 6a94744a80..caf8634926 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -427,7 +427,7 @@ void handleFileCreate() { 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. From d6db83d78bb42ec4125d506fffdfa8ca936fb183 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Wed, 29 Apr 2020 02:28:05 +0200 Subject: [PATCH 111/111] More explicit warning at the beginning of the .h version. --- .../examples/FSBrowser/extras/index.htm.gz | Bin 6261 -> 6261 bytes .../examples/FSBrowser/extras/index_htm.h | 7 ++++--- .../examples/FSBrowser/extras/reduce_index.sh | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index.htm.gz index 734a8475495971ae1ab58bd6ab04c3e4272e145b..34797b0589617ca9bfd7e2dec3cabe824efe45c0 100644 GIT binary patch delta 16 Xcmexr@YR4_zMF$%+Q}6g+4Cd-If@2R delta 16 Xcmexr@YR4_zMF%CW$~ho?0FIZG>Qe2 diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h index a4d2d5b4c7..a90d290535 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/index_htm.h @@ -1,8 +1,9 @@ +// WARNING: Auto-generated file. Please do not modify by hand. // This file is an embeddable version of the gzipped index.htm file. -// Please rerun the `reduce_index.sh` script located in the `extras` subfolder and recompile -// the sketch after each change to the `index.html` 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, 0x04, 0xa3, 0xa2, 0x5e, 0x00, 0x03, 0x69, 0x6e, + 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, diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh index e133a84180..969f2a768d 100755 --- a/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh +++ b/libraries/ESP8266WebServer/examples/FSBrowser/extras/reduce_index.sh @@ -45,10 +45,10 @@ then 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 +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

B@9DvDS^RdU0$mGZ3Tb>(#K!(5K9wq_gCdK?@9wZzj_-ELct zquM92Y)$MR->CPAc-56Oh-v8Z^`o4^ zchLebceRx9b1mZU;o4aHP1l88=5yoQV}DoteP1a8M6tevf>P~2OwZmi>pxs+V}<7? z5;L{}x)Gfl;Swv%gBb8DRwFi+yuS4Y&$TEMYs`q_*_io&k60{LykQrjzp*YJA7`nI zoeYk1QK49PJT5mYk|{js%p&~Gc*yeU$kS<7fK=%eDlgU=XooPVWiSg5R;i23SxM2m zgW+rOtFnn2|7r^OKzJQXSLxnA0Jk{2Eaw_4&!2#7%6_R@|r`GX@T#eA2#5q#asEXbF(3{E2wOP^^}>@hQf3 zP0g1Lh0LIwJ8m406>1t`i8aCE(1mjJ#mZ6vNP(meagG0Q2~V}zwggtb#|q6&Bt}{S zB6AbC#0vEww%IA_63fEp8)@NFtjVL+A-OuP!p6cA9Hk6kOF3&Zs2e4vRTzAs>aYtp zj>rR>4eB;Ml4RIAh^jCGi3Ak^)g_6-&q->r7^2`qvA`d;yAS7wb7E{sLmqu?*;bg# zeIH6izgsyV_*=FkVuxyngxV4>DRBx;{r=08Dc5vE$~>xqC{*%r=RY^xl^9mKG{g<6 ziYTt>jKf}k{A^Ok!#0f&#X~+TEKhs@%}CbFMr|xfO;ZUWhLNG>EeDtX31;vhhcjVPmxcAe+m#thMdz(&MP2VuZP>x+~o{>P`*_3 zDHfizw!qnmOsi1gL+tY(_KHwdU%}|y9`fh_1m!01h=pwIk%;b9b#1IrG)An__oTPo zP~Pr0jB3Twh9?#`cJRZ`gP)QIP*HbQNv#6amo$wGVGGmqSH!NQM@p~@KPnpsaEvAa z55XuG#gcLl7UcQX3GAWe;MIfAS6Q?M+#9|QN9vL9px%A2Su*4U69fY?yN40-5t7au-l^HZe*6IuG1 zQWhSkpxAHFo`!g>4kN%Xkhej};a8=3l^4Ff;{B=8h+@gjN?Ke`jyd+mSIQ+3$2&nH z<&EBed;ssK;)OH#Mf6wHQvi=xkr@A7UtNeZ5Zh20jrJ8J#(v4COM$rwe8g%1jVe|x z8Y9*iD)!C_XVp5p-!Li_OP+B0aAdhcndFO9fTbdR#~j599z->Ps*)SdBs9&lbJcc9sark-u|LvVia>6hWMA>nk zR8f^GPLb>7MC`I-pG#!NBK>+sj_p8#!J=49D>nKJqtB={vcZJhACDCRE=Gw5FsmW0 zTn&h#dWV~GV8tOx>)OFdb+(k`q`D2ABO&?rk`1>7+#}^u72G5uxC4W!;?4HP7$^j; zCDs(5qpze6SU{{P&T0eCMXZ}K^H?%UE{oFOzz!JPbTQFuv z%qt#kVmhR_R(i9k z$Awu4ymD92tzu?INolcO=YSIKL zR~5dQo8mO$7Gj-4J&$z>Q;)^eU=qqq?D>Z$M97iBS-UY7vz4g_2M1tWfvW@?m_^*0 zq>Fi&-@LG6f*THsi&fqO!XUhtUc)2hcM5e-YUTwqgL*L2+)^QDG;tB9h&)y{&FxUT zm{GuuaV!vX-Q5Xc1t%azE922%<~M4*oIo$d?N*3}e`a8aWtZC~I?0{NVQIK{#8kkb ziNrDLKJU5SGsGOKxW8H~odmd|h__#Lg zHlxJ)Veh-YkHD7eo(Ez3kY07HrFRFkPZ>>uJHl#;hOplp#>s!ej99(}S|}9mh8dN_ z`u&{nWh{}s0bj?AbVuioOz@bxI*wg$vmBRfxBkLviUus}db4!$t57Jci6T~^&;doP oLZJhSScO6d6tN10)p0s~10YZm`I2G^k^lez07*qoM6N<$f;sq?p8x;= literal 177869 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 z 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 Nla*4EtQ9v7`9ICfW Date: Sat, 14 Mar 2020 15:36:46 +0100 Subject: [PATCH 041/111] Return the filesystem time in the status object + Massive cleanup/merge/align with some code from the FSBrowser example and misc refactorings --- .../WebFileManager/WebFileManager.ino | 521 +++++++++++------- 1 file changed, 323 insertions(+), 198 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 06a74c534e..cdf5148f19 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -18,9 +18,18 @@ * - Filled filename box in header with the name of the currently selected file * * TODO: + * - Show file names as they come from server (with "/" for SPIFFS, without for others) and adapt the queries such as "delete" by only adding the leading "/" if not already present + * - Test fileSystem status in web page and indicate if ERROR + * - Show FS type in status + * - Cleanup (look for TODO below) * - Fix Editor (add Save/Discard buttons ?) * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? - * - Test on SPIFFS + * + * TEST: + * - Thoroughly on SPIFFS + SDFS + LittleFS : + * - List / Edit / View image / Download / Create file / Upload file / Mkdir at root + * - List / Edit / View image / Download / Create file / Upload file / Mkdir in subfolder + * (after PR): reported free space */ /* @@ -54,7 +63,7 @@ - for SDFS, copy that 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 - Once the data and sketch have been uploaded, access the editor by going to http://espfilemanager.local/edit + Once the data and sketch have been uploaded, access the editor by going to http://webfilemanager.local/edit Notes: - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line just before "FILESYSTEM.begin()", @@ -66,7 +75,7 @@ */ -////////////////// +//////////////////////////////// // Select the FileSystem by uncommenting one of the lines below @@ -74,7 +83,7 @@ //#define USE_SPIFFS //#define USE_LITTLEFS -////////////////// +//////////////////////////////// #include #include @@ -85,12 +94,15 @@ #if defined USE_SDFS #define FILESYSTEM SDFS #include + const char* fsName = "SDFS"; #elif defined USE_SPIFFS #define FILESYSTEM SPIFFS #include + const char* fsName = "SPIFFS"; #elif defined USE_LITTLEFS #define FILESYSTEM LittleFS #include + const char* fsName = "LittleFS"; #else #error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. #endif @@ -108,293 +120,406 @@ const char* host = "webfilemanager"; ESP8266WebServer server(80); -static bool hasSD = false; +static bool fsOK; File uploadFile; +//////////////////////////////// +// Utils to return HTTP codes and determine content-type + void returnOK() { server.send(200, "text/plain", ""); } +void returnNotFound(String msg) { + server.send(404, "text/plain", msg); +} + void returnFail(String msg) { + DBG_OUTPUT_PORT.println(msg); server.send(500, "text/plain", msg + "\r\n"); } -bool loadFromSdCard(String path) { - String dataType = "text/plain"; - if (path.endsWith("/")) { - path += "index.htm"; +String getContentType(String filename) { + if (filename.endsWith(".htm")) { + return "text/html"; + } else if (filename.endsWith(".html")) { + return "text/html"; + } else if (filename.endsWith(".css")) { + return "text/css"; + } else if (filename.endsWith(".js")) { + return "application/javascript"; + } else if (filename.endsWith(".png")) { + return "image/png"; + } else if (filename.endsWith(".gif")) { + return "image/gif"; + } else if (filename.endsWith(".jpg")) { + return "image/jpeg"; + } else if (filename.endsWith(".jpeg")) { + return "image/jpeg"; + } else if (filename.endsWith(".ico")) { + return "image/x-icon"; + } else if (filename.endsWith(".xml")) { + return "text/xml"; + } else if (filename.endsWith(".pdf")) { + return "application/x-pdf"; + } else if (filename.endsWith(".zip")) { + return "application/x-zip"; + } else if (filename.endsWith(".gz")) { + return "application/x-gzip"; } + return "text/plain"; +} - 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"; - } +bool isDir(File file) { +#ifdef USE_SPIFFS + return false; +#else + return file.isDirectory(); +#endif +} - File dataFile = FILESYSTEM.open(path.c_str(), "r"); - if (dataFile.isDirectory()) { - path += "/index.htm"; - dataType = "text/html"; - dataFile = FILESYSTEM.open(path.c_str(), "r"); - } - if (!dataFile) { - return false; + +//////////////////////////////// +// Request handlers + +/* + * Return the FS type, status and size info + */ +void handleStatus() { + FSInfo fs_info; + String json = String("{\"fs\":\"") + fsName + "\", \"isOk\":"; + + if (fsOK) { + FILESYSTEM.info(fs_info); + json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; + } + else { + json += "\"false\""; } + json += "}"; + server.send(200, "application/json", json); +} - if (server.hasArg("download")) { - dataType = "application/octet-stream"; + +/* + * Return the list of files in the directory specified by the "dir" query string parameter + */ +void handleFileList() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); } - if (server.streamFile(dataFile, dataType) != dataFile.size()) { - DBG_OUTPUT_PORT.println("Sent less data than expected!"); + if (!server.hasArg("dir")) { + return returnFail("BAD ARGS"); } - dataFile.close(); - return true; -} + String path = server.arg("dir"); + if (path != "/" && !FILESYSTEM.exists(path)) { + return returnFail("BAD PATH"); + } -void handleFileUpload() { - if (server.uri() != "/edit") { - return; + /* TODO should we still perform a test equivalent to this one + File dir = FILESYSTEM.open(path, "r"); + path = String(); + if (!dir.isDirectory()) { + dir.close(); + return returnFail("NOT DIR"); } - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - if (FILESYSTEM.exists((char *)upload.filename.c_str())) { - FILESYSTEM.remove((char *)upload.filename.c_str()); + */ + + DBG_OUTPUT_PORT.println("handleFileList: " + path); + Dir dir = FILESYSTEM.openDir(path); + path = String(); + + String output = "["; + while (dir.next()) { + File entry = dir.openFile("r"); + if (output != "[") { + output += ','; } - uploadFile = FILESYSTEM.open(upload.filename.c_str(), "w"); - 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); + output += "{\"type\":\""; + if (isDir(entry)) { + output += "dir"; } - DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize); - } else if (upload.status == UPLOAD_FILE_END) { - if (uploadFile) { - uploadFile.close(); + else { + output += "file"; + output += "\",\"size\":\""; + output += entry.size(); + } + output += "\",\"name\":\""; + + // original code only had + // output += entry.name(); + // TODO check that the following works with other FS than SPIFFS + // The convention is that files listed at the root don't start with "/" + if (entry.name()[0] == '/') { + output += &(entry.name()[1]); + } + else { + output += entry.name(); } - DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); + output += "\"}"; + entry.close(); } + + output += "]"; + server.send(200, "text/json", output); } -void deleteRecursive(String path) { - File file = FILESYSTEM.open((char *)path.c_str(), "r"); - if (!file.isDirectory()) { - file.close(); - FILESYSTEM.remove((char *)path.c_str()); - return; - } - file.rewindDirectory(); - while (true) { - File entry = file.openNextFile(); - if (!entry) { - break; +/* + * Read the given file from the filesystem and stream it back to the client + */ +bool handleFileRead(String path) { + DBG_OUTPUT_PORT.println("handleFileRead: " + path); + if (path.endsWith("/")) { + path += "index.htm"; + } + + String contentType; + if (server.hasArg("download")) { + contentType = "application/octet-stream"; + } else { + contentType = getContentType(path); + } + + String pathWithGz = path + ".gz"; + if (FILESYSTEM.exists(pathWithGz) || FILESYSTEM.exists(path)) { + if (FILESYSTEM.exists(pathWithGz)) { + path += ".gz"; } - String entryPath = path + "/" + entry.name(); - if (entry.isDirectory()) { - entry.close(); - deleteRecursive(entryPath); - } else { - entry.close(); - FILESYSTEM.remove((char *)entryPath.c_str()); + File file = FILESYSTEM.open(path, "r"); + if (server.streamFile(file, contentType) != file.size()) { + DBG_OUTPUT_PORT.println("Sent less data than expected!"); } - yield(); + file.close(); + return true; } - - FILESYSTEM.rmdir((char *)path.c_str()); - file.close(); + return false; } -void handleDelete() { - if (server.args() == 0) { - return returnFail("BAD ARGS"); - } - String path = server.arg(0); - if (path == "/" || !FILESYSTEM.exists((char *)path.c_str())) { - returnFail("BAD PATH"); - return; - } - deleteRecursive(path); - returnOK(); -} -void handleCreate() { +/* + * Handle the creation of a new file + */ +void handleFileCreate() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); + } if (server.args() == 0) { return returnFail("BAD ARGS"); } String path = server.arg(0); - if (path == "/" || FILESYSTEM.exists((char *)path.c_str())) { - returnFail("BAD PATH"); - return; + DBG_OUTPUT_PORT.println("handleFileCreate: " + path); + if (path == "/") { + return returnFail("BAD PATH"); + } + if (FILESYSTEM.exists(path)) { + return returnFail("FILE EXISTS"); } + // TODO what ? create file if it has an extension otherwise create folder ? if (path.indexOf('.') > 0) { - File file = FILESYSTEM.open((char *)path.c_str(), "w"); + File file = FILESYSTEM.open(path, "w"); if (file) { file.write((const char *)0); file.close(); } } else { - FILESYSTEM.mkdir((char *)path.c_str()); + FILESYSTEM.mkdir(path); } - returnOK(); + return returnOK(); } -void handleList() { - if (!server.hasArg("dir")) { - return returnFail("BAD ARGS"); - } - String path = server.arg("dir"); - if (path != "/" && !FILESYSTEM.exists((char *)path.c_str())) { - return returnFail("BAD PATH"); - } - File dir = FILESYSTEM.open((char *)path.c_str(), "r"); - path = String(); - 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 = ','; - } +/* + * Delete the given directory, including all nested files and directories + */ +void deleteRecursive(String path) { + Dir dir = FILESYSTEM.openDir(path); + path = String(); - output += "{\"type\":\""; - if (entry.isDirectory()) { - output += "dir"; + while (dir.next()) { + File entry = dir.openFile("r"); + String entryPath = path + "/" + entry.name(); + if (isDir(entry)) { + entry.close(); + deleteRecursive(entryPath); } else { - output += "file"; - output += "\",\"size\":\""; - output += entry.size(); + entry.close(); + FILESYSTEM.remove(entryPath); } - 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(); + + FILESYSTEM.rmdir(path); } -void handleStatus() { - FSInfo fs_info; - FILESYSTEM.info(fs_info); - String json = String("{\"totalBytes\":\"") + fs_info.totalBytes - + "\", \"usedBytes\":\"" + fs_info.usedBytes - + "\"}"; +/* + * Handle a file deletion request + */ +void handleFileDelete() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); + } + if (server.args() == 0) { + return returnFail("BAD ARGS"); + } + String path = server.arg(0); + DBG_OUTPUT_PORT.println("handleFileDelete: " + path); + if (path == "/" || !FILESYSTEM.exists(path)) { + return returnFail("BAD PATH"); + } + deleteRecursive(path); + returnOK(); +} - server.send(200, "application/json", json); +/* + * Handle a file upload request + */ +void handleFileUpload() { + if (!fsOK) { + return returnFail("FS INIT ERROR"); + } + 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.println("handleFileUpload Name: " + filename); + uploadFile = FILESYSTEM.open(filename, "w"); + DBG_OUTPUT_PORT.print("Upload: START, filename: " + filename); + } + else if (upload.status == UPLOAD_FILE_WRITE) { + if (uploadFile) { + uploadFile.write(upload.buf, upload.currentSize); + } + DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: " + upload.currentSize); + } + else if (upload.status == UPLOAD_FILE_END) { + if (uploadFile) { + uploadFile.close(); + } + DBG_OUTPUT_PORT.print("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 details + */ void handleNotFound() { - if (hasSD && loadFromSdCard(server.uri())) { - return; + if (!fsOK) { + return returnFail("FS INIT ERROR"); } - 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"; + if (!handleFileRead(server.uri())) { + String message = "Error: File not found\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"; + } + DBG_OUTPUT_PORT.print(message); + return returnNotFound(message); } - server.send(404, "text/plain", message); - DBG_OUTPUT_PORT.print(message); } + void setup(void) { + //////////////////////////////// + // SERIAL INIT DBG_OUTPUT_PORT.begin(115200); DBG_OUTPUT_PORT.setDebugOutput(true); DBG_OUTPUT_PORT.print("\n"); + + //////////////////////////////// + // FILESYSTEM INIT + + // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); + fsOK = FILESYSTEM.begin(); + DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "Filesystem init failed!"); + + { + // Debug: dump contents of root folder on console + Dir dir = FILESYSTEM.openDir("/"); + while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + ", size: " + dir.fileSize()); + DBG_OUTPUT_PORT.printf("\n"); + } + + //////////////////////////////// + // WI-FI INIT + DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid); 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 + while (WiFi.status() != WL_CONNECTED) { 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("."); + } + DBG_OUTPUT_PORT.println(""); DBG_OUTPUT_PORT.print("Connected! IP address: "); DBG_OUTPUT_PORT.println(WiFi.localIP()); + //////////////////////////////// + // MDNS INIT 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("Open http://"); DBG_OUTPUT_PORT.print(host); - DBG_OUTPUT_PORT.println(".local"); + DBG_OUTPUT_PORT.println(".local/edit to open the file manager"); } + //////////////////////////////// + // WEB SERVER INIT + // Filesystem status server.on("/status",HTTP_GET, handleStatus); - server.on("/list", HTTP_GET, handleList); - server.on("/edit", HTTP_DELETE, handleDelete); - server.on("/edit", HTTP_PUT, handleCreate); - server.on("/edit", HTTP_POST, []() { + + // List directory + server.on("/list", HTTP_GET, handleFileList); + + // Load editor + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit/index.htm")) returnNotFound("FileNotFound"); + }); + + // 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, /*[]() { returnOK(); - }, handleFileUpload); + }*/returnOK, 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"); - - // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); - if (FILESYSTEM.begin()) { - DBG_OUTPUT_PORT.println("SD Card initialized."); - hasSD = true; - } } + void loop(void) { server.handleClient(); MDNS.update(); From 82904ff0ca5736fed86fab7563654c8ef152ce44 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 15:40:26 +0100 Subject: [PATCH 042/111] Fixed folder handling --- .../examples/WebFileManager/data/edit/index.htm | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 7e1124f62d..922d196024 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -252,11 +252,8 @@ 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; - } + if (!path.value.startsWith("/")) path.value = "/" + path.value; + path.value = path.value.substring(0, path.value.lastIndexOf("/")+1) + filename; } } @@ -265,13 +262,13 @@ xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) { var statusObj = JSON.parse(xmlHttp.responseText); - var statusTxt = statusObj.fs + ": "; + var statusText = statusObj.fs + " - "; if (statusObj.isOk) { - statusTxt += "Free space: " + readableSize(statusObj.totalBytes - statusObj.usedBytes) + statusText += "Free space: " + readableSize(statusObj.totalBytes - statusObj.usedBytes) + ". Used: " + readableSize(statusObj.usedBytes) + ". Total: " + readableSize(statusObj.totalBytes); } else { - statusTxt += "INIT ERROR !"; + statusText += "INIT ERROR !"; } document.getElementById("status").innerHTML = statusText; } @@ -529,6 +526,7 @@ switch(ext){ case "png": case "jpg": + case "jpeg": case "gif": case "ico": return true; From b68f5fdb74db84c9873958087c51ce2782d0b6e7 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 15:48:23 +0100 Subject: [PATCH 043/111] Replaced the FILESYSTEM #define by a filesystem variable, and introduced FSConfig to prevent FS formating. Fixed recursive deletion. Got rid of specific isDir() for SPIFFS. --- .../WebFileManager/WebFileManager.ino | 145 ++++++++++-------- 1 file changed, 84 insertions(+), 61 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index cdf5148f19..8960d370a4 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -1,13 +1,13 @@ /* Changes : * - * Sketch: + * 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" * - * Web page: + * Web page improvements: * - Tree panel width is now proportional (20%) to see long names on big screens * - Added an icon for files, and indented them to the same level as folders * - Changed file/folder icon set to use lighter and more neutral ones from https://feathericons.com/ (passed the result through compresspng.com and base64-image.de) @@ -15,20 +15,42 @@ * - Added file size after each file name * - Added FS status information at the top right * - Replaced that FS status by operation status when upload/delete is in progress - * - Filled filename box in header with the name of the currently selected file + * - Filled filename box in header with the name of the last clicked file + * - Selecting a file for upload proposes to put it in the same file as the last clicked file + * - No limit to 8.3 lowercase filenames if not needed + * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) * * TODO: - * - Show file names as they come from server (with "/" for SPIFFS, without for others) and adapt the queries such as "delete" by only adding the leading "/" if not already present - * - Test fileSystem status in web page and indicate if ERROR - * - Show FS type in status + * - test creation of edit.txt/EDIT.TXT on LittleFS and SPIFFS (should succeed). (correclty fails on SDFS) + * - Check directory behaviour on SPIFFS (now that we call file.isDirectory() + * - Support Filenames without extension, Dirnames with extension ? * - Cleanup (look for TODO below) - * - Fix Editor (add Save/Discard buttons ?) + * - Reload tree (size) and status on Editor Save + * - Add Editor Save/Discard buttons ? * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? + * - After delete in subfolder, refresh folder, not full tree (with folders closed) * - * TEST: - * - Thoroughly on SPIFFS + SDFS + LittleFS : - * - List / Edit / View image / Download / Create file / Upload file / Mkdir at root - * - List / Edit / View image / Download / Create file / Upload file / Mkdir in subfolder + * TEST: (*) = failed vXXX = OK + * - On SPIFFS: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - with Long Filenames: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * + * - On LittleFS: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - with Long Filenames: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * + * - On SDFS: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder + * - with Long Filenames: + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder * (after PR): reported free space */ @@ -66,7 +88,7 @@ Once the data and sketch have been uploaded, access the editor by going to http://webfilemanager.local/edit Notes: - - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line just before "FILESYSTEM.begin()", + - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to - index.htm is the default index (works on subfolders as well) - Filesystem limitations apply. For example, SDFS is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . @@ -79,9 +101,9 @@ // Select the FileSystem by uncommenting one of the lines below +// #define USE_SPIFFS +// #define USE_LITTLEFS #define USE_SDFS -//#define USE_SPIFFS -//#define USE_LITTLEFS //////////////////////////////// @@ -91,22 +113,26 @@ #include #include -#if defined USE_SDFS - #define FILESYSTEM SDFS - #include - const char* fsName = "SDFS"; -#elif defined USE_SPIFFS - #define FILESYSTEM SPIFFS +#if defined USE_SPIFFS #include const char* fsName = "SPIFFS"; + FS* fileSystem = &SPIFFS; + SPIFFSConfig fileSystemConfig = SPIFFSConfig(); #elif defined USE_LITTLEFS - #define FILESYSTEM LittleFS #include const char* fsName = "LittleFS"; + FS* fileSystem = &LittleFS; + LitteFSConfig fileSystemConfig = LittleFSConfig(); +#elif defined USE_SDFS + #include + const char* fsName = "SDFS"; + FS* fileSystem = &SDFS; + SDFSConfig fileSystemConfig = SDFSConfig(); #else #error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch. #endif + #define DBG_OUTPUT_PORT Serial #ifndef STASSID @@ -171,15 +197,6 @@ String getContentType(String filename) { return "text/plain"; } -bool isDir(File file) { -#ifdef USE_SPIFFS - return false; -#else - return file.isDirectory(); -#endif -} - - //////////////////////////////// // Request handlers @@ -189,10 +206,10 @@ bool isDir(File file) { */ void handleStatus() { FSInfo fs_info; - String json = String("{\"fs\":\"") + fsName + "\", \"isOk\":"; + String json = String("{\"type\":\"") + fsName + "\", \"isOk\":"; if (fsOK) { - FILESYSTEM.info(fs_info); + fileSystem->info(fs_info); json += String("\"true\", \"totalBytes\":\"") + fs_info.totalBytes + "\", \"usedBytes\":\"" + fs_info.usedBytes + "\""; } else { @@ -216,12 +233,12 @@ void handleFileList() { } String path = server.arg("dir"); - if (path != "/" && !FILESYSTEM.exists(path)) { + if (path != "/" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } /* TODO should we still perform a test equivalent to this one - File dir = FILESYSTEM.open(path, "r"); + File dir = fileSystem->open(path, "r"); path = String(); if (!dir.isDirectory()) { dir.close(); @@ -230,7 +247,7 @@ void handleFileList() { */ DBG_OUTPUT_PORT.println("handleFileList: " + path); - Dir dir = FILESYSTEM.openDir(path); + Dir dir = fileSystem->openDir(path); path = String(); String output = "["; @@ -240,7 +257,7 @@ void handleFileList() { output += ','; } output += "{\"type\":\""; - if (isDir(entry)) { + if (entry.isDirectory()) { output += "dir"; } else { @@ -286,11 +303,11 @@ bool handleFileRead(String path) { } String pathWithGz = path + ".gz"; - if (FILESYSTEM.exists(pathWithGz) || FILESYSTEM.exists(path)) { - if (FILESYSTEM.exists(pathWithGz)) { + if (fileSystem->exists(pathWithGz) || fileSystem->exists(path)) { + if (fileSystem->exists(pathWithGz)) { path += ".gz"; } - File file = FILESYSTEM.open(path, "r"); + File file = fileSystem->open(path, "r"); if (server.streamFile(file, contentType) != file.size()) { DBG_OUTPUT_PORT.println("Sent less data than expected!"); } @@ -316,45 +333,50 @@ void handleFileCreate() { if (path == "/") { return returnFail("BAD PATH"); } - if (FILESYSTEM.exists(path)) { + if (fileSystem->exists(path)) { return returnFail("FILE EXISTS"); } // TODO what ? create file if it has an extension otherwise create folder ? if (path.indexOf('.') > 0) { - File file = FILESYSTEM.open(path, "w"); + File file = fileSystem->open(path, "w"); if (file) { file.write((const char *)0); file.close(); } } else { - FILESYSTEM.mkdir(path); + fileSystem->mkdir(path); } return returnOK(); } /* - * Delete the given directory, including all nested files and directories + * 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 */ void deleteRecursive(String path) { - Dir dir = FILESYSTEM.openDir(path); - path = String(); + 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()) { - File entry = dir.openFile("r"); - String entryPath = path + "/" + entry.name(); - if (isDir(entry)) { - entry.close(); - deleteRecursive(entryPath); - } - else { - entry.close(); - FILESYSTEM.remove(entryPath); - } + String entryPath = path + "/" + dir.fileName(); + deleteRecursive(entryPath); } - FILESYSTEM.rmdir(path); + // Then delete the folder itself + fileSystem->rmdir(path); } @@ -370,7 +392,7 @@ void handleFileDelete() { } String path = server.arg(0); DBG_OUTPUT_PORT.println("handleFileDelete: " + path); - if (path == "/" || !FILESYSTEM.exists(path)) { + if (path == "/" || !fileSystem->exists(path)) { return returnFail("BAD PATH"); } deleteRecursive(path); @@ -394,7 +416,7 @@ void handleFileUpload() { filename = "/" + filename; } DBG_OUTPUT_PORT.println("handleFileUpload Name: " + filename); - uploadFile = FILESYSTEM.open(filename, "w"); + uploadFile = fileSystem->open(filename, "w"); DBG_OUTPUT_PORT.print("Upload: START, filename: " + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { @@ -449,13 +471,14 @@ void setup(void) { //////////////////////////////// // FILESYSTEM INIT - // SDFS.setConfig(SDFSConfig().setCSPin(chipSelectPin)); - fsOK = FILESYSTEM.begin(); + fileSystemConfig.setAutoFormat(false); + fileSystem->setConfig(fileSystemConfig); + fsOK = fileSystem->begin(); DBG_OUTPUT_PORT.println(fsOK ? "Filesystem initialized." : "Filesystem init failed!"); { // Debug: dump contents of root folder on console - Dir dir = FILESYSTEM.openDir("/"); + Dir dir = fileSystem->openDir("/"); while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + ", size: " + dir.fileSize()); DBG_OUTPUT_PORT.printf("\n"); } From 842b9149d1e638e6c278b14df5243f5c0e1314f4 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 15:54:49 +0100 Subject: [PATCH 044/111] Made 8.3 lowercase filenames formating optional (disabled by default). Refresh only part of the tree when possible. Selecting a file for upload defaults to the same folder as the last clicked file. Removed the Mkdir button on SPIFFS. --- .../WebFileManager/data/edit/index.htm | 102 +++++++++++------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 922d196024..6d47033b9d 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -163,6 +163,8 @@ } From 2864f3590ea8183c1a42e47602c904e59b2071e4 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sat, 14 Mar 2020 21:19:17 +0100 Subject: [PATCH 046/111] Removed limitation "files must have an extension, folders may not". Case insensivity of the extension for the editor and preview. --- .../WebFileManager/WebFileManager.ino | 50 ++++++------ .../WebFileManager/data/edit/index.htm | 79 ++++++++++++------- 2 files changed, 74 insertions(+), 55 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 8960d370a4..3caede9d14 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -7,36 +7,35 @@ * - LittleFS.open() second parametsr is mandatory > specified "r" where needed * - 'FILE_WRITE' was not declared in this scope > replaced by "w" * - * Web page improvements: - * - Tree panel width is now proportional (20%) to see long names on big screens + * UI improvements: + * - Tree panel width is now proportional (20%) to see long names on big screens * - Added an icon for files, and indented them to the same level as folders * - Changed file/folder icon set to use lighter and more neutral ones from https://feathericons.com/ (passed the result through compresspng.com and base64-image.de) * - 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 - * - Replaced that FS status by operation status when upload/delete is in progress + * - Replaced that FS status by operation status when async operations are in progress * - Filled filename box in header with the name of the last clicked file - * - Selecting a file for upload proposes to put it in the same file as the last clicked file - * - No limit to 8.3 lowercase filenames if not needed + * - Selecting a file for upload proposes to put it in the same folder as the last clicked file + * - Removed limitation to 8.3 lowercase filenames + * - Removed limitation "files must have an extension, folders may not" * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) * * TODO: - * - test creation of edit.txt/EDIT.TXT on LittleFS and SPIFFS (should succeed). (correclty fails on SDFS) - * - Check directory behaviour on SPIFFS (now that we call file.isDirectory() + * - test creation of edit.txt/EDIT.TXT on LittleFS (should succeed). (Correclty supported on SPIFFS but not on SDFS) * - Support Filenames without extension, Dirnames with extension ? * - Cleanup (look for TODO below) * - Reload tree (size) and status on Editor Save * - Add Editor Save/Discard buttons ? - * - Dim screen (and http://www.ajaxload.info/ ) when Upload/Delete in progress ? - * - After delete in subfolder, refresh folder, not full tree (with folders closed) + * - Can we query the fatType of the SDFS ? * * TEST: (*) = failed vXXX = OK * - On SPIFFS: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder * - with Long Filenames: - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload - * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload at root + * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload in subfolder * * - On LittleFS: * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root @@ -51,7 +50,6 @@ * - with Long Filenames: * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir at root * - vList / vEdit / vView image / vMkFile / vDownload / vDelete / vUpload / vMkdir in subfolder - * (after PR): reported free space */ /* @@ -91,7 +89,7 @@ - For SDFS, if your card's CS pin is not connected the default pin (4), enable the line "fileSystemConfig.setCSPin(chipSelectPin);" specifying the GPIO the CS pin is connected to - index.htm is the default index (works on subfolders as well) - - Filesystem limitations apply. For example, SDFS is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . + - Filesystem limitations apply. For example, FAT16 is limited to 8.3 filenames - https://en.wikipedia.org/wiki/8.3_filename . SPIFFS and LittleFS also have limitations. Plese see the 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 @@ -101,9 +99,9 @@ // Select the FileSystem by uncommenting one of the lines below -// #define USE_SPIFFS -// #define USE_LITTLEFS -#define USE_SDFS +#define USE_SPIFFS +//#define USE_LITTLEFS +//#define USE_SDFS //////////////////////////////// @@ -128,6 +126,7 @@ 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 @@ -267,10 +266,6 @@ void handleFileList() { } output += "\",\"name\":\""; - // original code only had - // output += entry.name(); - // TODO check that the following works with other FS than SPIFFS - // The convention is that files listed at the root don't start with "/" if (entry.name()[0] == '/') { output += &(entry.name()[1]); } @@ -337,15 +332,18 @@ void handleFileCreate() { return returnFail("FILE EXISTS"); } - // TODO what ? create file if it has an extension otherwise create folder ? - if (path.indexOf('.') > 0) { + if (path.endsWith("/")) { + // Create a folder + path.remove(path.length() - 1); + fileSystem->mkdir(path); + } + else { + // Create a file File file = fileSystem->open(path, "w"); if (file) { file.write((const char *)0); file.close(); } - } else { - fileSystem->mkdir(path); } return returnOK(); } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 064e6afa03..75aba571f8 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -203,31 +203,35 @@ refreshStatus(); } } - function createPath(p){ + function createPath(path){ xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = postCallback; + xmlHttp.onload = postCallback; var formData = new FormData(); - formData.append("path", p); + formData.append("path", path); document.body.style.cursor = "wait"; xmlHttp.open("PUT", "/edit"); xmlHttp.send(formData); } mkfile.onclick = function(e){ - var name = path.value - if(name.indexOf(".") === -1) return; - createPath(name); - editor.loadUrl(name); + var name = path.value.trim(); + if (name === "") alert("A filename must be given"); + else if (name.endsWith("/")) alert("Filenames cannot end with a '/' character"); + else { + if (!name.startsWith("/")) name = "/" + name; + createPath(name); + editor.loadUrl(name); + } }; mkdir.onclick = function(e){ - var name = path.value - if(name.length < 2) return; - if(name.indexOf(".") !== -1){ - if(name.lastIndexOf("/") === 0) return; - name = name.substring(0, name.lastIndexOf("/")); + var name = path.value.trim(); + if (name.isEmpty()) alert("A folder name must be given"); + else { + if (!name.endsWith("/")) name = name + "/"; + if (!name.startsWith("/")) name = "/" + name; + createPath(name); } - createPath(name); }; input.onchange = function(e){ // A file has been selected @@ -243,7 +247,7 @@ if(input.files.length === 0) return; setStatus("Uploading..."); xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = postCallback; + xmlHttp.onload = postCallback; var formData = new FormData(); formData.append("data", input.files[0], path.value); document.body.style.cursor = "wait"; @@ -281,9 +285,9 @@ + ". Used: " + readableSize(fsInfo.usedBytes) + ". Total: " + readableSize(fsInfo.totalBytes); } else { - statusText += "INIT ERROR !"; + statusText += "INIT ERROR !"; } - document.getElementById("status").innerHTML = statusText; + setStatus(statusText); if (fsInfo.type != "SPIFFS") { document.getElementById("mkdir").style.display = "inline"; } @@ -292,8 +296,25 @@ function refreshStatus(){ var xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = statusCallback; - document.getElementById("status").innerHTML = "(refreshing...)"; + xmlHttp.onload = function() { + if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText); + else { + fsInfo = JSON.parse(xmlHttp.responseText); + var statusText = fsInfo.type + " - "; + if (fsInfo.isOk) { + statusText += "Free space: " + readableSize(fsInfo.totalBytes - fsInfo.usedBytes) + + ". Used: " + readableSize(fsInfo.usedBytes) + + ". Total: " + readableSize(fsInfo.totalBytes); + } else { + statusText += "INIT ERROR !"; + } + setStatus(statusText); + if (fsInfo.type != "SPIFFS") { + document.getElementById("mkdir").style.display = "inline"; + } + } + }; + setStatus("(refreshing...)"); xmlHttp.open("GET", "/status", true); xmlHttp.send(null); } @@ -449,9 +470,9 @@ function createTreeLeaf(path, name, size){ var leaf = document.createElement("li"); - leaf.id = ((path == "/")?"":path)+"/"+name; // Removed toLowerCase() + leaf.id = ((path == "/")?"":path)+"/"+name; var label = document.createElement("span"); - label.innerHTML = name + " (" + readableSize(size) + ")"; // Removed toLowerCase() + label.innerHTML = name + " (" + readableSize(size) + ")"; leaf.appendChild(label); leaf.onclick = function(e){ if(isTextFile(leaf.id)){ @@ -474,12 +495,12 @@ var leaf = document.createElement("li"); var check = document.createElement("input"); check.type = "checkbox"; - check.id = ((path == "/")?"":path)+"/"+name; // Removed toLowerCase() + check.id = ((path == "/")?"":path)+"/"+name; if(typeof disabled !== "undefined" && disabled) check.disabled = "disabled"; leaf.appendChild(check); var label = document.createElement("label"); label.for = check.id; - label.textContent = name; // Removed toLowerCase() + label.textContent = name; leaf.appendChild(label); check.onchange = function(e){ if(check.checked){ @@ -532,7 +553,7 @@ function isTextFile(path){ var ext = /(?:\.([^.]+))?$/.exec(path)[1]; if(typeof ext !== undefined){ - switch(ext){ + switch(ext.toLowerCase()){ case "txt": case "htm": case "html": @@ -552,7 +573,7 @@ function isImageFile(path){ var ext = /(?:\.([^.]+))?$/.exec(path)[1]; if(typeof ext !== undefined){ - switch(ext){ + switch(ext.toLowerCase()){ case "png": case "jpg": case "jpeg": @@ -600,7 +621,7 @@ function httpDelete(filename){ setStatus("Deleting..."); xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = deleteCallback(filename); + xmlHttp.onload = deleteCallback(filename); var formData = new FormData(); formData.append("path", filename); document.body.style.cursor = "wait"; @@ -617,7 +638,7 @@ function httpList(parent, path){ xmlHttp = new XMLHttpRequest(parent, path); - xmlHttp.onloadend = getCallback(parent, path); + xmlHttp.onload = getCallback(parent, path); document.body.style.cursor = "wait"; xmlHttp.open("GET", "/list?dir="+path, true); xmlHttp.send(null); @@ -665,7 +686,7 @@ var xmlHttp = null; var editor = ace.edit(element); - + //post function postCallback(){ document.body.style.cursor = "default"; @@ -673,7 +694,7 @@ } function httpPost(filename, data, type){ xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = postCallback; + xmlHttp.onload = postCallback; var formData = new FormData(); formData.append("data", new Blob([data], { type: type }), filename); document.body.style.cursor = "wait"; @@ -691,7 +712,7 @@ } function httpGet(theUrl){ xmlHttp = new XMLHttpRequest(); - xmlHttp.onloadend = getCallback; + xmlHttp.onload = getCallback; document.body.style.cursor = "wait"; xmlHttp.open("GET", theUrl, true); xmlHttp.send(null); From 992fa31b41c58f34fe0361ffc84e0de663dd26d0 Mon Sep 17 00:00:00 2001 From: vdeconinck Date: Sun, 15 Mar 2020 11:01:48 +0100 Subject: [PATCH 047/111] Support Filenames without extension, Dirnames with extension. Added Save/Discard/Help buttons to Editor, discard confirmation on leave, and refresh tree/status upon save. Removed redundant Ctrl-Z + Ctrl-Shift-Z shortcut declarations. Small bug fixes. + some refactoring --- .../WebFileManager/WebFileManager.ino | 29 +- .../WebFileManager/data/edit/index.htm | 361 ++++++++++++------ 2 files changed, 258 insertions(+), 132 deletions(-) diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino index 3caede9d14..5a7f218cb8 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino +++ b/libraries/ESP8266WebServer/examples/WebFileManager/WebFileManager.ino @@ -20,14 +20,15 @@ * - Removed limitation to 8.3 lowercase filenames * - Removed limitation "files must have an extension, folders may not" * - Improved refresh of parts of the tree (e.g. upon file delete, refresh subfolder, not root) + * - Support Filenames without extension, Dirnames with extension + * - Added Save/Discard/Help buttons to Editor, discard confirmation on leave, and refresh tree/status upon save * * TODO: - * - test creation of edit.txt/EDIT.TXT on LittleFS (should succeed). (Correclty supported on SPIFFS but not on SDFS) - * - Support Filenames without extension, Dirnames with extension ? - * - Cleanup (look for TODO below) - * - Reload tree (size) and status on Editor Save - * - Add Editor Save/Discard buttons ? - * - Can we query the fatType of the SDFS ? + * - Change lengthy FS status by a percentage graph, with numbers as tooltip + * - Bug: when deleting a file, we should refresh the parent node unless it has been deleted behind the scene, in which case recurse + * - Support Files not starting with '/' (SPIFFS) + * - Cleanup (look for TODO below and in HTML) + * - Can we query the fatType of the SDFS (and limit to 8.3 if FAT16) ? * * TEST: (*) = failed vXXX = OK * - On SPIFFS: @@ -99,8 +100,8 @@ // Select the FileSystem by uncommenting one of the lines below -#define USE_SPIFFS -//#define USE_LITTLEFS +//#define USE_SPIFFS +#define USE_LITTLEFS //#define USE_SDFS //////////////////////////////// @@ -120,7 +121,7 @@ #include const char* fsName = "LittleFS"; FS* fileSystem = &LittleFS; - LitteFSConfig fileSystemConfig = LittleFSConfig(); + LittleFSConfig fileSystemConfig = LittleFSConfig(); #elif defined USE_SDFS #include const char* fsName = "SDFS"; @@ -232,7 +233,7 @@ void handleFileList() { } String path = server.arg("dir"); - if (path != "/" && !fileSystem->exists(path)) { + if (path != "" && !fileSystem->exists(path)) { return returnFail("BAD PATH"); } @@ -413,9 +414,9 @@ void handleFileUpload() { if (!filename.startsWith("/")) { filename = "/" + filename; } - DBG_OUTPUT_PORT.println("handleFileUpload Name: " + filename); + DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); - DBG_OUTPUT_PORT.print("Upload: START, filename: " + filename); + DBG_OUTPUT_PORT.print(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { @@ -476,8 +477,8 @@ void setup(void) { { // Debug: dump contents of root folder on console - Dir dir = fileSystem->openDir("/"); - while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + ", size: " + dir.fileSize()); + Dir dir = fileSystem->openDir(""); + while (dir.next()) DBG_OUTPUT_PORT.println("FS File: " + dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + ")")); DBG_OUTPUT_PORT.printf("\n"); } diff --git a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm index 75aba571f8..b192b836b9 100644 --- a/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm +++ b/libraries/ESP8266WebServer/examples/WebFileManager/data/edit/index.htm @@ -124,7 +124,7 @@ } } - #uploader { + #header { position: absolute; top: 0; right: 0; @@ -154,7 +154,7 @@ background-color: #EEE; padding:5px; } - #uploader span { + #status { position: absolute; top: 4px; right: 10px; @@ -164,48 +164,131 @@ -