/////////////////////////////////////////////////////////////////////////////// /* alamode.js COPYRIGHT --------- Copyright (C) 2014-2020 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 ------- 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.0.a','12.0.b']; 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); } ///////////////////////////////////////////////////////////////////////////////