diff --git a/README.md b/README.md index 4659d70..c808335 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,9 @@ 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 diff --git a/data/js/controls.js b/data/js/controls.js index 36fabe3..0395071 100644 --- a/data/js/controls.js +++ b/data/js/controls.js @@ -60,6 +60,8 @@ 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; @@ -77,6 +79,8 @@ 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; @@ -190,6 +194,12 @@ function restart() { } function conStatusError() { + FragmentAssemblyTimer.forEach(element => { + clearInterval(element); + }); + FragmentAssemblyTimer = new Object(); + controlAssemblyArray = new Object(); + if (true === websockConnected) { websockConnected = false; websock.close(); @@ -210,17 +220,20 @@ 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 ( - window.location.port != "" || - window.location.port != 80 || - window.location.port != 443 + port != "" || + port != 80 || + port != 443 ) { - websock = new WebSocket( - "ws://" + window.location.hostname + ":" + window.location.port + "/ws" - ); + websock = new WebSocket( "ws://" + location + "/ws" ); } else { - websock = new WebSocket("ws://" + window.location.hostname + "/ws"); + websock = new WebSocket("ws://" + location + "/ws"); } // is the timer running? @@ -241,33 +254,54 @@ function start() { $("#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("Close evt: '" + evt + "'"); + // console.log("Close reason: '" + evt.reason + "'"); + // console.log("Close code: '" + evt.code + "'"); console.log("websock close"); conStatusError(); + FragmentAssemblyTimer.forEach(element => { + clearInterval(element); + }); + FragmentAssemblyTimer = new Object(); + controlAssemblyArray = new Object(); }; websock.onerror = function (evt) { console.log("websock Error"); - console.log(evt); + // console.log("Error evt: '" + evt + "'"); + // console.log("Error data: '" + evt.data + "'"); restart(); + FragmentAssemblyTimer.forEach(element => { + clearInterval(element); + }); + FragmentAssemblyTimer = new Object(); + controlAssemblyArray = new Object(); }; var handleEvent = function (evt) { - console.log(evt); + // console.log("handleEvent:Data evt: '" + evt + "'"); + // console.log("handleEvent:Data data: '" + evt.data + "'"); try { var data = JSON.parse(evt.data); } catch (Event) { console.error(Event); - // start the update over again + // console.info("start the update over again"); websock.send("uiok:" + 0); return; } var e = document.body; var center = ""; + // console.info("data.type: '" + data.type + "'"); switch (data.type) { case UI_INITIAL_GUI: @@ -279,7 +313,9 @@ function start() { if (data.sliderContinuous) { sliderContinuous = data.sliderContinuous; } + // console.info("UI_INITIAL_GUI:data record: '" + data + "'"); data.controls.forEach(element => { + // console.info("element: '" + JSON.stringify(element) + "'"); var fauxEvent = { data: JSON.stringify(element), }; @@ -295,7 +331,9 @@ function start() { break; case UI_EXTEND_GUI: + // console.info("UI_EXTEND_GUI data record: '" + data + "'"); data.controls.forEach(element => { + // console.info("UI_EXTEND_GUI:element: '" + JSON.stringify(element) + "'"); var fauxEvent = { data: JSON.stringify(element), }; @@ -601,6 +639,88 @@ function start() { websock.send("time:" + rv + ":" + data.id); break; + case UI_FRAGMENT: + let FragmentLen = data.length; + let FragementOffset = data.offset; + let NextFragmentOffset = FragementOffset + FragmentLen; + let Total = data.total; + let Arrived = (FragmentLen + FragementOffset); + let FragmentFinal = Total === Arrived; + // console.info("UI_FRAGMENT:FragmentLen '" + FragmentLen + "'"); + // console.info("UI_FRAGMENT:FragementOffset '" + FragementOffset + "'"); + // console.info("UI_FRAGMENT:NextFragmentOffset '" + NextFragmentOffset + "'"); + // console.info("UI_FRAGMENT:Total '" + Total + "'"); + // console.info("UI_FRAGMENT:Arrived '" + Arrived + "'"); + // console.info("UI_FRAGMENT:FragmentFinal '" + FragmentFinal + "'"); + + if (!data.hasOwnProperty('control')) + { + console.error("UI_FRAGMENT:Missing control record, skipping control"); + break; + } + let control = data.control; + StopFragmentAssemblyTimer(data.control.id); + + // is this the first fragment? + if(0 === FragementOffset) + { + // console.info("Found first fragment"); + controlAssemblyArray[control.id] = data; + // console.info("Value: " + controlAssemblyArray[control.id].control.value); + controlAssemblyArray[control.id].offset = NextFragmentOffset; + StartFragmentAssemblyTimer(control.id); + let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : NextFragmentOffset }); + websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":"); + // console.info("asked for fragment 2"); + break; + } + + // not first fragment. are we assembling this control? + if("undefined" === typeof controlAssemblyArray[control.id]) + { + // it looks like we missed the first fragment. Start the control over + console.error("Missing first fragment for control: " + control.id); + StartFragmentAssemblyTimer(control.id); + let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : 0 }); + websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":"); + // console.info("asked for fragment 1"); + break; + } + + // is this the expected next fragment + if(FragementOffset !== controlAssemblyArray[control.id].offset) + { + console.error("Wrong next fragment. Expected: " + controlAssemblyArray[control.id].offset + " Got: " + FragementOffset); + StartFragmentAssemblyTimer(control.id); + let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : controlAssemblyArray[control.id].length + controlAssemblyArray[control.id].offset }); + websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":"); + // console.info("asked for the expected fragment"); + break; + } + + // console.info("Add to existing fragment"); + controlAssemblyArray[control.id].control.value += control.value; + controlAssemblyArray[control.id].offset = NextFragmentOffset; + // console.info("Value: " + controlAssemblyArray[control.id].control.value); + + if(true === FragmentFinal) + { + var fauxEvent = { + data: JSON.stringify(controlAssemblyArray[control.id].control), + }; + handleEvent(fauxEvent); + controlAssemblyArray[control.id] = null; + // console.info("Found last fragment"); + } + else + { + // console.info("Ask for next fragment."); + StartFragmentAssemblyTimer(control.id); + let TotalRequest = JSON.stringify({ 'id' : control.id, 'offset' : NextFragmentOffset}); + websock.send("uifragmentok:" + 0 + ": " + TotalRequest + ":"); + } + break; + default: console.error("Unknown type or event"); break; @@ -650,6 +770,36 @@ function start() { websock.onmessage = handleEvent; } +function StartFragmentAssemblyTimer(Id) +{ + StopFragmentAssemblyTimer(Id); + FragmentAssemblyTimer[Id] = setInterval(function(_Id) + { + // does the fragment assembly still exist? + if("undefined" !== typeof controlAssemblyArray[_Id]) + { + if(null !== controlAssemblyArray[_Id]) + { + // we have a valid control that is being assembled + // ask for the next part + 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]); + } + } +} + function sliderchange(number) { var val = $("#sl" + number).val(); websock.send("slvalue:" + val + ":" + number); diff --git a/data/js/controls.min.js b/data/js/controls.min.js index 48be64b..9fa8cef 100644 --- a/data/js/controls.min.js +++ b/data/js/controls.min.js @@ -1,107 +1,128 @@ -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 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 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"";}} -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(){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(){document.addEventListener("visibilitychange",handleVisibilityChange,false);if(window.location.port!=""||window.location.port!=80||window.location.port!=443){websock=new WebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws");}else{websock=new WebSocket("ws://"+window.location.hostname+"/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;};websock.onclose=function(evt){console.log("websock close");conStatusError();};websock.onerror=function(evt){console.log("websock Error");console.log(evt);restart();};var handleEvent=function(evt){console.log(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;} -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);} -break;case UI_TAB:if(data.visible){$("#tabsnav").append("
  • "+data.value+"
  • ");$("#tabscontent").append("
    ");tabs=$(".tabscontent").tabbedContent({loop:true}).data("api");$("a").filter(function(){return $(this).attr("href")==="#click-to-switch";}).on("click",function(e){var tab=prompt("Tab to switch to (number or id)?");if(!tabs.switchTab(tab)){alert("That tab does not exist :\\");} -e.preventDefault();});} -break;case UI_OPTION:if(data.parentControl){var parent=$("#select"+data.parentControl);parent.append("");} -break;case UI_MIN:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("min",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("min",data.value);}} -break;case UI_MAX:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("max",data.value);}else if($('#text'+data.parentControl).length){$('#text'+data.parentControl).attr("maxlength",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("max",data.value);}} -break;case UI_STEP:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("step",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).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=new Date().getTime();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:$("#sl"+data.id).attr("value",data.value) -slider_move($("#sl"+data.id).parent().parent(),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);} -if(data.hasOwnProperty('inputType')){$("#text"+data.id).attr("type",data.inputType);} -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:$("#btn"+data.id).val(data.value);$("#btn"+data.id).text(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;case UPDATE_TIME:var rv=new Date().toISOString();websock.send("time:"+rv+":"+data.id);break;default:console.error("Unknown type or event");break;} -if(data.type>=UI_TITEL&&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)+ -"
    ";break;case UI_SEPARATOR:html="
    "+ -"
    "+data.label+"

    ";break;case UI_TIME:break;} -parent.append(html);}else{var parent=$("#id"+data.parentControl);parent.append(elementHTML(data));}} -var elementHTML=function(data){var id=data.id -var elementStyle=data.hasOwnProperty('elementStyle')?" style='"+data.elementStyle+"' ":"";var inputType=data.hasOwnProperty('inputType')?" type='"+data.inputType+"' ":"";switch(data.type){case UI_LABEL:return""+data.value+"";case UI_BUTTON:return"";case UI_SWITCHER:return"";case UI_CPAD:case UI_PAD:return"";case UI_SLIDER:return"
    "+ -""+ -data.value+"
    ";case UI_NUMBER:return"";case UI_TEXT_INPUT:return"";case UI_SELECT:return"";case UI_ACCEL:return"ACCEL // Not implemented fully!
    ";default:return"";}}
    -var processEnabled=function(data){switch(data.type){case UI_SWITCHER:case UPDATE_SWITCHER:if(data.enabled){$("#sl"+data.id).removeClass('disabled');$("#s"+data.id).prop("disabled",false);}else{$("#sl"+data.id).addClass('disabled');$("#s"+data.id).prop("disabled",true);}
    -break;case UI_SLIDER:case UPDATE_SLIDER:$("#sl"+data.id).prop("disabled",!data.enabled);break;case UI_NUMBER:case UPDATE_NUMBER:$("#num"+data.id).prop("disabled",!data.enabled);break;case UI_TEXT_INPUT:case UPDATE_TEXT_INPUT:$("#text"+data.id).prop("disabled",!data.enabled);break;case UI_SELECT:case UPDATE_SELECT:$("#select"+data.id).prop("disabled",!data.enabled);break;case UI_BUTTON:case UPDATE_BUTTON:$("#btn"+data.id).prop("disabled",!data.enabled);break;case UI_PAD:case UI_CPAD:case UPDATE_PAD:case UPDATE_CPAD:if(data.enabled){$("#id"+data.id+" nav").removeClass('disabled');}else{$("#id"+data.id+" nav").addClass('disabled');}
    +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"";}}
    +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 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+"/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);}
    +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;}
    +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);}
    +break;case UI_TAB:if(data.visible){$("#tabsnav").append("
  • "+data.value+"
  • ");$("#tabscontent").append("
    ");tabs=$(".tabscontent").tabbedContent({loop:true}).data("api");$("a").filter(function(){return $(this).attr("href")==="#click-to-switch";}).on("click",function(e){var tab=prompt("Tab to switch to (number or id)?");if(!tabs.switchTab(tab)){alert("That tab does not exist :\\");} +e.preventDefault();});} +break;case UI_OPTION:if(data.parentControl){var parent=$("#select"+data.parentControl);parent.append("");} +break;case UI_MIN:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("min",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("min",data.value);}} +break;case UI_MAX:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("max",data.value);}else if($('#text'+data.parentControl).length){$('#text'+data.parentControl).attr("maxlength",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("max",data.value);}} +break;case UI_STEP:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("step",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).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=new Date().getTime();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:$("#sl"+data.id).attr("value",data.value) +slider_move($("#sl"+data.id).parent().parent(),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);} +if(data.hasOwnProperty('inputType')){$("#text"+data.id).attr("type",data.inputType);} +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:$("#btn"+data.id).val(data.value);$("#btn"+data.id).text(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;case UPDATE_TIME:var rv=new Date().toISOString();websock.send("time:"+rv+":"+data.id);break;case UI_FRAGMENT:let FragmentLen=data.length;let FragementOffset=data.offset;let NextFragmentOffset=FragementOffset+FragmentLen;let Total=data.total;let Arrived=(FragmentLen+FragementOffset);let FragmentFinal=Total===Arrived;if(!data.hasOwnProperty('control')) +{console.error("UI_FRAGMENT:Missing control record, skipping control");break;} +let control=data.control;StopFragmentAssemblyTimer(data.control.id);if(0===FragementOffset) +{controlAssemblyArray[control.id]=data;controlAssemblyArray[control.id].offset=NextFragmentOffset;StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':NextFragmentOffset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;} +if("undefined"===typeof controlAssemblyArray[control.id]) +{console.error("Missing first fragment for control: "+control.id);StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':0});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;} +if(FragementOffset!==controlAssemblyArray[control.id].offset) +{console.error("Wrong next fragment. Expected: "+controlAssemblyArray[control.id].offset+" Got: "+FragementOffset);StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':controlAssemblyArray[control.id].length+controlAssemblyArray[control.id].offset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;} +controlAssemblyArray[control.id].control.value+=control.value;controlAssemblyArray[control.id].offset=NextFragmentOffset;if(true===FragmentFinal) +{var fauxEvent={data:JSON.stringify(controlAssemblyArray[control.id].control),};handleEvent(fauxEvent);controlAssemblyArray[control.id]=null;} +else +{StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':NextFragmentOffset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");} +break;default:console.error("Unknown type or event");break;} +if(data.type>=UI_TITEL&&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)+ +"
    ";break;case UI_SEPARATOR:html="
    "+ +"
    "+data.label+"

    ";break;case UI_TIME:break;} +parent.append(html);}else{var parent=$("#id"+data.parentControl);parent.append(elementHTML(data));}} +var elementHTML=function(data){var id=data.id +var elementStyle=data.hasOwnProperty('elementStyle')?" style='"+data.elementStyle+"' ":"";var inputType=data.hasOwnProperty('inputType')?" type='"+data.inputType+"' ":"";switch(data.type){case UI_LABEL:return""+data.value+"";case UI_BUTTON:return"";case UI_SWITCHER:return"";case UI_CPAD:case UI_PAD:return"";case UI_SLIDER:return"
    "+ +""+ +data.value+"
    ";case UI_NUMBER:return"";case UI_TEXT_INPUT:return"";case UI_SELECT:return"";case UI_ACCEL:return"ACCEL // Not implemented fully!
    ";default:return"";}}
    +var processEnabled=function(data){switch(data.type){case UI_SWITCHER:case UPDATE_SWITCHER:if(data.enabled){$("#sl"+data.id).removeClass('disabled');$("#s"+data.id).prop("disabled",false);}else{$("#sl"+data.id).addClass('disabled');$("#s"+data.id).prop("disabled",true);}
    +break;case UI_SLIDER:case UPDATE_SLIDER:$("#sl"+data.id).prop("disabled",!data.enabled);break;case UI_NUMBER:case UPDATE_NUMBER:$("#num"+data.id).prop("disabled",!data.enabled);break;case UI_TEXT_INPUT:case UPDATE_TEXT_INPUT:$("#text"+data.id).prop("disabled",!data.enabled);break;case UI_SELECT:case UPDATE_SELECT:$("#select"+data.id).prop("disabled",!data.enabled);break;case UI_BUTTON:case UPDATE_BUTTON:$("#btn"+data.id).prop("disabled",!data.enabled);break;case UI_PAD:case UI_CPAD:case UPDATE_PAD:case UPDATE_CPAD:if(data.enabled){$("#id"+data.id+" nav").removeClass('disabled');}else{$("#id"+data.id+" nav").addClass('disabled');}
     break;}}
    \ No newline at end of file
    diff --git a/src/ESPUI.h b/src/ESPUI.h
    index 43464fe..f32bf22 100644
    --- a/src/ESPUI.h
    +++ b/src/ESPUI.h
    @@ -1,7 +1,7 @@
     #pragma once
     
     // comment out to turn off debug output
    -#define DEBUG_ESPUI true
    +// #define DEBUG_ESPUI true
     #define WS_AUTHENTICATION false
     
     #include 
    @@ -87,16 +87,13 @@ enum Verbosity : uint8_t
     class ESPUIClass
     {
     public:
    -
    -#ifdef ESP32
         ESPUIClass()
         {
    +#ifdef ESP32
             ControlsSemaphore = xSemaphoreCreateMutex();
             xSemaphoreGive(ControlsSemaphore);
    -    }
    -    SemaphoreHandle_t ControlsSemaphore = NULL;
     #endif // def ESP32
    -
    +	}
         unsigned int jsonUpdateDocumentSize = 2000;
     #ifdef ESP8266
         unsigned int jsonInitialDocumentSize = 2000;
    @@ -211,14 +208,18 @@ public:
         void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false);
     
         Verbosity verbosity = Verbosity::Quiet;
    -    AsyncWebServer* server;
     
     protected:
         friend class ESPUIclient;
         friend class ESPUIcontrol;
     
    +#ifdef ESP32
    +    SemaphoreHandle_t ControlsSemaphore = NULL;
    +#endif // def ESP32
    +
         void        RemoveToBeDeletedControls();
     
    +    AsyncWebServer* server;
         AsyncWebSocket* ws;
     
         const char* basicAuthUsername = nullptr;
    diff --git a/src/ESPUIclient.cpp b/src/ESPUIclient.cpp
    index 9bce217..ef24ffe 100644
    --- a/src/ESPUIclient.cpp
    +++ b/src/ESPUIclient.cpp
    @@ -229,7 +229,21 @@ void ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t
                 if (cmd.equals(F("uiok")))
                 {
                     // Serial.println(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uiok:ProcessAck"));
    -                pCurrentFsmState->ProcessAck(id);
    +                pCurrentFsmState->ProcessAck(id, emptyString);
    +                break;
    +            }
    +
    +            if (cmd.equals(F("uifragmentok")))
    +            {
    +                if(!emptyString.equals(value))
    +                {
    +                    // Serial.println(String(F("ESPUIclient::OnWsEvent:WS_EVT_DATA:uifragmentok: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"));
    +                }
                     break;
                 }
     
    @@ -269,7 +283,8 @@ client will acknowledge receipt by requesting the next chunk.
      */
     uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
                                           DynamicJsonDocument & rootDoc,
    -                                      bool InUpdateMode)
    +                                      bool InUpdateMode,
    +                                      String FragmentRequestString)
     {
     #ifdef ESP32
         xSemaphoreTake(ESPUI.ControlsSemaphore, portMAX_DELAY);
    @@ -283,8 +298,61 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
             // Follow the list until control points to the startindex'th node
             Control* control = ESPUI.controls;
             uint32_t currentIndex = 0;
    +        uint32_t DataOffset = 0;
             JsonArray items = rootDoc[F("controls")];
    +        bool SingleControl = false;
     
    +        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 + "'");
    +
    +            // this is actually a fragment or directed update request
    +            // parse the string we got from the UI and try to update that specific 
    +            // control.
    +            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"));
    +                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"));
    +                break;
    +            }
    +
    +            if(!FragmentRequest.containsKey(F("id")))
    +            {
    +                Serial.println(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"));
    +                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"));
    +                break;
    +            }
    +
    +            // Serial.println(F("prepareJSONChunk:Fragmentation:disable the control search operation"));
    +            currentIndex = 1;
    +            startindex = 0;
    +            SingleControl = true;
    +        }
    +
    +        // find a control to send
             while ((startindex > currentIndex) && (nullptr != control))
             {
                 // only count active controls
    @@ -320,14 +388,14 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
             while (nullptr != control)
             {
                 // skip deleted controls or controls that have not been updated
    -            if (control->ToBeDeleted())
    +            if (control->ToBeDeleted() && !SingleControl)
                 {
                     // Serial.println(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id));
                     control = control->next;
                     continue;
                 }
     
    -            if(InUpdateMode)
    +            if(InUpdateMode && !SingleControl)
                 {
                     if(control->IsUpdated())
                     {
    @@ -343,7 +411,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
     
                 JsonObject item = items.createNestedObject();
                 elementcount++;
    -            control->MarshalControl(item, InUpdateMode);
    +            control->MarshalControl(item, InUpdateMode, DataOffset);
                 
                 if (rootDoc.overflowed() || (ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0))
                 {
    @@ -351,6 +419,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
                     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);
                         rootDoc.clear();
                         item = items.createNestedObject();
                         control->MarshalErrorMessage(item);
    @@ -366,7 +435,11 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
                     }
                     // exit the loop
                     control = nullptr;
    -
    +            }
    +            else if (SingleControl)
    +            {
    +                // Serial.println("prepareJSONChunk: exit loop");
    +                control = nullptr;
                 }
                 else
                 {
    @@ -404,8 +477,7 @@ CLIENT: controls.js:handleEvent()
     etc.
         Returns true if all controls have been sent (aka: Done)
     */
    -bool ESPUIclient::SendControlsToClient(uint16_t startidx,
    -                                       ClientUpdateType_t TransferMode)
    +bool ESPUIclient::SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode, String FragmentRequest)
     {
         bool Response = false;
         // Serial.println(String("ESPUIclient:SendControlsToClient:startidx: ") + String(startidx));
    @@ -417,9 +489,9 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx,
                 break;
             }
     
    -        if (startidx >= ESPUI.controlCount)
    +        else if ((startidx >= ESPUI.controlCount) && (emptyString.equals(FragmentRequest)))
             {
    -            // Serial.println("ESPUIclient:SendControlsToClient: No more controls to send.");
    +            // Serial.println(F("ERROR:ESPUIclient:SendControlsToClient: No more controls to send."));
                 Response = true;
                 break;
             }
    @@ -427,7 +499,7 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx,
             DynamicJsonDocument document(ESPUI.jsonInitialDocumentSize);
             FillInHeader(document);
             document[F("startindex")] = startidx;
    -        document[F("totalcontrols")] = 65534; // ESPUI.controlCount;
    +        document[F("totalcontrols")] = uint16_t(-1); // ESPUI.controlCount;
     
             if(0 == startidx)
             {
    @@ -437,7 +509,7 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx,
             // Serial.println(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"]));
     
             // Serial.println("ESPUIclient:SendControlsToClient: Build Controls.");
    -        if(prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode))
    +        if(prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode, FragmentRequest))
             {
                 #if defined(DEBUG_ESPUI)
                     if (ESPUI.verbosity >= Verbosity::VerboseJSON)
    diff --git a/src/ESPUIclient.h b/src/ESPUIclient.h
    index 5a34699..42d8abf 100644
    --- a/src/ESPUIclient.h
    +++ b/src/ESPUIclient.h
    @@ -44,8 +44,8 @@ protected:
     
         bool        CanSend();
         void        FillInHeader(ArduinoJson::DynamicJsonDocument& document);
    -    uint32_t    prepareJSONChunk(uint16_t startindex, DynamicJsonDocument& rootDoc, bool InUpdateMode);
    -    bool        SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode);
    +    uint32_t    prepareJSONChunk(uint16_t startindex, DynamicJsonDocument& rootDoc, bool InUpdateMode, String value);
    +    bool        SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode, String FragmentRequest);
     
         bool        SendClientNotification(ClientUpdateType_t value);
     
    diff --git a/src/ESPUIclientFsm.cpp b/src/ESPUIclientFsm.cpp
    index 1bd628b..0efb75f 100644
    --- a/src/ESPUIclientFsm.cpp
    +++ b/src/ESPUIclientFsm.cpp
    @@ -58,12 +58,20 @@ bool fsm_EspuiClient_state_Idle::NotifyClient()
         return Response;
     }
     
    -void fsm_EspuiClient_state_Idle::ProcessAck(uint16_t)
    +void fsm_EspuiClient_state_Idle::ProcessAck(uint16_t ControlIndex, String FragmentRequestString)
     {
    -    // This is an unexpected request for control data from the browser
    -    // treat it as if it was a rebuild operation
    -    // Serial.println(F("fsm_EspuiClient_state_Idle: ProcessAck"));
    -    Parent->NotifyClient(ClientUpdateType_t::RebuildNeeded);
    +    if(!emptyString.equals(FragmentRequestString))
    +    {
    +        // Serial.println(F("fsm_EspuiClient_state_Idle::ProcessAck:Fragmentation:Got fragment Header"));
    +        Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded, FragmentRequestString);
    +    }
    +    else
    +    {
    +        // This is an unexpected request for control data from the browser
    +        // treat it as if it was a rebuild operation
    +        // Serial.println(F("fsm_EspuiClient_state_Idle: ProcessAck"));
    +        Parent->NotifyClient(ClientUpdateType_t::RebuildNeeded);
    +    }
     }
     
     //----------------------------------------------
    @@ -75,10 +83,10 @@ bool fsm_EspuiClient_state_SendingUpdate::NotifyClient()
         return true; /* Ignore request */
     }
     
    -void fsm_EspuiClient_state_SendingUpdate::ProcessAck(uint16_t ControlIndex)
    +void fsm_EspuiClient_state_SendingUpdate::ProcessAck(uint16_t ControlIndex, String FragmentRequest)
     {
         // Serial.println(F("fsm_EspuiClient_state_SendingUpdate: ProcessAck"));
    -    if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded))
    +    if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded, FragmentRequest))
         {
             // No more data to send. Go back to idle or start next request
             Parent->fsm_EspuiClient_state_Idle_imp.Init();
    @@ -95,13 +103,25 @@ bool fsm_EspuiClient_state_Rebuilding::NotifyClient()
         return true; /* Ignore request */
     }
     
    -void fsm_EspuiClient_state_Rebuilding::ProcessAck(uint16_t ControlIndex)
    +void fsm_EspuiClient_state_Rebuilding::ProcessAck(uint16_t ControlIndex, String FragmentRequest)
     {
         // Serial.println(F("fsm_EspuiClient_state_Rebuilding: ProcessAck"));
    -    if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::RebuildNeeded))
    +    if(Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::RebuildNeeded, FragmentRequest))
         {
             // No more data to send. Go back to idle or start next request
             Parent->fsm_EspuiClient_state_Idle_imp.Init();
             Parent->fsm_EspuiClient_state_Idle_imp.NotifyClient();
         }
     }
    +
    +//----------------------------------------------
    +//----------------------------------------------
    +//----------------------------------------------
    +void fsm_EspuiClient_state_Reloading::ProcessAck(uint16_t ControlIndex, String FragmentRequestString)
    +{
    +    if(!emptyString.equals(FragmentRequestString))
    +    {
    +        // Serial.println(F("fsm_EspuiClient_state_Reloading::ProcessAck:Fragmentation:Got fragment Header"));
    +        Parent->SendControlsToClient(ControlIndex, ClientUpdateType_t::UpdateNeeded, FragmentRequestString);
    +    }
    +}
    diff --git a/src/ESPUIclientFsm.h b/src/ESPUIclientFsm.h
    index b04bb54..0b794c1 100644
    --- a/src/ESPUIclientFsm.h
    +++ b/src/ESPUIclientFsm.h
    @@ -20,7 +20,7 @@ public:
     
                 void Init();
         virtual bool NotifyClient() = 0;
    -    virtual void ProcessAck(uint16_t id) = 0;
    +    virtual void ProcessAck(uint16_t id, String FragmentRequest) = 0;
         virtual String GetStateName () = 0;
                 void SetParent(ESPUIclient * value) { Parent = value; }
     
    @@ -36,7 +36,7 @@ public:
         virtual     ~fsm_EspuiClient_state_Idle() {}
     
         virtual bool NotifyClient();
    -    virtual void ProcessAck(uint16_t id);
    +    virtual void ProcessAck(uint16_t id, String FragmentRequest);
                 String GetStateName() { return String(F("Idle")); }
     
     }; // fsm_EspuiClient_state_Idle
    @@ -48,7 +48,7 @@ public:
         virtual     ~fsm_EspuiClient_state_SendingUpdate() {}
     
         virtual bool NotifyClient();
    -    virtual void ProcessAck(uint16_t id);
    +    virtual void ProcessAck(uint16_t id, String FragmentRequest);
                 String GetStateName() { return String(F("Sending Update")); }
     
     }; // fsm_EspuiClient_state_SendingUpdate
    @@ -60,7 +60,7 @@ public:
         virtual     ~fsm_EspuiClient_state_Rebuilding() {}
     
         virtual bool NotifyClient();
    -    virtual void ProcessAck(uint16_t id);
    +    virtual void ProcessAck(uint16_t id, String FragmentRequest);
                 String GetStateName() { return String(F("Sending Rebuild")); }
     
     }; // fsm_EspuiClient_state_Rebuilding
    @@ -72,7 +72,7 @@ public:
         virtual     ~fsm_EspuiClient_state_Reloading() {}
     
         virtual bool NotifyClient() { return false; }
    -    virtual void ProcessAck(uint16_t) {}
    +    virtual void ProcessAck(uint16_t id, String FragmentRequest);
                 String GetStateName() { return String(F("Reloading")); }
     
     }; // fsm_EspuiClient_state_Reloading
    diff --git a/src/ESPUIcontrol.cpp b/src/ESPUIcontrol.cpp
    index f276c68..3acd6cf 100644
    --- a/src/ESPUIcontrol.cpp
    +++ b/src/ESPUIcontrol.cpp
    @@ -56,8 +56,43 @@ void Control::DeleteControl()
         callback = nullptr;
     }
     
    -void Control::MarshalControl(JsonObject & item, bool refresh)
    +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)
    @@ -68,8 +103,9 @@ void Control::MarshalControl(JsonObject & item, bool refresh)
         {
             item[F("type")] = uint32_t(TempType);
         }
    +
         item[F("label")]   = label;
    -    item[F ("value")]   = (ControlType::Password == type) ? F ("--------") : value;
    +    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;
    diff --git a/src/ESPUIcontrol.h b/src/ESPUIcontrol.h
    index d47c074..396f812 100644
    --- a/src/ESPUIcontrol.h
    +++ b/src/ESPUIcontrol.h
    @@ -30,6 +30,7 @@ enum ControlType : uint8_t
         Separator,
         Time,
     
    +    Fragment,
         Password = 99,
         UpdateOffset = 100,
     };
    @@ -83,7 +84,7 @@ public:
     
         void SendCallback(int type);
         bool HasCallback() { return ((nullptr != callback) || (nullptr != extendedCallback)); }
    -    void MarshalControl(ArduinoJson::JsonObject& item, bool refresh);
    +    void MarshalControl(ArduinoJson::JsonObject& item, bool refresh, uint32_t DataOffset);
         void MarshalErrorMessage(ArduinoJson::JsonObject& item);
         bool ToBeDeleted() { return (ControlSyncState_t::deleted == ControlSyncState); }
         void DeleteControl();
    diff --git a/src/dataControlsJS.h b/src/dataControlsJS.h
    index 1c15e0b..175b696 100644
    --- a/src/dataControlsJS.h
    +++ b/src/dataControlsJS.h
    @@ -1,111 +1,132 @@
    -const char JS_CONTROLS[] PROGMEM = R"=====(
    -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 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 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"";}}
    -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(){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(){document.addEventListener("visibilitychange",handleVisibilityChange,false);if(window.location.port!=""||window.location.port!=80||window.location.port!=443){websock=new WebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws");}else{websock=new WebSocket("ws://"+window.location.hostname+"/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;};websock.onclose=function(evt){console.log("websock close");conStatusError();};websock.onerror=function(evt){console.log("websock Error");console.log(evt);restart();};var handleEvent=function(evt){console.log(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;}
    -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);}
    -break;case UI_TAB:if(data.visible){$("#tabsnav").append("
  • "+data.value+"
  • ");$("#tabscontent").append("
    ");tabs=$(".tabscontent").tabbedContent({loop:true}).data("api");$("a").filter(function(){return $(this).attr("href")==="#click-to-switch";}).on("click",function(e){var tab=prompt("Tab to switch to (number or id)?");if(!tabs.switchTab(tab)){alert("That tab does not exist :\\");} -e.preventDefault();});} -break;case UI_OPTION:if(data.parentControl){var parent=$("#select"+data.parentControl);parent.append("");} -break;case UI_MIN:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("min",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("min",data.value);}} -break;case UI_MAX:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("max",data.value);}else if($('#text'+data.parentControl).length){$('#text'+data.parentControl).attr("maxlength",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("max",data.value);}} -break;case UI_STEP:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("step",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).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=new Date().getTime();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:$("#sl"+data.id).attr("value",data.value) -slider_move($("#sl"+data.id).parent().parent(),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);} -if(data.hasOwnProperty('inputType')){$("#text"+data.id).attr("type",data.inputType);} -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:$("#btn"+data.id).val(data.value);$("#btn"+data.id).text(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;case UPDATE_TIME:var rv=new Date().toISOString();websock.send("time:"+rv+":"+data.id);break;default:console.error("Unknown type or event");break;} -if(data.type>=UI_TITEL&&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)+ -"
    ";break;case UI_SEPARATOR:html="
    "+ -"
    "+data.label+"

    ";break;case UI_TIME:break;} -parent.append(html);}else{var parent=$("#id"+data.parentControl);parent.append(elementHTML(data));}} -var elementHTML=function(data){var id=data.id -var elementStyle=data.hasOwnProperty('elementStyle')?" style='"+data.elementStyle+"' ":"";var inputType=data.hasOwnProperty('inputType')?" type='"+data.inputType+"' ":"";switch(data.type){case UI_LABEL:return""+data.value+"";case UI_BUTTON:return"";case UI_SWITCHER:return"";case UI_CPAD:case UI_PAD:return"";case UI_SLIDER:return"
    "+ -""+ -data.value+"
    ";case UI_NUMBER:return"";case UI_TEXT_INPUT:return"";case UI_SELECT:return"";case UI_ACCEL:return"ACCEL // Not implemented fully!
    ";default:return"";}}
    -var processEnabled=function(data){switch(data.type){case UI_SWITCHER:case UPDATE_SWITCHER:if(data.enabled){$("#sl"+data.id).removeClass('disabled');$("#s"+data.id).prop("disabled",false);}else{$("#sl"+data.id).addClass('disabled');$("#s"+data.id).prop("disabled",true);}
    -break;case UI_SLIDER:case UPDATE_SLIDER:$("#sl"+data.id).prop("disabled",!data.enabled);break;case UI_NUMBER:case UPDATE_NUMBER:$("#num"+data.id).prop("disabled",!data.enabled);break;case UI_TEXT_INPUT:case UPDATE_TEXT_INPUT:$("#text"+data.id).prop("disabled",!data.enabled);break;case UI_SELECT:case UPDATE_SELECT:$("#select"+data.id).prop("disabled",!data.enabled);break;case UI_BUTTON:case UPDATE_BUTTON:$("#btn"+data.id).prop("disabled",!data.enabled);break;case UI_PAD:case UI_CPAD:case UPDATE_PAD:case UPDATE_CPAD:if(data.enabled){$("#id"+data.id+" nav").removeClass('disabled');}else{$("#id"+data.id+" nav").addClass('disabled');}
    -break;}}
    -)=====";
    -
    -const uint8_t JS_CONTROLS_GZIP[4306] PROGMEM = { 31,139,8,0,22,146,123,99,2,255,197,27,107,115,218,72,242,187,127,133,172,164,130,56,99,30,155,77,54,11,150,83,4,147,132,91,199,246,217,248,178,117,217,156,75,192,96,84,22,146,86,18,118,124,132,255,126,61,61,15,205,232,1,216,222,220,125,137,81,79,79,79,191,102,186,167,123,50,14,252,56,49,46,7,87,131,147,193,112,208,61,190,250,112,57,176,127,106,54,59,99,49,112,222,63,62,237,30,1,172,37,96,103,71,221,97,255,234,244,253,251,139,254,208,110,169,184,253,223,135,253,147,35,70,163,165,192,135,131,97,255,216,86,0,103,64,49,67,15,65,233,34,131,171,30,133,252,164,35,245,24,214,79,41,214,187,203,225,240,244,196,126,169,227,113,104,171,249,50,197,60,238,190,3,38,126,214,17,25,176,213,252,57,197,187,248,60,24,246,62,246,207,237,87,58,170,132,183,154,175,20,236,227,193,17,192,94,103,112,25,180,213,124,157,98,158,92,126,122,7,176,95,116,76,14,109,53,127,81,212,5,122,4,131,156,93,14,237,55,58,182,50,210,106,190,73,103,124,56,239,158,125,180,127,229,128,238,209,17,131,92,157,157,14,78,40,42,31,232,29,247,187,231,28,185,213,252,85,89,177,251,206,110,101,12,194,96,138,65,46,250,199,253,30,80,203,216,68,128,91,138,85,78,207,134,3,170,255,140,89,4,184,165,152,229,211,0,0,25,171,32,172,165,216,228,83,247,119,187,149,177,7,194,90,170,41,134,253,51,187,149,181,4,2,91,138,29,62,116,47,63,244,237,86,198,14,28,218,82,204,208,237,245,168,111,100,76,192,161,173,55,170,98,206,186,231,221,225,41,152,241,215,172,110,228,72,75,85,247,224,83,31,118,84,70,223,20,216,82,160,114,195,28,157,126,62,145,219,229,184,255,126,40,183,197,249,224,195,199,161,116,254,94,255,100,8,190,36,244,214,187,26,94,158,255,227,242,116,112,209,151,164,122,87,253,79,253,243,238,113,186,253,122,87,103,125,152,117,62,248,39,76,253,73,2,63,247,135,221,139,179,143,221,99,133,252,213,197,229,201,251,227,211,207,218,26,189,238,249,249,233,80,110,150,222,85,247,120,240,175,238,57,88,240,181,4,29,117,207,127,147,142,223,187,58,57,61,1,241,95,189,234,220,58,145,113,29,57,225,236,200,73,28,219,39,119,70,55,138,156,123,171,138,35,51,39,238,142,199,196,179,167,142,23,19,4,197,158,59,33,81,47,240,19,215,95,4,139,152,15,77,23,254,56,113,3,223,24,7,94,16,245,60,39,142,45,252,57,152,84,151,252,135,125,178,152,143,72,36,225,157,248,206,77,198,51,5,207,137,137,170,179,118,68,146,69,228,155,240,207,159,139,192,141,137,217,225,40,92,133,2,129,204,73,228,120,19,57,156,234,83,96,132,36,33,81,228,222,146,72,34,165,250,21,72,119,36,113,226,112,230,120,137,68,146,250,22,56,241,194,159,122,193,157,66,135,169,95,140,143,157,40,10,210,249,194,20,98,216,241,220,255,56,145,235,75,4,106,152,54,255,77,173,34,16,39,78,116,99,118,38,100,234,44,188,68,0,205,206,106,181,67,173,112,71,70,113,48,190,233,40,191,193,36,62,25,39,100,162,24,235,51,25,93,192,16,73,134,46,104,200,246,23,158,151,90,42,34,127,46,72,156,156,70,46,241,19,135,130,206,72,52,119,227,24,126,89,213,229,106,71,98,198,206,45,249,32,156,4,134,188,96,236,120,23,73,16,57,215,164,30,147,100,144,144,185,101,146,56,92,184,232,75,177,89,251,251,197,233,73,61,78,64,210,107,119,122,111,73,23,171,86,59,10,225,8,214,15,34,133,182,11,94,128,94,6,43,78,208,37,181,181,174,11,215,74,137,119,220,169,37,167,238,162,188,213,101,74,11,153,10,157,40,38,41,86,181,227,145,196,112,25,130,132,126,113,39,95,59,76,233,108,67,212,221,152,109,12,134,89,125,203,254,182,191,124,5,121,24,34,254,212,68,115,162,4,180,245,220,154,4,227,197,28,148,92,173,59,147,137,101,254,205,172,214,131,233,20,246,216,115,203,124,22,5,119,240,61,75,230,158,101,154,85,186,67,47,192,26,139,184,15,126,20,1,14,39,163,210,206,226,44,65,236,36,90,16,219,182,179,206,80,93,150,184,7,7,215,199,94,0,234,96,172,72,186,192,80,68,230,193,45,97,27,217,196,29,186,127,29,17,226,155,121,84,16,74,195,139,200,164,0,139,73,136,28,27,13,227,36,48,56,71,84,160,23,207,222,188,126,249,170,83,48,43,213,147,6,245,173,229,216,115,199,55,109,174,230,218,170,74,183,134,212,208,204,241,39,30,249,167,27,187,35,215,115,147,251,30,0,174,9,211,212,110,86,35,47,94,236,10,11,213,103,238,100,66,252,234,82,154,79,35,43,76,42,209,65,244,254,45,252,56,118,227,132,248,112,182,153,183,114,205,49,174,105,214,138,121,169,161,25,208,99,239,92,127,18,220,213,169,167,211,85,234,97,16,37,187,182,105,126,255,94,60,242,166,89,54,242,243,207,47,165,193,241,48,151,7,128,101,222,197,237,70,195,220,203,78,156,5,113,226,59,115,178,103,182,243,131,148,234,158,217,184,3,157,119,86,4,248,125,52,113,78,99,7,196,165,187,18,60,85,63,155,170,203,204,89,69,143,21,31,206,236,91,199,179,132,254,153,253,132,231,70,196,153,220,83,151,160,126,255,82,55,89,237,85,179,217,164,235,9,228,192,15,66,226,219,146,18,185,77,104,92,242,227,192,35,192,239,53,136,192,48,13,138,183,133,147,151,109,134,132,124,3,125,72,223,2,140,220,6,164,91,181,179,234,164,172,225,30,220,134,55,68,44,58,36,84,106,132,194,182,161,134,147,25,53,57,72,177,59,138,38,121,26,64,61,24,29,125,13,93,156,154,68,247,120,128,79,50,231,45,12,214,39,120,220,174,118,192,57,32,234,35,185,148,2,114,205,129,82,152,152,248,112,94,46,220,224,6,156,179,89,229,39,114,135,197,64,98,203,109,56,10,38,247,200,233,152,80,159,129,189,35,114,11,186,102,61,185,15,9,207,46,244,123,86,187,224,0,166,160,196,25,197,190,115,91,4,6,126,97,167,39,234,16,248,36,46,147,77,141,32,244,100,147,165,66,60,144,7,225,148,116,20,120,113,125,26,68,125,7,152,39,30,161,226,217,135,168,82,200,4,190,49,27,44,41,122,59,19,98,57,114,181,182,234,40,246,178,228,44,208,124,202,106,18,36,142,39,214,59,180,244,229,61,226,95,39,179,253,86,85,158,37,186,33,74,209,65,144,17,236,202,155,142,80,117,122,27,109,255,127,37,100,122,167,78,13,39,20,249,182,247,40,137,183,153,159,215,1,187,189,183,179,71,99,68,188,192,153,192,22,211,177,241,158,222,150,142,157,184,137,71,152,215,120,206,136,120,232,134,115,199,245,63,194,225,7,57,40,247,194,20,33,75,15,111,215,109,241,197,174,186,242,51,189,203,74,16,187,74,202,79,188,147,41,131,252,46,213,22,90,198,104,231,145,42,156,142,195,224,227,240,211,177,197,54,185,206,4,171,5,228,38,45,179,179,168,112,163,196,55,247,16,13,114,65,12,244,73,176,24,207,80,247,237,244,240,169,46,73,61,140,8,53,251,17,203,145,169,42,23,73,66,79,83,72,12,44,78,162,70,15,91,48,74,13,169,128,77,31,67,131,135,234,21,102,25,25,243,138,154,196,102,233,216,137,4,57,130,32,203,208,29,15,25,204,144,165,101,22,169,119,250,123,43,229,133,211,71,235,46,116,38,76,232,203,179,218,99,117,87,68,67,213,29,178,232,61,157,69,122,1,127,58,147,26,149,28,155,209,211,217,196,210,192,211,249,212,201,228,24,29,61,157,81,90,221,120,58,159,26,149,28,155,227,167,179,201,138,43,79,103,52,67,103,237,238,198,42,226,230,205,23,209,148,254,2,227,186,181,155,203,3,114,100,135,221,119,121,154,122,226,225,132,33,70,160,3,207,61,60,112,140,192,159,3,37,178,8,237,10,224,48,57,164,70,247,204,106,197,152,69,100,106,87,40,5,5,94,57,228,31,120,204,236,153,7,13,231,240,160,1,36,11,51,26,185,230,196,189,133,43,49,46,165,17,59,104,192,8,157,75,231,217,64,160,174,19,128,175,17,153,244,216,183,181,244,130,32,108,83,51,173,170,152,252,89,166,19,186,108,101,7,176,167,174,7,201,154,154,221,243,27,247,115,43,153,185,49,240,147,36,112,163,162,130,153,85,200,241,33,209,166,114,239,39,193,62,59,75,77,8,248,212,153,76,132,155,53,213,1,104,62,1,236,216,97,20,204,67,200,200,135,206,200,72,2,131,77,164,191,224,30,66,171,81,6,92,71,193,39,223,178,60,110,151,202,83,103,72,48,195,130,79,200,9,28,143,68,148,196,204,73,40,77,99,18,144,216,240,131,196,32,223,224,222,103,180,255,248,3,239,54,5,62,87,224,82,172,2,43,205,15,233,49,204,232,177,76,130,177,205,64,84,189,207,98,200,121,198,9,183,129,142,218,97,159,169,209,130,16,239,168,212,110,236,167,185,183,35,108,183,99,86,12,116,1,187,34,160,204,35,232,128,128,176,197,8,197,62,20,48,76,42,0,112,208,96,52,15,205,188,68,159,6,165,226,0,248,185,85,121,22,123,149,34,17,120,222,68,93,191,28,135,57,193,220,245,77,61,96,210,235,168,193,233,131,41,55,47,80,138,84,182,66,78,206,238,239,63,94,78,231,219,26,57,233,229,114,243,10,229,88,114,13,134,254,163,53,154,147,37,119,188,14,251,103,63,92,165,113,66,194,31,44,105,193,18,89,81,177,229,179,57,144,200,90,230,23,190,117,191,218,185,34,169,136,163,112,29,134,203,72,132,3,23,183,215,86,126,110,205,68,88,26,122,245,189,155,233,82,181,241,204,140,177,182,3,100,136,85,165,5,87,90,142,177,138,24,171,135,139,120,102,45,191,181,147,184,118,223,78,133,135,51,47,83,48,126,12,163,10,155,74,207,172,93,160,159,47,95,255,226,245,68,227,137,90,75,116,64,170,108,92,54,68,176,134,179,57,45,88,91,98,207,28,164,74,59,20,235,18,106,162,156,222,242,184,127,137,165,129,161,211,59,255,44,10,66,8,81,247,86,133,223,145,47,146,123,143,84,170,44,169,80,9,9,127,133,97,238,176,234,140,98,150,228,21,103,205,13,6,162,115,211,124,219,108,183,30,198,90,252,100,222,88,130,86,66,11,89,211,54,230,14,75,206,174,104,113,219,202,205,98,155,219,74,127,40,83,107,102,171,217,52,149,178,237,255,78,72,126,109,167,180,224,32,82,136,209,226,232,99,189,66,167,244,24,182,148,242,1,230,146,16,118,254,34,222,50,164,54,51,87,70,220,245,195,69,50,188,15,215,83,166,245,65,78,88,78,40,113,55,86,30,201,165,102,79,21,56,71,236,49,246,224,101,150,124,21,37,203,89,30,3,171,214,143,101,94,39,245,24,206,211,98,71,250,200,164,157,71,99,213,40,186,228,181,179,184,38,127,145,238,179,180,30,35,0,139,23,5,91,100,240,169,143,97,53,186,85,195,106,18,12,46,78,47,176,176,105,101,171,221,9,196,219,182,185,23,221,98,71,38,19,159,68,63,88,175,154,155,151,254,141,31,220,249,6,117,100,122,161,193,91,136,41,38,165,219,131,142,31,218,162,204,248,226,133,4,30,104,79,138,170,75,184,53,141,73,28,247,125,7,226,218,196,18,149,251,12,29,117,142,70,76,43,178,179,75,141,168,244,82,141,187,19,69,176,50,83,133,142,79,60,205,80,234,180,2,43,165,19,214,157,8,60,86,83,154,28,229,139,132,125,173,238,228,214,137,103,193,29,216,136,102,140,249,193,25,196,18,43,171,24,219,214,98,83,117,201,69,215,91,170,44,14,237,203,167,13,252,97,197,62,127,202,32,62,211,119,11,2,146,62,82,16,16,249,36,65,0,216,27,4,241,37,159,28,80,41,136,104,91,234,108,152,123,202,187,13,94,219,134,239,170,104,251,21,138,144,242,46,152,86,184,85,217,148,252,113,198,214,113,84,202,199,78,137,79,210,42,4,150,95,246,121,104,191,194,47,179,90,39,180,195,160,84,23,120,89,225,75,243,43,207,89,244,58,3,75,22,232,233,200,160,62,61,19,121,234,85,136,201,174,247,74,231,109,14,236,57,215,196,86,186,19,106,219,158,241,199,218,194,188,250,192,246,6,208,179,69,162,192,225,120,162,101,143,134,216,195,149,225,88,128,191,120,60,112,236,206,131,149,160,39,72,89,205,240,194,133,228,156,45,179,134,115,76,38,214,177,238,151,113,174,172,66,131,208,154,53,88,232,94,183,72,178,205,34,162,122,86,184,4,173,119,173,93,193,25,109,177,6,139,230,235,12,205,195,253,90,99,111,177,144,218,172,96,195,53,55,158,64,36,192,195,141,255,212,201,142,40,76,161,134,87,225,12,202,34,44,94,78,86,80,233,49,87,203,47,40,206,71,54,178,103,26,188,135,234,196,252,196,152,184,49,110,94,216,59,75,217,203,229,157,90,165,73,203,10,180,237,50,17,194,241,102,25,194,177,38,132,22,153,203,9,79,183,32,60,45,35,76,235,223,229,164,183,208,123,56,42,35,77,91,21,229,164,189,45,72,123,101,164,177,187,80,78,59,218,130,118,84,64,91,123,53,35,174,141,220,99,98,250,102,3,29,38,102,175,55,216,59,45,90,126,205,158,130,169,235,0,129,241,13,243,156,204,62,113,96,149,91,162,31,134,26,17,229,233,134,32,162,191,102,17,148,92,127,35,45,253,89,84,74,78,150,148,184,72,173,244,234,183,142,141,44,14,196,184,48,29,23,45,142,12,237,102,158,118,9,91,27,200,139,206,7,123,79,161,244,48,210,87,30,110,124,228,198,99,216,171,188,164,142,72,118,105,184,81,158,136,142,71,182,18,118,180,216,167,199,32,56,49,168,0,161,231,140,137,213,248,247,31,71,123,141,235,154,137,225,149,245,85,176,95,132,23,179,118,65,32,83,195,52,133,240,8,182,90,241,174,76,105,4,44,153,216,193,131,76,101,112,236,120,222,200,25,223,92,16,200,169,119,109,147,90,197,100,254,170,170,71,76,74,185,21,138,88,9,127,83,81,152,46,52,156,157,242,117,107,124,213,206,106,37,94,229,200,98,83,170,103,204,137,150,105,34,108,111,204,172,223,154,6,230,208,180,47,144,201,162,247,104,107,160,109,154,29,4,221,1,155,197,228,232,8,37,68,255,34,62,85,76,201,194,74,237,180,82,253,254,93,198,220,242,250,235,97,83,107,138,108,67,247,237,58,178,233,147,31,246,180,9,172,191,254,185,144,254,130,130,223,177,115,221,255,210,190,125,166,147,248,248,151,24,88,254,44,126,151,193,110,159,76,20,217,187,83,46,42,104,201,61,213,178,198,152,30,21,118,37,185,11,232,139,237,197,220,143,5,6,53,35,69,112,162,137,145,176,247,84,180,67,84,156,151,99,83,112,246,74,116,25,89,183,200,60,104,0,232,96,22,53,104,27,137,103,248,105,85,20,187,73,216,71,204,20,93,211,231,37,143,17,37,102,111,73,145,164,144,137,182,63,97,181,53,12,22,115,130,215,118,113,121,214,251,108,148,53,177,157,51,205,58,201,230,218,70,93,78,31,242,0,86,70,178,91,154,14,131,38,184,18,84,236,53,251,92,47,118,228,118,186,58,44,247,58,46,36,106,96,246,166,154,26,208,196,27,175,32,41,135,36,189,77,251,138,63,108,63,136,193,164,104,107,136,86,194,202,26,131,59,210,208,104,67,3,255,221,191,139,156,48,223,227,166,196,14,205,236,243,35,177,20,75,154,113,49,172,92,149,45,199,91,238,52,1,2,76,37,211,102,83,106,6,70,103,218,79,53,213,246,124,17,38,11,180,149,67,189,247,10,156,50,100,133,87,121,160,8,110,153,180,148,217,120,179,106,120,119,91,60,88,19,149,249,150,249,86,6,125,48,10,236,192,146,218,8,252,113,33,234,160,97,233,199,190,72,221,12,62,207,228,59,138,238,76,190,166,235,115,24,26,159,251,3,174,54,10,190,85,24,231,130,241,192,239,81,189,8,70,233,123,105,166,33,204,0,177,19,189,129,115,83,108,217,131,6,106,70,81,93,238,232,21,42,132,91,136,96,150,191,216,131,67,107,225,49,174,181,231,21,204,214,234,51,38,67,55,54,229,208,84,140,93,136,202,173,141,162,211,23,89,236,237,196,139,103,191,190,126,213,236,164,47,48,54,173,206,222,253,108,203,64,6,91,227,33,122,52,15,248,70,106,91,22,116,100,141,3,239,209,28,224,171,162,109,57,208,145,53,14,70,107,57,104,48,119,208,138,120,204,163,222,154,192,90,234,61,83,55,154,87,74,88,229,239,138,182,228,149,99,23,115,59,22,220,158,254,70,25,21,187,239,160,1,190,172,158,22,44,169,16,142,174,236,74,53,59,79,183,213,22,27,158,207,208,183,59,219,218,250,33,196,54,58,174,83,49,230,46,40,162,9,127,157,111,118,165,213,108,42,175,77,212,3,15,181,161,199,157,34,134,249,117,2,118,41,134,133,66,12,164,88,112,162,226,217,47,162,122,38,219,18,122,98,226,240,104,136,137,76,123,4,183,143,155,142,81,18,26,169,228,88,226,226,79,105,152,236,236,86,85,42,42,4,1,76,241,5,34,191,252,48,253,129,161,27,10,131,74,254,167,51,9,216,105,76,125,32,199,172,96,150,125,253,83,198,164,82,125,43,100,145,231,163,50,96,99,9,235,161,74,20,133,47,206,84,186,186,86,50,43,92,159,37,192,98,249,169,123,189,136,8,210,228,157,124,254,66,13,6,198,14,127,170,148,201,249,148,33,252,0,2,42,125,76,169,57,253,207,131,227,99,227,93,223,232,26,8,54,202,93,134,201,197,123,72,63,202,61,88,130,207,121,195,15,163,65,255,15,21,236,201,121,200,212,76,38,198,20,130,232,253,174,122,10,56,244,173,2,137,130,57,173,203,51,78,17,36,57,61,84,177,71,112,223,148,122,100,65,54,4,29,23,209,10,22,9,232,35,69,14,81,149,101,255,91,81,175,220,103,115,219,242,20,49,115,187,202,188,70,16,61,23,194,200,22,244,218,213,170,72,69,212,30,43,188,44,162,182,253,177,44,34,139,147,178,46,194,239,236,217,14,190,168,227,60,132,36,175,228,20,63,47,221,230,65,67,150,224,174,38,123,230,254,162,221,46,55,188,33,120,16,225,220,61,117,155,151,0,15,90,65,187,246,110,110,186,63,136,182,118,121,223,208,51,127,16,97,53,243,236,101,155,217,133,205,237,66,231,213,238,185,188,120,94,230,195,169,115,22,205,42,244,81,225,125,171,213,127,1,213,103,58,13,116,66,0,0 };
    +const char JS_CONTROLS[] PROGMEM = R"=====(
    +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"";}}
    +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 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+"/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);}
    +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;}
    +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);}
    +break;case UI_TAB:if(data.visible){$("#tabsnav").append("
  • "+data.value+"
  • ");$("#tabscontent").append("
    ");tabs=$(".tabscontent").tabbedContent({loop:true}).data("api");$("a").filter(function(){return $(this).attr("href")==="#click-to-switch";}).on("click",function(e){var tab=prompt("Tab to switch to (number or id)?");if(!tabs.switchTab(tab)){alert("That tab does not exist :\\");} +e.preventDefault();});} +break;case UI_OPTION:if(data.parentControl){var parent=$("#select"+data.parentControl);parent.append("");} +break;case UI_MIN:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("min",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("min",data.value);}} +break;case UI_MAX:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("max",data.value);}else if($('#text'+data.parentControl).length){$('#text'+data.parentControl).attr("maxlength",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("max",data.value);}} +break;case UI_STEP:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("step",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).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=new Date().getTime();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:$("#sl"+data.id).attr("value",data.value) +slider_move($("#sl"+data.id).parent().parent(),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);} +if(data.hasOwnProperty('inputType')){$("#text"+data.id).attr("type",data.inputType);} +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:$("#btn"+data.id).val(data.value);$("#btn"+data.id).text(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;case UPDATE_TIME:var rv=new Date().toISOString();websock.send("time:"+rv+":"+data.id);break;case UI_FRAGMENT:let FragmentLen=data.length;let FragementOffset=data.offset;let NextFragmentOffset=FragementOffset+FragmentLen;let Total=data.total;let Arrived=(FragmentLen+FragementOffset);let FragmentFinal=Total===Arrived;if(!data.hasOwnProperty('control')) +{console.error("UI_FRAGMENT:Missing control record, skipping control");break;} +let control=data.control;StopFragmentAssemblyTimer(data.control.id);if(0===FragementOffset) +{controlAssemblyArray[control.id]=data;controlAssemblyArray[control.id].offset=NextFragmentOffset;StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':NextFragmentOffset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;} +if("undefined"===typeof controlAssemblyArray[control.id]) +{console.error("Missing first fragment for control: "+control.id);StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':0});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;} +if(FragementOffset!==controlAssemblyArray[control.id].offset) +{console.error("Wrong next fragment. Expected: "+controlAssemblyArray[control.id].offset+" Got: "+FragementOffset);StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':controlAssemblyArray[control.id].length+controlAssemblyArray[control.id].offset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");break;} +controlAssemblyArray[control.id].control.value+=control.value;controlAssemblyArray[control.id].offset=NextFragmentOffset;if(true===FragmentFinal) +{var fauxEvent={data:JSON.stringify(controlAssemblyArray[control.id].control),};handleEvent(fauxEvent);controlAssemblyArray[control.id]=null;} +else +{StartFragmentAssemblyTimer(control.id);let TotalRequest=JSON.stringify({'id':control.id,'offset':NextFragmentOffset});websock.send("uifragmentok:"+0+": "+TotalRequest+":");} +break;default:console.error("Unknown type or event");break;} +if(data.type>=UI_TITEL&&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)+ +"
    ";break;case UI_SEPARATOR:html="
    "+ +"
    "+data.label+"

    ";break;case UI_TIME:break;} +parent.append(html);}else{var parent=$("#id"+data.parentControl);parent.append(elementHTML(data));}} +var elementHTML=function(data){var id=data.id +var elementStyle=data.hasOwnProperty('elementStyle')?" style='"+data.elementStyle+"' ":"";var inputType=data.hasOwnProperty('inputType')?" type='"+data.inputType+"' ":"";switch(data.type){case UI_LABEL:return""+data.value+"";case UI_BUTTON:return"";case UI_SWITCHER:return"";case UI_CPAD:case UI_PAD:return"";case UI_SLIDER:return"
    "+ +""+ +data.value+"
    ";case UI_NUMBER:return"";case UI_TEXT_INPUT:return"";case UI_SELECT:return"";case UI_ACCEL:return"ACCEL // Not implemented fully!
    ";default:return"";}}
    +var processEnabled=function(data){switch(data.type){case UI_SWITCHER:case UPDATE_SWITCHER:if(data.enabled){$("#sl"+data.id).removeClass('disabled');$("#s"+data.id).prop("disabled",false);}else{$("#sl"+data.id).addClass('disabled');$("#s"+data.id).prop("disabled",true);}
    +break;case UI_SLIDER:case UPDATE_SLIDER:$("#sl"+data.id).prop("disabled",!data.enabled);break;case UI_NUMBER:case UPDATE_NUMBER:$("#num"+data.id).prop("disabled",!data.enabled);break;case UI_TEXT_INPUT:case UPDATE_TEXT_INPUT:$("#text"+data.id).prop("disabled",!data.enabled);break;case UI_SELECT:case UPDATE_SELECT:$("#select"+data.id).prop("disabled",!data.enabled);break;case UI_BUTTON:case UPDATE_BUTTON:$("#btn"+data.id).prop("disabled",!data.enabled);break;case UI_PAD:case UI_CPAD:case UPDATE_PAD:case UPDATE_CPAD:if(data.enabled){$("#id"+data.id+" nav").removeClass('disabled');}else{$("#id"+data.id+" nav").addClass('disabled');}
    +break;}}
    +)=====";
    +
    +const uint8_t JS_CONTROLS_GZIP[4837] PROGMEM = { 31,139,8,0,112,220,0,101,2,255,213,60,107,119,218,72,178,223,253,43,100,101,78,128,107,130,33,153,100,51,96,57,135,96,146,176,107,27,95,27,111,230,108,38,215,71,160,198,104,45,36,173,36,252,88,134,255,126,171,171,31,234,214,3,176,61,185,59,247,75,12,213,213,213,245,234,238,170,234,34,147,192,143,19,227,114,112,53,56,29,140,6,221,227,171,207,151,3,235,117,179,217,153,136,129,243,254,241,176,123,4,176,150,128,157,29,117,71,253,171,225,167,79,23,253,145,213,82,113,251,191,142,250,167,71,140,70,75,129,143,6,163,254,177,165,0,206,128,98,134,30,130,210,69,6,87,61,10,121,173,35,245,24,214,235,20,235,227,229,104,52,60,181,222,232,120,28,218,106,190,73,49,143,187,31,129,137,159,117,68,6,108,53,127,78,241,46,190,14,70,189,47,253,115,235,173,142,42,225,173,230,91,5,251,120,112,4,176,119,25,92,6,109,53,223,165,152,167,151,39,31,1,246,23,29,147,67,91,205,191,40,234,2,61,130,65,206,46,71,214,123,29,91,25,105,53,223,167,51,62,159,119,207,190,88,191,112,64,247,232,136,65,174,206,134,131,83,138,202,7,122,199,253,238,57,71,110,53,127,81,86,236,126,180,90,25,131,48,152,98,144,139,254,113,191,7,212,50,54,17,224,150,98,149,225,217,104,64,245,159,49,139,0,183,20,179,156,12,0,144,177,10,194,90,138,77,78,186,191,90,173,140,61,16,214,82,77,49,234,159,89,173,172,37,16,216,82,236,240,185,123,249,185,111,181,50,118,224,208,150,98,134,110,175,71,125,35,99,2,14,109,189,87,21,115,214,61,239,142,134,96,198,95,178,186,145,35,45,85,221,131,147,62,236,168,140,190,41,176,245,90,217,37,159,206,187,159,79,250,96,192,215,169,105,228,46,58,26,126,61,149,123,232,184,255,105,36,247,202,249,224,243,151,145,220,17,61,32,0,14,38,148,217,187,26,93,158,255,247,229,112,112,209,151,164,122,87,253,147,254,121,247,56,221,147,189,171,179,62,204,58,31,252,29,166,190,150,192,175,253,81,247,226,236,75,247,88,33,127,117,113,121,250,233,120,248,85,91,163,215,61,63,31,142,228,14,234,93,117,143,7,255,232,158,131,89,223,73,208,81,247,252,111,114,55,244,174,78,135,167,160,147,183,111,59,183,118,100,0,48,137,2,175,27,199,100,62,246,30,186,81,100,63,88,62,185,51,134,227,127,146,73,82,173,33,214,167,200,190,158,19,63,17,104,35,119,78,162,60,218,117,100,135,179,35,59,177,113,8,105,241,145,153,29,119,39,19,226,89,83,219,139,9,130,98,207,117,72,212,131,245,93,127,17,44,98,62,52,93,248,147,196,13,124,224,204,11,162,158,103,199,113,21,63,14,156,218,146,127,176,78,23,243,49,137,36,188,19,223,185,201,100,166,224,217,49,81,13,208,142,72,178,136,124,19,254,249,215,34,112,99,98,118,56,10,183,135,64,32,32,151,237,57,114,56,53,142,192,8,73,66,162,200,189,37,145,68,74,141,37,144,238,72,98,199,225,204,246,18,137,36,141,39,112,226,133,63,245,130,59,133,14,179,165,24,159,216,81,20,164,243,133,93,197,176,237,185,255,182,35,215,151,8,212,202,109,254,153,154,88,32,58,118,116,99,118,28,50,181,23,94,34,128,102,103,181,218,161,86,184,35,227,56,152,220,116,148,207,96,18,31,108,74,28,197,88,95,201,248,2,134,72,194,45,191,240,188,212,82,17,249,215,130,196,201,48,114,193,69,108,10,58,35,209,220,141,99,248,84,173,45,87,59,18,51,182,111,201,103,225,36,48,228,5,19,219,187,72,2,112,47,210,136,73,50,72,200,188,106,146,56,92,184,232,75,177,89,255,235,197,240,180,17,39,32,233,181,59,125,168,74,23,171,213,58,10,225,8,214,15,34,133,182,11,94,128,94,6,43,58,232,146,218,90,215,133,107,165,196,59,238,180,42,167,238,162,188,181,101,74,11,153,10,237,40,38,41,86,173,227,145,196,112,25,130,132,126,115,157,239,29,166,116,182,33,26,110,204,54,6,195,172,125,96,127,219,223,190,131,60,12,17,63,106,162,217,17,108,177,229,79,85,39,152,44,232,62,172,53,108,199,169,154,255,101,214,26,193,116,10,123,236,167,170,249,34,10,238,224,251,44,153,123,85,211,172,209,237,126,1,214,88,196,125,240,163,8,112,56,25,149,118,22,103,89,184,209,27,211,32,234,219,176,191,136,71,232,160,117,184,156,120,196,142,6,62,108,133,91,219,19,112,32,93,235,108,113,84,108,62,115,64,251,73,180,32,150,101,101,125,178,182,44,241,82,14,110,76,188,0,172,194,52,34,197,3,189,68,100,30,220,18,118,158,152,120,80,188,186,142,8,241,205,60,42,232,86,195,139,136,83,128,197,20,141,138,51,246,141,211,192,224,28,81,189,190,124,241,254,221,155,183,157,130,89,169,185,52,168,95,5,149,186,147,155,54,183,118,29,52,185,82,12,53,179,125,199,35,127,119,99,119,236,122,110,242,208,3,192,53,136,185,4,77,237,102,53,242,242,229,174,112,148,198,204,117,28,226,215,150,210,139,52,178,194,179,168,231,210,253,65,129,214,157,235,59,193,93,67,124,111,204,130,56,241,237,57,65,255,14,131,40,201,97,80,96,71,46,9,234,235,223,194,135,99,55,78,136,15,199,180,121,43,249,158,32,223,102,189,88,158,58,154,18,205,79,73,238,90,166,249,251,239,236,211,251,166,248,244,243,207,111,164,19,160,215,200,179,169,106,222,197,237,253,125,115,79,48,182,103,238,223,129,122,59,43,2,100,31,59,103,7,184,160,251,30,156,80,63,253,106,203,204,105,72,15,46,177,21,132,106,153,105,132,83,70,196,118,30,168,181,169,75,191,209,173,81,127,219,108,54,233,122,2,57,240,131,144,248,150,164,68,110,19,122,243,249,113,224,17,208,249,53,176,204,48,13,138,183,133,255,150,249,121,66,238,65,126,233,54,128,145,219,91,116,23,118,254,36,135,194,170,147,106,8,119,249,54,42,66,196,162,211,240,79,40,20,161,172,109,35,20,202,0,66,165,110,244,167,145,134,5,123,116,115,227,25,144,145,38,137,30,240,86,118,50,151,40,12,54,28,188,67,87,59,176,13,129,101,156,157,138,143,170,225,64,169,177,152,248,112,9,46,220,224,166,109,238,53,107,252,154,237,176,192,134,88,242,64,26,7,206,3,11,118,9,149,28,78,21,17,48,210,53,27,201,67,72,120,200,168,167,231,237,130,91,149,130,18,123,28,251,246,109,17,152,170,8,22,81,135,224,24,192,101,178,241,46,196,19,217,8,184,16,15,228,65,56,215,126,92,96,85,42,27,132,119,247,76,229,75,138,222,206,196,77,194,212,245,85,71,49,79,85,206,66,31,16,172,38,65,98,123,98,189,195,170,190,188,71,252,235,100,246,170,85,147,167,176,110,136,82,116,16,100,12,7,225,77,71,168,58,45,98,180,255,179,18,50,189,211,173,4,23,27,185,223,123,146,196,219,204,207,235,128,21,125,218,217,27,53,34,94,96,59,176,163,116,108,44,239,180,165,99,39,110,226,17,230,53,158,61,38,30,186,225,220,118,253,47,112,223,64,98,193,189,48,69,200,210,195,162,76,91,124,99,21,18,249,53,45,129,72,16,171,64,200,175,152,202,43,131,60,5,111,11,45,227,189,239,145,26,92,72,163,224,203,232,228,184,202,54,185,206,4,43,33,229,38,45,179,179,168,112,227,196,55,247,16,13,2,124,12,155,146,96,49,153,161,238,219,233,89,83,91,146,70,24,17,106,246,35,150,248,80,85,46,146,132,222,28,16,102,85,57,137,58,189,223,192,40,117,164,2,54,125,10,13,30,180,172,48,102,203,152,87,148,178,54,75,199,78,36,136,150,4,89,134,110,123,200,96,134,44,173,206,73,189,211,207,91,41,47,156,62,89,119,161,237,48,161,47,207,234,79,213,93,17,13,85,119,200,162,247,124,22,105,137,230,249,76,106,84,114,108,70,207,103,19,139,71,207,231,83,39,147,99,116,252,124,70,105,253,235,249,124,106,84,114,108,78,158,207,38,43,191,61,159,209,12,157,181,187,27,139,207,155,55,95,68,147,155,11,188,215,171,187,185,56,32,71,118,212,253,152,167,169,7,30,118,24,226,13,116,224,185,135,7,182,17,248,115,160,68,22,161,85,1,28,38,135,212,232,158,89,171,24,179,136,76,173,10,165,160,192,43,135,252,11,30,51,123,230,193,190,125,120,176,15,36,11,35,26,185,166,227,222,26,174,131,75,105,196,14,246,97,132,206,165,243,44,32,208,208,9,192,183,49,113,122,236,123,117,233,5,65,216,166,102,90,213,48,248,171,154,118,232,178,149,109,192,158,186,30,4,107,106,66,197,203,40,63,85,147,153,27,3,63,73,2,185,37,21,204,172,65,90,5,185,13,149,251,85,18,188,98,103,169,9,23,62,117,38,19,225,102,93,117,0,26,79,0,59,86,24,5,243,16,146,160,145,61,54,146,192,96,19,233,39,72,253,104,137,209,128,228,30,124,242,3,139,227,118,169,60,13,134,4,51,170,240,21,98,2,219,35,17,37,49,179,19,74,211,112,2,18,27,126,144,24,228,30,50,96,163,253,219,111,152,78,22,248,92,129,75,177,194,189,52,63,132,199,48,163,199,34,9,198,54,3,81,245,190,136,33,230,153,36,220,6,58,106,135,125,77,141,22,132,152,241,83,187,177,143,230,222,142,176,221,142,89,49,208,5,172,138,128,50,143,160,3,2,194,22,35,20,251,80,192,48,168,0,192,193,62,163,121,104,230,37,58,25,148,138,3,224,159,170,149,23,177,87,41,18,129,199,77,212,245,203,113,152,19,204,93,223,212,47,76,154,241,27,156,62,152,114,243,2,165,72,101,43,228,228,236,254,250,227,229,180,239,215,200,73,243,249,205,43,148,99,201,53,24,250,143,214,104,78,150,220,241,58,234,159,253,112,149,198,9,9,127,176,164,5,75,100,69,197,151,194,205,23,137,44,80,127,227,91,247,187,149,171,124,139,123,20,210,97,72,70,34,28,184,184,189,174,230,231,214,77,132,165,87,175,190,119,51,143,155,109,60,51,99,76,248,129,12,169,214,104,21,157,150,9,170,69,140,53,194,69,60,171,46,239,219,73,92,127,104,167,194,195,153,151,121,5,120,10,163,10,155,202,83,107,187,64,63,223,190,255,193,235,137,247,74,106,45,241,172,85,99,227,242,149,11,203,102,155,195,130,181,239,38,153,131,84,121,69,199,186,132,26,40,167,89,30,247,47,177,52,48,52,188,243,207,162,32,132,43,234,161,90,225,57,242,69,242,224,145,74,141,5,21,42,33,225,175,48,204,29,86,157,81,204,146,76,113,214,100,48,112,59,55,205,15,205,118,235,113,172,197,207,230,141,5,104,37,180,144,53,109,99,238,176,224,236,138,62,21,84,115,179,216,230,174,166,31,148,169,117,179,213,108,154,74,1,251,255,78,72,158,182,83,90,112,16,41,196,104,137,239,169,94,161,83,122,10,91,74,249,0,99,73,184,118,254,32,222,50,164,54,51,87,70,220,245,195,69,50,122,8,215,83,166,245,65,78,88,78,40,113,55,86,30,201,133,102,207,21,56,71,236,41,246,224,101,150,124,21,37,203,89,30,3,31,10,158,202,188,78,234,41,156,167,197,142,180,55,169,157,71,99,213,40,186,228,181,189,184,38,127,144,238,179,180,158,34,0,187,47,10,182,200,224,164,143,215,106,116,171,94,171,73,48,184,24,94,96,97,179,154,173,118,39,112,223,182,205,189,232,118,207,108,151,222,79,162,157,165,77,95,236,68,97,255,152,248,188,84,136,145,75,71,140,33,243,195,233,52,38,9,27,15,240,51,142,159,130,225,197,124,142,146,153,178,167,144,199,41,35,90,89,181,210,34,43,2,187,216,49,225,88,85,5,123,47,67,169,214,81,185,253,228,250,64,134,17,179,44,62,31,115,176,66,203,241,90,43,24,109,39,243,102,96,170,250,56,161,151,171,127,45,186,94,140,136,76,130,200,169,27,241,141,27,134,202,128,41,52,186,218,161,76,113,168,165,150,117,59,23,73,16,22,62,154,104,213,95,180,14,176,221,4,41,178,2,35,171,185,23,149,111,233,204,239,184,98,103,19,22,183,152,149,183,22,48,105,71,73,49,151,42,131,210,112,231,44,36,177,50,181,245,101,197,117,42,237,116,70,189,194,150,172,180,243,107,174,242,15,52,83,142,192,30,106,192,115,33,169,84,151,163,190,156,106,28,180,101,46,32,66,155,186,62,113,76,208,27,61,128,131,169,177,73,13,121,211,11,115,79,221,8,18,113,193,133,49,13,100,223,19,229,68,85,196,15,215,87,243,143,80,79,198,145,118,45,107,75,23,201,171,232,107,20,128,130,124,176,162,212,79,195,232,223,135,152,234,43,218,217,68,121,207,52,62,7,9,157,144,219,214,63,92,167,27,89,100,39,222,182,162,60,219,66,27,215,17,159,89,145,197,210,190,62,103,183,167,109,51,218,49,10,86,223,226,253,108,91,174,215,60,176,109,60,206,176,111,108,181,67,19,236,157,229,255,167,179,73,220,234,162,141,46,123,199,248,55,126,112,231,27,244,164,162,37,67,172,243,233,123,86,62,53,31,90,226,33,239,229,75,9,60,208,122,189,107,203,48,10,38,36,142,251,190,13,153,163,83,21,111,227,25,58,234,28,141,152,246,140,205,202,134,226,45,149,198,52,174,163,132,14,101,193,80,104,251,196,211,66,33,117,90,65,28,148,78,88,23,115,243,108,152,210,228,40,223,36,12,142,240,220,58,241,44,184,131,40,8,93,38,55,56,131,108,173,154,85,140,101,105,217,95,109,201,69,215,91,192,88,166,247,74,118,132,242,126,212,87,188,3,84,124,77,219,61,5,36,237,237,20,16,217,201,41,0,172,117,83,124,147,157,154,84,10,34,90,164,116,54,232,57,43,219,93,121,252,0,223,107,162,119,169,80,132,148,119,193,180,194,173,202,166,228,143,51,182,142,163,82,62,118,74,124,146,214,249,241,129,227,21,79,158,175,240,155,89,107,16,250,134,175,212,239,121,225,254,91,243,59,175,10,232,149,124,150,142,211,252,131,65,233,165,84,229,197,141,66,76,86,64,87,26,104,230,192,30,92,62,150,114,60,169,221,142,107,14,156,129,83,163,7,82,89,68,71,123,140,11,71,190,13,224,84,43,236,255,186,66,146,122,44,179,187,62,150,129,41,223,217,28,122,78,150,221,234,28,235,81,199,98,158,66,99,235,139,20,177,159,121,51,174,86,171,122,171,217,108,214,7,78,77,183,200,26,141,151,107,175,212,20,108,206,186,97,189,1,170,28,147,178,172,244,73,162,103,179,230,69,254,50,196,78,85,32,98,137,34,14,135,99,182,153,85,84,236,161,207,130,142,224,47,166,110,28,187,243,232,237,163,23,175,178,123,138,63,42,73,206,217,50,107,56,199,66,207,58,214,253,50,206,149,85,104,129,96,205,26,172,172,178,110,145,100,155,69,196,203,102,225,18,244,45,114,237,10,246,120,139,53,88,165,101,157,161,121,41,102,173,177,183,88,72,109,36,97,195,117,55,118,32,134,192,107,145,127,212,201,142,41,76,161,134,207,20,25,148,69,88,188,156,124,221,166,59,168,158,95,80,220,172,108,4,130,121,222,223,102,199,252,174,113,220,24,143,125,56,117,151,178,207,142,119,209,41,13,116,236,241,188,93,38,66,56,217,44,67,56,209,132,208,170,38,229,132,167,91,16,158,150,17,166,189,9,229,164,183,208,123,56,46,35,77,219,72,202,73,123,91,144,246,202,72,99,231,71,57,237,104,11,218,81,1,109,237,220,19,37,125,238,49,49,109,97,70,135,137,89,51,51,251,97,4,45,203,100,79,193,212,117,128,192,228,134,121,78,102,159,216,176,202,45,209,15,67,141,136,210,201,44,136,232,205,220,130,18,164,57,155,104,233,63,0,72,201,201,231,62,46,82,43,45,203,175,99,35,139,3,209,81,152,142,139,246,147,12,237,102,158,118,9,91,27,200,139,174,20,214,235,170,244,151,164,13,183,110,124,228,198,19,216,171,188,221,1,145,172,210,235,70,249,77,214,100,108,41,215,142,118,247,233,119,16,156,24,84,128,208,179,39,164,186,255,63,191,29,237,237,95,215,77,12,204,88,207,11,246,242,96,209,188,93,112,145,169,1,30,133,240,27,108,181,226,29,51,165,55,96,201,196,14,30,100,42,131,19,219,243,198,246,228,230,130,64,54,182,107,153,212,42,38,243,87,85,61,98,82,202,173,80,196,74,248,155,138,194,116,161,225,236,148,175,91,231,171,118,86,43,209,32,45,31,2,83,61,99,52,189,76,83,40,107,99,78,246,193,52,48,251,162,61,27,153,252,107,143,182,109,180,77,179,131,160,59,96,179,152,28,29,161,132,232,95,196,47,173,175,106,239,218,149,218,239,191,203,59,183,252,109,252,176,169,53,172,108,67,247,195,58,178,105,59,54,235,50,7,235,175,111,229,214,187,91,249,251,71,174,51,179,180,167,50,211,229,245,244,46,89,124,154,46,238,153,101,47,3,76,20,217,87,165,164,184,104,201,61,213,178,198,132,30,21,86,37,185,11,232,79,36,23,115,63,22,24,212,140,20,193,142,28,35,97,189,238,180,123,167,56,163,195,134,173,217,91,209,1,198,58,121,204,131,125,0,29,204,162,125,218,226,195,115,195,244,197,26,59,125,176,199,43,243,224,144,182,254,62,69,148,152,253,106,10,73,10,153,104,107,26,172,182,134,193,98,78,240,73,69,148,93,244,30,40,202,154,216,206,153,70,42,201,230,218,38,170,156,62,228,1,172,140,100,183,52,29,6,77,112,37,168,216,107,246,185,254,16,149,219,233,234,176,220,235,184,144,120,159,180,54,189,119,2,77,172,149,8,146,114,72,210,219,180,175,248,47,73,15,98,48,41,218,26,110,43,97,101,141,193,29,105,104,180,161,129,255,190,186,139,236,48,223,127,72,137,29,154,217,214,112,177,20,11,154,113,49,124,85,44,91,142,183,67,210,0,8,48,149,72,155,77,169,27,120,59,211,94,55,83,109,157,44,194,100,23,109,229,80,239,139,3,78,25,178,194,171,60,80,4,183,76,90,202,108,188,89,53,188,243,80,252,152,64,116,77,180,204,15,242,210,7,163,192,14,44,169,170,193,31,23,110,29,52,44,253,242,74,132,110,6,159,103,242,29,69,119,38,95,211,245,57,12,141,207,253,1,87,27,7,247,21,198,185,96,60,240,123,84,47,130,81,250,171,62,166,33,140,0,177,75,112,3,231,166,216,178,7,251,168,25,69,117,185,163,87,168,16,178,16,193,172,120,225,59,60,88,120,140,107,173,245,149,217,90,109,49,55,116,99,83,14,77,197,216,133,168,220,218,40,58,237,150,103,125,173,47,95,252,242,238,109,179,147,118,199,110,90,157,245,100,111,203,64,6,91,227,33,122,50,15,216,191,190,45,11,58,178,198,129,247,100,14,176,227,123,91,14,116,100,141,131,241,90,14,246,153,59,104,229,95,230,81,31,76,96,45,245,158,169,27,205,43,37,172,242,158,239,45,121,229,216,197,220,78,4,183,195,191,81,70,197,238,59,216,7,95,86,79,11,22,84,8,71,87,118,165,26,157,167,219,106,139,13,207,103,232,219,157,109,109,253,16,98,27,29,215,169,24,115,23,20,209,132,191,246,189,85,105,53,155,74,39,176,122,224,161,54,244,123,167,136,97,158,78,192,46,197,107,161,16,3,41,22,156,168,120,246,139,91,61,19,109,9,61,49,113,248,109,136,129,76,123,12,217,199,77,199,40,185,26,169,228,88,226,226,109,206,76,118,150,85,149,138,10,151,0,134,248,2,145,39,63,76,127,96,232,125,133,65,37,254,211,153,4,236,244,78,125,36,199,172,96,150,237,204,46,99,82,169,190,21,178,200,227,81,121,97,99,9,235,177,74,20,133,47,206,84,186,186,86,50,43,92,159,5,192,98,249,169,123,189,136,8,210,228,93,150,252,215,3,48,48,177,121,27,121,38,230,83,134,240,11,16,80,233,99,72,205,233,127,29,28,31,27,31,251,70,215,64,176,81,238,50,76,46,222,223,243,163,220,131,5,248,156,55,252,98,236,211,255,45,0,246,228,60,100,106,38,142,49,133,75,244,97,87,61,5,108,218,71,74,162,96,78,95,116,24,167,8,146,156,30,170,216,99,200,55,165,30,217,37,27,130,142,139,104,5,139,4,244,145,34,135,168,202,178,255,30,68,127,243,201,198,182,229,33,98,38,187,202,116,138,138,215,58,194,200,22,244,65,170,85,145,138,168,61,86,120,89,68,109,201,196,178,136,44,78,202,186,8,207,217,179,221,149,162,142,243,24,146,188,146,83,252,211,159,109,154,77,179,4,119,53,217,51,249,139,150,93,110,232,239,124,20,225,92,158,186,77,151,230,163,86,208,210,222,205,13,145,143,162,173,37,239,27,250,25,31,69,88,141,60,123,217,70,195,194,198,195,66,231,213,242,92,94,60,47,243,225,212,57,139,102,21,250,168,240,190,213,234,127,1,218,94,66,97,71,78,0,0 };