diff --git a/README.md b/README.md index da14940..ee04e88 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,12 @@ A number box needs to have a min and a max value. To set it up just use: `ESPUI.number("Numbertest", &numberCall, ControlColor::Alizarin, 5, 0, 10);` +Note that HTML number boxes will respect their min and max when the user +clicks the up and down arrows, but it is possible on most clients to simply type +any number in. As with all user input, numbers should be validated in callback code +because all client side checks can be skipped. If any value from the UI might +cause a problem, validate it. + #### Text Input ![text](https://github.com/s00500/ESPUI/blob/master/docs/ui_text.png) @@ -267,6 +273,17 @@ The textinput works very similar like the number input but with a string. You can enter a String into it and when you are done with your change it is sent to the ESP. +If you attach a Max control to the text input then a max length will be applied +to the control. + +``` + text = ESPUI.text("Label", callback, ControlColor::Dark, "Initial value"); + ESPUI.addControl(ControlType::Max, "", "32", ControlColor::None, text); +``` + +However even with a set maximum length, user input should still be validated +because it is easy to bypass client-side checks. Never trust user input. + #### Graph ![graph](https://github.com/s00500/ESPUI/blob/master/docs/ui_graph.png) diff --git a/data/js/controls.js b/data/js/controls.js index 19f0a19..9339c08 100644 --- a/data/js/controls.js +++ b/data/js/controls.js @@ -102,6 +102,7 @@ function colorClass(colorId) { case C_ALIZARIN: return "alizarin"; + case C_DARK: case C_NONE: return "dark"; default: @@ -252,6 +253,13 @@ function start() { }; handleEvent(fauxEvent); }); + + //If there are more elements in the complete UI, then request them + //Note: we subtract 1 from data.controls.length because the controls always + //includes the title element + if(data.totalcontrols > (data.controls.length - 1)) { + websock.send("uiok:" + (data.controls.length - 1)); + } break; case UI_EXTEND_GUI: @@ -261,6 +269,11 @@ function start() { }; handleEvent(fauxEvent); }); + + //Do we need to keep requesting more UI elements? + if(data.totalcontrols > data.startindex + (data.controls.length - 1)) { + websock.send("uiok:" + (data.startindex + (data.controls.length - 1))); + } break; case UI_RELOAD: @@ -428,7 +441,13 @@ function start() { if (data.parentControl) { var parent = $("#id" + data.parentControl + " input"); if (parent.size()) { - parent.attr("max", data.value); + if(!parent.attr("type")) { + //type is not set so therefore it is a text input + parent.attr("maxlength", data.value); + } else { + //type might be range (slider) or number + parent.attr("max", data.value); + } } } break; @@ -488,6 +507,7 @@ function start() { break; case UPDATE_SLIDER: + $("#sl" + data.id).attr("value", data.value) slider_move($("#id" + data.id), data.value, "100", false); if(data.hasOwnProperty('elementStyle')) { $("#sl" + data.id).attr("style", data.elementStyle); @@ -516,6 +536,12 @@ function start() { break; case UPDATE_BUTTON: + $("#btn" + data.id).val(data.value); + if(data.hasOwnProperty('elementStyle')) { + $("#btn" + data.id).attr("style", data.elementStyle); + } + break; + case UPDATE_PAD: case UPDATE_CPAD: break; @@ -565,6 +591,10 @@ function start() { 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) { @@ -619,7 +649,7 @@ function padclick(type, number, isdown) { function switcher(number, state) { if (state == null) { - if ($("#s" + number).is(":checked")) { + if (!$("#sl" + number).hasClass("checked")) { websock.send("sactive:" + number); $("#sl" + number).addClass("checked"); } else { @@ -686,7 +716,7 @@ var addToHTML = function(data) { case UI_ACCEL: html = "
" + data.label + "

