/** ** ============================== ** 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-2019 bLAB ** $CREATED: 09-11-16 by sbosse. ** $VERSION: 1.4.3 ** $INFO: ** ** Standalone JAM with app.js UI for Cordova/nw.js (w/o DOS) using jamlib.browser ** Javascript part of User Interface Module (page content structure in app.html) ** Used with Cordova and NW. ** ** $ENDOFINFO ** */ var version = "1.4.3M" global=window; global.simulation=false; global.dos=false; inspect = global.inspect = JAMLIB.Io.inspect // Android layout if (App.platform=='node-webkit') document.body.className += ' app-android app-android-4'; Logger.init(); // providesn global system log() Logger.close(); // open logger initially console.log=log; console.warn=log; //print=log; log('Target: '+global.TARGET+(typeof HackTimer != 'undefined'?' with HackTimerWorker':'')); if (typeof jamConfig == 'undefined') log ('No custom configuration file config.js loaded.') else log ('Custom configuration file config.js loaded.'); log(process.execPath) appCordova.initialize(); log('appCordova now initializing...'); // Optional App configuration loaded from file (config.js) if (typeof jamConfig == 'undefined') jamConfig = { startJam: false, startNetwork:false, loadAgents: [], startAgents: [], network:[ {ip:"localhost",ipport:10001,enable:true}, {}, {}, {} ], log:{agent:true,parent:false,time:false,Time:true,class:false}, // Message flags: agent parent time class capabilities : [ "", "", "", "" ], chatDelay:undefined, default:true, popupLevel : 1, verbose:1, } function completeConfig() { jamConfig.version = version; if (!jamConfig.log) jamConfig.log={agent:true,parent:false,time:false,Time:true,class:false}; if (!jamConfig.capabilities) jamConfig.capabilities=["","","",""]; if (typeof Network != 'undefined') Network.options.link=jamConfig.network; } completeConfig() log('JAMapp Version '+jamConfig.version); var _log2List=[]; var _classes=[]; var _agents=[]; var _agentArgs="{}"; var verbose = jamConfig.verbose; var clickevent = "click" var nameopts = { world:{length:8, memorable:true, uppercase:true}, node: {length:8, memorable:true, lowercase:true} } /********** UTILS ******************/ function replaceContentInContainer(target, source) { document.getElementById(target).innerHTML = document.getElementById(source).innerHTML; } function scrollToBottom(id){ var div = document.getElementById(id); if (div) div.scrollTop = div.scrollHeight - div.clientHeight; } /** Resolve file path (../.) * */ function resolvePath(path) { var entries = path.split('/'), newentries=[],sep='', abs=path[0]=='/'; for (var i in entries) { if (entries[i]=='.') continue; if (entries[i]=='..') { if (newentries.length==0) return null; newentries.pop(); } else newentries.push(entries[i]); } return newentries.join('/'); } /********** TUPLE INTERFACE *******************/ function provider(pat) { if (pat && pat.length == 3 && pat[0] == 'SENSOR') { if (appSensorsMap[pat[1]]) return [pat[0],pat[1],appSensorsMap[pat[1]].read()] } } /********** FILEMANAGER ******************/ var fmmode ='load', fmselect=false, fmselected, fmlabel, fmmultiselect=false, fmdir, fmaction = function () {}; // Action handler function fmexec() { if (fmselected) fmaction(fmselected); } // Action setup (called from app.html) function fmsetup(action,label,select) { fmaction = action; fmlabel = label; fmselected = null; fmselect = select; App.load('page-filemanager'); } /********** DIALOGS ******************/ var pb = new PromptBoxes({ attrPrefix: 'pb', speeds: { backdrop: 250, // The enter/leaving animation speed of the backdrop toasts: 250 // The enter/leaving animation speed of the toast }, alert: { okText: 'Ok', // The text for the ok button okClass: '', // A class for the ok button closeWithEscape: false, // Allow closing with escaping absolute: false // Show prompt popup as absolute }, confirm: { confirmText: 'Confirm', // The text for the confirm button confirmClass: '', // A class for the confirm button cancelText: 'Cancel', // The text for the cancel button cancelClass: '', // A class for the cancel button closeWithEscape: true, // Allow closing with escaping absolute: false // Show prompt popup as absolute }, choices: { choicesClass: '', // A class for the choices button cancelText: 'Cancel', // The text for the cancel button cancelClass: '', // A class for the cancel button closeWithEscape: true, // Allow closing with escaping absolute: false // Show prompt popup as absolute }, prompt: { inputType: 'text', // The type of input 'text' | 'password' etc. submitText: 'Submit', // The text for the submit button submitClass: '', // A class for the submit button cancelText: 'Cancel', // The text for the cancel button cancelClass: '', // A class for the cancel button closeWithEscape: true, // Allow closing with escaping absolute: false // Show prompt popup as absolute }, toasts: { direction: 'bottom', // Which direction to show the toast 'top' | 'bottom' max: 5, // The number of toasts that can be in the stack duration: 5000, // The time the toast appears showTimerBar: false, // Show timer bar countdown closeWithEscape: true, // Allow closing with escaping allowClose: false, // Whether to show a "x" to close the toast } }); var _pbinfo=pb.info.bind(pb); pb.info=function (msg) {if (jamConfig.popupLevel<1) log(msg); else _pbinfo(msg) } /********** AIOS/NETWORK *************/ Aios = JAMLIB.Aios; JAMLIB.Name = JAMLIB.Aios.Name; JAMLIB.setup = function (options,callback) { log('Creating JAM platform ...'); var jam=JAMLIB.Jam({ print:options.print, print2:options.print2, nameopts:nameopts.node, Nameopts:nameopts.world, provider:provider, verbose:options.verbose, log:jamConfig.log, type:'mobileapp' }); jam.init(function () { options.nodename = jam.getNodeName(); options.worldname = jam.getWorldName(); if (callback) callback(jam); }); var S=jam.security,N=jam.getNode(); // Capability 1 contains the JAM node private port and security port (random field) if (jamConfig.capabilities[0]!='') { var cap = S.Capability.ofString(jamConfig.capabilities[0]); N.port=cap.cap_port; N.random[N.port]=cap.cap_priv.prv_rand; } else { // Use random generated ports - secret!!! jamConfig.capabilities[0]=S.Capability.toString(S.Capability(N.port,S.Private(0,0xFF,N.random[N.port]))); } return jam; } // AIOS log level 1 (agent) logging function function log2(msg) { var elem = document.getElementById("app-agent-messages"), page = document.getElementById("page-agents"), scrollEnd; if (msg!=undefined && elem) { scrollEnd = (page.scrollTop-(page.scrollHeight- page.clientHeight))==0; elem.appendChild(document.createTextNode(msg)); elem.appendChild(document.createElement('br')); if (scrollEnd) page.scrollTop = page.scrollHeight - page.clientHeight; } _log2List.push(msg); } /******************** PAGE UPDATES ****************/ // Each time the app-message page is opened the current log must be refreshed function log2Update (clear) { var elem = document.getElementById("app-agent-messages"); while (elem && elem.firstChild) { elem.removeChild(elem.firstChild); } if (clear) { _log2List=[]; } else { for(var i in _log2List) { elem.appendChild(document.createTextNode(_log2List[i])); elem.appendChild(document.createElement('br')); } } } // Full page refresh chat update function chatUpdate () { if (verbose>1) log('chatUpdate BotUI='+(BotUI?'true':'false')); nextChatHistory=[]; botui = new BotUI('chat-bot',{callback:function (msg) { // Record message history for page refresh if (msg.content) { if (msg.human) nextChatHistory.push({kind:"answer", delay:jamConfig.chatDelay, content:msg.content}); else nextChatHistory.push({kind:"message", delay:jamConfig.chatDelay, content:msg.content}); } }}); chatRefresh(); } // Update agent class list function classUpdate() { var list = document.getElementById("app-list-classes"); while (list.firstChild) list.removeChild(list.firstChild); for(var name in _classes) { function add(name) { var selected=_classes[name].selected; var ListItem = document.createElement("li"); ListItem.appendChild(document.createTextNode(name)); ListItem.addEventListener(clickevent,function () { for(var p in _classes) if (_classes[p].selected) { _classes[p].li.style.background='white'; _classes[name].selected=false } ListItem.style.background = '#BBB'; _classes[name].selected=true; }); ListItem.style["cursor"]='pointer'; list.appendChild(ListItem); if (selected) ListItem.style.background = ListItem.style.background = '#BBB'; _classes[name]={li:ListItem,selected:selected}; } add(name) } } // Update running agent list function runUpdate() { var list = document.getElementById("app-list-agent-running"); while (list.firstChild) list.removeChild(list.firstChild); _agents=[]; for(var name in Jam.jam.stats('agent')) { function add(name) { var ListItem = document.createElement("li"); ListItem.appendChild(document.createTextNode(name)); ListItem.addEventListener(clickevent,function () { for(var p in _agents) if (_agents[p].selected) { _agents[p].li.style.background='white'; _agents[name].selected=false } ListItem.style.background = '#BBB'; _agents[name].selected=true; }); ListItem.style["cursor"]='pointer'; list.appendChild(ListItem); _agents[name]={li:ListItem,selected:false}; } add(name) } } // Update sensor list function sensorUpdate() { var list = document.getElementById("app-list-sensors"); for(var name in appSensors) { var data; if (verbose>1) log('sensorUpdate '+name+' read='+ String(appSensors[name].read)+' error='+String(appSensors[name].error)); if (!appSensors[name].read) continue; function format(data) { if (typeof data == 'string') return data; else return data[0]!=undefined?data.join(' , '):String(data); } function add(name,data) { var ListLabel = document.createElement("label"); var ListItem = document.createElement("li"); ListLabel.setAttribute("id",'SensorListLabel_'+name); ListItem.setAttribute("id",'SensorListItem1_'+name); ListLabel.appendChild(document.createTextNode(name+' ('+appSensors[name].sensor+')')); ListItem.appendChild(document.createTextNode(format(data))); list.appendChild(ListLabel); list.appendChild(ListItem); } function update(name,data) { var ListLabel = document.getElementById('SensorListLabel_'+name); var ListItem = document.getElementById('SensorListItem1_'+name); if (!ListLabel || !ListItem) return false; ListItem.firstChild.nodeValue=format(data); return true; } data=appSensors[name].read(); if (data==undefined) continue; if (!update(name,data)) add(name,data); appSensors[name].last=data; } } var _fmdir; // Filemanager directory list update function fmUpdate() { var label = document.getElementById("page-filemanager-label"); function fm(root) { switch (root) { case "Download": fmdir=cordova.file.downloadDirectory||cordova.file.externalRootDirectory+'/Download'; break; case "Temp": fmdir=cordova.file.tempDirectory; break; case "Home": fmdir=cordova.file.userDirectory||"/"; break; case "App": fmdir=cordova.file.applicationDirectory; break; case "Data": fmdir=cordova.file.dataDirectory; break; case "Last": fmdir=_fmdir; break; } if (!fmdir) fmdir=cordova.file.dataDirectory; label.innerHTML=resolvePath(fmdir); listDir(fmdir,function (entries, err) { if (err) { fmdir=null; return pb.error('listDir error: '+JAMLIB.Io.inspect(err))}; var list = document.getElementById("page-filemanager-list"); while (list.firstChild) list.removeChild(list.firstChild); if (entries) entries = entries.concat([{isFile:false,isDirectory:true,name:'..'}]).sort(function (a,b) { if (a.name > b.name) return 1; else return -1; }); for(var index in entries) { var entry = entries[index]; function add(entry) { var ListItem = document.createElement("li"); ListItem.appendChild(document.createTextNode(entry.name)); ListItem.addEventListener(clickevent,function () { if (entry.isDirectory) { fmdir=resolvePath(fmdir+'/'+entry.name); fmUpdate() } else if (fmselect) { // Select only if (fmselected && !fmmultiselect) fmselected.item.style.background='white'; fmselected = {item:ListItem,entry:entry}; ListItem.style.background = '#BBB'; } else { entry.dir=fmdir; fmselected = entry; ListItem.style.background = '#BBB'; fmexec(); App.back() } }); if (entry.isDirectory) ListItem.style["font-weight"] = 'bold'; ListItem.style["cursor"]='pointer'; list.appendChild(ListItem); } add(entry) } }); } if (!fmdir) pb.choices(function (choice) { if (choice) fm(choice); else { App.back() } },"Select Location:",["Download","Data","App","Temp","Home","Last"]); else fm(); } function copy (o,isArray) { var _o=isArray?[]:{}; for(var k in o) _o[k]=o[k]; return _o; } function merge(obj1,obj2){ var obj3 = {}; for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } return obj3; } // Load or update agent class from url function loadClass(url,cb) { if (url) { var file = basename(url); var aclass = classname(file); // Load from remote source; script loading creates a global constructor function! if (typeof window[aclass] != 'undefined') { return pb.error('Cannot load agent class constructor '+aclass+': Exists already!'); } console.log('Loading '+url) loadjs(url,function (ev) { if (Jam.jam) { // Jam.jam.compileClass(aclass,constr,{verbose:1}); if (typeof window[aclass] == 'function') { try { Jam.jam.compileClass(aclass,window[aclass],{verbose:1}) } catch (e) { return cb?cb():null } }; if (_classes[aclass]==undefined) { _classes[aclass]=false; } if (cb) cb(aclass); } }); } } // Load a class from a local file function loadClassFromFile(path, file, cb, onerror) { var aclass = classname(file); readFile(path, file, function (data) { // log(data) if (Jam.jam) { try { Jam.jam.compileClass(aclass,data,{verbose:1}) } catch (e) { return cb?cb():null }; if (_classes[aclass]==undefined) { _classes[aclass]=false; } if (cb) cb(aclass); } }, function (err) { if (onerror) onerror(err) else cb(null,err); }) } function loadClassFromFileObj(file, cb, onerror) { return loadClassFromFile(file.dir,file.name,cb,onerror) } // Save a class to local file function saveClassToFile(path, file, aclass, cb, onerror) { } function loadConfig(cb) { var path = cordova.file.dataDirectory, file = 'config.js'; readFile(path, file, function (data) { eval(data); completeConfig(); log('Loaded config file '+path+'/'+file); cb() }, function (err) { log(err) cb(err) }) } // Save config file to App directory function saveConfig(cb) { var path = cordova.file.dataDirectory, file = 'config.js'; var data = JSON.stringify(jamConfig,null,2); function onsucc () { if (jamConfig.verbose>1) log('saveConfig '+file+': ok'); if (cb) cb() } function onerror(e) { pb.error('saveConfig: '+e.toString()+(e&&e.code?(' (Code '+e.code+')'):'')); if (cb) cb(e); } writeFile(path, file, 'jamConfig='+data, onsucc, onerror); checkIfFileExists(path,file, function (res) { log('exists? '+res) }) } // Create an agent (class must be selected or given by argument) function createAgent (ac,args) { var ac, selected, args; if (!Jam.jam) return pb.error('JAM not existing!'); if (ac) { var id=Jam.jam.createAgent(ac,args,2); return pb.info('Agent '+id+' started.'); } for (var name in _classes) if (_classes[name].selected) { selected=name; break; } if (!selected) return pb.error('No class selected!'); ac=selected; pb.prompt( function (value) { if (value) { _agentArgs=value; eval('args='+value); } else args={}; if (!Jam.state) pb.info('JAM not started!'); var id=Jam.jam.createAgent(ac,args,2); pb.info('Agent '+id+' started.'); }, 'Agent arguments?', 'text', 'Set Arguments', 'No Arguments', { value:_agentArgs||'{}' } ); } function infoAgent() { var list = document.getElementById("app-list-agent-running"); var selected,name; for(name in _agents) if (_agents[name].selected) { selected=name; break; } if (!selected) return pb.error('No agent selected!'); var stats=Jam.jam.stats('agent'); for(var name in stats) { var stat=stats[name]; if (name==selected) pb.alert( function () {}, name+': pid='+ stat.pid+ ' gid='+stat.gid+ ' state='+stat.state+ ' next='+stat.next, 'Ok'); } } function killAgent() { var list = document.getElementById("app-list-agent-running"); var selected,name; for(name in _agents) if (_agents[name].selected) { selected=name; break; } if (!selected) return pb.error('No agent selected!'); Jam.jam.kill(selected); runUpdate(); pb.info('Killed agent '+name); } /************************** NETWORK **********************/ log('Creating network module...'); var Network = { options: {link:jamConfig.network}, location: undefined, state: 0, start: function () { var el; if (!Jam.jam) return; el=document.getElementById("app-net-start"); if (el) el.style.opacity=0.2; el=document.getElementById("app-net-stop"); if (el) el.style.opacity=1.0; Network.state=2; for (var i=0;i<4;i++) if (Network.options.link[i].ip && Network.options.link[i].ipport && Network.options.link[i].enable) Jam.jam.connectTo(JAMLIB.Aios.DIR.IP(Network.options.link[i].ip+':'+ Network.options.link[i].ipport)); Network.status(); pb.info('Network started'); }, status: function () { if (!document.getElementById("status-network-init")) return; switch (Network.state) { case 0: case 1: document.getElementById("status-network-init").style.visibility = "hidden"; document.getElementById("status-network-connected").style.visibility = "hidden"; document.getElementById("status-network-not-connected").style.visibility = "hidden"; document.getElementById("status-network-published").style.visibility = "hidden"; break; case 2: document.getElementById("status-network-init").style.visibility = "visible"; document.getElementById("status-network-connected").style.visibility = "hidden"; document.getElementById("status-network-not-connected").style.visibility = "hidden"; document.getElementById("status-network-published").style.visibility = "hidden"; break; case 3: document.getElementById("status-network-init").style.visibility = "visible"; document.getElementById("status-network-connected").style.visibility = "visible"; document.getElementById("status-network-not-connected").style.visibility = "hidden"; document.getElementById("status-network-published").style.visibility = "hidden"; break; case 4: document.getElementById("status-network-init").style.visibility = "visible"; document.getElementById("status-network-connected").style.visibility = "visible"; document.getElementById("status-network-not-connected").style.visibility = "hidden"; document.getElementById("status-network-published").style.visibility = "visible"; break; } switch (Jam.state) { case 0: document.getElementById("status-jam-active").style.visibility = "hidden"; break; case 1: document.getElementById("status-jam-active").style.visibility = "visible"; break; } }, stop: function () { var el; if (!Jam.jam) return; el=document.getElementById("app-net-start"); if (el) el.style.opacity=1.0; el=document.getElementById("app-net-stop"); if (el) el.style.opacity=0.2; Network.state=0; for (var i=0;i<4;i++) if (Network.options.link[i].ip && Network.options.link[i].ipport) Jam.jam.disconnect(JAMLIB.Aios.DIR.IP(Network.options.link[i].ip+':'+ Network.options.link[i].ipport)); Network.status(); pb.info('Network stopped'); } } /*********************** JAM **********************/ log('Creating agent module...'); var Jam = { options:{ domain:'default', nodename : undefined, worldname : undefined, log:jamConfig.log, print:log, print2:log2, verbose:1, }, jam: undefined, state : 0, refresh: function () { if (!this.jam) return; var stats=this.jam.stats('node'); // Update JAM statistics document.getElementById('statistics-cpu').innerHTML = (stats.cpu|0).toString(); document.getElementById('statistics-create').innerHTML = stats.create.toString(); document.getElementById('statistics-fastcopy').innerHTML= stats.fastcopy.toString(); document.getElementById('statistics-fork').innerHTML = stats.fork.toString(); document.getElementById('statistics-received').innerHTML= stats.received.toString(); document.getElementById('statistics-handled').innerHTML = stats.handled.toString(); document.getElementById('statistics-migrate').innerHTML = stats.migrate.toString(); document.getElementById('statistics-signal').innerHTML = stats.signal.toString(); document.getElementById('statistics-error').innerHTML = stats.error.toString(); document.getElementById('statistics-agents').innerHTML = stats.agents.toString(); document.getElementById('statistics-tsout').innerHTML = stats.tsout.toString(); document.getElementById('statistics-tsin').innerHTML = stats.tsin.toString(); }, start: function () { var el; if (this.jam) { this.jam.start(); Jam.state=1; el=document.getElementById("app-jam-start"); if (el) el.style.opacity=0.2; el=document.getElementById("app-jam-stop"); if (el) el.style.opacity=1.0; el=document.getElementById("app-jam-reset"); if (el) el.style.opacity=1.0; Network.status(); pb.info('Agent processing started'); } }, stop: function () { var el; if (this.jam && Jam.state==1) { this.jam.stop(); Jam.state=0; el=document.getElementById("app-jam-start"); if (el) el.style.opacity=1.0; el=document.getElementById("app-jam-stop"); if (el) el.style.opacity=0.2; el=document.getElementById("app-jam-reset"); if (el) el.style.opacity=0.2; Network.status() pb.info('Agent processing stopped'); } }, reset: function () {} } //if (window.location.href.indexOf('file:')!=0) // Network.options.link1.ip = window.location.href.replace(/http:\/\//,'').replace(/:[0-9]+[\/]*/,''); /********************* ** APP Controller *********************/ log('Creating app controller ...'); App.setDefaultTransition({ ios : 'fade' , // iOS iosFallback : 'fade' , // iOS <5 android : 'fade' , // Android androidFallback : 'fade' , // Android < 4 fallback : 'instant' // non-iOS, non-Android }); App.controller('home', function (page) { // put stuff here }); App.controller('page-network', function(page) { $(page).on('appShow', function () { Network.status(); switch (Network.state) { case 0: case 1: document.getElementById("app-net-start").style.opacity=1.0; document.getElementById("app-net-stop").style.opacity=0.2; break; case 2: case 3: document.getElementById("app-net-start").style.opacity=0.2; document.getElementById("app-net-stop").style.opacity=1.0; break; } }); $(page).on('appBack', function () { }); }); App.controller('page-jam', function (page) { // put stuff here $(page).on('appShow', function () { if (Jam.options.worldname) document.getElementById('jam-world').innerHTML = Jam.options.worldname; if (Jam.options.domain) document.getElementById('jam-domain').innerHTML = Jam.options.domain; if (Jam.options.nodename) document.getElementById('jam-node').innerHTML = Jam.options.nodename; switch (Jam.state) { case 0: document.getElementById("app-jam-start").style.opacity=1.0; document.getElementById("app-jam-stop").style.opacity=0.2; document.getElementById("app-jam-reset").style.opacity=0.2; break; case 1: document.getElementById("app-jam-start").style.opacity=0.2; document.getElementById("app-jam-stop").style.opacity=1.0; document.getElementById("app-jam-reset").style.opacity=1.0; break; } Jam.refresh(); /* if (Network.state>1) Jam.start(); else App.dialog({ title : 'Network Error', text : 'You need to start the network service first!', okButton : 'Ok', cancelButton : 'Cancel' }, function (tryAgain) { if (tryAgain) { // try again } }); */ }); $(page).on('appBack', function () { }); }); App.controller('page-agents', function (page) { // put stuff here // log('JAM is a '+JAMLIB.Jam); // the first message $(page).on('appShow', function () { log2Update(); }); $(page).on('appBack', function () { }); }); App.controller('page-agents-manager', function (page) { $(page).on('appShow', function () { runUpdate(); }); $(page).on('appBack', function () { }); }); App.controller('page-agents-class', function (page) { $(page).on('appShow', function () { classUpdate(); }); $(page).on('appBack', function () { }); }); App.controller('page-filemanager', function (page) { // put stuff here $(page).find('.app-topbar').find('.app-button').text(fmlabel); $(page).on('appShow', function () { fmdir=null; fmUpdate(); }); $(page).on('appBack', function () { _fmdir=fmdir; }); }); App.controller('page-setup', function (page) { $(page).on('appShow', function () { document.getElementById('setup-link1-enable').checked=Network.options.link[0].enable; document.getElementById('setup-link1-ip').value=Network.options.link[0].ip; document.getElementById('setup-link1-ipport').value=Network.options.link[0].ipport; if (Network.options.link[1].ip) document.getElementById('setup-link2-enable').checked=Network.options.link[1].enable, document.getElementById('setup-link2-ip').value=Network.options.link[1].ip, document.getElementById('setup-link2-ipport').value=Network.options.link[1].ipport; if (Network.options.link[2].ip) document.getElementById('setup-link3-enable').checked=Network.options.link[2].enable, document.getElementById('setup-link3-ip').value=Network.options.link[2].ip, document.getElementById('setup-link3-ipport').value=Network.options.link[2].ipport; if (Network.options.link[3].ip) document.getElementById('setup-link4-enable').checked=Network.options.link[3].enable, document.getElementById('setup-link4-ip').value=Network.options.link[3].ip, document.getElementById('setup-link4-ipport').value=Network.options.link[3].ipport; document.getElementById('setup-msg-agent').checked = false; document.getElementById('setup-msg-parent').checked = false; document.getElementById('setup-msg-time').checked = false; document.getElementById('setup-msg-Time').checked = false; document.getElementById('setup-msg-class').checked = false; if (Jam.options.log.agent) document.getElementById('setup-msg-agent').checked=true; if (Jam.options.log.parent) document.getElementById('setup-msg-parent').checked=true; if (Jam.options.log.time) document.getElementById('setup-msg-time').checked=true; if (Jam.options.log.Time) document.getElementById('setup-msg-Time').checked=true; if (Jam.options.log.class) document.getElementById('setup-msg-class').checked=true; document.getElementById('setup-cap1').value=jamConfig.capabilities[0]; document.getElementById('setup-cap2').value=jamConfig.capabilities[1]; document.getElementById('setup-cap3').value=jamConfig.capabilities[2]; document.getElementById('setup-cap4').value=jamConfig.capabilities[3]; }); $(page).on('appBack', function () { if (document.getElementById('setup-link1-ip').value!='') Network.options.link[0].enable=document.getElementById('setup-link1-enable').checked, Network.options.link[0].ip=document.getElementById('setup-link1-ip').value, Network.options.link[0].ipport=Number(document.getElementById('setup-link1-ipport').value); if (document.getElementById('setup-link2-ip').value!='') Network.options.link[1].enable=document.getElementById('setup-link2-enable').checked, Network.options.link[1].ip=document.getElementById('setup-link2-ip').value, Network.options.link[1].ipport=Number(document.getElementById('setup-link2-ipport').value); if (document.getElementById('setup-link3-ip').value!='') Network.options.link[2].enable=document.getElementById('setup-link3-enable').checked, Network.options.link[2].ip=document.getElementById('setup-link3-ip').value, Network.options.link[2].ipport=Number(document.getElementById('setup-link3-ipport').value); if (document.getElementById('setup-link4-ip').value!='') Network.options.link[3].enable=document.getElementById('setup-link4-enable').checked, Network.options.link[3].ip=document.getElementById('setup-link4-ip').value, Network.options.link[3].ipport=Number(document.getElementById('setup-link4-ipport').value); Jam.options.log.agent= document.getElementById('setup-msg-agent').checked; Jam.options.log.parent= document.getElementById('setup-msg-parent').checked; Jam.options.log.time= document.getElementById('setup-msg-time').checked; Jam.options.log.Time= document.getElementById('setup-msg-Time').checked; Jam.options.log.class= document.getElementById('setup-msg-class').checked; JAMLIB.Aios.config({log:Jam.options.log}) saveConfig() }); }); var pageInfoTimer; App.controller('page-info', function (page) { $(page).on('appShow', function () { sensorUpdate(); pageInfoTimer=setInterval(sensorUpdate,500); }); $(page).on('appBack', function () { if (pageInfoTimer) clearInterval(pageInfoTimer); pageInfoTimer=undefined; }); }); /***************** AGENT CHAT ***********************/ /** Actions (todos): * {kind:'message', delay: ms, content: message } * {kind:'button' , delay: ms, action: {text:string, value:string} []} * {kind:'value' , delay: ms, action: {size:px, icon?:string, value:number, sub_type:'number', placeholder:number}} * {kind:'checkpoint', callback:callback} * * */ var chatHistory=[]; var nextChatHistory=[]; var chatActive=false; // is chat window visible? var chatLastAction; // Save last action for page refresh var chatOpen=false; // if set to false no questions are accpeted (denied) var botui; var chatActions=[]; // copy(chatActionsInit,true); // Clear the chat function chatReset () { chatActions=[]; chatHistory=[]; chatOpen=false; if (botui) botui.clear(); chatUpdate(); } // Refresh the chat by adding old/new messages and actions function chatRefresh () { if (verbose>1) log('chatRefresh: botui='+ (botui?'true':'false')+' chatActive='+ (chatActive?'true':'false')+ ' chatHistory='+chatHistory.length+ ' chatActions='+chatActions.length); if (!botui || !chatActive) return; function exec(l,last) { var todo=l.shift(); if (!todo) return; function doit(todo,last) { if (verbose>1) log('chatRefresh: '+todo.kind); switch (todo.kind) { case 'message': last=botui.message.bot(todo); break; case 'answer': last=botui.message.human(todo); break; case 'wait': if (last) last=last.then(function () {}); break; case 'button': chatLastAction=[todo]; if (last) last=last.then(function () { return botui.action.button(todo); }).then(function (res) { chatLastAction=[]; return { then:function (f) { f(res) } } }); else { last=botui.action.button(todo).then(function (res) { chatLastAction=undefined; return { then:function (f) { f(res) } } }); } break; case 'value': chatLastAction=[todo]; if (last) last=last.then(function () { return botui.action.text(todo); }).then(function (res) { chatLastAction=[]; return { then:function (f) { f(res) } } }); else { last=botui.action.text(todo).then(function (res) { chatLastAction=undefined; return { then:function (f) { f(res) } } }); } break; case 'checkpoint': if (chatLastAction.length) 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(chatHistory); exec(chatActions); setTimeout(function () { scrollToBottom('chat-bot-content'); },500); } // Add a chat action, but only if last action is different (chatActions/chatHistory) function chatAction (action) { var last; function eq (a1,a2) { if (a1.kind != a2.kind) return false; if (a1.content && a2.content && a1.content==a2.content) return true; return false; } if (chatHistory.length) last = chatHistory[chatHistory.length-1]; else last = undefined; if (last && eq(last,action)) return; if (nextChatHistory.length) last = nextChatHistory[nextChatHistory.length-1]; else last = undefined; if (last && eq(last,action)) return; if (chatActions.length) last = chatActions[chatActions.length-1]; else last = undefined; if (last && eq(last,action)) return; chatActions.push(action); if (verbose>1) log('chatAction: ['+chatActions.length+']'); } // External chat requests function chatQuestion (id,question, action, callback, timeout) { // TODO: Remove question after timeout! var actions=[],timer; chatAction({kind:'message', delay: jamConfig.chatDelay, content: id+': '+question }); if (timeout) timer=setTimeout(function () { chatLastAction=[]; // console.log('chatQuestion timeout'); chatActions=chatActions.filter(function (todo) { if (todo.kind=='checkpoint' || todo.action) return false; else return true; }); if (botui && chatActive) 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: jamConfig.chatDelay, action: action}); chatAction({kind:'checkpoint', callback:callback, timer:timer}); } else if (typeof action == 'object') { // Value or text chatAction({kind:'value', delay: jamConfig.chatDelay, action: action}); chatAction({kind:'checkpoint', callback:callback, timer:timer}); } if (chatActive) chatRefresh(); } function chatMessage (id,message) { chatAction({kind:'message', delay: jamConfig.chatDelay, content: id+': '+message }); if (chatActive) chatRefresh(); } App.controller('page-chat', function(page) { $(page).on('appShow', function () { chatActive=true; chatUpdate(); }) $(page).on('appHide', function () { chatHistory=nextChatHistory; if (chatLastAction && chatLastAction.length) chatHistory=chatHistory.concat(chatLastAction); chatLastAction=[]; chatActive=false; botui=undefined; }) }) // Create, setup, and start JAM/Network function start(next) { // Create JAM instance JAMLIB.setup(Jam.options, function (jam) { log2 ('JAM '+Jam.options.nodename+ '['+Jam.options.worldname+'] is ready.'); log ('JAMLIB Ver. '+JAMLIB.options.version); Jam.jam=jam; // Now create communication port(s) var options={}; options.proto='http'; options.verbose=Jam.options.verbose; options.multicast=true; var port=jam.createPort(jam.DIR.IP(),options); port.link.on("link+",function (addr) {Network.state=3; pb.info('Connected to '+addr); Network.status()}) port.link.on("link-",function () {Network.state=2; Network.status()}) port.link.on("error",function (err,arg) {if (err=='link') { Network.state=0; pb.error('Link to '+arg+' failed!'); } Network.status()}) }); // Try localization ... Jam.jam.locate(undefined, function (loc,err) { Network.location = loc; if (!loc) { log('locate error: '+err.toString()); pb.error('locate error: '+err.toString()+'. You should disable ad-blocker for this page and reload page (top button)!'); } else log('My location is '+loc.ip+' '+loc.geo.city+' '+loc.geo.country); }); // Chat agent API extension Jam.jam.extend([2,3],'message',chatMessage); Jam.jam.extend([2,3],'question',function (id, question, choices, callback, timeout) { var process = Jam.jam.getProcess(); chatQuestion (id,question, choices, function (res) { process.callback(callback,res?[res.value]:null); process.wakeup(); Jam.jam.schedule(); },timeout); process.suspend(Aios.timeout(timeout)); }); // PB extension Jam.jam.extend([2,3],'notify',function (msg) { pb.info(msg) }); // Initialization on start up... if (jamConfig.startJam) Jam.start(); if (jamConfig.startNetwork) Network.start(); if (jamConfig.loadAgents.length) { for (var i in jamConfig.loadAgents) { var file=jamConfig.loadAgents[i]; loadClassFromFile(cordova.file.dataDirectory, file, function (aclass, err) { if (!aclass) return pb.error('Compiling agent from file '+file+' failed.'); if (jamConfig.startAgents[aclass]) { createAgent(aclass,jamConfig.startAgents[aclass]); } else if (jamConfig.startAgents.indexOf && jamConfig.startAgents.indexOf(aclass)>=0) createAgent(aclass,{}); }); } } if (next) next() } // Prepare and setup files (transfer files to app data directory) function init (next) { var resPath = cordova.file.applicationDirectory+(App.platform!='node-webkit'?'/www':'')+'/js'; function setupConfig(next) { checkIfFileExists(cordova.file.dataDirectory,'config.js', function (exists) { if (exists) loadConfig(function (res) { next() }); else saveConfig(function (res) { log('Saved config.js in App data storage'); next() }); }) } function setupAgents(next) { if (jamConfig.loadAgents) { jamConfig.loadAgents.forEach(function (file) { Schedule([ function (next) { checkIfFileExists(cordova.file.dataDirectory,file, function (exists) { if (exists) next (); else readFile(resPath,file, function (data) { writeFile(cordova.file.dataDirectory,file,data, function () { log('Saved '+file+' in App data storage'); next() }, function () { next()}) }, function (err) { next () }) }) } ]) }); } next(); } Schedule([setupConfig,setupAgents]); next(); } Schedule([init,start]); log('Loading APP GUI...'); App.load('home');