diff --git a/examples/mqttRSSI/mqttRSSI.ino b/examples/mqttRSSI/mqttRSSI.ino index 7954e74..3205f94 100644 --- a/examples/mqttRSSI/mqttRSSI.ino +++ b/examples/mqttRSSI/mqttRSSI.ino @@ -44,6 +44,11 @@ static const char AUX_mqtt_setting[] PROGMEM = R"raw( "uri": "/mqtt_setting", "menu": true, "element": [ + { + "name": "style", + "type": "ACStyle", + "value": "label+input,label+select{position:sticky;left:120px;width:230px!important;box-sizing:border-box;}" + }, { "name": "header", "type": "ACText", diff --git a/examples/mqttRSSI_FS/data/mqtt_setting.json b/examples/mqttRSSI_FS/data/mqtt_setting.json index 6e5efca..36587e6 100644 --- a/examples/mqttRSSI_FS/data/mqtt_setting.json +++ b/examples/mqttRSSI_FS/data/mqtt_setting.json @@ -3,6 +3,11 @@ "uri": "/mqtt_setting", "menu": true, "element": [ + { + "name": "style", + "type": "ACStyle", + "value": "label+input,label+select{position:sticky;left:120px;width:230px!important;box-sizing:border-box;}" + }, { "name": "header", "type": "ACText", diff --git a/examples/mqttRSSI_NA/mqttRSSI_NA.ino b/examples/mqttRSSI_NA/mqttRSSI_NA.ino index 2c0dd2c..ed30a46 100644 --- a/examples/mqttRSSI_NA/mqttRSSI_NA.ino +++ b/examples/mqttRSSI_NA/mqttRSSI_NA.ino @@ -46,6 +46,7 @@ typedef WebServer WiFiWebServer; // facility. // Declare AutoConnectElements for the page asf /mqtt_setting +ACStyle(style, "label+input,label+select{position:sticky;left:120px;width:230px!important;box-sizing:border-box;}"); ACText(header, "

MQTT broker settings