" + - elementHTML(data.type, data.id, data.value, elementStyle) + + elementHTML(data.type, data.id, data.value, data.label, elementStyle) + "
"; break; case UI_SEPARATOR: @@ -700,11 +730,11 @@ var addToHTML = function(data) { } else { //We are adding to an existing panel so we only need the HTML for the element var parent = $("#id" + data.parentControl); - parent.append(elementHTML(data.type, data.id, data.value, elementStyle)); + parent.append(elementHTML(data.type, data.id, data.value, data.label, elementStyle)); } } -var elementHTML = function(type, id, value, elementStyle) { +var elementHTML = function(type, id, value, label, elementStyle) { switch(type) { case UI_LABEL: return "{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});break;case UI_EXTEND_GUI:data.controls.forEach(element=>{var fauxEvent={data:JSON.stringify(element),};handleEvent(fauxEvent);});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);},});} +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_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);} @@ -24,37 +26,39 @@ data.selected+ data.label+ "");} break;case UI_MIN:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("min",data.value);}} -break;case UI_MAX:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("max",data.value);}} +break;case UI_MAX:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){if(!parent.attr("type")){parent.attr("maxlength",data.value);}else{parent.attr("max",data.value);}}} break;case UI_STEP:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("step",data.value);}} break;case UI_GRAPH:if(data.visible){addToHTML(data);graphData[data.id]=restoreGraphData(data.id);renderGraphSvg(graphData[data.id],"graph"+data.id);} break;case ADD_GRAPH_POINT:var ts=Math.round(new Date().getTime()/1000);graphData[data.id].push({x:ts,y:data.value});saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case CLEAR_GRAPH:graphData[data.id]=[];saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case UI_ACCEL:if(hasAccel)break;hasAccel=true;if(data.visible){addToHTML(data);requestOrientationPermission();} break;case UPDATE_LABEL:$("#l"+data.id).html(data.value);if(data.hasOwnProperty('elementStyle')){$("#l"+data.id).attr("style",data.elementStyle);} break;case UPDATE_SWITCHER:switcher(data.id,data.value=="0"?0:1);if(data.hasOwnProperty('elementStyle')){$("#sl"+data.id).attr("style",data.elementStyle);} -break;case UPDATE_SLIDER:slider_move($("#id"+data.id),data.value,"100",false);if(data.hasOwnProperty('elementStyle')){$("#sl"+data.id).attr("style",data.elementStyle);} +break;case UPDATE_SLIDER:$("#sl"+data.id).attr("value",data.value) +slider_move($("#id"+data.id),data.value,"100",false);if(data.hasOwnProperty('elementStyle')){$("#sl"+data.id).attr("style",data.elementStyle);} break;case UPDATE_NUMBER:$("#num"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#num"+data.id).attr("style",data.elementStyle);} break;case UPDATE_TEXT_INPUT:$("#text"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#text"+data.id).attr("style",data.elementStyle);} break;case UPDATE_SELECT:$("#select"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#select"+data.id).attr("style",data.elementStyle);} -break;case UPDATE_BUTTON:case UPDATE_PAD:case UPDATE_CPAD:break;case UPDATE_GAUGE:$("#gauge"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#gauge"+data.id).attr("style",data.elementStyle);} +break;case UPDATE_BUTTON:$("#btn"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#btn"+data.id).attr("style",data.elementStyle);} +break;case UPDATE_PAD:case UPDATE_CPAD:break;case UPDATE_GAUGE:$("#gauge"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#gauge"+data.id).attr("style",data.elementStyle);} break;case UPDATE_ACCEL:break;default:console.error("Unknown type or event");break;} if(data.type>=UPDATE_OFFSET&&data.type0){var parent=data.hasOwnProperty('parentControl')?$("#tab"+data.parentControl):$("#row");var html="";switch(data.type){case UI_LABEL:case UI_BUTTON:case UI_SWITCHER:case UI_CPAD:case UI_PAD:case UI_SLIDER:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GRAPH:case UI_GAUGE:case UI_ACCEL:html="
"+data.label+"

"+ -elementHTML(data.type,data.id,data.value,elementStyle)+ +elementHTML(data.type,data.id,data.value,data.label,elementStyle)+ "
";break;case UI_SEPARATOR:html="
"+ "
"+data.label+"

";break;} -parent.append(html);}else{var parent=$("#id"+data.parentControl);parent.append(elementHTML(data.type,data.id,data.value,elementStyle));}} -var elementHTML=function(type,id,value,elementStyle){switch(type){case UI_LABEL:return""+value+"";case UI_BUTTON:return"