/** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2022 bLAB ** $CREATED: 1-1-19 by sbosse. ** $VERSION: 1.8.1 ** $RCS: $Id:$ ** $INFO: ** ** Main Application JAM LAB WEBUI ** ** $ENDOFINFO */ var version = "1.8.3" console.log(version) var UI= $$; var logtext= ''; var treemap=[]; var sourcetext=''; var printloc=false; var shell=[]; var shellWin=[]; var infoShellWin=[]; var configShellWin=[]; var shellNum=1; var monitorWin=[]; var monitorNum=1; var monitor=[] var reporterWin=[]; var reporterNum=1; var editor=[]; var editorWin=[]; var editorNum=1; var configEditorWin=[]; var netNodes=[] var state=0; var cookieConfig = 'jam.webui.config'; var help={md:{}}; var Marked; if (typeof jamConfig == 'undefined') info ('No custom configuration file config.js loaded.') else info ('Custom configuration file config.js loaded.'); /** Main Options **/ // Optional App configuration loaded from file (config.js) if (typeof jamConfig == 'undefined') jamConfig = { name : '?', link1:{ip:"ag-0.de", ipport:10001, proto:'http', secure:'', enable:true, shells:[1]}, link2:{ip:"localhost",ipport:10001, proto:'http', secure:'', enable:false, shells:[1]}, link3:{ip:"",ipport:0, proto:'http', secure:'', enable:false, shells:[1]}, link4:{ip:"",ipport:0, proto:'http', secure:'', enable:false, shells:[1]}, log:{agent:true,parent:false,time:false,Time:true,class:false}, // Message flags: agent parent time class fontsize: 14, group:'jamweb', user:'user', workdir : '/', webclipTime:300, webclipUrl:'edu-9.de:9176', } jamConfig.version=version; // load config from cookie function loadConfig() { var data = typeof localStorage=='undefined'?Utils.getCookie(cookieConfig):localStorage.getItem(cookieConfig); if (!data) data=Utils.getCookie(cookieConfig); if (data) { console.log('Loaded jamConfig from cookie '+cookieConfig); var obj = JSON.parse(data); delete obj.version; Object.assign(jamConfig,obj); /* jamConfig.link1=obj.link1; jamConfig.link2=obj.link2; jamConfig.link3=obj.link3; jamConfig.link4=obj.link4; jamConfig.log=obj.log; jamConfig.version=version; */ } } function saveConfig() { var obj = {} /* link1:jamConfig.link1, link2:jamConfig.link2, link3:jamConfig.link3, link4:jamConfig.link4, log:jamConfig.log, } */ Object.assign(obj,jamConfig); console.log('Saving jamConfig in cookie '+cookieConfig); var data = JSON.stringify(obj); setCookie(cookieConfig,obj,2) if (typeof localStorage!='undefined') localStorage.setItem(cookieConfig, data); } function updateConfig(shell) { shell.config({log:jamConfig.log}); } // get config updates from URL params function urlConfig() { var params = parseUrl(document.URL); console.log(document.URL,params); if (params && (params.link||params.link1)) { var tokens=(params.link||params.link1).split(':'); if (tokens.length==2) { jamConfig.link1.ip=tokens[0]; jamConfig.link1.ipport=Number(tokens[1]); jamConfig.link1.enable=true; } } if (params && params.link2) { var tokens=params.link2.split(':'); if (tokens.length==2) { jamConfig.link2.ip=tokens[0]; jamConfig.link2.ipport=Number(tokens[1]); jamConfig.link2.enable=true; } } if (params && (params.secure||params.secure1)) { jamConfig.link1.secure=(params.secure||params.secure1); } if (params && params.secure2) { jamConfig.link2.secure=params.secure2; } if (params.proto) { jamConfig.link1.proto=params.proto; jamConfig.link2.proto=params.proto; jamConfig.link3.proto=params.proto; jamConfig.link4.proto=params.proto; } } loadConfig(); urlConfig(); var classTemplate = "function ac (options) {\n this.x=null;\n this.act = {\n a1: () => {log('Start')},\n a2: () => {log('End');kill()} }\n this.trans = {\n a1:a2\n }\n this.on = {\n signal : function () {}\n }\n this.next = a1\n} " // Info and error message toasts ... function info (text) { webix.message({text:text,type:'Info'}) } function error (text) { webix.message({text:text,type:'error'}) } function clear (widget) { var view = UI(widget); var log = UI(widget+'LogText'); var scroll = view.getBody(); logtext = ''; log.removeView(widget+'LogTextView'); log.addView({template:'
'+logtext+'', autoheight:true, borderless:true, id:widget+'LogTextView'}); scroll.scrollTo(0,1000000) view.show(); } function deltree() { var root,tree = this.UI('navTree'); if (tree._roots) { for (var p in tree._roots) { p=tree._roots[p]; tree.remove(p); } } else tree.remove('root'); root={ id:"root", open:true, value:'Nodes @'+SHELL.Io.Time() } tree.add(root); } function maketree() { var i=0,tree = this.UI('navTree'); for(var p in netNodes) { tree.add({id:p,value:netNodes[p]+' '+p},i,'root'); i++; } } // Update node information view in shell window async function updateInfo(shellcmd, shellnum) { var node = await shellcmd.info('node').id, stats = await shellcmd.stats('node'); function makeTree(element,root) { var i=0; var rows=[]; if (element == undefined) return { id:root+'-'+i, value:'null' } else if (typeof element == 'number' || typeof element == 'string' || typeof element == 'boolean') return { id:root+'-'+i, value:String(element) } else if (typeof element == 'function') return { id:root+'-'+i, value:'function' } else if (typeof element == 'object') { for(var p in element) { if (p=='self') continue; var tree=makeTree(element[p],root+'-'+i); if (!tree) continue; if (tree && tree.length) rows.push({ id:root+'-'+i, open:false, value:p, data : tree}); else { tree.value=p+'='+tree.value; rows.push(tree); } i++; } return rows } } var rows = [ { view:'label', label:'Node '+node+'', height:18} ] var agents = await shellcmd.stats('agent'); var i = 1, height=50; var treeAgents = []; function click(o) { console.log(o) } for(var p in agents) { var info='agent.'+p+' pid='+agents[p].pid+' class='+agents[p].class+' state='+agents[p].state; var agent = await shellcmd.info('agent-data',p); treeAgents.push({ id:info, open:false, value:p, data : [ { id:info+'-'+0, value:'class='+agents[p].class }, { id:info+'-'+1, value:'pid='+agents[p].pid }, { id:info+'-'+2, value:'gid='+agents[p].gid }, { id:info+'-'+3, value:'parent='+agents[p].parent }, { id:info+'-'+4, value:'state='+agents[p].state }, { id:info+'-'+5, value:'next='+agents[p].next }, { id:info+'-'+6, value:'time='+agents[p].resources.consumed }, { id:info+'-'+7, open:false, value:'agent', data : makeTree(agent,info+'-'+7)}, ]}); i++; } height=50+i*25; var tree1 = { view:"tree", id:'treeAgentsNode'+shellnum, select:true, tooltip:true, height:'auto', data: [ {id:"root", value:"Agents", open:true, data:treeAgents} ], on:{ // the default click behavior that is true for any datatable cell "onItemClick":function(id, e, trg){ if (id.indexOf('agent.')==0) { } } }, } rows.push(tree1) for (var p in stats) rows.push({ view:"text", label:p, readonly:true, type:'text', value:stats[p].toString()}) return rows; } // Update window pull-down menus function updateMenu() { var i,items=[]; UI('menuShells').clearAll(); for(i in shellWin) items.push((i)+' : '+shellWin[i]._options.name); UI('menuShells').add({value:"Shells", submenu:items}); items=[]; for(i in editorWin) items.push((i)+' : '+editorWin[i]._options.file); UI('menuEditors').clearAll(); UI('menuEditors').add({value:"Editors", submenu:items}); items=[]; for(i in monitorWin) items.push((i)+' : '+monitorWin[i]._options.name); UI('menuMonitors').clearAll(); UI('menuMonitors').add({value:"Monitors", submenu:items}); } // Update conenction tree async function updateConn(cmd,name) { var links = await cmd.connected(DIR.IP('%')), ips = await cmd.connected(DIR.IP('*')); // netNodes={}; for(var i in netNodes) { if (i.indexOf(name)==0) delete netNodes[i]; } for(var i in links) netNodes[name+':'+links[i]]=ips[i]; deltree(); maketree(); } function removeConn(name) { for(var i in netNodes) { if (i.indexOf(name)==0) delete netNodes[i]; } deltree(); maketree(); } function autoLayout() { var x=0, y=50; for(i in shellWin) { var shell=shellWin[i]; if (!shell) continue; var size=shell.collapse(true); x=$('body').width()-size.width; shell.window.setPosition(x,y); y+=(size.height+1); } for(i in editorWin) { var shell=editorWin[i]; if (!shell) continue; var size=shell.collapse(true); x=$('body').width()-size.width; shell.window.setPosition(x,y); y+=(size.height+1); } for(i in monitorWin) { var shell=monitorWin[i]; if (!shell) continue; var size=shell.collapse(true); x=$('body').width()-size.width; shell.window.setPosition(x,y); y+=(size.height+1); } } /************ NETWORK ***************/ Network = { state:0, start: async function () { if (Network.state<2) { var proto=jamConfig.link1.proto||'http'; for(var i in shell) { if (shell[i] && shell[i].options.shell && !shell[i].port) shell[i].port=await shell[i].options.cmd.port(DIR.IP(),{proto:proto,verbose:shell[i].options.log.Network}); } Network.state=1; } for(var i in shell) { if (!shell[i] || !shell[i].options.shell) continue; if (jamConfig.link1.enable && jamConfig.link1.ipport) shell[i].options.cmd.connect(DIR.IP(jamConfig.link1.proto+'://'+jamConfig.link1.ip+':'+ jamConfig.link1.ipport+ (jamConfig.link1.secure!=''?'?secure='+jamConfig.link1.secure:''))); if (jamConfig.link2.enable && jamConfig.link2.ipport) shell[i].options.cmd.connect(DIR.IP(jamConfig.link2.proto+'://'+jamConfig.link2.ip+':'+ jamConfig.link2.ipport+ (jamConfig.link2.secure!=''?'?secure='+jamConfig.link2.secure:''))); if (jamConfig.link3.enable && jamConfig.link3.ipport) shell[i].options.cmd.connect(DIR.IP(jamConfig.link3.proto+'://'+jamConfig.link3.ip+':'+ jamConfig.link3.ipport+ (jamConfig.link3.secure!=''?'?secure='+jamConfig.link3.secure:''))); if (jamConfig.link4.enable && jamConfig.link4.ipport) shell[i].options.cmd.connect(DIR.IP(jamConfig.link4.proto+'://'+jamConfig.link4.ip+':'+ jamConfig.link4.ipport+ (jamConfig.link4.secure!=''?'?secure='+jamConfig.link4.secure:''))); } Network.state=2; }, stop: function () { for(var i in shell) { if (!shell[i] || !shell[i].options.shell) continue; // disconnect w/o proto!! if (jamConfig.link1.enable) shell[i].options.cmd.disconnect(DIR.IP(jamConfig.link1.ip+':'+ jamConfig.link1.ipport)); if (jamConfig.link2.enable) shell[i].options.cmd.disconnect(DIR.IP(jamConfig.link2.ip+':'+ jamConfig.link2.ipport)); if (jamConfig.link3.enable) shell[i].options.cmd.disconnect(DIR.IP(jamConfig.link3.ip+':'+ jamConfig.link3.ipport)); if (jamConfig.link4.enable) shell[i].options.cmd.disconnect(DIR.IP(jamConfig.link4.ip+':'+ jamConfig.link4.ipport)); } Network.state=1; }, update : function (options,num) { if (shell[num].port) shell[num].port.amp.config(options); } } jam = { state:0, start: function () { for(var i in shell) { if (shell[i] && shell[i].options.shell) shell[i].options.shell.env.start(); } jam.state=1; }, stop: function () { for(var i in shell) { if (shell[i] && shell[i].options.shell) shell[i].options.shell.env.stop(); } jam.state=0; } } /************ TOP TOOLBAR ********/ toolbar = this.webix.ui({ view:"toolbar", id:"myToolbar", left:0, top:0, width:'100%', cols:[ { view:"button", type:"icon", icon:"play", id:'button.jam.start', tooltip:'Start', width:30, click:function () { if (jam.state==0) { UI('button.jam.start').disable(); UI('button.jam.stop').enable(); jam.start(); } }}, { view:"button", type:"icon", icon:"stop", id:'button.jam.stop', tooltip:'Stop', width:30, click:function () { UI('button.jam.start').enable(); UI('button.jam.stop').disable(); jam.stop(); }}, { view:"button", type:"icon", icon:"chain", id:'button.net.start', tooltip:'Connect', width:30, click:function () { if (Network.state<2) { UI('button.net.start').disable(); UI('button.net.stop').enable(); Network.start(); } }}, { view:"button", type:"icon", icon:"chain-broken", id:'button.net.stop', tooltip:'Disconnect', width:30, click:function () { UI('button.net.start').enable(); UI('button.net.stop').disable(); Network.stop(); }}, { view:"button", type:"icon", icon:"navicon", tooltip:'Configuration', width:30, click:function () { mainConfigWin.show(); }}, { view:"button", type:"icon", icon:"plus", tooltip:'New Shell Worker', width:30, click:function () { createShell(shellNum,{worker:true}); shellNum++; }}, { view:"button", type:"icon", icon:"sort-amount-asc", tooltip:'Bigger Fonts', width:30, click:function () { jamConfig.fontsize++; changeCSS('.input','font-size',jamConfig.fontsize+'px') changeCSS('.normalInput','font-size',jamConfig.fontsize+'px') changeCSS('.normalOutput','font-size',jamConfig.fontsize+'px') changeCSS('.error','font-size',jamConfig.fontsize+'px') changeCSS('.webix_view','font-size',jamConfig.fontsize+'px') }}, { view:"button", type:"icon", icon:"sort-amount-desc", tooltip:'Smaller Fonts', width:30, click:function () { jamConfig.fontsize--; changeCSS('.input','font-size',jamConfig.fontsize+'px') changeCSS('.normalInput','font-size',jamConfig.fontsize+'px') changeCSS('.normalOutput','font-size',jamConfig.fontsize+'px') changeCSS('.error','font-size',jamConfig.fontsize+'px') changeCSS('.webix_view','font-size',jamConfig.fontsize+'px') }}, { view:"button", type:"icon", icon:"user-plus", tooltip:'Chat Dialog', width:30, click:function () { if (chatWin.isVisible()) chatWin.hide(); else { chatWin.show(); chatInit(); } }}, { view:"button", type:"icon", icon:"hand-o-right", tooltip:'Visual Pointer', width:30, click:function () { var el = document.getElementsByTagName("body")[0]; self._cursorToggle=!self._cursorToggle; if (self._cursorToggle) el.style.cursor = "url(redpointer.png), auto"; else el.style.cursor = "auto"; ['webix_view'].forEach(function (id) { var ell = document.getElementsByClassName(id); if (!ell) return; ell.forEach(function (el) { if (self._cursorToggle) el.style.cursor = "url(redpointer.png), auto"; else el.style.cursor = "auto"; }); }) }}, { view:"button", type:"icon", icon:"question", tooltip:'Help', width:30, click:function () { helpWin.show(); }}, { view:"button", type:"icon", icon:"th", tooltip:'Auto Layout', width:30, click:function () { autoLayout() }}, { view:"button", type:"icon", icon:"terminal", tooltip:'New Developer Console', width:30, click:function () { var shell = Shell.default({ label:'Developer Console (JS)', hide:false, editor:true, }); shell.run = function (code) { var ___error; with ({ error:shell.env.error, keys:Object.keys.bind(Object), load:Utils.loadScript, print:shell.env.print, time:Date.now, }) { try { var result = eval(code) } catch (e) { ___error=result=e }}; if (!code.match(/;[ ]*$/)) shell.env.print(result); else if (___error) shell.env.error(___error); } }}, { view:"menu", id:"menuShells", autowidth:true, width:100, data:[ //menu data { value:"Shells", submenu:["1"] }, ], type:{ subsign:true, }, on:{ onMenuItemClick:function(id){ var name = this.getMenuItem(id).value.split(':'); var win=shellWin[Number(name[0])].window; if (win) win.isVisible()?win.hide():win.show(); } } }, { view:"menu", id:"menuEditors", autowidth:true, width:100, data:[ //menu data { value:"Editors", submenu:[] }, ], type:{ subsign:true, }, on:{ onMenuItemClick:function(id){ var name = this.getMenuItem(id).value.split(':'); var win=editorWin[Number(name[0])].window; if (win) win.isVisible()?win.hide():win.show(); } } }, { view:"menu", id:"menuMonitors", autowidth:true, width:100, data:[ //menu data { value:"Monitors", submenu:[] }, ], type:{ subsign:true, }, on:{ onMenuItemClick:function(id){ var name = this.getMenuItem(id).value.split(':'); var win=monitorWin[Number(name[0])].window; if (win) win.isVisible()?win.hide():win.show(); } } }, { view:"label", id:'myTopLabel', label:'JAM Laboratory WEB API (C) Dr. Stefan Bosse Ver. '+jamConfig.version, align:'right'}, { view:"button", type:"icon", icon:"close", tooltip:'Exit', width:30, click:function () { }}, ] }); UI('button.jam.stop').disable(); UI('button.net.stop').disable(); toolbar.show(); /************ MAIN CONFIG ********/ mainConfigWin=webix.ui({ id:'mainConfigWin', view:"window", width:300, height:450, left:90, top:90, move:true, resize: true, toFront:true, head: { view:"toolbar", cols:[ { view:"label", label:"Main Configuration", align:'right'}, { view:"button", type:"icon", icon:"check-square", align:'center', width:30, click: function () { jamConfig.link1.ip=UI('mainConfigWinLink1IPAddress').getValue(); jamConfig.link1.ipport=UI('mainConfigWinLink1IPPort').getValue(); jamConfig.link1.proto=UI('mainConfigWinLink1IPProto').getValue(); jamConfig.link1.secure=UI('mainConfigWinLink1SecPort').getValue(); jamConfig.link2.ip=UI('mainConfigWinLink2IPAddress').getValue(); jamConfig.link2.ipport=UI('mainConfigWinLink2IPPort').getValue(); jamConfig.link2.proto=UI('mainConfigWinLink2IPProto').getValue(); jamConfig.link2.secure=UI('mainConfigWinLink2SecPort').getValue(); jamConfig.link3.ip=UI('mainConfigWinLink3IPAddress').getValue(); jamConfig.link3.ipport=UI('mainConfigWinLink3IPPort').getValue(); jamConfig.link3.proto=UI('mainConfigWinLink3IPProto').getValue(); jamConfig.link3.secure=UI('mainConfigWinLink3SecPort').getValue(); jamConfig.link4.ip=UI('mainConfigWinLink4IPAddress').getValue(); jamConfig.link4.ipport=UI('mainConfigWinLink4IPPort').getValue(); jamConfig.link4.proto=UI('mainConfigWinLink4IPProto').getValue(); jamConfig.link4.secure=UI('mainConfigWinLink4SecPort').getValue(); jamConfig.webclipUrl=UI('mainConfigWinClipUrl').getValue(); jamConfig.user=UI('mainConfigWinUser').getValue(); jamConfig.workdir=UI('mainConfigWinWorkdir').getValue(); saveConfig(); mainConfigWin.hide(); }} ] }, body: { rows:[ { view:"form", scroll:true, width:300, height:400, elements: [ { view:"label", label:"JAM Network Link 1", height:'18px', align:'left'}, { view:"checkbox", customCheckbox:false, labelRight:"Enable", height:'18px', value:jamConfig.link1.enable?1:0, click: function (o) {jamConfig.link1.enable=Number(self.UI(o).getValue())?true:false}}, { view:"text", id:"mainConfigWinLink1IPAddress", label:"IP Address", value:jamConfig.link1.ip}, { view:"text", id:"mainConfigWinLink1IPPort", label:"IP Port", value:jamConfig.link1.ipport}, { view:"text", id:"mainConfigWinLink1IPProto", label:"IP Proto", value:jamConfig.link1.proto}, { view:"text", id:"mainConfigWinLink1SecPort", label:"Secure Port (Opt.)", value:jamConfig.link1.secure}, { view:"label", label:"JAM Network Link 2", height:'18px', align:'left'}, { view:"checkbox", customCheckbox:false, labelRight:"Enable", height:'18px', value:jamConfig.link2.enable?1:0, click: function (o) {jamConfig.link2.enable=Number(self.UI(o).getValue())?true:false}}, { view:"text", id:"mainConfigWinLink2IPAddress", label:"IP Address", value:jamConfig.link2.ip}, { view:"text", id:"mainConfigWinLink2IPPort", label:"IP Port", value:jamConfig.link2.ipport}, { view:"text", id:"mainConfigWinLink2IPProto", label:"IP Proto", value:jamConfig.link2.proto}, { view:"text", id:"mainConfigWinLink2SecPort", label:"Secure Port (Opt.)", value:jamConfig.link2.secure}, { view:"label", label:"JAM Network Link 3", height:'18px', align:'left'}, { view:"checkbox", customCheckbox:false, labelRight:"Enable", height:'18px', value:jamConfig.link3.enable?1:0, click: function (o) {jamConfig.link3.enable=Number(self.UI(o).getValue())?true:false}}, { view:"text", id:"mainConfigWinLink3IPAddress", label:"IP Address", value:jamConfig.link3.ip}, { view:"text", id:"mainConfigWinLink3IPPort", label:"IP Port", value:jamConfig.link3.ipport}, { view:"text", id:"mainConfigWinLink3IPProto", label:"IP Proto", value:jamConfig.link3.proto}, { view:"text", id:"mainConfigWinLink3SecPort", label:"Secure Port (Opt.)", value:jamConfig.link3.secure}, { view:"label", label:"JAM Network Link 4", height:'18px', align:'left'}, { view:"checkbox", customCheckbox:false, labelRight:"Enable", height:'18px', value:jamConfig.link4.enable?1:0, click: function (o) {jamConfig.link4.enable=Number(self.UI(o).getValue())?true:false}}, { view:"text", id:"mainConfigWinLink4IPAddress", label:"IP Address", value:jamConfig.link4.ip}, { view:"text", id:"mainConfigWinLink4IPPort", label:"IP Port", value:jamConfig.link4.ipport}, { view:"text", id:"mainConfigWinLink4IPProto", label:"IP Proto", value:jamConfig.link4.proto}, { view:"text", id:"mainConfigWinLink4SecPort", label:"Secure Port (Opt.)", value:jamConfig.link4.secure}, { view:"label", label:"WebClipboard", height:'18px', align:'left'}, { view:"text", id:"mainConfigWinClipUrl", label:"URL", value:jamConfig.webclipUrl}, { view:"text", id:"mainConfigWinUser", label:"User", value:jamConfig.user}, { view:"text", id:"mainConfigWinWorkdir", label:"WorkDir", value:jamConfig.workdir}, ]} ] } }); helpWin=webix.ui({ id:'helpWin', view:"window", width:500, height:450, left:90, top:90, move:true, resize: true, toFront:true, css:'gray_toolbar', head: { view:"toolbar", cols:[ { view:"button", value:'JAMSH', align:'center', click: function () { if (helpWin._view) UI('helpWinScrollView').removeView(helpWin._view); var helpRendered=help.md.jamsh?help.md.jamsh:Marked(help.jamsh); help.md.jamsh=helpRendered; helpWin._view=UI('helpWinScrollView').addView( { rows : [ {template:'