", "text-align:center;color:#2f4f4f;padding:10px;"); ACText(caption, "Publishing the WiFi signal strength to MQTT channel. RSSI value of ESP8266 to the channel created on ThingSpeak", "font-family:serif;color:#4682b4;"); ACInput(mqttserver, "", "Server", "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$", "MQTT broker server"); @@ -60,6 +61,7 @@ ACSubmit(discard, "Discard", "/"); // Declare the custom Web page as /mqtt_setting and contains the AutoConnectElements AutoConnectAux mqtt_setting(AUX_SETTING_URI, "MQTT Setting", true, { + style, header, caption, mqttserver, diff --git a/src/AutoConnectAux.cpp b/src/AutoConnectAux.cpp index d6de164..c38e14c 100644 --- a/src/AutoConnectAux.cpp +++ b/src/AutoConnectAux.cpp @@ -36,6 +36,7 @@ const char AutoConnectAux::_PAGE_AUX[] PROGMEM = { "{{CSS_INPUT_BUTTON}}" "{{CSS_INPUT_TEXT}}" "{{CSS_LUXBAR}}" + "{{AUX_CSS}}" "" "" "" @@ -409,8 +410,14 @@ const String AutoConnectAux::_insertElement(PageArgument& args) { } // Generate HTML for all AutoConnectElements contained in the page. - for (AutoConnectElement& addon : _addonElm) - body += addon.toHTML(); + for (AutoConnectElement& addon : _addonElm) { + // Since the style sheet has already drained at the time of the + // _insertElement function call, it skips the call to the HTML + // generator by each element. + if (addon.typeOf() != AC_Style) + // Invoke an HTML generator by each element + body += addon.toHTML(); + } // Call user handler after HTML generation. if (_handler) { @@ -422,6 +429,21 @@ const String AutoConnectAux::_insertElement(PageArgument& args) { return body; } +/** + * Insert user defined CSS code to AutoConnectAux page. + * @param args A reference of PageArgument but unused. + * @return HTML string that should be inserted. + */ +const String AutoConnectAux::_insertStyle(PageArgument& args) { + String css = String(""); + + for (AutoConnectElement& elm : _addonElm) { + if (elm.typeOf() == AC_Style) + css += elm.toHTML(); + } + return css; +} + /** * Generate an auxiliary page assembled with the AutoConnectElement. * This function is the core procedure of AutoConnectAux, and uses @@ -455,6 +477,7 @@ PageElement* AutoConnectAux::_setupPage(const String& uri) { elm->addToken(String(FPSTR("CSS_INPUT_BUTTON")), std::bind(&AutoConnect::_token_CSS_INPUT_BUTTON, mother, std::placeholders::_1)); elm->addToken(String(FPSTR("CSS_INPUT_TEXT")), std::bind(&AutoConnect::_token_CSS_INPUT_TEXT, mother, std::placeholders::_1)); elm->addToken(String(FPSTR("CSS_LUXBAR")), std::bind(&AutoConnect::_token_CSS_LUXBAR, mother, std::placeholders::_1)); + elm->addToken(String(FPSTR("AUX_CSS")), std::bind(&AutoConnectAux::_insertStyle, this, std::placeholders::_1)); elm->addToken(String(FPSTR("MENU_PRE")), std::bind(&AutoConnect::_token_MENU_PRE, mother, std::placeholders::_1)); elm->addToken(String(FPSTR("MENU_AUX")), std::bind(&AutoConnect::_token_MENU_AUX, mother, std::placeholders::_1)); elm->addToken(String(FPSTR("MENU_POST")), std::bind(&AutoConnect::_token_MENU_POST, mother, std::placeholders::_1)); @@ -618,6 +641,10 @@ AutoConnectElement* AutoConnectAux::_createElement(const JsonObject& json) { AutoConnectSelect* cert_elm = new AutoConnectSelect; return reinterpret_cast(cert_elm); } + case AC_Style: { + AutoConnectStyle* cert_elm = new AutoConnectStyle; + return reinterpret_cast(cert_elm); + } case AC_Submit: { AutoConnectSubmit* cert_elm = new AutoConnectSubmit; return reinterpret_cast(cert_elm); @@ -892,6 +919,7 @@ ACElement_t AutoConnectAux::_asElementType(const String& type) { { AUTOCONNECT_JSON_TYPE_ACINPUT, AC_Input }, { AUTOCONNECT_JSON_TYPE_ACRADIO, AC_Radio }, { AUTOCONNECT_JSON_TYPE_ACSELECT, AC_Select }, + { AUTOCONNECT_JSON_TYPE_ACSTYLE, AC_Style }, { AUTOCONNECT_JSON_TYPE_ACSUBMIT, AC_Submit }, { AUTOCONNECT_JSON_TYPE_ACTEXT, AC_Text } }; diff --git a/src/AutoConnectAux.h b/src/AutoConnectAux.h index fceb7b2..e19b5b9 100644 --- a/src/AutoConnectAux.h +++ b/src/AutoConnectAux.h @@ -92,6 +92,7 @@ class AutoConnectAux : public PageBuilder { void _join(AutoConnect& ac); /**< Make a link to AutoConnect */ PageElement* _setupPage(const String& uri); /**< AutoConnectAux page builder */ const String _insertElement(PageArgument& args); /**< Insert a generated HTML to the page built by PageBuilder */ + const String _insertStyle(PageArgument& args); /**< Insert CSS style */ const String _injectTitle(PageArgument& args) const { (void)(args); return _title; } /**< Returns title of this page to PageBuilder */ const String _injectMenu(PageArgument& args); /**< Inject menu title of this page to PageBuilder */ const String _indicateUri(PageArgument& args); /**< Inject the uri that caused the request */ diff --git a/src/AutoConnectAuxImpl.h b/src/AutoConnectAuxImpl.h index 6ee9cc3..649d302 100644 --- a/src/AutoConnectAuxImpl.h +++ b/src/AutoConnectAuxImpl.h @@ -133,6 +133,23 @@ AutoConnectSelectBasis& AutoConnectAux::getElement(const String& name) { return reinterpret_cast(_nullElement()); } +/** + * Get AutoConnectStyleBasis element. + * @param name An element name. + * @return A reference of AutoConnectStyle class. + */ +template<> +AutoConnectStyleBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Style) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + /** * Get AutoConnectSubmitBasis element. * @param name An element name. diff --git a/src/AutoConnectElement.h b/src/AutoConnectElement.h index 4896a60..3bbcc39 100644 --- a/src/AutoConnectElement.h +++ b/src/AutoConnectElement.h @@ -21,6 +21,7 @@ using AutoConnectFile = AutoConnectFileJson; using AutoConnectInput = AutoConnectInputJson; using AutoConnectRadio = AutoConnectRadioJson; using AutoConnectSelect = AutoConnectSelectJson; +using AutoConnectStyle = AutoConnectStyleJson; using AutoConnectSubmit = AutoConnectSubmitJson; using AutoConnectText = AutoConnectTextJson; #define AUTOCONNECT_JSON_BUFFER_SIZE 256 @@ -32,6 +33,7 @@ using AutoConnectFile = AutoConnectFileBasis; using AutoConnectInput = AutoConnectInputBasis; using AutoConnectRadio = AutoConnectRadioBasis; using AutoConnectSelect = AutoConnectSelectBasis; +using AutoConnectStyle = AutoConnectStyleBasis; using AutoConnectSubmit = AutoConnectSubmitBasis; using AutoConnectText = AutoConnectTextBasis; #endif // !AUTOCONNECT_USE_JSON @@ -49,6 +51,7 @@ using AutoConnectText = AutoConnectTextBasis; #define ACRadio(n, ...) AutoConnectRadio n(#n, ##__VA_ARGS__) #define ACSelect(n, ...) AutoConnectSelect n(#n, ##__VA_ARGS__) #define ACSubmit(n, ...) AutoConnectSubmit n(#n, ##__VA_ARGS__) +#define ACStyle(n, ...) AutoConnectStyle n(#n, ##__VA_ARGS__) #define ACText(n, ...) AutoConnectText n(#n, ##__VA_ARGS__) #endif // _AUTOCONNECTELEMENT_H_ diff --git a/src/AutoConnectElementBasis.h b/src/AutoConnectElementBasis.h index 516144d..533d068 100644 --- a/src/AutoConnectElementBasis.h +++ b/src/AutoConnectElementBasis.h @@ -35,6 +35,7 @@ typedef enum { AC_Input, AC_Radio, AC_Select, + AC_Style, AC_Submit, AC_Text, AC_Unknown = -1 @@ -236,6 +237,19 @@ class AutoConnectSelectBasis : AC_AUTOCONNECTELEMENT_ON_VIRTUAL public AutoConne std::vector _options; /**< List options array */ }; +/** + * An element class for inserting CSS in AutoConnectAux page. + * @param name Style name string. + * @param value CSS Native code. + */ +class AutoConnectStyleBasis : AC_AUTOCONNECTELEMENT_ON_VIRTUAL public AutoConnectElementBasis { + public: + explicit AutoConnectStyleBasis(const char* name = "", const char* value = "") : AutoConnectElementBasis(name, value, AC_Tag_None) { + _type = AC_Style; + } + virtual ~AutoConnectStyleBasis() {} +}; + /** * Submit button arrangement class, a part of AutoConnectAux element. * Place a submit button with a label that can be added by user sketch. @@ -325,6 +339,13 @@ inline AutoConnectSelectBasis& AutoConnectElementBasis::as(this)); } +template<> +inline AutoConnectStyleBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Style) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + template<> inline AutoConnectSubmitBasis& AutoConnectElementBasis::as(void) { if (typeOf() != AC_Submit) diff --git a/src/AutoConnectElementJson.h b/src/AutoConnectElementJson.h index c3d8ff8..c8fb7b9 100644 --- a/src/AutoConnectElementJson.h +++ b/src/AutoConnectElementJson.h @@ -39,6 +39,7 @@ #define AUTOCONNECT_JSON_TYPE_ACINPUT "ACInput" #define AUTOCONNECT_JSON_TYPE_ACRADIO "ACRadio" #define AUTOCONNECT_JSON_TYPE_ACSELECT "ACSelect" +#define AUTOCONNECT_JSON_TYPE_ACSTYLE "ACStyle" #define AUTOCONNECT_JSON_TYPE_ACSUBMIT "ACSubmit" #define AUTOCONNECT_JSON_TYPE_ACTEXT "ACText" #define AUTOCONNECT_JSON_VALUE_BR "br" @@ -276,6 +277,28 @@ class AutoConnectSelectJson : public AutoConnectElementJson, public AutoConnectS void serialize(JsonObject& json) override; }; +/** + * CSS style arrangement class, a part of AutoConnectAux element. + * This element assumes CSS that came into effect as a style code will + * assign. Therefore, it does not check whether the CSS error exists in + * the value set in AutoConnectStyle. Also, because AutoConnect inserts + * its style code at the end of the style block on the AutoConnectAux + * page, it may affect the AutoConnect web page elements. + * @param name A style name string. + * @param value CSS style code. + */ +class AutoConnectStyleJson : public AutoConnectElementJson, public AutoConnectStyleBasis { + public: + explicit AutoConnectStyleJson(const char* name = "", const char* value = "") { + AutoConnectStyleBasis::name = String(name); + AutoConnectStyleBasis::value = String(value); + AutoConnectStyleBasis::post = AC_Tag_None; + } + ~AutoConnectStyleJson() {} + bool loadMember(const JsonObject& json) override; + void serialize(JsonObject& json) override; +}; + /** * Submit button arrangement class, a part of AutoConnectAux element. * Place a submit button with a label that can be added by user sketch. @@ -371,6 +394,13 @@ inline AutoConnectSelectJson& AutoConnectElementJson::as( return *(reinterpret_cast(this)); } +template<> +inline AutoConnectStyleJson& AutoConnectElementJson::as(void) { + if (typeOf() != AC_Style) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + template<> inline AutoConnectSubmitJson& AutoConnectElementJson::as(void) { if (typeOf() != AC_Submit) diff --git a/src/AutoConnectElementJsonImpl.h b/src/AutoConnectElementJsonImpl.h index 2e06cbc..f3c0647 100644 --- a/src/AutoConnectElementJsonImpl.h +++ b/src/AutoConnectElementJsonImpl.h @@ -404,6 +404,31 @@ void AutoConnectSelectJson::serialize(JsonObject& json) { json[F(AUTOCONNECT_JSON_KEY_SELECTED)] = selected; } +/** + * Load an element member value from the JSON object. + * @param json JSON object with the definition of AutoConnectStyle. + * @return true AutoConnectStyle loaded + * @return false Type of AutoConnectStyle is mismatched. + */ +bool AutoConnectStyleJson::loadMember(const JsonObject& json) { + String type = json[F(AUTOCONNECT_JSON_KEY_TYPE)].as(); + if (type.equalsIgnoreCase(F(AUTOCONNECT_JSON_TYPE_ACSTYLE))) { + _setMember(json); + return true; + } + return false; +} + +/** + * Serialize AutoConnectStyle to JSON. + * @param json JSON object to be serialized. + */ +void AutoConnectStyleJson::serialize(JsonObject& json) { + _serialize(json); + json[F(AUTOCONNECT_JSON_KEY_TYPE)] = String(F(AUTOCONNECT_JSON_TYPE_ACSTYLE)); + json[F(AUTOCONNECT_JSON_KEY_VALUE)] = value; +} + /** * Returns JSON object size. * @return An object size for JsonBuffer.