jam/js/ui/cordova/www/js/jamui.js

1215 lines
42 KiB
JavaScript

/**
** ==============================
** 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');