///////////////////////////////////////////////////////////////////////////////
/*
alamode.js
COPYRIGHT
---------
Copyright (C) 2014-2024 Mark G.Daniel
This program, comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under the
conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version.
http://www.gnu.org/licenses/gpl.txt
VERSION
-------
30-JAN-2024 MGD v12.2.0
24-NOV-2020 MGD v12.0.0
28-OCT-2018 MGD v11.3.0
03-MAR-2018 MGD v11.2.a incorporate (instance) status report
11-AUG-2017 MGD v11.1.c
27-JUL-2017 MGD v11.1.b
06-MAY-2017 MGD v11.1.a
10-APR-2016 MGD v11.0.0 incorporate HTTP/2 and WASD v11.0
02-AUG-2015 MGD v1.1.0
02-FEB-2014 MGD initial
*/
///////////////////////////////////////////////////////////////////////////////
// versions of MONDESI.EXE this JavaScript is compatible with
var alam_AcmeVersions = ['1.0.0','1.0.1'];
var alam_ExeVersions = ['12.2.a'];
var alam_UsedAlertPercent = 85;
var alam_ThousandComma = ',';
var alam_ColorConnected = 'black'
var alam_ColorDisconnected = 'red'
var alam_ResourcePath = '/alamode/-/';
var alam_LoadedFrom = location.protocol + location.host;
var alam_AcmeIncompatible = 'JavaScript and Acme module incompatible!\n' +
'The multiple sites have cannot interwork.'
var alam_ExeIncompatible = 'JavaScript and executable are incompatible!\n' +
'Reload and/or clear browser cache.';
var alam_Running = true;
var alam_BarHeight = 16,
alam_BarWidth = 300,
alam_BarLabelWidth = 60;
var alam_HistHeight = 100,
alam_HistWidth5 = 300,
alam_HistWidth15 = 920;
var alam_DetailData,
alam_InstanceData,
alam_ProxyData,
alam_RequestData,
alam_ScriptData,
alam_StaticData,
alam_StatusData,
alam_SummaryData,
alam_WebDavData;
///////////////////////////////////////////////////////////////////////////////
////////////////////////////
// general infrastructure //
////////////////////////////
///////////////////////////////////////////////////////////////////////////////
var alam_MSIEversion = 0;
if (navigator.appName == 'Microsoft Internet Explorer')
{
var ua = navigator.userAgent;
var re = new RegExp('MSIE ([0-9]{1,}[\.0-9]{0,})');
if (re.exec(ua) != null) alam_MSIEversion = parseFloat(RegExp.$1);
}
// return a percentage string
function alamPercent(val,of)
{
if (!of) return '0%';
return (val * 100 / of).toFixed(0) + '%';
}
// return a parenthsized percentage string
function alamParenPercent(val,of)
{
if (!of) return '(0%)';
return '(' + (val * 100 / of).toFixed(0) + '%)';
}
// return a parenthsized percentage string separated by two spaces
function alamSpacedPercent(val,of)
{
if (!val || !of) return '';
return ' (' + (val * 100 / of).toFixed(0) + '%)';
}
///////////////////////////////////////////////////////////////////////////////
/*
Quick and dirty "query string" parser. Usage:
var thisParam = alamFromQuery('this_param');
var thatParam = alamFromQuery('that_param');
It returns a corresponding element from the request query string or falls back
to a query string supplied by the executable in logical name "ALAMODE_QUERY".
*/
function alamFromQuery (element)
{
if (!this.qs) this.qs = [];
if (!this.qs.length)
{
var ls = location.search;
ls = ls.substring(ls.indexOf('?')+1).split('&');
for (var idx = 0; idx < ls.length; idx++)
this.qs [ls[idx].split('=')[0]] =
decodeURIComponent(ls[idx].split('=')[1]);
}
if (this.qs[element]) return (this.qs[element]);
return (alamFromConfig(element));
}
///////////////////////////////////////////////////////////////////////////////
/*
Same as alamFromQuery() only different.
It uses a "query string" derived from executable static data "ALAMODE_QUERY".
*/
function alamFromConfig (element)
{
if (!this.qs) this.qs = [];
if (!alam_StaticData) return null;
if (!alam_StaticData.ALAMODE_QUERY) return null;
if (!this.qs.length)
{
var sd = alam_StaticData.ALAMODE_QUERY;
if (sd.indexOf('?') >= 0) sd = sd.substring(sd.indexOf('?')+1);
sd = sd.split('&');
for (var idx = 0; idx < sd.length; idx++)
this.qs [sd[idx].split('=')[0]] = sd[idx].split('=')[1];
}
return (this.qs[element]);
}
///////////////////////////////////////////////////////////////////////////////
/*
Dynamically modify a stylesheet.
*/
function alamCSS (selector, property, value)
{
for (var idx = 0; idx < document.styleSheets.length; idx++)
{
var sheet = document.styleSheets[idx];
try {
var rule = selector + '{' + property + ':' + value + ';}';
sheet.insertRule(rule,sheet.cssRules.length);
}
catch(err) {
try {
sheet.addRule(selector,property+':'+value);
}
catch(err) {}
}
}
}
///////////////////////////////////////////////////////////////////////////////
/////////
// IPC //
/////////
///////////////////////////////////////////////////////////////////////////////
var alam_StreamingData = false;
function alamStreamData (start)
{
// close any existing connections
alam_StreamingData = false;
acmeIpcClose();
if (!start) return;
if (acmeIpcOpen())
{
alam_StreamingData = true;
return;
}
}
///////////////////////////////////////////////////////////////////////////////
/*
Receive JSON data from the executable.
*/
function alamReceiveData (data)
{
data = JSON.parse(data);
if (data.$data == 'alert')
alamAlert(data.time,data.message);
else
if (data.$data == 'detail')
{
alam_DetailData = data;
if (alam_UpdateDisplay && alam_DetailDisplay) alamDetailData();
}
else
if (data.$data == 'instance')
{
alam_InstanceData = data;
if (alam_UpdateDisplay && alam_InstanceDisplay) alamInstanceData();
}
else
if (data.$data == 'proxy')
{
alam_ProxyData = data;
if (alam_UpdateDisplay && alam_ProxyDisplay) alamProxyData();
}
else
if (data.$data == 'request')
{
alam_RequestData = data;
if (alam_UpdateDisplay && alam_RequestDisplay) alamRequestData();
}
else
if (data.$data == 'script')
{
alam_ScriptData = data;
if (alam_UpdateDisplay && alam_ScriptDisplay) alamScriptData();
}
else
if (data.$data == 'static')
{
alam_StaticData = data;
// and add the node to the window title
var json = '{"$AddToTitle":true,"node":"' +
alam_StaticData.nodeName + '"}';
window.parent.postMessage(json,'*');
if (alam_StaticData.proxyEnabled)
$byId('checkboxProxy').disabled = null;
if (alam_StaticData.webdavEnabled)
$byId('checkboxWebDav').disabled = null;
if (alam_StaticData.statusReport)
$byId('checkboxStatus').disabled = null;
}
else
if (data.$data == 'status')
{
alam_StatusData = data;
if (alam_UpdateDisplay && alam_StatusDisplay) alamStatusData();
}
else
if (data.$data == 'summary')
{
alam_SummaryData = data;
alamStoreData(data);
if (alam_UpdateDisplay) alamGraphSummary();
}
else
if (data.$data == 'webdav')
{
alam_WebDavData = data;
if (alam_UpdateDisplay && alam_WebDavDisplay) alamWebDavData();
}
else
if (data.$data != 'runtime')
{
alert('DATA ERROR: ' + data.$data);
console.log(data);
}
if (alam_InsightDisplay) alamDisplayInsight(data);
}
///////////////////////////////////////////////////////////////////////////////
/*
Append this message to the alert section in the node summary area.
*/
var alam_AlertMonth = [ "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC" ];
function alam_AlertZero (num) { if (num < 10) return '0' + num; return num; }
function alamAlert (time,msg)
{
if (typeof msg == 'undefined')
{
msg = time;
time = new Date().getTime();
var date = new Date();
time = alam_AlertZero(date.getDate()) + '-' +
alam_AlertMonth[date.getMonth()] + '-' +
date.getFullYear() + ' ' +
alam_AlertZero(date.getHours()) + ':' +
alam_AlertZero(date.getMinutes()) + ':' +
alam_AlertZero(date.getSeconds());
}
var alert = $byId('alert');
var closedAlert = $byId('closedAlert');
if (typeof msg == 'string')
{
if (alam_AlertsDisplay)
{
if (msg.substr(0,1) == '!')
var html = '' + time +
' ' + msg.substr(1) + '';
else
var html = '' + time +
' ' + msg.substr(1) + '';
if (alert.innerHTML) alert.innerHTML += '
';
alert.innerHTML += html;
closedAlert.innerHTML = html;
alert.onclick = alamAlert;
}
}
else
alert.innerHTML = closedAlert.innerHTML = '';
acmeAdjustSize();
}
///////////////////////////////////////////////////////////////////////////////
////////////////
// data store //
////////////////
///////////////////////////////////////////////////////////////////////////////
var alam_DynamicStore = [];
var alam_StoreSeconds = 0;
var alam_StoreTimeStamp = 0;
function alamStoreData (data)
{
var seconds = Date.now() / 1000;
if (seconds - alam_StoreTimeStamp > alam_StoreSeconds) alamZeroClick();
alam_StoreTimeStamp = seconds;
var store = alam_DynamicStore;
store.push(data);
/* periodically remove old data */
if (store.length > (alam_StoreSeconds/alam_StaticData.interval)+4)
store.splice(0,3);
}
// convenience routine to return size of store
function alamStoreSize ()
{
return alam_DynamicStore.length;
}
function alamStoreSeconds (secs)
{
if (alam_StoreSeconds < secs) alam_StoreSeconds = secs;
}
///////////////////////////////////////////////////////////////////////////////
/*
Return a datum from a data store (if the datum does not exist return zero) by
default from the array of summary data if the first parameter is a reference to
a specific store than from that.
*/
function alamGetDataWithCommas(par1,datum)
{
return alamWithCommas(alamGetData(par1,datum));
}
function alamGetData(datum,store,idx,retval)
{
if (typeof retval == 'undefined') retval = 0;
if (typeof store == 'undefined') store = alam_DynamicStore;
if (store == null) store = alam_DynamicStore;
if (!(store.constructor === Array))
{
// not a (array of) data store
if (typeof store[datum] == 'undefined')
return retval;
else
return store[datum];
}
// if not specified get the most recent
if (typeof idx == 'undefined' || idx == -1) idx = store.length - 1;
// if no datum supplied return a reference to the array element
if (typeof datum == 'undefined') return (store[idx]);
if (typeof store == 'undefined' ||
typeof store[idx] == 'undefined' ||
typeof store[idx][datum] == 'undefined')
return retval;
else
return store[idx][datum];
}
///////////////////////////////////////////////////////////////////////////////
/*
Function name says it all!
*/
function alamZeroClick ()
{
alam_ScriptData = null;
alam_SummaryData = null;
alam_WebDavData = null;
alam_DynamicStore = [];
alamCollectFor (true);
if (alam_UpdateDisplay) alamGraphSummary();
if (alam_ScriptDisplay) alamScriptData();
if (alam_WebDavDisplay) alamWebDavData();
}
///////////////////////////////////////////////////////////////////////////////
/*
This IPC has connected browser to server script.
*/
function alamOnOpen ()
{
$byId('sumNodeName').style.color = alam_ColorConnected;
$byId('monitorClosed').style.color = alam_ColorConnected;
if ($WebSocket) alamTheseData();
}
///////////////////////////////////////////////////////////////////////////////
/*
For CGI (non-WebSocket) access delay the indication to accomodate the periodic
expected long-push disconnect/reconnects.
*/
function alamOnClose (indicate)
{
if (acmeIpcConnected()) return;
if ($WebSocket) indicate = true;
if (indicate)
{
$byId('sumNodeName').style.color = alam_ColorDisconnected;
$byId('monitorClosed').style.color = alam_ColorDisconnected;
}
else
{
// delay a little for re-request
var disc = "$byId('sumNodeName').style.color = \
$byId('monitorClosed').style.color = alam_ColorDisconnected;";
alam_OnCloseTimer = setTimeout(disc,alam_StaticData.interval*2000);
}
}
///////////////////////////////////////////////////////////////////////////////
////////////////
// build page //
////////////////
///////////////////////////////////////////////////////////////////////////////
function $alamode ()
{
acmeIpcOnOpen(alamOnOpen);
acmeIpcOnClose(alamOnClose);
acmeIpcOnMessage(alamReceiveData);
acmeLoadFile('alamode.css');
acmeLoadFile('graph.js');
acmeLoadFile('display.js');
acmeLoadFile('build.js','alamBuildPage()');
}
if (alam_AcmeVersions.indexOf($AcmeVersion) == -1)
{
alert(alam_AcmeIncompatible);
throw new Error(alam_AcmeIncompatible);
}
if (alam_ExeVersions.indexOf($ExeVersion) == -1)
{
alert(alam_ExeIncompatible);
throw new Error(alam_ExeIncompatible);
}
///////////////////////////////////////////////////////////////////////////////