diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..37216a2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +set(includedirs src) + +set(req arduino ESPAsyncWebServer ArduinoJson) + +idf_component_register( + INCLUDE_DIRS ${includedirs} + SRC_DIRS src + REQUIRES ${req}) diff --git a/README.md b/README.md index dffefc9..4659d70 100644 --- a/README.md +++ b/README.md @@ -62,9 +62,6 @@ The Library runs on any kind of **ESP8266** and **ESP32** (NodeMCU, AI Thinker, - Time control by @iangray001 - Vertical controls by @iangray001 - Time/date/password/color input types by @pcbbc -- Delayed response support @MartinMueller2003 -- Fragmented control transfer @ MartinMueller2003 -- Extended Callback @MartinMueller2003 ## Roadmap @@ -156,9 +153,7 @@ This section will explain in detail how the Library is to be used from the Ardui

Alternativly you may use the extended callback funtion which provides three parameters to the callback function `myCallback(Control *sender, int eventname, void * UserParameter)`. The `UserParameter` is provided as part of the `ESPUI.addControl` method set and allows the user to define contextual information that is to be presented to the callback function in an unmodified form.

-It also possible to use a lambda function in the callback parameter. It also allows the user to define, in a more C++ way, contextual information in any form. This is shown by the [completeLambda](examples/completeLambda/completeLambda.ino) example. -

-The below example creates a button and defines a lambda function to invoke a more specialized button callback handler: +The below example creates a button and defines a lambda function to implicitly create an `ExtendedCallback` which then invokes a more specialized button callback handler. The example uses the `UserParameter` to hold the `this` pointer to an object instance, providing a mechanism for sending the event to a specific object without the need for a switch / map / lookup translation of the Sender Id to an object reference. ``` void YourClassName::setup() { @@ -168,18 +163,26 @@ void YourClassName::setup() " Button Face Text ", ControlColor::None, ParentElementId, - [&](Control *sender, int eventname) + [](Control *sender, int eventname, void* param) { - myButtonCallback(sender, eventname); // class method - }); + if(param) + { + reinterpret_cast(param)->myButtonCallback(sender, eventname); + } + }, + this); // <-Third parameter for the extended callback // or ButtonElementId = ESPUI.button( " Button Face Text ", - [&](Control *sender, int eventname) + [](Control *sender, int eventname, void* param) { - myButtonCallback(sender, eventname); // class method - }); + if(param) + { + reinterpret_cast(param)->myButtonCallback(sender, eventname); + } + }, + this); // <-Third parameter for the extended callback } ``` ``` diff --git a/data/css/style.css b/data/css/style.css index f5d46f7..0fdd597 100644 --- a/data/css/style.css +++ b/data/css/style.css @@ -1198,3 +1198,79 @@ input:disabled { .switch.checked.disabled { background: #b1d092; } + +.Alert { + position: absolute; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + min-width: 350px; + max-width: 20%; + height: auto; + margin: 20px; + padding: 15px; + border-radius: 15px; + font-size: 20px; + text-align: center; + color: white; + background-color: rgba(0,100,0,0.75); +} + +.AlertIcon { + position: absolute; + font-size: 55px; + left: 25px +} + +/* Styles the div container for the message */ +.AlertMessage { + height: auto; + width: 80%; + overflow-wrap: break-word; +} + +/* Styles the div container for the controls */ +.AlertControls { + margin-top: 10px; + margin-bottom: 15px; +} + +/* Styles the buttons used for the dialog type */ +.BtnConfirm { + margin-right: 30px; + transition: 0.3s; + border-radius: 2px; + border: none; + padding: 8px; + padding-left: 15px; + padding-right: 15px; + outline: none; + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; + cursor: pointer; +} + +.BtnConfirm:hover { + background-color: rgba(37, 155, 53, 0.75); + color: white; + text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5); +} + +.BtnCancel { + margin-left: 30px; + transition: 0.3s; + border-radius: 2px; + border: none; + padding: 8px; + padding-left: 15px; + padding-right: 15px; + outline: none; + box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; + cursor: pointer; +} + +.BtnCancel:hover { + background-color: rgba(155, 37, 37, 0.75); + color: white; + text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5); +} diff --git a/data/css/style.min.css b/data/css/style.min.css index 28cdbdb..1ae65fa 100644 --- a/data/css/style.min.css +++ b/data/css/style.min.css @@ -1 +1 @@ -.container{position:relative;width:79%;margin:20px;box-sizing:border-box}.column,.columns{width:100%;float:left}.card{min-height:100px;border-radius:6px;box-shadow:0 4px 4px rgba(204,197,185,0.5);padding-left:20px;padding-right:20px;margin-bottom:40px;min-width:500px;color:#fff}@media(min-width:1205px){.wide.card{min-width:1075px}}@media(min-width:1790px){.wide.card{min-width:1650px}}@media(max-width:630px){.card{min-width:98%}}.sectionbreak.columns{color:black}.sectionbreak.columns hr{border:0;height:2px;background-color:#666}.turquoise{background:#1abc9c;border-bottom:#16a085 3px solid}.emerald{background:#2ecc71;border-bottom:#27ae60 3px solid}.peterriver{background:#3498db;border-bottom:#2980b9 3px solid}.wetasphalt{background:#34495e;border-bottom:#2c3e50 3px solid}.sunflower{background:#f1c40f;border-bottom:#e6bb0f 3px solid}.carrot{background:#e67e22;border-bottom:#d35400 3px solid}.alizarin{background:#e74c3c;border-bottom:#c0392b 3px solid}.dark{background:#444857;border-bottom:#444857 3px solid}.label{box-sizing:border-box;white-space:nowrap;border-radius:.2em;padding:.12em .4em .14em;text-align:center;color:#fff;font-weight:700;line-height:1.3;margin-bottom:5px;display:inline-block;white-space:nowrap;vertical-align:baseline;position:relative;top:-.15em;background-color:#999;margin-bottom:10px}.label-wrap{width:90%;white-space:pre-wrap;word-wrap:break-word}.label.color-blue{background-color:#6f9ad1}.label.color-red{background-color:#d37c7c}.label.color-green{background-color:#9bc268}.label.color-orange{background-color:#dea154}.label.color-yellow{background-color:#e9d641}.label.color-purple{background-color:#9f83d1}@media(min-width:400px){.container{width:84%}}@media(min-width:630px){.container{width:98%}.column,.columns{margin-right:35px}.column:first-child,.columns:first-child{margin-left:0}.one.column,.one.columns{width:4.66666666667%}.two.columns{width:13.3333333333%}.three.columns{width:22%}.four.columns{width:30.6666666667%}.five.columns{width:39.3333333333%}.six.columns{width:48%}.seven.columns{width:56.6666666667%}.eight.columns{width:65.3333333333%}.nine.columns{width:74%}.ten.columns{width:82.6666666667%}.eleven.columns{width:91.3333333333%}.twelve.columns{width:100%;margin-left:0}.one-third.column{width:30.6666666667%}.two-thirds.column{width:65.3333333333%}.one-half.column{width:48%}.offset-by-one.column,.offset-by-one.columns{margin-left:8.66666666667%}.offset-by-two.column,.offset-by-two.columns{margin-left:17.3333333333%}.offset-by-three.column,.offset-by-three.columns{margin-left:26%}.offset-by-four.column,.offset-by-four.columns{margin-left:34.6666666667%}.offset-by-five.column,.offset-by-five.columns{margin-left:43.3333333333%}.offset-by-six.column,.offset-by-six.columns{margin-left:52%}.offset-by-seven.column,.offset-by-seven.columns{margin-left:60.6666666667%}.offset-by-eight.column,.offset-by-eight.columns{margin-left:69.3333333333%}.offset-by-nine.column,.offset-by-nine.columns{margin-left:78%}.offset-by-ten.column,.offset-by-ten.columns{margin-left:86.6666666667%}.offset-by-eleven.column,.offset-by-eleven.columns{margin-left:95.3333333333%}.offset-by-one-third.column,.offset-by-one-third.columns{margin-left:34.6666666667%}.offset-by-two-thirds.column,.offset-by-two-thirds.columns{margin-left:69.3333333333%}.offset-by-one-half.column,.offset-by-one-half.columns{margin-left:52%}}html{font-size:62.5%}body{margin:0;font-size:1.5em;line-height:1;font-weight:400;font-family:"Open Sans",sans-serif;color:#222;background-color:#ecf0f1}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:300}h1{font-size:4rem;line-height:1.2;letter-spacing:-.1rem}h2{font-size:3.6rem;line-height:1.25;letter-spacing:-.1rem}h3{font-size:3rem;line-height:1.3;letter-spacing:-.1rem}h4{font-size:2.4rem;line-height:1.35;letter-spacing:-.08rem}h5{font-size:1.8rem;line-height:1.5;letter-spacing:-.05rem}h6{font-size:1.5rem;line-height:1.6;letter-spacing:0}@media(min-width:630px){h1{font-size:5rem}h2{font-size:4.2rem}h3{font-size:3.6rem}h4{font-size:3rem}h5{font-size:2rem}h6{font-size:1.5rem}}p{margin-top:0}a{color:#1eaedb}a:hover{color:#0fa0ce}button{display:inline-block;padding:10px;border-radius:3px;color:#fff;background-color:#999}button:enabled:active{background-color:#666;transform:translateX(4px) translateY(4px)}#mainHeader{display:inline-block}#conStatus{position:inherit;font-size:.75em}button,.button{margin-bottom:1rem;margin-left:.3rem;margin-right:.3rem}.u-full-width{width:100%;box-sizing:border-box}.u-max-full-width{max-width:100%;box-sizing:border-box}.u-pull-right{float:right}.u-pull-left{float:left}.tcenter{text-align:center}hr{margin-top:.5rem;margin-bottom:1.2rem;border-width:0;border-top:1px solid #e1e1e1}.container:after,.row:after,.u-cf{content:"";display:table;clear:both}.control{background-color:#ddd;background-image:linear-gradient(hsla(0,0%,0%,0.1),hsla(0,0%,100%,0.1));border-radius:50%;box-shadow:inset 0 1px 1px 1px hsla(0,0%,100%,0.5),0 0 1px 1px hsla(0,0%,100%,0.75),0 0 1px 2px hsla(0,0%,100%,0.25),0 0 1px 3px hsla(0,0%,100%,0.25),0 0 1px 4px hsla(0,0%,100%,0.25),0 0 1px 6px hsla(0,0%,0%,0.75);height:9em;margin:3em auto;position:relative;width:9em}.control ul{height:100%;padding:0;transform:rotate(45deg)}.control li{border-radius:100% 0 0 0;box-shadow:inset -1px -1px 1px hsla(0,0%,100%,0.5),0 0 1px hsla(0,0%,0%,0.75);display:inline-block;height:50%;overflow:hidden;width:50%}.control ul li:nth-child(2){transform:rotate(90deg)}.control ul li:nth-child(3){transform:rotate(-90deg)}.control ul li:nth-child(4){transform:rotate(180deg)}.control ul a{height:200%;position:relative;transform:rotate(-45deg);width:200%}.control a:hover,.control a:focus{background-color:hsla(0,0%,100%,0.25)}.control a{border-radius:50%;color:#333;display:block;font:bold 1em/3 sans-serif;text-align:center;text-decoration:none;text-shadow:0 1px 1px hsla(0,0%,100%,0.4);transition:.15s}.control .confirm{background-color:#ddd;background-image:linear-gradient(hsla(0,0%,0%,0.15),hsla(0,0%,100%,0.25));box-shadow:inset 0 1px 1px 1px hsla(0,0%,100%,0.5),0 0 1px 1px hsla(0,0%,100%,0.25),0 0 1px 2px hsla(0,0%,100%,0.25),0 0 1px 3px hsla(0,0%,100%,0.25),0 0 1px 4px hsla(0,0%,100%,0.25),0 0 1px 6px hsla(0,0%,0%,0.85);left:50%;line-height:3;margin:-1.5em;position:absolute;top:50%;width:3em}.control .confirm:hover,.control .confirm:focus{background-color:#eee}.control:not(.disabled) a.confirm:active{background-color:#777}.control:not(.disabled) li:active{background-color:#777}.switch{display:inline-block !important;background-color:#bebebe;border-radius:4px;box-shadow:inset 0 0 6px rgba(0,0,0,0.3);color:#fff;cursor:pointer;display:block;font-size:14px;height:26px;margin-left:.3rem;margin-right:.3rem;position:relative;width:60px;-webkit-transition:background-color .2s ease-in-out;-moz-transition:background-color .2s ease-in-out;-o-transition:background-color .2s ease-in-out;-ms-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out}.switch.checked{background-color:#76d21d}.switch input[type="checkbox"]{display:none;cursor:pointer;height:10px;left:12px;position:absolute;top:8px;width:10px}.in{position:absolute;top:8px;left:12px;-webkit-transition:left .08s ease-in-out;-moz-transition:left .08s ease-in-out;-o-transition:left .08s ease-in-out;-ms-transition:left .08s ease-in-out;transition:left .08s ease-in-out}.switch.checked div{left:38px}.switch .in:before{background:#fff;background:-moz-linear-gradient(top,#fff 0,#f0f0f0 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#f0f0f0));background:-webkit-linear-gradient(top,#fff 0,#f0f0f0 100%);background:-o-linear-gradient(top,#fff 0,#f0f0f0 100%);background:-ms-linear-gradient(top,#fff 0,#f0f0f0 100%);background:linear-gradient(to bottom,#fff 0,#f0f0f0 100%);border:1px solid #fff;border-radius:2px;box-shadow:0 0 4px rgba(0,0,0,0.3);content:"";height:18px;position:absolute;top:-5px;left:-9px;width:26px}.switch .in:after{background:#f0f0f0;background:-moz-linear-gradient(top,#f0f0f0 0,#fff 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#f0f0f0),color-stop(100%,#fff));background:-webkit-linear-gradient(top,#f0f0f0 0,#fff 100%);background:-o-linear-gradient(top,#f0f0f0 0,#fff 100%);background:-ms-linear-gradient(top,#f0f0f0 0,#fff 100%);background:linear-gradient(to bottom,#f0f0f0 0,#fff 100%);border-radius:10px;content:"";height:12px;margin:-1px 0 0 -1px;position:absolute;width:12px}.rkmd-slider{display:block;position:relative;font-size:16px;font-family:"Roboto",sans-serif}.rkmd-slider input[type="range"]{overflow:hidden;position:absolute;width:1px;height:1px;opacity:0}.rkmd-slider input[type="range"]+.slider{display:block;position:relative;width:100%;height:27px;border-radius:13px;background-color:#bebebe}@media(pointer:fine){.rkmd-slider input[type="range"]+.slider{height:4px;border-radius:0}}.rkmd-slider input[type="range"]+.slider .slider-fill{display:block;position:absolute;width:0;height:100%;user-select:none;z-index:1}.rkmd-slider input[type="range"]+.slider .slider-handle{cursor:pointer;position:absolute;top:12px;left:0;width:15px;height:15px;margin-left:-8px;border-radius:50%;transition:all .2s ease;user-select:none;z-index:2}@media(pointer:fine){.rkmd-slider input[type="range"]+.slider .slider-handle{top:-5.5px}}.rkmd-slider input[type="range"]:disabled+.slider{background-color:#b0b0b0 !important}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle{cursor:default !important;background-color:#b0b0b0 !important}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill .slider-label,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle .slider-label{display:none;background-color:#b0b0b0 !important}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill.is-active,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle.is-active{top:-5.5px;width:15px;height:15px;margin-left:-8px}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill.is-active .slider-label,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle.is-active .slider-label{display:none;border-radius:50%;transform:none}.rkmd-slider input[type="range"]:disabled+.slider .slider-handle:active{box-shadow:none !important;transform:scale(1) !important}.rkmd-slider.slider-discrete .slider .slider-handle{position:relative;z-index:1}.rkmd-slider.slider-discrete .slider .slider-handle .slider-label{position:absolute;top:-17.5px;left:4px;width:30px;height:30px;-webkit-transform-origin:50% 100%;transform-origin:50% 100%;border-radius:50%;-webkit-transform:scale(1) rotate(-45deg);transform:scale(1) rotate(-45deg);-webkit-transition:all .2s ease;transition:all .2s ease}@media(pointer:fine){.rkmd-slider.slider-discrete .slider .slider-handle .slider-label{left:-2px;-webkit-transform:scale(0.5) rotate(-45deg);transform:scale(0.5) rotate(-45deg)}}.rkmd-slider.slider-discrete .slider .slider-handle .slider-label span{position:absolute;top:7px;left:0;width:100%;color:#fff;font-size:16px;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media(pointer:fine){.rkmd-slider.slider-discrete .slider .slider-handle .slider-label span{font-size:12px}}.rkmd-slider.slider-discrete .slider .slider-handle.is-active{top:0;margin-left:-2px;width:4px;height:4px}.rkmd-slider.slider-discrete .slider .slider-handle.is-active .slider-label{top:-15px;left:-2px;border-radius:15px 15px 15px 0;-webkit-transform:rotate(-45deg) translate(23px,-25px);transform:rotate(-45deg) translate(23px,-25px)}.rkmd-slider.slider-discrete .slider .slider-handle.is-active .slider-label span{opacity:1}.rkmd-slider.slider-discrete.slider-turquoise .slider-label{background-color:#16a085}.rkmd-slider.slider-discrete.slider-emerald .slider-label{background-color:#27ae60}.peterriver{background:#3498db;border-bottom:#2980b9 3px solid}.rkmd-slider.slider-discrete.slider-peterriver .slider-label{background-color:#2980b9}.wetasphalt{background:#34495e;border-bottom:#2c3e50 3px solid}.rkmd-slider.slider-discrete.slider-wetasphalt .slider-label{background-color:#2c3e50}.sunflower{background:#f1c40f;border-bottom:#e6bb0f 3px solid}.rkmd-slider.slider-discrete.slider-sunflower .slider-label{background-color:#e6bb0f}.carrot{background:#e67e22;border-bottom:#d35400 3px solid}.rkmd-slider.slider-discrete.slider-carrot .slider-label{background-color:#d35400}.alizarin{background:#e74c3c;border-bottom:#c0392b 3px solid}.rkmd-slider.slider-discrete.slider-alizarin .slider-label{background-color:#c0392b}input{margin:0 auto 1.2rem auto;padding:2px 5px;width:100%;box-sizing:border-box;border:0;border-radius:4px;box-shadow:inset 0 0 6px rgba(0,0,0,0.3);background:rgba(255,255,255,0.8)}select{margin:0 auto 1.2rem auto;padding:2px 5px;width:100%;box-sizing:border-box;border:0;border-radius:4px;box-shadow:inset 0 0 6px rgba(0,0,0,0.3);background:rgba(255,255,255,0.8)}input[id^="num"]{max-width:6em;width:auto;text-align:right;font-weight:bold;font-size:115%}body div>ul.navigation{margin:0;margin-bottom:30px;padding:0;border-bottom:3px solid #666;overflow:hidden}ul.navigation li{list-style:none;float:left;margin-right:4px}ul.navigation li.controls{float:right}ul.navigation li a{font-weight:bold;display:inline-block;padding:6px 12px;color:#888;outline:0;text-decoration:none;background:#f3f3f3;background:-webkit-gradient(linear,0 0,0 bottom,from(#eee),to(#e4e4e4));background:-moz-linear-gradient(#eee,#e4e4e4);background:linear-gradient(#eee,#e4e4e4);-pie-background:linear-gradient(#eee,#e4e4e4)}ul.navigation li.active a{pointer-events:none;color:white;background:#666;background:-webkit-gradient(linear,0 0,0 bottom,from(#888),to(#666));background:-moz-linear-gradient(#888,#666);background:linear-gradient(#888,#666);-pie-background:linear-gradient(#888,#666)}div.tabscontent>div{padding:0 15px}#tabsnav:empty{display:none}.range-slider{margin:0}.range-slider{width:100%}.range-slider__range{-webkit-appearance:none;width:calc(100% - (45px));height:10px;border-radius:5px;outline:0;padding:0;margin:0}.range-slider__value{display:inline-block;position:relative;width:30px;color:#fff;line-height:20px;text-align:center;border-radius:3px;padding:5px 5px;margin-left:2px}.range-slider__value:after{position:absolute;top:8px;left:-7px;width:0;height:0;content:""}::-moz-range-track{border:0}input::-moz-focus-inner,input::-moz-focus-outer{border:0}svg{display:block;width:100%;height:100%}.y-axis path,.x-axis path{stroke:gray;stroke-width:1;fill:none}.series{stroke:steelblue;stroke-width:3;fill:none}.data-points circle{stroke:steelblue;stroke-width:2;fill:white}.data-points text{display:none}.data-points circle:hover{fill:steelblue;stroke-width:6}.data-points circle:hover+text{display:inline-block}text{text-anchor:end}.vert-switcher{transform:rotate(270deg);margin-top:15px;margin-bottom:25px}.vert-slider{width:150px;transform:rotate(270deg);display:inline-block;margin:50px -42px 70px -42px}.vert-slider span{transform:rotate(90deg)}button:disabled{color:#333;background-color:#999}select:disabled{color:#333;background-color:#999}input:disabled{color:#333;background-color:#999}.range-slider__range:disabled{background-color:#999}.range-slider__range:disabled::-webkit-slider-thumb{background-color:#aaa}.range-slider__range:disabled::-moz-range-thumb{background-color:#aaa}.switch.disabled .in::before{background:#bbb;border:1px solid #ddd}.switch.disabled .in::after{background:#bbb}.switch.checked.disabled{background:#b1d092} \ No newline at end of file +.container{position:relative;width:79%;margin:20px;box-sizing:border-box}.column,.columns{width:100%;float:left}.card{min-height:100px;border-radius:6px;box-shadow:0 4px 4px rgba(204,197,185,0.5);padding-left:20px;padding-right:20px;margin-bottom:40px;min-width:500px;color:#fff}@media(min-width:1205px){.wide.card{min-width:1075px}}@media(min-width:1790px){.wide.card{min-width:1650px}}@media(max-width:630px){.card{min-width:98%}}.sectionbreak.columns{color:black}.sectionbreak.columns hr{border:0;height:2px;background-color:#666}.turquoise{background:#1abc9c;border-bottom:#16a085 3px solid}.emerald{background:#2ecc71;border-bottom:#27ae60 3px solid}.peterriver{background:#3498db;border-bottom:#2980b9 3px solid}.wetasphalt{background:#34495e;border-bottom:#2c3e50 3px solid}.sunflower{background:#f1c40f;border-bottom:#e6bb0f 3px solid}.carrot{background:#e67e22;border-bottom:#d35400 3px solid}.alizarin{background:#e74c3c;border-bottom:#c0392b 3px solid}.dark{background:#444857;border-bottom:#444857 3px solid}.label{box-sizing:border-box;white-space:nowrap;border-radius:.2em;padding:.12em .4em .14em;text-align:center;color:#fff;font-weight:700;line-height:1.3;margin-bottom:5px;display:inline-block;white-space:nowrap;vertical-align:baseline;position:relative;top:-.15em;background-color:#999;margin-bottom:10px}.label-wrap{width:90%;white-space:pre-wrap;word-wrap:break-word}.label.color-blue{background-color:#6f9ad1}.label.color-red{background-color:#d37c7c}.label.color-green{background-color:#9bc268}.label.color-orange{background-color:#dea154}.label.color-yellow{background-color:#e9d641}.label.color-purple{background-color:#9f83d1}@media(min-width:400px){.container{width:84%}}@media(min-width:630px){.container{width:98%}.column,.columns{margin-right:35px}.column:first-child,.columns:first-child{margin-left:0}.one.column,.one.columns{width:4.66666666667%}.two.columns{width:13.3333333333%}.three.columns{width:22%}.four.columns{width:30.6666666667%}.five.columns{width:39.3333333333%}.six.columns{width:48%}.seven.columns{width:56.6666666667%}.eight.columns{width:65.3333333333%}.nine.columns{width:74%}.ten.columns{width:82.6666666667%}.eleven.columns{width:91.3333333333%}.twelve.columns{width:100%;margin-left:0}.one-third.column{width:30.6666666667%}.two-thirds.column{width:65.3333333333%}.one-half.column{width:48%}.offset-by-one.column,.offset-by-one.columns{margin-left:8.66666666667%}.offset-by-two.column,.offset-by-two.columns{margin-left:17.3333333333%}.offset-by-three.column,.offset-by-three.columns{margin-left:26%}.offset-by-four.column,.offset-by-four.columns{margin-left:34.6666666667%}.offset-by-five.column,.offset-by-five.columns{margin-left:43.3333333333%}.offset-by-six.column,.offset-by-six.columns{margin-left:52%}.offset-by-seven.column,.offset-by-seven.columns{margin-left:60.6666666667%}.offset-by-eight.column,.offset-by-eight.columns{margin-left:69.3333333333%}.offset-by-nine.column,.offset-by-nine.columns{margin-left:78%}.offset-by-ten.column,.offset-by-ten.columns{margin-left:86.6666666667%}.offset-by-eleven.column,.offset-by-eleven.columns{margin-left:95.3333333333%}.offset-by-one-third.column,.offset-by-one-third.columns{margin-left:34.6666666667%}.offset-by-two-thirds.column,.offset-by-two-thirds.columns{margin-left:69.3333333333%}.offset-by-one-half.column,.offset-by-one-half.columns{margin-left:52%}}html{font-size:62.5%}body{margin:0;font-size:1.5em;line-height:1;font-weight:400;font-family:"Open Sans",sans-serif;color:#222;background-color:#ecf0f1}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:300}h1{font-size:4rem;line-height:1.2;letter-spacing:-.1rem}h2{font-size:3.6rem;line-height:1.25;letter-spacing:-.1rem}h3{font-size:3rem;line-height:1.3;letter-spacing:-.1rem}h4{font-size:2.4rem;line-height:1.35;letter-spacing:-.08rem}h5{font-size:1.8rem;line-height:1.5;letter-spacing:-.05rem}h6{font-size:1.5rem;line-height:1.6;letter-spacing:0}@media(min-width:630px){h1{font-size:5rem}h2{font-size:4.2rem}h3{font-size:3.6rem}h4{font-size:3rem}h5{font-size:2rem}h6{font-size:1.5rem}}p{margin-top:0}a{color:#1eaedb}a:hover{color:#0fa0ce}button{display:inline-block;padding:10px;border-radius:3px;color:#fff;background-color:#999}button:enabled:active{background-color:#666;transform:translateX(4px) translateY(4px)}#mainHeader{display:inline-block}#conStatus{position:inherit;font-size:.75em}button,.button{margin-bottom:1rem;margin-left:.3rem;margin-right:.3rem}.u-full-width{width:100%;box-sizing:border-box}.u-max-full-width{max-width:100%;box-sizing:border-box}.u-pull-right{float:right}.u-pull-left{float:left}.tcenter{text-align:center}hr{margin-top:.5rem;margin-bottom:1.2rem;border-width:0;border-top:1px solid #e1e1e1}.container:after,.row:after,.u-cf{content:"";display:table;clear:both}.control{background-color:#ddd;background-image:linear-gradient(hsla(0,0%,0%,0.1),hsla(0,0%,100%,0.1));border-radius:50%;box-shadow:inset 0 1px 1px 1px hsla(0,0%,100%,0.5),0 0 1px 1px hsla(0,0%,100%,0.75),0 0 1px 2px hsla(0,0%,100%,0.25),0 0 1px 3px hsla(0,0%,100%,0.25),0 0 1px 4px hsla(0,0%,100%,0.25),0 0 1px 6px hsla(0,0%,0%,0.75);height:9em;margin:3em auto;position:relative;width:9em}.control ul{height:100%;padding:0;transform:rotate(45deg)}.control li{border-radius:100% 0 0 0;box-shadow:inset -1px -1px 1px hsla(0,0%,100%,0.5),0 0 1px hsla(0,0%,0%,0.75);display:inline-block;height:50%;overflow:hidden;width:50%}.control ul li:nth-child(2){transform:rotate(90deg)}.control ul li:nth-child(3){transform:rotate(-90deg)}.control ul li:nth-child(4){transform:rotate(180deg)}.control ul a{height:200%;position:relative;transform:rotate(-45deg);width:200%}.control a:hover,.control a:focus{background-color:hsla(0,0%,100%,0.25)}.control a{border-radius:50%;color:#333;display:block;font:bold 1em/3 sans-serif;text-align:center;text-decoration:none;text-shadow:0 1px 1px hsla(0,0%,100%,0.4);transition:.15s}.control .confirm{background-color:#ddd;background-image:linear-gradient(hsla(0,0%,0%,0.15),hsla(0,0%,100%,0.25));box-shadow:inset 0 1px 1px 1px hsla(0,0%,100%,0.5),0 0 1px 1px hsla(0,0%,100%,0.25),0 0 1px 2px hsla(0,0%,100%,0.25),0 0 1px 3px hsla(0,0%,100%,0.25),0 0 1px 4px hsla(0,0%,100%,0.25),0 0 1px 6px hsla(0,0%,0%,0.85);left:50%;line-height:3;margin:-1.5em;position:absolute;top:50%;width:3em}.control .confirm:hover,.control .confirm:focus{background-color:#eee}.control:not(.disabled) a.confirm:active{background-color:#777}.control:not(.disabled) li:active{background-color:#777}.switch{display:inline-block !important;background-color:#bebebe;border-radius:4px;box-shadow:inset 0 0 6px rgba(0,0,0,0.3);color:#fff;cursor:pointer;display:block;font-size:14px;height:26px;margin-left:.3rem;margin-right:.3rem;position:relative;width:60px;-webkit-transition:background-color .2s ease-in-out;-moz-transition:background-color .2s ease-in-out;-o-transition:background-color .2s ease-in-out;-ms-transition:background-color .2s ease-in-out;transition:background-color .2s ease-in-out}.switch.checked{background-color:#76d21d}.switch input[type="checkbox"]{display:none;cursor:pointer;height:10px;left:12px;position:absolute;top:8px;width:10px}.in{position:absolute;top:8px;left:12px;-webkit-transition:left .08s ease-in-out;-moz-transition:left .08s ease-in-out;-o-transition:left .08s ease-in-out;-ms-transition:left .08s ease-in-out;transition:left .08s ease-in-out}.switch.checked div{left:38px}.switch .in:before{background:#fff;background:-moz-linear-gradient(top,#fff 0,#f0f0f0 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(100%,#f0f0f0));background:-webkit-linear-gradient(top,#fff 0,#f0f0f0 100%);background:-o-linear-gradient(top,#fff 0,#f0f0f0 100%);background:-ms-linear-gradient(top,#fff 0,#f0f0f0 100%);background:linear-gradient(to bottom,#fff 0,#f0f0f0 100%);border:1px solid #fff;border-radius:2px;box-shadow:0 0 4px rgba(0,0,0,0.3);content:"";height:18px;position:absolute;top:-5px;left:-9px;width:26px}.switch .in:after{background:#f0f0f0;background:-moz-linear-gradient(top,#f0f0f0 0,#fff 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#f0f0f0),color-stop(100%,#fff));background:-webkit-linear-gradient(top,#f0f0f0 0,#fff 100%);background:-o-linear-gradient(top,#f0f0f0 0,#fff 100%);background:-ms-linear-gradient(top,#f0f0f0 0,#fff 100%);background:linear-gradient(to bottom,#f0f0f0 0,#fff 100%);border-radius:10px;content:"";height:12px;margin:-1px 0 0 -1px;position:absolute;width:12px}.rkmd-slider{display:block;position:relative;font-size:16px;font-family:"Roboto",sans-serif}.rkmd-slider input[type="range"]{overflow:hidden;position:absolute;width:1px;height:1px;opacity:0}.rkmd-slider input[type="range"]+.slider{display:block;position:relative;width:100%;height:27px;border-radius:13px;background-color:#bebebe}@media(pointer:fine){.rkmd-slider input[type="range"]+.slider{height:4px;border-radius:0}}.rkmd-slider input[type="range"]+.slider .slider-fill{display:block;position:absolute;width:0;height:100%;user-select:none;z-index:1}.rkmd-slider input[type="range"]+.slider .slider-handle{cursor:pointer;position:absolute;top:12px;left:0;width:15px;height:15px;margin-left:-8px;border-radius:50%;transition:all .2s ease;user-select:none;z-index:2}@media(pointer:fine){.rkmd-slider input[type="range"]+.slider .slider-handle{top:-5.5px}}.rkmd-slider input[type="range"]:disabled+.slider{background-color:#b0b0b0 !important}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle{cursor:default !important;background-color:#b0b0b0 !important}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill .slider-label,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle .slider-label{display:none;background-color:#b0b0b0 !important}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill.is-active,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle.is-active{top:-5.5px;width:15px;height:15px;margin-left:-8px}.rkmd-slider input[type="range"]:disabled+.slider .slider-fill.is-active .slider-label,.rkmd-slider input[type="range"]:disabled+.slider .slider-handle.is-active .slider-label{display:none;border-radius:50%;transform:none}.rkmd-slider input[type="range"]:disabled+.slider .slider-handle:active{box-shadow:none !important;transform:scale(1) !important}.rkmd-slider.slider-discrete .slider .slider-handle{position:relative;z-index:1}.rkmd-slider.slider-discrete .slider .slider-handle .slider-label{position:absolute;top:-17.5px;left:4px;width:30px;height:30px;-webkit-transform-origin:50% 100%;transform-origin:50% 100%;border-radius:50%;-webkit-transform:scale(1) rotate(-45deg);transform:scale(1) rotate(-45deg);-webkit-transition:all .2s ease;transition:all .2s ease}@media(pointer:fine){.rkmd-slider.slider-discrete .slider .slider-handle .slider-label{left:-2px;-webkit-transform:scale(0.5) rotate(-45deg);transform:scale(0.5) rotate(-45deg)}}.rkmd-slider.slider-discrete .slider .slider-handle .slider-label span{position:absolute;top:7px;left:0;width:100%;color:#fff;font-size:16px;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media(pointer:fine){.rkmd-slider.slider-discrete .slider .slider-handle .slider-label span{font-size:12px}}.rkmd-slider.slider-discrete .slider .slider-handle.is-active{top:0;margin-left:-2px;width:4px;height:4px}.rkmd-slider.slider-discrete .slider .slider-handle.is-active .slider-label{top:-15px;left:-2px;border-radius:15px 15px 15px 0;-webkit-transform:rotate(-45deg) translate(23px,-25px);transform:rotate(-45deg) translate(23px,-25px)}.rkmd-slider.slider-discrete .slider .slider-handle.is-active .slider-label span{opacity:1}.rkmd-slider.slider-discrete.slider-turquoise .slider-label{background-color:#16a085}.rkmd-slider.slider-discrete.slider-emerald .slider-label{background-color:#27ae60}.peterriver{background:#3498db;border-bottom:#2980b9 3px solid}.rkmd-slider.slider-discrete.slider-peterriver .slider-label{background-color:#2980b9}.wetasphalt{background:#34495e;border-bottom:#2c3e50 3px solid}.rkmd-slider.slider-discrete.slider-wetasphalt .slider-label{background-color:#2c3e50}.sunflower{background:#f1c40f;border-bottom:#e6bb0f 3px solid}.rkmd-slider.slider-discrete.slider-sunflower .slider-label{background-color:#e6bb0f}.carrot{background:#e67e22;border-bottom:#d35400 3px solid}.rkmd-slider.slider-discrete.slider-carrot .slider-label{background-color:#d35400}.alizarin{background:#e74c3c;border-bottom:#c0392b 3px solid}.rkmd-slider.slider-discrete.slider-alizarin .slider-label{background-color:#c0392b}input{margin:0 auto 1.2rem auto;padding:2px 5px;width:100%;box-sizing:border-box;border:0;border-radius:4px;box-shadow:inset 0 0 6px rgba(0,0,0,0.3);background:rgba(255,255,255,0.8)}select{margin:0 auto 1.2rem auto;padding:2px 5px;width:100%;box-sizing:border-box;border:0;border-radius:4px;box-shadow:inset 0 0 6px rgba(0,0,0,0.3);background:rgba(255,255,255,0.8)}input[id^="num"]{max-width:6em;width:auto;text-align:right;font-weight:bold;font-size:115%}body div>ul.navigation{margin:0;margin-bottom:30px;padding:0;border-bottom:3px solid #666;overflow:hidden}ul.navigation li{list-style:none;float:left;margin-right:4px}ul.navigation li.controls{float:right}ul.navigation li a{font-weight:bold;display:inline-block;padding:6px 12px;color:#888;outline:0;text-decoration:none;background:#f3f3f3;background:-webkit-gradient(linear,0 0,0 bottom,from(#eee),to(#e4e4e4));background:-moz-linear-gradient(#eee,#e4e4e4);background:linear-gradient(#eee,#e4e4e4);-pie-background:linear-gradient(#eee,#e4e4e4)}ul.navigation li.active a{pointer-events:none;color:white;background:#666;background:-webkit-gradient(linear,0 0,0 bottom,from(#888),to(#666));background:-moz-linear-gradient(#888,#666);background:linear-gradient(#888,#666);-pie-background:linear-gradient(#888,#666)}div.tabscontent>div{padding:0 15px}#tabsnav:empty{display:none}.range-slider{margin:0}.range-slider{width:100%}.range-slider__range{-webkit-appearance:none;width:calc(100% - (45px));height:10px;border-radius:5px;outline:0;padding:0;margin:0}.range-slider__value{display:inline-block;position:relative;width:30px;color:#fff;line-height:20px;text-align:center;border-radius:3px;padding:5px 5px;margin-left:2px}.range-slider__value:after{position:absolute;top:8px;left:-7px;width:0;height:0;content:""}::-moz-range-track{border:0}input::-moz-focus-inner,input::-moz-focus-outer{border:0}svg{display:block;width:100%;height:100%}.y-axis path,.x-axis path{stroke:gray;stroke-width:1;fill:none}.series{stroke:steelblue;stroke-width:3;fill:none}.data-points circle{stroke:steelblue;stroke-width:2;fill:white}.data-points text{display:none}.data-points circle:hover{fill:steelblue;stroke-width:6}.data-points circle:hover+text{display:inline-block}text{text-anchor:end}.vert-switcher{transform:rotate(270deg);margin-top:15px;margin-bottom:25px}.vert-slider{width:150px;transform:rotate(270deg);display:inline-block;margin:50px -42px 70px -42px}.vert-slider span{transform:rotate(90deg)}button:disabled{color:#333;background-color:#999}select:disabled{color:#333;background-color:#999}input:disabled{color:#333;background-color:#999}.range-slider__range:disabled{background-color:#999}.range-slider__range:disabled::-webkit-slider-thumb{background-color:#aaa}.range-slider__range:disabled::-moz-range-thumb{background-color:#aaa}.switch.disabled .in::before{background:#bbb;border:1px solid #ddd}.switch.disabled .in::after{background:#bbb}.switch.checked.disabled{background:#b1d092}.Alert{position:absolute;display:flex;flex-direction:row;justify-content:center;align-items:center;min-width:350px;max-width:20%;height:auto;margin:20px;padding:15px;border-radius:15px;font-size:20px;text-align:center;color:white;background-color:rgba(0,100,0,0.75)}.AlertIcon{position:absolute;font-size:55px;left:25px}.AlertMessage{height:auto;width:80%;overflow-wrap:break-word}.AlertControls{margin-top:10px;margin-bottom:15px}.BtnConfirm{margin-right:30px;transition:.3s;border-radius:2px;border:0;padding:8px;padding-left:15px;padding-right:15px;outline:0;box-shadow:rgba(0,0,0,0.35) 0 5px 15px;cursor:pointer}.BtnConfirm:hover{background-color:rgba(37,155,53,0.75);color:white;text-shadow:2px 2px 2px rgba(0,0,0,0.5)}.BtnCancel{margin-left:30px;transition:.3s;border-radius:2px;border:0;padding:8px;padding-left:15px;padding-right:15px;outline:0;box-shadow:rgba(0,0,0,0.35) 0 5px 15px;cursor:pointer}.BtnCancel:hover{background-color:rgba(155,37,37,0.75);color:white;text-shadow:2px 2px 2px rgba(0,0,0,0.5)} \ No newline at end of file diff --git a/data/js/controls.js b/data/js/controls.js index 84b001b..75d9372 100644 --- a/data/js/controls.js +++ b/data/js/controls.js @@ -4,6 +4,11 @@ const UPDATE_OFFSET = 100; const UI_EXTEND_GUI = 210; +const ALERT_I = 240; +const ALERT_W = 241; +const ALERT_E = 242; +const ALERT_S = 243; + const UI_TITEL = 0; const UI_PAD = 1; @@ -80,11 +85,169 @@ const C_DARK = 7; const C_NONE = 255; var controlAssemblyArray = new Object(); -var FragmentAssemblyTimer = new Object(); +var FragmentAssemblyTimer = 0; var graphData = new Array(); var hasAccel = false; var sliderContinuous = false; +class Alert { + static fire( + icon, + message, + position = "tl", + type = "", + options = {}, + onConfirm = function () {}, + onCancel = function () {} + ) { + // Creates essential elements + const el = document.createElement("div"); + el.className = "Alert"; + const divIcon = document.createElement("div"); + divIcon.className = "AlertIcon"; + const divMessage = document.createElement("div"); + divMessage.className = "AlertMessage"; + + // Appends elements to parent element + el.appendChild(divIcon); + el.appendChild(divMessage); + + // Handles icon selection based on parameter "icon" + if (icon == "succ") { + divIcon.innerHTML = '✓'; + } else if (icon == "err") { + divIcon.innerHTML = '⨷'; + el.style.backgroundColor = "rgba(180, 0, 0, 0.75)" + message = "ERROR
" + message; + } else if (icon == "info") { + divIcon.innerHTML = '⚠'; + } else if (icon == "warn") { + divIcon.innerHTML = "𝕎"; + el.style.backgroundColor = "rgba(100, 0, 0, 0.75);" + } + + // Sets message based on parameter "message" + divMessage.innerHTML = "

" + message + "

"; + + // Creates Alert instance of type "dialog" based on "type" parameter + if (type == "dialog") { + // Necessary changes to styling for dialog + el.style.flexDirection = "column"; + divIcon.style.position = "relative"; + divIcon.style.left = "0px"; + divIcon.style.marginTop = "15px"; + + // Creates necessary table elements for dialog + const divControls = document.createElement("div"); + divControls.className = "AlertControls"; + + const btnConfirm = document.createElement("button"); + btnConfirm.className = "BtnConfirm"; + btnConfirm.innerHTML = "Confirm"; + const btnCancel = document.createElement("button"); + btnCancel.className = "BtnCancel"; + btnCancel.innerHTML = "Cancel"; + + // Appends elements to parent element + el.appendChild(divControls); + divControls.appendChild(btnConfirm); + divControls.appendChild(btnCancel); + + // Handles functions on dialog button click + btnConfirm.onclick = function () { + onConfirm(); + removeAlert(el); + }; + + btnCancel.onclick = function () { + onCancel(); + removeAlert(el); + }; + + // Sets dialog specific options based on "options" object parameter + if (Object.keys(options).length > 0) { + if (options.confirmButtonText) { + btnConfirm.innerHTML = options.confirmButtonText; + } + if (options.cancelButtonText) { + btnCancel.innerHTML = options.cancelButtonText; + } + if (options.confirmButtonColor) { + btnConfirm.style.backgroundColor = options.confirmButtonColor; + } + if (options.cancelButtonColor) { + btnCancel.style.backgroundColor = options.cancelButtonColor; + } + } + } + + // Handles position based on parameter "position" + el.style.left = 0; + switch (position) { + case "tl": + el.style.top = 0; + break; + + case "tm": + el.style.right = 0; + el.style.marginLeft = "auto"; + el.style.marginRight = "auto"; + break; + + case "bl": + el.style.bottom = 0; + break; + + case "bm": + el.style.right = 0; + el.style.bottom = 0; + el.style.marginLeft = "auto"; + el.style.marginRight = "auto"; + break; + + case "center": + el.style.right = 0; + el.style.marginLeft = "auto"; + el.style.marginRight = "auto"; + el.style.marginTop = "20%"; + break; + } + + // Sets general options based on "options" object passed as parameter + if (Object.keys(options).length > 0) { + if (options.backgroundColor) { + el.style.backgroundColor = options.backgroundColor; + } + if (options.fontColor) { + divMessage.style.color = options.fontColor; + } + if (options.iconColor) { + divIcon.style.color = options.iconColor; + } + if (options.borderRadius) { + el.style.borderRadius = options.borderRadius; + } + if (options.border) { + el.style.border = options.border; + } + } + + // Finally appends Alert alert element to document body + document.body.appendChild(el); + + // Handles behavior of Alert alert upon deletion based on type + if (type == "") { //notification + setTimeout(() => { + el.parentNode.removeChild(el); + }, 8000); + } + + function remAlert(element) { + element.parentNode.removeChild(element); + } + } +} + function colorClass(colorId) { colorId = Number(colorId); switch (colorId) { @@ -193,11 +356,16 @@ function restart() { start(); } +function FragmentAssemblyTimerStop(){ + if(FragmentAssemblyTimer) + FragmentAssemblyTimer.forEach(element => { + clearInterval(element); + }); + FragmentAssemblyTimer = 0; + } + function conStatusError() { - FragmentAssemblyTimer.forEach(element => { - clearInterval(element); - }); - FragmentAssemblyTimer = new Object(); + FragmentAssemblyTimerStop(); controlAssemblyArray = new Object(); if (true === websockConnected) { @@ -222,8 +390,6 @@ function handleVisibilityChange() { function start() { let location = window.location.hostname; let port = window.location.port; -// let location = "192.168.10.229"; -// let port = ""; document.addEventListener("visibilitychange", handleVisibilityChange, false); if ( @@ -243,7 +409,7 @@ function start() { // console.info("Periodic Timer has expired"); // is the socket closed? if (websock.readyState === 3) { - // console.info("Web Socket Is Closed"); + console.info("Web Socket Is Closed"); restart(); } }, 5000); @@ -254,10 +420,7 @@ function start() { $("#conStatus").addClass("color-green"); $("#conStatus").text("Connected"); websockConnected = true; - FragmentAssemblyTimer.forEach(element => { - clearInterval(element); - }); - FragmentAssemblyTimer = new Object(); + controlAssemblyArray = new Object(); }; @@ -267,10 +430,7 @@ function start() { // console.log("Close code: '" + evt.code + "'"); console.log("websock close"); conStatusError(); - FragmentAssemblyTimer.forEach(element => { - clearInterval(element); - }); - FragmentAssemblyTimer = new Object(); + FragmentAssemblyTimerStop(); controlAssemblyArray = new Object(); }; @@ -280,10 +440,7 @@ function start() { // console.log("Error data: '" + evt.data + "'"); restart(); - FragmentAssemblyTimer.forEach(element => { - clearInterval(element); - }); - FragmentAssemblyTimer = new Object(); + FragmentAssemblyTimerStop(); controlAssemblyArray = new Object(); }; @@ -301,7 +458,7 @@ function start() { } var e = document.body; var center = ""; - // console.info("data.type: '" + data.type + "'"); + console.info("data.type: '" + data.type + "'"); switch (data.type) { case UI_INITIAL_GUI: @@ -354,6 +511,19 @@ function start() { document.title = data.label; $("#mainHeader").html(data.label); break; + + case ALERT_I: + Alert.fire("info", data.value); + break; + case ALERT_W: + Alert.fire("warn", data.value); + break; + case ALERT_E: + Alert.fire("err", data.value); + break; + case ALERT_S: + Alert.fire("succ", data.value); + break; /* Most elements have the same behaviour when added. @@ -773,6 +943,10 @@ function start() { function StartFragmentAssemblyTimer(Id) { StopFragmentAssemblyTimer(Id); + + if(!FragmentAssemblyTimer) + FragmentAssemblyTimer = new Object(); + FragmentAssemblyTimer[Id] = setInterval(function(_Id) { // does the fragment assembly still exist? @@ -791,13 +965,8 @@ function StartFragmentAssemblyTimer(Id) function StopFragmentAssemblyTimer(Id) { - if("undefined" !== typeof FragmentAssemblyTimer[Id]) - { - if(FragmentAssemblyTimer[Id]) - { - clearInterval(FragmentAssemblyTimer[Id]); - } - } + if(!FragmentAssemblyTimer && ("undefined" !== typeof FragmentAssemblyTimer[Id]) && FragmentAssemblyTimer[Id]) + clearInterval(FragmentAssemblyTimer[Id]); } function sliderchange(number) { diff --git a/data/js/controls.min.js b/data/js/controls.min.js index 520ba7c..0331a59 100644 --- a/data/js/controls.min.js +++ b/data/js/controls.min.js @@ -1,19 +1,35 @@ -const UI_INITIAL_GUI=200;const UI_RELOAD=201;const UPDATE_OFFSET=100;const UI_EXTEND_GUI=210;const UI_TITEL=0;const UI_PAD=1;const UPDATE_PAD=101;const UI_CPAD=2;const UPDATE_CPAD=102;const UI_BUTTON=3;const UPDATE_BUTTON=103;const UI_LABEL=4;const UPDATE_LABEL=104;const UI_SWITCHER=5;const UPDATE_SWITCHER=105;const UI_SLIDER=6;const UPDATE_SLIDER=106;const UI_NUMBER=7;const UPDATE_NUMBER=107;const UI_TEXT_INPUT=8;const UPDATE_TEXT_INPUT=108;const UI_GRAPH=9;const ADD_GRAPH_POINT=10;const CLEAR_GRAPH=109;const UI_TAB=11;const UPDATE_TAB=111;const UI_SELECT=12;const UPDATE_SELECT=112;const UI_OPTION=13;const UPDATE_OPTION=113;const UI_MIN=14;const UPDATE_MIN=114;const UI_MAX=15;const UPDATE_MAX=115;const UI_STEP=16;const UPDATE_STEP=116;const UI_GAUGE=17;const UPDATE_GAUGE=117;const UI_ACCEL=18;const UPDATE_ACCEL=118;const UI_SEPARATOR=19;const UPDATE_SEPARATOR=119;const UI_TIME=20;const UPDATE_TIME=120;const UI_FRAGMENT=21;const UP=0;const DOWN=1;const LEFT=2;const RIGHT=3;const CENTER=4;const C_TURQUOISE=0;const C_EMERALD=1;const C_PETERRIVER=2;const C_WETASPHALT=3;const C_SUNFLOWER=4;const C_CARROT=5;const C_ALIZARIN=6;const C_DARK=7;const C_NONE=255;var controlAssemblyArray=new Object();var FragmentAssemblyTimer=new Object();var graphData=new Array();var hasAccel=false;var sliderContinuous=false;function colorClass(colorId){colorId=Number(colorId);switch(colorId){case C_TURQUOISE:return"turquoise";case C_EMERALD:return"emerald";case C_PETERRIVER:return"peterriver";case C_WETASPHALT:return"wetasphalt";case C_SUNFLOWER:return"sunflower";case C_CARROT:return"carrot";case C_ALIZARIN:return"alizarin";case C_DARK:case C_NONE:return"dark";default:return"";}} +const UI_INITIAL_GUI=200;const UI_RELOAD=201;const UPDATE_OFFSET=100;const UI_EXTEND_GUI=210;const ALERT_I=240;const ALERT_W=241;const ALERT_E=242;const ALERT_S=243;const UI_TITEL=0;const UI_PAD=1;const UPDATE_PAD=101;const UI_CPAD=2;const UPDATE_CPAD=102;const UI_BUTTON=3;const UPDATE_BUTTON=103;const UI_LABEL=4;const UPDATE_LABEL=104;const UI_SWITCHER=5;const UPDATE_SWITCHER=105;const UI_SLIDER=6;const UPDATE_SLIDER=106;const UI_NUMBER=7;const UPDATE_NUMBER=107;const UI_TEXT_INPUT=8;const UPDATE_TEXT_INPUT=108;const UI_GRAPH=9;const ADD_GRAPH_POINT=10;const CLEAR_GRAPH=109;const UI_TAB=11;const UPDATE_TAB=111;const UI_SELECT=12;const UPDATE_SELECT=112;const UI_OPTION=13;const UPDATE_OPTION=113;const UI_MIN=14;const UPDATE_MIN=114;const UI_MAX=15;const UPDATE_MAX=115;const UI_STEP=16;const UPDATE_STEP=116;const UI_GAUGE=17;const UPDATE_GAUGE=117;const UI_ACCEL=18;const UPDATE_ACCEL=118;const UI_SEPARATOR=19;const UPDATE_SEPARATOR=119;const UI_TIME=20;const UPDATE_TIME=120;const UI_FRAGMENT=21;const UP=0;const DOWN=1;const LEFT=2;const RIGHT=3;const CENTER=4;const C_TURQUOISE=0;const C_EMERALD=1;const C_PETERRIVER=2;const C_WETASPHALT=3;const C_SUNFLOWER=4;const C_CARROT=5;const C_ALIZARIN=6;const C_DARK=7;const C_NONE=255;var controlAssemblyArray=new Object();var FragmentAssemblyTimer=0;var graphData=new Array();var hasAccel=false;var sliderContinuous=false;class Alert{static fire(icon,message,position="tl",type="",options={},onConfirm=function(){},onCancel=function(){}){const el=document.createElement("div");el.className="Alert";const divIcon=document.createElement("div");divIcon.className="AlertIcon";const divMessage=document.createElement("div");divMessage.className="AlertMessage";el.appendChild(divIcon);el.appendChild(divMessage);if(icon=="succ"){divIcon.innerHTML='✓';}else if(icon=="err"){divIcon.innerHTML='⨷';el.style.backgroundColor="rgba(180, 0, 0, 0.75)" +message="ERROR
"+message;}else if(icon=="info"){divIcon.innerHTML='⚠';}else if(icon=="warn"){divIcon.innerHTML="𝕎";el.style.backgroundColor="rgba(100, 0, 0, 0.75);"} +divMessage.innerHTML="

"+message+"

";if(type=="dialog"){el.style.flexDirection="column";divIcon.style.position="relative";divIcon.style.left="0px";divIcon.style.marginTop="15px";const divControls=document.createElement("div");divControls.className="AlertControls";const btnConfirm=document.createElement("button");btnConfirm.className="BtnConfirm";btnConfirm.innerHTML="Confirm";const btnCancel=document.createElement("button");btnCancel.className="BtnCancel";btnCancel.innerHTML="Cancel";el.appendChild(divControls);divControls.appendChild(btnConfirm);divControls.appendChild(btnCancel);btnConfirm.onclick=function(){onConfirm();removeAlert(el);};btnCancel.onclick=function(){onCancel();removeAlert(el);};if(Object.keys(options).length>0){if(options.confirmButtonText){btnConfirm.innerHTML=options.confirmButtonText;} +if(options.cancelButtonText){btnCancel.innerHTML=options.cancelButtonText;} +if(options.confirmButtonColor){btnConfirm.style.backgroundColor=options.confirmButtonColor;} +if(options.cancelButtonColor){btnCancel.style.backgroundColor=options.cancelButtonColor;}}} +el.style.left=0;switch(position){case"tl":el.style.top=0;break;case"tm":el.style.right=0;el.style.marginLeft="auto";el.style.marginRight="auto";break;case"bl":el.style.bottom=0;break;case"bm":el.style.right=0;el.style.bottom=0;el.style.marginLeft="auto";el.style.marginRight="auto";break;case"center":el.style.right=0;el.style.marginLeft="auto";el.style.marginRight="auto";el.style.marginTop="20%";break;} +if(Object.keys(options).length>0){if(options.backgroundColor){el.style.backgroundColor=options.backgroundColor;} +if(options.fontColor){divMessage.style.color=options.fontColor;} +if(options.iconColor){divIcon.style.color=options.iconColor;} +if(options.borderRadius){el.style.borderRadius=options.borderRadius;} +if(options.border){el.style.border=options.border;}} +document.body.appendChild(el);if(type==""){setTimeout(()=>{el.parentNode.removeChild(el);},8000);} +function remAlert(element){element.parentNode.removeChild(element);}}} +function colorClass(colorId){colorId=Number(colorId);switch(colorId){case C_TURQUOISE:return"turquoise";case C_EMERALD:return"emerald";case C_PETERRIVER:return"peterriver";case C_WETASPHALT:return"wetasphalt";case C_SUNFLOWER:return"sunflower";case C_CARROT:return"carrot";case C_ALIZARIN:return"alizarin";case C_DARK:case C_NONE:return"dark";default:return"";}} var websock;var websockConnected=false;var WebSocketTimer=null;function requestOrientationPermission(){} function saveGraphData(){localStorage.setItem("espuigraphs",JSON.stringify(graphData));} function restoreGraphData(id){var savedData=localStorage.getItem("espuigraphs",graphData);if(savedData!=null){savedData=JSON.parse(savedData);let idData=savedData[id];return Array.isArray(idData)?idData:[];} return[];} function restart(){$(document).add("*").off();$("#row").html("");conStatusError();start();} -function conStatusError(){FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Object();controlAssemblyArray=new Object();if(true===websockConnected){websockConnected=false;websock.close();$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection ↻");$("#conStatus").off();$("#conStatus").on({click:restart,});}} +function FragmentAssemblyTimerStop(){if(FragmentAssemblyTimer) +FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=0;} +function conStatusError(){FragmentAssemblyTimerStop();controlAssemblyArray=new Object();if(true===websockConnected){websockConnected=false;websock.close();$("#conStatus").removeClass("color-green");$("#conStatus").addClass("color-red");$("#conStatus").html("Error / No Connection ↻");$("#conStatus").off();$("#conStatus").on({click:restart,});}} function handleVisibilityChange(){if(!websockConnected&&!document.hidden){restart();}} function start(){let location=window.location.hostname;let port=window.location.port;document.addEventListener("visibilitychange",handleVisibilityChange,false);if(port!=""||port!=80||port!=443){websock=new WebSocket("ws://"+location+":"+port+"/ws");}else{websock=new WebSocket("ws://"+location+"/ws");} -if(null===WebSocketTimer){WebSocketTimer=setInterval(function(){if(websock.readyState===3){restart();}},5000);} -websock.onopen=function(evt){console.log("websock open");$("#conStatus").addClass("color-green");$("#conStatus").text("Connected");websockConnected=true;FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Object();controlAssemblyArray=new Object();};websock.onclose=function(evt){console.log("websock close");conStatusError();FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Object();controlAssemblyArray=new Object();};websock.onerror=function(evt){console.log("websock Error");restart();FragmentAssemblyTimer.forEach(element=>{clearInterval(element);});FragmentAssemblyTimer=new Object();controlAssemblyArray=new Object();};var handleEvent=function(evt){try{var data=JSON.parse(evt.data);} +if(null===WebSocketTimer){WebSocketTimer=setInterval(function(){if(websock.readyState===3){console.info("Web Socket Is Closed");restart();}},5000);} +websock.onopen=function(evt){console.log("websock open");$("#conStatus").addClass("color-green");$("#conStatus").text("Connected");websockConnected=true;controlAssemblyArray=new Object();};websock.onclose=function(evt){console.log("websock close");conStatusError();FragmentAssemblyTimerStop();controlAssemblyArray=new Object();};websock.onerror=function(evt){console.log("websock Error");restart();FragmentAssemblyTimerStop();controlAssemblyArray=new Object();};var handleEvent=function(evt){try{var data=JSON.parse(evt.data);} catch(Event){console.error(Event);websock.send("uiok:"+0);return;} -var e=document.body;var center="";switch(data.type){case UI_INITIAL_GUI:$("#row").html("");$("#tabsnav").html("");$("#tabscontent").html("");if(data.sliderContinuous){sliderContinuous=data.sliderContinuous;} +var e=document.body;var center="";console.info("data.type: '"+data.type+"'");switch(data.type){case UI_INITIAL_GUI:$("#row").html("");$("#tabsnav").html("");$("#tabscontent").html("");if(data.sliderContinuous){sliderContinuous=data.sliderContinuous;} data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});if(data.totalcontrols>(data.controls.length-1)){websock.send("uiok:"+(data.controls.length-1));} break;case UI_EXTEND_GUI:data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});if(data.totalcontrols>data.startindex+(data.controls.length-1)){websock.send("uiok:"+(data.startindex+(data.controls.length-1)));} -break;case UI_RELOAD:window.location.reload();break;case UI_TITEL:document.title=data.label;$("#mainHeader").html(data.label);break;case UI_LABEL:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GAUGE:case UI_SEPARATOR:if(data.visible)addToHTML(data);break;case UI_BUTTON:if(data.visible){addToHTML(data);$("#btn"+data.id).on({touchstart:function(e){e.preventDefault();buttonclick(data.id,true);},touchend:function(e){e.preventDefault();buttonclick(data.id,false);},});} +break;case UI_RELOAD:window.location.reload();break;case UI_TITEL:document.title=data.label;$("#mainHeader").html(data.label);break;case ALERT_I:Alert.fire("info",data.value);break;case ALERT_W:Alert.fire("warn",data.value);break;case ALERT_E:Alert.fire("err",data.value);break;case ALERT_S:Alert.fire("succ",data.value);break;case UI_LABEL:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GAUGE:case UI_SEPARATOR:if(data.visible)addToHTML(data);break;case UI_BUTTON:if(data.visible){addToHTML(data);$("#btn"+data.id).on({touchstart:function(e){e.preventDefault();buttonclick(data.id,true);},touchend:function(e){e.preventDefault();buttonclick(data.id,false);},});} break;case UI_SWITCHER:if(data.visible){addToHTML(data);switcher(data.id,data.value);} break;case UI_CPAD:case UI_PAD:if(data.visible){addToHTML(data);$("#pf"+data.id).on({touchstart:function(e){e.preventDefault();padclick(UP,data.id,true);},touchend:function(e){e.preventDefault();padclick(UP,data.id,false);},});$("#pl"+data.id).on({touchstart:function(e){e.preventDefault();padclick(LEFT,data.id,true);},touchend:function(e){e.preventDefault();padclick(LEFT,data.id,false);},});$("#pr"+data.id).on({touchstart:function(e){e.preventDefault();padclick(RIGHT,data.id,true);},touchend:function(e){e.preventDefault();padclick(RIGHT,data.id,false);},});$("#pb"+data.id).on({touchstart:function(e){e.preventDefault();padclick(DOWN,data.id,true);},touchend:function(e){e.preventDefault();padclick(DOWN,data.id,false);},});$("#pc"+data.id).on({touchstart:function(e){e.preventDefault();padclick(CENTER,data.id,true);},touchend:function(e){e.preventDefault();padclick(CENTER,data.id,false);},});} break;case UI_SLIDER:if(data.visible){addToHTML(data);rangeSlider(!sliderContinuous);} @@ -65,14 +81,14 @@ if(data.type==UPDATE_SLIDER){element.removeClass("slider-turquoise slider-emeral processEnabled(data);} $(".range-slider__range").each(function(){$(this)[0].value=$(this).attr("value");$(this).next().html($(this).attr("value"));});};websock.onmessage=handleEvent;} function StartFragmentAssemblyTimer(Id) -{StopFragmentAssemblyTimer(Id);FragmentAssemblyTimer[Id]=setInterval(function(_Id) +{StopFragmentAssemblyTimer(Id);if(!FragmentAssemblyTimer) +FragmentAssemblyTimer=new Object();FragmentAssemblyTimer[Id]=setInterval(function(_Id) {if("undefined"!==typeof controlAssemblyArray[_Id]) {if(null!==controlAssemblyArray[_Id]) {let TotalRequest=JSON.stringify({'id':controlAssemblyArray[_Id].control.id,'offset':controlAssemblyArray[_Id].offset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");}}},1000,Id);} function StopFragmentAssemblyTimer(Id) -{if("undefined"!==typeof FragmentAssemblyTimer[Id]) -{if(FragmentAssemblyTimer[Id]) -{clearInterval(FragmentAssemblyTimer[Id]);}}} +{if(!FragmentAssemblyTimer&&("undefined"!==typeof FragmentAssemblyTimer[Id])&&FragmentAssemblyTimer[Id]) +clearInterval(FragmentAssemblyTimer[Id]);} function sliderchange(number){var val=$("#sl"+number).val();websock.send("slvalue:"+val+":"+number);$(".range-slider__range").each(function(){$(this).attr("value",$(this)[0].value);});} function numberchange(number){var val=$("#num"+number).val();websock.send("nvalue:"+val+":"+number);} function textchange(number){var val=$("#text"+number).val();websock.send("tvalue:"+val+":"+number);} diff --git a/data/js/graph.min.js b/data/js/graph.min.js index fcc1846..7b0013b 100644 --- a/data/js/graph.min.js +++ b/data/js/graph.min.js @@ -1,15 +1,15 @@ -function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};} -function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%(pixelsPerTick*3)===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=new Date(Math.floor(transform(i))).toLocaleTimeString();text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}} -axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);} -function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} -var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+ -xTransform(xAccessor.next())+ -" "+ -yTransform(yAccessor.next());} -line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);} -function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} -while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);} -parent.appendChild(pointGroup);} -xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);} -function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);} +function lineGraph(parent,xAccessor,yAccessor){const width=620;const height=420;const gutter=40;const pixelsPerTick=30;function numericTransformer(dataMin,dataMax,pxMin,pxMax){var dataDiff=dataMax-dataMin,pxDiff=pxMax-pxMin,dataRatio=pxDiff/dataDiff,coordRatio=dataDiff/pxDiff;return{toCoord:function(data){return(data-dataMin)*dataRatio+pxMin;},toData:function(coord){return(coord-pxMin)*coordRatio+dataMin;}};} +function axisRenderer(orientation,transform){var axisGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var axisPath=document.createElementNS("http://www.w3.org/2000/svg","path");axisGroup.setAttribute("class",orientation+"-axis");var xMin=gutter;var xMax=width-gutter;var yMin=height-gutter;var yMax=gutter;if(orientation==="x"){axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMax+" "+yMin);for(var i=xMin;i<=xMax;i++){if((i-xMin)%(pixelsPerTick*3)===0&&i!==xMin){var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=new Date(Math.floor(transform(i))).toLocaleTimeString();text.setAttribute("x",i);text.setAttribute("y",yMin);text.setAttribute("dy","1em");axisGroup.appendChild(text);}}}else{axisPath.setAttribute("d","M "+xMin+" "+yMin+" L "+xMin+" "+yMax);for(var i=yMax;i<=yMin;i++){if((i-yMin)%pixelsPerTick===0&&i!==yMin){var tickGroup=document.createElementNS("http://www.w3.org/2000/svg","g");var gridLine=document.createElementNS("http://www.w3.org/2000/svg","path");text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(transform(i));text.setAttribute("x",xMin);text.setAttribute("y",i);text.setAttribute("dx","-.5em");text.setAttribute("dy",".3em");gridLine.setAttribute("d","M "+xMin+" "+i+" L "+xMax+" "+i);tickGroup.appendChild(gridLine);tickGroup.appendChild(text);axisGroup.appendChild(tickGroup);}}} +axisGroup.appendChild(axisPath);parent.appendChild(axisGroup);} +function lineRenderer(xAccessor,yAccessor,xTransform,yTransform){var line=document.createElementNS("http://www.w3.org/2000/svg","path");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} +var pathString="M "+xTransform(xAccessor.next())+" "+yTransform(yAccessor.next());while(xAccessor.hasNext()&&yAccessor.hasNext()){pathString+=" L "+ +xTransform(xAccessor.next())+ +" "+ +yTransform(yAccessor.next());} +line.setAttribute("class","series");line.setAttribute("d",pathString);parent.appendChild(line);} +function pointRenderer(xAccessor,yAccessor,xTransform,yTransform){var pointGroup=document.createElementNS("http://www.w3.org/2000/svg","g");pointGroup.setAttribute("class","data-points");xAccessor.reset();yAccessor.reset();if(!xAccessor.hasNext()||!yAccessor.hasNext()){return;} +while(xAccessor.hasNext()&&yAccessor.hasNext()){var xDataValue=xAccessor.next();var x=xTransform(xDataValue);var yDataValue=yAccessor.next();var y=yTransform(yDataValue);var circle=document.createElementNS("http://www.w3.org/2000/svg","circle");circle.setAttribute("cx",x);circle.setAttribute("cy",y);circle.setAttribute("r","4");var text=document.createElementNS("http://www.w3.org/2000/svg","text");text.innerHTML=Math.floor(xDataValue)+" / "+Math.floor(yDataValue);text.setAttribute("x",x);text.setAttribute("y",y);text.setAttribute("dx","1em");text.setAttribute("dy","-.7em");pointGroup.appendChild(circle);pointGroup.appendChild(text);} +parent.appendChild(pointGroup);} +xTransform=numericTransformer(xAccessor.min(),xAccessor.max(),0+gutter,width-gutter);yTransform=numericTransformer(yAccessor.min(),yAccessor.max(),height-gutter,0+gutter);axisRenderer("x",xTransform.toData);axisRenderer("y",yTransform.toData);lineRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);pointRenderer(xAccessor,yAccessor,xTransform.toCoord,yTransform.toCoord);} +function renderGraphSvg(dataArray,renderId){var figure=document.getElementById(renderId);while(figure.hasChildNodes()){figure.removeChild(figure.lastChild);} var svg=document.createElementNS("http://www.w3.org/2000/svg","svg");svg.setAttribute("viewBox","0 0 640 440");svg.setAttribute("preserveAspectRatio","xMidYMid meet");lineGraph(svg,(function(data,min,max){var i=0;return{hasNext:function(){return i #include #include -#ifndef CORE_MOCK #ifndef MMU_IRAM_HEAP #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) @@ -40,7 +39,6 @@ #error on ESP8266 and ESPUI, you must define OOM debug option when developping #endif #endif -#endif //Settings #define SLOW_BOOT 0 @@ -398,8 +396,7 @@ void extendedCallback(Control* sender, int type, void* param) Serial.print(sender->label); Serial.print("' = "); Serial.println(sender->value); - Serial.print("param = "); - Serial.println((long)param); + Serial.println(String("param = ") + String((int)param)); } void setup() { @@ -446,7 +443,6 @@ void loop() { #if !defined(ESP32) ((void (*)())0xf00fdead)(); #endif - break; default: Serial.print('#'); break; diff --git a/examples/completeExample/completeExample.ino b/examples/completeExample/completeExample.ino index a4db808..ff7bd09 100644 --- a/examples/completeExample/completeExample.ino +++ b/examples/completeExample/completeExample.ino @@ -1,4 +1 @@ // placeholder -#if CORE_MOCK -#include "completeExample.cpp" -#endif diff --git a/examples/gui-generic-api/gui-generic-api.ino b/examples/gui-generic-api/gui-generic-api.ino index 556596b..815e66d 100644 --- a/examples/gui-generic-api/gui-generic-api.ino +++ b/examples/gui-generic-api/gui-generic-api.ino @@ -11,17 +11,15 @@ DNSServer dnsServer; // esp8266 #include #include -#ifndef CORE_MOCK #ifndef MMU_IRAM_HEAP #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) #warning then check http:///heap #endif // MMU_IRAM_HEAP -#if !defined(DEBUG_ESP_OOM) +#ifndef DEBUG_ESP_OOM #error on ESP8266 and ESPUI, you must define OOM debug option when developping #endif #endif -#endif const char* ssid = "ESPUI"; const char* password = "espui"; @@ -69,8 +67,7 @@ void buttonCallback(Control* sender, int type) void buttonExample(Control* sender, int type, void* param) { - Serial.print("param: "); - Serial.println((long)param); + Serial.println(String("param: ") + String(long(param))); switch (type) { case B_DOWN: diff --git a/examples/gui/gui.ino b/examples/gui/gui.ino index 71ab831..1aca2f1 100644 --- a/examples/gui/gui.ino +++ b/examples/gui/gui.ino @@ -11,7 +11,6 @@ DNSServer dnsServer; // esp8266 #include #include -#ifndef CORE_MOCK #ifndef MMU_IRAM_HEAP #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) @@ -21,7 +20,6 @@ DNSServer dnsServer; #error on ESP8266 and ESPUI, you must define OOM debug option when developping #endif #endif -#endif const char* ssid = "ESPUI"; const char* password = "espui"; @@ -74,8 +72,7 @@ void buttonCallback(Control* sender, int type) void buttonExample(Control* sender, int type, void* param) { - Serial.print("param: "); - Serial.println((long)param); + Serial.println(String("param: ") + String(long(param))); switch (type) { case B_DOWN: diff --git a/examples/tabbedGui/tabbedGui.ino b/examples/tabbedGui/tabbedGui.ino index 4831a6b..7aa0b2f 100644 --- a/examples/tabbedGui/tabbedGui.ino +++ b/examples/tabbedGui/tabbedGui.ino @@ -11,7 +11,6 @@ DNSServer dnsServer; // esp8266 #include #include -#ifndef CORE_MOCK #ifndef MMU_IRAM_HEAP #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) #warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) @@ -21,7 +20,6 @@ DNSServer dnsServer; #error on ESP8266 and ESPUI, you must define OOM debug option when developping #endif #endif -#endif const char* ssid = "ESPUI"; const char* password = "espui"; @@ -68,8 +66,7 @@ void buttonCallback(Control* sender, int type) void buttonExample(Control* sender, int type, void* param) { - Serial.print("param: "); - Serial.println((long)param); + Serial.println(String("param: ") + String(long(param))); switch (type) { case B_DOWN: diff --git a/library.json b/library.json index 2d40950..76d3173 100644 --- a/library.json +++ b/library.json @@ -24,6 +24,11 @@ "name": "ArduinoJson", "authors": "Benoit Blanchon", "frameworks": "arduino" + }, + { + "name": "LittleFS_esp32", + "authors": "lorol", + "frameworks": "arduino" } ], "version": "2.2.3", diff --git a/library.properties b/library.properties index 05c272b..26ad357 100644 --- a/library.properties +++ b/library.properties @@ -5,6 +5,6 @@ maintainer=Lukas Bachschwell sentence=ESP32 and ESP8266 Web Interface Library paragraph=A simple library that implements a web graphical user interface for ESP32 and ESP8266. It is simple to use and works side by side with your sketch. category=Communication -url=https://github.com/s00500/ESPUI +url=https://github.com/telesyst/ESPUI architectures=* depends=ArduinoJson diff --git a/src/ESPUI.cpp b/src/ESPUI.cpp index 8fdb806..a5f44a3 100644 --- a/src/ESPUI.cpp +++ b/src/ESPUI.cpp @@ -5,7 +5,11 @@ #include #include "dataControlsJS.h" + +#ifndef ESPU_DISABLE_GRAPH #include "dataGraphJS.h" +#endif + #include "dataIndexHTML.h" #include "dataNormalizeCSS.h" #include "dataSliderJS.h" @@ -17,6 +21,14 @@ #include #endif +#if defined(DEBUG) && defined(ESPU_DEBUG) + #define ESPU_DBG(arg) Serial.print(arg) + #define ESPU_DBGL(arg) Serial.println(arg) +#else + #define ESPU_DBG(arg) + #define ESPU_DBGL(arg) +#endif + static String heapInfo(const __FlashStringHelper* mode) { String result; @@ -72,7 +84,7 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.printf_P(PSTR("Listing directory: %s\n"), dirname); + ESPU_DBGf_P(PSTR("Listing directory: %s\n"), dirname); } #endif @@ -91,7 +103,7 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("Failed to open directory")); + ESPU_DBGL(F("Failed to open directory")); } #endif @@ -103,7 +115,7 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("Not a directory")); + ESPU_DBGL(F("Not a directory")); } #endif @@ -119,8 +131,8 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.print(F(" DIR : ")); - Serial.println(file.name()); + ESPU_DBG(F(" DIR : ")); + ESPU_DBGL(file.name()); } #endif @@ -138,10 +150,10 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.print(F(" FILE: ")); - Serial.print(file.name()); - Serial.print(F(" SIZE: ")); - Serial.println(file.size()); + ESPU_DBG(F(" FILE: ")); + ESPU_DBG(file.name()); + ESPU_DBG(F(" SIZE: ")); + ESPU_DBGL(file.size()); } #endif } @@ -156,7 +168,7 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.printf_P(PSTR("Listing directory: %s\n"), dirname); + ESPU_DBGf_P(PSTR("Listing directory: %s\n"), dirname); } #endif @@ -169,8 +181,8 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.print(F(" DIR : ")); - Serial.println(dir.fileName()); + ESPU_DBG(F(" DIR : ")); + ESPU_DBGL(dir.fileName()); } #endif if (levels) @@ -185,10 +197,10 @@ void listDir(const char* dirname, uint8_t levels) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.print(F(" FILE: ")); - Serial.print(dir.fileName()); - Serial.print(F(" SIZE: ")); - Serial.println(dir.fileSize()); + ESPU_DBG(F(" FILE: ")); + ESPU_DBG(dir.fileName()); + ESPU_DBG(F(" SIZE: ")); + ESPU_DBGL(dir.fileSize()); } #endif } @@ -206,13 +218,13 @@ void ESPUIClass::list() if (!LITTLEFS.begin()) #endif { - Serial.println(F("LITTLEFS Mount Failed")); + ESPU_DBGL(F("LITTLEFS Mount Failed")); return; } #else if (!LittleFS.begin()) { - Serial.println(F("LittleFS Mount Failed")); + ESPU_DBG(F("LittleFS Mount Failed")); return; } #endif @@ -220,27 +232,27 @@ void ESPUIClass::list() listDir("/", 1); #if defined(ESP32) - Serial.print(F("Total KB: ")); + ESPU_DBG(F("Total KB: ")); #if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4 - Serial.println(LittleFS.totalBytes() / 1024); + ESPU_DBGL(LittleFS.totalBytes() / 1024); #else - Serial.println(LITTLEFS.totalBytes() / 1024); + ESPU_DBGL(LITTLEFS.totalBytes() / 1024); #endif - Serial.print(F("Used KB: ")); + ESPU_DBG(F("Used KB: ")); #if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION_MINOR >= 4) || ESP_IDF_VERSION_MAJOR > 4 - Serial.println(LittleFS.usedBytes() / 1024); + ESPU_DBGL(LittleFS.usedBytes() / 1024); #else - Serial.println(LITTLEFS.usedBytes() / 1024); + ESPU_DBGL(LITTLEFS.usedBytes() / 1024); #endif #else FSInfo fs_info; LittleFS.info(fs_info); - Serial.print(F("Total KB: ")); - Serial.println(fs_info.totalBytes / 1024); - Serial.print(F("Used KB: ")); - Serial.println(fs_info.usedBytes / 1024); + ESPU_DBG(F("Total KB: ")); + ESPU_DBGL(fs_info.totalBytes / 1024); + ESPU_DBG(F("Used KB: ")); + ESPU_DBGL(fs_info.usedBytes / 1024); #endif } @@ -261,7 +273,7 @@ void deleteFile(const char* path) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.printf_P(PSTR("File: %s does not exist, not deleting\n"), path); + ESPU_DBGf_P(PSTR("File: %s does not exist, not deleting\n"), path); } #endif @@ -271,7 +283,7 @@ void deleteFile(const char* path) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.printf_P(PSTR("Deleting file: %s\n"), path); + ESPU_DBGf_P(PSTR("Deleting file: %s\n"), path); } #endif @@ -289,7 +301,7 @@ void deleteFile(const char* path) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("File deleted")); + ESPU_DBGL(F("File deleted")); } #endif } @@ -298,7 +310,7 @@ void deleteFile(const char* path) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("Delete failed")); + ESPU_DBGL(F("Delete failed")); } #endif } @@ -309,7 +321,7 @@ void writeFile(const char* path, const char* data) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.printf_P(PSTR("Writing file: %s\n"), path); + ESPU_DBGf_P(PSTR("Writing file: %s\n"), path); } #endif @@ -327,7 +339,7 @@ void writeFile(const char* path, const char* data) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("Failed to open file for writing")); + ESPU_DBGL(F("Failed to open file for writing")); } #endif @@ -341,7 +353,7 @@ void writeFile(const char* path, const char* data) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("File written")); + ESPU_DBGL(F("File written")); } #endif } @@ -350,7 +362,7 @@ void writeFile(const char* path, const char* data) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("Write failed")); + ESPU_DBGL(F("Write failed")); } #endif } @@ -362,7 +374,7 @@ void writeFile(const char* path, const char* data) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("File written")); + ESPU_DBGL(F("File written")); } #endif } @@ -371,7 +383,7 @@ void writeFile(const char* path, const char* data) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("Write failed")); + ESPU_DBGL(F("Write failed")); } #endif } @@ -389,7 +401,7 @@ void ESPUIClass::prepareFileSystem(bool format) #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("About to prepare filesystem...")); + ESPU_DBGL(F("About to prepare filesystem...")); } #endif @@ -409,7 +421,7 @@ void ESPUIClass::prepareFileSystem(bool format) #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("LittleFS Format Failed")); + ESPU_DBGL(F("LittleFS Format Failed")); } #endif return; @@ -425,7 +437,7 @@ void ESPUIClass::prepareFileSystem(bool format) #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("LittleFS Formatted")); + ESPU_DBGL(F("LittleFS Formatted")); } #endif } @@ -434,7 +446,7 @@ void ESPUIClass::prepareFileSystem(bool format) if (verbosity) { listDir("/", 1); - Serial.println(F("LittleFS Mount ESP32 Done")); + ESPU_DBGL(F("LittleFS Mount ESP32 Done")); } #endif @@ -447,7 +459,7 @@ void ESPUIClass::prepareFileSystem(bool format) #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("LittleFS Formatted")); + ESPU_DBGL(F("LittleFS Formatted")); } #endif } @@ -456,7 +468,7 @@ void ESPUIClass::prepareFileSystem(bool format) #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("LittleFS Mount Failed")); + ESPU_DBGL(F("LittleFS Mount Failed")); } #endif return; @@ -468,7 +480,7 @@ void ESPUIClass::prepareFileSystem(bool format) #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("LittleFS Formatted")); + ESPU_DBGL(F("LittleFS Formatted")); } #endif } @@ -477,7 +489,7 @@ void ESPUIClass::prepareFileSystem(bool format) if (verbosity) { listDir("/", 1); - Serial.println(F("LittleFS Mount ESP8266 Done")); + ESPU_DBGL(F("LittleFS Mount ESP8266 Done")); } #endif @@ -491,13 +503,15 @@ void ESPUIClass::prepareFileSystem(bool format) deleteFile("/js/zepto.min.js"); deleteFile("/js/controls.js"); deleteFile("/js/slider.js"); +#ifndef ESPU_DISABLE_GRAPH deleteFile("/js/graph.js"); +#endif deleteFile("/js/tabbedcontent.js"); #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("Cleanup done")); + ESPU_DBGL(F("Cleanup done")); } #endif @@ -512,8 +526,9 @@ void ESPUIClass::prepareFileSystem(bool format) writeFile("/js/zepto.min.js", JS_ZEPTO); writeFile("/js/controls.js", JS_CONTROLS); writeFile("/js/slider.js", JS_SLIDER); +#ifndef ESPU_DISABLE_GRAPH writeFile("/js/graph.js", JS_GRAPH); - +#endif writeFile("/js/tabbedcontent.js", JS_TABBEDCONTENT); #else writeFile("/index.htm", HTML_INDEX); @@ -524,8 +539,9 @@ void ESPUIClass::prepareFileSystem(bool format) writeFile("/js/zepto.min.js", JS_ZEPTO); writeFile("/js/controls.js", JS_CONTROLS); writeFile("/js/slider.js", JS_SLIDER); +#ifndef ESPU_DISABLE_GRAPH writeFile("/js/graph.js", JS_GRAPH); - +#endif writeFile("/js/tabbedcontent.js", JS_TABBEDCONTENT); #endif #else @@ -537,15 +553,16 @@ void ESPUIClass::prepareFileSystem(bool format) writeFile("/js/zepto.min.js", JS_ZEPTO); writeFile("/js/controls.js", JS_CONTROLS); writeFile("/js/slider.js", JS_SLIDER); - writeFile("/js/graph.js", JS_GRAPH); - +#ifndef ESPU_DISABLE_GRAPH + writeFile("/js/graph.js", JS_GRAPH); +#endif writeFile("/js/tabbedcontent.js", JS_TABBEDCONTENT); #endif #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("Done Initializing filesystem :-)")); + ESPU_DBGL(F("Done Initializing filesystem :-)")); } #endif @@ -575,7 +592,7 @@ void ESPUIClass::prepareFileSystem(bool format) void ESPUIClass::onWsEvent( AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { - // Serial.println(String("ESPUIClass::OnWsEvent: type: ") + String(type)); + // ESPU_DBGL(String("ESPUIClass::OnWsEvent: type: ") + String(type)); RemoveToBeDeletedControls(); if (WS_EVT_DISCONNECT == type) @@ -583,13 +600,13 @@ void ESPUIClass::onWsEvent( #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("WS_EVT_DISCONNECT")); + ESPU_DBGL(F("WS_EVT_DISCONNECT")); } #endif if (MapOfClients.end() != MapOfClients.find(client->id())) { - // Serial.println("Delete client."); + // ESPU_DBGL("Delete client."); delete MapOfClients[client->id()]; MapOfClients.erase(client->id()); } @@ -598,13 +615,13 @@ void ESPUIClass::onWsEvent( { if (MapOfClients.end() == MapOfClients.find(client->id())) { - // Serial.println("ESPUIClass::OnWsEvent:Create new client."); + // ESPU_DBGL("ESPUIClass::OnWsEvent:Create new client."); MapOfClients[client->id()] = new ESPUIclient(client); } if(MapOfClients[client->id()]->onWsEvent(type, arg, data, len)) { - // Serial.println("ESPUIClass::OnWsEvent:notify the clients that they need to be updated."); + // ESPU_DBGL("ESPUIClass::OnWsEvent:notify the clients that they need to be updated."); NotifyClients(ESPUIclient::UpdateNeeded); } } @@ -630,7 +647,19 @@ uint16_t ESPUIClass::addControl(ControlType type, const char* label, const Strin uint16_t ESPUIClass::addControl( ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl) { - return addControl(type, label, value, color, parentControl, new Control(type, label, nullptr, value, color, true, parentControl)); + Control * ctrl = new Control(type, label, nullptr, value, color, true, parentControl); + if (auto_update_values && ctrl) + ctrl->auto_update_value = true; + return addControl(ctrl); +} + +uint16_t ESPUIClass::addControl( + ControlType type, const __FlashStringHelper* label, const String& value, ControlColor color, uint16_t parentControl) +{ + Control* ctrl = new Control(type, label, nullptr, value, color, true, parentControl); + if (auto_update_values && ctrl) + ctrl->auto_update_value = true; + return addControl(ctrl); } uint16_t ESPUIClass::addControl(ControlType type, const char* label, const String& value, ControlColor color, @@ -642,8 +671,16 @@ uint16_t ESPUIClass::addControl(ControlType type, const char* label, const Strin return id; } -uint16_t ESPUIClass::addControl( - ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, Control* control) +uint16_t ESPUIClass::addControl(ControlType type, const __FlashStringHelper* label, const String& value, ControlColor color, + uint16_t parentControl, std::function callback) +{ + uint16_t id = addControl(type, label, value, color, parentControl); + // set the original style callback + getControl(id)->callback = callback; + return id; +} + +uint16_t ESPUIClass::addControl(Control* control) { #ifdef ESP32 xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); @@ -699,7 +736,7 @@ bool ESPUIClass::removeControl(uint16_t id, bool force_rebuild_ui) #ifdef DEBUG_ESPUI else { - // Serial.println(String("Could not Remove Control ") + String(id)); + // ESPU_DBGL(String("Could not Remove Control ") + String(id)); } #endif // def DEBUG_ESPUI @@ -768,6 +805,10 @@ uint16_t ESPUIClass::button(const char* label, std::function callback, uint16_t parentControl, ControlColor color){ + return addControl(ControlType::Button, label, text, color, parentControl, callback); +} + uint16_t ESPUIClass::switcher(const char* label, std::function callback, ControlColor color, bool startState) { return addControl(ControlType::Switcher, label, startState ? "1" : "0", color, Control::noParent, callback); @@ -853,7 +894,7 @@ Control* ESPUIClass::getControlNoLock(uint16_t id) void ESPUIClass::updateControl(Control* control, int) { - if (!control) + if (!control || !ws) { return; } @@ -916,7 +957,7 @@ void ESPUIClass::setEnabled(uint16_t id, bool enabled, int clientId) Control* control = getControl(id); if (control) { - // Serial.println(String("CreateAllowed: id: ") + String(clientId) + " State: " + String(enabled)); + // ESPU_DBGL(String("CreateAllowed: id: ") + String(clientId) + " State: " + String(enabled)); control->enabled = enabled; updateControl(control, clientId); } @@ -940,7 +981,7 @@ void ESPUIClass::updateControl(uint16_t id, int clientId) #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.printf_P(PSTR("Error: Update Control: There is no control with ID %d\n"), id); + ESPU_DBGf_P(PSTR("Error: Update Control: There is no control with ID %d\n"), id); } #endif return; @@ -969,7 +1010,7 @@ void ESPUIClass::updateControlValue(uint16_t id, const String& value, int client #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.printf_P(PSTR("Error: updateControlValue Control: There is no control with ID %d\n"), id); + ESPU_DBGf_P(PSTR("Error: updateControlValue Control: There is no control with ID %d\n"), id); } #endif return; @@ -990,12 +1031,30 @@ void ESPUIClass::updateControlLabel(Control* control, const char* value, int cli #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.printf_P(PSTR("Error: updateControlLabel Control: There is no control with the requested ID \n")); + ESPU_DBGf_P(PSTR("Error: updateControlLabel Control: There is no control with the requested ID \n")); } #endif return; } - control->label = value; + control->label_r = value; + control->lablel_is_in_flash = 0; + updateControl(control, clientId); +} + +void ESPUIClass::updateControlLabel(Control* control, const __FlashStringHelper* value, int clientId) +{ + if (!control) + { +#if defined(DEBUG_ESPUI) + if (verbosity) + { + ESPU_DBGf_P(PSTR("Error: updateControlLabel Control: There is no control with the requested ID \n")); + } +#endif + return; + } + control->label_f = value; + control->lablel_is_in_flash = 1; updateControl(control, clientId); } @@ -1103,11 +1162,11 @@ void ESPUIClass::addGraphPoint(uint16_t id, int nValue, int clientId) } while (false); } -bool ESPUIClass::SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, uint16_t clientId) +bool ESPUIClass::SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, int clientId) { bool Response = false; - if (0 > clientId) + if (clientId >= 0) { if (MapOfClients.end() != MapOfClients.find(clientId)) { @@ -1143,7 +1202,7 @@ void ESPUIClass::jsonReload() { for (auto& CurrentClient : MapOfClients) { - // Serial.println("Requesting Reload"); + // ESPU_DBGL("Requesting Reload"); CurrentClient.second->NotifyClient(ClientUpdateType_t::ReloadNeeded); } } @@ -1186,7 +1245,7 @@ void ESPUIClass::beginLITTLEFS(const char* _title, const char* username, const c #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("LITTLEFS Mount Failed, PLEASE CHECK THE README ON HOW TO " + ESPU_DBGL(F("LITTLEFS Mount Failed, PLEASE CHECK THE README ON HOW TO " "PREPARE YOUR ESP!!!!!!!")); } #endif @@ -1215,7 +1274,7 @@ void ESPUIClass::beginLITTLEFS(const char* _title, const char* username, const c #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("Please read the README!!!!!!!, Make sure to " + ESPU_DBGL(F("Please read the README!!!!!!!, Make sure to " "prepareFileSystem() once in an empty sketch")); } #endif @@ -1282,7 +1341,7 @@ void ESPUIClass::beginLITTLEFS(const char* _title, const char* username, const c #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("UI Initialized")); + ESPU_DBGL(F("UI Initialized")); } #endif } @@ -1362,17 +1421,18 @@ void ESPUIClass::begin(const char* _title, const char* username, const char* pas request->send(response); }); +#ifndef ESPU_DISABLE_GRAPH server->on("/js/graph.js", HTTP_GET, [](AsyncWebServerRequest* request) { if (ESPUI.basicAuth && !request->authenticate(ESPUI.basicAuthUsername, ESPUI.basicAuthPassword)) { return request->requestAuthentication(); } - AsyncWebServerResponse* response = request->beginResponse_P(200, "application/javascript", JS_GRAPH_GZIP, sizeof(JS_GRAPH_GZIP)); response->addHeader("Content-Encoding", "gzip"); request->send(response); }); +#endif server->on("/js/tabbedcontent.js", HTTP_GET, [](AsyncWebServerRequest* request) { if (ESPUI.basicAuth && !request->authenticate(ESPUI.basicAuthUsername, ESPUI.basicAuthPassword)) @@ -1432,13 +1492,17 @@ void ESPUIClass::begin(const char* _title, const char* username, const char* pas request->send(404); } }); + + if (onCreateServerCallback) { + onCreateServerCallback(server); + } server->begin(); #if defined(DEBUG_ESPUI) if (verbosity) { - Serial.println(F("UI Initialized")); + ESPU_DBGL(F("UI Initialized")); } #endif } @@ -1448,4 +1512,18 @@ void ESPUIClass::setVerbosity(Verbosity v) verbosity = v; } +void ESPUIClass::Alert(const char * message,alert_type_t alert_type, int clientId) +{ + if (!message && !strlen(message)) + return; + + DynamicJsonDocument document(jsonUpdateDocumentSize); + JsonObject root = document.to(); + + root[F("type")] = (int)MessageTypes::AlertInfo + (int)alert_type; + root[F("value")] = message; + + SendJsonDocToWebSocket(document, clientId); +} + ESPUIClass ESPUI; diff --git a/src/ESPUI.h b/src/ESPUI.h index 23d65ad..f5e6609 100644 --- a/src/ESPUI.h +++ b/src/ESPUI.h @@ -28,9 +28,7 @@ #else -#include #include -#include #include #include @@ -47,6 +45,10 @@ enum MessageTypes : uint8_t ExtendGUI = 210, UpdateGui = 220, ExtendedUpdateGui = 230, + AlertInfo = 240, + AlertWarning, + AlertError, + AllertSuccess }; #define UI_INITIAL_GUI MessageTypes::InitialGui @@ -105,8 +107,16 @@ public: bool sliderContinuous = false; void onWsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); bool captivePortal = true; + bool auto_update_values = false; void setVerbosity(Verbosity verbosity); + + typedef std::function onCreateServerCallback_t; + void onCreateServerCallbackSet(onCreateServerCallback_t callback) + { + onCreateServerCallback = callback; + } + void begin(const char* _title, const char* username = nullptr, const char* password = nullptr, uint16_t port = 80); // Setup server and page in Memorymode void beginSPIFFS(const char* _title, const char* username = nullptr, const char* password = nullptr, @@ -122,13 +132,18 @@ public: uint16_t addControl(ControlType type, const char* label, const String& value); uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color); uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl); + uint16_t addControl(ControlType type, const __FlashStringHelper* label, const String& value, ControlColor color, uint16_t parentControl); uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, std::function callback); + uint16_t addControl(ControlType type, const __FlashStringHelper * label, const String& value, ControlColor color, uint16_t parentControl, std::function callback); bool removeControl(uint16_t id, bool force_rebuild_ui = false); // create Elements // Create Event Button uint16_t button(const char* label, std::function callback, ControlColor color, const String& value = ""); + uint16_t button(const __FlashStringHelper* label, const __FlashStringHelper* value, + std::function callback, uint16_t parentControl = Control::noParent, ControlColor color = ControlColor::Dark); + uint16_t switcher(const char* label, std::function callback, ControlColor color, bool startState = false); // Create Toggle Button uint16_t pad(const char* label, std::function callback, ControlColor color); // Create Pad Control uint16_t padWithCenter(const char* label, std::function callback, ControlColor color); // Create Pad Control with Centerbutton @@ -158,6 +173,7 @@ public: void updateControlLabel(uint16_t control, const char * value, int clientId = -1); void updateControlLabel(Control* control, const char * value, int clientId = -1); + void updateControlLabel(Control* control, const __FlashStringHelper* value, int clientId = -1); void updateControl(uint16_t id, int clientId = -1); void updateControl(Control* control, int clientId = -1); @@ -186,8 +202,31 @@ public: void updateVisibility(uint16_t id, bool visibility, int clientId = -1); + typedef enum + { + ALERT_INFO = 0, + ALERT_WARNING, + ALERT_ERROR, + ALERT_SUCCESS + } alert_type_t; + + void Alert(const char* message, alert_type_t alert_type = ALERT_INFO, int clientId = -1); + void AlertInfo(const char* message) { Alert(message); } + void AlertWarning(const char* message){ Alert(message, ALERT_WARNING); } + void AlertError(const char* message){ Alert(message, ALERT_ERROR); } + void AlertSuccess(const char* message){ Alert(message, ALERT_SUCCESS); } + void AlertInfo(String& message) { AlertInfo(message.c_str()); } + void AlertWarning(String& message){ AlertWarning(message.c_str()); } + void AlertError(String& message){ AlertError(message.c_str()); } + void AlertSuccess(String& message){ AlertSuccess(message.c_str()); } + void AlertInfo(const __FlashStringHelper * message) { AlertInfo(String(message).c_str()); } + void AlertWarning(const __FlashStringHelper * message){ AlertWarning(String(message).c_str()); } + void AlertError(const __FlashStringHelper * message){ AlertError(String(message).c_str()); } + void AlertSuccess(const __FlashStringHelper * message){ AlertSuccess(String(message).c_str()); } + // Variables const char* ui_title = "ESPUI"; // Store UI Title and Header Name + Control* controls = nullptr; void jsonReload(); void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false); @@ -231,6 +270,9 @@ public: { return accelerometer(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color); } + + AsyncWebServer* WebServer() {return server;} + AsyncWebSocket* WebSocket() {return ws;} AsyncWebServer* WebServer() {return server;} AsyncWebSocket* WebSocket() {return ws;} @@ -247,19 +289,21 @@ protected: AsyncWebServer* server; AsyncWebSocket* ws; - + + + onCreateServerCallback_t onCreateServerCallback = nullptr; const char* basicAuthUsername = nullptr; const char* basicAuthPassword = nullptr; bool basicAuth = true; uint16_t controlCount = 0; - uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, Control* control); + uint16_t addControl(Control* control); #define ClientUpdateType_t ESPUIclient::ClientUpdateType_t void NotifyClients(ClientUpdateType_t newState); void NotifyClient(uint32_t WsClientId, ClientUpdateType_t newState); - bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, uint16_t clientId); + bool SendJsonDocToWebSocket(ArduinoJson::DynamicJsonDocument& document, int clientId); std::map MapOfClients; diff --git a/src/ESPUIclient.cpp b/src/ESPUIclient.cpp index a0b6cc3..75eb2d5 100644 --- a/src/ESPUIclient.cpp +++ b/src/ESPUIclient.cpp @@ -2,6 +2,15 @@ #include "ESPUIclient.h" #include "ESPUIcontrol.h" +#if defined(DEBUG) && defined(ESPU_DEBUG) + #define ESPU_DBG(arg) Serial.print(arg) + #define ESPU_DBGL(arg) Serial.println(arg) +#else + #define ESPU_DBG(arg) + #define ESPU_DBGL(arg) +#endif + + // JSONSlave: // helper to process exact JSON serialization size // it takes ~2ms on esp8266 and avoid large String reallocation which is really worth the cost @@ -100,7 +109,7 @@ bool ESPUIclient::SendClientNotification(ClientUpdateType_t value) { if(!CanSend()) { - // Serial.println(F("ESPUIclient::SendClientNotification:CannotSend")); + // ESPU_DBGL(F("ESPUIclient::SendClientNotification:CannotSend")); break; } @@ -108,13 +117,13 @@ bool ESPUIclient::SendClientNotification(ClientUpdateType_t value) FillInHeader(document); if(ClientUpdateType_t::ReloadNeeded == value) { - // Serial.println(F("ESPUIclient::SendClientNotification:set type to reload")); + // ESPU_DBGL(F("ESPUIclient::SendClientNotification:set type to reload")); document["type"] = int(UI_RELOAD); } // dont send any controls Response = SendJsonDocToWebSocket(document); - // Serial.println(String("ESPUIclient::SendClientNotification:NotificationSent:Response: ") + String(Response)); + // ESPU_DBGL(String("ESPUIclient::SendClientNotification:NotificationSent:Response: ") + String(Response)); } while (false); return Response; @@ -130,7 +139,7 @@ void ESPUIclient::NotifyClient(ClientUpdateType_t newState) bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t len) { bool Response = false; - // Serial.println(String("ESPUIclient::OnWsEvent: type: ") + String(type)); + // ESPU_DBGL(String("ESPUIclient::OnWsEvent: type: ") + String(type)); switch (type) { @@ -139,7 +148,7 @@ bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_PONG")); + ESPU_DBGL(F("ESPUIclient::OnWsEvent:WS_EVT_PONG")); } #endif break; @@ -150,7 +159,7 @@ bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_ERROR")); + ESPU_DBGL(F("ESPUIclient::OnWsEvent:WS_EVT_ERROR")); } #endif break; @@ -161,19 +170,19 @@ bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_CONNECT")); - Serial.println(client->id()); + ESPU_DBGL(F("ESPUIclient::OnWsEvent:WS_EVT_CONNECT")); + ESPU_DBGL(client->id()); } #endif - // Serial.println("ESPUIclient:onWsEvent:WS_EVT_CONNECT: Call NotifyClient: RebuildNeeded"); + // ESPU_DBGL("ESPUIclient:onWsEvent:WS_EVT_CONNECT: Call NotifyClient: RebuildNeeded"); NotifyClient(ClientUpdateType_t::RebuildNeeded); break; } case WS_EVT_DATA: { - // Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_DATA")); + // ESPU_DBGL(F("ESPUIclient::OnWsEvent:WS_EVT_DATA")); String msg = ""; msg.reserve(len + 1); @@ -189,50 +198,50 @@ bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t #if defined(DEBUG_ESPUI) if (ESPUI.verbosity >= Verbosity::VerboseJSON) { - Serial.println(String(F(" WS msg: ")) + msg); - Serial.println(String(F(" WS cmd: ")) + cmd); - Serial.println(String(F(" WS id: ")) + String(id)); - Serial.println(String(F("WS value: ")) + String(value)); + ESPU_DBGL(String(F(" WS msg: ")) + msg); + ESPU_DBGL(String(F(" WS cmd: ")) + cmd); + ESPU_DBGL(String(F(" WS id: ")) + String(id)); + ESPU_DBGL(String(F("WS value: ")) + String(value)); } #endif if (cmd.equals(F("uiok"))) { - // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck:")) + pCurrentFsmState->GetStateName()); + // ESPU_DBGL(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck:")) + pCurrentFsmState->GetStateName()); pCurrentFsmState->ProcessAck(id, emptyString); break; } if (cmd.equals(F("uifragmentok"))) { - // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck"); + // ESPU_DBGL(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck"); if(!emptyString.equals(value)) { - // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck:value:'" + value + "'"); + // ESPU_DBGL(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:uifragmentok:")) + pCurrentFsmState->GetStateName() + ":ProcessAck:value:'" + value + "'"); pCurrentFsmState->ProcessAck(uint16_t(-1), value); } else { - Serial.println(F("ERROR:ESPUIclient::OnWsEvent:WS_EVT_DATA:uifragmentok:ProcessAck:Fragment Header is missing")); + ESPU_DBGL(F("ERROR:ESPUIclient::OnWsEvent:WS_EVT_DATA:uifragmentok:ProcessAck:Fragment Header is missing")); } break; } if (cmd.equals(F("uiuok"))) { - // Serial.println(F("WS_EVT_DATA: uiuok. Unlock new async notifications")); + // ESPU_DBGL(F("WS_EVT_DATA: uiuok. Unlock new async notifications")); break; } - // Serial.println(F("WS_EVT_DATA:Process Control")); + // ESPU_DBGL(F("WS_EVT_DATA:Process Control")); Control* control = ESPUI.getControl(id); if (nullptr == control) { #if defined(DEBUG_ESPUI) if (ESPUI.verbosity) { - Serial.println(String(F("No control found for ID ")) + String(id)); + ESPU_DBGL(String(F("No control found for ID ")) + String(id)); } #endif break; @@ -245,7 +254,7 @@ bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t default: { - // Serial.println(F("ESPUIclient::OnWsEvent:default")); + // ESPU_DBGL(F("ESPUIclient::OnWsEvent:default")); break; } } // end switch @@ -267,7 +276,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, xSemaphoreTake(ESPUI.ControlsSemaphore, portMAX_DELAY); #endif // def ESP32 - // Serial.println(String("prepareJSONChunk: Start. InUpdateMode: ") + String(InUpdateMode)); + // ESPU_DBGL(String("prepareJSONChunk: Start. InUpdateMode: ") + String(InUpdateMode)); int elementcount = 0; do // once @@ -281,10 +290,10 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, if(!emptyString.equals(FragmentRequestString)) { - // Serial.println(F("prepareJSONChunk:Fragmentation:Got Header (1)")); - // Serial.println(String("prepareJSONChunk:startindex: ") + String(startindex)); - // Serial.println(String("prepareJSONChunk:currentIndex: ") + String(currentIndex)); - // Serial.println(String("prepareJSONChunk:FragmentRequestString: '") + FragmentRequestString + "'"); + // ESPU_DBGL(F("prepareJSONChunk:Fragmentation:Got Header (1)")); + // ESPU_DBGL(String("prepareJSONChunk:startindex: ") + String(startindex)); + // ESPU_DBGL(String("prepareJSONChunk:currentIndex: ") + String(currentIndex)); + // ESPU_DBGL(String("prepareJSONChunk:FragmentRequestString: '") + FragmentRequestString + "'"); // this is actually a fragment or directed update request // parse the string we got from the UI and try to update that specific @@ -292,38 +301,38 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, DynamicJsonDocument FragmentRequest(FragmentRequestString.length() * 3); if(0 >= FragmentRequest.capacity()) { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not allocate memory for a fragmentation request. Skipping Response")); + ESPU_DBGL(F("ERROR:prepareJSONChunk:Fragmentation:Could not allocate memory for a fragmentation request. Skipping Response")); break; } size_t FragmentRequestStartOffset = FragmentRequestString.indexOf("{"); DeserializationError error = deserializeJson(FragmentRequest, FragmentRequestString.substring(FragmentRequestStartOffset)); if(DeserializationError::Ok != error) { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not extract json from the fragment request")); + ESPU_DBGL(F("ERROR:prepareJSONChunk:Fragmentation:Could not extract json from the fragment request")); break; } if(!FragmentRequest.containsKey(F("id"))) { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a control ID")); + ESPU_DBGL(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a control ID")); break; } uint16_t ControlId = uint16_t(FragmentRequest[F("id")]); if(!FragmentRequest.containsKey(F("offset"))) { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a starting offset")); + ESPU_DBGL(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a starting offset")); break; } DataOffset = uint16_t(FragmentRequest[F("offset")]); control = ESPUI.getControlNoLock(ControlId); if(nullptr == control) { - Serial.println(String(F("ERROR:prepareJSONChunk:Fragmentation:Requested control: ")) + String(ControlId) + F(" does not exist")); + ESPU_DBGL(String(F("ERROR:prepareJSONChunk:Fragmentation:Requested control: ")) + String(ControlId) + F(" does not exist")); break; } - // Serial.println(F("prepareJSONChunk:Fragmentation:disable the control search operation")); + // ESPU_DBGL(F("prepareJSONChunk:Fragmentation:disable the control search operation")); currentIndex = 1; startindex = 0; SingleControl = true; @@ -355,7 +364,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, // any controls left to be processed? if(nullptr == control) { - // Serial.println("prepareJSONChunk: No controls to process"); + // ESPU_DBGL("prepareJSONChunk: No controls to process"); break; } @@ -367,7 +376,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, // skip deleted controls or controls that have not been updated if (control->ToBeDeleted() && !SingleControl) { - // Serial.println(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id)); + // ESPU_DBGL(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id)); control = control->next; continue; } @@ -395,8 +404,8 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, // String("prepareJSONChunk: too much data in the message. Remove the last entry"); if (1 == elementcount) { - Serial.println(String(F("ERROR: prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser.")); - // Serial.println(String(F("ERROR: prepareJSONChunk: value: ")) + control->value); + ESPU_DBGL(String(F("ERROR: prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser.")); + // ESPU_DBGL(String(F("ERROR: prepareJSONChunk: value: ")) + control->value); rootDoc.clear(); item = items.createNestedObject(); control->MarshalErrorMessage(item); @@ -404,8 +413,8 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, } else { - // Serial.println(String("prepareJSONChunk: Defering control: ") + String(control->id)); - // Serial.println(String("prepareJSONChunk: elementcount: ") + String(elementcount)); + // ESPU_DBGL(String("prepareJSONChunk: Defering control: ") + String(control->id)); + // ESPU_DBGL(String("prepareJSONChunk: elementcount: ") + String(elementcount)); items.remove(elementcount); --elementcount; @@ -415,7 +424,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, } else if (SingleControl) { - // Serial.println("prepareJSONChunk: exit loop"); + // ESPU_DBGL("prepareJSONChunk: exit loop"); control = nullptr; } else @@ -430,7 +439,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, xSemaphoreGive(ESPUI.ControlsSemaphore); #endif // def ESP32 - // Serial.println(String("prepareJSONChunk: elementcount: ") + String(elementcount)); + // ESPU_DBGL(String("prepareJSONChunk: elementcount: ") + String(elementcount)); return elementcount; } @@ -457,18 +466,18 @@ etc. bool ESPUIclient::SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode, String FragmentRequest) { bool Response = false; - // Serial.println(String("ESPUIclient:SendControlsToClient:startidx: ") + String(startidx)); + // ESPU_DBGL(String("ESPUIclient:SendControlsToClient:startidx: ") + String(startidx)); do // once { if(!CanSend()) { - // Serial.println("ESPUIclient:SendControlsToClient: Cannot Send to clients."); + // ESPU_DBGL("ESPUIclient:SendControlsToClient: Cannot Send to clients."); break; } else if ((startidx >= ESPUI.controlCount) && (emptyString.equals(FragmentRequest))) { - // Serial.println(F("ERROR:ESPUIclient:SendControlsToClient: No more controls to send.")); + // ESPU_DBGL(F("ERROR:ESPUIclient:SendControlsToClient: No more controls to send.")); Response = true; break; } @@ -480,44 +489,44 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx, ClientUpdateType_t Tra if(0 == startidx) { - // Serial.println("ESPUIclient:SendControlsToClient: Tell client we are starting a transfer of controls."); + // ESPU_DBGL("ESPUIclient:SendControlsToClient: Tell client we are starting a transfer of controls."); document["type"] = (ClientUpdateType_t::RebuildNeeded == TransferMode) ? UI_INITIAL_GUI : UI_EXTEND_GUI; CurrentSyncID = NextSyncID; NextSyncID = ESPUI.GetNextControlChangeId(); } - // Serial.println(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"])); + // ESPU_DBGL(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"])); - // Serial.println("ESPUIclient:SendControlsToClient: Build Controls."); + // ESPU_DBGL("ESPUIclient:SendControlsToClient: Build Controls."); if(prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode, FragmentRequest)) { #if defined(DEBUG_ESPUI) if (ESPUI.verbosity >= Verbosity::VerboseJSON) { - Serial.println(F("ESPUIclient:SendControlsToClient: Sending elements --------->")); + ESPU_DBGL(F("ESPUIclient:SendControlsToClient: Sending elements --------->")); serializeJson(document, Serial); - Serial.println(); + ESPU_DBGL(); } #endif - // Serial.println("ESPUIclient:SendControlsToClient: Send message."); + // ESPU_DBGL("ESPUIclient:SendControlsToClient: Send message."); if(true == SendJsonDocToWebSocket(document)) { - // Serial.println("ESPUIclient:SendControlsToClient: Sent."); + // ESPU_DBGL("ESPUIclient:SendControlsToClient: Sent."); } else { - // Serial.println("ESPUIclient:SendControlsToClient: Send failed."); + // ESPU_DBGL("ESPUIclient:SendControlsToClient: Send failed."); } } else { - // Serial.println("ESPUIclient:SendControlsToClient: No elements to send."); + // ESPU_DBGL("ESPUIclient:SendControlsToClient: No elements to send."); Response = true; } } while(false); - // Serial.println(String("ESPUIclient:SendControlsToClient:Response: ") + String(Response)); + // ESPU_DBGL(String("ESPUIclient:SendControlsToClient:Response: ") + String(Response)); return Response; } @@ -532,10 +541,10 @@ bool ESPUIclient::SendJsonDocToWebSocket(DynamicJsonDocument& document) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity >= Verbosity::VerboseJSON) { - Serial.println(F("ESPUIclient::SendJsonDocToWebSocket: Cannot Send to client. Not sending websocket message")); + ESPU_DBGL(F("ESPUIclient::SendJsonDocToWebSocket: Cannot Send to client. Not sending websocket message")); } #endif - // Serial.println("ESPUIclient::SendJsonDocToWebSocket: Cannot Send to client. Not sending websocket message"); + // ESPU_DBGL("ESPUIclient::SendJsonDocToWebSocket: Cannot Send to client. Not sending websocket message"); Response = false; break; } @@ -545,17 +554,17 @@ bool ESPUIclient::SendJsonDocToWebSocket(DynamicJsonDocument& document) #if defined(DEBUG_ESPUI) if (ESPUI.verbosity >= Verbosity::VerboseJSON) { - Serial.println(String(F("ESPUIclient::SendJsonDocToWebSocket: json: '")) + json + "'"); + ESPU_DBGL(String(F("ESPUIclient::SendJsonDocToWebSocket: json: '")) + json + "'"); } #endif #if defined(DEBUG_ESPUI) if (ESPUI.verbosity >= Verbosity::VerboseJSON) { - Serial.println(F("ESPUIclient::SendJsonDocToWebSocket: client.text")); + ESPU_DBGL(F("ESPUIclient::SendJsonDocToWebSocket: client.text")); } #endif - // Serial.println(F("ESPUIclient::SendJsonDocToWebSocket: client.text")); + // ESPU_DBGL(F("ESPUIclient::SendJsonDocToWebSocket: client.text")); client->text(json); } while (false); diff --git a/src/ESPUIcontrol.cpp b/src/ESPUIcontrol.cpp index 75606de..26b9343 100644 --- a/src/ESPUIcontrol.cpp +++ b/src/ESPUIcontrol.cpp @@ -1,282 +1,302 @@ -#include "ESPUI.h" - -static uint16_t idCounter = 0; -static const String ControlError = "*** ESPUI ERROR: Could not transfer control ***"; - -Control::Control(ControlType type, const char* label, std::function callback, - const String& value, ControlColor color, bool visible, uint16_t parentControl) - : type(type), - label(label), - callback(callback), - value(value), - color(color), - visible(visible), - wide(false), - vertical(false), - enabled(true), - parentControl(parentControl), - next(nullptr) -{ - id = ++idCounter; - ControlChangeID = 1; -} - -Control::Control(const Control& Control) - : type(Control.type), - id(Control.id), - label(Control.label), - callback(Control.callback), - value(Control.value), - color(Control.color), - visible(Control.visible), - parentControl(Control.parentControl), - next(Control.next), - ControlChangeID(Control.ControlChangeID) -{ } - -void Control::SendCallback(int type) -{ - if(callback) - { - callback(this, type); - } -} - -void Control::DeleteControl() -{ - _ToBeDeleted = true; - callback = nullptr; -} - -void Control::MarshalControl(JsonObject & _item, bool refresh, uint32_t StartingOffset) -{ - JsonObject & item = _item; - uint32_t length = value.length(); - uint32_t maxLength = uint32_t(double(ESPUI.jsonInitialDocumentSize) * 0.75); - if((length > maxLength) || StartingOffset) - { - /* - Serial.println(String("MarshalControl:Start Fragment Processing")); - Serial.println(String("MarshalControl:id: ") + String(id)); - Serial.println(String("MarshalControl:length: ") + String(length)); - Serial.println(String("MarshalControl:StartingOffset: ") + String(StartingOffset)); - Serial.println(String("MarshalControl:maxLength: ") + String(maxLength)); - - if(0 == StartingOffset) - { - Serial.println(String("MarshalControl: New control to fragement. ID: ") + String(id)); - } - else - { - Serial.println(String("MarshalControl: Next fragement. ID: ") + String(id)); - } - */ - - // indicate that no additional controls should be sent - _item[F("type")] = uint32_t(ControlType::Fragment); - _item[F("id")] = id; - - length = min((length - StartingOffset), maxLength); - // Serial.println(String("MarshalControl:Final length: ") + String(length)); - - _item[F("offset")] = StartingOffset; - _item[F("length")] = length; - _item[F("total")] = value.length(); - item = _item.createNestedObject(F("control")); - } - - item[F("id")] = id; - ControlType TempType = (ControlType::Password == type) ? ControlType::Text : type; - if(refresh) - { - item[F("type")] = uint32_t(TempType) + uint32_t(ControlType::UpdateOffset); - } - else - { - item[F("type")] = uint32_t(TempType); - } - - item[F("label")] = label; - item[F ("value")] = (ControlType::Password == type) ? F ("--------") : value.substring(StartingOffset, length + StartingOffset); - item[F("visible")] = visible; - item[F("color")] = (int)color; - item[F("enabled")] = enabled; - - if (!panelStyle.isEmpty()) {item[F("panelStyle")] = panelStyle;} - if (!elementStyle.isEmpty()) {item[F("elementStyle")] = elementStyle;} - if (!inputType.isEmpty()) {item[F("inputType")] = inputType;} - if (wide == true) {item[F("wide")] = true;} - if (vertical == true) {item[F("vertical")] = true;} - if (parentControl != Control::noParent) - { - item[F("parentControl")] = String(parentControl); - } - - // special case for selects: to preselect an option, you have to add - // "selected" to