[Cleanup] Reduce build size by using CDN for CSS and JS

This commit is contained in:
TD-er
2020-12-21 02:31:34 +01:00
parent 5d0ea57e23
commit acd4219eb7
23 changed files with 921 additions and 578 deletions
+35 -1
View File
@@ -45,6 +45,12 @@ To create/register a plugin, you have to :
#ifndef WEBSERVER_FAVICON
#define WEBSERVER_FAVICON
#endif
#ifndef WEBSERVER_CSS
#define WEBSERVER_CSS
#endif
#ifndef WEBSERVER_INCLUDE_JS
#define WEBSERVER_INCLUDE_JS
#endif
#ifndef WEBSERVER_LOG
#define WEBSERVER_LOG
#endif
@@ -323,6 +329,12 @@ To create/register a plugin, you have to :
#ifdef WEBSERVER_FAVICON
#undef WEBSERVER_FAVICON
#endif
#ifdef WEBSERVER_CSS
#undef WEBSERVER_CSS
#endif
#ifdef WEBSERVER_INCLUDE_JS
#undef WEBSERVER_INCLUDE_JS
#endif
#ifdef WEBSERVER_LOG
#undef WEBSERVER_LOG
#endif
@@ -1037,7 +1049,10 @@ To create/register a plugin, you have to :
/******************************************************************************\
* Libraries dependencies *****************************************************
\******************************************************************************/
#if defined(USES_P049) || defined(USES_P052) || defined(USES_P053) || defined(USES_P056) || defined(USES_P071) || defined(USES_P075) || defined(USES_P082) || defined(USES_P087) || defined(USES_P094)
#if defined(USES_P049) || defined(USES_P052) || defined(USES_P053) || defined(USES_P056) || defined(USES_P071) || defined(USES_P075) || defined(USES_P078) || defined(USES_P082) || defined(USES_P085) || defined(USES_P087) || defined(USES_P094) || defined(USES_P102)
#ifndef PLUGIN_USES_SERIAL
#define PLUGIN_USES_SERIAL
#endif
// At least one plugin uses serial.
#else
// No plugin uses serial, so make sure software serial is not included.
@@ -1113,6 +1128,11 @@ To create/register a plugin, you have to :
#undef WEBSERVER_TIMINGSTATS
#endif
// Do not include large blobs but fetch them from CDN
#ifndef WEBSERVER_USE_CDN_JS_CSS
#define WEBSERVER_USE_CDN_JS_CSS
#endif
#ifndef BUILD_NO_DEBUG
#define BUILD_NO_DEBUG
#endif
@@ -1179,7 +1199,21 @@ To create/register a plugin, you have to :
#ifndef BUILD_NO_RAM_TRACKER
#define BUILD_NO_RAM_TRACKER
#endif
#endif
// Do not include large blobs but fetch them from CDN
#ifdef WEBSERVER_USE_CDN_JS_CSS
#ifdef WEBSERVER_FAVICON
#ifndef WEBSERVER_FAVICON_CDN
#define WEBSERVER_FAVICON_CDN
#endif
#endif
#ifdef WEBSERVER_CSS
#undef WEBSERVER_CSS
#endif
#ifdef WEBSERVER_INCLUDE_JS
#undef WEBSERVER_INCLUDE_JS
#endif
#endif
#if defined(USES_C002) || defined (USES_C005) || defined(USES_C006) || defined(USES_C014) || defined(USES_P037)
+2 -3
View File
@@ -4,6 +4,5 @@ void Caches::clearAllCaches()
{
taskIndexName.clear();
taskIndexValueName.clear();
}
fileExistsMap.clear();
}
+5 -3
View File
@@ -5,15 +5,17 @@
#include "../../ESPEasy_common.h"
#include "../Globals/Plugins.h"
typedef std::map<String, taskIndex_t> TaskIndexNameMap;
typedef std::map<String, byte> TaskIndexValueNameMap;
typedef std::map<String, taskIndex_t>TaskIndexNameMap;
typedef std::map<String, byte> TaskIndexValueNameMap;
typedef std::map<String, bool> FilePresenceMap;
struct Caches {
void clearAllCaches();
TaskIndexNameMap taskIndexName;
TaskIndexNameMap taskIndexName;
TaskIndexValueNameMap taskIndexValueName;
FilePresenceMap fileExistsMap;
};
+17 -3
View File
@@ -117,7 +117,14 @@ String appendToFile(const String& fname, const uint8_t *data, unsigned int size)
}
bool fileExists(const String& fname) {
return ESPEASY_FS.exists(patch_fname(fname));
const String patched_fname = patch_fname(fname);
auto search = Cache.fileExistsMap.find(patched_fname);
if (search != Cache.fileExistsMap.end()) {
return search->second;
}
bool res = ESPEASY_FS.exists(patched_fname);
Cache.fileExistsMap[patched_fname] = res;
return res;
}
fs::File tryOpenFile(const String& fname, const String& mode) {
@@ -129,8 +136,11 @@ fs::File tryOpenFile(const String& fname, const String& mode) {
bool exists = fileExists(fname);
if ((mode == F("r")) && !exists) {
return f;
if (!exists) {
if (mode == F("r")) {
return f;
}
Cache.fileExistsMap.clear();
}
f = ESPEASY_FS.open(patch_fname(fname), mode.c_str());
STOP_TIMER(TRY_OPEN_FILE);
@@ -138,7 +148,9 @@ fs::File tryOpenFile(const String& fname, const String& mode) {
}
bool tryRenameFile(const String& fname_old, const String& fname_new) {
Cache.fileExistsMap.clear();
if (fileExists(fname_old) && !fileExists(fname_new)) {
clearAllCaches();
return ESPEASY_FS.rename(patch_fname(fname_old), patch_fname(fname_new));
}
return false;
@@ -148,6 +160,7 @@ bool tryDeleteFile(const String& fname) {
if (fname.length() > 0)
{
bool res = ESPEASY_FS.remove(patch_fname(fname));
clearAllCaches();
// A call to GarbageCollection() will at most erase a single block. (e.g. 8k block size)
// A deleted file may have covered more than a single block, so try to clear multiple blocks.
@@ -277,6 +290,7 @@ void fileSystemCheck()
if (ESPEASY_FS.begin())
{
clearAllCaches();
#if defined(ESP8266)
fs::FSInfo fs_info;
ESPEASY_FS.info(fs_info);
+2
View File
@@ -218,6 +218,7 @@ String doFormatUserVar(struct EventStruct *event, byte rel_index, bool mustCheck
if (valueCount <= rel_index) {
isvalid = false;
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("No sensor value for TaskIndex: ");
log += event->TaskIndex + 1;
@@ -227,6 +228,7 @@ String doFormatUserVar(struct EventStruct *event, byte rel_index, bool mustCheck
log += getSensorTypeLabel(sensorType);
addLog(LOG_LEVEL_ERROR, log);
}
#endif
return "";
}
+10 -12
View File
@@ -8,8 +8,6 @@
#include "../WebServer/AccessControl.h"
#include <ArduinoJson.h>
HandledWebCommand_result handle_command_from_web(EventValueSource::Enum source, String& webrequest)
{
if (!clientIPallowed()) { return HandledWebCommand_result::IP_not_allowed; }
@@ -57,11 +55,11 @@ HandledWebCommand_result handle_command_from_web(EventValueSource::Enum source,
if (sendOK) {
if (printToWebJSON) {
// Format "OK" to JSON format
const int capacity = JSON_OBJECT_SIZE(2);
StaticJsonDocument<capacity> root;
root[F("return")] = F("OK");
root[F("command")] = webrequest;
serializeJson(root, printWebString);
printWebString = F("{\"return\": \"");
printWebString += F("OK");
printWebString += F("\",\"command\": \"");
printWebString += webrequest;
printWebString += F("\"}");
} else {
printWebString = F("OK");
}
@@ -71,11 +69,11 @@ HandledWebCommand_result handle_command_from_web(EventValueSource::Enum source,
if (printToWebJSON) {
// Format error to JSON format
const int capacity = JSON_OBJECT_SIZE(2);
StaticJsonDocument<capacity> root;
root[F("return")] = F("Unknown or restricted command");
root[F("command")] = webrequest;
serializeJson(root, printWebString);
printWebString = F("{\"return\": \"");
printWebString += F("Unknown or restricted command");
printWebString += F("\",\"command\": \"");
printWebString += webrequest;
printWebString += F("\"}");
}
return HandledWebCommand_result::Unknown_or_restricted_command;
}
+20 -12
View File
@@ -161,11 +161,15 @@ String formatDomoticzSensorType(struct EventStruct *event) {
break;
default:
{
String log = F("Domoticz Controller: Not yet implemented sensor type: ");
log += static_cast<byte>(event->sensorType);
log += F(" idx: ");
log += event->idx;
addLog(LOG_LEVEL_ERROR, log);
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("Domoticz Controller: Not yet implemented sensor type: ");
log += static_cast<byte>(event->sensorType);
log += F(" idx: ");
log += event->idx;
addLog(LOG_LEVEL_ERROR, log);
}
#endif
break;
}
}
@@ -178,13 +182,17 @@ String formatDomoticzSensorType(struct EventStruct *event) {
}
values.trim();
{
String log = F(" Domoticz: Sensortype: ");
log += static_cast<byte>(event->sensorType);
log += F(" idx: ");
log += event->idx;
log += F(" values: ");
log += values;
addLog(LOG_LEVEL_INFO, log);
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F(" Domoticz: Sensortype: ");
log += static_cast<byte>(event->sensorType);
log += F(" idx: ");
log += event->idx;
log += F(" values: ");
log += values;
addLog(LOG_LEVEL_INFO, log);
}
#endif
}
return values;
}
+121
View File
@@ -0,0 +1,121 @@
#include "../Static/WebStaticData.h"
#include "../Globals/Cache.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../WebServer/HTML_wrappers.h"
String generate_external_URL(const String& fname) {
String url;
url.reserve(80 + fname.length());
url = F("https://cdn.jsdelivr.net/gh/letscontrolit/ESPEasy@mega-20201130/static/");
url += fname;
return url;
}
void serve_CSS() {
String url = F("esp.css");
if (!fileExists(url))
{
#ifndef WEBSERVER_CSS
url = generate_external_URL(F("espeasy_default.css"));
#else
addHtml(F("<style>"));
// Send CSS in chunks
TXBuffer += DATA_ESPEASY_DEFAULT_MIN_CSS;
addHtml(F("</style>"));
return;
#endif
}
addHtml(F("<link"));
addHtmlAttribute(F("rel"), F("stylesheet"));
addHtmlAttribute(F("type"), F("text/css"));
addHtmlAttribute(F("href"), url);
addHtml('/');
addHtml('>');
}
void serve_favicon() {
#ifdef WEBSERVER_FAVICON_CDN
addHtml(F("<link"));
addHtmlAttribute(F("rel"), F("icon"));
addHtmlAttribute(F("type"), F("image/x-icon"));
addHtmlAttribute(F("href"), generate_external_URL(F("favicon.ico")));
addHtml('/');
addHtml('>');
#endif
}
void serve_JS(JSfiles_e JSfile) {
String url;
switch (JSfile) {
case JSfiles_e::UpdateSensorValuesDevicePage:
url = F("update_sensor_values_device_page.js");
break;
case JSfiles_e::FetchAndParseLog:
url = F("fetch_and_parse_log.js");
break;
case JSfiles_e::SaveRulesFile:
url = F("rules_save.js");
break;
case JSfiles_e::GitHubClipboard:
url = F("github_clipboard.js");
break;
case JSfiles_e::Reboot:
url = F("reboot.js");
break;
case JSfiles_e::Toasting:
url = F("toasting.js");
break;
}
if (!fileExists(url))
{
#ifndef WEBSERVER_INCLUDE_JS
url = generate_external_URL(url);
#else
html_add_script(true);
switch (JSfile) {
case JSfiles_e::UpdateSensorValuesDevicePage:
#ifdef WEBSERVER_DEVICES
TXBuffer += DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS;
#endif
break;
case JSfiles_e::FetchAndParseLog:
#ifdef WEBSERVER_LOG
TXBuffer += DATA_FETCH_AND_PARSE_LOG_JS;
#endif
break;
case JSfiles_e::SaveRulesFile:
#ifdef WEBSERVER_RULES
TXBuffer += jsSaveRules;
#endif
break;
case JSfiles_e::GitHubClipboard:
#ifdef WEBSERVER_GITHUB_COPY
TXBuffer += DATA_GITHUB_CLIPBOARD_JS;
#endif
break;
case JSfiles_e::Reboot:
TXBuffer += DATA_REBOOT_JS;
break;
case JSfiles_e::Toasting:
TXBuffer += jsToastMessageBegin;
// we can push custom messages here in future releases...
addHtml(F("Submitted"));
TXBuffer += jsToastMessageEnd;
break;
}
html_add_script_end();
return;
#endif
}
addHtml(F("<script"));
addHtml(F(" defer"));
addHtmlAttribute(F("src"), url);
addHtml('>');
html_add_script_end();
}
File diff suppressed because one or more lines are too long
+20 -4
View File
@@ -355,9 +355,13 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task
if ((Device[DeviceIndex].Type == DEVICE_TYPE_SERIAL) ||
(Device[DeviceIndex].Type == DEVICE_TYPE_SERIAL_PLUS1))
{
#ifdef PLUGIN_USES_SERIAL
serialHelper_webformSave(&TempEvent);
}
#else
addLog(LOG_LEVEL_ERROR, F("PLUGIN_USES_SERIAL not defined"));
#endif
}
const byte valueCount = getValueCountForTask(taskIndex);
@@ -394,9 +398,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task
// ********************************************************************************
void handle_devicess_ShowAllTasksTable(byte page)
{
html_add_script(true);
TXBuffer += DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS;
html_add_script_end();
serve_JS(JSfiles_e::UpdateSensorValuesDevicePage);
html_table_class_multirow();
html_TR();
html_table_header("", 70);
@@ -523,7 +525,12 @@ void handle_devicess_ShowAllTasksTable(byte page)
}
case DEVICE_TYPE_SERIAL:
case DEVICE_TYPE_SERIAL_PLUS1:
#ifdef PLUGIN_USES_SERIAL
addHtml(serialHelper_getSerialTypeLabel(&TempEvent));
#else
addHtml(F("PLUGIN_USES_SERIAL not defined"));
#endif
break;
default:
@@ -627,8 +634,12 @@ void handle_devicess_ShowAllTasksTable(byte page)
// fallthrough
case DEVICE_TYPE_SERIAL:
{
#ifdef PLUGIN_USES_SERIAL
addHtml(serialHelper_getGpioDescription(static_cast<ESPEasySerialPort>(Settings.TaskDevicePort[x]), Settings.TaskDevicePin1[x],
Settings.TaskDevicePin2[x], F("<BR>")));
#else
addHtml(F("PLUGIN_USES_SERIAL not defined"));
#endif
if (showpin3) {
html_BR();
@@ -893,7 +904,12 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, byte page)
case DEVICE_TYPE_SERIAL:
case DEVICE_TYPE_SERIAL_PLUS1:
{
#ifdef PLUGIN_USES_SERIAL
devicePage_show_serial_config(taskIndex);
#else
addHtml(F("PLUGIN_USES_SERIAL not defined"));
#endif
break;
}
+3
View File
@@ -62,6 +62,9 @@ bool loadFromFS(boolean spiffs, String path) {
if (spiffs)
{
if (!fileExists(path)) {
return false;
}
fs::File dataFile = tryOpenFile(path.c_str(), "r");
if (!dataFile) {
+1 -3
View File
@@ -33,9 +33,7 @@ void handle_log() {
addCheckBox(F("autoscroll"), true);
addHtml(F("<BR></body>"));
html_add_script(true);
TXBuffer += DATA_FETCH_AND_PARSE_LOG_JS;
html_add_script_end();
serve_JS(JSfiles_e::FetchAndParseLog);
#else // ifdef WEBSERVER_LOG
addHtml(F("Not included in build"));
+4 -2
View File
@@ -45,12 +45,14 @@ void handle_root() {
// if index.htm exists on FS serve that one (first check if gziped version exists)
if (loadFromFS(true, F("/index.htm.gz"))) { return; }
#ifdef FEATURE_SD
if (loadFromFS(false, F("/index.htm.gz"))) { return; }
#endif
if (loadFromFS(true, F("/index.htm"))) { return; }
#ifdef FEATURE_SD
if (loadFromFS(false, F("/index.htm"))) { return; }
#endif
TXBuffer.startStream();
String sCommand = web_server.arg(F("cmd"));
+1 -3
View File
@@ -95,9 +95,7 @@ void handle_rules() {
addButton(fileName, F("Download to file"));
html_end_table();
html_add_script(true);
TXBuffer += jsSaveRules;
html_add_script_end();
serve_JS(JSfiles_e::SaveRulesFile);
sendHeadandTail_stdtemplate(true);
TXBuffer.endStream();
+4 -4
View File
@@ -251,9 +251,8 @@ void handle_sysinfo() {
addCopyButton(F("copyText"), F("\\n"), F("Copy info to clipboard"));
TXBuffer += githublogo;
html_add_script(false);
TXBuffer += DATA_GITHUB_CLIPBOARD_JS;
html_add_script_end();
serve_JS(JSfiles_e::GitHubClipboard);
# else // ifdef WEBSERVER_GITHUB_COPY
addFormHeader(F("System Info"));
@@ -682,7 +681,7 @@ void handle_sysinfo_Storage() {
html += F(" kB free)");
addHtml(html);
}
#ifndef LIMIT_BUILD_SIZE
addRowLabel(F("Page size"));
addHtml(String(SpiffsPagesize()));
@@ -704,6 +703,7 @@ void handle_sysinfo_Storage() {
# endif // if defined(ESP8266)
}
#endif
# ifndef BUILD_MINIMAL_OTA
+22 -20
View File
@@ -4,6 +4,7 @@
#include "../WebServer/AccessControl.h"
#include "../WebServer/HTML_wrappers.h"
#include "../Globals/Cache.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../../ESPEasy-Globals.h"
@@ -14,7 +15,8 @@
// ********************************************************************************
// Web Interface upload page
// ********************************************************************************
byte uploadResult = 0;
uploadResult_e uploadResult = uploadResult_e::UploadStarted;
void handle_upload() {
if (!isLoggedIn()) { return; }
navMenuIndex = MENU_INDEX_TOOLS;
@@ -43,22 +45,22 @@ void handle_upload_post() {
TXBuffer.startStream();
sendHeadandTail_stdtemplate();
if (uploadResult == 1)
{
addHtml(F("Upload OK!<BR>You may need to reboot to apply all settings..."));
LoadSettings();
switch (uploadResult) {
case uploadResult_e::Success:
addHtml(F("Upload OK!<BR>You may need to reboot to apply all settings..."));
clearAllCaches();
LoadSettings();
break;
case uploadResult_e::InvalidFile:
addHtml(F("<font color=\"red\">Upload file invalid!</font>"));
break;
case uploadResult_e::NoFilename:
addHtml(F("<font color=\"red\">No filename!</font>"));
break;
case uploadResult_e::UploadStarted:
break;
}
if (uploadResult == 2) {
addHtml(F("<font color=\"red\">Upload file invalid!</font>"));
}
if (uploadResult == 3) {
addHtml(F("<font color=\"red\">No filename!</font>"));
}
addHtml(F("Upload finished"));
sendHeadandTail_stdtemplate(true);
TXBuffer.endStream();
@@ -71,7 +73,7 @@ void handle_upload_json() {
#ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("handle_upload_post"));
#endif
uint8_t result = uploadResult;
uint8_t result = static_cast<int>(uploadResult);
if (!isLoggedIn()) { result = 255; }
@@ -102,7 +104,7 @@ void handleFileUpload() {
if (upload.filename.c_str()[0] == 0)
{
uploadResult = 3;
uploadResult = uploadResult_e::NoFilename;
return;
}
@@ -114,7 +116,7 @@ void handleFileUpload() {
addLog(LOG_LEVEL_INFO, log);
}
valid = false;
uploadResult = 0;
uploadResult = uploadResult_e::UploadStarted;
}
else if (upload.status == UPLOAD_FILE_WRITE)
{
@@ -180,10 +182,10 @@ void handleFileUpload() {
}
if (valid) {
uploadResult = 1;
uploadResult = uploadResult_e::Success;
}
else {
uploadResult = 2;
uploadResult = uploadResult_e::InvalidFile;
}
}
+10 -1
View File
@@ -8,7 +8,16 @@
// ********************************************************************************
// Web Interface upload page
// ********************************************************************************
extern byte uploadResult;
enum class uploadResult_e {
// Int values are used in JSON, so keep them numbered like this.
UploadStarted = 0,
Success = 1,
InvalidFile = 2,
NoFilename = 3
};
extern uploadResult_e uploadResult;
void handle_upload();
// ********************************************************************************
+11 -21
View File
@@ -144,9 +144,7 @@ void sendHeadandTail(const String& tmplName, boolean Tail, boolean rebooting) {
if (shouldReboot) {
// we only add this here as a seperate chunk to prevent using too much memory at once
html_add_script(false);
TXBuffer += DATA_REBOOT_JS;
html_add_script_end();
serve_JS(JSfiles_e::Reboot);
}
STOP_TIMER(HANDLE_SERVING_WEBPAGE);
}
@@ -159,6 +157,7 @@ void sendHeadandTail_stdtemplate(boolean Tail, boolean rebooting) {
addHtmlError(F("Warning: Connected via AP"));
}
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
const int nrArgs = web_server.args();
@@ -176,6 +175,7 @@ void sendHeadandTail_stdtemplate(boolean Tail, boolean rebooting) {
addLog(LOG_LEVEL_INFO, log);
}
}
#endif
}
}
@@ -559,11 +559,7 @@ void getWebPageTemplateVar(const String& varName)
addHtml(F("<a "));
if (i == navMenuIndex) {
addHtmlAttribute(F("class"), F("menu active"));
} else {
addHtmlAttribute(F("class"), F("menu"));
}
addHtmlAttribute(F("class"), (i == navMenuIndex) ? F("menu active") : F("menu"));
addHtmlAttribute(F("href"), getGpMenuURL(i));
addHtml('>');
addHtml(getGpMenuIcon(i));
@@ -585,25 +581,17 @@ void getWebPageTemplateVar(const String& varName)
else if (varName == F("css"))
{
if (fileExists(F("esp.css"))) // now css is written in writeDefaultCSS() to FS and always present
// if (0) //TODO
{
addHtml(F("<link rel=\"stylesheet\" type=\"text/css\" href=\"esp.css\">"));
}
else
{
addHtml(F("<style>"));
// Send CSS per chunk to avoid sending either too short or too large strings.
TXBuffer += DATA_ESPEASY_DEFAULT_MIN_CSS;
addHtml(F("</style>"));
}
serve_favicon();
serve_CSS();
}
else if (varName == F("js"))
{
html_add_autosubmit_form();
// FIXME TD-er: Can only call this after tag has been set for the file.
//serve_JS(JSfiles_e::Toasting);
html_add_script(false);
TXBuffer += jsToastMessageBegin;
@@ -626,11 +614,13 @@ void getWebPageTemplateVar(const String& varName)
else
{
#ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
String log = F("Templ: Unknown Var : ");
log += varName;
addLog(LOG_LEVEL_ERROR, log);
}
#endif
// no return string - eat var name
}
+545 -424
View File
@@ -1,424 +1,545 @@
h1,h6{
color:#07D
}
.checkmark:after,.dotmark:after{
content:''
}
.button,.menu{
text-decoration:none
}
.div_l,.menu{
float:left
}
*{
box-sizing:border-box;
font-family:sans-serif;
font-size:12pt;
margin:0;
padding:0
}
h1{
font-size:16pt;
font-weight:700;
margin:8px 0
}
h2,h3{
font-size:12pt;
font-weight:700
}
h2{
background-color:#444;
color:#FFF;
margin:0 -4px;
padding:6px
}
h3{
background-color:#EEE;
color:#444;
margin:16px -4px 0;
padding:4px
}
h6{
font-size:10pt
}
code,kbd,pre,samp,tt,xmp{
font-family:monospace,monospace;
font-size:1em
}
.button{
background-color:#07D;
border:none;
border-radius:4px;
color:#FFF;
margin:4px;
padding:4px 16px
}
.button.link.wide{
display:inline-block;
text-align:center;
width:100%
}
.button.link.red{
background-color:red
}
.button.help{
border-color:gray;
border-radius:50%;
border-style:solid;
border-width:1px;
padding:2px 4px
}
.checkmark,input,select,textarea{
border-color:gray;
border-radius:4px;
border-style:solid;
border-width:1px
}
.button:hover{
background:#369
}
input:hover,select:hover{
background-color:#ccc
}
input,select,textarea{
background-color:#eee;
margin:4px;
padding:4px 8px
}
input.wide, select.wide{
max-width:500px;
width:80%
}
.widenumber{
max-width:500px;
width:7em
}
.container,.container2{
font-size:12pt;
padding-left:35px;
cursor:pointer
}
.container{
-moz-user-select:none;
-ms-user-select:none;
-webkit-user-select:none;
display:block;
margin-left:4px;
margin-top:0;
position:relative;
user-select:none
}
.container input{
cursor:pointer;
opacity:0;
position:absolute
}
.checkmark.disabled{
background-color:grey
}
.checkmark{
background-color:#eee;
height:25px;
left:0;
position:absolute;
top:0;
width:25px
}
.container:hover input~.checkmark{
background-color:#ccc
}
.container input:checked~.checkmark{
background-color:#07D
}
.checkmark:after{
display:none;
position:absolute
}
.container input:checked~.checkmark:after,.container2{
display:block
}
.container .checkmark:after{
-ms-transform:rotate(45deg);
-webkit-transform:rotate(45deg);
border:solid #fff;
border-width:0 3px 3px 0;
height:10px;
left:7px;
top:3px;
transform:rotate(45deg);
width:5px
}
#toastmessage,.dotmark{
border-width:1px;
border-color:gray;
border-style:solid
}
.container2{
-moz-user-select:none;
-ms-user-select:none;
-webkit-user-select:none;
margin-bottom:20px;
margin-left:9px;
position:relative;
user-select:none
}
.container2 input{
cursor:pointer;
opacity:0;
position:absolute
}
.dotmark{
background-color:#eee;
border-radius:50%;
height:26px;
left:0;
position:absolute;
top:0;
width:26px
}
.container2:hover input~.dotmark{
background-color:#ccc
}
.container2 input:checked~.dotmark{
background-color:#07D
}
.dotmark:after{
display:none;
position:absolute
}
.container2 input:checked~.dotmark:after{
display:block
}
.container2 .dotmark:after{
background:#fff;
border-radius:50%;
height:8px;
left:8px;
top:8px;
width:8px
}
.logviewer,textarea{
font-family:'Lucida Console',Monaco,monospace;
max-width:1000px;
padding:4px 8px;
width:80%
}
#toastmessage{
background-color:#07D;
border-radius:4px;
bottom:30%;
color:#fff;
font-size:17px;
left:282px;
margin-left:-125px;
min-width:250px;
padding:16px;
position:fixed;
text-align:center;
visibility:hidden;
z-index:1
}
#toastmessage.show{
-webkit-animation:fadein .5s,fadeout .5s 2.5s;
animation:fadein .5s,fadeout .5s 2.5s;
visibility:visible
}
@-webkit-keyframes fadein{
from{
bottom:20%;
opacity:0
}
to{
bottom:30%;
opacity:.9
}
}
@keyframes fadein{
from{
bottom:20%;
opacity:0
}
to{
bottom:30%;
opacity:.9
}
}
@-webkit-keyframes fadeout{
from{
bottom:30%;
opacity:.9
}
to{
bottom:0;
opacity:0
}
}
@keyframes fadeout{
from{
bottom:30%;
opacity:.9
}
to{
bottom:0;
opacity:0
}
}
.level_0{
color:#F1F1F1
}
.level_1{
color:#FCFF95
}
.level_2{
color:#9DCEFE
}
.level_3{
color:#A4FC79
}
.level_4{
color:#F2AB39
}
.level_9{
color:#F50
}
.logviewer{
background-color:#272727;
border-color:gray;
border-style:solid;
color:#F1F1F1;
height:530px;
overflow:auto
}
textarea:hover{
background-color:#ccc
}
table.multirow th,table.normal th{
background-color:#444;
border-color:#888;
color:#FFF;
font-weight:700;
padding:6px;
align-content: center;
text-align:center
}
table.multirow,table.normal{
border-collapse:collapse;
color:#000;
min-width:420px;
width:100%
}
table.multirow tr,table.normal td,table.normal tr{
padding:4px
}
table.normal td{
height:30px
}
table.multirow td{
height:30px;
padding:4px;
text-align:center
}
table.multirow tr:nth-child(even){
background-color:#DEE6FF
}
.highlight td{
background-color:#dbff0075
}
.apheader,.headermenu{
background-color:#F8F8F8;
padding:8px 12px
}
.note{
color:#444;
font-style:italic
}
.headermenu{
border-bottom:1px solid #DDD;
height:90px;
left:0;
position:fixed;
right:0;
top:0;
z-index:1
}
.bodymenu{
margin-top:96px
}
.menubar{
position:inherit;
top:55px
}
.menu{
border:solid transparent;
border-radius:4px 4px 0 0;
border-width:4px 1px 1px;
color:#444;
padding:4px 16px 8px;
white-space:nowrap
}
.menu.active{
background-color:#FFF;
border-color:#07D #DDD #FFF;
color:#000
}
.menu:hover{
background:#DEF;
color:#000
}
.menu_button{
display:none
}
.on{
color:green
}
.off{
color:red
}
.div_r{
background-color:#080;
border-radius:4px;
color:#fff;
float:right;
margin:2px;
padding:1px 10px
}
.alert,.warning{
margin-bottom:15px;
padding:20px;
color:#fff
}
.div_br{
clear:both
}
.alert{
background-color:#f44336
}
.warning{
background-color:#ffca17
}
.closebtn{
color:#fff;
cursor:pointer;
float:right;
font-size:22px;
font-weight:700;
line-height:20px;
margin-left:15px;
transition:.3s
}
.closebtn:hover{
color:#000
}
section{
overflow-x:auto;
width:100%
}
@media screen and (max-width:960px){
.showmenulabel{
display:none
}
.menu{
max-width:11vw;
max-width:48px
}
}
.closebtn,
h1,
h2,
h3 {
font-weight: 700
}
h1,
h6 {
color: #07D
}
.checkmark:after,
.dotmark:after {
content: ''
}
.button,
.menu {
text-decoration: none
}
.div_l,
.menu {
float: left
}
.closebtn,
.div_r {
float: right;
color: #fff
}
* {
box-sizing: border-box;
font-family: sans-serif;
font-size: 12pt;
margin: 0;
padding: 0
}
h1 {
font-size: 16pt;
margin: 8px 0
}
h2,
h3 {
font-size: 12pt
}
h2 {
background-color: #444;
color: #FFF;
margin: 0 -4px;
padding: 6px
}
h3 {
background-color: #EEE;
color: #444;
margin: 16px -4px 0;
padding: 4px
}
h6 {
font-size: 10pt
}
code,
kbd,
pre,
samp,
tt,
xmp {
font-family: monospace, monospace;
font-size: 1em
}
.button {
background-color: #07D;
border: none;
border-radius: 4px;
color: #FFF;
margin: 4px;
padding: 4px 16px
}
.button.help,
.checkmark,
input,
select,
textarea {
border-color: gray;
border-style: solid;
border-width: 1px
}
.button.link.wide {
display: inline-block;
text-align: center;
width: 100%
}
.button.link.red {
background-color: red
}
.button.help {
border-radius: 50%;
padding: 2px 4px
}
.checkmark,
input,
select,
textarea {
border-radius: 4px
}
.button:hover {
background: #369
}
input:hover,
select:hover {
background-color: #ccc
}
input,
select,
textarea {
background-color: #eee;
margin: 4px;
padding: 4px 8px
}
input.wide,
select.wide {
max-width: 500px;
width: 80%
}
.widenumber {
max-width: 500px;
width: 100px
}
.container,
.container2 {
font-size: 12pt;
padding-left: 35px;
cursor: pointer
}
.container {
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
display: block;
margin-left: 4px;
margin-top: 0;
position: relative;
user-select: none
}
.container input {
cursor: pointer;
opacity: 0;
position: absolute
}
.checkmark.disabled {
background-color: grey
}
.checkmark {
background-color: #eee;
height: 25px;
left: 0;
position: absolute;
top: 0;
width: 25px
}
.container:hover input~.checkmark {
background-color: #ccc
}
.container input:checked~.checkmark {
background-color: #07D
}
.checkmark:after {
display: none;
position: absolute
}
.container input:checked~.checkmark:after,
.container2 {
display: block
}
.container .checkmark:after {
-ms-transform: rotate(45deg);
-webkit-transform: rotate(45deg);
border: solid #fff;
border-width: 0 3px 3px 0;
height: 10px;
left: 7px;
top: 3px;
transform: rotate(45deg);
width: 5px
}
#toastmessage,
.dotmark,
.logviewer {
border-color: gray;
border-style: solid
}
#toastmessage,
.dotmark {
border-width: 1px
}
.container2 {
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
margin-bottom: 20px;
margin-left: 9px;
position: relative;
user-select: none
}
.container2 input {
cursor: pointer;
opacity: 0;
position: absolute
}
.dotmark {
background-color: #eee;
border-radius: 50%;
height: 26px;
left: 0;
position: absolute;
top: 0;
width: 26px
}
.container2:hover input~.dotmark {
background-color: #ccc
}
.container2 input:checked~.dotmark {
background-color: #07D
}
.dotmark:after {
display: none;
position: absolute
}
.container2 input:checked~.dotmark:after {
display: block
}
.container2 .dotmark:after {
background: #fff;
border-radius: 50%;
height: 8px;
left: 8px;
top: 8px;
width: 8px
}
.logviewer,
textarea {
font-family: 'Lucida Console', Monaco, monospace;
max-width: 1000px;
padding: 4px 8px;
width: 80%
}
#toastmessage {
background-color: #07D;
border-radius: 4px;
bottom: 30%;
color: #fff;
font-size: 17px;
left: 282px;
margin-left: -125px;
min-width: 250px;
padding: 16px;
position: fixed;
text-align: center;
visibility: hidden;
z-index: 1
}
#toastmessage.show {
-webkit-animation: fadein .5s, fadeout .5s 2.5s;
animation: fadein .5s, fadeout .5s 2.5s;
visibility: visible
}
@-webkit-keyframes fadein {
from {
bottom: 20%;
opacity: 0
}
to {
bottom: 30%;
opacity: .9
}
}
@keyframes fadein {
from {
bottom: 20%;
opacity: 0
}
to {
bottom: 30%;
opacity: .9
}
}
@-webkit-keyframes fadeout {
from {
bottom: 30%;
opacity: .9
}
to {
bottom: 0;
opacity: 0
}
}
@keyframes fadeout {
from {
bottom: 30%;
opacity: .9
}
to {
bottom: 0;
opacity: 0
}
}
.level_0 {
color: #F1F1F1
}
.level_1 {
color: #FCFF95
}
.level_2 {
color: #9DCEFE
}
.level_3 {
color: #A4FC79
}
.level_4 {
color: #F2AB39
}
.level_9 {
color: #F50
}
.logviewer {
background-color: #272727;
color: #F1F1F1;
height: 530px;
overflow: auto
}
textarea:hover {
background-color: #ccc
}
table.multirow th,
table.normal th {
background-color: #444;
border-color: #888;
color: #FFF;
font-weight: 700;
padding: 6px;
align-content: center;
text-align: center
}
table.multirow,
table.normal {
border-collapse: collapse;
color: #000;
min-width: 420px;
width: 100%
}
table.multirow tr,
table.normal td,
table.normal tr {
padding: 4px
}
table.normal td {
height: 30px
}
table.multirow td {
height: 30px;
padding: 4px;
text-align: center
}
table.multirow tr:nth-child(even) {
background-color: #DEE6FF
}
.highlight td {
background-color: #dbff0075
}
.apheader,
.headermenu {
background-color: #F8F8F8;
padding: 8px 12px
}
.note {
color: #444;
font-style: italic
}
.headermenu {
border-bottom: 1px solid #DDD;
height: 90px;
left: 0;
position: fixed;
right: 0;
top: 0;
z-index: 1
}
.bodymenu {
margin-top: 96px
}
.menubar {
position: inherit;
top: 55px
}
.menu {
border: solid transparent;
border-radius: 4px 4px 0 0;
border-width: 4px 1px 1px;
color: #444;
padding: 4px 16px 8px;
white-space: nowrap
}
.menu.active {
background-color: #FFF;
border-color: #07D #DDD #FFF;
color: #000
}
.menu:hover {
background: #DEF;
color: #000
}
.menu_button {
display: none
}
.on {
color: green
}
.off {
color: red
}
.div_r {
background-color: #080;
border-radius: 4px;
margin: 2px;
padding: 1px 10px
}
.alert,
.warning {
margin-bottom: 15px;
padding: 20px;
color: #fff
}
.div_br {
clear: both
}
.alert {
background-color: #f44336
}
.warning {
background-color: #ffca17
}
.closebtn {
cursor: pointer;
font-size: 22px;
line-height: 20px;
margin-left: 15px;
transition: .3s
}
.closebtn:hover {
color: #000
}
section {
overflow-x: auto;
width: 100%
}
@media screen and (max-width: 960px) {
.showmenulabel {
display: none
}
.menu {
max-width: 11vw;
max-width: 48px
}
}
+1 -2
View File
@@ -117,5 +117,4 @@ function loopDeLoop(timeForNext, activeRequests) {
};
check = 1;
}, timeForNext);
}
}
+16 -22
View File
@@ -1,33 +1,27 @@
i = document.getElementById('rbtmsg');
i.innerHTML = "Please reboot: <input id='reboot' class='button link' value='Reboot' type='submit' onclick='r()'>";
var x = new XMLHttpRequest();
i = document.getElementById("rbtmsg"),
i.innerHTML = "Please reboot: <input id='reboot' class='button link' value='Reboot' type='submit' onclick='r()'>";
var x = new XMLHttpRequest;
//done
function d() {
i.innerHTML = '';
clearTimeout(t);
i.innerHTML = "",
clearTimeout(t)
}
//keep requesting mainpage until no more errors
function c() {
i.innerHTML += '.';
x.onload = d;
x.open('GET', window.location.origin);
x.send();
i.innerHTML += ".",
x.onload = d,
x.open("GET", window.location.origin),
x.send()
}
//rebooting
function b() {
i.innerHTML = 'Rebooting..';
t = setInterval(c, 2000);
i.innerHTML = "Rebooting..",
t = setInterval(c, 2e3)
}
//request reboot
function r() {
i.innerHTML += ' (requesting)';
x.onload = b;
x.open('GET', window.location.origin + '/?cmd=reboot');
x.send();
}
i.innerHTML += " (requesting)",
x.onload = b,
x.open("GET", window.location.origin + "/?cmd=reboot"),
x.send()
}
+33 -35
View File
@@ -1,36 +1,34 @@
function saveRulesFile() {
let button = document.getElementById('save_button');
let size = document.getElementById('size');
let ruleTextNew = document.getElementById('rules').value;
ruleTextNew = ruleTextNew.replace(/\\r?\\n/g, '\\r\\n');
let ruleNumber = document.getElementById('set').value;
let ruleTextFileData = new File([ruleTextNew], 'rules' + ruleNumber + '.txt', {
type: 'text/plain'
});
let formData = new FormData();
formData.append('file', ruleTextFileData);
formData.append('enctype', 'multipart/form-data');
let url = '/rules' + ruleNumber + '.txt?callback=' + Date.now();
fetch(url).then(res => res.text()).then((ruleTextOld) => {
if (ruleTextNew === ruleTextOld) {
console.log('nothing to save...');
} else {
let url = '/upload';
fetch(url, {
method: 'POST',
body: formData
}).then(
response => response.text()).then(html => {
let url = '/rules' + ruleNumber + '.txt?callback=' + Date.now();
fetch(url).then(res => res.text()).then((ruleTextNewCheck) => {
if (ruleTextNew === ruleTextNewCheck) {
toasting();
size.innerHTML = ruleTextNew.length;
} else {
console.log('error when saving...');
}
});
});
}
});
};
"use strict";
let size = document.getElementById("size");
let ruleTextNew = document.getElementById("rules").value;
ruleTextNew = ruleTextNew.replace(/\\r?\\n/g, "\\r\\n");
let ruleNumber = document.getElementById("set").value;
let ruleTextFileData = new File([ruleTextNew], "rules" + ruleNumber + ".txt", {
type: "text/plain"
});
let formData = new FormData();
formData.append("file", ruleTextFileData);
formData.append("enctype", "multipart/form-data");
let url = "/rules" + ruleNumber + ".txt?callback=" + Date.now();
fetch(url).then(res => res.text()).then(ruleTextOld => {
if (ruleTextNew === ruleTextOld) {
console.log("nothing to save...");
} else {
fetch("/upload", {
method: "POST",
body: formData
}).then(response => response.text()).then(l => {
let url = "/rules" + ruleNumber + ".txt?callback=" + Date.now();
fetch(url).then(res => res.text()).then(ruleTextNewCheck => {
if (ruleTextNew === ruleTextNewCheck) {
toasting();
size.innerHTML = ruleTextNew.length;
} else {
console.log("error when saving...");
}
});
});
}
});
}
+9
View File
@@ -0,0 +1,9 @@
function toasting() {
var x = document.getElementById('toastmessage');
x.innerHTML = 'Submitted';
x.className = 'show';
setTimeout(function() {
x.innerHTML = '';
x.className = x.className.replace('show', '');
}, 2000);
}