var _ws = null; var _rootId = "0"; var _nextEleId = 0; var _focusId = null; var _focusVal = null; var _rootDivEle = null; var _rootEle = null; function set_app_title( suffix, className ) { var ele = document.getElementById('connectTitleId'); if(ele != null) { ele.innerHTML = suffix ele.className = className } else { console.log("Ele. not found. Set title failed.") } } function dom_child_by_id( parentEle, child_id ) { var childrenL = parentEle.children for(var i=0; i<childrenL.length; i++) { if( childrenL[i].id == child_id ) { return childrenL[i]; } } return null; } function dom_set_option_by_text( ele_id, text ) { var ele = dom_id_to_ele(ele_id); for (var i = 0; i < ele.options.length; i++) { if (ele.options[i].text === text) { ele.selectedIndex = i; break; } } } function dom_set_number( ele_id, val ) { dom_id_to_ele(ele_id).value = val } //============================================================================== function dom_id_to_ele( id ) { var ele = null; if( _rootEle == null ) { console.log("fail dtoe: root null"); } else { if( id == _rootId ) return _rootEle //ele = _rootEle.getElementById(id); ele = document.getElementById(id) if( ele == null ) { console.log("fail dtoe:" + id + " " + typeof(id) ); } } return ele } // { return document.getElementById(id); } function dom_set_checkbox( ele_id, fl ) { dom_id_to_ele(ele_id).checked = fl } function dom_get_checkbox( ele_id ) { return dom_id_to_ele(ele_id).checked } function dom_create_ele( ele_type ) { ele = document.createElement(ele_type); return ele } //============================================================================== function ui_error( msg ) { console.log("Error: " + msg ) } function ui_send_value( ele, typeId, value ) { ws_send("value " + ele.id + " " + typeId + " : " + value) } function ui_send_corrupt_state( ele ) { ws_send("corrupt " + ele.id) } function ui_send_bool_value( ele, value ) { ui_send_value(ele,'b',value); } function ui_send_int_value( ele, value ) { ui_send_value(ele,'i',value); } function ui_send_float_value( ele, value ) { ui_send_value(ele,'d',value); } function ui_send_string_value( ele, value ) { ui_send_value(ele,'s',value); } function ui_send_click( ele ) { //console.log("click " + ele.id ) ws_send("click " + ele.id ) } function ui_send_select( ele, selectedFl ) { let selected_value = selectedFl ? 1 : 0; ws_send("select " + ele.id + " " + selected_value ) } function ui_send_echo( ele ) { ws_send("echo " + ele.id ) } function ui_print_children( eleId ) { var childrenL = dom_id_to_ele(eleId).children for(var i=0; i<childrenL.length; i++) { console.log( childrenL[i] ) } } function ui_get_parent( parentId ) { if( parentId==null || parentId.trim().length == 0 ) parentId = _rootId parent_ele = dom_id_to_ele(parentId); if( parent_ele == null ) { ui_error("Parent element id: " + parentId + " not found.") } return parent_ele; } function ui_on_click( ele, evt ) { ui_send_click(ele); evt.stopPropagation(); } function ui_create_ele( parent_ele, ele_type, d, dfltClassName ) { // create the ctl object var ele = dom_create_ele(ele_type); if( ele == null ) ui_error(ele_type +" element create failed."); else { ele.id = d.uuId; if(d.hasOwnProperty('className') ) ele.className = d.className; else ele.className = dfltClassName; if(d.hasOwnProperty('addClassName') ) { ele.className += " " + d.addClassName } if(d.hasOwnProperty('appId')) ele.appId = d.appId; else ele.appId = null; if( d.hasOwnProperty('clickable') ) ui_set_clickable( ele, d.clickable ); if( d.hasOwnProperty('enable') ) ui_set_enable( ele, d.enable ) //console.log("Created: " + ele_type + " parent:" + d.parentUuId + " id:" + ele.id + " appId:" + ele.appId) parent_ele.appendChild(ele); if( d.hasOwnProperty('order') ) ui_set_order_key(ele,d.order) } return ele } function ui_create_ctl( parent_ele, ele_type, label, d, dfltEleClassName ) { // create an enclosing div var div_ele = dom_create_ele("div"); div_ele.className = "uiCtlDiv " + dfltEleClassName + "Div" parent_ele.appendChild( div_ele ); var label_ele = div_ele // if label is not null then create an enclosing 'label' element if( label != null ) { label = label.trim(); if( label.length > 0) { label_ele = dom_create_ele("label"); label_ele.innerHTML = label; div_ele.appendChild(label_ele) } } return ui_create_ele( div_ele, ele_type, d, dfltEleClassName ); } function ui_create_div( parent_ele, d ) { var div_ele = ui_create_ele( parent_ele, "div", d, "uiDiv" ); if( div_ele != null ) { if( d.title != null ) { var title = d.title.trim() if( title.length > 0 ) { var p_ele = dom_create_ele("p") p_ele.innerHTML = title div_ele.appendChild( p_ele ) } } } return div_ele; } function ui_create_panel_div( parent_ele, d ) { d.type = "div" if( !d.hasOwnProperty('className') ) d.className = "uiPanel" var div_ele = ui_create_div( parent_ele, d ); return div_ele } function ui_create_row_div( parent_ele, d ) { d.type = "div" if( !d.hasOwnProperty('className') ) d.className = "uiRow" var div_ele = ui_create_div( parent_ele, d ); return div_ele } function ui_create_col_div( parent_ele, d ) { d.type = "div" if( !d.hasOwnProperty('className') ) d.className = "uiCol" var div_ele = ui_create_div( parent_ele, d ); return div_ele } function ui_create_label( parent_ele, d ) { var ele = ui_create_ele( parent_ele, "label", d, "uiLabel" ); if( ele != null ) { ele.innerHTML = d.title; } return ele; } function ui_create_button( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "button", null, d, "uiButton" ); if( ele != null ) { ele.innerHTML = d.title; ele.onclick = function() { ui_send_int_value(this,1); } } return ele; } function ui_create_check( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiCheck" ) if( ele != null ) { ele.type = "checkbox"; ele.onclick = function() { ui_send_bool_value(this,dom_get_checkbox(this.id)); } if( !d.hasOwnProperty('value') ) { ui_send_echo(ele) } else { dom_set_checkbox(ele.id, d.value ); ui_send_bool_value(ele,dom_get_checkbox(ele.id)) } } return ele; } // // Note: The value of a 'select' widget is always set by the 'appId' // of the selected 'option'. Likewise the 'appId' of the selected // option is returned as the value of the select widget. // function ui_on_select( ele ) { ui_send_int_value(ele,ele.options[ ele.selectedIndex ].appId); } function ui_select_set_from_option_app_id( sel_ele, option_appId ) { var i; for(i=0; i<sel_ele.options.length; ++i) if( sel_ele.options[i].appId == option_appId ) { sel_ele.selectedIndex = i; return; } ui_error("Select option index not found."); } function ui_create_select( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "select", d.title, d, "uiSelect" ); ele.onchange = function() { ui_on_select(this) } if( !d.hasOwnProperty('value') ) { ui_send_echo(ele) } else { // Note that d.value is the appId of the default selected option ele.defaultOptionAppId = d.value; ui_send_int_value(ele,ele.defaultOptionAppId) } return ele; } function ui_create_option( parent_ele, d ) { var opt_ele = ui_create_ele( parent_ele, "option", d, "uiOption" ); if( opt_ele != null ) { if(d.hasOwnProperty('className')) opt_ele.className = d.className; opt_ele.innerHTML = d.title; // d.value, if it exists, is a boolean indicating that this is the default option var fl0 = d.hasOwnProperty('value') && d.value != 0; // The parent 'select' element may also have been given the app id of the default option // (and this option may be it) var fl1 = parent_ele.hasOwnProperty('defaultOptionAppId') && parent_ele.defaultOptionAppId == ele.appId; if(fl0 || fl1 ) { parent_ele.selectedIndex = parent_ele.options.length-1; } } return opt_ele; } function _ui_on_focus( ele ) { _focusId = ele.id; _focusVal = ele.value; } function ui_set_str_display( ele_id, value ) { var ele = dom_id_to_ele(ele_id); if( typeof(value)=="string") { ele.innerHTML = value; } } function ui_create_str_display( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "label", d.title, d, "uiStringDisp" ); if( ele != null ) { if( d.hasOwnProperty('value') ) { ui_set_str_display(ele.id, d.value); } else { ui_send_echo(ele); } } return ele; } function _ui_on_string_blur( ele ) { if( ele.id == _focusId ) { if( ele.value != _focusVal ) ui_send_string_value(ele,ele.value) } } function ui_create_string( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiString" ); if( ele != null ) { ele.addEventListener('keyup', function(e) { if(e.keyCode===13){ ui_send_string_value(this, this.value); }} ); ele.addEventListener('focus', function(e) { _ui_on_focus(this); } ); ele.addEventListener('blur', function(e) { _ui_on_string_blur(this); } ); if( !d.hasOwnProperty('value') ) ui_send_echo(ele); else { ele.value = d.value; ui_send_string_value(ele,ele.value) } } return ele; } function _ui_send_number( ele ) { var val = 0; if( ele.decpl == 0 ) val = Number.parseInt(ele.value) else val = Number.parseFloat(ele.value) if( !(ele.minValue<=val && val<=ele.maxValue)) { ele.style.borderColor = "red" ui_send_corrupt_state(ele) } else { ele.style.borderColor = "" if( ele.decpl == 0 ) ui_send_int_value(ele,ele.value); else ui_send_float_value(ele,ele.value); } } function ui_number_keyup( e ) { if( e.keyCode===13 ) { var ele = dom_id_to_ele(e.target.id) if( ele != null ) { //console.log("min:"+ele.minValue+" max:"+ele.maxValue) _ui_send_number(ele) } } } function _ui_on_number_blur( ele ) { if( ele.id == _focusId ) { if( ele.value != _focusVal ) _ui_send_number(ele) } } function _ui_set_number_range( ele, d ) { if(d.max < d.min) { ui_error("Numeric range max: " + d.maxValue + " is not greater than " + d.minValue + ".") } else { ele.maxValue = d.max; ele.minValue = d.min; ele.stepValue = d.step; ele.decpl = d.decpl; } } function ui_set_number_value( ele, value ) { var min_ok_fl = (!ele.hasOwnProperty('minValue')) || (value >= ele.minValue) var max_ok_fl = (!ele.hasOwnProperty('maxValue')) || (value <= ele.maxValue) if( min_ok_fl && max_ok_fl ) { ele.value = value; if( ele.decpl == 0 ) ui_send_int_value( ele, ele.value ) else ui_send_float_value( ele, ele.value ) } else { ui_error("Number value " + value + " out of range. min:" + ele.minValue + " max:" +ele.maxValue ) } } function ui_set_number_range( ele, d ) { _ui_set_number_range(ele,d) if( d.hasOwnProperty('value') ) ui_set_number_value(ele,d.value) } function ui_create_number( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "input", d.title, d, "uiNumber" ); if( ele != null ) { ele.addEventListener('keyup', ui_number_keyup ); ele.addEventListener('focus', function(e) { _ui_on_focus(this); } ); ele.addEventListener('blur', function(e) { _ui_on_number_blur(this); } ); _ui_set_number_range(ele,d) if( d.hasOwnProperty('value') ) { ui_set_number_value(ele,d.value) } else { ui_send_echo(ele); } } return ele; } function ui_set_number_display( ele_id, value ) { //console.log("Numb disp: " + ele_id + " " + value) var ele = dom_id_to_ele(ele_id); if( typeof(value)=="number") { var val = value.toString(); if( ele.decpl == 0 ) ele.innerHTML = parseInt(val,10); else ele.innerHTML = parseFloat(val); } } function ui_create_number_display( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "label", d.title, d, "uiNumbDisp" ); if( ele != null ) { ele.decpl = d.decpl; if( d.hasOwnProperty('value') ) { ui_set_number_display(ele.id, d.value); } else { ui_send_echo(ele); } } return ele; } function ui_create_text_display( parent_ele, d ) { return ui_create_ctl( parent_ele, "label", d.title, d, "uiTextDisp" ); } function ui_set_progress( ele, value ) { ele.value = Math.round( ele.max * (value - ele.minValue) / (ele.maxValue - ele.minValue)); } function _ui_set_prog_range( ele, d ) { ele.maxValue = d.max; ele.minValue = d.min; } function ui_create_progress( parent_ele, d ) { var ele = ui_create_ctl( parent_ele, "progress", d.title, d, "uiProgress" ); if( ele != null ) { ele.max = 100; _ui_set_prog_range(ele,d) if( !d.hasOwnProperty('value') ) ui_send_echo(ele); else { ui_set_progress( ele, d.value ); ui_send_int_value( ele, ele.value ); } } return ele } function ui_set_prog_range( ele, d ) { _ui_set_prog_range(ele,d) if( d.hasOwnProperty('value')) ui_set_progress(ele,d.value) } function _on_log_click( evt ) { var pre_ele = dom_id_to_ele(evt.target.id) pre_ele.auto_scroll_flag = !pre_ele.auto_scroll_flag; } function ui_set_log_text( ele, value ) { var child_id = ele.id + "_pre" for(var i=0; i<ele.children.length; ++i) { var pre_ele = ele.children[i] if( pre_ele.id == child_id ) { pre_ele.innerHTML += value if(pre_ele.auto_scroll_flag) ele.scrollTop = pre_ele.clientHeight break; } } } function ui_create_log( parent_ele, d ) { // create a containing div with the label if( !d.hasOwnProperty('className') ) d.className = "uiLog" var log_ele = ui_create_ctl( parent_ele, "div", d.title, d, "uiLog" ) // add a <pre> to the containing div var ele = dom_create_ele("pre") ele.id = log_ele.id + "_pre" ele.onclick = _on_log_click; ele.auto_scroll_flag = true; log_ele.appendChild(ele) return log_ele } function ui_create_list( parent_ele, d ) { //console.log(d) var list_ele = ui_create_ctl( parent_ele, "div", d.title, d, "uiList" ) return list_ele } function ui_set_value( d ) { var eleId = d.uuId.toString() var ele = dom_id_to_ele(eleId) if( ele == null ) console.log("ele '"+eleId+"' not found"); else { if( !ele.hasOwnProperty("uiEleType") ) { console.log("No type"); } } if( ele != null && ele.hasOwnProperty("uiEleType")) { //console.log("found: "+ele.uiEleType) switch( ele.uiEleType ) { case "div": break; case "label": ele.innerHTML = d.value break; case "button": break; case "check": dom_set_checkbox(ele.id,d.value) break; case "select": ui_select_set_from_option_app_id(ele,d.value) break; case "option": break; case "str_disp": ui_set_str_display(ele.id,d.value); break case "string": ele.value = d.value break; case "number": ele.value = d.value break; case "numb_disp": ui_set_number_display(ele.id,d.value); break; case "progress": ui_set_progress( ele, d.value ) //ele.value = d.value break; case "log": ui_set_log_text( ele, d.value ) break default: ui_error("Unknown UI element type on set value: " + d.type ) } } } function _ui_modify_class( ele, classLabelArg, enableFl ) { let classLabel = " " + classLabelArg; // prefix the class label with a space //console.log(ele.id + " " + classLabelArg + " " + enableFl ) let isEnabledFl = false; if( ele.hasOwnProperty("className") ) isEnabledFl = ele.className.includes(classLabel) else ele.className = "" // if the class is not already enabled/disabled if( enableFl != isEnabledFl ) { if( enableFl ) ele.className += classLabel; else ele.className = ele.className.replace(classLabel, ""); } //console.log(ele.id + " " + ele.className + " " + enableFl ) } function ui_set_select( ele, enableFl ) { _ui_modify_class(ele,"uiSelected",enableFl) ui_send_select( ele, enableFl ) } function ui_set_clickable( ele, enableFl ) { ele.clickableFl = enableFl if(enableFl) ele.onclick = function( evt ){ ui_on_click( this, evt ); } else ele.onclick = null } function ui_set_visible( ele, enableFl ) { if(enableFl) { if(ele.hasOwnProperty("style_display") ) { ele.style.display = ele.style_display; } else { ele.style.display = "block"; } } else { ele.style_display = ele.style.display; ele.style.display = "none"; } } function ui_set_enable( ele, enableFl ) { ele.disabled = !enableFl } function ui_set_order_key(ele, orderKey) { let parent = ele.parentElement // get the parent of the element to reorder ele = parent.removeChild( ele ) // remove the element to reorder from the parent list ele.order = orderKey let i = 0; for(i=0; i<parent.children.length; ++i) { if( parent.children[i].hasOwnProperty("order") && parent.children[i].order >= orderKey) { parent.insertBefore( ele, parent.children[i] ) break } } // no element was found greater than this element .... if( i == parent.children.length ) parent.appendChild(ele) // ... insert the element at the end of the child lsit } function ui_scroll_top(ele) { /* var rect = ele.getBoundingClientRect(); var prect = ele.parentElement.getBoundingClientRect(); console.log(ele.id, rect.top, rect.right, rect.bottom, rect.left, ele.offsetTop); console.log(prect.top, prect.right, prect.bottom, prect.left ); var scr_ele = document.getElementById(42408) var sr = scr_ele.getBoundingClientRect() console.log(sr.top, sr.right, sr.bottom, sr.left, scr_ele.offsetTop); ele.parentElement.scrollTop = sr.top - prect.top console.log(sr.top, sr.right, sr.bottom, sr.left, scr_ele.offsetTop); */ var er = ele.getBoundingClientRect() var pr = ele.parentElement.getBoundingClientRect() ele.parentElement.scrollTop = er.top - pr.top } function ui_set( d ) { //console.log(d) var ele = dom_id_to_ele(d.uuId.toString()) if( ele == null ) console.log("ele not found"); if( ele != null) { switch( d.type ) { case "number_range": ui_set_number_range(ele, d) break; case "progress_range": ui_set_prog_range(ele, d) break; case "select": ui_set_select(ele,d.value) break case "clickable": ui_set_clickable(ele,d.value) break case "visible": ui_set_visible(ele,d.value) break case "enable": ui_set_enable(ele,d.value) break case "order": ui_set_order_key(ele,d.value) break case "scroll_top": ui_scroll_top(ele) } } } function ui_cache( d ) { for(i=0; i<d.array.length; ++i) { _ws_on_msg( d.array[i] ) } } function ui_create( d ) { if( typeof(d.parentUuId) == "number") d.parentUuId = d.parentUuId.toString() if( typeof(d.uuId) == "number" ) d.uuId = d.uuId.toString() var parent_ele = ui_get_parent(d.parentUuId); var ele = null; if( parent_ele == null ) console.log("Parent ele not found.",d) else { switch( d.type ) { case "div": ele = ui_create_div( parent_ele, d ) break; case "panel": ele = ui_create_panel_div( parent_ele, d ) break; case "row": ele = ui_create_row_div( parent_ele, d ) break; case "col": ele = ui_create_col_div( parent_ele, d ) break; case "label": ele = ui_create_label( parent_ele, d ) break; case "button": ele = ui_create_button( parent_ele, d ) break; case "check": ele = ui_create_check( parent_ele, d ) break; case "select": ele = ui_create_select( parent_ele, d ); break; case "option": ele = ui_create_option( parent_ele, d ); break; case "str_disp": ele = ui_create_str_display( parent_ele, d ); break; case "string": ele = ui_create_string( parent_ele, d ); break; case "number": ele = ui_create_number( parent_ele, d ); break; case "numb_disp": ele = ui_create_number_display( parent_ele, d ); break; case "text_disp": ele = ui_create_text_display( parent_ele, d ); break; case "progress": ele = ui_create_progress( parent_ele, d ); break; case "log": ele = ui_create_log( parent_ele, d ); break; case "list": ele = ui_create_list( parent_ele, d ); break; default: ui_error("Unknown UI element type: " + d.type ) } if( ele != null ) { ele.uiEleType = d.type; } } } function ui_destroy( d ) { if( typeof(d.uuId) == "number" ) d.uuId = d.uuId.toString() var ele = dom_id_to_ele(d.uuId) if( ele != null ) ele.parentElement.removeChild( ele ) } function ui_attach( d ) { console.log("ATTACH"); //_rootDivEle.appendChild(_rootEle) } function ws_send( s ) { //console.log(s) _ws.send(s+"\0") } function _ws_on_msg( d ) { switch( d.op ) { case 'cache': ui_cache( d ) break; case 'create': ui_create( d ) break; case 'destroy': ui_destroy( d ) break case 'value': ui_set_value( d ) break; case 'set': ui_set( d ) break; case 'attach': ui_attach(d) break; default: ui_error("Unknown UI operation. " + d.op ) } } function ws_on_msg( jsonMsg ) { //console.log(jsonMsg) d = JSON.parse(jsonMsg.data); _ws_on_msg(d) } function ws_on_open() { set_app_title( "Connected", "title_connected" ); ws_send("init") } function ws_on_close() { set_app_title( "Disconnected", "title_disconnected" ); // remove the body of UI var rootEle = dom_id_to_ele(_rootId) if( rootEle != null ) document.body.removeChild( rootEle ) } function ws_form_url(urlSuffix) { var pcol; var u = document.URL; pcol = "ws://"; if (u.substring(0, 4) === "http") u = u.substr(7); u = u.split("/"); return pcol + u[0] + "/" + urlSuffix; } function main_0() { d = { "className":"uiAppDiv", "uuId":_rootId } rootEle = ui_create_div( document.body, d ) rootEle.uuId = 0; rootEle.id = _nextEleId; _nextEleId += 1; //console.log(ws_form_url("")) _ws = new WebSocket(ws_form_url(""),"ui_protocol") _ws.onmessage = ws_on_msg _ws.onopen = ws_on_open _ws.onclose = ws_on_close; } function main() { d = { "className":"uiAppDiv", "uuId":"rootDivEleId" } _rootDivEle = ui_create_div( document.body, d ) //_rootEle = document.createDocumentFragment(); _rootEle = _rootDivEle _rootEle.uuId = 0; _rootEle.id = _nextEleId; _nextEleId += 1; //console.log(ws_form_url("")) _ws = new WebSocket(ws_form_url(""),"ui_protocol") _ws.onmessage = ws_on_msg _ws.onopen = ws_on_open _ws.onclose = ws_on_close; console.log("main() done.") }