"
);
$("#tabscontent").append("");
tabs = $(".tabscontent").tabbedContent({ loop: true }).data("api");
// switch to tab...
$("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) {
//Is it applied to a slider?
if ($('#sl' + data.parentControl).length) {
$('#sl' + data.parentControl).attr("min", data.value);
} else if ($('#num' + data.parentControl).length) {
//Or a number
$('#num' + data.parentControl).attr("min", data.value);
}
}
break;
case UI_MAX:
if (data.parentControl) {
//Is it applied to a slider?
if ($('#sl' + data.parentControl).length) {
$('#sl' + data.parentControl).attr("max", data.value);
} else if ($('#text' + data.parentControl).length) {
//Is it a text element
$('#text' + data.parentControl).attr("maxlength", data.value);
} else if ($('#num' + data.parentControl).length) {
//Or a number
$('#num' + data.parentControl).attr("max", data.value);
}
}
break;
case UI_STEP:
if (data.parentControl) {
//Is it applied to a slider?
if ($('#sl' + data.parentControl).length) {
$('#sl' + data.parentControl).attr("step", data.value);
} else if ($('#num' + data.parentControl).length) {
//Or a number
$('#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 UI_FILEDISPLAY:
if (data.visible)
{
addToHTML(data);
FileDisplayUploadFile(data);
}
break;
/*
* Update messages change the value/style of a component without adding new HTML
*/
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 UPDATE_FILEDISPLAY:
FileDisplayUploadFile(data);
break;
case UI_FRAGMENT:
// console.info("Starting Fragment Processing");
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");
// console.info("Done Fragment Processing");
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 " + TotalRequest);
// console.info("Done Fragment Processing");
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 " + TotalRequest);
// console.info("Done Fragment Processing");
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: " + TotalRequest);
// console.info("Done Fragment Processing");
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 + ":");
// console.info("asked for the next fragment: " + TotalRequest);
}
// console.info("Done Fragment Processing");
break;
default:
console.error("Unknown type or event");
break;
}
if (data.type >= UI_TITEL && data.type < UPDATE_OFFSET) {
//A UI element was just added to the DOM
processEnabled(data);
}
if (data.type >= UPDATE_OFFSET && data.type < UI_INITIAL_GUI) {
//An "update" message was just recieved and processed
var element = $("#id" + data.id);
if (data.hasOwnProperty('panelStyle')) {
$("#id" + data.id).attr("style", data.panelStyle);
}
if (data.hasOwnProperty('visible')) {
if (data['visible'])
$("#id" + data.id).show();
else
$("#id" + data.id).hide();
}
if (data.type == UPDATE_SLIDER) {
element.removeClass(
"slider-turquoise slider-emerald slider-peterriver slider-wetasphalt slider-sunflower slider-carrot slider-alizarin"
);
element.addClass("slider-" + colorClass(data.color));
} else {
element.removeClass(
"turquoise emerald peterriver wetasphalt sunflower carrot alizarin"
);
element.addClass(colorClass(data.color));
}
processEnabled(data);
}
$(".range-slider__range").each(function () {
$(this)[0].value = $(this).attr("value");
$(this).next().html($(this).attr("value"));
});
};
websock.onmessage = handleEvent;
}
async function FileDisplayUploadFile(data)
{
let text = await downloadFile(data.value);
let ItemToUpdateId = "fd" + data.id;
// console.info("ItemToUpdateId: " + ItemToUpdateId);
// console.info(" text: " + text);
// populate the text object
$("#" + ItemToUpdateId).val(text);
$("#" + ItemToUpdateId).css("textAlign", "left");
$("#" + ItemToUpdateId).css("white-space", "nowrap");
$("#" + ItemToUpdateId).css("overflow", "scroll");
$("#" + ItemToUpdateId).css("overflow-y", "scroll");
$("#" + ItemToUpdateId).css("overflow-x", "scroll");
$("#" + ItemToUpdateId).scrollTop($("#" + ItemToUpdateId).val().length);
// scroll the page to the updated control
// $("#" + ItemToUpdateId).focus();
} // FileDisplayUploadFile
async function downloadFile(filename)
{
let response = await fetch(filename);
if(response.status != 200) {
throw new Error("File Read Server Error: '" + response.status + "'");
}
// read response stream as text
let text_data = await response.text();
return text_data;
} // downloadFile
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);
$(".range-slider__range").each(function () {
$(this).attr("value", $(this)[0].value);
});
}
function numberchange(number) {
var val = $("#num" + number).val();
websock.send("nvalue:" + val + ":" + number);
}
function textchange(number) {
var val = $("#text" + number).val();
websock.send("tvalue:" + val + ":" + number);
}
function tabclick(number) {
var val = $("#tab" + number).val();
websock.send("tabvalue:" + val + ":" + number);
}
function selectchange(number) {
var val = $("#select" + number).val();
websock.send("svalue:" + val + ":" + number);
}
function buttonclick(number, isdown) {
if (isdown) websock.send("bdown:" + number);
else websock.send("bup:" + number);
}
function padclick(type, number, isdown) {
if ($("#id" + number + " nav").hasClass("disabled")) {
return;
}
switch (type) {
case CENTER:
if (isdown) websock.send("pcdown:" + number);
else websock.send("pcup:" + number);
break;
case UP:
if (isdown) websock.send("pfdown:" + number);
else websock.send("pfup:" + number);
break;
case DOWN:
if (isdown) websock.send("pbdown:" + number);
else websock.send("pbup:" + number);
break;
case LEFT:
if (isdown) websock.send("pldown:" + number);
else websock.send("plup:" + number);
break;
case RIGHT:
if (isdown) websock.send("prdown:" + number);
else websock.send("prup:" + number);
break;
}
}
function switcher(number, state) {
if (state == null) {
if (!$("#sl" + number).hasClass("checked")) {
websock.send("sactive:" + number);
$("#sl" + number).addClass("checked");
} else {
websock.send("sinactive:" + number);
$("#sl" + number).removeClass("checked");
}
} else if (state == 1) {
$("#sl" + number).addClass("checked");
$("#sl" + number).prop("checked", true);
} else if (state == 0) {
$("#sl" + number).removeClass("checked");
$("#sl" + number).prop("checked", false);
}
}
var rangeSlider = function (isDiscrete) {
var range = $(".range-slider__range");
var slidercb = function () {
sliderchange($(this).attr("id").replace(/^\D+/g, ""));
};
range.on({
input: function () {
$(this).next().html(this.value)
}
});
range.each(function () {
$(this).next().html(this.value);
if ($(this).attr("callbackSet") != "true") {
if (!isDiscrete) {
$(this).on({ input: slidercb }); //input fires when dragging
} else {
$(this).on({ change: slidercb }); //change fires only once released
}
$(this).attr("callbackSet", "true");
}
});
};
var addToHTML = function (data) {
panelStyle = data.hasOwnProperty('panelStyle') ? " style='" + data.panelStyle + "' " : "";
panelwide = data.hasOwnProperty('wide') ? "wide" : "";
if (!data.hasOwnProperty('parentControl') || $("#tab" + data.parentControl).length > 0) {
//We add the control with its own panel
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:
case UI_FILEDISPLAY:
html = "
" + data.label + "
" +
elementHTML(data) +
"
";
break;
case UI_SEPARATOR:
html = "
" +
"
" + data.label + "
";
break;
case UI_TIME:
//Invisible element
break;
}
parent.append(html);
} else {
//We are adding to an existing panel so we only need the HTML for the element
var parent = $("#id" + data.parentControl);
parent.append(elementHTML(data));
}
}
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_FILEDISPLAY:
return "";
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_GRAPH:
return "";
case UI_GAUGE:
return "WILL BE A GAUGE ";
case UI_ACCEL:
return "ACCEL // Not implemented fully!
";
default:
return "";
}
}
var processEnabled = function (data) {
//Handle the enabling and disabling of controls
//Most controls can be disabled through the use of $("#").prop("disabled", true) and CSS will style it accordingly
//The switcher and pads also require the addition of the "disabled" class
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:
case UI_FILEDISPLAY:
case UPDATE_FILEDISPLAY:
if (data.enabled) {
$("#id" + data.id + " nav").removeClass('disabled');
} else {
$("#id" + data.id + " nav").addClass('disabled');
}
break;
}
}