diff --git a/js/ui/webui/webapp.js b/js/ui/webui/webapp.js new file mode 100644 index 0000000..d461a81 --- /dev/null +++ b/js/ui/webui/webapp.js @@ -0,0 +1,1908 @@ +/** + ** ============================== + ** 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:'
'+helpRendered+'
', autoheight:true} + ] + } + ); + } + }, + { view:"button", value:'AIOS', align:'center', click: function () { + if (helpWin._view) UI('helpWinScrollView').removeView(helpWin._view); + var helpRendered=help.md.aios?help.md.aios:Marked(help.aios); + help.md.aios=helpRendered; + helpWin._view=UI('helpWinScrollView').addView( + { + rows : [ + {template:'
'+helpRendered+'
', autoheight:true} + ] + } + ); + } + }, + { view:"button", value:'JS', align:'center', click: function () { + if (helpWin._view) UI('helpWinScrollView').removeView(helpWin._view); + var helpRendered=help.md.js?help.md.js:Marked(help.js); + help.md.js=helpRendered; + helpWin._view=UI('helpWinScrollView').addView( + { + rows : [ + {template:'
'+helpRendered+'
', autoheight:true} + ] + } + ); + } + }, + { view:"label", label:"Help", align:'right'}, + { view:"button", type:"icon", icon:"check-square", align:'center', width:30, click: function () { + helpWin.hide(); + }} + ] + }, + body:{ + view : 'scrollview', + scroll : 'y', + body : { + id:'helpWinScrollView', + rows : [ + ] + } + } + }); + +/*********** Network Tree **********/ +navTree=webix.ui({ + id:'navTree', + left:50, top:50, + view:'tree', + type:'lineTree', + select:true, + data: [ + { id:"root", open:true, value:"Not connected!"} + ] +}) +navTree.show(); +navTree.attachEvent("onAfterSelect", function(id){ + webix.message({text:id,type:'Info'}); + if (id!='root') for (var i in configShellWin) { + if (configShellWin[i] && configShellWin[i].isVisible()) { + UI('configRemoteHostIp'+i).setValue(id); + } + } +}); + + + + +/************ SHELL ********/ + +// Only one shell (num=1) supported! + +function _createShell(num,params) { + shell[num] = winShell(jamConfig); // shell[num].options.shell contains the jamsh object + var options = shell[num].options; + var cmd; + + function Label () { + return options.name+" / Shell "+num + } + + shellWin[num]=webix.ui({ + id:'ShellWin'+num, + view:"window", + height:350, + width:600, + left:250, top:250, + move:true, + resize: true, + toFront:true, + css:'red_toolbar', + head: { + view:"toolbar", + cols:[ + { view:"button", type:"icon", icon:"eraser", tooltip:'Clear', width:30, click:function () { + shell[num].commands.clear() + }}, + { view:"button", type:"icon", icon:"navicon", tooltip:'Config', width:30, click:function () { + configShellWin[num].show(); + }}, + { view:"button", type:"icon", icon:"edit", tooltip:'New Agent Editor', width:30, click:function () { + createEditor(editorNum,{shell:num,name:options.name}); + editorNum++; + }}, + { view:"button", type:"icon", icon:"envelope", tooltip:'Agent Monitor', width:30, click:function () { + if (!monitor[num]) { + createMonitor(num,{shell:shell[num].options.shell}) + } else monitorWin[num].show(); + }}, + { view:"button", type:"icon", icon:"info", tooltip:'Node Information', width:30, click:function () { + UI('infoShellWinLabel'+num).setValue("Info / "+Label()); + if (options.infoView) UI('infoShellWinScrollView'+num).removeView(options.infoView); + options.infoView=UI('infoShellWinScrollView'+num).addView( + { + rows : updateInfo(cmd,num) + } + ); + infoShellWin[num].show(); + }}, + { view:"button", type:"icon", icon:"dot-circle-o", tooltip:'Scroll Auto', id:'ShellWinLogFilmBut'+num, width:30, click:function () { + UI('ShellWinLog'+num).scrollAuto=!UI('ShellWinLog'+num).scrollAuto; + if (UI('ShellWinLog'+num).scrollAuto) UI('ShellWinLogFilmBut'+num).define('icon','arrow-circle-down') + else UI('ShellWinLogFilmBut'+num).define('icon','dot-circle-o'); + UI('ShellWinLogFilmBut'+num).refresh(); + }}, + { view:"label", label:"Shell "+num, id:'ShellWinLabel'+num, align:'right'}, + { view:"button", type:"icon", icon:"gear", tooltip:'New Script Editor', width:30, click:function () { + createEditor(editorNum,{shell:num,name:options.name,script:true}); + editorNum++; + }}, + { view:"button", type:"icon", icon:"close", tooltip:'Close Shell', width:30, click:function () { + var self=shellWin[num]; + self.hide(); + }}, + ] + }, + body:{ + id : 'ShellWinLog'+num, + view : 'scrollview', + scroll : 'y', + body : { + id:'ShellWinLogText'+num, + rows : [ + {template:('
'), height:"auto", borderless:true}, + {template:('
'), height:"auto", borderless:true}, + ] + } + } + }); + shellWin[num].show(); + + /************** SHELL CONFIG ****************/ + configShellWin[num]=webix.ui({ + id:'configShellWin'+num, + view:"window", + width:300, + height:450, + left:90, top:90, + move:true, + resize: true, + toFront:true, + css:'gray_toolbar', + head: { + view:"toolbar", + id:"configShellWinToolbarWin"+num, + cols:[ + { view:"label", label:"Configuration", align:'right'}, + { view:"button", type:"icon", icon:"check-square", align:'center', width:30, click: function () { + configShellWin[num].hide(); + updateConfig(cmd); + saveConfig(); + }} + ] + }, + body: { + rows:[ + { view:"form", scroll:true, width:300, height:400, elements: [ + { view:'label', label:'Agent Logging', height:0}, + { view:"checkbox", customCheckbox:false, labelRight:"agent", height:'18px', value:jamConfig.log.agent?1:0, + click: function (o) {jamConfig.log.agent=Number(self.UI(o).getValue())?true:false;}}, + { view:"checkbox", customCheckbox:false, labelRight:"class", height:'18px', value:jamConfig.log.class?1:0, + click: function (o) {jamConfig.log.class=Number(self.UI(o).getValue())?true:false;}}, + { view:"checkbox", customCheckbox:false, labelRight:"parent", height:'18px', value:jamConfig.log.parent?1:0, + click: function (o) {jamConfig.log.parent=Number(self.UI(o).getValue())?true:false;}}, + { view:"checkbox", customCheckbox:false, labelRight:"time", height:'18px', value:jamConfig.log.time?1:0, + click: function (o) {jamConfig.log.time=Number(self.UI(o).getValue())?true:false; }}, + { view:"checkbox", customCheckbox:false, labelRight:"Time", height:'18px', value:jamConfig.log.Time?1:0, + click: function (o) {jamConfig.log.Time=Number(self.UI(o).getValue())?true:false;}}, + + { view:'label', label:'Shell Logging', height:0}, + { view:"checkbox", customCheckbox:false, labelRight:"Time", height:'18px', value:options.log.Time?1:0, + click: function (o) {options.log.Time=Number(self.UI(o).getValue())?true:false;}}, + { view:"checkbox", customCheckbox:false, labelRight:"Network", height:'18px', value:options.log.Network?1:0, + click: function (o) {options.log.Network=Number(self.UI(o).getValue())?true:false; + Network.update({verbose:options.log.Network?1:0},num)}}, + ]} + ] + } + }); + + /************** SHELL INFO ****************/ + infoShellWin[num]=webix.ui({ + id:'infoShellWin'+num, + view:"window", + height:350, + width:500, + left:90, top:90, + move:true, + resize: true, + toFront:true, + css:'gray_toolbar', + head: { + view:"toolbar", + id:"infoShellWinToolbarWin"+num, + cols:[ + { view:"button", type:"icon", icon:"repeat", tooltip:'Refresh', width:30, click:function () { + if (options.infoView) UI('infoShellWinScrollView'+num).removeView(options.infoView); + options.infoView=UI('infoShellWinScrollView'+num).addView( + { + rows : updateInfo(cmd,num) + } + ); + }}, + { view:"button", type:"icon", icon:"times-circle", tooltip:'Kill Agent', width:30, click:function () { + var tree = UI('treeAgentsNode'+options.name); + var selectedId = tree.getSelectedId(); + if (selectedId.indexOf('agent.')==0) { + var agentid = selectedId.substring(selectedId.indexOf('.')+1,selectedId.indexOf(' ')); + cmd.kill(agentid); + } + if (options.infoView) UI('infoShellWinScrollView'+num).removeView(options.infoView); + options.infoView=UI('infoShellWinScrollView'+num).addView( + { + rows : updateInfo(cmd,num) + } + ); + }}, + { view:"label", label:"Info / "+Label(), id:'infoShellWinLabel'+num, align:'right'}, + { view:"button", type:"icon", icon:"check-square", align:'center', width:30, click: function () { + infoShellWin[num].hide(); + }} + ] + }, + body:{ + view : 'form', + scroll : 'y', + height: 300, + id:'infoShellWinScrollView'+num, + rows : [ + ] + } + }); + shell[num].init("ShellWinInput"+num,"ShellWinOutput"+num,UI('ShellWinLog'+num)); + UI('ShellWinLog'+num).scrollAuto=false; + jamConfig.name = options.name; + cmd = options.cmd; + shell[num].run=cmd; + Marked = options.cmd.marked; + cmd.on('link+',function (arg){ + updateConn(cmd,options.name); + }); + cmd.on('link-',function (arg){ + updateConn(cmd,options.name); + }); + + shellWin[num].attachEvent("onViewMove", function(pos, e){ + }); + shellWin[num].attachEvent("onViewMoveEnd", function(pos, e){ + }); + UI('ShellWinLabel'+num).setValue(Label()); + updateMenu(); + var versions = cmd.versions(); + shell[num].print('JAMlib Version '+versions.lib+' JAMShell Version '+versions.shell+' JAMAios Version '+versions.aios); +} + + + + +// New shell widget (includes monitor, config, info, and editor widgets, too) +function createShell(num,params) { + var shellControl; + var config = { + log : Object.assign({},jamConfig.log), + verbose : jamConfig.verbose||0, + }; + Object.assign(config,params); + shellControl = shell[num] = Node(config); // shell[num].options.shell contains the jamsh object + var options = shell[num].options; + var cmd; + function Label () { + return options.name+' / Shell '+num + } + var shellWidget = UI.Shell.default({ + buttons:{ + 'config:bars:Configuration' : function () { + UI.Form.create({ + 'Shell Logging':options.log, + 'JAM Logging':config.log, + },{}, function (_config) { + Object.assign(config.log,_config['JAM Logging']); + Object.assign(options.log,_config['Shell Logging']); + shellWidget.setLabel(Label()) + shellControl.setup(config); + cmd.config({log:config.log}); + }) + }, + 'edit:edit:New Agent Editor' : function () { + var optionsE = { + shell:num, + level:1, + arguments:'{}', + file:'agent.js', + } + var numed = editorNum++; + function Label() { + return optionsE.file+(optionsE.acname? ' ('+optionsE.acname+')':'')+' / '+options.name+' AgE'+numed+':S'+optionsE.shell + } + var editorWidget = UI.Editor.default({ + label:Label(), + mode:'js', + //x:options.x, + //y:options.y, + // hide:false, + buttons : { + 'config:bars:Configure' : function (label,ev) { + /********** EDITOR CONFIG ****************/ + if (!configEditorWin[numed]) { + configEditorWin[numed]=webix.ui({ + id:'configEditorWin'+numed, + view:"window", + width:300, + height:450, + left:ev.clientX-50, top:ev.clientY, + move:true, + resize: true, + toFront:true, + css:'gray_toolbar', + head: { + view:"toolbar", + cols:[ + { view:"label", label:"Runtime Configuration", align:'right'}, + { view:"button", type:"icon", icon:"check-square", align:'center', width:30, click: function () { + optionsE.arguments = UI('configEditorAgentArgs'+numed).getValue(); + optionsE.level = Number(UI('configEditorAgentLevel'+numed).getValue())||0; + optionsE.file=UI('configEditorFileName'+numed).getValue(); + editorWidget.setLabel(Label()); + configEditorWin[numed].hide(); + updateMenu(); + }} + ] + }, + body: { + rows:[ + { view:"form", scroll:true, width:300, height:400, elements: [ + { view:"label", label:"Agent Creation", height:'18px', align:'left'}, + { view:"textarea", id:"configEditorAgentArgs"+numed, label:"Argument", height:150, value:optionsE.arguments}, + { view:"text", id:"configEditorAgentLevel"+numed, label:"Level", value:optionsE.level}, + { view:"label", label:"Code", height:'18px', align:'left'}, + { view:"text", id:"configEditorFileName"+numed, label:"File Name", value:optionsE.file}, + ]} + ] + } + }); + } + configEditorWin[numed].show(); + }, + 'compile:indent:Compile Code' : async function () { + var code = editorWidget.editor.get(); + var acname = await options.shell.env.compile(code); + if (acname) { + optionsE.acname=acname; + editorWidget.setLabel(Label()); + optionsE.compiled=true; + editorWidget.code=code; + } else { + optionsE.compiled=false; + info('Agent class function not compiled.'); + } + updateMenu(); + }, + 'compile:play:Start Agent' : async function () { + var code = editorWidget.editor.get(); + if (!optionsE.compiled || code!=editorWidget.code) { + var acname = options.shell.env.compile(code); + if (acname) { + optionsE.acname=acname; + editorWidget.setLabel(Label()); + optionsE.compiled=true; + editorWidget.code=code; + updateMenu(); + } else { + optionsE.compiled=false + return info('No agent started, class function not compiled.'); + } + } + var ac=optionsE.acname||optionsE.file.replace(/\.js$/,''),args; + if (optionsE.arguments!="") eval('args='+optionsE.arguments); + else args={}; + var id=await options.shell.env.create(ac,args,optionsE.level); + if (id) { + info('Agent '+id+' of class '+ac+' started.'); + shellControl.commands.print('[JLB] Agent '+id+' of class '+ac+' started.'); + } else { + error('Starting agent of class '+ac+' failed.'); + shellControl.commands.error('[JLB] Error: Starting agent of class '+ac+' failed.'); + } + + }, + + } + }) + editorWin[numed]=editorWidget; + editorWin[numed]._options=optionsE; + editorWidget.on('file',function (filename) { + optionsE.file=filename; + editorWidget.setLabel(Label()) + updateMenu(); + }) + editorWidget.on('close',function () { + delete editorWin[numed]; + updateMenu(); + }) + editorWin[numed].editor.set(classTemplate); + updateMenu(); + }, + 'monitor:envelope:Agent Monitor' : function () { + if (!monitorWin[num]) { + // createMonitor(num,{shell:shell[num].options.shell}) + // TODO + monitorWidget=UI.Monitor.default({ + label:options.name+' / Monitor S'+num, + close:false, + run : function (line) { + shellControl.question(line); + } + }) + monitorWin[num]=monitorWidget; + monitorWin[num]._options=options; + options.shell.options.outputAgent = function (line) { monitorWidget.env.print(line) }; + updateMenu(); + } else monitorWin[num].window.show(); + }, + 'info:info:Node Information' : async function () { + UI('infoShellWinLabel'+num).setValue("Info / "+Label()); + if (options.infoView) UI('infoShellWinScrollView'+num).removeView(options.infoView); + options.infoView=UI('infoShellWinScrollView'+num).addView( + { + rows : await updateInfo(cmd,num) + } + ); + infoShellWin[num].show(); + }, + 'script:gear:New Script Editor': function () { + var optionsSE = { + shell:num, + file:'script.js', + } + var numed = editorNum++; + function Label() { + return optionsSE.file+' / '+options.name+' ScE'+numed+':S'+optionsSE.shell + } + var editorWidget = UI.Editor.default({ + label:Label(), + mode:'js', + buttons : { + 'compile:play:Start Agent' : function () { + var code = editorWidget.editor.get(); + shellControl.options.cmd.exec(code); + } + } + }) + editorWin[numed]=editorWidget; + editorWin[numed]._options=optionsSE; + editorWidget.on('file',function (filename) { + optionsSE.file=filename; + editorWidget.setLabel(Label()) + updateMenu(); + }) + editorWidget.on('close',function () { + delete editorWin[numed]; + updateMenu(); + }) + updateMenu(); + } + }, + run : function (line,env) { + shellControl.question(line); + }, + label : Label(), + close : num>1, + }) + shellControl.init(shellWidget, async function () { + shellWin[num]=shellWidget; + shellWin[num]._options=options; + shellWidget.on('close', function () { + shellControl.stop() + }) + // config.name = options.name; + cmd = options.cmd; + shell[num].run=cmd; + cmd.on('link+',function (arg){ + updateConn(cmd,options.name); + }); + cmd.on('link-',function (arg){ + updateConn(cmd,options.name); + }); + if (num==1) Marked = cmd.marked; + updateMenu(); + var versions = await cmd.versions(); + shellControl.commands.print('JAMlib Version '+versions.lib+' JAMShell Version '+versions.shell+' JAMAios Version '+versions.aios); + shellWidget.setLabel(Label()); + }); + if (num>1) { + shellWidget.on('close',function () { + removeConn(options.name); + shellControl.stop(); + delete shell[num]; + delete shellWin[num]; + }) + } + + /************** SHELL INFO MANAGER ****************/ + infoShellWin[num]=webix.ui({ + id:'infoShellWin'+num, + view:"window", + height:350, + width:500, + left:90, top:90, + move:true, + resize: true, + toFront:true, + css:'gray_toolbar', + head: { + view:"toolbar", + id:"infoShellWinToolbarWin"+num, + cols:[ + { view:"button", type:"icon", icon:"repeat", tooltip:'Refresh', width:30, click:async function () { + if (options.infoView) UI('infoShellWinScrollView'+num).removeView(options.infoView); + options.infoView=UI('infoShellWinScrollView'+num).addView( + { + rows : await updateInfo(cmd,num) + } + ); + }}, + { view:"button", type:"icon", icon:"times-circle", tooltip:'Kill Agent', width:30, click:async function () { + var tree = UI('treeAgentsNode'+num); + var selectedId = tree.getSelectedId(); + if (selectedId.indexOf('agent.')==0) { + var agentid = selectedId.substring(selectedId.indexOf('.')+1,selectedId.indexOf(' ')); + await cmd.kill(agentid); + } + if (options.infoView) UI('infoShellWinScrollView'+num).removeView(options.infoView); + options.infoView=UI('infoShellWinScrollView'+num).addView( + { + rows : await updateInfo(cmd,num) + } + ); + }}, + { view:"label", label:"Info / "+Label(), id:'infoShellWinLabel'+num, align:'right'}, + { view:"button", type:"icon", icon:"check-square", align:'center', width:30, click: function () { + infoShellWin[num].hide(); + }} + ] + }, + body:{ + view : 'form', + scroll : 'y', + height: 300, + id:'infoShellWinScrollView'+num, + rows : [ + ] + } + }); + +} +createShell(shellNum,{}); +shellNum++; + + + + +/************ EDITOR *****************/ + +function createEditor(num,opts) { + var options = { + arguments : "{}", + level:2, + compiled:false, + file:'untitled', + shell:opts.shell, // number + script:opts.script, + name : opts.name + } + function Label() { + if (options.script) + return options.file+' / script / Shell '+options.shell+' / Editor '+num + else + return options.file+' / '+options.name+" / Shell "+options.shell+" / Editor "+num + } + var head = { + view:"toolbar", + cols:[ + { view:"button", type:"icon", icon:"folder-open", tooltip:'Open File', width:30, click:function () { + options.compiled = false; + loadFile(function (text,file) { + if (text) { + UI('SourceText'+num).setValue(text) + options.file=file + UI('SourceTextWinLabel'+num).setValue(Label()) + updateMenu(); + } + }); + }}, + { view:"button", type:"icon", icon:"save", tooltip:'Save File', width:30, click:function () { + var code = UI('SourceText'+num).getValue(); + if (!/\.js$/.test(options.file)) options.file+='.js'; + saveFile(code,options.file,'text/plain',function (file) { + if (file) { + options.file=file + UI('SourceTextWinLabel'+num).setValue(Label()) + updateMenu() + } + }); + }}, + { view:"button", type:"icon", icon:"file", tooltip:'New File', width:30, click:function () { + options.compiled = false; + }}, + opts.script?null:{ view:"button", type:"icon", icon:"navicon", tooltip:'Config', width:30, click:function () { + configEditorWin[num].show(); + }}, + { view:"button", type:"icon", icon:"user", tooltip:'Share Code', width:30, click:function () { + Clip.share(num); + }}, + opts.script?null:{ view:"button", type:"icon", icon:"indent", tooltip:'Compile Class', width:30, click:function () { + var code = UI('SourceText'+num).getValue(); + var acname = shell[options.shell].options.shell.env.compile(code); + if (acname) { + options.file = acname+'.js'; + UI('SourceTextWinLabel'+num).setValue(Label()); + options.compiled = true; + } + updateMenu(); + }}, + opts.script?{ view:"button", type:"icon", icon:"play", tooltip:'Execute script', width:30, click:function () { + var code = 'later(1,function () {'+UI('SourceText'+num).getValue()+'})'; + shell[options.shell].ask(code); + }} + :{ view:"button", type:"icon", icon:"play", tooltip:'Create Agent', width:30, click:function () { + if (options.compiled == false) { + var code = UI('SourceText'+num).getValue(); + var acname = shell[options.shell].options.shell.env.compile(code); + if (acname) { + options.file = acname+'.js'; + UI('SourceTextWinLabel'+num).setValue(Label()); + } + options.compiled = true; + updateMenu(); + } + var ac=options.file.replace(/\.js$/,''),args; + if (options.arguments!="") eval('args='+options.arguments); + else args={}; + var id=shell[options.shell].options.shell.env.create(ac,args,options.level); + if (id) { + info('Agent '+id+' of class '+ac+' started.'); + shell[options.shell].print('[JLB] Agent '+id+' of class '+ac+' started.'); + } else { + error('Starting agent of class '+ac+' failed.'); + shell[options.shell].print('[JLB] Error: Starting agent of class '+ac+' failed.'); + } + }}, + { view:"label", label:Label(), id:'SourceTextWinLabel'+num, align:'right'}, + { view:"button", type:"icon", icon:"close", tooltip:'Close Editor', width:30, click:function () { + var self=editorWin[num]; + // editorWin[num]=undefined; + self.hide(); + }}, + ].filter(function (o) { return o }) + } + var config = { + id:'SourceTextWin'+num, + view:"window", + height:350, + width:600, + left:250, top:50, + move:true, + resize: true, + toFront:true, + css:opts.script?'blue_toolbar':'green_toolbar', + head:head, + body:{ + id : 'SourceText'+num, + view: "codemirror-editor", + attributes : { spellcheck:false, smartIndent:false, indentUnit:2}, + } + } + + editorWin[num]=webix.ui(config); + if (!options.script) UI('SourceText'+num).setValue(classTemplate); + + editorWin[num]._options=options; + editorWin[num].show(); + + /********** EDITOR CONFIG ****************/ + configEditorWin[num]=webix.ui({ + id:'configEditorWin'+num, + view:"window", + width:300, + height:450, + left:90, top:90, + move:true, + resize: true, + toFront:true, + css:'gray_toolbar', + head: { + view:"toolbar", + cols:[ + { view:"label", label:"Configuration", align:'right'}, + { view:"button", type:"icon", icon:"check-square", align:'center', width:30, click: function () { + options.arguments = UI('configEditorAgentArgs'+num).getValue(); + options.level = Number(UI('configEditorAgentLevel'+num).getValue())||0; + options.file=UI('configEditorFileName'+num).getValue(); + UI('SourceTextWinLabel'+num).setValue(Label()); + configEditorWin[num].hide(); + }} + ] + }, + body: { + rows:[ + { view:"form", scroll:true, width:300, height:400, elements: [ + + { view:"label", label:"Agent Creation", height:'18px', align:'left'}, + { view:"textarea", id:"configEditorAgentArgs"+num, label:"Argument", height:150, value:options.arguments}, + { view:"text", id:"configEditorAgentLevel"+num, label:"Level", value:options.level}, + { view:"label", label:"Code", height:'18px', align:'left'}, + { view:"text", id:"configEditorFileName"+num, label:"File Name", value:options.file}, + ]} + ] + } + }); + updateMenu(); +} + + + +/****************** MONITOR *******************/ + +function createMonitor(num,opts) { + var tables={},reporter; + opts.cmd = function (cmd) { + // @cmd arg1 arg2 ... + var obj,table, + prefix=cmd.substr(0,cmd.indexOf(' ')), + args=cmd.substr(cmd.indexOf(' ')+1); + console.log(prefix); + console.log(args); + switch (prefix) { + case '@monitor:new': + obj=JSON.parse(args); + options.header=obj.header; + options.id=obj.id; + if (!reporterWin[num]) createReporter(num,options); + break; + case '@monitor:data': + if (!reporterWin[num]) return; + obj=JSON.parse(args); + table=UI(reporterWin[num]._datatable.id); + table.add(obj.row) + break; + } + } + monitor[num] = winShell(opts); + var options = monitor[num].options; + options.shell = opts.shell; + options.closed = false; + + function Label () { + return jamConfig.name+" / Shell "+num+" / Agent Monitor" + } + monitorWin[num]=webix.ui({ + id:'MonitorWin'+num, + view:"window", + height:350, + width:600, + left:350, top:250, + move:true, + resize: true, + toFront:true, + css:'black_toolbar', + head: { + view:"toolbar", + cols:[ + { view:"button", type:"icon", icon:"eraser", tooltip:'Clear', width:30, click:function () { + monitor[num].commands.clear() + }}, + { view:"button", type:"icon", icon:"dot-circle-o", tooltip:'Scroll Auto', id:'MonitorWinLogFilmBut'+num, width:30, click:function () { + UI('MonitorWinLog'+num).scrollAuto=!UI('MonitorWinLog'+num).scrollAuto; + if (UI('MonitorWinLog'+num).scrollAuto) UI('MonitorWinLogFilmBut'+num).define('icon','arrow-circle-down') + else UI('MonitorWinLogFilmBut'+num).define('icon','dot-circle-o'); + UI('MonitorWinLogFilmBut'+num).refresh(); + }}, + { view:"button", type:"icon", icon:"table", tooltip:'Data Monitor', width:30, click:function () { + if (!reporterWin[num]) return; + // createReporter(num,options); + reporterWin[num].show(); + }}, + { view:"label", label:Label(), id:'MonitorWinLabel'+num, align:'right'}, + { view:"button", type:"icon", icon:"close", tooltip:'Kill', width:30, click:function () { + monitorWin[num].hide(); + }}, + ] + }, + body:{ + id : 'MonitorWinLog'+num, + view : 'scrollview', + scroll : 'y', + body : { + id:'MonitorWinLogText'+num, + rows : [ + {template:('
'), height:"auto", borderless:true}, + ] + } + } + }); + UI('MonitorWinLog'+num).scrollAuto=false; + monitorWin[num].show(); + monitor[num].init(undefined,"MonitorWinOutput"+num,UI('MonitorWinLog'+num)); + UI('MonitorWinLabel'+num).setValue(Label()); + updateMenu() +} + +/****************** REPORTER *******************/ + +function createReporter(num,options) { + function makeReport(tbl) { + console.log(tbl) + var header = tbl.shift(), + columns = [], + hash =[], + data =[]; + header.forEach(function (col,i) { + console.log(i,col) + columns.push({id:col,header:col,editor:'text'}); hash[i]=col; + }); + tbl.forEach (function (row,i) { + var obj={id:i}; + row.forEach(function (col,j) { obj[hash[j]]=col; }); + data.push(obj); + }); + var datatbl = { + view:"datatable", + id:'DataTable'+options.id+num, + columns:columns, + select:"cell", + multiselect:true, + blockselect:true, + clipboard:"selection", + data: data, + on:{ + onBeforeBlockSelect:function(start, end, fin){ + if (start.column === "rank") + end.column = "votes"; + + if (fin && start.column == "rank"){ + var mode = this.isSelected(start) ? -1 : 1; + this.selectRange( + start.row, start.column, end.row, end.column, + mode + ); + return false; + } + } + }, + }; + return datatbl; + } + var datatable = makeReport([options.header.map(function (col) { return Object.keys(col)[0]})]); + reporterWin[num]=webix.ui({ + id:'ReporterWin'+num, + view:"window", + height:350, + width:600, + left:350, top:250, + move:true, + resize: true, + toFront:true, + head: { + view:"toolbar", + cols:[ + { view:"button", type:"icon", icon:"eraser", tooltip:'Clear', width:30, click:function () { + }}, + { view:"button", type:"icon", icon:"flag", tooltip:'Scroll Auto', id:'ReporterWinLogFilmBut'+num, width:30, click:function () { + UI('ReporterWinLog'+num).scrollAuto=!UI('ReporterWinLog'+num).scrollAuto; + if (UI('ReporterWinLog'+num).scrollAuto) UI('ReporterWinLogFilmBut'+num).define('icon','flag-o') + else UI('ReporterWinLogFilmBut'+num).define('icon','flag'); + UI('ReporterWinLogFilmBut'+num).refresh(); + }}, + { view:"button", type:"icon", icon:"table", tooltip:'Data Monitor', width:30, click:function () { + }}, + { view:"label", label:"Thread "+options.thread+ " / "+options.ip+':'+options.port+" / Shell "+options.shell+" / Report "+num, id:'ReporterWinLabel'+num, align:'right'}, + { view:"button", type:"icon", icon:"close", tooltip:'Kill', width:30, click:function () { + var self=reporterWin[num]; + self.hide(); + }}, + ] + }, + body:datatable + + }); + reporterWin[num]._datatable=datatable; + return reporterWin[num] +} + +/* Load help files */ +help.aios = SHELL.doc.aios; +help.jamsh = SHELL.doc.shell; +help.js = SHELL.doc.javascript; + +///////////// +// CHAT WIN +///////////// +chatWin = webix.ui({ + id:'chatWin', + view:"window", + height:400, + width:400, + left:100, top:100, + move:true, + resize: true, + toFront:true, + head: { + view:"toolbar", + id:"myToolbarchatWin", + cols:[ + { view:"button", type:"icon", icon:"eraser", tooltip:'Clear', width:50, click:function () { + chatReset(); + }}, + { view:"button", type:"icon", icon:"user", tooltip:'Talk', width:50, click:function () { + // interrupt request (question) + }}, + { view:"label", label:"Agent Chat", align:'right'}, + { view:"button", type:"icon", icon:"close", align:'center', width:30, click: function () { + chatWin.hide(); + }} + ] + }, + body:{ + template : '
', + borderless:false + } +}); + +chatWin.chatActions=[]; // copy(chatActionsInit,true); +chatWin.chatLastAction=undefined; +chatWin.nextChatHistory=[]; +chatWin.chatHistory=[]; +chatWin.chatOpen=false; + +// Clear the chat +function chatReset () { + chatWin.chatActions=[]; + chatWin.chatHistory=[]; + chatWin.nextChatHistory=[]; + chatWin.chatLastAction=undefined; + if (chatWin.botui) { + chatWin.botui.message.removeAll(); + } + chatRefresh(); +} +// Refresh the chat by adding old/new messages and actions + +function chatRefresh () { + if (!chatWin.botui) return; + function exec(l,last) { + var todo=l.shift(); + if (!todo) return; + function doit(todo,last) { + switch (todo.kind) { + case 'message': + last=chatWin.botui.message.bot(todo); + break; + case 'answer': + last=chatWin.botui.message.human(todo); + break; + case 'wait': + if (last) last=last.then(function () {}); + break; + case 'button': + chatWin.chatLastAction=[todo]; + if (last) last=last.then(function () { return chatWin.botui.action.button(todo); }).then(function (res) { + chatWin.chatLastAction=[]; + return { then:function (f) { f(res) } } + }); + else { + last=chatWin.botui.action.button(todo).then(function (res) { + chatWin.chatLastAction=undefined; + return { then:function (f) { f(res) } } + }); + } + break; + case 'value': + chatWin.chatLastAction=[todo]; + if (last) last=last.then(function () { return chatWin.botui.action.text(todo); }).then(function (res) { + chatWin.chatLastAction=[]; + return { then:function (f) { f(res) } } + }); + else { + last=chatWin.botui.action.text(todo).then(function (res) { + chatWin.chatLastAction=undefined; + return { then:function (f) { f(res) } } + }); + } + break; + case 'checkpoint': + if (chatWin.chatLastAction.length) chatWin.chatLastAction.push(todo); + if (last) last=last.then(function (res) { if (todo.timer) clearTimeout(todo.timer); return todo.callback(res) }); + break; + } + return last + } + last=doit(todo,last); + if (l.length) exec(l,last); + } + exec(chatWin.chatHistory); + exec(chatWin.chatActions); +} +// Add a chat action +function chatAction (action) { + chatWin.chatActions.push(action); +} + +// External chat requests +function chatQuestion (id, question, action, callback, timeout) { + // TODO: Remove question after timeout! + var actions=[],timer; + chatAction({kind:'message', delay:0, content: id+': '+question }); + if (timeout) timer=setTimeout(function () { + chatWin.chatLastAction=[]; + // console.log('chatQuestion timeout'); + chatWin.chatActions=chatWin.chatActions.filter(function (todo) { + if (todo.kind=='checkpoint' || todo.action) return false; + else return true; + }); + if (chatWin.botui) chatWin.botui.removeAction(); + if (callback) callback(); + },timeout); + if (action instanceof Array) { + // Buttons/Choices + if (typeof action[0] == 'string') action=action.map(function (s) { return { text:s, value:s } }); + chatAction({kind:'button', delay:0, action: action}); + chatAction({kind:'checkpoint', callback:callback, timer:timer}); + } else if (typeof action == 'object') { + // Value or text + chatAction({kind:'value', delay:0, action: action}); + chatAction({kind:'checkpoint', callback:callback, timer:timer}); + } + chatRefresh(); +} + +function chatMessage (id,message) { + chatAction({kind:'message', delay:0, content: id+': '+message }); + chatRefresh(); +} + + + + +/* Chat Script Interpreter */ +// {question:string, choices?:[], value?:number|string, eval?:function, +// cond?:function, timeout?:numebr, tag?:string } +// {message:string} +// {finalize:function} +// {process:function} +// {init:function} +// ! After reply to a question the question record contains an answer attribute ! +chatScript = { + script : [], + id:'Anonymous', + index : 0, + cancel : function () { + self.chatScript.script=[]; + }, + init: function (id,script) { + self.chatScript.script=script; + self.chatScript.index=0; + self.chatScript.id=id; + }, + next : function () { + var process = shell[1].run.Aios.current.process; + var next = self.chatScript.script[self.chatScript.index]; + self.chatScript.index++; + if (!next) return 0; + if (next.question) { + if (next.cond && !next.cond.call(process.agent,self.chatScript.script)) return -1; + if (next.eval) { + var replace = next.eval.call(process.agent,self.chatScript.script); + // TODO + } + var timeout = next.timeout||30000; + chatQuestion(self.chatScript.id, + next.question, + next.choices||{value:next.value}, + function (res) { + if (next.callback) + shell[1].run.Aios.CB(process,next.callback,res?[res.value]:null); + process.wakeup(); + shell[1].run.schedule(); + }, + timeout + ); + process.suspend(shell[1].run.Aios.timeout(timeout)); + } else if (next.message) { + chatMessage(self.chatScript.id, + next.message) + } + else if (next.process) next.process(self.chatScript.script); + else if (next.init) next.init.call(process.agent,self.chatScript.script); + else if (next.finalize) next.finalize(self.chatScript.script); + return 1; + }, + reset : function () { + self.chatScript.index=0; + }, +} + +chatInit = function () { + if (!chatWin.botui) { + chatWin.nextChatHistory=[]; + chatWin.botui = new BotUI('chat-bot',{callback:function (msg) { + // Record message history for page refresh + if (msg.content) { + if (msg.human) chatWin.nextChatHistory.push({kind:"answer", delay:0, content:msg.content}); + else chatWin.nextChatHistory.push({kind:"message", delay:0, content:msg.content}); + } + }}); + chatMessage('Chat','Ready.'); + chatRefresh(); + } +} +chatAgent = function (action) { + if (!chatWin.agent) { + if (action.load) + return clippy.load(action.load, function (agent) { + chatWin.agent=agent; + }); + else return; + } + for(var p in action) { + switch (p) { + case 'play' : chatWin.agent.play(action[p]); break; + case 'speak': chatWin.agent.speak(action[p]); break; + case 'hide': chatWin.agent.stop(); chatWin.agent.hide(); break; + case 'moveTo': chatWin.agent.moveTo(action[p].x,action[p].y,action[p].time); break; + case 'load': chatWin.agent.show(true); break; + case 'show': chatWin.agent.show(action[p]); break; + case 'stop': chatWin.agent.stop(); break; + case 'stopCurrent': chatWin.agent.stopCurrent(); break; + } + } +} +shell[1].run.extend([2,3],'message',chatMessage,2); +shell[1].run.extend([2,3],'question',function (id, question, action, callback, timeout) { + var process = shell[1].run.Aios.current.process; + if (timeout==undefined) timeout=60000; + chatQuestion (id, question, action, function (res) { + shell[1].run.Aios.CB(process,callback,res?[res.value]:null); + process.wakeup(); + shell[1].run.schedule(); + },timeout); + process.suspend(shell[1].run.Aios.timeout(timeout)); + },[4,5]); + + +function openWin(num,content,options) { + var panel = webix.ui({ + id:'Panel'+num, + view:"window", + height:350, + width:600, + left:350, top:250, + move:true, + resize: true, + toFront:true, + css:'black_toolbar', + head: { + view:"toolbar", + cols:[ + { view:"label", label:options.title||("Window "+num), align:'right'}, + { view:"button", type:"icon", icon:"close", tooltip:'Close', width:30, click:function () { + panel.close(); + }}, + ] + }, + body: { + template : content, + borderless:false + } + }); + panel.show(); +} + +Config=jamConfig; +////////////////////////////////////////////////////////////// +