From 198f12a876352b1e0e940dde770a4f1dbb005025 Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Sat, 13 Oct 2018 23:59:00 -0400 Subject: [PATCH 01/13] dev-ds/spring-fixes: add a .nvmrc file to specify version of node to use --- build/.nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/.nvmrc diff --git a/build/.nvmrc b/build/.nvmrc new file mode 100644 index 00000000..b0d756fd --- /dev/null +++ b/build/.nvmrc @@ -0,0 +1 @@ +v10.9.0 From fdab927119854a4b88e91204e2973a9a560ebd72 Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Thu, 3 Jan 2019 06:51:18 -0500 Subject: [PATCH 02/13] dev-ds/offline-view: try cache manifest hacks --- build/app/assets/index.ejs | 2 +- build/app/assets/netcreate.cache | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 build/app/assets/netcreate.cache diff --git a/build/app/assets/index.ejs b/build/app/assets/index.ejs index 0210f848..0fbb45a8 100644 --- a/build/app/assets/index.ejs +++ b/build/app/assets/index.ejs @@ -1,5 +1,5 @@ - + NetCreate diff --git a/build/app/assets/netcreate.cache b/build/app/assets/netcreate.cache new file mode 100644 index 00000000..043d97d3 --- /dev/null +++ b/build/app/assets/netcreate.cache @@ -0,0 +1,6 @@ +CACHE MANIFEST +# version 1.0 +scripts/netc-lib.js +scripts/netc-app.js +styles/netc-app.css +templates/alexander.json \ No newline at end of file From af955ffac8d1de7cb2577cc0299d252d42fb4c39 Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Thu, 3 Jan 2019 07:32:19 -0500 Subject: [PATCH 03/13] dev-ds/offline-view: add M_OFFLINE state to client-network.js error handler, which set NetMessage OFFLINE mode --- build/app/assets/netcreate.cache | 7 +- build/app/unisys/client-network.js | 380 ++++++++------ build/app/unisys/common-netmessage-class.js | 547 ++++++++++++-------- 3 files changed, 545 insertions(+), 389 deletions(-) diff --git a/build/app/assets/netcreate.cache b/build/app/assets/netcreate.cache index 043d97d3..f6ee2612 100644 --- a/build/app/assets/netcreate.cache +++ b/build/app/assets/netcreate.cache @@ -1,6 +1,9 @@ CACHE MANIFEST -# version 1.0 +# see https://www.html5rocks.com/en/tutorials/appcache/beginner/ +# note that HTML file with the manifest reference is automatically cached scripts/netc-lib.js scripts/netc-app.js styles/netc-app.css -templates/alexander.json \ No newline at end of file +templates/alexander.json +# will be auto generated in the future +# 006 diff --git a/build/app/unisys/client-network.js b/build/app/unisys/client-network.js index 7cfee410..8df6c8a0 100644 --- a/build/app/unisys/client-network.js +++ b/build/app/unisys/client-network.js @@ -6,211 +6,267 @@ if (window.NC_DBG) console.log(`inc ${module.id}`); \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ -const DBG = { connect:false, handle:false }; +const DBG = { connect: false, handle: false }; /// LOAD LIBRARIES //////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const SETTINGS = require('settings'); -const NetMessage = require('unisys/common-netmessage-class'); -const PROMPTS = require('system/util/prompts'); -const PR = PROMPTS.Pad('NETWORK'); -const WARN = PROMPTS.Pad('!!!'); -const ERR_NM_REQ = "arg1 must be NetMessage instance"; +const SETTINGS = require("settings"); +const NetMessage = require("unisys/common-netmessage-class"); +const PROMPTS = require("system/util/prompts"); +const PR = PROMPTS.Pad("NETWORK"); +const WARN = PROMPTS.Pad("!!!"); +const ERR_NM_REQ = "arg1 must be NetMessage instance"; const ERR_NO_SOCKET = "Network socket has not been established yet"; const ERR_BAD_UDATA = "An instance of 'client-datalink-class' is required"; /// GLOBAL NETWORK INFO (INJECTED ON INDEX) /////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -var NETSOCK = SETTINGS.EJSProp('socket'); -var NETCLIENT = SETTINGS.EJSProp('client'); -var NETSERVER = SETTINGS.EJSProp('server'); +var NETSOCK = SETTINGS.EJSProp("socket"); +var NETCLIENT = SETTINGS.EJSProp("client"); +var NETSERVER = SETTINGS.EJSProp("server"); /// NETWORK ID VALUES ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const M0_INIT = 0; +const M0_INIT = 0; const M1_CONNECTING = 1; -const M2_CONNECTED = 2; +const M2_CONNECTED = 2; const M3_REGISTERED = 3; -const M4_READY = 4; -var m_status = M0_INIT; -var m_options = {}; +const M4_READY = 4; +const M_OFFLINE = 5; +const M_NOCONNECT = 6; +var m_status = M0_INIT; +var m_options = {}; /// API METHODS /////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -var NETWORK = {}; -var UDATA = null; // assigned during NETWORK.Connect() +var NETWORK = {}; +var UDATA = null; // assigned during NETWORK.Connect() /// CONNECT /////////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Establish connection to UNISYS server. This is called by client.js during NetworkInitialize(), which itself fires after the application has rendered completely. -/*/ NETWORK.Connect = function ( datalink, opt ) { - // if multiple network connections occur, emit warning - // warning: don't modify this unless you have a deep knowledge of how - // the webapp system works or you might break something - if (m_status>0) { - let err = 'called twice...other views may be calling UNISYS outside of lifecycle'; - console.error(WARN,err); - return; - } - m_status = M1_CONNECTING; +/*/ +NETWORK.Connect = function(datalink, opt) { + // if multiple network connections occur, emit warning + // warning: don't modify this unless you have a deep knowledge of how + // the webapp system works or you might break something + if (m_status > 0) { + let err = + "called twice...other views may be calling UNISYS outside of lifecycle"; + console.error(WARN, err); + return; + } + m_status = M1_CONNECTING; - // check and save parms - if (datalink.constructor.name!=='UnisysDataLink') throw Error(ERR_BAD_UDATA); - if (!UDATA) UDATA = datalink; - m_options = opt || {}; + // check and save parms + if (datalink.constructor.name !== "UnisysDataLink") { + throw Error(ERR_BAD_UDATA); + } + if (!UDATA) UDATA = datalink; + m_options = opt || {}; - // create websocket - // uses values that were embedded in index.ejs on load - let wsURI = `ws://${NETSOCK.uaddr}:${NETSOCK.uport}`; - NETSOCK.ws = new WebSocket(wsURI); - if (DBG.connect) console.log(PR,'OPEN SOCKET TO',wsURI); + // create websocket + // uses values that were embedded in index.ejs on load + let wsURI = `ws://${NETSOCK.uaddr}:${NETSOCK.uport}`; + NETSOCK.ws = new WebSocket(wsURI); + if (DBG.connect) console.log(PR, "OPEN SOCKET TO", wsURI); + + // create listeners + NETWORK.AddListener("open", function(event) { + if (DBG.connect) console.log(PR, "..OPEN", event.target.url); + m_status = M2_CONNECTED; + // message handling continues in 'message' handler + // the first message is assumed to be registration data + }); + NETWORK.AddListener("close", function(event) { + if (DBG.connect) console.log(PR, "..CLOSE", event.target.url); + m_status = M0_INIT; + }); + // handle socket errors + NETWORK.AddListener("error", function(event) { + /*/ DSHACK: For Spring 2019, adding manifest support to try to + avoid rewriting the app with service workers + /*/ + let appCache = window.applicationCache; + switch (appCache.status) { + case appCache.UNCACHED: + // this occurs if there is not a cached page + console.warn(WARN, "ERROR opening command socket", event); + throw Error("error with command socket"); + break; + case appCache.IDLE: /* falls-through */ + case appCache.CHECKING: /* falls-through */ + case appCache.DOWNLOADING: /* falls-through */ + case appCache.UPDATEREADY: /* falls-through */ + case appCache.OBSOLETE: + // this occurs + console.info(WARN, "OFFLINE MODE. USING CACHED DATA"); + m_status = M_OFFLINE; + NetMessage.GlobalOfflineMode(); // deregister socket + break; + default: + m_status = M_NOCONNECT; + throw Error("unknown appcache status. dumping", appCache); + break; + } + }); + // handle incoming messages + NETWORK.AddListener("message", m_HandleRegistrationMessage); +}; // Connect() - // create listeners - NETWORK.AddListener('open', function( event ) { - if (DBG.connect) console.log(PR,'..OPEN',event.target.url); - m_status = M2_CONNECTED; - // message handling continues in 'message' handler - // the first message is assumed to be registration data - }); - NETWORK.AddListener('close', function( event ) { - if (DBG.connect) console.log(PR,'..CLOSE',event.target.url); - m_status = M0_INIT; - }); - // handle socket errors - NETWORK.AddListener('error', function( event ) { - console.warn(WARN,'ERROR opening command socket',event); - throw ("error with command socket"); - }); - // handle incoming messages - NETWORK.AddListener('message', m_HandleRegistrationMessage); - }; // Connect() /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ After 'open' event, we expect the first message on the socket to contain network session-related messages -/*/ function m_HandleRegistrationMessage( msgEvent ) { - let regData = JSON.parse(msgEvent.data); - let { HELLO, UADDR } = regData; - // (1) after receiving the initial message, switch over to regular - // message handler - NETWORK.RemoveListener('message', m_HandleRegistrationMessage); - m_status = M3_REGISTERED; - // (2) initialize global settings for netmessage - if (DBG.connect) console.log(PR,`connected to ${UADDR}`,NETSOCK); - NETSOCK.ws.UADDR = NetMessage.DefaultServerUADDR(); - NetMessage.GlobalSetup({ uaddr : UADDR, netsocket : NETSOCK.ws}); - // (3) connect regular message handler - NETWORK.AddListener('message', m_HandleMessage); - m_status = M4_READY; - // (4) network is initialized - if (typeof m_options.success==='function') m_options.success(); - } +/*/ +function m_HandleRegistrationMessage(msgEvent) { + let regData = JSON.parse(msgEvent.data); + let { HELLO, UADDR } = regData; + // (1) after receiving the initial message, switch over to regular + // message handler + NETWORK.RemoveListener("message", m_HandleRegistrationMessage); + m_status = M3_REGISTERED; + // (2) initialize global settings for netmessage + if (DBG.connect) console.log(PR, `connected to ${UADDR}`, NETSOCK); + NETSOCK.ws.UADDR = NetMessage.DefaultServerUADDR(); + NetMessage.GlobalSetup({ uaddr: UADDR, netsocket: NETSOCK.ws }); + // (3) connect regular message handler + NETWORK.AddListener("message", m_HandleMessage); + m_status = M4_READY; + // (4) network is initialized + if (typeof m_options.success === "function") m_options.success(); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function m_HandleMessage( msgEvent ) { - let pkt = new NetMessage(msgEvent.data); - let msg = pkt.Message(); - if (pkt.IsOwnResponse()) { - if (DBG.handle) console.log(PR,'completing transaction',msg); - pkt.CompleteTransaction(); - return; +function m_HandleMessage(msgEvent) { + let pkt = new NetMessage(msgEvent.data); + let msg = pkt.Message(); + if (pkt.IsOwnResponse()) { + if (DBG.handle) console.log(PR, "completing transaction", msg); + pkt.CompleteTransaction(); + return; + } + let data = pkt.Data(); + let type = pkt.Type(); + let dbgout = DBG.handle && !msg.startsWith("SRV_"); + /// otherwise, incoming invocation from network + switch (type) { + case "state": + if (dbgout) console.log(PR, "received state change", msg); + break; + case "msig": + if (dbgout) { + console.warn( + PR, + `ME_${NetMessage.SocketUADDR()} received msig '${msg}' from ${pkt.SourceAddress()}`, + data + ); } - let data = pkt.Data(); - let type = pkt.Type(); - let dbgout = DBG.handle && !msg.startsWith('SRV_'); - /// otherwise, incoming invocation from network - switch (type) { - case 'state': - if (dbgout) console.log(PR,'received state change',msg); - break; - case 'msig': - if (dbgout) console.warn(PR,`ME_${NetMessage.SocketUADDR()} received msig '${msg}' from ${pkt.SourceAddress()}`,data); - UDATA.LocalSignal(msg,data); - pkt.ReturnTransaction(); - break; - case 'msend': - if (dbgout) console.warn(PR,`ME_${NetMessage.SocketUADDR()} received msend '${msg}' from ${pkt.SourceAddress()}`,data); - UDATA.LocalSend(msg,data); - pkt.ReturnTransaction(); - break; - case 'mcall': - if (dbgout) console.warn(PR,`ME_${NetMessage.SocketUADDR()} received mcall '${msg}' from ${pkt.SourceAddress()}`); - UDATA.LocalCall(msg,data) - .then((result)=>{ - if (dbgout) console.log(`ME_${NetMessage.SocketUADDR()} forwarded '${msg}', returning ${JSON.stringify(result)}`); - // now return the packet - pkt.SetData(result); - pkt.ReturnTransaction(); - }); - break; - default: - throw Error('unknown packet type',type); + UDATA.LocalSignal(msg, data); + pkt.ReturnTransaction(); + break; + case "msend": + if (dbgout) { + console.warn( + PR, + `ME_${NetMessage.SocketUADDR()} received msend '${msg}' from ${pkt.SourceAddress()}`, + data + ); } - } + UDATA.LocalSend(msg, data); + pkt.ReturnTransaction(); + break; + case "mcall": + if (dbgout) { + console.warn( + PR, + `ME_${NetMessage.SocketUADDR()} received mcall '${msg}' from ${pkt.SourceAddress()}` + ); + } + UDATA.LocalCall(msg, data).then(result => { + if (dbgout) { + console.log( + `ME_${NetMessage.SocketUADDR()} forwarded '${msg}', returning ${JSON.stringify( + result + )}` + ); + } + // now return the packet + pkt.SetData(result); + pkt.ReturnTransaction(); + }); + break; + default: + throw Error("unknown packet type", type); + } +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Send a packet on socket connection, assuming it is valid -/*/ NETWORK.Send = function ( pkt ) { - if (!(pkt instanceof NetMessage)) throw Error(ERR_NM_REQ); - if (NETSOCK.ws.readyState===1) { - let json = pkt.JSON(); - if (DBG) console.log('SENDING',pkt.Message(),pkt.Data(),pkt.SeqNum()); - NETSOCK.ws.send(json); - } else { - console.log('Socket not ReadyState 1, is',NETSOCK.ws.readyState); - } - }; +/*/ +NETWORK.Send = function(pkt) { + if (!(pkt instanceof NetMessage)) throw Error(ERR_NM_REQ); + if (NETSOCK.ws.readyState === 1) { + let json = pkt.JSON(); + if (DBG) console.log("SENDING", pkt.Message(), pkt.Data(), pkt.SeqNum()); + NETSOCK.ws.send(json); + } else { + console.log("Socket not ReadyState 1, is", NETSOCK.ws.readyState); + } +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Send a packet on socket connection, return Promise -/*/ NETWORK.Call = function ( pkt ) { - if (!(pkt instanceof NetMessage)) throw Error(ERR_NM_REQ); - if (NETSOCK.ws.readyState===1) { - let json = pkt.JSON(); - if (DBG) console.log('CALLING',pkt.Message(),json); - NETSOCK.ws.send(json); - } else { - console.log('Socket not ReadyState 1, is',NETSOCK.ws.readyState); - } - }; +/*/ +NETWORK.Call = function(pkt) { + if (!(pkt instanceof NetMessage)) throw Error(ERR_NM_REQ); + if (NETSOCK.ws.readyState === 1) { + let json = pkt.JSON(); + if (DBG) console.log("CALLING", pkt.Message(), json); + NETSOCK.ws.send(json); + } else { + console.log("Socket not ReadyState 1, is", NETSOCK.ws.readyState); + } +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Force close of connection, for example if UNISYS.AppReady() fails -/*/ NETWORK.Close = function ( code, reason ) { - code = code || 1000; - reason = reason || 'unisys forced close'; - NETSOCK.ws.close(code,reason); - }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.AddListener = function ( event, handlerFunction ) { - if (NETSOCK.ws instanceof WebSocket) { - NETSOCK.ws.addEventListener(event, handlerFunction); - } else { - throw Error(ERR_NO_SOCKET); - } - } +/*/ +NETWORK.Close = function(code, reason) { + code = code || 1000; + reason = reason || "unisys forced close"; + NETSOCK.ws.close(code, reason); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.RemoveListener = function ( event, handlerFunction ) { - if (NETSOCK.ws instanceof WebSocket) { - NETSOCK.ws.removeEventListener(event, handlerFunction); - } else { - throw Error(ERR_NO_SOCKET); - } - } +NETWORK.AddListener = function(event, handlerFunction) { + if (NETSOCK.ws instanceof WebSocket) { + NETSOCK.ws.addEventListener(event, handlerFunction); + } else { + throw Error(ERR_NO_SOCKET); + } +}; +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +NETWORK.RemoveListener = function(event, handlerFunction) { + if (NETSOCK.ws instanceof WebSocket) { + NETSOCK.ws.removeEventListener(event, handlerFunction); + } else { + throw Error(ERR_NO_SOCKET); + } +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.LocalInfo = function () { - return NETCLIENT; - }; +NETWORK.LocalInfo = function() { + return NETCLIENT; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.ServerInfo = function () { - return NETSERVER; - }; +NETWORK.ServerInfo = function() { + return NETSERVER; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.ServerSocketInfo = function () { - return NETSOCK; - }; +NETWORK.ServerSocketInfo = function() { + return NETSOCK; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.SocketUADDR = function () { - return NetMessage.SocketUADDR(); - }; +NETWORK.SocketUADDR = function() { + return NetMessage.SocketUADDR(); +}; /// EXPORT MODULE DEFINITION ////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/app/unisys/common-netmessage-class.js b/build/app/unisys/common-netmessage-class.js index 8d845659..2648c311 100644 --- a/build/app/unisys/common-netmessage-class.js +++ b/build/app/unisys/common-netmessage-class.js @@ -19,325 +19,422 @@ //////////////////////////////////////////////////////////////////////////////// /** MODULE DECLARATIONS *******************************************************/ - const DBG = { send:false, transact:false }; +const DBG = { send: false, transact: false }; - var m_id_counter = 0; - var m_id_prefix = 'PKT'; - var m_transactions = {}; - var m_netsocket = null; - var m_group_id = null; +var m_id_counter = 0; +var m_id_prefix = "PKT"; +var m_transactions = {}; +var m_netsocket = null; +var m_group_id = null; - // constants - const PROMPTS = require('../system/util/prompts'); - const PR = PROMPTS.Pad('PKT'); - const ERR = ":ERR:"; - const ERR_NOT_NETMESG = ERR+PR+"obj does not seem to be a NetMessage"; - const ERR_BAD_PROP = ERR+PR+"property argument must be a string"; - const ERR_ERR_BAD_CSTR = ERR+PR+"constructor args are string, object"; - const ERR_BAD_SOCKET = ERR+PR+"sender object must implement send()"; - const ERR_DUPE_TRANS = ERR+PR+"this packet transaction is already registered!"; - const ERR_NO_GLOB_UADDR = ERR+PR+"packet sending attempted before UADDR is set!"; - const ERR_UNKNOWN_TYPE = ERR+PR+"packet type is unknown:"; - const ERR_NOT_PACKET = ERR+PR+"passed object is not a NetMessage"; - const ERR_UNKNOWN_RMODE = ERR+PR+"packet routine mode is unknown:"; - const KNOWN_TYPES = ['msend','msig','mcall','state']; - const ROUTING_MODE = ['req','res']; +const M_INIT = "init"; +const M_ONLINE = "online"; +const M_OFFLINE = "offline"; +const M_CLOSED = "closed"; +const M_ERROR = "error"; +var m_mode = M_INIT; + +// constants +const PROMPTS = require("../system/util/prompts"); +const PR = PROMPTS.Pad("PKT"); +const ERR = ":ERR:"; +const ERR_NOT_NETMESG = ERR + PR + "obj does not seem to be a NetMessage"; +const ERR_BAD_PROP = ERR + PR + "property argument must be a string"; +const ERR_ERR_BAD_CSTR = ERR + PR + "constructor args are string, object"; +const ERR_BAD_SOCKET = ERR + PR + "sender object must implement send()"; +const ERR_DUPE_TRANS = + ERR + PR + "this packet transaction is already registered!"; +const ERR_NO_GLOB_UADDR = + ERR + PR + "packet sending attempted before UADDR is set!"; +const ERR_UNKNOWN_TYPE = ERR + PR + "packet type is unknown:"; +const ERR_NOT_PACKET = ERR + PR + "passed object is not a NetMessage"; +const ERR_UNKNOWN_RMODE = ERR + PR + "packet routine mode is unknown:"; +const KNOWN_TYPES = ["msend", "msig", "mcall", "state"]; +const ROUTING_MODE = ["req", "res"]; /// UNISYS NETMESSAGE CLASS /////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ A UNetMessage encapsulates a specific message and data payload for sending across the network. -/*/ class NetMessage { - constructor( msg, data, type ) { - // OPTION 1 - // create NetMessage from (generic object) - if ((typeof msg==='object') && (data===undefined)) { - // make sure it has a msg and data obj - if ((typeof msg.msg!=='string')||(typeof msg.data!=='object')) throw ERR_NOT_NETMESG; - // merge properties into this new class instance and return it - Object.assign(this,msg); - m_SeqIncrement(this); - return this; - } - // OPTION 2 - // create NetMessage from JSON-encoded string - if ((typeof msg==='string') && (data===undefined)) { - let obj = JSON.parse(msg); - Object.assign(this,obj); - m_SeqIncrement(this); - return this; - } - // OPTION 3 - // create new NetMessage from scratch (mesg,data) - // unique id for every NetMessage - if (typeof type==='string') m_CheckType(type); - if ((typeof msg!=='string') || (typeof data!=='object')) throw ERR_ERR_BAD_CSTR; - // allow calls with null data by setting to empty object - this.data = data || {}; - this.msg = msg; - // id and debugging memo support - this.id = this.MakeNewID(); - this.rmode = ROUTING_MODE[0]; // is default 't_req' (trans request) - this.type = type || KNOWN_TYPES[0]; // is default 'msend' (no return) - this.memo = ''; - // transaction support - this.seqnum = 0; // positive when part of transaction - this.seqlog = []; // transaction log - // addressing support - this.s_uaddr = NetMessage.SocketUADDR() || null; // first originating uaddr set by SocketSend() - this.s_group = null; // session groupid is set by external module once validated - this.s_uid = null; // first originating UDATA srcUID - // filtering support - } // constructor +/*/ +class NetMessage { + constructor(msg, data, type) { + // OPTION 1 + // create NetMessage from (generic object) + if (typeof msg === "object" && data === undefined) { + // make sure it has a msg and data obj + if (typeof msg.msg !== "string" || typeof msg.data !== "object") { + throw ERR_NOT_NETMESG; + } + // merge properties into this new class instance and return it + Object.assign(this, msg); + m_SeqIncrement(this); + return this; + } + // OPTION 2 + // create NetMessage from JSON-encoded string + if (typeof msg === "string" && data === undefined) { + let obj = JSON.parse(msg); + Object.assign(this, obj); + m_SeqIncrement(this); + return this; + } + // OPTION 3 + // create new NetMessage from scratch (mesg,data) + // unique id for every NetMessage + if (typeof type === "string") m_CheckType(type); + if (typeof msg !== "string" || typeof data !== "object") { + throw ERR_ERR_BAD_CSTR; + } + // allow calls with null data by setting to empty object + this.data = data || {}; + this.msg = msg; + // id and debugging memo support + this.id = this.MakeNewID(); + this.rmode = ROUTING_MODE[0]; // is default 't_req' (trans request) + this.type = type || KNOWN_TYPES[0]; // is default 'msend' (no return) + this.memo = ""; + // transaction support + this.seqnum = 0; // positive when part of transaction + this.seqlog = []; // transaction log + // addressing support + this.s_uaddr = NetMessage.SocketUADDR() || null; // first originating uaddr set by SocketSend() + this.s_group = null; // session groupid is set by external module once validated + this.s_uid = null; // first originating UDATA srcUID + // filtering support + } // constructor /// ACCESSSOR METHODS /////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ returns the type - /*/ Type() { return this.type } + /*/ + Type() { + return this.type; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ returns true if type matches - /*/ IsType( type ) { return this.type===type } + /*/ + IsType(type) { + return this.type === type; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ returns the type - /*/ SetType( type ) { this.type = m_CheckType(type) } + /*/ + SetType(type) { + this.type = m_CheckType(type); + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ returns the message - /*/ Message() { return this.msg; } + /*/ + Message() { + return this.msg; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ sets the message field - /*/ SetMessage( msgstr ) { this.msg = msgstr; } + /*/ + SetMessage(msgstr) { + this.msg = msgstr; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ returns the entire data payload or the property within the data payload (can return undefined if property doesn't exist) - /*/ Data( prop ) { - if (!prop) return this.data; - if (typeof prop==='string') return this.data[prop]; - throw ERR_BAD_PROP; - } + /*/ + Data(prop) { + if (!prop) return this.data; + if (typeof prop === "string") return this.data[prop]; + throw ERR_BAD_PROP; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ convenience method to set data object entirely - /*/ SetData( propOrVal, val ) { - if (typeof propOrVal==='object') { this.data=propOrVal; return } - if (typeof propOrVal==='string') { this.data[propOrVal]=val; return } - throw ERR_BAD_PROP; - } + /*/ + SetData(propOrVal, val) { + if (typeof propOrVal === "object") { + this.data = propOrVal; + return; + } + if (typeof propOrVal === "string") { + this.data[propOrVal] = val; + return; + } + throw ERR_BAD_PROP; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ returns truthy value (this.data) if the passed msgstr matches the message associated with this NetMessage - /*/ Is( msgstr ) { - return (msgstr===this.msg) ? this.data : undefined; - } + /*/ + Is(msgstr) { + return msgstr === this.msg ? this.data : undefined; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ convenience function return true if server message - /*/ IsServerMessage() { - return this.msg.startsWith('SRV_'); - } + /*/ + IsServerMessage() { + return this.msg.startsWith("SRV_"); + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ getter/setter for the memo description field - /*/ Memo() { return this.memo; } - SetMemo( memo ) { this.memo = memo; } + /*/ + Memo() { + return this.memo; + } + SetMemo(memo) { + this.memo = memo; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ convenience function to return JSON version of this object - /*/ JSON() { - return JSON.stringify(this); - } + /*/ + JSON() { + return JSON.stringify(this); + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ return the session groupid (CLASS-PROJ-HASH) that's been set globally - /*/ SourceGroupID() { return this.s_group } + /*/ + SourceGroupID() { + return this.s_group; + } /// TRANSACTION SUPPORT ///////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ The sequence number is positive if this packet is reused - /*/ SeqNum() { - return this.seqnum; - } + /*/ + SeqNum() { + return this.seqnum; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Return the originating address of this netmessage packet. It is valid only after the packet has been sent at least once. - /*/ SourceAddress() { - // is this packet originating from server to a remote? - if (this.s_uaddr===NetMessage.DefaultServerUADDR() && (!this.msg.startsWith('SVR_'))) { - return this.s_uaddr; - } - // this is a regular message forward to remote handlers - return this.IsTransaction() - ? this.seqlog[0] - : this.s_uaddr; - } + /*/ + SourceAddress() { + // is this packet originating from server to a remote? + if ( + this.s_uaddr === NetMessage.DefaultServerUADDR() && + !this.msg.startsWith("SVR_") + ) { + return this.s_uaddr; + } + // this is a regular message forward to remote handlers + return this.IsTransaction() ? this.seqlog[0] : this.s_uaddr; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CopySourceAddress( pkt ) { - if (pkt.constructor.name!=='NetMessage') throw Error(ERR_NOT_PACKET); - this.s_uaddr = pkt.SourceAddress(); - } + CopySourceAddress(pkt) { + if (pkt.constructor.name !== "NetMessage") throw Error(ERR_NOT_PACKET); + this.s_uaddr = pkt.SourceAddress(); + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ return an informational string about the packet useful for logging - /*/ Info( key ) { - switch (key) { - case 'src': /* falls-through */ - default: - return this.SourceGroupID() - ? `${this.SourceAddress()} [${this.SourceGroupID()}]` - : `${this.SourceAddress()}`; - } - } + /*/ Info( + key + ) { + switch (key) { + case "src": /* falls-through */ + default: + return this.SourceGroupID() + ? `${this.SourceAddress()} [${this.SourceGroupID()}]` + : `${this.SourceAddress()}`; + } + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MakeNewID() { - let idStr = (++m_id_counter).toString(); - this.id = m_id_prefix+idStr.padStart(5,'0'); - return this.id; - } + MakeNewID() { + let idStr = (++m_id_counter).toString(); + this.id = m_id_prefix + idStr.padStart(5, "0"); + return this.id; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Send packet on either provided socket or default socket. Servers provide the socket because it's handling multiple sockets from different clients. - /*/ SocketSend( socket=m_netsocket ) { - this.s_group = NetMessage.GlobalGroupID(); - let dst = socket.UADDR || 'unregistered socket'; - if (!socket) throw Error('SocketSend(sock) requires a valid socket'); - if (DBG.send) { - let status = `sending '${this.Message()}' to ${dst}`; - console.log(PR,status); - } - socket.send(this.JSON()); - // FYI: global m_netsocket is not defined on server, since packets arrive on multiple sockets - } + /*/ + SocketSend(socket = m_netsocket) { + this.s_group = NetMessage.GlobalGroupID(); + let dst = socket.UADDR || "unregistered socket"; + if (!socket) throw Error("SocketSend(sock) requires a valid socket"); + if (DBG.send) { + let status = `sending '${this.Message()}' to ${dst}`; + console.log(PR, status); + } + socket.send(this.JSON()); + // FYI: global m_netsocket is not defined on server, since packets arrive on multiple sockets + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Create a promise to resolve when packet returns - /*/ QueueTransaction( socket=m_netsocket ) { - // global m_netsocket is not defined on server, since packets arrive on multiple sockets - if (!socket) throw Error('QueueTransaction(sock) requires a valid socket'); - // save our current UADDR - this.seqlog.push(NetMessage.UADDR); - let dbg = (DBG.transact)&&(!this.IsServerMessage()); - let p = new Promise((resolve,reject) => { - var hash = m_GetHashKey(this); - if (m_transactions[hash]) { - reject(Error(ERR_DUPE_TRANS+':'+hash)); - } else { - // save the resolve function in transactions table; - // promise will resolve on remote invocation with data - m_transactions[hash] = function (data) { - if (dbg) console.log(PR,'resolving promise with',JSON.stringify(data)); - resolve(data); - }; - this.SocketSend(socket); + /*/ + QueueTransaction(socket = m_netsocket) { + // global m_netsocket is not defined on server, since packets arrive on multiple sockets + if (!socket) throw Error("QueueTransaction(sock) requires a valid socket"); + // save our current UADDR + this.seqlog.push(NetMessage.UADDR); + let dbg = DBG.transact && !this.IsServerMessage(); + let p = new Promise((resolve, reject) => { + var hash = m_GetHashKey(this); + if (m_transactions[hash]) { + reject(Error(ERR_DUPE_TRANS + ":" + hash)); + } else { + // save the resolve function in transactions table; + // promise will resolve on remote invocation with data + m_transactions[hash] = function(data) { + if (dbg) { + console.log(PR, "resolving promise with", JSON.stringify(data)); } - }); - return p; + resolve(data); + }; + this.SocketSend(socket); } + }); + return p; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ return the 'routing mode': req/res is request/reply (message requests and optional response) f_req/f_res is forwarded request/reply (forwarded messages and optional return) the f_res is converted to res and sent back to original requester - /*/ RoutingMode() { return this.rmode; } + /*/ + RoutingMode() { + return this.rmode; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IsRequest() { return this.rmode==='req'; } - IsOwnResponse() { return this.rmode==='res'; } + IsRequest() { + return this.rmode === "req"; + } + IsOwnResponse() { + return this.rmode === "res"; + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ If this packet is a returned transaction, then return true - /*/ IsTransaction() { - return (this.rmode!==ROUTING_MODE[0])&&(this.seqnum>0)&&(this.seqlog[0]===NetMessage.UADDR); - } + /*/ + IsTransaction() { + return ( + this.rmode !== ROUTING_MODE[0] && + this.seqnum > 0 && + this.seqlog[0] === NetMessage.UADDR + ); + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ update the sequence metadata and return on same socket - /*/ ReturnTransaction( socket=m_netsocket ) { - // global m_netsocket is not defined on server, since packets arrive on multiple sockets - if (!socket) throw Error('ReturnTransaction(sock) requires a valid socket'); - // note: seqnum is already incremented by the constructor if this was - // a received packet - // add this to the sequence log - this.seqlog.push(NetMessage.UADDR); - this.rmode = m_CheckRMode('res'); - this.SocketSend(socket); - } + /*/ + ReturnTransaction(socket = m_netsocket) { + // global m_netsocket is not defined on server, since packets arrive on multiple sockets + if (!socket) throw Error("ReturnTransaction(sock) requires a valid socket"); + // note: seqnum is already incremented by the constructor if this was + // a received packet + // add this to the sequence log + this.seqlog.push(NetMessage.UADDR); + this.rmode = m_CheckRMode("res"); + this.SocketSend(socket); + } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ If this is a transaction packet that is returned, then execute the stored resolver function from the promise stored in m_transactions, which will then trigger .then() following any calls - /*/ CompleteTransaction() { - let dbg = (DBG.transact) && (!this.IsServerMessage()); - var hash = m_GetHashKey(this); - var resolverFunc = m_transactions[hash]; - if (dbg) console.log(PR,'CompleteTransaction',hash); - if (typeof resolverFunc!=='function') { - throw Error(`transaction [${hash}] resolverFunction is type ${typeof resolverFunc}`); - } else { - resolverFunc(this.data); - Reflect.deleteProperty(m_transactions[hash]); - } - } - } // class NetMessage + /*/ + CompleteTransaction() { + let dbg = DBG.transact && !this.IsServerMessage(); + var hash = m_GetHashKey(this); + var resolverFunc = m_transactions[hash]; + if (dbg) console.log(PR, "CompleteTransaction", hash); + if (typeof resolverFunc !== "function") { + throw Error( + `transaction [${hash}] resolverFunction is type ${typeof resolverFunc}` + ); + } else { + resolverFunc(this.data); + Reflect.deleteProperty(m_transactions[hash]); + } + } +} // class NetMessage /// STATIC CLASS METHODS ////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ set the NETWORK interface object that implements Send() -/*/ NetMessage.GlobalSetup = function( config ) { - let { netsocket, uaddr } = config; - if (uaddr) NetMessage.UADDR = uaddr; - // NOTE: m_netsocket is set only on clients since on server, there are multiple sockets - if (netsocket) { - if (typeof netsocket.send!=='function') throw ERR_BAD_SOCKET; - m_netsocket = netsocket; - } - }; +/*/ +NetMessage.GlobalSetup = function(config) { + let { netsocket, uaddr } = config; + if (uaddr) NetMessage.UADDR = uaddr; + // NOTE: m_netsocket is set only on clients since on server, there are multiple sockets + if (netsocket) { + if (typeof netsocket.send !== "function") throw ERR_BAD_SOCKET; + m_netsocket = netsocket; + m_mode = M_ONLINE; + } +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ cleanup any allocated storage /*/ NetMessage.GlobalCleanup = function() { - if (m_netsocket) { - console.log(PR,'GlobalCleanup: deallocating netsocket'); - m_netsocket = null; - } - } + if (m_netsocket) { + console.log(PR, "GlobalCleanup: deallocating netsocket"); + m_netsocket = null; + m_mode = M_CLOSED; + } +}; +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/*/ cleanup any allocated storage +/*/ +NetMessage.GlobalOfflineMode = function() { + if (m_netsocket) { + console.log(PR, "GlobalDisconnect: deallocating netsocket"); + m_netsocket = null; + m_mode = M_OFFLINE; + } +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ return the address (socket_id) assigned to this app instance -/*/ NetMessage.SocketUADDR = function() { - return NetMessage.UADDR; - } +/*/ +NetMessage.SocketUADDR = function() { + return NetMessage.UADDR; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Return a default server UADDR -/*/ NetMessage.DefaultServerUADDR = function() { - return 'SVR_01'; - } +/*/ +NetMessage.DefaultServerUADDR = function() { + return "SVR_01"; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Return current SessionID string -/*/ NetMessage.GlobalGroupID = function () { - return m_group_id; - } +/*/ +NetMessage.GlobalGroupID = function() { + return m_group_id; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NetMessage.GlobalSetGroupID = function ( token ) { - m_group_id = token; - } +NetMessage.GlobalSetGroupID = function(token) { + m_group_id = token; +}; /// PRIVATE CLASS HELPERS ///////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ when a packet is reconstructed from an existing object or json string, its sequence number is incremented, and the old source uaddr is pushed onto the seqlog stack. -/*/ function m_SeqIncrement( pkt ) { - pkt.seqnum++; - return pkt; - } +/*/ +function m_SeqIncrement(pkt) { + pkt.seqnum++; + return pkt; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ return the hash used for storing transaction callbacks -/*/ function m_GetHashKey( pkt ) { - let hash = `${pkt.SourceAddress()}:${pkt.id}`; - return hash; - } +/*/ +function m_GetHashKey(pkt) { + let hash = `${pkt.SourceAddress()}:${pkt.id}`; + return hash; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ is this an allowed type? throw error if not -/*/ function m_CheckType( type ) { - if (type===undefined) throw new Error('must pass a type string, not '+type); - if (!(KNOWN_TYPES.includes(type))) throw `${ERR_UNKNOWN_TYPE} '${type}'`; - return type; - } +/*/ +function m_CheckType(type) { + if (type === undefined) { + throw new Error("must pass a type string, not " + type); + } + if (!KNOWN_TYPES.includes(type)) throw `${ERR_UNKNOWN_TYPE} '${type}'`; + return type; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ is this an allowed mode? throw error if not -/*/ function m_CheckRMode( mode ) { - if (mode===undefined) throw new Error('must pass a mode string, not '+mode); - if (!(ROUTING_MODE.includes(mode))) throw `${ERR_UNKNOWN_RMODE} '${mode}'`; - return mode; - } +/*/ +function m_CheckRMode(mode) { + if (mode === undefined) { + throw new Error("must pass a mode string, not " + mode); + } + if (!ROUTING_MODE.includes(mode)) throw `${ERR_UNKNOWN_RMODE} '${mode}'`; + return mode; +} /// EXPORT CLASS DEFINITION /////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 2fe44e8536d2940c7d6aefabdd9c6cc6141bf5ac Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Thu, 3 Jan 2019 08:12:52 -0500 Subject: [PATCH 04/13] dev-ds/offline-view: add GlobalOfflineMode() calls from Network to NetMessage to suppress traffic to net --- build/app/unisys/client-network.js | 5 +-- build/app/unisys/common-netmessage-class.js | 35 +++++++++++++++------ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/build/app/unisys/client-network.js b/build/app/unisys/client-network.js index 8df6c8a0..2e77718f 100644 --- a/build/app/unisys/client-network.js +++ b/build/app/unisys/client-network.js @@ -6,7 +6,7 @@ if (window.NC_DBG) console.log(`inc ${module.id}`); \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ -const DBG = { connect: false, handle: false }; +const DBG = { connect: true, handle: false }; /// LOAD LIBRARIES //////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -82,7 +82,8 @@ NETWORK.Connect = function(datalink, opt) { }); NETWORK.AddListener("close", function(event) { if (DBG.connect) console.log(PR, "..CLOSE", event.target.url); - m_status = M0_INIT; + NetMessage.GlobalOfflineMode(); + m_status = M_OFFLINE; }); // handle socket errors NETWORK.AddListener("error", function(event) { diff --git a/build/app/unisys/common-netmessage-class.js b/build/app/unisys/common-netmessage-class.js index 2648c311..aaa6b4b2 100644 --- a/build/app/unisys/common-netmessage-class.js +++ b/build/app/unisys/common-netmessage-class.js @@ -244,14 +244,26 @@ class NetMessage { the socket because it's handling multiple sockets from different clients. /*/ SocketSend(socket = m_netsocket) { - this.s_group = NetMessage.GlobalGroupID(); - let dst = socket.UADDR || "unregistered socket"; - if (!socket) throw Error("SocketSend(sock) requires a valid socket"); - if (DBG.send) { - let status = `sending '${this.Message()}' to ${dst}`; - console.log(PR, status); + if (m_mode === M_ONLINE || m_mode === M_INIT) { + this.s_group = NetMessage.GlobalGroupID(); + let dst = socket.UADDR || "unregistered socket"; + if (!socket) throw Error("SocketSend(sock) requires a valid socket"); + if (DBG.send) { + let status = `sending '${this.Message()}' to ${dst}`; + console.log(PR, status); + } + socket.send(this.JSON()); + } else if (m_mode !== M_OFFLINE) { + console.log( + PR, + "SocketSend: Can't send because NetMessage mode is", + m_mode + ); + } else { + console.log( + "SocketSend(): Network is in offline mode: all network traffic suppressed" + ); } - socket.send(this.JSON()); // FYI: global m_netsocket is not defined on server, since packets arrive on multiple sockets } /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -352,15 +364,18 @@ NetMessage.GlobalSetup = function(config) { // NOTE: m_netsocket is set only on clients since on server, there are multiple sockets if (netsocket) { if (typeof netsocket.send !== "function") throw ERR_BAD_SOCKET; + console.log(PR, "GlobalSetup: netsocket set, mode online"); m_netsocket = netsocket; m_mode = M_ONLINE; } }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ cleanup any allocated storage -/*/ NetMessage.GlobalCleanup = function() { +/*/ + +NetMessage.GlobalCleanup = function() { if (m_netsocket) { - console.log(PR, "GlobalCleanup: deallocating netsocket"); + console.log(PR, "GlobalCleanup: deallocating netsocket, mode closed"); m_netsocket = null; m_mode = M_CLOSED; } @@ -373,6 +388,8 @@ NetMessage.GlobalOfflineMode = function() { console.log(PR, "GlobalDisconnect: deallocating netsocket"); m_netsocket = null; m_mode = M_OFFLINE; + } else { + console.log(PR, "GlobalDisconnect: netsocket already deallocated"); } }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 4c01b3983eea78bcbe5b23e18414120f5e63a8f0 Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Thu, 3 Jan 2019 08:15:35 -0500 Subject: [PATCH 05/13] dev-ds/offline-view: reformat datastore.js whitespace --- build/app/system/datastore.js | 216 +++++++++++++++++----------------- 1 file changed, 110 insertions(+), 106 deletions(-) diff --git a/build/app/system/datastore.js b/build/app/system/datastore.js index c60ce178..8f41155a 100644 --- a/build/app/system/datastore.js +++ b/build/app/system/datastore.js @@ -7,151 +7,155 @@ \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ -const DBG = { load:true }; +const DBG = { load: true }; /// SYSTEM LIBRARIES ////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const SETTINGS = require('settings'); -const SESSION = require('unisys/common-session'); -const UNISYS = require('unisys/client'); -const PROMPTS = require('system/util/prompts'); -const PR = PROMPTS.Pad('Datastore'); -const NetMessage = require('unisys/common-netmessage-class'); +const SETTINGS = require("settings"); +const SESSION = require("unisys/common-session"); +const UNISYS = require("unisys/client"); +const PROMPTS = require("system/util/prompts"); +const PR = PROMPTS.Pad("Datastore"); +const NetMessage = require("unisys/common-netmessage-class"); /// CONSTANTS ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const HASH_ABET = 'ABCDEFGHIJKLMNPQRSTVWXYZ23456789'; +const HASH_ABET = "ABCDEFGHIJKLMNPQRSTVWXYZ23456789"; const HASH_MINLEN = 3; /// INITIALIZE MODULE ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -let DSTOR = UNISYS.NewModule(module.id); -let UDATA = UNISYS.NewDataLink(DSTOR); -let D3DATA = {}; +let DSTOR = UNISYS.NewModule(module.id); +let UDATA = UNISYS.NewDataLink(DSTOR); +let D3DATA = {}; /// LIFECYCLE ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ establish message handlers during INITIALIZE phase -/*/ DSTOR.Hook('INITIALIZE',()=>{ - - UDATA.HandleMessage('DB_UPDATE', function( data ) { - DSTOR.UpdateServerDB(data); - }); - - UDATA.HandleMessage('GROUPID_CHANGE', function( data ) { - DSTOR.SetSessionGroupID(data); - console.log('Handling GROUPID_CHANGE'); - }); - - }); +/*/ +DSTOR.Hook("INITIALIZE", () => { + UDATA.HandleMessage("DB_UPDATE", function(data) { + DSTOR.UpdateServerDB(data); + }); + UDATA.HandleMessage("GROUPID_CHANGE", function(data) { + DSTOR.SetSessionGroupID(data); + console.log("Handling GROUPID_CHANGE"); + }); +}); /// SESSION /////////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ datastore needs to set NetMessage GroupID property on behalf of SESSIONS because SESSION can't include NetMessage (or vice versa) -/*/ DSTOR.SetSessionGroupID = function ( token ) { - let decoded = SESSION.DecodeToken(token); - if (decoded.isValid) { - NetMessage.GlobalSetGroupID(token); - console.log('setting NetMessage group id',token); - } else { - console.warn('will not set bad group id:',token); - } - UDATA.SetAppState('SESSION',decoded); - } +/*/ +DSTOR.SetSessionGroupID = function(token) { + let decoded = SESSION.DecodeToken(token); + if (decoded.isValid) { + NetMessage.GlobalSetGroupID(token); + console.log("setting NetMessage group id", token); + } else { + console.warn("will not set bad group id:", token); + } + UDATA.SetAppState("SESSION", decoded); +}; /// DB INTERFACE ////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: Placeholder DATA access function -/*/ DSTOR.Data = function () { - return D3DATA; - }; +/*/ +DSTOR.Data = function() { + return D3DATA; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: Write update to database -/*/ DSTOR.UpdateServerDB = function( data ) { - UDATA.Call('SRV_DBUPDATE',data) - .then((res)=>{ - if (res.OK) { - console.log(PR,`server db transaction`,data,`success`); - } else { - console.log(PR,'error updating server db',res); - } - }); +/*/ +DSTOR.UpdateServerDB = function(data) { + UDATA.Call("SRV_DBUPDATE", data).then(res => { + if (res.OK) { + console.log(PR, `server db transaction`, data, `success`); + } else { + console.log(PR, "error updating server db", res); } + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ get a unique NodeID -/*/ DSTOR.PromiseNewNodeID = function() { - return new Promise((resolve,reject)=>{ - UDATA.NetCall('SRV_DBGETNODEID') - .then(( data )=>{ - if (data.nodeID) { - if (DBG) console.log(PR,'server allocated node_id',data.nodeID); - resolve(data.nodeID); - } else { - reject(new Error('unknown error'+JSON.stringify(data))); - } - }) - }); - }; +/*/ +DSTOR.PromiseNewNodeID = function() { + return new Promise((resolve, reject) => { + UDATA.NetCall("SRV_DBGETNODEID").then(data => { + if (data.nodeID) { + if (DBG) console.log(PR, "server allocated node_id", data.nodeID); + resolve(data.nodeID); + } else { + reject(new Error("unknown error" + JSON.stringify(data))); + } + }); + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ get a unique Edge -/*/ DSTOR.PromiseNewEdgeID = function() { - return new Promise((resolve,reject)=>{ - UDATA.NetCall('SRV_DBGETEDGEID') - .then(( data )=>{ - if (data.edgeID) { - if (DBG) console.log(PR,'server allocated edge_id:',data.edgeID); - resolve(data.edgeID); - } else { - reject(new Error('unknown error'+JSON.stringify(data))); - } - }) - }); - }; +/*/ +DSTOR.PromiseNewEdgeID = function() { + return new Promise((resolve, reject) => { + UDATA.NetCall("SRV_DBGETEDGEID").then(data => { + if (data.edgeID) { + if (DBG) console.log(PR, "server allocated edge_id:", data.edgeID); + resolve(data.edgeID); + } else { + reject(new Error("unknown error" + JSON.stringify(data))); + } + }); + }); +}; /// DATABASE LOADER /////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: Load default data set from a JSON file in /assets/data -/*/ DSTOR.PromiseJSONFile = function ( jsonFile ) { - if (typeof jsonFile!=='string') throw new Error('pass arg '); - let promise = new Promise((resolve,reject)=>{ - let xobj = new XMLHttpRequest(); - xobj.addEventListener('load',(event)=>{ - if (event.target.status===404) { - reject(new Error(`file not found`)); - return; - } - let data = event.target.responseText; - D3DATA = Object.assign(D3DATA,JSON.parse(data)); - resolve(D3DATA); - }); - xobj.open('GET',`data/${jsonFile}`, true); - xobj.send(); - }); - return promise; - }; +/*/ +DSTOR.PromiseJSONFile = function(jsonFile) { + if (typeof jsonFile !== "string") { + throw new Error("pass arg "); + } + let promise = new Promise((resolve, reject) => { + let xobj = new XMLHttpRequest(); + xobj.addEventListener("load", event => { + if (event.target.status === 404) { + reject(new Error(`file not found`)); + return; + } + let data = event.target.responseText; + D3DATA = Object.assign(D3DATA, JSON.parse(data)); + resolve(D3DATA); + }); + xobj.open("GET", `data/${jsonFile}`, true); + xobj.send(); + }); + return promise; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: Load D3 Database -/*/ DSTOR.PromiseD3Data = function () { - // UDATA.Call() returns a promise - return UDATA.Call('SRV_DBGET',{}); - }; +/*/ +DSTOR.PromiseD3Data = function() { + // UDATA.Call() returns a promise + return UDATA.Call("SRV_DBGET", {}); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: (WIP) write database from d3data-formatted object -/*/ DSTOR.OverwriteDataPromise = function ( d3data ) { - return new Promise((resolve,reject)=>{ - UDATA.Call('SRV_DBSET',d3data) - .then((res)=>{ - if (res.OK) { - console.log(PR,`database set OK`); - resolve(res); - } else { - reject(new Error(JSON.stringify(res))); - } - }); - }); - }; +/*/ +DSTOR.OverwriteDataPromise = function(d3data) { + return new Promise((resolve, reject) => { + UDATA.Call("SRV_DBSET", d3data).then(res => { + if (res.OK) { + console.log(PR, `database set OK`); + resolve(res); + } else { + reject(new Error(JSON.stringify(res))); + } + }); + }); +}; /// EXPORT MODULE ///////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From b76cf3d2241b3456647194a7a8c7b79408d0617f Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Thu, 3 Jan 2019 08:29:42 -0500 Subject: [PATCH 06/13] dev-ds/offline-view: whitespace reformat to experimental code standards --- build/app/view/netcreate/nc-logic.js | 1166 ++++++++++++++------------ 1 file changed, 640 insertions(+), 526 deletions(-) diff --git a/build/app/view/netcreate/nc-logic.js b/build/app/view/netcreate/nc-logic.js index fbb20d59..76d47acc 100644 --- a/build/app/view/netcreate/nc-logic.js +++ b/build/app/view/netcreate/nc-logic.js @@ -35,19 +35,19 @@ \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ -const DBG = false; +const DBG = false; /// LIBRARIES ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const SETTINGS = require('settings'); -const UNISYS = require('unisys/client'); -const JSCLI = require('system/util/jscli'); -const D3 = require('d3'); +const SETTINGS = require("settings"); +const UNISYS = require("unisys/client"); +const JSCLI = require("system/util/jscli"); +const D3 = require("d3"); /// INITIALIZE MODULE ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -var MOD = UNISYS.NewModule(module.id); -var UDATA = UNISYS.NewDataLink(MOD); +var MOD = UNISYS.NewModule(module.id); +var UDATA = UNISYS.NewDataLink(MOD); /// APP STATE/DATA STRUCTURES ///////////////////////////////////////////////// /*/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \*\ @@ -128,137 +128,170 @@ var UDATA = UNISYS.NewDataLink(MOD); * edges: all edges (not all may be actually changed) \*\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/*/ -var D3DATA = null; // see above for description -var TEMPLATE = null; // template definition for prompts -const DATASTORE = require('system/datastore'); -const SESSION = require('unisys/common-session'); -const PROMPTS = require('system/util/prompts'); -const PR = PROMPTS.Pad('NCLOGIC'); +var D3DATA = null; // see above for description +var TEMPLATE = null; // template definition for prompts +const NETWORK = require("unisys/client-network"); +const DATASTORE = require("system/datastore"); +const SESSION = require("unisys/common-session"); +const PROMPTS = require("system/util/prompts"); +const PR = PROMPTS.Pad("NCLOGIC"); /// CONSTANTS ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const DESELECTED_COLOR = ''; -const SEARCH_COLOR = '#008800'; -const SOURCE_COLOR = '#0000DD'; -const TARGET_COLOR = '#FF0000'; +const DESELECTED_COLOR = ""; +const SEARCH_COLOR = "#008800"; +const SOURCE_COLOR = "#0000DD"; +const TARGET_COLOR = "#FF0000"; /// UNISYS LIFECYCLE HOOKS //////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ LOADASSETS fires before react components are loaded see client-lifecycle.js for description -/*/ MOD.Hook('LOADASSETS',()=>{ - // load data into D3DATA - let p1 = DATASTORE.PromiseD3Data() - .then((data)=>{ - if (DBG) console.log(PR,'DATASTORE returned data',data); - m_ConvertData( data ); - m_RecalculateAllEdgeWeights( data ); - UDATA.SetAppState('D3DATA',data); - // Save off local reference because we don't have D3DATA AppStateChange handler - D3DATA = data; - }); - // load Template data and return it as a promise - // so that react render is called only after the template is loaded - let templateURL = '../templates/alexander.json'; - let p2 = DATASTORE.PromiseJSONFile( templateURL ) - .then((data)=>{ - if (DBG) console.log(PR,'DATASTORE returned json',data); - TEMPLATE = data; - UDATA.SetAppState('TEMPLATE',TEMPLATE); - // Process Node, NodeColorMap and Edge options - - // Validate the template file - try { - // nodePrompts - let nodePrompts = TEMPLATE.nodePrompts; - if (nodePrompts===undefined) throw "Missing `nodePrompts` nodePrompts="+nodePrompts; - if (nodePrompts.label===undefined) throw "Missing `nodePrompts.label` label="+nodePrompts.label; - if (nodePrompts.type===undefined) throw "Missing `nodePrompts.type` type= "+nodePrompts.type; - if ( (nodePrompts.type.options===undefined) || - !Array.isArray(nodePrompts.type.options) ) { - throw "Missing or bad `nodePrompts.type.options` options="+nodePrompts.type.options; - } - if (nodePrompts.notes===undefined) throw "Missing `nodePrompts.notes` notes="+nodePrompts.notes; - if (nodePrompts.info===undefined) throw "Missing `nodePrompts.info` info="+nodePrompts.info; - - // edgePrompts - let edgePrompts = TEMPLATE.edgePrompts; - if (edgePrompts===undefined) throw "Missing `edgePrompts` edgePrompts="+edgePrompts; - if (edgePrompts.source===undefined) throw "Missing `edgePrompts.source` source="+edgePrompts.source; - if (edgePrompts.type===undefined) throw "Missing `edgePrompts.type` type= "+edgePrompts.type; - if ( (edgePrompts.type.options===undefined) || - !Array.isArray(edgePrompts.type.options) ) { - throw "Missing or bad `edgePrompts.type.options` options="+edgePrompts.type.options; - } - if (edgePrompts.target===undefined) throw "Missing `edgePrompts.target` label="+edgePrompts.target; - if (edgePrompts.notes===undefined) throw "Missing `edgePrompts.notes` notes="+edgePrompts.notes; - if (edgePrompts.info===undefined) throw "Missing `edgePrompts.info` info="+edgePrompts.info; - if (edgePrompts.citation===undefined) throw "Missing `edgePrompts.citation` info="+edgePrompts.citation; - } catch (error) { - console.error( PR+"Error loading template `",templateURL,"`::::",error ); - } - - // REVIEW: Load ColorMap in d3? or elsewhere? does it need its own state? - try { - let nodeColorMap = {}; - TEMPLATE.nodePrompts.type.options.forEach( (o)=>{nodeColorMap[o.label] = o.color;}); - UDATA.SetAppState('NODECOLORMAP', nodeColorMap); - } catch (error) { - console.error(PR,'received bad TEMPLATE node options. ERROR:',error,'. DATA:',data); - } - - }); - - return Promise.all([p1,p2]); - }); // end LOADASSETS HOOK +/*/ +MOD.Hook("LOADASSETS", () => { + // load data into D3DATA + let p1 = DATASTORE.PromiseD3Data().then(data => { + if (DBG) console.log(PR, "DATASTORE returned data", data); + m_ConvertData(data); + m_RecalculateAllEdgeWeights(data); + UDATA.SetAppState("D3DATA", data); + // Save off local reference because we don't have D3DATA AppStateChange handler + D3DATA = data; + }); + // load Template data and return it as a promise + // so that react render is called only after the template is loaded + let templateURL = "../templates/alexander.json"; + let p2 = DATASTORE.PromiseJSONFile(templateURL).then(data => { + if (DBG) console.log(PR, "DATASTORE returned json", data); + TEMPLATE = data; + UDATA.SetAppState("TEMPLATE", TEMPLATE); + // Process Node, NodeColorMap and Edge options + + // Validate the template file + try { + // nodePrompts + let nodePrompts = TEMPLATE.nodePrompts; + if (nodePrompts === undefined) { + throw "Missing `nodePrompts` nodePrompts=" + nodePrompts; + } + if (nodePrompts.label === undefined) + throw "Missing `nodePrompts.label` label=" + nodePrompts.label; + if (nodePrompts.type === undefined) + throw "Missing `nodePrompts.type` type= " + nodePrompts.type; + if ( + nodePrompts.type.options === undefined || + !Array.isArray(nodePrompts.type.options) + ) { + throw "Missing or bad `nodePrompts.type.options` options=" + + nodePrompts.type.options; + } + if (nodePrompts.notes === undefined) + throw "Missing `nodePrompts.notes` notes=" + nodePrompts.notes; + if (nodePrompts.info === undefined) + throw "Missing `nodePrompts.info` info=" + nodePrompts.info; + + // edgePrompts + let edgePrompts = TEMPLATE.edgePrompts; + if (edgePrompts === undefined) + throw "Missing `edgePrompts` edgePrompts=" + edgePrompts; + if (edgePrompts.source === undefined) + throw "Missing `edgePrompts.source` source=" + edgePrompts.source; + if (edgePrompts.type === undefined) + throw "Missing `edgePrompts.type` type= " + edgePrompts.type; + if ( + edgePrompts.type.options === undefined || + !Array.isArray(edgePrompts.type.options) + ) { + throw "Missing or bad `edgePrompts.type.options` options=" + + edgePrompts.type.options; + } + if (edgePrompts.target === undefined) + throw "Missing `edgePrompts.target` label=" + edgePrompts.target; + if (edgePrompts.notes === undefined) + throw "Missing `edgePrompts.notes` notes=" + edgePrompts.notes; + if (edgePrompts.info === undefined) + throw "Missing `edgePrompts.info` info=" + edgePrompts.info; + if (edgePrompts.citation === undefined) + throw "Missing `edgePrompts.citation` info=" + edgePrompts.citation; + } catch (error) { + console.error( + PR + "Error loading template `", + templateURL, + "`::::", + error + ); + } + + // REVIEW: Load ColorMap in d3? or elsewhere? does it need its own state? + try { + let nodeColorMap = {}; + TEMPLATE.nodePrompts.type.options.forEach(o => { + nodeColorMap[o.label] = o.color; + }); + UDATA.SetAppState("NODECOLORMAP", nodeColorMap); + } catch (error) { + console.error( + PR, + "received bad TEMPLATE node options. ERROR:", + error, + ". DATA:", + data + ); + } + }); + return Promise.all([p1, p2]); +}); // end LOADASSETS HOOK /// UNISYS HANDLERS /////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ lifecycle INITIALIZE handler -/*/ MOD.Hook('INITIALIZE', () => { +/*/ MOD.Hook("INITIALIZE", () => { /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ Handle D3-related updates based on state changes. Subcomponents are responsible for updating themselves. - /*/ UDATA.OnAppStateChange('SELECTION',( stateChange ) => { - if (DBG) console.log('nc-logic: Got SELECTION', stateChange); - let { nodes, edges } = stateChange; - // NODE LIST UPDATE - if (nodes!==undefined) { - if (nodes.length>0) { - let color = '#0000DD'; - nodes.forEach( node => { - m_MarkNodeById(node.id,color); - UNISYS.Log('select node',node.id,node.label); - }); - } else { - m_UnMarkAllNodes(); - } - } - }); // StateChange SELECTION + /*/ + UDATA.OnAppStateChange("SELECTION", stateChange => { + if (DBG) console.log("nc-logic: Got SELECTION", stateChange); + let { nodes, edges } = stateChange; + // NODE LIST UPDATE + if (nodes !== undefined) { + if (nodes.length > 0) { + let color = "#0000DD"; + nodes.forEach(node => { + m_MarkNodeById(node.id, color); + UNISYS.Log("select node", node.id, node.label); + }); + } else { + m_UnMarkAllNodes(); + } + } + }); // StateChange SELECTION /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ Search field has been updated - /*/ UDATA.OnAppStateChange('SEARCH',( stateChange ) => { - if (DBG) console.log('nc-logic: Got SEARCH', stateChange); - let { nodes, edges } = stateChange; - let { searchLabel } = stateChange; - let { activeAutoCompleteId } = stateChange; - // NODE LIST UPDATE - if (nodes!==undefined) { - if (nodes.length>0) { - let color = SEARCH_COLOR; - nodes.forEach( node => m_MarkNodeById(node.id,color)); - } else { - m_UnMarkAllNodes(); - } - } - // SEARCH LABEL UPDATE - if (searchLabel==='') { - m_UnStrokeAllNodes(); - } else if (searchLabel!==undefined) { - m_SetStrokeColorThatMatch(searchLabel,SEARCH_COLOR); - } - }); // StateChange SELECTION + /*/ + + UDATA.OnAppStateChange("SEARCH", stateChange => { + if (DBG) console.log("nc-logic: Got SEARCH", stateChange); + let { nodes, edges } = stateChange; + let { searchLabel } = stateChange; + let { activeAutoCompleteId } = stateChange; + // NODE LIST UPDATE + if (nodes !== undefined) { + if (nodes.length > 0) { + let color = SEARCH_COLOR; + nodes.forEach(node => m_MarkNodeById(node.id, color)); + } else { + m_UnMarkAllNodes(); + } + } + // SEARCH LABEL UPDATE + if (searchLabel === "") { + m_UnStrokeAllNodes(); + } else if (searchLabel !== undefined) { + m_SetStrokeColorThatMatch(searchLabel, SEARCH_COLOR); + } + }); // StateChange SELECTION /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ User has clicked on a suggestion from the AutoCopmlete suggestion list. The source node should be loaded in NodeSelector. @@ -269,397 +302,454 @@ const TARGET_COLOR = '#FF0000'; in the app at any one time, though nodeLabels is passed as an array. SEE ALSO: AutoComplete.onSuggestionSelected() and D3SimpleNetGraph._UpdateGraph click handler - /*/ UDATA.HandleMessage('SOURCE_SELECT', function( data ) { - if (DBG) console.log(PR,'SOURCE_SELECT got data',data); + /*/ UDATA.HandleMessage( + "SOURCE_SELECT", + function(data) { + if (DBG) console.log(PR, "SOURCE_SELECT got data", data); + + let { nodeLabels = [], nodeIDs = [] } = data; + let nodeLabel = nodeLabels.shift(); + let nodeID = nodeIDs.shift(); + let node, newState; + + if (nodeID) { + node = m_FindNodeById(nodeID); // Node IDs should be integers, not strings + } else if (nodeLabel) { + node = m_FindMatchingNodesByLabel(nodeLabel).shift(); + } else { + // No node selected, so deselect + } - let { nodeLabels=[], nodeIDs=[] } = data; - let nodeLabel = nodeLabels.shift(); - let nodeID = nodeIDs.shift(); - let node, newState; + if (DBG) console.log(PR, "SOURCE_SELECT found", node); + if (node === undefined) { + // Node not found, create a new node + newState = { + nodes: [], + edges: [] + }; + } else { + // Load existing node and edges + let edges = []; if (nodeID) { - node = m_FindNodeById(nodeID); // Node IDs should be integers, not strings - } else if (nodeLabel) { - node = m_FindMatchingNodesByLabel(nodeLabel).shift(); + edges = edges.concat( + D3DATA.edges.filter( + edge => edge.source.id === nodeID || edge.target.id === nodeID + ) + ); } else { - // No node selected, so deselect - } - - if (DBG) console.log(PR,'SOURCE_SELECT found',node); - - if (node===undefined) { - // Node not found, create a new node - newState = { - nodes : [], - edges : [] - }; - } else { - // Load existing node and edges - let edges = []; - if (nodeID) { - edges = edges.concat( D3DATA.edges.filter( edge => edge.source.id===nodeID || edge.target.id===nodeID) ); - } else { - edges = edges.concat( D3DATA.edges.filter( edge => edge.source.label===nodeLabel || edge.target.label===nodeLabel) ); - } - // create state change object - newState = { - nodes : [ node ], - edges : edges - }; + edges = edges.concat( + D3DATA.edges.filter( + edge => + edge.source.label === nodeLabel || + edge.target.label === nodeLabel + ) + ); } + // create state change object + newState = { + nodes: [node], + edges: edges + }; + } - // Set the SELECTION state so that listeners such as NodeSelectors update themselves - UDATA.SetAppState('SELECTION',newState); - }); + // Set the SELECTION state so that listeners such as NodeSelectors update themselves + UDATA.SetAppState("SELECTION", newState); + } + ); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ SOURCE_SEARCH sets the current matching term as entered in an AutoComplete field. - /*/ UDATA.HandleMessage('SOURCE_SEARCH', function( data ) { - let { searchString } = data; - let matches = m_FindMatchingNodesByLabel(searchString); - let newState = { - suggestedNodes : matches.map(n=>{return {id: n.id, label: n.label}}), - searchLabel : searchString - }; - // let SELECTION state listeners handle display updates - UDATA.SetAppState('SEARCH',newState); - }); + /*/ + UDATA.HandleMessage("SOURCE_SEARCH", function(data) { + let { searchString } = data; + let matches = m_FindMatchingNodesByLabel(searchString); + let newState = { + suggestedNodes: matches.map(n => { + return { id: n.id, label: n.label }; + }), + searchLabel: searchString + }; + // let SELECTION state listeners handle display updates + UDATA.SetAppState("SEARCH", newState); + }); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ SOURCE_HILITE updates the currently rolled-over node name in a list of selections. The hilite can be selected via either the label or the node id. - /*/ UDATA.HandleMessage('SOURCE_HILITE', function( data ) { - let { nodeLabel, nodeID, color } = data; - if (nodeLabel) { - // Only mark nodes if something is selected - m_UnMarkAllNodes(); - m_MarkNodeByLabel(nodeLabel,SOURCE_COLOR); - } - if (nodeID) { - // Only mark nodes if something is selected - m_UnMarkAllNodes(); - m_MarkNodeById(nodeID,SOURCE_COLOR); - } + /*/ + UDATA.HandleMessage("SOURCE_HILITE", function(data) { + let { nodeLabel, nodeID, color } = data; + if (nodeLabel) { + // Only mark nodes if something is selected + m_UnMarkAllNodes(); + m_MarkNodeByLabel(nodeLabel, SOURCE_COLOR); + } + if (nodeID) { + // Only mark nodes if something is selected + m_UnMarkAllNodes(); + m_MarkNodeById(nodeID, SOURCE_COLOR); + } - // NOTE: State is updated in the "MaryNodeBy*" functions above. - }); + // NOTE: State is updated in the "MaryNodeBy*" functions above. + }); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ SOURCE_UPDATE is called when the properties of a node has changed Globally updates DATASTORE and working D3DATA objects with the new node data. NOTE: SOURCE_UPDATE can be invoked remotely by the server on a DATABASE update. - /*/ UDATA.HandleMessage('SOURCE_UPDATE', function( data ) { - let { node } = data; - // try updating existing nodes with this id? - let updatedNodes = m_SetMatchingNodesByProp({id:node.id},node); - if (DBG) console.log('SOURCE_UPDATE: updated',updatedNodes); - // if no nodes had matched, then add a new node! - if (updatedNodes.length>1) { - console.error('SOURCE_UPDATE: duplicate ids in',updatedNodes); - throw Error('SOURCE_UPDATE: found duplicate IDs'); - } - if (updatedNodes.length===0) D3DATA.nodes.push(node); - UDATA.SetAppState('D3DATA',D3DATA); - }); + /*/ + UDATA.HandleMessage("SOURCE_UPDATE", function(data) { + let { node } = data; + // try updating existing nodes with this id? + let updatedNodes = m_SetMatchingNodesByProp({ id: node.id }, node); + if (DBG) console.log("SOURCE_UPDATE: updated", updatedNodes); + // if no nodes had matched, then add a new node! + if (updatedNodes.length > 1) { + console.error("SOURCE_UPDATE: duplicate ids in", updatedNodes); + throw Error("SOURCE_UPDATE: found duplicate IDs"); + } + if (updatedNodes.length === 0) D3DATA.nodes.push(node); + UDATA.SetAppState("D3DATA", D3DATA); + }); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ EDGE_UPDATE is called when the properties of an edge has changed NOTE: SOURCE_UPDATE can be invoked remotely by the server on a DATABASE update. - /*/ UDATA.HandleMessage('EDGE_UPDATE', function( data ) { - let { edge } = data; - // edge.source and edge.target are initially ids - // replace then with node data - edge.source = m_FindNodeById(edge.source); - edge.target = m_FindNodeById(edge.target); - // set matching nodes - let updatedEdges = m_SetMatchingEdgesByProp({id:edge.id},edge); - if (DBG) console.log('EDGE_UPDATE: updated',updatedEdges); - - // if no nodes had matched, then add a new node! - if (updatedEdges.length===0) { - if (DBG) console.log('EDGE_UPDATE: pushing edge',edge); - // created edges should have a default size - edge.size = 1; - D3DATA.edges.push(edge); - // Edge source and target links should be stored as - // ids rather than references to the actual source and - // target node objects. - // - // d3 will map the source and target ids to the - // node objects themsleves during the _UpdateGraph method. - // - // So we explicitly set and store ids rather than objects here. - // - // (If we don't do this, the edges become disconnected from nodes) - edge.source = edge.source.id; - edge.target = edge.target.id; - // Calculate Edge Size - edge.size = m_CalculateEdgeWeight( edge, D3DATA.edges ); - } - // if there was one node - if (updatedEdges.length===1) { - // Edge source and target links should be stored as - // ids rather than references to the actual source and - // target node objects. - // - // d3 will map the source and target ids to the - // node objects themsleves during the _UpdateGraph method. - // - // So we explicitly set and store ids rather than objects here. - // - // (If we don't do this, the edges become disconnected from nodes) - edge.source = edge.source.id; - edge.target = edge.target.id; - edge.size = m_CalculateEdgeWeight( edge, D3DATA.edges ); - } - // if there were more edges than expected - if (updatedEdges.length>1) { - throw Error("EdgeUpdate found duplicate IDs"); - } - UDATA.SetAppState('D3DATA',D3DATA); - }); + /*/ + UDATA.HandleMessage("EDGE_UPDATE", function(data) { + let { edge } = data; + // edge.source and edge.target are initially ids + // replace then with node data + edge.source = m_FindNodeById(edge.source); + edge.target = m_FindNodeById(edge.target); + // set matching nodes + let updatedEdges = m_SetMatchingEdgesByProp({ id: edge.id }, edge); + if (DBG) console.log("EDGE_UPDATE: updated", updatedEdges); + + // if no nodes had matched, then add a new node! + if (updatedEdges.length === 0) { + if (DBG) console.log("EDGE_UPDATE: pushing edge", edge); + // created edges should have a default size + edge.size = 1; + D3DATA.edges.push(edge); + // Edge source and target links should be stored as + // ids rather than references to the actual source and + // target node objects. + // + // d3 will map the source and target ids to the + // node objects themsleves during the _UpdateGraph method. + // + // So we explicitly set and store ids rather than objects here. + // + // (If we don't do this, the edges become disconnected from nodes) + edge.source = edge.source.id; + edge.target = edge.target.id; + // Calculate Edge Size + edge.size = m_CalculateEdgeWeight(edge, D3DATA.edges); + } + // if there was one node + if (updatedEdges.length === 1) { + // Edge source and target links should be stored as + // ids rather than references to the actual source and + // target node objects. + // + // d3 will map the source and target ids to the + // node objects themsleves during the _UpdateGraph method. + // + // So we explicitly set and store ids rather than objects here. + // + // (If we don't do this, the edges become disconnected from nodes) + edge.source = edge.source.id; + edge.target = edge.target.id; + edge.size = m_CalculateEdgeWeight(edge, D3DATA.edges); + } + // if there were more edges than expected + if (updatedEdges.length > 1) { + throw Error("EdgeUpdate found duplicate IDs"); + } + UDATA.SetAppState("D3DATA", D3DATA); + }); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ EDGE_DELETE is called when an edge should be removed from...something? - /*/ UDATA.HandleMessage('EDGE_DELETE', function( data ) { - let { edgeID } = data; - let edges = []; - // remove specified edge from edge list - D3DATA.edges = m_DeleteMatchingEdgeByProp({id:edgeID}); - UDATA.SetAppState('D3DATA',D3DATA); - // Also update selection so edges in EdgeEditor will update - let selection = UDATA.AppState('SELECTION'); - if ((selection.nodes===undefined) || (selection.nodes.length<1) || (selection.nodes[0].id===undefined)) { - if (DBG) console.log(PR,'no selection:',selection); - } else { - if (DBG) console.log(PR,'updating selection:',selection); - let nodeID = selection.nodes[0].id; - // Remove the deleted edge from the selection - if ((selection.edges!==undefined) && (selection.edges.length>0)) { - edges = edges.concat( selection.edges.filter( edge => edge.id!==edgeID )); - } - } - UDATA.SetAppState('SELECTION',{ - nodes: selection.nodes, - edges: edges - }); - }); + /*/ + UDATA.HandleMessage("EDGE_DELETE", function(data) { + let { edgeID } = data; + let edges = []; + // remove specified edge from edge list + D3DATA.edges = m_DeleteMatchingEdgeByProp({ id: edgeID }); + UDATA.SetAppState("D3DATA", D3DATA); + // Also update selection so edges in EdgeEditor will update + let selection = UDATA.AppState("SELECTION"); + if ( + selection.nodes === undefined || + selection.nodes.length < 1 || + selection.nodes[0].id === undefined + ) { + if (DBG) console.log(PR, "no selection:", selection); + } else { + if (DBG) console.log(PR, "updating selection:", selection); + let nodeID = selection.nodes[0].id; + // Remove the deleted edge from the selection + if (selection.edges !== undefined && selection.edges.length > 0) { + edges = edges.concat( + selection.edges.filter(edge => edge.id !== edgeID) + ); + } + } + UDATA.SetAppState("SELECTION", { + nodes: selection.nodes, + edges: edges + }); + }); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ AUTOCOMPLETE_SELECT is called by components to tell the module which one has the current focus. - /*/ UDATA.HandleMessage('AUTOCOMPLETE_SELECT', function( data ) { - m_HandleAutoCompleteSelect( data ); - }); - }); // end UNISYS_INIT + /*/ + UDATA.HandleMessage("AUTOCOMPLETE_SELECT", function(data) { + m_HandleAutoCompleteSelect(data); + }); +}); // end UNISYS_INIT - function m_HandleAutoCompleteSelect ( data ) { - if (DBG) console.log('ACL: Setting activeAutoCompleteId to',data.id); - UDATA.SetAppState('ACTIVEAUTOCOMPLETE',{ - activeAutoCompleteId: data.id - }); - } +function m_HandleAutoCompleteSelect(data) { + if (DBG) console.log("ACL: Setting activeAutoCompleteId to", data.id); + UDATA.SetAppState("ACTIVEAUTOCOMPLETE", { + activeAutoCompleteId: data.id + }); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ lifecycle RESET handler -/*/ MOD.Hook('RESET', () => { - // Force an AppState update here so that the react components will load - // the data after they've been initialized. The SetAppState call in - // LOADASSETS is broadcast before react components have been loaded. - UDATA.SetAppState('D3DATA',D3DATA); - }); // end UNISYS_RESET +/*/ +MOD.Hook("RESET", () => { + // Force an AppState update here so that the react components will load + // the data after they've been initialized. The SetAppState call in + // LOADASSETS is broadcast before react components have been loaded. + UDATA.SetAppState("D3DATA", D3DATA); +}); // end UNISYS_RESET /// APP_READY MESSAGE REGISTRATION //////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ The APP_READY hook is fired after all initialization phases have finished and may also fire at other times with a valid info packet -/*/ MOD.Hook('APP_READY', function( info ) { - /// RETURN PROMISE to prevent phase from continuing until after registration - /// of messages is successful - return new Promise((resolve,reject) => { - if (DBG) console.log(`${PR}HOOK 'UNISYS_INIT' Registering Message Handlers...`); - // timeout for broken network registration - let timeout = setTimeout(()=>{ - reject(new Error('UNISYS REGISTER TIMEOUT')); - },5000); - - // register ONLY messages we want to make public - UNISYS.RegisterMessagesPromise([ - 'SOURCE_UPDATE', - 'EDGE_UPDATE', - 'EDGE_DELETE' - ]) - .then((d)=>{ - clearTimeout(timeout); - if (DBG) console.log(`${PR}HOOK 'UNISYS_INIT' Registered Message Handlers ${JSON.stringify(d.registered)}`); - if (DBG) console.log(`INFO: %cMy socket address is ${UNISYS.SocketUADDR()}`,'color:blue;font-weight:bold' ); - resolve(); - }); - }); - }); // end UNISYS_READY - +/*/ +MOD.Hook("APP_READY", function(info) { + /// RETURN PROMISE to prevent phase from continuing until after registration + /// of messages is successful + return new Promise((resolve, reject) => { + if (DBG) + console.log(`${PR}HOOK 'UNISYS_INIT' Registering Message Handlers...`); + // timeout for broken network registration + let timeout = setTimeout(() => { + reject(new Error("UNISYS REGISTER TIMEOUT")); + }, 5000); + + // register ONLY messages we want to make public + UNISYS.RegisterMessagesPromise([ + "SOURCE_UPDATE", + "EDGE_UPDATE", + "EDGE_DELETE" + ]).then(d => { + clearTimeout(timeout); + if (DBG) + console.log( + `${PR}HOOK 'UNISYS_INIT' Registered Message Handlers ${JSON.stringify( + d.registered + )}` + ); + if (DBG) + console.log( + `INFO: %cMy socket address is ${UNISYS.SocketUADDR()}`, + "color:blue;font-weight:bold" + ); + resolve(); + }); + }); +}); // end UNISYS_READY /// OBJECT HELPERS //////////////////////////////////////////////////////////// /// these probably should go into a utility class /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Return array of objects that match the match_me object keys/values NOTE: make sure that strings are compared with strings, etc -/*/ function m_FindMatchingObjsByProp( obj_list, match_me={} ) { - // operate on arrays only - if (!Array.isArray(obj_list)) throw Error("FindMatchingObjectsByProp arg1 must be array"); - let matches = obj_list.filter( obj => { - let pass = true; - for (let key in match_me) { - if (match_me[key]!==obj[key]) pass=false; break; - } - return pass; - }); - // return array of matches (can be empty array) - return matches; +/*/ +function m_FindMatchingObjsByProp(obj_list, match_me = {}) { + // operate on arrays only + if (!Array.isArray(obj_list)) + throw Error("FindMatchingObjectsByProp arg1 must be array"); + let matches = obj_list.filter(obj => { + let pass = true; + for (let key in match_me) { + if (match_me[key] !== obj[key]) pass = false; + break; } + return pass; + }); + // return array of matches (can be empty array) + return matches; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Set array of objects that match to key/values of yes/no respectively Returns array of matched objects -?*/ function m_SetMatchingObjsByProp( obj_list, match_me={}, yes={}, no={} ) { - // operate on arrays only - if (!Array.isArray(obj_list)) throw Error("SetMatchingObjsByPropp arg1 must be array"); - - let returnMatches = []; - obj_list.forEach( node => { - let matched = true; - for (let key in match_me) { - if (match_me[key]!==node[key]) matched=false; break; - } - if (matched) { - for (let key in yes) node[key]=yes[key]; - returnMatches.push(node); - } else { - for (let key in no) node[key]=no[key]; - } - }); - return returnMatches; +/*/ +function m_SetMatchingObjsByProp(obj_list, match_me = {}, yes = {}, no = {}) { + // operate on arrays only + if (!Array.isArray(obj_list)) + throw Error("SetMatchingObjsByPropp arg1 must be array"); + + let returnMatches = []; + obj_list.forEach(node => { + let matched = true; + for (let key in match_me) { + if (match_me[key] !== node[key]) matched = false; + break; } + if (matched) { + for (let key in yes) node[key] = yes[key]; + returnMatches.push(node); + } else { + for (let key in no) node[key] = no[key]; + } + }); + return returnMatches; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Update props of everything in obj_list -/*/ function m_SetAllObjs( obj_list, all={} ) { - // operate on arrays only - if (!Array.isArray(obj_list)) throw Error("SetAllNodes arg1 must be array"); - obj_list.forEach(obj => { - for (let key in all) obj[key]=all[key]; - }); - } +/*/ +function m_SetAllObjs(obj_list, all = {}) { + // operate on arrays only + if (!Array.isArray(obj_list)) throw Error("SetAllNodes arg1 must be array"); + obj_list.forEach(obj => { + for (let key in all) obj[key] = all[key]; + }); +} /// NODE HELPERS ////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Return array of nodes that match the match_me object keys/values NOTE: make sure that strings are compared with strings, etc -/*/ function m_FindMatchingNodeByProp( match_me={} ) { - return m_FindMatchingObjsByProp(D3DATA.nodes,match_me); - } +/*/ +function m_FindMatchingNodeByProp(match_me = {}) { + return m_FindMatchingObjsByProp(D3DATA.nodes, match_me); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Convenience function to retrieve node by ID -/*/ function m_FindNodeById( id ) { - return m_FindMatchingNodeByProp({ id })[0]; - } +/*/ function m_FindNodeById(id) { + return m_FindMatchingNodeByProp({ id })[0]; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Return array of nodes with labels that partially match str -/*/ function m_FindMatchingNodesByLabel( str='' ) { - if (!str) return []; - str = u_EscapeRegexChars(str.trim()); - if (str==='') return []; - const regex = new RegExp(/*'^'+*/str,'i'); - return D3DATA.nodes.filter(node=>regex.test(node.label)); - } +/*/ +function m_FindMatchingNodesByLabel(str = "") { + if (!str) return []; + str = u_EscapeRegexChars(str.trim()); + if (str === "") return []; + const regex = new RegExp(/*'^'+*/ str, "i"); + return D3DATA.nodes.filter(node => regex.test(node.label)); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Set nodes that PARTIALLY match 'str' to 'yes' props. All others nodes are set to 'no' props. Return matches Optionally resets all the NON matching nodes as well -/*/ function m_SetMatchingNodesByLabel( str='', yes={}, no={} ) { - let returnMatches = []; - str = u_EscapeRegexChars(str.trim()); - if (str==='') return undefined; - const regex = new RegExp(/*'^'+*/str,'i'); - D3DATA.nodes.forEach(node => { - if (regex.test(node.label)) { - for (let key in yes) node[key]=yes[key]; - returnMatches.push(node); - } else { - for (let key in no) node[key]=no[key]; - } - }); - return returnMatches; +/*/ +function m_SetMatchingNodesByLabel(str = "", yes = {}, no = {}) { + let returnMatches = []; + str = u_EscapeRegexChars(str.trim()); + if (str === "") return undefined; + const regex = new RegExp(/*'^'+*/ str, "i"); + D3DATA.nodes.forEach(node => { + if (regex.test(node.label)) { + for (let key in yes) node[key] = yes[key]; + returnMatches.push(node); + } else { + for (let key in no) node[key] = no[key]; } + }); + return returnMatches; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Update props of exact matching nodes, returns matches Optionally resets all the NON matching nodes as well -/*/ function m_SetMatchingNodesByProp( match_me={}, yes={}, no={} ) { - return m_SetMatchingObjsByProp( D3DATA.nodes, match_me, yes, no ); - } +/*/ +function m_SetMatchingNodesByProp(match_me = {}, yes = {}, no = {}) { + return m_SetMatchingObjsByProp(D3DATA.nodes, match_me, yes, no); +} /// EDGE HELPERS ////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Return array of edges that DON'T match del_me object keys/values -/*/ function m_DeleteMatchingEdgeByProp( del_me={} ) { - let matches = D3DATA.edges.filter( (edge) => { - let pass = false; - for (let key in del_me) { - if (del_me[key]!==edge[key]) { - pass=true; break; - } - } - return pass; - }); - // return array of matches (can be empty array) - return matches; +/*/ +function m_DeleteMatchingEdgeByProp(del_me = {}) { + let matches = D3DATA.edges.filter(edge => { + let pass = false; + for (let key in del_me) { + if (del_me[key] !== edge[key]) { + pass = true; + break; + } } + return pass; + }); + // return array of matches (can be empty array) + return matches; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Update props of exact matching edges, returns matches -/*/ function m_SetMatchingEdgesByProp( match_me={}, yes={}, no={} ) { - return m_SetMatchingObjsByProp( D3DATA.edges, match_me, yes, no ); - } +/*/ +function m_SetMatchingEdgesByProp(match_me = {}, yes = {}, no = {}) { + return m_SetMatchingObjsByProp(D3DATA.edges, match_me, yes, no); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Count number of edges with the same source/target to determine weight `data` is passed by reference This modifies `data` data = { nodes: [], edges: [] } -/*/ function m_RecalculateAllEdgeWeights( data ) { - data.edges.forEach( (edge) => { - edge.size = m_CalculateEdgeWeight( edge, data.edges ); - }); - return data; - } +/*/ +function m_RecalculateAllEdgeWeights(data) { + data.edges.forEach(edge => { + edge.size = m_CalculateEdgeWeight(edge, data.edges); + }); + return data; +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Count number of edges with the same source/target to determine weight -/*/ function m_CalculateEdgeWeight( edge, edges ) { - // REVIEW: If there's a match, BOTH edge sizes ought to be set! - - let size = edges.reduce( (accumulator,e) => { - // Ignore self - if (e.id===edge.id) return accumulator; - // source and target might be ids or might be node objects depending - // on whether D3 has processed the edge object. - let sourceId = e.source.id || e.source; - let targetId = e.target.id || e.target; - let edgeSourceId = edge.source.id || edge.source; - let edgeTargetId = edge.target.id || edge.target; - //console.log('comparing sourceId',sourceId,'to',edgeSourceId,' / targetId',targetId,'to',edgeTargetId); - if ( ((sourceId===edgeSourceId) && (targetId===edgeTargetId)) || - ((sourceId===edgeTargetId) && (targetId===edgeSourceId)) - ) return accumulator + 1; - return accumulator; - }, 1); - return size; - } +/*/ +function m_CalculateEdgeWeight(edge, edges) { + // REVIEW: If there's a match, BOTH edge sizes ought to be set! + + let size = edges.reduce((accumulator, e) => { + // Ignore self + if (e.id === edge.id) return accumulator; + // source and target might be ids or might be node objects depending + // on whether D3 has processed the edge object. + let sourceId = e.source.id || e.source; + let targetId = e.target.id || e.target; + let edgeSourceId = edge.source.id || edge.source; + let edgeTargetId = edge.target.id || edge.target; + //console.log('comparing sourceId',sourceId,'to',edgeSourceId,' / targetId',targetId,'to',edgeTargetId); + if ( + (sourceId === edgeSourceId && targetId === edgeTargetId) || + (sourceId === edgeTargetId && targetId === edgeSourceId) + ) + return accumulator + 1; + return accumulator; + }, 1); + return size; +} /// UTILITIES ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ REGEX: the chars in brackets are part of matching character set. Declaring this as a constant makes the RegEx run faster (I think). -/*/ const REGEX_REGEXCHARS = /[.*+?^${}()|[\]\\]/g; +/*/ +const REGEX_REGEXCHARS = /[.*+?^${}()|[\]\\]/g; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Adds a \ in front of characters that have special RegEx meaning From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expression -/*/ function u_EscapeRegexChars( string ) { - return string.replace(REGEX_REGEXCHARS,'\\$&'); // $& means the whole matched string - } +/*/ +function u_EscapeRegexChars(string) { + return string.replace(REGEX_REGEXCHARS, "\\$&"); // $& means the whole matched string +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Convert all IDs to integers Node and Edge IDs should be integers. @@ -670,136 +760,160 @@ const TARGET_COLOR = '#FF0000'; `data` is passed by reference This modifies `data` data = { nodes: [], edges: [] } -/*/ function m_ConvertData( data ) { - data.nodes.forEach( (node) => {node.id = parseInt(node.id);} ); - data.edges.forEach( (edge) => { - edge.id = parseInt(edge.id); - // before D3 processing, edge.source and edge.target are ids - edge.source = parseInt(edge.source); - edge.target = parseInt(edge.target); - }); - } +/*/ +function m_ConvertData(data) { + data.nodes.forEach(node => { + node.id = parseInt(node.id); + }); + data.edges.forEach(edge => { + edge.id = parseInt(edge.id); + // before D3 processing, edge.source and edge.target are ids + edge.source = parseInt(edge.source); + edge.target = parseInt(edge.target); + }); +} /// NODE MARKING METHODS ////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Visually change all nodes to the deselected color -/*/ function m_UnMarkAllNodes() { - let props = { selected : DESELECTED_COLOR }; - m_SetAllObjs(D3DATA.nodes,props); - UDATA.SetAppState('D3DATA',D3DATA); - } +/*/ +function m_UnMarkAllNodes() { + let props = { selected: DESELECTED_COLOR }; + m_SetAllObjs(D3DATA.nodes, props); + UDATA.SetAppState("D3DATA", D3DATA); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Remove the stroke color. Used to unmark search matches. -/*/ function m_UnStrokeAllNodes() { - let props = { strokeColor : undefined }; - m_SetAllObjs(D3DATA.nodes,props); - UDATA.SetAppState('D3DATA',D3DATA); - } +/*/ +function m_UnStrokeAllNodes() { + let props = { strokeColor: undefined }; + m_SetAllObjs(D3DATA.nodes, props); + UDATA.SetAppState("D3DATA", D3DATA); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Sets the `node.selected` property to `color` so it is hilited on graph -/*/ function m_MarkNodeById( id, color ) { - let marked = { selected : SOURCE_COLOR }; - let normal = { selected : DESELECTED_COLOR }; - // NOTE: this.getSelectedNodeColor(node,color) and - // this.getDeselectedNodeColor(node,color) are not yet implemented - // to override the properties - m_SetMatchingNodesByProp({id},marked,normal); - UDATA.SetAppState('D3DATA',D3DATA); - } +/*/ +function m_MarkNodeById(id, color) { + let marked = { selected: SOURCE_COLOR }; + let normal = { selected: DESELECTED_COLOR }; + // NOTE: this.getSelectedNodeColor(node,color) and + // this.getDeselectedNodeColor(node,color) are not yet implemented + // to override the properties + m_SetMatchingNodesByProp({ id }, marked, normal); + UDATA.SetAppState("D3DATA", D3DATA); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Sets the `node.selected` property to `color` so it is hilited on graph -/*/ function m_MarkNodeByLabel( label, color ) { - let marked = { selected : color }; - let normal = { selected : DESELECTED_COLOR }; - // NOTE: this.getSelectedNodeColor(node,color) and - // this.getDeselectedNodeColor(node,color) are not yet implemented - // to override the properties - m_SetMatchingNodesByLabel(label,marked,normal); - UDATA.SetAppState('D3DATA',D3DATA); - } +/*/ +function m_MarkNodeByLabel(label, color) { + let marked = { selected: color }; + let normal = { selected: DESELECTED_COLOR }; + // NOTE: this.getSelectedNodeColor(node,color) and + // this.getDeselectedNodeColor(node,color) are not yet implemented + // to override the properties + m_SetMatchingNodesByLabel(label, marked, normal); + UDATA.SetAppState("D3DATA", D3DATA); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Sets matching node labels to the passed selection color -/*/ function m_MarkNodesThatMatch( searchString, color ) { - if (searchString==='') { - m_UnMarkAllNodes(); - return; - } - let select = { selected : color }; - let deselect = { selected : DESELECTED_COLOR }; - m_SetMatchingNodesByLabel(searchString, select, deselect); - UDATA.SetAppState('D3DATA',D3DATA); - } +/*/ +function m_MarkNodesThatMatch(searchString, color) { + if (searchString === "") { + m_UnMarkAllNodes(); + return; + } + let select = { selected: color }; + let deselect = { selected: DESELECTED_COLOR }; + m_SetMatchingNodesByLabel(searchString, select, deselect); + UDATA.SetAppState("D3DATA", D3DATA); +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Sets matching node labels to the passed selection color This sets the stroke color, which is used to display the matching nodes during a search. If the node is also selected, the selected color will override this color. -/*/ function m_SetStrokeColorThatMatch( searchString, color ) { - let matched = { strokeColor : color }; - let notmatched = { strokeColor : undefined }; - m_SetMatchingNodesByLabel(searchString, matched, notmatched); - UDATA.SetAppState('D3DATA',D3DATA); - } +/*/ +function m_SetStrokeColorThatMatch(searchString, color) { + let matched = { strokeColor: color }; + let notmatched = { strokeColor: undefined }; + m_SetMatchingNodesByLabel(searchString, matched, notmatched); + UDATA.SetAppState("D3DATA", D3DATA); +} /// COMMAND LINE UTILITIES //////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Command: RESET THE DATABASE from default data -/*/ JSCLI.AddFunction(function ncPushDatabase( jsonFile ) { - jsonFile = jsonFile || 'data.reducedlinks.json'; - DATASTORE.PromiseJSONFile(jsonFile) - .then((data)=>{ - // data is { nodes, edges } - console.log(PR,`Sending data from ${jsonFile} to Server`,data); - // UDATA.Call() returns a promise, so return it to - // continue the asynchronous chain - return UDATA.Call('SRV_DBSET', data); - }) - .then((d)=>{ - if (d.OK) { - window.alert(`assets/data/${jsonFile} was pushed to Server.\nPress OK to refresh this page and MANUALLY REFRESH other clients.\n\n(note: if data hasn't changed, try command again)`); - console.log(`${PR} %cServer Database has been overwritten with ${jsonFile}`,'color:blue'); - console.log(`${PR} Reload apps to see new data`); - setTimeout(UNISYS.ForceReloadOnNavigation,1000); - } else { - console.error(PR,'Server Error',d); - window.alert(`Error ${JSON.stringify(d)}`); - } - }); - // return syntax help - return "FYI: ncPushDatabase(jsonFile) can load file in assets/data"; +/*/ +JSCLI.AddFunction(function ncPushDatabase(jsonFile) { + jsonFile = jsonFile || "data.reducedlinks.json"; + DATASTORE.PromiseJSONFile(jsonFile) + .then(data => { + // data is { nodes, edges } + console.log(PR, `Sending data from ${jsonFile} to Server`, data); + // UDATA.Call() returns a promise, so return it to + // continue the asynchronous chain + return UDATA.Call("SRV_DBSET", data); + }) + .then(d => { + if (d.OK) { + window.alert( + `assets/data/${jsonFile} was pushed to Server.\nPress OK to refresh this page and MANUALLY REFRESH other clients.\n\n(note: if data hasn't changed, try command again)` + ); + console.log( + `${PR} %cServer Database has been overwritten with ${jsonFile}`, + "color:blue" + ); + console.log(`${PR} Reload apps to see new data`); + setTimeout(UNISYS.ForceReloadOnNavigation, 1000); + } else { + console.error(PR, "Server Error", d); + window.alert(`Error ${JSON.stringify(d)}`); + } }); + // return syntax help + return "FYI: ncPushDatabase(jsonFile) can load file in assets/data"; +}); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Command: EMPTY THE DATABASE from default data -/*/ JSCLI.AddFunction(function ncEmptyDatabase() { - window.ncPushDatabase('nada.json'); - return "FYI: pushing empty database from assets/data/nada.json...reloading"; - }); +/*/ JSCLI.AddFunction( + function ncEmptyDatabase() { + window.ncPushDatabase("nada.json"); + return "FYI: pushing empty database from assets/data/nada.json...reloading"; + } +); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Command: Token Generator -/*/ JSCLI.AddFunction( function ncMakeTokens (clsId, projId, numGroups ) { - // type checking - if (typeof clsId!=='string') return "args: str classId, str projId, int numGroups" - if (typeof projId!=='string') return "args: str classId, str projId, int numGroups" - if (clsId.length>12) return "classId arg1 should be 12 chars or less"; - if (projId.length>12) return "classId arg1 should be 12 chars or less"; - if (!Number.isInteger(numGroups)) return "numGroups arg3 must be integer"; - if (numGroups<1) return "numGroups arg3 must be positive integeger"; - // let's do this! - let out = `\nTOKEN LIST for class '${clsId}' project '${projId}'\n\n`; - let pad = String(numGroups).length; - for (let i=1; i<=numGroups; i++) { - let id = String(i); - id = id.padStart(pad,'0'); - out += `group ${id}\t${SESSION.MakeToken(clsId,projId,i)}\n`; - } - if (window && window.location) { - let ubits = new URL(window.location); - let hash = ubits.hash.split('/')[0]; - let url = `${ubits.protocol}//${ubits.host}/${hash}`; - out += `\nexample url: ${SETTINGS.ServerAppURL()}/edit/${SESSION.MakeToken(clsId,projId,1)}\n`; - } - return out; - }); +/*/ +JSCLI.AddFunction(function ncMakeTokens(clsId, projId, numGroups) { + // type checking + if (typeof clsId !== "string") + return "args: str classId, str projId, int numGroups"; + if (typeof projId !== "string") + return "args: str classId, str projId, int numGroups"; + if (clsId.length > 12) return "classId arg1 should be 12 chars or less"; + if (projId.length > 12) return "classId arg1 should be 12 chars or less"; + if (!Number.isInteger(numGroups)) return "numGroups arg3 must be integer"; + if (numGroups < 1) return "numGroups arg3 must be positive integeger"; + // let's do this! + let out = `\nTOKEN LIST for class '${clsId}' project '${projId}'\n\n`; + let pad = String(numGroups).length; + for (let i = 1; i <= numGroups; i++) { + let id = String(i); + id = id.padStart(pad, "0"); + out += `group ${id}\t${SESSION.MakeToken(clsId, projId, i)}\n`; + } + if (window && window.location) { + let ubits = new URL(window.location); + let hash = ubits.hash.split("/")[0]; + let url = `${ubits.protocol}//${ubits.host}/${hash}`; + out += `\nexample url: ${SETTINGS.ServerAppURL()}/edit/${SESSION.MakeToken( + clsId, + projId, + 1 + )}\n`; + } + return out; +}); /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From c85ff6176541a02d51502cd908927dc33f4f3cef Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Thu, 3 Jan 2019 20:41:39 -0500 Subject: [PATCH 07/13] dev-ds/offline-view: rudimentary cached app by URL (e.g. http://localhost:3000/#/edit/MEEP-ONE-9V3) --- build/app/assets/netcreate.cache | 3 +- build/app/init.jsx | 146 +++++--- build/app/unisys/client-lifecycle.js | 244 ++++++++------ build/app/unisys/client-network.js | 38 ++- build/app/unisys/client.js | 350 +++++++++++--------- build/app/unisys/common-netmessage-class.js | 24 +- build/app/view/netcreate/nc-logic.js | 322 ++++++++++-------- 7 files changed, 646 insertions(+), 481 deletions(-) diff --git a/build/app/assets/netcreate.cache b/build/app/assets/netcreate.cache index f6ee2612..7a124891 100644 --- a/build/app/assets/netcreate.cache +++ b/build/app/assets/netcreate.cache @@ -4,6 +4,7 @@ CACHE MANIFEST scripts/netc-lib.js scripts/netc-app.js styles/netc-app.css +favicon.ico templates/alexander.json # will be auto generated in the future -# 006 +# 059 \ No newline at end of file diff --git a/build/app/init.jsx b/build/app/init.jsx index 9c716bbe..3a316612 100644 --- a/build/app/init.jsx +++ b/build/app/init.jsx @@ -16,74 +16,118 @@ require("babel-polyfill"); // enables regenerators for async/await /// LIBRARIES ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const React = require('react'); -const ReactDOM = require('react-dom'); -const { HashRouter } = require('react-router-dom'); +const React = require("react"); +const ReactDOM = require("react-dom"); +const { HashRouter } = require("react-router-dom"); /// SYSTEM MODULES //////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /// demo: require system modules; this will likely be removed -const UNISYS = require('unisys/client'); -const AppShell = require('init-appshell'); +const UNISYS = require("unisys/client"); +const AppShell = require("init-appshell"); /// UNISYS LIFECYCLE LOADER /////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ When the DOM is loaded, initialize UNISYS -/*/ document.addEventListener('DOMContentLoaded', () => { - console.group('init.jsx bootstrap'); - console.log('%cINIT %cDOMContentLoaded. Starting UNISYS Lifecycle!','color:blue','color:auto'); - m_SetLifecycleScope(); - (async () => { - await UNISYS.JoinNet(); // UNISYS socket connection (that is all) - await UNISYS.EnterApp(); // TEST_CONF, INITIALIZE, LOADASSETS, CONFIGURE - await m_RenderApp(); // compose React view - await UNISYS.SetupDOM(); // DOM_READY - await UNISYS.SetupRun(); // RESET, START, APP_READY, RUN - console.log('%cINIT %cUNISYS Lifecycle Initialization Complete','color:blue','color:auto'); - console.groupEnd(); - })(); - }); +/*/ +document.addEventListener("DOMContentLoaded", () => { + console.group("init.jsx bootstrap"); + console.log( + "%cINIT %cDOMContentLoaded. Starting UNISYS Lifecycle!", + "color:blue", + "color:auto" + ); + m_SetLifecycleScope(); + (async () => { + await UNISYS.JoinNet(); // UNISYS socket connection (that is all) + await UNISYS.EnterApp(); // TEST_CONF, INITIALIZE, LOADASSETS, CONFIGURE + await m_RenderApp(); // compose React view + await UNISYS.SetupDOM(); // DOM_READY + await UNISYS.SetupRun(); // RESET, START, APP_READY, RUN + console.log( + "%cINIT %cUNISYS Lifecycle Initialization Complete", + "color:blue", + "color:auto" + ); + console.groupEnd(); + })(); +}); + +/// UNISYS LIFECYCLE CLOSE EVENT ////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/*/ this custom event accesses post-run lifecycles defined for 'DOMContentLoaded' +/*/ +document.addEventListener("UNISYSDisconnect", () => { + console.log( + "%cDISCONNECT %cUNISYSDisconnect. Closing UNISYS Lifecycle!", + "color:blue", + "color:auto" + ); + (async () => { + await UNISYS.ServerDisconnect(); // UNISYS has dropped server + console.log( + "%cDISCONNECT %cUNISYSDisconnect Complete", + "color:blue", + "color:auto" + ); + })(); +}); /// LIFECYCLE HELPERS ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ helper to infer view module scope before module is routed lated (!) /*/ function m_SetLifecycleScope() { - // set scope for UNISYS execution - let routes = AppShell.Routes; - // check #, and remove any trailing parameters in slashes - // we want the first one - let hashbits = window.location.hash.split('/'); - let hash = hashbits[0]; - let loc = '/'+hash.substring(1); - let matches = routes.filter((route)=>{return route.path===loc}); - if (matches.length) { - if (DBG) console.log(`Lifecycle Module Scope is ${hash}`); - let component = matches[0].component; - if (component.UMOD===undefined) console.warn(`WARNING: root view '${loc}' has no UMOD property, so can not set UNISYS scope`); - let modscope = component.UMOD || '/init.jsx'; - UNISYS.SetScope(modscope); - } else { - console.warn(`m_SetLifecycleScope() could not match scope ${loc}`); - } + // set scope for UNISYS execution + let routes = AppShell.Routes; + // check #, and remove any trailing parameters in slashes + // we want the first one + let hashbits = window.location.hash.split("/"); + let hash = hashbits[0]; + let loc = "/" + hash.substring(1); + let matches = routes.filter(route => { + return route.path === loc; + }); + if (matches.length) { + if (DBG) console.log(`Lifecycle Module Scope is ${hash}`); + let component = matches[0].component; + if (component.UMOD === undefined) + console.warn( + `WARNING: root view '${loc}' has no UMOD property, so can not set UNISYS scope` + ); + let modscope = component.UMOD || "/init.jsx"; + UNISYS.SetScope(modscope); + } else { + console.warn(`m_SetLifecycleScope() could not match scope ${loc}`); } +} /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Wraps ReactDOM.render() in a Promise. Execution continues in and the routed view in AppShell.Routes /*/ function m_RenderApp() { - if (DBG) console.log('%cINIT %cReactDOM.render() begin','color:blue','color:auto'); - return new Promise(( resolve, reject ) => { - try { - ReactDOM.render(( - - - - ), document.querySelector( '#app-container' ), ()=>{ - console.log('%cINIT %cReactDOM.render() complete','color:blue','color:auto'); - resolve(); - }) - } catch (e) { - console.error('m_RenderApp() Lifecycle Error. Check phase execution order effect on data validity.\n',e); - debugger; + if (DBG) + console.log("%cINIT %cReactDOM.render() begin", "color:blue", "color:auto"); + return new Promise((resolve, reject) => { + try { + ReactDOM.render( + + + , + document.querySelector("#app-container"), + () => { + console.log( + "%cINIT %cReactDOM.render() complete", + "color:blue", + "color:auto" + ); + resolve(); } - }); // promise + ); + } catch (e) { + console.error( + "m_RenderApp() Lifecycle Error. Check phase execution order effect on data validity.\n", + e + ); + debugger; } + }); // promise +} diff --git a/build/app/unisys/client-lifecycle.js b/build/app/unisys/client-lifecycle.js index 84615225..e04a31f2 100644 --- a/build/app/unisys/client-lifecycle.js +++ b/build/app/unisys/client-lifecycle.js @@ -5,149 +5,173 @@ if (window.NC_DBG) console.log(`inc ${module.id}`); \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ -const DBG = window.NC_DBG && window.NC_DBG.lifecycle; -const BAD_PATH = "module_path must be a string derived from the module's module.id"; +const DBG = window.NC_DBG && window.NC_DBG.lifecycle; +const BAD_PATH = + "module_path must be a string derived from the module's module.id"; /// LIBRARIES ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const PATH = require('system/util/path'); +const PATH = require("system/util/path"); /// DECLARATIONS ////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var PHASE_HOOKS = new Map(); // functions that might right a Promise +var PHASE_HOOKS = new Map(); // functions that might right a Promise /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const PHASES = [ - 'TEST_CONF', // setup tests - 'INITIALIZE', // module data structure init - 'LOADASSETS', // load any external data, make connections - 'CONFIGURE', // configure runtime data structures - 'DOM_READY', // when viewsystem has completely composed - 'RESET', // reset runtime data structures - 'START', // start normal execution run - 'APP_READY', // synchronize to UNISYS network server - 'RUN', // system starts running - 'UPDATE', // system is running (periodic call w/ time) - 'PREPAUSE', // system wants to pause run - 'PAUSE', // system has paused (periodic call w/ time) - 'POSTPAUSE', // system wants to resume running - 'STOP', // system wants to stop current run - 'UNLOADASSETS', // system releases any connections - 'SHUTDOWN' // system wants to shut down - ]; +const PHASES = [ + "TEST_CONF", // setup tests + "INITIALIZE", // module data structure init + "LOADASSETS", // load any external data, make connections + "CONFIGURE", // configure runtime data structures + "DOM_READY", // when viewsystem has completely composed + "RESET", // reset runtime data structures + "START", // start normal execution run + "APP_READY", // synchronize to UNISYS network server + "RUN", // system starts running + "UPDATE", // system is running (periodic call w/ time) + "PREPAUSE", // system wants to pause run + "PAUSE", // system has paused (periodic call w/ time) + "POSTPAUSE", // system wants to resume running + "STOP", // system wants to stop current run + "DISCONNECT", // unisys server has gone offline + "RECONNECT", // unisys server has reconnected + "UNLOADASSETS", // system releases any connections + "SHUTDOWN" // system wants to shut down +]; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var PHASE = PHASES[0]+'_PENDING'; // current phase +var PHASE = PHASES[0] + "_PENDING"; // current phase /// MODULE DEFINITION ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var MOD = { - name : 'LifeCycle', - scope : 'system/booting' // overwritten by UNISYS.SystemInitialize() - }; +var MOD = { + name: "LifeCycle", + scope: "system/booting" // overwritten by UNISYS.SystemInitialize() +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ UTILITY: compare the destination scope with the acceptable scope (the module.id of the root JSX component in a view). Any module not in the system directory will not get called -/*/ function m_ExecuteScopedPhase( phase, o ) { - // check for special unisys or system directory - if (o.scope.indexOf('system')===0) return o.f(); - if (o.scope.indexOf('unisys')===0) return o.f(); - // check for subdirectory - if (o.scope.includes(MOD.scope,0)) return o.f(); - // else do nothing - if (DBG) console.info(`LIFECYCLE: skipping [${phase}] for ${o.scope} because scope is ${MOD.scope}`); - return undefined; - } +/*/ +function m_ExecuteScopedPhase(phase, o) { + // check for special unisys or system directory + if (o.scope.indexOf("system") === 0) return o.f(); + if (o.scope.indexOf("unisys") === 0) return o.f(); + // check for subdirectory + if (o.scope.includes(MOD.scope, 0)) return o.f(); + // else do nothing + if (DBG) + console.info( + `LIFECYCLE: skipping [${phase}] for ${o.scope} because scope is ${ + MOD.scope + }` + ); + return undefined; +} /// LIFECYCLE METHODS ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: register a Phase Handler which is invoked by MOD.Execute() phase is a string constant from PHASES array above f is a function that does work immediately, or returns a Promise -/*/ MOD.Hook = ( phase, f, scope ) => { - // make sure scope is included - if (typeof scope!=='string') throw Error(` scope is required (set to module.id)`); - // does this phase exist? - if (typeof phase!=='string') throw Error(" must be PHASENAME (e.g. 'LOADASSETS')"); - if (!PHASES.includes(phase)) throw Error(phase,"is not a recognized lifecycle phase"); - // did we also get a promise? - if (!(f instanceof Function)) throw Error(" must be a function optionally returning Promise"); - - // get the list of promises associated with this phase - // and add the new promise - if (!PHASE_HOOKS.has(phase)) PHASE_HOOKS.set(phase,[]); - PHASE_HOOKS.get(phase).push({f,scope}); - if (DBG) console.log(`[${phase}] added handler`); - }; +/*/ +MOD.Hook = (phase, f, scope) => { + // make sure scope is included + if (typeof scope !== "string") + throw Error(` scope is required (set to module.id)`); + // does this phase exist? + if (typeof phase !== "string") + throw Error(" must be PHASENAME (e.g. 'LOADASSETS')"); + if (!PHASES.includes(phase)) + throw Error(phase, "is not a recognized lifecycle phase"); + // did we also get a promise? + if (!(f instanceof Function)) + throw Error(" must be a function optionally returning Promise"); + + // get the list of promises associated with this phase + // and add the new promise + if (!PHASE_HOOKS.has(phase)) PHASE_HOOKS.set(phase, []); + PHASE_HOOKS.get(phase).push({ f, scope }); + if (DBG) console.log(`[${phase}] added handler`); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: Execute all Promises associated with a phase, completing when all the callback functions complete. If the callback function returns a Promise, this is added to a list of Promises to wait for before the function returns control to the calling code. -/*/ MOD.Execute = async (phase) => { - // require scope to be set - if (MOD.scope===false) throw Error(`UNISYS.SetScope() must be set to RootJSX View's module.id. Aborting.`); - - // note: contents of PHASE_HOOKs are promise-generating functions - if (!PHASES.includes(phase)) throw Error(`${phase} is not a recognized lifecycle phase`); - let hooks = PHASE_HOOKS.get(phase); - if (hooks===undefined) { - if (DBG) console.log(`[${phase}] no subscribers`); - return; - } - - // phase housekeeping - PHASE = phase+'_PENDING'; - - // now execute handlers and promises - let icount = 0; - if (DBG) console.group(phase); - // get an array of promises - // o contains f, scope pushed in Hook() above - let promises = hooks.map((o) => { - let retval = m_ExecuteScopedPhase(phase,o); - if (retval instanceof Promise) { - icount++; - return retval; - } - // return undefined to signal no special handling - return undefined; - }); - promises = promises.filter((e)=>{return e!==undefined}); - if (DBG && hooks.length) console.log(`[${phase}] HANDLERS PROCESSED : ${hooks.length}`); - if (DBG && icount) console.log(`[${phase}] PROMISES QUEUED : ${icount}`); - - // wait for all promises to execute - await Promise.all(promises). - then((values) => { - if (DBG && values.length) console.log(`[${phase}] PROMISES RETURNED : ${values.length}`,values); - if (DBG) console.groupEnd(); - return values; - }). - catch((err)=>{ - if (DBG) console.log(`[${phase} EXECUTE ERROR ${err}`); - throw Error(`[${phase} EXECUTE ERROR ${err}`); - }); - - // phase housekeeping - PHASE = phase; - }; - +/*/ +MOD.Execute = async phase => { + // require scope to be set + if (MOD.scope === false) + throw Error( + `UNISYS.SetScope() must be set to RootJSX View's module.id. Aborting.` + ); + + // note: contents of PHASE_HOOKs are promise-generating functions + if (!PHASES.includes(phase)) + throw Error(`${phase} is not a recognized lifecycle phase`); + let hooks = PHASE_HOOKS.get(phase); + if (hooks === undefined) { + if (DBG) console.log(`[${phase}] no subscribers`); + return; + } + + // phase housekeeping + PHASE = phase + "_PENDING"; + + // now execute handlers and promises + let icount = 0; + if (DBG) console.group(phase); + // get an array of promises + // o contains f, scope pushed in Hook() above + let promises = hooks.map(o => { + let retval = m_ExecuteScopedPhase(phase, o); + if (retval instanceof Promise) { + icount++; + return retval; + } + // return undefined to signal no special handling + return undefined; + }); + promises = promises.filter(e => { + return e !== undefined; + }); + if (DBG && hooks.length) + console.log(`[${phase}] HANDLERS PROCESSED : ${hooks.length}`); + if (DBG && icount) console.log(`[${phase}] PROMISES QUEUED : ${icount}`); + + // wait for all promises to execute + await Promise.all(promises) + .then(values => { + if (DBG && values.length) + console.log(`[${phase}] PROMISES RETURNED : ${values.length}`, values); + if (DBG) console.groupEnd(); + return values; + }) + .catch(err => { + if (DBG) console.log(`[${phase} EXECUTE ERROR ${err}`); + throw Error(`[${phase} EXECUTE ERROR ${err}`); + }); + + // phase housekeeping + PHASE = phase; +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: The scope is used to filter lifecycle events within a particular application path, which are defined under the view directory. -/*/ MOD.SetScope = ( module_path ) => { - if (typeof module_path!=='string') throw Error(BAD_PATH); - if (DBG) console.log(`setting lifecycle scope to ${module_path}`); - // strip out filename, if one exists - MOD.scope = PATH.Dirname(module_path); - }; +/*/ +MOD.SetScope = module_path => { + if (typeof module_path !== "string") throw Error(BAD_PATH); + if (DBG) console.log(`setting lifecycle scope to ${module_path}`); + // strip out filename, if one exists + MOD.scope = PATH.Dirname(module_path); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: The scope -/*/ MOD.Scope = () => { - return MOD.scope; - }; +/*/ +MOD.Scope = () => { + return MOD.scope; +}; /// STATIC METHODS //////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/app/unisys/client-network.js b/build/app/unisys/client-network.js index 2e77718f..388b4fd7 100644 --- a/build/app/unisys/client-network.js +++ b/build/app/unisys/client-network.js @@ -9,7 +9,7 @@ if (window.NC_DBG) console.log(`inc ${module.id}`); const DBG = { connect: true, handle: false }; /// LOAD LIBRARIES //////////////////////////////////////////////////////////// -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const SETTINGS = require("settings"); const NetMessage = require("unisys/common-netmessage-class"); const PROMPTS = require("system/util/prompts"); @@ -20,13 +20,13 @@ const ERR_NO_SOCKET = "Network socket has not been established yet"; const ERR_BAD_UDATA = "An instance of 'client-datalink-class' is required"; /// GLOBAL NETWORK INFO (INJECTED ON INDEX) /////////////////////////////////// -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var NETSOCK = SETTINGS.EJSProp("socket"); var NETCLIENT = SETTINGS.EJSProp("client"); var NETSERVER = SETTINGS.EJSProp("server"); /// NETWORK ID VALUES ///////////////////////////////////////////////////////// -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const M0_INIT = 0; const M1_CONNECTING = 1; const M2_CONNECTED = 2; @@ -38,12 +38,12 @@ var m_status = M0_INIT; var m_options = {}; /// API METHODS /////////////////////////////////////////////////////////////// -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - var NETWORK = {}; var UDATA = null; // assigned during NETWORK.Connect() /// CONNECT /////////////////////////////////////////////////////////////////// -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Establish connection to UNISYS server. This is called by client.js during NetworkInitialize(), which itself fires after the application has rendered completely. @@ -106,6 +106,8 @@ NETWORK.Connect = function(datalink, opt) { console.info(WARN, "OFFLINE MODE. USING CACHED DATA"); m_status = M_OFFLINE; NetMessage.GlobalOfflineMode(); // deregister socket + // force promise to succeed + if (typeof m_options.success === "function") m_options.success(); break; default: m_status = M_NOCONNECT; @@ -117,7 +119,7 @@ NETWORK.Connect = function(datalink, opt) { NETWORK.AddListener("message", m_HandleRegistrationMessage); }; // Connect() -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ After 'open' event, we expect the first message on the socket to contain network session-related messages /*/ @@ -138,7 +140,7 @@ function m_HandleRegistrationMessage(msgEvent) { // (4) network is initialized if (typeof m_options.success === "function") m_options.success(); } -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function m_HandleMessage(msgEvent) { let pkt = new NetMessage(msgEvent.data); let msg = pkt.Message(); @@ -202,7 +204,7 @@ function m_HandleMessage(msgEvent) { } } -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Send a packet on socket connection, assuming it is valid /*/ NETWORK.Send = function(pkt) { @@ -215,7 +217,7 @@ NETWORK.Send = function(pkt) { console.log("Socket not ReadyState 1, is", NETSOCK.ws.readyState); } }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Send a packet on socket connection, return Promise /*/ NETWORK.Call = function(pkt) { @@ -228,7 +230,7 @@ NETWORK.Call = function(pkt) { console.log("Socket not ReadyState 1, is", NETSOCK.ws.readyState); } }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ Force close of connection, for example if UNISYS.AppReady() fails /*/ NETWORK.Close = function(code, reason) { @@ -236,7 +238,7 @@ NETWORK.Close = function(code, reason) { reason = reason || "unisys forced close"; NETSOCK.ws.close(code, reason); }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.AddListener = function(event, handlerFunction) { if (NETSOCK.ws instanceof WebSocket) { NETSOCK.ws.addEventListener(event, handlerFunction); @@ -244,7 +246,7 @@ NETWORK.AddListener = function(event, handlerFunction) { throw Error(ERR_NO_SOCKET); } }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.RemoveListener = function(event, handlerFunction) { if (NETSOCK.ws instanceof WebSocket) { NETSOCK.ws.removeEventListener(event, handlerFunction); @@ -252,22 +254,26 @@ NETWORK.RemoveListener = function(event, handlerFunction) { throw Error(ERR_NO_SOCKET); } }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.LocalInfo = function() { return NETCLIENT; }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.ServerInfo = function() { return NETSERVER; }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.ServerSocketInfo = function() { return NETSOCK; }; -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETWORK.SocketUADDR = function() { return NetMessage.SocketUADDR(); }; +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +NETWORK.IsOfflineMode = function() { + return m_status === M_OFFLINE; +}; /// EXPORT MODULE DEFINITION ////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/app/unisys/client.js b/build/app/unisys/client.js index c2f066db..bccb6747 100644 --- a/build/app/unisys/client.js +++ b/build/app/unisys/client.js @@ -17,77 +17,81 @@ if (window.NC_DBG) console.log(`inc ${module.id}`); \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ const DBG = { - hook : false + hook: false }; /// CLASSES /////////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const UniData = require('unisys/client-datalink-class'); -const UniModule = require('unisys/client-module-class'); -const UniComponent = require('unisys/client-react-component'); +const UniData = require("unisys/client-datalink-class"); +const UniModule = require("unisys/client-module-class"); +const UniComponent = require("unisys/client-react-component"); /// LIBRARIES ///////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const SETTINGS = require('settings'); -const LIFECYCLE = require('unisys/client-lifecycle'); -const STATE = require('unisys/client-state'); -const NETWORK = require('unisys/client-network'); -const PROMPTS = require('system/util/prompts'); -const PR = PROMPTS.Pad('UNISYS'); +const SETTINGS = require("settings"); +const LIFECYCLE = require("unisys/client-lifecycle"); +const STATE = require("unisys/client-state"); +const NETWORK = require("unisys/client-network"); +const PROMPTS = require("system/util/prompts"); +const PR = PROMPTS.Pad("UNISYS"); /// INITIALIZE MAIN MODULE //////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -var UNISYS = new UniModule(module.id); -var UDATA = new UniData(UNISYS); +var UNISYS = new UniModule(module.id); +var UDATA = new UniData(UNISYS); /// UNISYS MODULE MAKING ////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: Make new module with UNISYS convenience methods -/*/ UNISYS.NewModule = ( uniqueName ) => { - return new UniModule(uniqueName); - }; +/*/ UNISYS.NewModule = uniqueName => { + return new UniModule(uniqueName); +}; /// UNISYS CONNECTOR ////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: Make new module with UNISYS convenience methods -/*/ UNISYS.NewDataLink = ( module, optName ) => { - return new UniData(module,optName); - }; +/*/ UNISYS.NewDataLink = ( + module, + optName +) => { + return new UniData(module, optName); +}; /// UNISYS MESSAGE REGISTRATION /////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UNISYS.RegisterMessagesPromise = ( messages=[] ) => { - if (messages.length) { - try { - messages = UniData.ValidateMessageNames(messages); - } catch (e) { - console.error(e); - } - } else { - messages = UniData.MessageNames(); - } - return new Promise((resolve,reject)=>{ - UDATA.Call('SRV_REG_HANDLERS',{ messages }) - .then((data)=>{ - resolve(data); - }); - }); - }; +UNISYS.RegisterMessagesPromise = (messages = []) => { + if (messages.length) { + try { + messages = UniData.ValidateMessageNames(messages); + } catch (e) { + console.error(e); + } + } else { + messages = UniData.MessageNames(); + } + return new Promise((resolve, reject) => { + UDATA.Call("SRV_REG_HANDLERS", { messages }).then(data => { + resolve(data); + }); + }); +}; /// LIFECYCLE METHODS ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: LIFECYCLE Hook() functions -/*/ UNISYS.Hook = ( phase, f ) => { - if (typeof phase!=='string') throw Error('arg1 is phase as string'); - if (typeof f!=='function') throw Error('arg2 is function callback'); - LIFECYCLE.Hook(phase,f,UNISYS.ModuleID()); // pass phase and hook function - }; +/*/ +UNISYS.Hook = (phase, f) => { + if (typeof phase !== "string") throw Error("arg1 is phase as string"); + if (typeof f !== "function") throw Error("arg2 is function callback"); + LIFECYCLE.Hook(phase, f, UNISYS.ModuleID()); // pass phase and hook function +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: System Initialize -/*/ UNISYS.SystemInitialize = ( module_id ) => { - UNISYS.SetScope(module_id); - SETTINGS.ForceReloadSingleApp(); - }; +/*/ +UNISYS.SystemInitialize = module_id => { + UNISYS.SetScope(module_id); + SETTINGS.ForceReloadSingleApp(); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API HELPER: LIFECYCLE Scope() functions The 'scope' is used by LIFECYCLE to determine what modules implementing @@ -95,162 +99,198 @@ var UDATA = new UniData(UNISYS); be considered the umbrella of "allowed to hook" modules. For REACT apps, this is the root directory of the root view component. Additionally, the unisys and system directories are allowed to run their hooks -/*/ UNISYS.SetScope = ( root_module_id ) => { - LIFECYCLE.SetScope(root_module_id); // pass phase and hook function - } +/*/ UNISYS.SetScope = root_module_id => { + LIFECYCLE.SetScope(root_module_id); // pass phase and hook function +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API HELPER: SETTINGS ForceReloadSingleApp checks to see if settings flag is "dirty"; if it is, then reload the location to ensure no linger apps are running in the background. Yes this is a bit of a hack. -/*/ UNISYS.ForceReloadOnNavigation = () => { - SETTINGS.ForceReloadOnNavigation(); - } +/*/ +UNISYS.ForceReloadOnNavigation = () => { + SETTINGS.ForceReloadOnNavigation(); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API HELPER: return TRUE if passed module.id is within the current set scope -/*/ UNISYS.InScope = ( module_id ) => { - let currentScope = LIFECYCLE.Scope(); - return (module_id.includes(currentScope)) - } +/*/ +UNISYS.InScope = module_id => { + let currentScope = LIFECYCLE.Scope(); + return module_id.includes(currentScope); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: application startup -/*/ UNISYS.EnterApp = () => { - return new Promise( async ( resolve, reject ) => { - try { - await LIFECYCLE.Execute('TEST_CONF'); // TESTCONFIG hook - await LIFECYCLE.Execute('INITIALIZE'); // INITIALIZE hook - await LIFECYCLE.Execute('LOADASSETS'); // LOADASSETS hook - await LIFECYCLE.Execute('CONFIGURE'); // CONFIGURE support modules - resolve(); - } catch (e) { - console.error('EnterApp() Lifecycle Error. Check phase execution order effect on data validity.\n',e); - debugger; - } - }); - }; +/*/ +UNISYS.EnterApp = () => { + return new Promise(async (resolve, reject) => { + try { + await LIFECYCLE.Execute("TEST_CONF"); // TESTCONFIG hook + await LIFECYCLE.Execute("INITIALIZE"); // INITIALIZE hook + await LIFECYCLE.Execute("LOADASSETS"); // LOADASSETS hook + await LIFECYCLE.Execute("CONFIGURE"); // CONFIGURE support modules + resolve(); + } catch (e) { + console.error( + "EnterApp() Lifecycle Error. Check phase execution order effect on data validity.\n", + e + ); + debugger; + } + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: call this when the view system's DOM has stabilized and is ready for manipulation by other code -/*/ UNISYS.SetupDOM = () => { - return new Promise( async ( resolve, reject ) => { - try { - await LIFECYCLE.Execute('DOM_READY'); // GUI layout has finished composing - resolve(); - } catch (e) { - console.error('SetupDOM() Lifecycle Error. Check phase execution order effect on data validity.\n',e); - debugger; - } - }); - }; +/*/ +UNISYS.SetupDOM = () => { + return new Promise(async (resolve, reject) => { + try { + await LIFECYCLE.Execute("DOM_READY"); // GUI layout has finished composing + resolve(); + } catch (e) { + console.error( + "SetupDOM() Lifecycle Error. Check phase execution order effect on data validity.\n", + e + ); + debugger; + } + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: network startup -/*/ UNISYS.JoinNet = () => { - return new Promise(( resolve, reject ) => { - try { - NETWORK.Connect(UDATA,{success: resolve}); - } catch (e) { - console.error('EnterNet() Lifecycle Error. Check phase execution order effect on data validity.\n',e); - debugger; - } - }); - }; +/*/ +UNISYS.JoinNet = () => { + return new Promise((resolve, reject) => { + try { + NETWORK.Connect(UDATA, { success: resolve, failure: reject }); + } catch (e) { + console.error( + "EnterNet() Lifecycle Error. Check phase execution order effect on data validity.\n", + e + ); + debugger; + } + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: configure system before run -/*/ UNISYS.SetupRun = () => { - return new Promise( async ( resolve, reject ) => { - try { - await LIFECYCLE.Execute('RESET'); // RESET runtime datastructures - await LIFECYCLE.Execute('START'); // START running - await LIFECYCLE.Execute('APP_READY'); // tell network APP_READY - await LIFECYCLE.Execute('RUN'); // tell network APP_READY - resolve(); - } catch (e) { - console.error('SetupRun() Lifecycle Error. Check phase execution order effect on data validity.\n',e); - debugger; - } - }); - }; +/*/ +UNISYS.SetupRun = () => { + return new Promise(async (resolve, reject) => { + try { + await LIFECYCLE.Execute("RESET"); // RESET runtime datastructures + await LIFECYCLE.Execute("START"); // START running + await LIFECYCLE.Execute("APP_READY"); // tell network APP_READY + await LIFECYCLE.Execute("RUN"); // tell network APP_READY + resolve(); + } catch (e) { + console.error( + "SetupRun() Lifecycle Error. Check phase execution order effect on data validity.\n", + e + ); + debugger; + } + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: handle periodic updates for a simulation-driven timestep -/*/ UNISYS.Run = () => { - return new Promise( async ( resolve, reject ) => { - try { - await LIFECYCLE.Execute('UPDATE'); - resolve(); - } catch (e) { - console.error(e); - } - }); - }; +/*/ +UNISYS.Run = () => { + return new Promise(async (resolve, reject) => { + try { + await LIFECYCLE.Execute("UPDATE"); + resolve(); + } catch (e) { + console.error(e); + } + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: do the Shutdown lifecycle NOTE ASYNC ARROW FUNCTION (necessary?) -/*/ UNISYS.BeforePause = () => { - return new Promise( async ( resolve, reject ) => { - await LIFECYCLE.Execute('PREPAUSE'); - resolve(); - }); - }; +/*/ +UNISYS.BeforePause = () => { + return new Promise(async (resolve, reject) => { + await LIFECYCLE.Execute("PREPAUSE"); + resolve(); + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: do the Shutdown lifecycle NOTE ASYNC ARROW FUNCTION (necessary?) -/*/ UNISYS.Paused = () => { - return new Promise( async ( resolve, reject ) => { - await LIFECYCLE.Execute('PAUSE'); - resolve(); - }); - }; +/*/ +UNISYS.Paused = () => { + return new Promise(async (resolve, reject) => { + await LIFECYCLE.Execute("PAUSE"); + resolve(); + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: do the Shutdown lifecycle NOTE ASYNC ARROW FUNCTION (necessary?) -/*/ UNISYS.PostPause = () => { - return new Promise( async ( resolve, reject ) => { - await LIFECYCLE.Execute('POSTPAUSE'); - resolve(); - }); - }; +/*/ +UNISYS.PostPause = () => { + return new Promise(async (resolve, reject) => { + await LIFECYCLE.Execute("POSTPAUSE"); + resolve(); + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: do the Shutdown lifecycle NOTE ASYNC ARROW FUNCTION (necessary?) -/*/ UNISYS.CleanupRun = () => { - return new Promise( async ( resolve, reject ) => { - await LIFECYCLE.Execute('STOP'); - resolve(); - }); - }; +/*/ +UNISYS.CleanupRun = () => { + return new Promise(async (resolve, reject) => { + await LIFECYCLE.Execute("STOP"); + resolve(); + }); +}; +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/*/ API: application offline + NOTE ASYNC ARROW FUNCTION (necessary?) +/*/ +UNISYS.ServerDisconnect = () => { + return new Promise(async (resolve, reject) => { + await LIFECYCLE.Execute("DISCONNECT"); + resolve(); + }); +}; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ API: application shutdown NOTE ASYNC ARROW FUNCTION (necessary?) -/*/ UNISYS.ExitApp = () => { - return new Promise( async ( resolve, reject ) => { - await LIFECYCLE.Execute('UNLOADASSETS'); - await LIFECYCLE.Execute('SHUTDOWN'); - resolve(); - }); - }; +/*/ +UNISYS.ExitApp = () => { + return new Promise(async (resolve, reject) => { + await LIFECYCLE.Execute("UNLOADASSETS"); + await LIFECYCLE.Execute("SHUTDOWN"); + resolve(); + }); +}; /// NETWORK INFORMATION /////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ return the current connected Socket Address (e.g. UADDR_12) -/*/ UNISYS.SocketUADDR = () => { - return NETWORK.SocketUADDR(); - }; +/*/ +UNISYS.SocketUADDR = () => { + return NETWORK.SocketUADDR(); +}; /// DATA LOGGING ////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ send a logging message -/*/ UNISYS.Log = ( event, ...items ) => { - if (typeof event!=='string') { - console.error("UNISYS.Log( 'eventString', value, value, value... )"); - } - UDATA.NetSignal('SRV_LOG_EVENT',{event,items}); - }; +/*/ +UNISYS.Log = (event, ...items) => { + if (typeof event !== "string") { + console.error("UNISYS.Log( 'eventString', value, value, value... )"); + } + UDATA.NetSignal("SRV_LOG_EVENT", { event, items }); +}; /// REACT INTEGRATION ///////////////////////////////////////////////////////// /*/ return the referene to the UNISYS extension of React.Component -/*/ UNISYS.Component = UniComponent; - +/*/ +UNISYS.Component = UniComponent; /// EXPORT MODULE DEFINITION ////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/app/unisys/common-netmessage-class.js b/build/app/unisys/common-netmessage-class.js index aaa6b4b2..bf10efc3 100644 --- a/build/app/unisys/common-netmessage-class.js +++ b/build/app/unisys/common-netmessage-class.js @@ -5,7 +5,7 @@ a NetMessage does not require addressing since the SERVER distributes messages to UNISYS addresses that have registered for them. - The NetMessage declaration is shared in both node and browser javascript + The NetMessage declaration is SHARED in both node and browser javascript codebases. NetMessages also provide the data context for "transactions" of calls. @@ -270,6 +270,10 @@ class NetMessage { /*/ Create a promise to resolve when packet returns /*/ QueueTransaction(socket = m_netsocket) { + if (m_mode === M_OFFLINE) { + console.log("ISOFFLINE"); + return Promise.resolve(); + } // global m_netsocket is not defined on server, since packets arrive on multiple sockets if (!socket) throw Error("QueueTransaction(sock) requires a valid socket"); // save our current UADDR @@ -357,6 +361,8 @@ class NetMessage { /// STATIC CLASS METHODS ////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ set the NETWORK interface object that implements Send() + This class operates both under the server and the client. + This is a client feature. /*/ NetMessage.GlobalSetup = function(config) { let { netsocket, uaddr } = config; @@ -370,9 +376,9 @@ NetMessage.GlobalSetup = function(config) { } }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/*/ cleanup any allocated storage +/*/ cleanup any allocated storage. This class operates both under the + server and the client. This is a client feature. /*/ - NetMessage.GlobalCleanup = function() { if (m_netsocket) { console.log(PR, "GlobalCleanup: deallocating netsocket, mode closed"); @@ -381,15 +387,17 @@ NetMessage.GlobalCleanup = function() { } }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/*/ cleanup any allocated storage +/*/ cleanup any allocated storage internally. This class operates both under the + server and the client. This is a client feature. /*/ NetMessage.GlobalOfflineMode = function() { + m_mode = M_OFFLINE; if (m_netsocket) { - console.log(PR, "GlobalDisconnect: deallocating netsocket"); + console.error(PR, "GlobalDisconnect: deallocating netsocket"); m_netsocket = null; - m_mode = M_OFFLINE; - } else { - console.log(PR, "GlobalDisconnect: netsocket already deallocated"); + let event = new CustomEvent("UNISYSDisconnect", {}); + console.log("dispatching event to", document, event); + document.dispatchEvent(event); } }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/app/view/netcreate/nc-logic.js b/build/app/view/netcreate/nc-logic.js index 76d47acc..fd646aff 100644 --- a/build/app/view/netcreate/nc-logic.js +++ b/build/app/view/netcreate/nc-logic.js @@ -143,110 +143,149 @@ const SEARCH_COLOR = "#008800"; const SOURCE_COLOR = "#0000DD"; const TARGET_COLOR = "#FF0000"; +const TEMPLATE_URL = "../templates/alexander.json"; + /// UNISYS LIFECYCLE HOOKS //////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ LOADASSETS fires before react components are loaded see client-lifecycle.js for description /*/ MOD.Hook("LOADASSETS", () => { - // load data into D3DATA - let p1 = DATASTORE.PromiseD3Data().then(data => { - if (DBG) console.log(PR, "DATASTORE returned data", data); - m_ConvertData(data); - m_RecalculateAllEdgeWeights(data); - UDATA.SetAppState("D3DATA", data); - // Save off local reference because we don't have D3DATA AppStateChange handler - D3DATA = data; - }); - // load Template data and return it as a promise - // so that react render is called only after the template is loaded - let templateURL = "../templates/alexander.json"; - let p2 = DATASTORE.PromiseJSONFile(templateURL).then(data => { - if (DBG) console.log(PR, "DATASTORE returned json", data); - TEMPLATE = data; - UDATA.SetAppState("TEMPLATE", TEMPLATE); - // Process Node, NodeColorMap and Edge options - - // Validate the template file - try { - // nodePrompts - let nodePrompts = TEMPLATE.nodePrompts; - if (nodePrompts === undefined) { - throw "Missing `nodePrompts` nodePrompts=" + nodePrompts; - } - if (nodePrompts.label === undefined) - throw "Missing `nodePrompts.label` label=" + nodePrompts.label; - if (nodePrompts.type === undefined) - throw "Missing `nodePrompts.type` type= " + nodePrompts.type; - if ( - nodePrompts.type.options === undefined || - !Array.isArray(nodePrompts.type.options) - ) { - throw "Missing or bad `nodePrompts.type.options` options=" + - nodePrompts.type.options; - } - if (nodePrompts.notes === undefined) - throw "Missing `nodePrompts.notes` notes=" + nodePrompts.notes; - if (nodePrompts.info === undefined) - throw "Missing `nodePrompts.info` info=" + nodePrompts.info; - - // edgePrompts - let edgePrompts = TEMPLATE.edgePrompts; - if (edgePrompts === undefined) - throw "Missing `edgePrompts` edgePrompts=" + edgePrompts; - if (edgePrompts.source === undefined) - throw "Missing `edgePrompts.source` source=" + edgePrompts.source; - if (edgePrompts.type === undefined) - throw "Missing `edgePrompts.type` type= " + edgePrompts.type; - if ( - edgePrompts.type.options === undefined || - !Array.isArray(edgePrompts.type.options) - ) { - throw "Missing or bad `edgePrompts.type.options` options=" + - edgePrompts.type.options; - } - if (edgePrompts.target === undefined) - throw "Missing `edgePrompts.target` label=" + edgePrompts.target; - if (edgePrompts.notes === undefined) - throw "Missing `edgePrompts.notes` notes=" + edgePrompts.notes; - if (edgePrompts.info === undefined) - throw "Missing `edgePrompts.info` info=" + edgePrompts.info; - if (edgePrompts.citation === undefined) - throw "Missing `edgePrompts.citation` info=" + edgePrompts.citation; - } catch (error) { - console.error( - PR + "Error loading template `", - templateURL, - "`::::", - error - ); - } + if (NETWORK.IsOfflineMode()) { + console.warn("NCLOGIC LOADASSETS DETECT OFFLINE MODE"); + return new Promise((resolve, reject) => { + const lstore = window.localStorage; + let ld3 = lstore.getItem("D3DATA"); + D3DATA = JSON.parse(ld3); + if (!D3DATA) reject(Error("couldn't get D3DATA from Local Store")); + UDATA.SetAppState("D3DATA", D3DATA); + let tem = lstore.getItem("TEMPLATE"); + TEMPLATE = JSON.parse(tem); + console.log(D3DATA, TEMPLATE); + if (!TEMPLATE) reject(Error("couldn't get TEMPLATE from Local Store")); + UDATA.SetAppState("TEMPLATE", TEMPLATE); + resolve(); + }); + } else { + // not offline startup, so access network + // load data into D3DATA + let p1 = DATASTORE.PromiseD3Data().then(data => { + if (DBG) console.log(PR, "DATASTORE returned data", data); + m_ConvertData(data); + m_RecalculateAllEdgeWeights(data); + UDATA.SetAppState("D3DATA", data); + // Save off local reference because we don't have D3DATA AppStateChange handler + D3DATA = data; + }); + // load Template data and return it as a promise + // so that react render is called only after the template is loaded + let p2 = DATASTORE.PromiseJSONFile(TEMPLATE_URL).then(data => { + if (DBG) console.log(PR, "DATASTORE returned json", data); + TEMPLATE = data; + UDATA.SetAppState("TEMPLATE", TEMPLATE); + }); + return Promise.all([p1, p2]); + } +}); // loadassets - // REVIEW: Load ColorMap in d3? or elsewhere? does it need its own state? - try { - let nodeColorMap = {}; - TEMPLATE.nodePrompts.type.options.forEach(o => { - nodeColorMap[o.label] = o.color; - }); - UDATA.SetAppState("NODECOLORMAP", nodeColorMap); - } catch (error) { - console.error( - PR, - "received bad TEMPLATE node options. ERROR:", - error, - ". DATA:", - data - ); +/// UNISYS LIFECYCLE HOOKS //////////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/*/ CONFIGURE fires after LOADASSETS, so this is a good place to put TEMPLATE + validation. +/*/ +MOD.Hook("CONFIGURE", () => { + // Process Node, NodeColorMap and Edge options + + // Validate the template file + try { + // nodePrompts + let nodePrompts = TEMPLATE.nodePrompts; + if (nodePrompts === undefined) { + throw "Missing `nodePrompts` nodePrompts=" + nodePrompts; } - }); + if (nodePrompts.label === undefined) + throw "Missing `nodePrompts.label` label=" + nodePrompts.label; + if (nodePrompts.type === undefined) + throw "Missing `nodePrompts.type` type= " + nodePrompts.type; + if ( + nodePrompts.type.options === undefined || + !Array.isArray(nodePrompts.type.options) + ) { + throw "Missing or bad `nodePrompts.type.options` options=" + + nodePrompts.type.options; + } + if (nodePrompts.notes === undefined) + throw "Missing `nodePrompts.notes` notes=" + nodePrompts.notes; + if (nodePrompts.info === undefined) + throw "Missing `nodePrompts.info` info=" + nodePrompts.info; + + // edgePrompts + let edgePrompts = TEMPLATE.edgePrompts; + if (edgePrompts === undefined) + throw "Missing `edgePrompts` edgePrompts=" + edgePrompts; + if (edgePrompts.source === undefined) + throw "Missing `edgePrompts.source` source=" + edgePrompts.source; + if (edgePrompts.type === undefined) + throw "Missing `edgePrompts.type` type= " + edgePrompts.type; + if ( + edgePrompts.type.options === undefined || + !Array.isArray(edgePrompts.type.options) + ) { + throw "Missing or bad `edgePrompts.type.options` options=" + + edgePrompts.type.options; + } + if (edgePrompts.target === undefined) + throw "Missing `edgePrompts.target` label=" + edgePrompts.target; + if (edgePrompts.notes === undefined) + throw "Missing `edgePrompts.notes` notes=" + edgePrompts.notes; + if (edgePrompts.info === undefined) + throw "Missing `edgePrompts.info` info=" + edgePrompts.info; + if (edgePrompts.citation === undefined) + throw "Missing `edgePrompts.citation` info=" + edgePrompts.citation; + } catch (error) { + console.error( + PR + "Error loading template `", + TEMPLATE_URL, + "`::::", + error + ); + } + + // REVIEW: Load ColorMap in d3? or elsewhere? does it need its own state? + try { + let nodeColorMap = {}; + TEMPLATE.nodePrompts.type.options.forEach(o => { + nodeColorMap[o.label] = o.color; + }); + UDATA.SetAppState("NODECOLORMAP", nodeColorMap); + } catch (error) { + console.error( + PR, + "received bad TEMPLATE node options. ERROR:", + error, + ". DATA:", + TEMPLATE + ); + } +}); // end CONFIGURE HOOK - return Promise.all([p1, p2]); -}); // end LOADASSETS HOOK +/// UNISYS LIFECYCLE HOOKS //////////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/*/ DISCONNECT fires when NetMessage.GlobalOfflineMode() +/*/ +MOD.Hook("DISCONNECT", () => { + console.log("DISCONNECT HOOK"); + const lstore = window.localStorage; + lstore.setItem("D3DATA", JSON.stringify(D3DATA)); + lstore.setItem("TEMPLATE", JSON.stringify(TEMPLATE)); + console.log("saving d3data, template to localstore"); +}); /// UNISYS HANDLERS /////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ lifecycle INITIALIZE handler -/*/ MOD.Hook("INITIALIZE", () => { +/*/ +MOD.Hook("INITIALIZE", () => { /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ Handle D3-related updates based on state changes. Subcomponents are responsible for updating themselves. @@ -267,10 +306,10 @@ MOD.Hook("LOADASSETS", () => { } } }); // StateChange SELECTION + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ Search field has been updated /*/ - UDATA.OnAppStateChange("SEARCH", stateChange => { if (DBG) console.log("nc-logic: Got SEARCH", stateChange); let { nodes, edges } = stateChange; @@ -292,6 +331,7 @@ MOD.Hook("LOADASSETS", () => { m_SetStrokeColorThatMatch(searchLabel, SEARCH_COLOR); } }); // StateChange SELECTION + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ User has clicked on a suggestion from the AutoCopmlete suggestion list. The source node should be loaded in NodeSelector. @@ -302,61 +342,59 @@ MOD.Hook("LOADASSETS", () => { in the app at any one time, though nodeLabels is passed as an array. SEE ALSO: AutoComplete.onSuggestionSelected() and D3SimpleNetGraph._UpdateGraph click handler - /*/ UDATA.HandleMessage( - "SOURCE_SELECT", - function(data) { - if (DBG) console.log(PR, "SOURCE_SELECT got data", data); + /*/ + UDATA.HandleMessage("SOURCE_SELECT", function(data) { + if (DBG) console.log(PR, "SOURCE_SELECT got data", data); - let { nodeLabels = [], nodeIDs = [] } = data; - let nodeLabel = nodeLabels.shift(); - let nodeID = nodeIDs.shift(); - let node, newState; + let { nodeLabels = [], nodeIDs = [] } = data; + let nodeLabel = nodeLabels.shift(); + let nodeID = nodeIDs.shift(); + let node, newState; - if (nodeID) { - node = m_FindNodeById(nodeID); // Node IDs should be integers, not strings - } else if (nodeLabel) { - node = m_FindMatchingNodesByLabel(nodeLabel).shift(); - } else { - // No node selected, so deselect - } + if (nodeID) { + node = m_FindNodeById(nodeID); // Node IDs should be integers, not strings + } else if (nodeLabel) { + node = m_FindMatchingNodesByLabel(nodeLabel).shift(); + } else { + // No node selected, so deselect + } - if (DBG) console.log(PR, "SOURCE_SELECT found", node); + if (DBG) console.log(PR, "SOURCE_SELECT found", node); - if (node === undefined) { - // Node not found, create a new node - newState = { - nodes: [], - edges: [] - }; + if (node === undefined) { + // Node not found, create a new node + newState = { + nodes: [], + edges: [] + }; + } else { + // Load existing node and edges + let edges = []; + if (nodeID) { + edges = edges.concat( + D3DATA.edges.filter( + edge => edge.source.id === nodeID || edge.target.id === nodeID + ) + ); } else { - // Load existing node and edges - let edges = []; - if (nodeID) { - edges = edges.concat( - D3DATA.edges.filter( - edge => edge.source.id === nodeID || edge.target.id === nodeID - ) - ); - } else { - edges = edges.concat( - D3DATA.edges.filter( - edge => - edge.source.label === nodeLabel || - edge.target.label === nodeLabel - ) - ); - } - // create state change object - newState = { - nodes: [node], - edges: edges - }; + edges = edges.concat( + D3DATA.edges.filter( + edge => + edge.source.label === nodeLabel || edge.target.label === nodeLabel + ) + ); } - - // Set the SELECTION state so that listeners such as NodeSelectors update themselves - UDATA.SetAppState("SELECTION", newState); + // create state change object + newState = { + nodes: [node], + edges: edges + }; } - ); + + // Set the SELECTION state so that listeners such as NodeSelectors update themselves + UDATA.SetAppState("SELECTION", newState); + }); + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ SOURCE_SEARCH sets the current matching term as entered in an AutoComplete field. @@ -373,6 +411,7 @@ MOD.Hook("LOADASSETS", () => { // let SELECTION state listeners handle display updates UDATA.SetAppState("SEARCH", newState); }); + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ SOURCE_HILITE updates the currently rolled-over node name in a list of selections. The hilite can be selected via either the label or @@ -412,6 +451,7 @@ MOD.Hook("LOADASSETS", () => { if (updatedNodes.length === 0) D3DATA.nodes.push(node); UDATA.SetAppState("D3DATA", D3DATA); }); + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ EDGE_UPDATE is called when the properties of an edge has changed NOTE: SOURCE_UPDATE can be invoked remotely by the server on a DATABASE @@ -470,6 +510,7 @@ MOD.Hook("LOADASSETS", () => { } UDATA.SetAppState("D3DATA", D3DATA); }); + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ EDGE_DELETE is called when an edge should be removed from...something? /*/ @@ -502,6 +543,7 @@ MOD.Hook("LOADASSETS", () => { edges: edges }); }); + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inside hook /*/ AUTOCOMPLETE_SELECT is called by components to tell the module which one has the current focus. From 469e0ad239bb5725bafae0ed7cd73e683af5eb9a Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Wed, 9 Jan 2019 22:10:58 -0500 Subject: [PATCH 08/13] dev-ds-offline-view: fix syntax error introduced by merge --- build/app/unisys/common-netmessage-class.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/app/unisys/common-netmessage-class.js b/build/app/unisys/common-netmessage-class.js index 057e16df..f6a2a205 100644 --- a/build/app/unisys/common-netmessage-class.js +++ b/build/app/unisys/common-netmessage-class.js @@ -224,8 +224,6 @@ class NetMessage { /*/ return an informational string about the packet useful for logging /*/ Info(key) { - key - ) { switch (key) { case "src": /* falls-through */ default: From ae6d7a260a5e758af6d10f08fda319ffa5b6ee74 Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Sat, 12 Jan 2019 01:18:54 -0500 Subject: [PATCH 09/13] dev-ds-offline-view: remove cache manifest --- build/app/assets/index.ejs | 2 +- build/app/assets/netcreate.cache | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 build/app/assets/netcreate.cache diff --git a/build/app/assets/index.ejs b/build/app/assets/index.ejs index 0fbb45a8..0210f848 100644 --- a/build/app/assets/index.ejs +++ b/build/app/assets/index.ejs @@ -1,5 +1,5 @@ - + NetCreate diff --git a/build/app/assets/netcreate.cache b/build/app/assets/netcreate.cache deleted file mode 100644 index 7a124891..00000000 --- a/build/app/assets/netcreate.cache +++ /dev/null @@ -1,10 +0,0 @@ -CACHE MANIFEST -# see https://www.html5rocks.com/en/tutorials/appcache/beginner/ -# note that HTML file with the manifest reference is automatically cached -scripts/netc-lib.js -scripts/netc-app.js -styles/netc-app.css -favicon.ico -templates/alexander.json -# will be auto generated in the future -# 059 \ No newline at end of file From 2d09470a896e6f0deca107c895acd9231e9a2e0f Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Sat, 12 Jan 2019 01:20:42 -0500 Subject: [PATCH 10/13] dev-ds-offline-view: add classroom and classroom_zip brunch configurations, with NPM script entries --- build/app/unisys/server-database.js | 4 +- build/brunch-config.js | 36 +++++++++- build/brunch-server.js | 104 ++++++++++++++-------------- build/package.json | 4 +- 4 files changed, 89 insertions(+), 59 deletions(-) diff --git a/build/app/unisys/server-database.js b/build/app/unisys/server-database.js index 42e94a0f..c0f47de8 100644 --- a/build/app/unisys/server-database.js +++ b/build/app/unisys/server-database.js @@ -4,7 +4,7 @@ DATABASE SERVER \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ -const DBG = true; +const DBG = false; /// LOAD LIBRARIES //////////////////////////////////////////////////////////// /// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = @@ -37,7 +37,6 @@ let DB = {}; /*/ API: Initialize the database /*/ DB.InitializeDatabase = function(options = {}) { - console.log(PR, `InitializeDatabase`); FS.ensureDir(PATH.dirname(DB_FILE)); if (!FS.existsSync(DB_FILE)) { console.log(PR, `No ${DB_FILE} yet, so filling from ${DB_CLONEMASTER}...`); @@ -54,7 +53,6 @@ DB.InitializeDatabase = function(options = {}) { ropt = Object.assign(ropt, options); m_db = new Loki(DB_FILE, ropt); m_options = ropt; - console.log(PR, `Initialized LokiJS Database '${DB_FILE}'`); // callback on load function f_DatabaseInitialize() { diff --git a/build/brunch-config.js b/build/brunch-config.js index 681ba18a..a4dd76d0 100644 --- a/build/brunch-config.js +++ b/build/brunch-config.js @@ -60,7 +60,8 @@ module.exports = { // npm i --save-dev babel babel-preset-env babel-preset-react // npm i --save-dev babel-brunch@github:babel/babel-brunch presets : ['env', 'react'] - } + }, + autoReload : { enabled: true } }, /// SERVER CONFIGURATION ////////////////////////////////////////////////////// @@ -71,7 +72,7 @@ module.exports = { /*/ server: { // viewing url is http://localhost:3000 port : 3000 - }, + }, /// NPM INTEGRATION /////////////////////////////////////////////////////////// /*/ Brunch is aware of the node_modules directory but sometimes needs help to @@ -84,6 +85,35 @@ module.exports = { globals: { jquery: 'jquery' } + }, + +/// OVERRIDES FOR PRODUCTION ////////////////////////////////////////////////// +/*/ Brunch configuration settings default to development mode in the + environment. You can override each env (e.g. production) after all other + declarations are done. +/*/ overrides: { + classroom: { + plugins: { + autoReload: { enabled: false } + }, + hooks: { + onCompile() { + const server = require("./brunch-server"); + return new Promise((resolve, reject) => { + server({ port : 3000}, function() { + resolve(); + }); + }); + } + } + }, + classroom_zip: { + optimize: false, + sourceMaps: true, + plugins: { + autoReload: { enabled: false } + } + } } -}; +}; // module.exports diff --git a/build/brunch-server.js b/build/brunch-server.js index e2d765dc..5312ebb4 100644 --- a/build/brunch-server.js +++ b/build/brunch-server.js @@ -24,28 +24,31 @@ const GIT = PROMPTS.Pad('GIT'); var UKEY_IDX = 0; const USRV_START = new Date(Date.now()).toISOString(); +/// BRUNCH CUSTOM SERVER START FUNCTION /////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +module.exports = (config, callback) => { /// STARTUP UNISYS //////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /*/ This happens early because we need to inject UNISYS connection parameters - into index.ejs + into index.ejs /*/ let unetOptions = UNISYS.InitializeNetwork(); - console.log(PR,'Created Network',unetOptions); /// CONFIGURE EXPRESS ///////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /// declare paths used by Express configuration const PATH_PUBLIC = PATH.join(__dirname,'/public'); const PATH_TEMPLATE = PATH.join(__dirname,'/app/assets'); + /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /// configure cookies middleware (appears in req.cookies) APP.use(COOKIEP()); /// configure headers to allow cross-domain requests of media elements - APP.use(function(req, res, next) { - res.header("Access-Control-Allow-Origin", "*"); - res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + APP.use(function(req, res, next) { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); - }); + }); /// configure template engine then serve templated index.ejs page APP.set('view engine','ejs'); APP.get('/', function(req, res, next) { @@ -87,51 +90,48 @@ const USRV_START = new Date(Date.now()).toISOString(); res.send('POST action completed!'); }); -/// BRUNCH CUSTOM SERVER START FUNCTION /////////////////////////////////////// -/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -/*/ the brunch build tool will call this exported function to start the server -/*/ module.exports = (config, callback) => { - // start app listener - APP.listen(config.port, function () { - // setup prompts - console.log(PR); - console.log(PR,DP,'GO TO ONE OF THESE URLS in CHROME WEB BROWSER',DP); - console.log(PR,DP,'MAINAPP - http://localhost:'+config.port); - console.log(PR,DP,'CLIENTS - http://'+IP.address()+':'+config.port); - console.log(PR); - // git branch information - EXEC('git symbolic-ref --short -q HEAD',(error,stdout,stderr) => { - if (error) { - // console.error(BP,'git symbolic-ref query error',error); - console.log(GIT,'You are running a branch'); - } - if (stdout) { - stdout = stdout.trim(); - console.log(GIT,'You are running the "'+stdout+'" branch'); - } - }); - // now start the UNISYS network - UNISYS.RegisterHandlers(); - UNISYS.StartNetwork(); - // invoke brunch callback - callback(); - }). - on('error', function(err) { - let errstring = `### NETCREATE STARTUP ERROR: '${err.errno}'\n`; - switch (err.errno) { - case 'EADDRINUSE': - errstring += `Another program is already using port ${config.port}.\n`; - errstring += `Go to "http://localhost:${config.port}" to check if NetCreate is already running.\n\n`; - errstring += `Still broken? See https://github.com/daveseah/netcreate-2018/issues/4\n`; - break; - default: - errstring += `${err}`; - console.log(err); +/// START APP SERVER ////////////////////////////////////////////////////////// + APP.listen(config.port, function () { + // setup prompts + console.log(PR); + console.log(PR,DP,'GO TO ONE OF THESE URLS in CHROME WEB BROWSER',DP); + console.log(PR,DP,'MAINAPP - http://localhost:'+config.port); + console.log(PR,DP,'CLIENTS - http://'+IP.address()+':'+config.port); + console.log(PR); + // git branch information + EXEC('git symbolic-ref --short -q HEAD',(error,stdout,stderr) => { + if (error) { + // console.error(BP,'git symbolic-ref query error',error); + console.log(GIT,'You are running a branch'); + } + if (stdout) { + stdout = stdout.trim(); + console.log(GIT,'You are running the "'+stdout+'" branch'); } - console.log(`\n\n${errstring}\n### PROGRAM STOP\n`); - throw new Error(err.errno); }); - // Return the APP; it has the `close()` method, which would be ran when - // Brunch server is terminated. This is a requirement. - return APP; - }; + // now start the UNISYS network + UNISYS.RegisterHandlers(); + UNISYS.StartNetwork(); + // invoke brunch callback + callback(); + }). + on('error', function(err) { + let errstring = `### NETCREATE STARTUP ERROR: '${err.errno}'\n`; + switch (err.errno) { + case 'EADDRINUSE': + errstring += `Another program is already using port ${config.port}.\n`; + errstring += `Go to "http://localhost:${config.port}" to check if NetCreate is already running.\n\n`; + errstring += `Still broken? See https://github.com/daveseah/netcreate-2018/issues/4\n`; + break; + default: + errstring += `${err}`; + console.log(err); + } + console.log(`\n\n${errstring}\n### PROGRAM STOP\n`); + throw new Error(err.errno); + }); + // Return the APP; it has the `close()` method, which would be ran when + // Brunch server is terminated. This is a requirement. + return APP; + + }; // module.exports diff --git a/build/package.json b/build/package.json index e159587a..ab5d6c48 100644 --- a/build/package.json +++ b/build/package.json @@ -5,9 +5,11 @@ "scripts": { "bare": "node --inspect brunch-server.js", "dev": "brunch watch -s", + "start": "brunch build -e classroom", + "zip": "brunch build -e classroom_zip", "clean": "rm -rf ./public ./node_modules; npm ci", "debug": "LOGGY_STACKS=true BRUNCH_DEVTOOLS=true ./brunch-debug watch --server", - "logtail": "tail -f -n100 \"$(ls -at ./runtime/logs/* | head -n 1)\"" + "log": "tail -f -n100 \"$(ls -at ./runtime/logs/* | head -n 1)\"" }, "repository": { "type": "git", From 55cb29334bb2054844e49443a24666d4d059434c Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Sat, 12 Jan 2019 09:38:22 -0500 Subject: [PATCH 11/13] dev-ds-offline-view: cleanup format ruined by prettifier --- build/app/unisys/server-database.js | 35 ++++++++++------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/build/app/unisys/server-database.js b/build/app/unisys/server-database.js index c0f47de8..59ec1217 100644 --- a/build/app/unisys/server-database.js +++ b/build/app/unisys/server-database.js @@ -99,9 +99,8 @@ DB.InitializeDatabase = function(options = {}) { function f_AutosaveStatus() { let nodeCount = NODES.count(); let edgeCount = EDGES.count(); - if (DBG) - console.log( - PR, + if (DBG) console.log( + PR, `autosaving ${nodeCount} nodes and ${edgeCount} edges...` ); } @@ -114,8 +113,7 @@ DB.InitializeDatabase = function(options = {}) { DB.PKT_GetDatabase = function(pkt) { let nodes = NODES.chain().data({ removeMeta: true }); let edges = EDGES.chain().data({ removeMeta: true }); - if (DBG) - console.log( + if (DBG) console.log( PR, `PKT_GetDatabase ${pkt.Info()} (loaded ${nodes.length} nodes, ${ edges.length @@ -147,15 +145,13 @@ DB.PKT_SetDatabase = function(pkt) { /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DB.PKT_GetNewNodeID = function(pkt) { m_max_nodeID += 1; - if (DBG) - console.log(PR, `PKT_GetNewNodeID ${pkt.Info()} nodeID ${m_max_nodeID}`); + if (DBG) console.log(PR, `PKT_GetNewNodeID ${pkt.Info()} nodeID ${m_max_nodeID}`); return { nodeID: m_max_nodeID }; }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - DB.PKT_GetNewEdgeID = function(pkt) { m_max_edgeID += 1; - if (DBG) - console.log(PR, `PKT_GetNewEdgeID ${pkt.Info()} edgeID ${m_max_edgeID}`); + if (DBG) console.log(PR, `PKT_GetNewEdgeID ${pkt.Info()} edgeID ${m_max_edgeID}`); return { edgeID: m_max_edgeID }; }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -168,8 +164,7 @@ DB.PKT_Update = function(pkt) { let matches = NODES.find({ id: node.id }); if (matches.length === 0) { // if there was no node, then this is an insert new operation - if (DBG) - console.log( + if (DBG) console.log( PR, `PKT_Update ${pkt.Info()} INSERT nodeID ${JSON.stringify(node)}` ); @@ -180,8 +175,7 @@ DB.PKT_Update = function(pkt) { } else if (matches.length === 1) { // there was one match to update NODES.findAndUpdate({ id: node.id }, n => { - if (DBG) - console.log( + if (DBG) console.log( PR, `PKT_Update ${pkt.Info()} UPDATE nodeID ${node.id} ${JSON.stringify( node @@ -193,8 +187,7 @@ DB.PKT_Update = function(pkt) { }); retval = { op: "update", node }; } else { - if (DBG) - console.log( + if (DBG) console.log( PR, `WARNING: multiple nodeID ${node.id} x${matches.length}` ); @@ -209,8 +202,7 @@ DB.PKT_Update = function(pkt) { let matches = EDGES.find({ id: edge.id }); if (matches.length === 0) { // this is a new edge - if (DBG) - console.log( + if (DBG) console.log( PR, `PKT_Update ${pkt.Info()} INSERT edgeID ${edge.id} ${JSON.stringify( edge @@ -223,8 +215,7 @@ DB.PKT_Update = function(pkt) { } else if (matches.length === 1) { // update this edge EDGES.findAndUpdate({ id: edge.id }, e => { - if (DBG) - console.log( + if (DBG) console.log( PR, `PKT_Update ${pkt.SourceGroupID()} UPDATE edgeID ${ edge.id @@ -245,8 +236,7 @@ DB.PKT_Update = function(pkt) { // DELETE NODES if (nodeID !== undefined) { - if (DBG) - console.log(PR, `PKT_Update ${pkt.Info()} DELETE nodeID ${nodeID}`); + if (DBG) console.log(PR, `PKT_Update ${pkt.Info()} DELETE nodeID ${nodeID}`); // Log first so it's apparent what is triggering the edge changes LOGGER.Write(pkt.Info(), `delete node`, nodeID); @@ -297,8 +287,7 @@ DB.PKT_Update = function(pkt) { // DELETE EDGES if (edgeID !== undefined) { - if (DBG) - console.log(PR, `PKT_Update ${pkt.Info()} DELETE edgeID ${edgeID}`); + if (DBG) console.log(PR, `PKT_Update ${pkt.Info()} DELETE edgeID ${edgeID}`); LOGGER.Write(pkt.Info(), `delete edge`, edgeID); EDGES.findAndRemove({ id: edgeID }); return { op: "delete", edgeID }; From b354ba5a7747da2ccf2a3b8b4f57e15d36520f61 Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Sat, 12 Jan 2019 18:39:31 -0500 Subject: [PATCH 12/13] dev-ds-offline-view: npm run package, npm run package:debug creates standalone version of netcreate --- build/app/assets/data/data.json | 3952 ------------------ build/app/assets/data/data.reducedlinks.json | 2500 ----------- build/app/assets/data/empty.json | 35 - build/app/assets/index.html | 33 + build/app/system/datastore.js | 6 + build/app/unisys/client-network.js | 10 + build/app/unisys/client.js | 8 + build/app/unisys/common-netmessage-class.js | 9 +- build/app/unisys/server-database.js | 38 +- build/app/view/netcreate/nc-logic.js | 73 +- build/brunch-config.js | 28 +- build/brunch-server-static.js | 54 + build/brunch-server.js | 4 +- build/package.json | 8 +- 14 files changed, 234 insertions(+), 6524 deletions(-) delete mode 100644 build/app/assets/data/data.json delete mode 100644 build/app/assets/data/data.reducedlinks.json delete mode 100644 build/app/assets/data/empty.json create mode 100644 build/app/assets/index.html create mode 100644 build/brunch-server-static.js diff --git a/build/app/assets/data/data.json b/build/app/assets/data/data.json deleted file mode 100644 index fce5d3c2..00000000 --- a/build/app/assets/data/data.json +++ /dev/null @@ -1,3952 +0,0 @@ -{ - "nodes": [ - { - "label": "Medical Society ", - "x": 406.58514404296875, - "y": -520.6112670898438, - "id": "64", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 67.14286041259766 - }, - { - "label": "Walter Wyman", - "x": -1123.283935546875, - "y": 2779.2333984375, - "id": "129", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "42", - "Extra Info": "1897", - "Notes": "published influential report,1897 argued that pestis bacteria traveled person to person " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Resettlement camps", - "x": 124.39276885986328, - "y": -202.5792694091797, - "id": "158", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "George Boardman", - "x": 1847.9420166015625, - "y": -239.294189453125, - "id": "73", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "high ranking civil servant" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Li Khai Fai", - "x": -1752.5238037109375, - "y": 2505.98583984375, - "id": "55", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "15", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Leaders of provisonal government", - "x": -624.6123046875, - "y": 2709.421142578125, - "id": "53", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "14", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Japanese residents of Hawaii", - "x": 427.28497314453125, - "y": 30.681453704833984, - "id": "21", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Ah Leong", - "x": -2178.183837890625, - "y": -2082.634765625, - "id": "162", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "54", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "U.S. Marines", - "x": -1659.2589111328125, - "y": -1699.1190185546875, - "id": "3", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Tin-Yuke Char", - "x": -1734.217529296875, - "y": -2309.596923828125, - "id": "10", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "1", - "Extra Info": "Birth: 1905 Death: 1990", - "Notes": "Prominent local historian who characterized the fire of 1900 as "the holocaust of Honolulu"" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Local Businessmen", - "x": -1446.5330810546875, - "y": -2553.841064453125, - "id": "134", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "45", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Soga Yasutaro", - "x": 1659.075927734375, - "y": 2289.090576171875, - "id": "38", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "13", - "Extra Info": "", - "Notes": ""Junior editor who saved Hawaii Chimpo's printing press and escaped from the Kalihi detention camp"" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Indigent People", - "x": 991.4237060546875, - "y": 2827.591064453125, - "id": "51", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "6", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Sing Chong", - "x": 2719.11376953125, - "y": 1551.435546875, - "id": "168", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "60", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Kauohi", - "x": 1387.509765625, - "y": 693.3858642578125, - "id": "56", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "national guardsman" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "levy", - "x": 2011.9287109375, - "y": 2251.984375, - "id": "165", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "57", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Marine Hospital Services ", - "x": -183.01510620117188, - "y": -1002.1433715820312, - "id": "74", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 67.14286041259766 - }, - { - "label": "Grover Cleveland", - "x": -615.08544921875, - "y": 1768.645263671875, - "id": "156", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Chinese Inhabitants", - "x": 986.7825927734375, - "y": -237.38943481445312, - "id": "130", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "George W. Smith", - "x": -710.3975830078125, - "y": 47.74352264404297, - "id": "47", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He was a prominent representative of the American commercial elite" - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Petitions", - "x": -2291.76123046875, - "y": 2158.27685546875, - "id": "167", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "59", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "San Jose, California", - "x": 762.2874755859375, - "y": 1362.149658203125, - "id": "43", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Kong Tai Heong", - "x": -1629.8875732421875, - "y": 2662.3349609375, - "id": "57", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "15", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Joseph Kinyoun", - "x": -190.3436279296875, - "y": -1491.7652587890625, - "id": "91", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Pioneered the first bacteriological laboratory, fought the plague pandemic of the 1890s (page 199)" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Pasteur Institute labs", - "x": -2559.06005859375, - "y": -1625.598876953125, - "id": "172", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "64", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "U.S.S Eleu", - "x": -1160.4884033203125, - "y": 2572.50390625, - "id": "29", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "8", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Ah Sing", - "x": 381.6581726074219, - "y": -3030.6943359375, - "id": "83", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "25", - "Extra Info": "Death: 1900-02-26", - "Notes": "Employee of the Hotel stables that died of the plague. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Burning of Chinatown", - "x": 1124.5023193359375, - "y": 1670.5574951171875, - "id": "150", - "attributes": { - "Node_Type": "Event", - "Modularity Class": "20", - "Extra Info": "1900-01-20", - "Notes": "" - }, - "color": "rgb(35,150,111)", - "size": 55.71428680419922 - }, - { - "label": "Ke Aloha Aina", - "x": 40.79916000366211, - "y": 617.1716918945312, - "id": "154", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "Hawaiin Language Press", - "x": -2943.6611328125, - "y": -1292.8353271484375, - "id": "160", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "52", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Japanese Aid Society ", - "x": -1236.708251953125, - "y": 327.23590087890625, - "id": "48", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Benjamin Harrison", - "x": -1777.935302734375, - "y": -1471.763427734375, - "id": "143", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Honolulu Citizens", - "x": 2416.791748046875, - "y": 1488.619140625, - "id": "122", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "40", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Board of Health", - "x": -23.73048210144043, - "y": 189.0130615234375, - "id": "0", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "The Board of Health was given emergency control of the Republic of Hawaii at noon on December 12, 1899" - }, - "color": "rgb(140,185,0)", - "size": 250.0 - }, - { - "label": "Incoming Honolulu travelers", - "x": 272.9108581542969, - "y": 525.7716674804688, - "id": "146", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "1894 +", - "Notes": "Stopped and inspected by Honolulu board of health years after 1894" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "mori iga", - "x": 2884.275390625, - "y": 1024.7257080078125, - "id": "131", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "43", - "Extra Info": "", - "Notes": "volunteered to stop the plague" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Chamber of Commerce", - "x": -270.996337890625, - "y": 856.6937255859375, - "id": "142", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "S.G. Wilder", - "x": 701.9417724609375, - "y": 3101.128662109375, - "id": "16", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "6", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Ah Kau", - "x": -75.57275390625, - "y": 500.10089111328125, - "id": "111", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Francis Hatch", - "x": -2882.886962890625, - "y": 868.04638671875, - "id": "132", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "44", - "Extra Info": "", - "Notes": "Government authority who spoke on behalf of white businesspeople" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Honolulu", - "x": 1895.0631103515625, - "y": -103.1948471069336, - "id": "33", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 67.14286041259766 - }, - { - "label": "Chen", - "x": -2016.9681396484375, - "y": 2170.7333984375, - "id": "32", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "9", - "Extra Info": "", - "Notes": "Plague victim" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Emerson, Day and Wood", - "x": -575.0821533203125, - "y": -799.270751953125, - "id": "108", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Propose use of fire in Honolulu Pg. 87-88" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Residents of Chinatown", - "x": -504.87335205078125, - "y": 737.679443359375, - "id": "66", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Society for the Relief of the Destitute", - "x": 281.27850341796875, - "y": 1255.8016357421875, - "id": "109", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Kalanianaole", - "x": -2565.31201171875, - "y": -35.22377014160156, - "id": "144", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Hawaiian Gazette", - "x": 3106.99658203125, - "y": 751.8287353515625, - "id": "174", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "66", - "Extra Info": "1899-12-13", - "Notes": "Hawaiian Newspaper" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Miki Saito", - "x": -775.68798828125, - "y": 160.0733184814453, - "id": "1", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Associates", - "x": -349.3084716796875, - "y": -2992.665771484375, - "id": "119", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "38", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Gu Kim Fui", - "x": -213.29364013671875, - "y": -2731.0419921875, - "id": "31", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "12", - "Extra Info": "", - "Notes": "He is giving an interview to a commercial advertiser to urge people to only burn buildings that were confirmed plague locations. Birth: 1835; Death: 1908-5-3 " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Refugees", - "x": 158.0921630859375, - "y": 814.5675048828125, - "id": "45", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "White Newspaper", - "x": 1183.344482421875, - "y": 775.9883422851562, - "id": "135", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "Toshiyuki Mitamura", - "x": 1531.4549560546875, - "y": -2619.44189453125, - "id": "65", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "19", - "Extra Info": "", - "Notes": "Practiced in Hawaiian islands since 1888. Mediated flow of information from Japanese community to Board of Health. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Yamato Shimbun", - "x": -1656.7593994140625, - "y": 1558.0814208984375, - "id": "54", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Newspaper business in Honolulu" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "King David Kalakaua", - "x": 677.822021484375, - "y": 1569.7186279296875, - "id": "71", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Frank Damon", - "x": 2669.32861328125, - "y": -1148.830322265625, - "id": "127", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "41", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Liliuokalani", - "x": -1710.1346435546875, - "y": -1875.5576171875, - "id": "26", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "John Stevens", - "x": -1562.6160888671875, - "y": -1370.8785400390625, - "id": "2", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Sanitary Committee", - "x": -178.0287628173828, - "y": -34.579139709472656, - "id": "6", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "1899", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Kauehoa", - "x": 1278.761474609375, - "y": 667.7413330078125, - "id": "52", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "national guardsman" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Mori Iga", - "x": -323.6142883300781, - "y": -394.3799133300781, - "id": "133", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Birth: 1864; Death: ?", - "Notes": "Physician previously working with Wood and Day" - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Ichitaro Katsuki", - "x": 2153.9326171875, - "y": 1854.865966796875, - "id": "88", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "26", - "Extra Info": "1865 - 1967", - "Notes": "Japanese-born physician who was a special representative for the San Francisco Board of Health (page 198)" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Wei Pin Yang", - "x": 359.3658142089844, - "y": 381.8961181640625, - "id": "12", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Chinese empire's consul in Hawaii" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Ethel Johnson", - "x": 1832.267333984375, - "y": -2414.38818359375, - "id": "17", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "4", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Kalihi Drill Shed", - "x": -993.3411865234375, - "y": 792.0265502929688, - "id": "35", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "21.331522,-157.8977807", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Upper class white", - "x": 1612.1856689453125, - "y": 2599.891845703125, - "id": "169", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "61", - "Extra Info": "", - "Notes": "Includes Nuuanu Valley Residents, EF Bishop, white supporters of Sarah Boardman" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Wa Ha Pau", - "x": -202.810302734375, - "y": 2974.511474609375, - "id": "115", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "37", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Bishop Estate", - "x": -581.5835571289062, - "y": 539.404052734375, - "id": "86", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Operated around Kalihi Beach" - }, - "color": "rgb(255,112,69)", - "size": 61.42857360839844 - }, - { - "label": "Hawaiian Residents", - "x": 1918.6142578125, - "y": 2471.3232421875, - "id": "166", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "58", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Opening Streets", - "x": 2396.54931640625, - "y": 1792.5787353515625, - "id": "70", - "attributes": { - "Node_Type": "Event", - "Modularity Class": "21", - "Extra Info": "February, 19", - "Notes": "After finding that the situation was improving, the three physicians opened several streets not open since the Chinatown Fire." - }, - "color": "rgb(35,150,111)", - "size": 50.0 - }, - { - "label": "Press", - "x": -848.4004516601562, - "y": 490.6308288574219, - "id": "84", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Commercial Advertiser", - "x": -56.3062744140625, - "y": -2648.83642578125, - "id": "37", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "12", - "Extra Info": "", - "Notes": "Merchant commercial advertiser" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "United Chinese Society", - "x": 126.43289947509766, - "y": 492.1632385253906, - "id": "110", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "U.S.S Iroquois ", - "x": -2515.0, - "y": 1495.220947265625, - "id": "27", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "7", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "William McKinley", - "x": -1144.4354248046875, - "y": 1497.6971435546875, - "id": "85", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Kalihi Detention Camp", - "x": -1476.9674072265625, - "y": 1742.327880859375, - "id": "40", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Young Physicians", - "x": -279.798828125, - "y": 222.00369262695312, - "id": "68", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Wong Chow", - "x": 2423.847412109375, - "y": -1752.607177734375, - "id": "173", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "65", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Politicians", - "x": 2684.988525390625, - "y": -783.3707275390625, - "id": "82", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "24", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Committee of Safety", - "x": -1253.04296875, - "y": -939.4849243164062, - "id": "137", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "White Settlers", - "x": 729.6915283203125, - "y": 1906.969482421875, - "id": "76", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Lau Chung", - "x": 3064.009521484375, - "y": 138.21681213378906, - "id": "141", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "48", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "San Jose Fire Chief", - "x": 2646.749267578125, - "y": 1228.983642578125, - "id": "171", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "63", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Duncan A. Carmichael", - "x": -109.4676742553711, - "y": -488.2804260253906, - "id": "23", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He is not formally on the board, but he acted as an ex-officio advisor to Emerson, Day, and Wood." - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Yim Quan Quan, Yim", - "x": -2316.714599609375, - "y": 1811.2091064453125, - "id": "106", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "34", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Henry E Cooper", - "x": -1697.8192138671875, - "y": -308.675537109375, - "id": "46", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He was the elected officer whom the Board officially reported to and he was on the Board because the Hawaiian Constitution stipulated the A.G. would function as the president of the Board of Health." - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Jordans Dry goods store", - "x": 2026.3525390625, - "y": -33.51780319213867, - "id": "79", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "fort street" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Matrons", - "x": 236.30030822753906, - "y": 270.209716796875, - "id": "105", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Thurston", - "x": -497.4589538574219, - "y": -2894.209716796875, - "id": "89", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "38", - "Extra Info": "1900-02-19 ", - "Notes": "Thurston clashed openly with Emerson and Day. Thurston appeared bent upon using the Hotel Stables incident to put his Citizens' Sanitary Commission in command of the city in place of the Board of Health that created and authorized it. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "J. Weir Robertson", - "x": -2437.354736328125, - "y": -1513.9146728515625, - "id": "98", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "30", - "Extra Info": "", - "Notes": "Honolulu had optimism that the plague did not exist there anymore until Robertson came down with it on February 3" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Emma", - "x": -2493.77001953125, - "y": 119.39424896240234, - "id": "155", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "George Augur", - "x": 1966.0927734375, - "y": 179.22039794921875, - "id": "77", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "doctor" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Nakauaila", - "x": 1346.6380615234375, - "y": 98.52766418457031, - "id": "102", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "One of the first 5 deaths in Chinatown" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Government ", - "x": -891.2184448242188, - "y": 1146.3494873046875, - "id": "25", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 78.57142639160156 - }, - { - "label": "McGrew", - "x": -2631.060546875, - "y": 1124.1904296875, - "id": "75", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "22", - "Extra Info": "1900-02-19", - "Notes": "The Board's old medical nemesis, publicly accused Emerson, Day, and of dangerous inconsistency. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Kaumakapili Church", - "x": 71.8370590209961, - "y": 1453.9866943359375, - "id": "49", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "20.8642661,-155.2436817", - "Notes": "Symbolic center of the traditional Hawaiian community in Chinatown" - }, - "color": "rgb(0,199,255)", - "size": 67.14286041259766 - }, - { - "label": "Chinese Chronicle", - "x": -62.353790283203125, - "y": 3051.356689453125, - "id": "117", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "37", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "William Galbraith", - "x": 835.73291015625, - "y": 2623.951904296875, - "id": "114", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "36", - "Extra Info": "", - "Notes": "Chief surgeon to the Union Pacific Railroad" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "US army medical observers", - "x": -2851.1943359375, - "y": 1317.3681640625, - "id": "161", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "53", - "Extra Info": "1896", - "Notes": "Stationed in Pearl Harbor in 1896. Reported back to Washington that Honolulu was a healthy and secure place. " - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Robertsons Physician", - "x": -32.93634796142578, - "y": -162.04486083984375, - "id": "92", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Wing Wo Tai Company", - "x": 3002.2880859375, - "y": 328.972900390625, - "id": "157", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "48", - "Extra Info": "", - "Notes": "Manufacturing and retail operation" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Oahu railroad station", - "x": 3220.4482421875, - "y": -635.9133911132812, - "id": "78", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "23", - "Extra Info": "20.8642661,-155.2436817", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Hawaiian Aid Society", - "x": -2871.222412109375, - "y": -1038.8953857421875, - "id": "138", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "47", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Plague Victims", - "x": 910.0609130859375, - "y": 143.7878875732422, - "id": "50", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Suffered from plague" - }, - "color": "rgb(140,185,0)", - "size": 72.85714721679688 - }, - { - "label": "Hawaiian Treasury ", - "x": -1118.3380126953125, - "y": -2821.495849609375, - "id": "152", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "50", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Consul Yang", - "x": 1920.8751220703125, - "y": -2194.518798828125, - "id": "8", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "0", - "Extra Info": "Chinese Aid Society; 1900-02-08", - "Notes": "Administered fund for providing essential goods to Chinese evacuees. Accused of stealing funds for himself. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Hoffman", - "x": -324.4167785644531, - "y": -577.9707641601562, - "id": "9", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "1899", - "Notes": "Medical bacteriological trained with the latest techniques. Persuaded to accept the job from other members on the Board. He was the 5th physician member of the Board. Board of Health agrees with him that the plague was definitely in Honolulu" - }, - "color": "rgb(217,125,216)", - "size": 90.0 - }, - { - "label": "Board of Physicians", - "x": 1186.501953125, - "y": -292.3986511230469, - "id": "118", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 67.14286041259766 - }, - { - "label": "Hawaiian Hotel Stables", - "x": -1771.521728515625, - "y": -767.6389770507812, - "id": "81", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "A hotel which was run by the Japanese" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Charles B Cooper", - "x": 606.0535278320312, - "y": -990.7427978515625, - "id": "140", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "President of the Medical Society" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Kitasato", - "x": -577.9833984375, - "y": -2464.58642578125, - "id": "22", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "18", - "Extra Info": "", - "Notes": "Influential Japanese physician" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "James H Raymond", - "x": -400.8027038574219, - "y": -1062.8873291015625, - "id": "147", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Chinese Aid Society", - "x": 2883.422119140625, - "y": -850.0891723632812, - "id": "15", - "attributes": { - "Node_Type": "Event", - "Modularity Class": "3", - "Extra Info": "1900-02-08", - "Notes": "Used to provide transitional support for Chinese evacuees that left the camps. " - }, - "color": "rgb(35,150,111)", - "size": 50.0 - }, - { - "label": "W.E.Taylor", - "x": 250.91287231445312, - "y": -129.217529296875, - "id": "80", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Mention of his comments on the actions of the Board: 1900-02-19", - "Notes": "Physician that avoided the Board. Didn't understand the reasoning for burning the Hotel stables but accepted that the Board thought it was a good idea. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Hawaiians", - "x": -178.50238037109375, - "y": 1719.9852294921875, - "id": "94", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Chinatown", - "x": 926.4285888671875, - "y": 414.4527282714844, - "id": "4", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 118.5714340209961 - }, - { - "label": "Tong San Kai", - "x": 347.0498046875, - "y": 163.98956298828125, - "id": "95", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Nuuanu Valley, Honolulu", - "x": 283.0437927246094, - "y": 2803.927978515625, - "id": "170", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "62", - "Extra Info": "", - "Notes": "Wealthy residence in Honolulu, mostly populated by white residents. " - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "John F. Colburn", - "x": 715.4049072265625, - "y": -896.4007568359375, - "id": "90", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Birth: 1859-9-30; Death: 1920-3-16 Last Minister of the Interior of Queen Liliuokalani." - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Volunteers", - "x": -1891.026611328125, - "y": -598.2655029296875, - "id": "113", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "The Committee of Safety", - "x": -458.0998840332031, - "y": 1838.6939697265625, - "id": "39", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Day", - "x": -561.4107055664062, - "y": -589.927490234375, - "id": "69", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Henry Cooper", - "x": -469.6681213378906, - "y": 156.6598663330078, - "id": "44", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Sarah Boardman", - "x": 1619.651611328125, - "y": 17.47919273376465, - "id": "67", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "white resident living in wealthy Nuuanu Valley, honolulu " - }, - "color": "rgb(217,125,216)", - "size": 78.57142639160156 - }, - { - "label": "Robert Koch", - "x": -741.9411010742188, - "y": -2398.66943359375, - "id": "63", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "18", - "Extra Info": "Experimenting: 1860s", - "Notes": "Lead a group of investigators in Berlin. They investigated bacteriology of different strains of disease causing microorganisms. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Blount", - "x": -2982.00830078125, - "y": -716.1730346679688, - "id": "151", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "49", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "F.M. Brooks", - "x": 935.9578857421875, - "y": -776.1165771484375, - "id": "120", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Charles Garvin", - "x": -979.759521484375, - "y": -402.0263366699219, - "id": "20", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Started volunteering: 1899-12-01", - "Notes": "One of the first private practice physicians to volunteer with the Board." - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Qing Empire", - "x": 1370.5941162109375, - "y": 418.33538818359375, - "id": "19", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Ruling empire of China" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Tam Ping Sam Kee", - "x": -1904.0238037109375, - "y": 2373.614013671875, - "id": "112", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "15", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "George R Carter", - "x": 1424.5906982421875, - "y": 542.6140747070312, - "id": "125", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Micronesia", - "x": 2103.401611328125, - "y": -1932.3634033203125, - "id": "100", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "32", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Independent", - "x": -1171.96337890625, - "y": -2490.67333984375, - "id": "61", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "17", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Council of State", - "x": 1072.0174560546875, - "y": -2862.64306640625, - "id": "164", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "56", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Hawaiian Republic Supreme Court", - "x": 1164.1864013671875, - "y": 2532.36767578125, - "id": "97", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "29", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "KuKui bridge", - "x": -1252.02490234375, - "y": 168.2989501953125, - "id": "126", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "President Wood", - "x": 393.03759765625, - "y": -941.5281372070312, - "id": "128", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Hawaii Land Company", - "x": -227.0077667236328, - "y": 522.2977294921875, - "id": "145", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "Berlin", - "x": -555.9338989257812, - "y": -1028.7991943359375, - "id": "62", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Emerson", - "x": -202.35391235351562, - "y": -754.7462158203125, - "id": "11", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Ended the quarintine" - }, - "color": "rgb(217,125,216)", - "size": 78.57142639160156 - }, - { - "label": "Kakaako ", - "x": 1379.3619384765625, - "y": -2711.2607421875, - "id": "13", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "19", - "Extra Info": "https://www.google.com/maps/place/Kaka'ako,+Honolulu,+HI/@21.292413,-157.857571,2921m/data=!3m1!1e3!4m5!3m4!1s0x7c006de2a01853e3:0x5e4718558b846b42!8m2!3d21.2963142!4d-157.8554882!6m1!1e1", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Lorrin Thurston", - "x": -813.6103515625, - "y": -398.75274658203125, - "id": "41", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 78.57142639160156 - }, - { - "label": "Kee Mong", - "x": 2423.330078125, - "y": -1527.7327880859375, - "id": "96", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "28", - "Extra Info": "1900-02-26", - "Notes": "Chinese man that died of plague in tenement house." - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Hawaii China Town", - "x": 1508.1676025390625, - "y": -2358.075439453125, - "id": "59", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "16", - "Extra Info": "21.3150194,-157.8751437", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Hawaiian Refugees", - "x": -2460.93359375, - "y": -134.19821166992188, - "id": "136", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 67.14286041259766 - }, - { - "label": "Camp McKinley", - "x": 159.31454467773438, - "y": -2960.66455078125, - "id": "107", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "35", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Ah Sop", - "x": -367.6977844238281, - "y": -52.11518096923828, - "id": "87", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Talked to Wood: 1900-02-26", - "Notes": "Witness that stated the Hotel Stables should be confirmed as a plague spot." - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Sanford Ballard Dole", - "x": -387.0519104003906, - "y": 1393.179931640625, - "id": "42", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Birth: 1844; Death 1926", - "Notes": "Born in Honolulu" - }, - "color": "rgb(217,125,216)", - "size": 90.0 - }, - { - "label": "Levys father ", - "x": 187.78109741210938, - "y": 22.660493850708008, - "id": "153", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "complained about the board" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Sanitary Inspectors ", - "x": -2019.8177490234375, - "y": -2372.10888671875, - "id": "159", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "51", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Louis Pasteur", - "x": 2651.887451171875, - "y": -1389.0079345703125, - "id": "99", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "31", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Firefighters", - "x": 681.7515258789062, - "y": 69.75890350341797, - "id": "124", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 78.57142639160156 - }, - { - "label": "City Mill", - "x": 1492.4901123046875, - "y": -608.94384765625, - "id": "139", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Western Medicine Doctors and Physicians", - "x": -335.6007080078125, - "y": 394.5499572753906, - "id": "101", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Ah Hung", - "x": -1501.589599609375, - "y": -546.5320434570312, - "id": "58", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Found dead: 1900-02-19", - "Notes": "Body was found at Hawaiian Hotel Stables. Owned the horses that were used for most of the city's tourism. " - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Kim Fui Gu", - "x": -2520.626220703125, - "y": 1872.5833740234375, - "id": "14", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "2", - "Extra Info": "", - "Notes": "Yang's vice-consul" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Chug Kun Ai", - "x": -614.9652099609375, - "y": 2922.237548828125, - "id": "18", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "5", - "Extra Info": "", - "Notes": "In China Town when it was on fire. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Nathaniel B Emerson", - "x": 846.8594970703125, - "y": 2963.567138671875, - "id": "24", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "6", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Washington", - "x": 2238.045654296875, - "y": -2008.6993408203125, - "id": "163", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "55", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Pro-American Planters and Merchants", - "x": -1841.0323486328125, - "y": -1939.3216552734375, - "id": "28", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Kuamakapili Church ", - "x": 2073.710205078125, - "y": 2061.510498046875, - "id": "36", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "11", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "The Medical Society", - "x": 558.7947998046875, - "y": -2800.140625, - "id": "93", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "27", - "Extra Info": "", - "Notes": "Medical Society in Hawaii" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "James Hunt", - "x": 524.253662109375, - "y": 998.2268676757812, - "id": "30", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 84.28572082519531 - }, - { - "label": "Saito Miki", - "x": -177.76515197753906, - "y": 886.4339599609375, - "id": "116", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Honolulu Fire Department", - "x": 920.8604736328125, - "y": 1428.706298828125, - "id": "103", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Japanese", - "x": -856.416015625, - "y": -2735.734375, - "id": "104", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "33", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "David Malo", - "x": -153.59605407714844, - "y": -1247.91162109375, - "id": "123", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Birth: 1793 ; Death: 1853", - "Notes": "Published author of Hawaiian mythology and folklore" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Soga Yasutaro", - "x": -1360.396240234375, - "y": 1503.2244873046875, - "id": "7", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Japanese man who ended up in the Kalihi detention camp, where he eventually broke out and played a major role in organizing the Provisional Japanese Council" - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Armstrong Smith", - "x": 871.511474609375, - "y": -2724.671630859375, - "id": "34", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "10", - "Extra Info": "Started working at hospital: 1899-12-00; Got plague and recovered: 1900-02-08", - "Notes": "Recent school graduate that volunteered to run the plague hospital. Got sick during second week of February. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Clifford B. Wood", - "x": -546.984375, - "y": -202.4073486328125, - "id": "5", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He went on a 3-month trip to Asia, and presented a paper to the Hawaiian Medical Association summarizing what he learned. " - }, - "color": "rgb(217,125,216)", - "size": 107.14286041259766 - }, - { - "label": "Kukui Street", - "x": -2973.21240234375, - "y": 543.77880859375, - "id": "121", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "39", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "George Herbert", - "x": 3119.40673828125, - "y": -53.748085021972656, - "id": "149", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "48", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "L D Keliipio", - "x": -1315.8326416015625, - "y": -118.42870330810547, - "id": "60", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He had served in the past, but has seldom been active. Civilian" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Boys Brigade", - "x": -2459.985107421875, - "y": -360.5040588378906, - "id": "72", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Trained Volunteers", - "x": 1100.731201171875, - "y": -40.3592414855957, - "id": "148", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - } - ], - "edges": [ - { - "source": "135", - "target": "4", - "id": "97", - "attributes": { - "Relationship": "Written communication", - "Citations": "66; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "41", - "target": "58", - "id": "84", - "attributes": { - "Relationship": "", - "Citations": "20; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "66", - "id": "31", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "32; 90; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "11", - "target": "69", - "id": "87", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "143; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "123", - "target": "11", - "id": "129", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "196; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "50", - "id": "16", - "attributes": { - "Relationship": "", - "Citations": "78; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "44", - "target": "0", - "id": "17", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "84; 86; 86; " - }, - "color": "rgb(0,0,0)", - "size": 3.0 - }, - { - "source": "95", - "target": "0", - "id": "58", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "72; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "9", - "target": "5", - "id": "34", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "196; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "31", - "target": "37", - "id": "24", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "107; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "4", - "target": "50", - "id": "65", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "64; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "46", - "target": "58", - "id": "106", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "108; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "30", - "target": "103", - "id": "69", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "125; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "21", - "id": "72", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "117; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "1", - "target": "126", - "id": "122", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "133; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "133", - "target": "11", - "id": "107", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "195; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "9", - "target": "62", - "id": "27", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "196; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "5", - "id": "92", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "181; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "87", - "target": "5", - "id": "59", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "176; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "2", - "target": "137", - "id": "98", - "attributes": { - "Relationship": "", - "Citations": "20; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "128", - "target": "120", - "id": "85", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "123; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "84", - "target": "25", - "id": "54", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "72; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "42", - "target": "156", - "id": "124", - "attributes": { - "Relationship": "", - "Citations": "21; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "133", - "target": "0", - "id": "90", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "178; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "4", - "target": "52", - "id": "19", - "attributes": { - "Relationship": "", - "Citations": "; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "5", - "target": "20", - "id": "23", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "172; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "124", - "id": "130", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "119; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "63", - "target": "22", - "id": "42", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "10; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "26", - "target": "3", - "id": "134", - "attributes": { - "Relationship": "", - "Citations": "21; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "68", - "id": "35", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "72; 64; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "85", - "target": "25", - "id": "48", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "25; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "116", - "target": "0", - "id": "115", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "182; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "1", - "target": "5", - "id": "2", - "attributes": { - "Relationship": "", - "Citations": "119; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "49", - "target": "42", - "id": "43", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "64; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "30", - "id": "71", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "125; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "91", - "target": "74", - "id": "56", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "199; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "23", - "target": "74", - "id": "64", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "148; 50; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "133", - "target": "108", - "id": "99", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "195; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "19", - "target": "4", - "id": "8", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "62; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "7", - "target": "25", - "id": "7", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "155; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "73", - "target": "33", - "id": "36", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "92", - "target": "0", - "id": "57", - "attributes": { - "Relationship": "Written communication", - "Citations": "164-165; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "9", - "target": "69", - "id": "38", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "196; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "112", - "target": "55", - "id": "88", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "38; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "55", - "target": "57", - "id": "39", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "152; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "41", - "target": "69", - "id": "96", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "101; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "143", - "target": "2", - "id": "105", - "attributes": { - "Relationship": "", - "Citations": "20; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "64", - "id": "74", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "72; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "154", - "target": "0", - "id": "126", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "66; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "9", - "target": "23", - "id": "51", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "52; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "153", - "target": "0", - "id": "123", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "180; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "67", - "target": "33", - "id": "29", - "attributes": { - "Relationship": "", - "Citations": "; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "24", - "target": "51", - "id": "26", - "attributes": { - "Relationship": "", - "Citations": "43; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "87", - "id": "52", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "175; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "115", - "target": "117", - "id": "76", - "attributes": { - "Relationship": "Written communication", - "Citations": "96; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "47", - "target": "60", - "id": "37", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "49; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "26", - "target": "28", - "id": "9", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "20; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "80", - "target": "0", - "id": "53", - "attributes": { - "Relationship": "Written communication", - "Citations": "175; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "67", - "target": "73", - "id": "33", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "4", - "target": "124", - "id": "132", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "127; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "86", - "target": "35", - "id": "55", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "146-147; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "52", - "target": "56", - "id": "22", - "attributes": { - "Relationship": "", - "Citations": "; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "79", - "target": "33", - "id": "47", - "attributes": { - "Relationship": "", - "Citations": "120; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "103", - "target": "150", - "id": "131", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "126; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "4", - "target": "56", - "id": "21", - "attributes": { - "Relationship": "", - "Citations": "; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "11", - "target": "128", - "id": "125", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "123; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "66", - "target": "25", - "id": "111", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "128; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "113", - "target": "58", - "id": "113", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "129; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "16", - "target": "24", - "id": "10", - "attributes": { - "Relationship": "Written communication", - "Citations": "42; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "47", - "target": "0", - "id": "62", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "86; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "136", - "target": "155", - "id": "127", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "140; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "145", - "target": "0", - "id": "110", - "attributes": { - "Relationship": "Written communication", - "Citations": "93; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "133", - "target": "5", - "id": "112", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "195; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "67", - "target": "50", - "id": "100", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "120; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "140", - "target": "64", - "id": "101", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "104; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "42", - "target": "25", - "id": "44", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "24; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "41", - "target": "137", - "id": "91", - "attributes": { - "Relationship": "", - "Citations": "20; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "41", - "target": "0", - "id": "25", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "85; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "77", - "target": "67", - "id": "40", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "120; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "45", - "target": "0", - "id": "18", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "156; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "101", - "id": "66", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "39; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "69", - "target": "5", - "id": "82", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "49; 143; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "0", - "target": "146", - "id": "119", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "pg. 16; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "50", - "target": "102", - "id": "67", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "64; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "158", - "id": "133", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "95; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "84", - "target": "5", - "id": "86", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "181; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "142", - "target": "0", - "id": "108", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "66; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "136", - "target": "72", - "id": "94", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "7", - "target": "54", - "id": "20", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "194; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "46", - "target": "60", - "id": "28", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "49; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "124", - "target": "130", - "id": "95", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "119; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "39", - "target": "42", - "id": "13", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "20; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "147", - "target": "9", - "id": "120", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "104; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "58", - "target": "81", - "id": "49", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "174; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "49", - "target": "94", - "id": "68", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "63-4; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "30", - "target": "71", - "id": "128", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "125; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "86", - "target": "0", - "id": "93", - "attributes": { - "Relationship": "Written communication", - "Citations": "93; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "47", - "target": "44", - "id": "32", - "attributes": { - "Relationship": "", - "Citations": "49; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "89", - "target": "119", - "id": "77", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "86; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "118", - "target": "120", - "id": "80", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "123; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "149", - "target": "141", - "id": "118", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "38; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "1", - "target": "48", - "id": "15", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "133; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "20", - "target": "20", - "id": "6", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "181; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "124", - "target": "21", - "id": "104", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "119; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "118", - "target": "139", - "id": "103", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "123; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "4", - "target": "4", - "id": "3", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "129-141; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "71", - "target": "76", - "id": "46", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "44; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "9", - "target": "74", - "id": "41", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "163-4; 164; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "42", - "target": "94", - "id": "60", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "25; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "12", - "target": "0", - "id": "109", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "182; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "116", - "target": "42", - "id": "79", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "63; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "141", - "target": "157", - "id": "135", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "39; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "43", - "target": "30", - "id": "12", - "attributes": { - "Relationship": "", - "Citations": "119; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "7", - "target": "40", - "id": "14", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "194; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "2", - "target": "3", - "id": "1", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "20; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "109", - "target": "45", - "id": "75", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "140; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "110", - "target": "0", - "id": "78", - "attributes": { - "Relationship": "Written communication", - "Citations": "96; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "124", - "target": "148", - "id": "117", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "119; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "65", - "target": "13", - "id": "50", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "94; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "5", - "target": "41", - "id": "30", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "85; 145; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "0", - "target": "105", - "id": "73", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "118; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "6", - "id": "4", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "83; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "79", - "target": "67", - "id": "45", - "attributes": { - "Relationship": "", - "Citations": "120; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "23", - "target": "0", - "id": "63", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "51; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "1", - "target": "0", - "id": "0", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "116; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "30", - "target": "49", - "id": "70", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "125; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "0", - "target": "111", - "id": "89", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "90; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "11", - "target": "5", - "id": "102", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "143; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "30", - "target": "4", - "id": "11", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "118, 125; 125; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "0", - "target": "4", - "id": "114", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "124; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "136", - "target": "144", - "id": "116", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "140; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "90", - "target": "64", - "id": "61", - "attributes": { - "Relationship": "Written communication", - "Citations": "107; 122; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "0", - "target": "9", - "id": "5", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "118; 73; 52; " - }, - "color": "rgb(0,0,0)", - "size": 3.0 - }, - { - "source": "125", - "target": "4", - "id": "81", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "100; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "42", - "target": "142", - "id": "121", - "attributes": { - "Relationship": "Two way conversation between people or group of people", - "Citations": "183; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "118", - "target": "4", - "id": "83", - "attributes": { - "Relationship": "Participation in an event", - "Citations": "80; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - } - ] -} \ No newline at end of file diff --git a/build/app/assets/data/data.reducedlinks.json b/build/app/assets/data/data.reducedlinks.json deleted file mode 100644 index 5b599668..00000000 --- a/build/app/assets/data/data.reducedlinks.json +++ /dev/null @@ -1,2500 +0,0 @@ -{ - "nodes": [ - { - "label": "Medical Society ", - "x": 406.58514404296875, - "y": -520.6112670898438, - "id": "64", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 67.14286041259766 - }, - { - "label": "Walter Wyman", - "x": -1123.283935546875, - "y": 2779.2333984375, - "id": "129", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "42", - "Extra Info": "1897", - "Notes": "published influential report,1897 argued that pestis bacteria traveled person to person " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Resettlement camps", - "x": 124.39276885986328, - "y": -202.5792694091797, - "id": "158", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "George Boardman", - "x": 1847.9420166015625, - "y": -239.294189453125, - "id": "73", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "high ranking civil servant" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Li Khai Fai", - "x": -1752.5238037109375, - "y": 2505.98583984375, - "id": "55", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "15", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Leaders of provisonal government", - "x": -624.6123046875, - "y": 2709.421142578125, - "id": "53", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "14", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Japanese residents of Hawaii", - "x": 427.28497314453125, - "y": 30.681453704833984, - "id": "21", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Ah Leong", - "x": -2178.183837890625, - "y": -2082.634765625, - "id": "162", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "54", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "U.S. Marines", - "x": -1659.2589111328125, - "y": -1699.1190185546875, - "id": "3", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Tin-Yuke Char", - "x": -1734.217529296875, - "y": -2309.596923828125, - "id": "10", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "1", - "Extra Info": "Birth: 1905 Death: 1990", - "Notes": "Prominent local historian who characterized the fire of 1900 as "the holocaust of Honolulu"" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Local Businessmen", - "x": -1446.5330810546875, - "y": -2553.841064453125, - "id": "134", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "45", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Soga Yasutaro", - "x": 1659.075927734375, - "y": 2289.090576171875, - "id": "38", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "13", - "Extra Info": "", - "Notes": ""Junior editor who saved Hawaii Chimpo's printing press and escaped from the Kalihi detention camp"" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Indigent People", - "x": 991.4237060546875, - "y": 2827.591064453125, - "id": "51", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "6", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Sing Chong", - "x": 2719.11376953125, - "y": 1551.435546875, - "id": "168", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "60", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Kauohi", - "x": 1387.509765625, - "y": 693.3858642578125, - "id": "56", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "national guardsman" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "levy", - "x": 2011.9287109375, - "y": 2251.984375, - "id": "165", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "57", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Marine Hospital Services ", - "x": -183.01510620117188, - "y": -1002.1433715820312, - "id": "74", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 67.14286041259766 - }, - { - "label": "Grover Cleveland", - "x": -615.08544921875, - "y": 1768.645263671875, - "id": "156", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Chinese Inhabitants", - "x": 986.7825927734375, - "y": -237.38943481445312, - "id": "130", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "George W. Smith", - "x": -710.3975830078125, - "y": 47.74352264404297, - "id": "47", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He was a prominent representative of the American commercial elite" - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Petitions", - "x": -2291.76123046875, - "y": 2158.27685546875, - "id": "167", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "59", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "San Jose, California", - "x": 762.2874755859375, - "y": 1362.149658203125, - "id": "43", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Kong Tai Heong", - "x": -1629.8875732421875, - "y": 2662.3349609375, - "id": "57", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "15", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Joseph Kinyoun", - "x": -190.3436279296875, - "y": -1491.7652587890625, - "id": "91", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Pioneered the first bacteriological laboratory, fought the plague pandemic of the 1890s (page 199)" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Pasteur Institute labs", - "x": -2559.06005859375, - "y": -1625.598876953125, - "id": "172", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "64", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "U.S.S Eleu", - "x": -1160.4884033203125, - "y": 2572.50390625, - "id": "29", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "8", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Ah Sing", - "x": 381.6581726074219, - "y": -3030.6943359375, - "id": "83", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "25", - "Extra Info": "Death: 1900-02-26", - "Notes": "Employee of the Hotel stables that died of the plague. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Burning of Chinatown", - "x": 1124.5023193359375, - "y": 1670.5574951171875, - "id": "150", - "attributes": { - "Node_Type": "Event", - "Modularity Class": "20", - "Extra Info": "1900-01-20", - "Notes": "" - }, - "color": "rgb(35,150,111)", - "size": 55.71428680419922 - }, - { - "label": "Ke Aloha Aina", - "x": 40.79916000366211, - "y": 617.1716918945312, - "id": "154", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "Hawaiin Language Press", - "x": -2943.6611328125, - "y": -1292.8353271484375, - "id": "160", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "52", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Japanese Aid Society ", - "x": -1236.708251953125, - "y": 327.23590087890625, - "id": "48", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Benjamin Harrison", - "x": -1777.935302734375, - "y": -1471.763427734375, - "id": "143", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Honolulu Citizens", - "x": 2416.791748046875, - "y": 1488.619140625, - "id": "122", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "40", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Board of Health", - "x": -23.73048210144043, - "y": 189.0130615234375, - "id": "0", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "The Board of Health was given emergency control of the Republic of Hawaii at noon on December 12, 1899" - }, - "color": "rgb(140,185,0)", - "size": 250.0 - }, - { - "label": "Incoming Honolulu travelers", - "x": 272.9108581542969, - "y": 525.7716674804688, - "id": "146", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "1894 +", - "Notes": "Stopped and inspected by Honolulu board of health years after 1894" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "mori iga", - "x": 2884.275390625, - "y": 1024.7257080078125, - "id": "131", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "43", - "Extra Info": "", - "Notes": "volunteered to stop the plague" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Chamber of Commerce", - "x": -270.996337890625, - "y": 856.6937255859375, - "id": "142", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "S.G. Wilder", - "x": 701.9417724609375, - "y": 3101.128662109375, - "id": "16", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "6", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Ah Kau", - "x": -75.57275390625, - "y": 500.10089111328125, - "id": "111", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Francis Hatch", - "x": -2882.886962890625, - "y": 868.04638671875, - "id": "132", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "44", - "Extra Info": "", - "Notes": "Government authority who spoke on behalf of white businesspeople" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Honolulu", - "x": 1895.0631103515625, - "y": -103.1948471069336, - "id": "33", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 67.14286041259766 - }, - { - "label": "Chen", - "x": -2016.9681396484375, - "y": 2170.7333984375, - "id": "32", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "9", - "Extra Info": "", - "Notes": "Plague victim" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Emerson, Day and Wood", - "x": -575.0821533203125, - "y": -799.270751953125, - "id": "108", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Propose use of fire in Honolulu Pg. 87-88" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Residents of Chinatown", - "x": -504.87335205078125, - "y": 737.679443359375, - "id": "66", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Society for the Relief of the Destitute", - "x": 281.27850341796875, - "y": 1255.8016357421875, - "id": "109", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Kalanianaole", - "x": -2565.31201171875, - "y": -35.22377014160156, - "id": "144", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Hawaiian Gazette", - "x": 3106.99658203125, - "y": 751.8287353515625, - "id": "174", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "66", - "Extra Info": "1899-12-13", - "Notes": "Hawaiian Newspaper" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Miki Saito", - "x": -775.68798828125, - "y": 160.0733184814453, - "id": "1", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Associates", - "x": -349.3084716796875, - "y": -2992.665771484375, - "id": "119", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "38", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Gu Kim Fui", - "x": -213.29364013671875, - "y": -2731.0419921875, - "id": "31", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "12", - "Extra Info": "", - "Notes": "He is giving an interview to a commercial advertiser to urge people to only burn buildings that were confirmed plague locations. Birth: 1835; Death: 1908-5-3 " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Refugees", - "x": 158.0921630859375, - "y": 814.5675048828125, - "id": "45", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "White Newspaper", - "x": 1183.344482421875, - "y": 775.9883422851562, - "id": "135", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "Toshiyuki Mitamura", - "x": 1531.4549560546875, - "y": -2619.44189453125, - "id": "65", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "19", - "Extra Info": "", - "Notes": "Practiced in Hawaiian islands since 1888. Mediated flow of information from Japanese community to Board of Health. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Yamato Shimbun", - "x": -1656.7593994140625, - "y": 1558.0814208984375, - "id": "54", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Newspaper business in Honolulu" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "King David Kalakaua", - "x": 677.822021484375, - "y": 1569.7186279296875, - "id": "71", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Frank Damon", - "x": 2669.32861328125, - "y": -1148.830322265625, - "id": "127", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "41", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Liliuokalani", - "x": -1710.1346435546875, - "y": -1875.5576171875, - "id": "26", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "John Stevens", - "x": -1562.6160888671875, - "y": -1370.8785400390625, - "id": "2", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Sanitary Committee", - "x": -178.0287628173828, - "y": -34.579139709472656, - "id": "6", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "1899", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Kauehoa", - "x": 1278.761474609375, - "y": 667.7413330078125, - "id": "52", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "national guardsman" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Mori Iga", - "x": -323.6142883300781, - "y": -394.3799133300781, - "id": "133", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Birth: 1864; Death: ?", - "Notes": "Physician previously working with Wood and Day" - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Ichitaro Katsuki", - "x": 2153.9326171875, - "y": 1854.865966796875, - "id": "88", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "26", - "Extra Info": "1865 - 1967", - "Notes": "Japanese-born physician who was a special representative for the San Francisco Board of Health (page 198)" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Wei Pin Yang", - "x": 359.3658142089844, - "y": 381.8961181640625, - "id": "12", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Chinese empire's consul in Hawaii" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Ethel Johnson", - "x": 1832.267333984375, - "y": -2414.38818359375, - "id": "17", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "4", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Kalihi Drill Shed", - "x": -993.3411865234375, - "y": 792.0265502929688, - "id": "35", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "21.331522,-157.8977807", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Upper class white", - "x": 1612.1856689453125, - "y": 2599.891845703125, - "id": "169", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "61", - "Extra Info": "", - "Notes": "Includes Nuuanu Valley Residents, EF Bishop, white supporters of Sarah Boardman" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Wa Ha Pau", - "x": -202.810302734375, - "y": 2974.511474609375, - "id": "115", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "37", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Bishop Estate", - "x": -581.5835571289062, - "y": 539.404052734375, - "id": "86", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Operated around Kalihi Beach" - }, - "color": "rgb(255,112,69)", - "size": 61.42857360839844 - }, - { - "label": "Hawaiian Residents", - "x": 1918.6142578125, - "y": 2471.3232421875, - "id": "166", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "58", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Opening Streets", - "x": 2396.54931640625, - "y": 1792.5787353515625, - "id": "70", - "attributes": { - "Node_Type": "Event", - "Modularity Class": "21", - "Extra Info": "February, 19", - "Notes": "After finding that the situation was improving, the three physicians opened several streets not open since the Chinatown Fire." - }, - "color": "rgb(35,150,111)", - "size": 50.0 - }, - { - "label": "Press", - "x": -848.4004516601562, - "y": 490.6308288574219, - "id": "84", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Commercial Advertiser", - "x": -56.3062744140625, - "y": -2648.83642578125, - "id": "37", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "12", - "Extra Info": "", - "Notes": "Merchant commercial advertiser" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "United Chinese Society", - "x": 126.43289947509766, - "y": 492.1632385253906, - "id": "110", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "U.S.S Iroquois ", - "x": -2515.0, - "y": 1495.220947265625, - "id": "27", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "7", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "William McKinley", - "x": -1144.4354248046875, - "y": 1497.6971435546875, - "id": "85", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Kalihi Detention Camp", - "x": -1476.9674072265625, - "y": 1742.327880859375, - "id": "40", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Young Physicians", - "x": -279.798828125, - "y": 222.00369262695312, - "id": "68", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Wong Chow", - "x": 2423.847412109375, - "y": -1752.607177734375, - "id": "173", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "65", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Politicians", - "x": 2684.988525390625, - "y": -783.3707275390625, - "id": "82", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "24", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Committee of Safety", - "x": -1253.04296875, - "y": -939.4849243164062, - "id": "137", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "White Settlers", - "x": 729.6915283203125, - "y": 1906.969482421875, - "id": "76", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Lau Chung", - "x": 3064.009521484375, - "y": 138.21681213378906, - "id": "141", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "48", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "San Jose Fire Chief", - "x": 2646.749267578125, - "y": 1228.983642578125, - "id": "171", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "63", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Duncan A. Carmichael", - "x": -109.4676742553711, - "y": -488.2804260253906, - "id": "23", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He is not formally on the board, but he acted as an ex-officio advisor to Emerson, Day, and Wood." - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Yim Quan Quan, Yim", - "x": -2316.714599609375, - "y": 1811.2091064453125, - "id": "106", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "34", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Henry E Cooper", - "x": -1697.8192138671875, - "y": -308.675537109375, - "id": "46", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He was the elected officer whom the Board officially reported to and he was on the Board because the Hawaiian Constitution stipulated the A.G. would function as the president of the Board of Health." - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Jordans Dry goods store", - "x": 2026.3525390625, - "y": -33.51780319213867, - "id": "79", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "fort street" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Matrons", - "x": 236.30030822753906, - "y": 270.209716796875, - "id": "105", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Thurston", - "x": -497.4589538574219, - "y": -2894.209716796875, - "id": "89", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "38", - "Extra Info": "1900-02-19 ", - "Notes": "Thurston clashed openly with Emerson and Day. Thurston appeared bent upon using the Hotel Stables incident to put his Citizens' Sanitary Commission in command of the city in place of the Board of Health that created and authorized it. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "J. Weir Robertson", - "x": -2437.354736328125, - "y": -1513.9146728515625, - "id": "98", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "30", - "Extra Info": "", - "Notes": "Honolulu had optimism that the plague did not exist there anymore until Robertson came down with it on February 3" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Emma", - "x": -2493.77001953125, - "y": 119.39424896240234, - "id": "155", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "George Augur", - "x": 1966.0927734375, - "y": 179.22039794921875, - "id": "77", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "doctor" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Nakauaila", - "x": 1346.6380615234375, - "y": 98.52766418457031, - "id": "102", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "One of the first 5 deaths in Chinatown" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Government ", - "x": -891.2184448242188, - "y": 1146.3494873046875, - "id": "25", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 78.57142639160156 - }, - { - "label": "McGrew", - "x": -2631.060546875, - "y": 1124.1904296875, - "id": "75", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "22", - "Extra Info": "1900-02-19", - "Notes": "The Board's old medical nemesis, publicly accused Emerson, Day, and of dangerous inconsistency. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Kaumakapili Church", - "x": 71.8370590209961, - "y": 1453.9866943359375, - "id": "49", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "20.8642661,-155.2436817", - "Notes": "Symbolic center of the traditional Hawaiian community in Chinatown" - }, - "color": "rgb(0,199,255)", - "size": 67.14286041259766 - }, - { - "label": "Chinese Chronicle", - "x": -62.353790283203125, - "y": 3051.356689453125, - "id": "117", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "37", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "William Galbraith", - "x": 835.73291015625, - "y": 2623.951904296875, - "id": "114", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "36", - "Extra Info": "", - "Notes": "Chief surgeon to the Union Pacific Railroad" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "US army medical observers", - "x": -2851.1943359375, - "y": 1317.3681640625, - "id": "161", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "53", - "Extra Info": "1896", - "Notes": "Stationed in Pearl Harbor in 1896. Reported back to Washington that Honolulu was a healthy and secure place. " - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Robertsons Physician", - "x": -32.93634796142578, - "y": -162.04486083984375, - "id": "92", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Wing Wo Tai Company", - "x": 3002.2880859375, - "y": 328.972900390625, - "id": "157", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "48", - "Extra Info": "", - "Notes": "Manufacturing and retail operation" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Oahu railroad station", - "x": 3220.4482421875, - "y": -635.9133911132812, - "id": "78", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "23", - "Extra Info": "20.8642661,-155.2436817", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Hawaiian Aid Society", - "x": -2871.222412109375, - "y": -1038.8953857421875, - "id": "138", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "47", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Plague Victims", - "x": 910.0609130859375, - "y": 143.7878875732422, - "id": "50", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Suffered from plague" - }, - "color": "rgb(140,185,0)", - "size": 72.85714721679688 - }, - { - "label": "Hawaiian Treasury ", - "x": -1118.3380126953125, - "y": -2821.495849609375, - "id": "152", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "50", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Consul Yang", - "x": 1920.8751220703125, - "y": -2194.518798828125, - "id": "8", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "0", - "Extra Info": "Chinese Aid Society; 1900-02-08", - "Notes": "Administered fund for providing essential goods to Chinese evacuees. Accused of stealing funds for himself. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Hoffman", - "x": -324.4167785644531, - "y": -577.9707641601562, - "id": "9", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "1899", - "Notes": "Medical bacteriological trained with the latest techniques. Persuaded to accept the job from other members on the Board. He was the 5th physician member of the Board. Board of Health agrees with him that the plague was definitely in Honolulu" - }, - "color": "rgb(217,125,216)", - "size": 90.0 - }, - { - "label": "Board of Physicians", - "x": 1186.501953125, - "y": -292.3986511230469, - "id": "118", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 67.14286041259766 - }, - { - "label": "Hawaiian Hotel Stables", - "x": -1771.521728515625, - "y": -767.6389770507812, - "id": "81", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "A hotel which was run by the Japanese" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Charles B Cooper", - "x": 606.0535278320312, - "y": -990.7427978515625, - "id": "140", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "President of the Medical Society" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Kitasato", - "x": -577.9833984375, - "y": -2464.58642578125, - "id": "22", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "18", - "Extra Info": "", - "Notes": "Influential Japanese physician" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "James H Raymond", - "x": -400.8027038574219, - "y": -1062.8873291015625, - "id": "147", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Chinese Aid Society", - "x": 2883.422119140625, - "y": -850.0891723632812, - "id": "15", - "attributes": { - "Node_Type": "Event", - "Modularity Class": "3", - "Extra Info": "1900-02-08", - "Notes": "Used to provide transitional support for Chinese evacuees that left the camps. " - }, - "color": "rgb(35,150,111)", - "size": 50.0 - }, - { - "label": "W.E.Taylor", - "x": 250.91287231445312, - "y": -129.217529296875, - "id": "80", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Mention of his comments on the actions of the Board: 1900-02-19", - "Notes": "Physician that avoided the Board. Didn't understand the reasoning for burning the Hotel stables but accepted that the Board thought it was a good idea. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Hawaiians", - "x": -178.50238037109375, - "y": 1719.9852294921875, - "id": "94", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Chinatown", - "x": 926.4285888671875, - "y": 414.4527282714844, - "id": "4", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 118.5714340209961 - }, - { - "label": "Tong San Kai", - "x": 347.0498046875, - "y": 163.98956298828125, - "id": "95", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Nuuanu Valley, Honolulu", - "x": 283.0437927246094, - "y": 2803.927978515625, - "id": "170", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "62", - "Extra Info": "", - "Notes": "Wealthy residence in Honolulu, mostly populated by white residents. " - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "John F. Colburn", - "x": 715.4049072265625, - "y": -896.4007568359375, - "id": "90", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Birth: 1859-9-30; Death: 1920-3-16 Last Minister of the Interior of Queen Liliuokalani." - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Volunteers", - "x": -1891.026611328125, - "y": -598.2655029296875, - "id": "113", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "The Committee of Safety", - "x": -458.0998840332031, - "y": 1838.6939697265625, - "id": "39", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Day", - "x": -561.4107055664062, - "y": -589.927490234375, - "id": "69", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Henry Cooper", - "x": -469.6681213378906, - "y": 156.6598663330078, - "id": "44", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Sarah Boardman", - "x": 1619.651611328125, - "y": 17.47919273376465, - "id": "67", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "white resident living in wealthy Nuuanu Valley, honolulu " - }, - "color": "rgb(217,125,216)", - "size": 78.57142639160156 - }, - { - "label": "Robert Koch", - "x": -741.9411010742188, - "y": -2398.66943359375, - "id": "63", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "18", - "Extra Info": "Experimenting: 1860s", - "Notes": "Lead a group of investigators in Berlin. They investigated bacteriology of different strains of disease causing microorganisms. " - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Blount", - "x": -2982.00830078125, - "y": -716.1730346679688, - "id": "151", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "49", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "F.M. Brooks", - "x": 935.9578857421875, - "y": -776.1165771484375, - "id": "120", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Charles Garvin", - "x": -979.759521484375, - "y": -402.0263366699219, - "id": "20", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Started volunteering: 1899-12-01", - "Notes": "One of the first private practice physicians to volunteer with the Board." - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Qing Empire", - "x": 1370.5941162109375, - "y": 418.33538818359375, - "id": "19", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Ruling empire of China" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Tam Ping Sam Kee", - "x": -1904.0238037109375, - "y": 2373.614013671875, - "id": "112", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "15", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "George R Carter", - "x": 1424.5906982421875, - "y": 542.6140747070312, - "id": "125", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Micronesia", - "x": 2103.401611328125, - "y": -1932.3634033203125, - "id": "100", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "32", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Independent", - "x": -1171.96337890625, - "y": -2490.67333984375, - "id": "61", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "17", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 50.0 - }, - { - "label": "Council of State", - "x": 1072.0174560546875, - "y": -2862.64306640625, - "id": "164", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "56", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Hawaiian Republic Supreme Court", - "x": 1164.1864013671875, - "y": 2532.36767578125, - "id": "97", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "29", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "KuKui bridge", - "x": -1252.02490234375, - "y": 168.2989501953125, - "id": "126", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "President Wood", - "x": 393.03759765625, - "y": -941.5281372070312, - "id": "128", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Hawaii Land Company", - "x": -227.0077667236328, - "y": 522.2977294921875, - "id": "145", - "attributes": { - "Node_Type": "Thing", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(255,112,69)", - "size": 55.71428680419922 - }, - { - "label": "Berlin", - "x": -555.9338989257812, - "y": -1028.7991943359375, - "id": "62", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Emerson", - "x": -202.35391235351562, - "y": -754.7462158203125, - "id": "11", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Ended the quarintine" - }, - "color": "rgb(217,125,216)", - "size": 78.57142639160156 - }, - { - "label": "Kakaako ", - "x": 1379.3619384765625, - "y": -2711.2607421875, - "id": "13", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "19", - "Extra Info": "https://www.google.com/maps/place/Kaka'ako,+Honolulu,+HI/@21.292413,-157.857571,2921m/data=!3m1!1e3!4m5!3m4!1s0x7c006de2a01853e3:0x5e4718558b846b42!8m2!3d21.2963142!4d-157.8554882!6m1!1e1", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Lorrin Thurston", - "x": -813.6103515625, - "y": -398.75274658203125, - "id": "41", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 78.57142639160156 - }, - { - "label": "Kee Mong", - "x": 2423.330078125, - "y": -1527.7327880859375, - "id": "96", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "28", - "Extra Info": "1900-02-26", - "Notes": "Chinese man that died of plague in tenement house." - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Hawaii China Town", - "x": 1508.1676025390625, - "y": -2358.075439453125, - "id": "59", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "16", - "Extra Info": "21.3150194,-157.8751437", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Hawaiian Refugees", - "x": -2460.93359375, - "y": -134.19821166992188, - "id": "136", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 67.14286041259766 - }, - { - "label": "Camp McKinley", - "x": 159.31454467773438, - "y": -2960.66455078125, - "id": "107", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "35", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Ah Sop", - "x": -367.6977844238281, - "y": -52.11518096923828, - "id": "87", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Talked to Wood: 1900-02-26", - "Notes": "Witness that stated the Hotel Stables should be confirmed as a plague spot." - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Sanford Ballard Dole", - "x": -387.0519104003906, - "y": 1393.179931640625, - "id": "42", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Birth: 1844; Death 1926", - "Notes": "Born in Honolulu" - }, - "color": "rgb(217,125,216)", - "size": 90.0 - }, - { - "label": "Levys father ", - "x": 187.78109741210938, - "y": 22.660493850708008, - "id": "153", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "complained about the board" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Sanitary Inspectors ", - "x": -2019.8177490234375, - "y": -2372.10888671875, - "id": "159", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "51", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "Louis Pasteur", - "x": 2651.887451171875, - "y": -1389.0079345703125, - "id": "99", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "31", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Firefighters", - "x": 681.7515258789062, - "y": 69.75890350341797, - "id": "124", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 78.57142639160156 - }, - { - "label": "City Mill", - "x": 1492.4901123046875, - "y": -608.94384765625, - "id": "139", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 55.71428680419922 - }, - { - "label": "Western Medicine Doctors and Physicians", - "x": -335.6007080078125, - "y": 394.5499572753906, - "id": "101", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Ah Hung", - "x": -1501.589599609375, - "y": -546.5320434570312, - "id": "58", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Found dead: 1900-02-19", - "Notes": "Body was found at Hawaiian Hotel Stables. Owned the horses that were used for most of the city's tourism. " - }, - "color": "rgb(217,125,216)", - "size": 72.85714721679688 - }, - { - "label": "Kim Fui Gu", - "x": -2520.626220703125, - "y": 1872.5833740234375, - "id": "14", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "2", - "Extra Info": "", - "Notes": "Yang's vice-consul" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Chug Kun Ai", - "x": -614.9652099609375, - "y": 2922.237548828125, - "id": "18", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "5", - "Extra Info": "", - "Notes": "In China Town when it was on fire. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Nathaniel B Emerson", - "x": 846.8594970703125, - "y": 2963.567138671875, - "id": "24", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "6", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Washington", - "x": 2238.045654296875, - "y": -2008.6993408203125, - "id": "163", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "55", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "Pro-American Planters and Merchants", - "x": -1841.0323486328125, - "y": -1939.3216552734375, - "id": "28", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Kuamakapili Church ", - "x": 2073.710205078125, - "y": 2061.510498046875, - "id": "36", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "11", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "The Medical Society", - "x": 558.7947998046875, - "y": -2800.140625, - "id": "93", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "27", - "Extra Info": "", - "Notes": "Medical Society in Hawaii" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "James Hunt", - "x": 524.253662109375, - "y": 998.2268676757812, - "id": "30", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 84.28572082519531 - }, - { - "label": "Saito Miki", - "x": -177.76515197753906, - "y": 886.4339599609375, - "id": "116", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Honolulu Fire Department", - "x": 920.8604736328125, - "y": 1428.706298828125, - "id": "103", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 61.42857360839844 - }, - { - "label": "Japanese", - "x": -856.416015625, - "y": -2735.734375, - "id": "104", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "33", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 50.0 - }, - { - "label": "David Malo", - "x": -153.59605407714844, - "y": -1247.91162109375, - "id": "123", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "Birth: 1793 ; Death: 1853", - "Notes": "Published author of Hawaiian mythology and folklore" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "Soga Yasutaro", - "x": -1360.396240234375, - "y": 1503.2244873046875, - "id": "7", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "Japanese man who ended up in the Kalihi detention camp, where he eventually broke out and played a major role in organizing the Provisional Japanese Council" - }, - "color": "rgb(217,125,216)", - "size": 67.14286041259766 - }, - { - "label": "Armstrong Smith", - "x": 871.511474609375, - "y": -2724.671630859375, - "id": "34", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "10", - "Extra Info": "Started working at hospital: 1899-12-00; Got plague and recovered: 1900-02-08", - "Notes": "Recent school graduate that volunteered to run the plague hospital. Got sick during second week of February. " - }, - "color": "rgb(217,125,216)", - "size": 50.0 - }, - { - "label": "Clifford B. Wood", - "x": -546.984375, - "y": -202.4073486328125, - "id": "5", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He went on a 3-month trip to Asia, and presented a paper to the Hawaiian Medical Association summarizing what he learned. " - }, - "color": "rgb(217,125,216)", - "size": 107.14286041259766 - }, - { - "label": "Kukui Street", - "x": -2973.21240234375, - "y": 543.77880859375, - "id": "121", - "attributes": { - "Node_Type": "Place", - "Modularity Class": "39", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(0,199,255)", - "size": 50.0 - }, - { - "label": "George Herbert", - "x": 3119.40673828125, - "y": -53.748085021972656, - "id": "149", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "48", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(217,125,216)", - "size": 55.71428680419922 - }, - { - "label": "L D Keliipio", - "x": -1315.8326416015625, - "y": -118.42870330810547, - "id": "60", - "attributes": { - "Node_Type": "Person", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "He had served in the past, but has seldom been active. Civilian" - }, - "color": "rgb(217,125,216)", - "size": 61.42857360839844 - }, - { - "label": "Boys Brigade", - "x": -2459.985107421875, - "y": -360.5040588378906, - "id": "72", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "46", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - }, - { - "label": "Trained Volunteers", - "x": 1100.731201171875, - "y": -40.3592414855957, - "id": "148", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "20", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 55.71428680419922 - } - ], - "edges": [ - { - "source": "0", - "target": "66", - "id": "31", - "attributes": { - "Relationship": "One way conversation like a speech to a crowd", - "Citations": "32; 90; " - }, - "color": "rgb(0,0,0)", - "size": 2.0 - }, - { - "source": "0", - "target": "50", - "id": "16", - "attributes": { - "Relationship": "", - "Citations": "78; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - }, - { - "source": "44", - "target": "0", - "id": "17", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "84; 86; 86; " - }, - "color": "rgb(0,0,0)", - "size": 3.0 - }, - { - "source": "95", - "target": "0", - "id": "58", - "attributes": { - "Relationship": "Reference or mention", - "Citations": "72; " - }, - "color": "rgb(0,0,0)", - "size": 1.0 - } - ] -} \ No newline at end of file diff --git a/build/app/assets/data/empty.json b/build/app/assets/data/empty.json deleted file mode 100644 index 7b56bacb..00000000 --- a/build/app/assets/data/empty.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "nodes": [ - { - "label": "Node 1", - "x": 100, - "y": -100, - "id": "1", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "1", - "Extra Info": "", - "Notes": "" - }, - "color": "rgb(140,185,0)", - "size": 67.14286041259766 - }, - { - "label": "Node 2", - "x": -100, - "y": 100, - "id": "2", - "attributes": { - "Node_Type": "Group", - "Modularity Class": "1", - "Extra Info": "This is Node 2", - "Notes": "Node 2 Notes" - }, - "color": "rgb(217,125,216)", - "size": 50.0 - } - ], - "edges": [ - - ] -} diff --git a/build/app/assets/index.html b/build/app/assets/index.html new file mode 100644 index 00000000..ef2f11bf --- /dev/null +++ b/build/app/assets/index.html @@ -0,0 +1,33 @@ + + + + NetCreate + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/build/app/system/datastore.js b/build/app/system/datastore.js index 8f41155a..f60bf196 100644 --- a/build/app/system/datastore.js +++ b/build/app/system/datastore.js @@ -71,6 +71,12 @@ DSTOR.Data = function() { /*/ API: Write update to database /*/ DSTOR.UpdateServerDB = function(data) { + // check that network is online + if (UNISYS.IsOfflineMode()) { + console.warn(PR,`OFFLINE MODE: UpdateServerDB() suppressed!`); + return; + } + // it is! UDATA.Call("SRV_DBUPDATE", data).then(res => { if (res.OK) { console.log(PR, `server db transaction`, data, `success`); diff --git a/build/app/unisys/client-network.js b/build/app/unisys/client-network.js index 388b4fd7..82beb76c 100644 --- a/build/app/unisys/client-network.js +++ b/build/app/unisys/client-network.js @@ -49,6 +49,16 @@ var UDATA = null; // assigned during NETWORK.Connect() completely. /*/ NETWORK.Connect = function(datalink, opt) { + // special case: OFFLINE mode is set by a different set of magical + // window.NC_UNISYS properties + if (window.NC_UNISYS.server.ip==='standalone') { + m_status = M_OFFLINE; + console.warn(PR,"OFFLINE MODE: NETWORK.Connect() suppressed!"); + NetMessage.GlobalOfflineMode(); + if (typeof opt.success === "function") opt.success(); + return; + } + // if multiple network connections occur, emit warning // warning: don't modify this unless you have a deep knowledge of how // the webapp system works or you might break something diff --git a/build/app/unisys/client.js b/build/app/unisys/client.js index bccb6747..af88eec7 100644 --- a/build/app/unisys/client.js +++ b/build/app/unisys/client.js @@ -60,6 +60,10 @@ var UDATA = new UniData(UNISYS); /// UNISYS MESSAGE REGISTRATION /////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UNISYS.RegisterMessagesPromise = (messages = []) => { + if (NETWORK.IsOfflineMode()) { + console.warn(PR,"OFFLINE MODE: RegisterMessagesPromise() suppressed!"); + return Promise.resolve(); + } if (messages.length) { try { messages = UniData.ValidateMessageNames(messages); @@ -275,6 +279,10 @@ UNISYS.ExitApp = () => { UNISYS.SocketUADDR = () => { return NETWORK.SocketUADDR(); }; +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +UNISYS.IsOfflineMode = () => { + return NETWORK.IsOfflineMode(); +} /// DATA LOGGING ////////////////////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/app/unisys/common-netmessage-class.js b/build/app/unisys/common-netmessage-class.js index f6a2a205..851bf813 100644 --- a/build/app/unisys/common-netmessage-class.js +++ b/build/app/unisys/common-netmessage-class.js @@ -259,8 +259,9 @@ class NetMessage { m_mode ); } else { - console.log( - "SocketSend(): Network is in offline mode: all network traffic suppressed" + console.warn( + PR, + "OFFLINE MODE: SocketSend() suppressed!" ); } // FYI: global m_netsocket is not defined on server, since packets arrive on multiple sockets @@ -270,7 +271,7 @@ class NetMessage { /*/ QueueTransaction(socket = m_netsocket) { if (m_mode === M_OFFLINE) { - console.log("ISOFFLINE"); + console.warn(PR, "OFFLINE MODE: QueueTransaction() suppressed!"); return Promise.resolve(); } // global m_netsocket is not defined on server, since packets arrive on multiple sockets @@ -392,7 +393,7 @@ NetMessage.GlobalCleanup = function() { NetMessage.GlobalOfflineMode = function() { m_mode = M_OFFLINE; if (m_netsocket) { - console.error(PR, "GlobalDisconnect: deallocating netsocket"); + console.warn(PR, "OFFLINE MODE: NetMessage disabling network"); m_netsocket = null; let event = new CustomEvent("UNISYSDisconnect", {}); console.log("dispatching event to", document, event); diff --git a/build/app/unisys/server-database.js b/build/app/unisys/server-database.js index 59ec1217..a972ec59 100644 --- a/build/app/unisys/server-database.js +++ b/build/app/unisys/server-database.js @@ -37,7 +37,7 @@ let DB = {}; /*/ API: Initialize the database /*/ DB.InitializeDatabase = function(options = {}) { - FS.ensureDir(PATH.dirname(DB_FILE)); + FS.ensureDirSync(PATH.dirname(DB_FILE)); if (!FS.existsSync(DB_FILE)) { console.log(PR, `No ${DB_FILE} yet, so filling from ${DB_CLONEMASTER}...`); FS.copySync(`./runtime/${DB_CLONEMASTER}`, DB_FILE); @@ -89,10 +89,14 @@ DB.InitializeDatabase = function(options = {}) { } else { m_max_edgeID = 0; } - console.log( + if (DBG) console.log( PR, `highest ids: NODE.id='${m_max_nodeID}', EDGE.id='${m_max_edgeID}'` ); + + if (typeof m_options.onLoadComplete==='function') { + m_options.onLoadComplete(); + } } // end f_DatabaseInitialize // UTILITY FUNCTION @@ -341,6 +345,36 @@ DB.FilterEdgeLog = function(edge) { Reflect.deleteProperty(newEdge, "_elog"); return newEdge; }; + +/// JSON EXPORT /////////////////////////////////////////////////////////////// +/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +/*/ called by brunch to generate an up-to-date JSON file to path. + creates the path if it doesn't exist +/*/ +DB.WriteJSON = function( filePath ) { + let db = new Loki(DB_FILE,{ + autoload: true, + autoloadCallback: () => { + if (typeof filePath==='string') { + console.log(PR,`writing { nodes, edges } to '${filePath}'`); + let nodes = db.getCollection("nodes").chain() + .data({ removeMeta: true }); + let edges = db.getCollection("edges").chain() + .data({ removeMeta: true }); + let data = { nodes, edges }; + let json = JSON.stringify(data); + console.log(PR,`ensuring DIR ${PATH.dirname(filePath)}`); + FS.ensureDirSync(PATH.dirname( filePath )); + console.log(PR,`writing file ${filePath}`); + FS.writeFileSync( filePath, json ); + } else { + console.log(PR,`ERR path ${filePath} must be a pathname`); + } + } + } + ); +}; + /// EXPORT MODULE DEFINITION ////////////////////////////////////////////////// /// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = module.exports = DB; diff --git a/build/app/view/netcreate/nc-logic.js b/build/app/view/netcreate/nc-logic.js index 2597703a..6901b218 100644 --- a/build/app/view/netcreate/nc-logic.js +++ b/build/app/view/netcreate/nc-logic.js @@ -151,41 +151,64 @@ const TEMPLATE_URL = "../templates/alexander.json"; see client-lifecycle.js for description /*/ MOD.Hook("LOADASSETS", () => { - if (NETWORK.IsOfflineMode()) { - console.warn("NCLOGIC LOADASSETS DETECT OFFLINE MODE"); - return new Promise((resolve, reject) => { - const lstore = window.localStorage; - let ld3 = lstore.getItem("D3DATA"); - D3DATA = JSON.parse(ld3); - if (!D3DATA) reject(Error("couldn't get D3DATA from Local Store")); - UDATA.SetAppState("D3DATA", D3DATA); - let tem = lstore.getItem("TEMPLATE"); - TEMPLATE = JSON.parse(tem); - console.log(D3DATA, TEMPLATE); - if (!TEMPLATE) reject(Error("couldn't get TEMPLATE from Local Store")); - UDATA.SetAppState("TEMPLATE", TEMPLATE); - resolve(); - }); - } else { - // not offline startup, so access network - // load data into D3DATA - let p1 = DATASTORE.PromiseD3Data().then(data => { - if (DBG) console.log(PR, "DATASTORE returned data", data); + if (UNISYS.IsOfflineMode()) { + + const USE_CACHE = false; + if (USE_CACHE) { + console.warn(PR,"OFFLINE MODE: 'LOADASSETS' using browser cache"); + return new Promise((resolve, reject) => { + const lstore = window.localStorage; + let ld3 = lstore.getItem("D3DATA"); + D3DATA = JSON.parse(ld3); + if (!D3DATA) reject(Error("couldn't get D3DATA from Local Store")); + UDATA.SetAppState("D3DATA", D3DATA); + let tem = lstore.getItem("TEMPLATE"); + TEMPLATE = JSON.parse(tem); + console.log(D3DATA, TEMPLATE); + if (!TEMPLATE) reject(Error("couldn't get TEMPLATE from Local Store")); + UDATA.SetAppState("TEMPLATE", TEMPLATE); + resolve(); + }); + } + // don't use cache, but instead try loading standalone files + console.warn(PR,"OFFLINE MODE: 'LOADASSETS' is using files (USE_CACHE=false)"); + let p1 = DATASTORE.PromiseJSONFile("../data/standalone-db.json") + .then(data => { + console.log(data); m_ConvertData(data); m_RecalculateAllEdgeWeights(data); UDATA.SetAppState("D3DATA", data); // Save off local reference because we don't have D3DATA AppStateChange handler D3DATA = data; }); - // load Template data and return it as a promise - // so that react render is called only after the template is loaded - let p2 = DATASTORE.PromiseJSONFile(TEMPLATE_URL).then(data => { - if (DBG) console.log(PR, "DATASTORE returned json", data); + // load template + let p2 = DATASTORE.PromiseJSONFile(TEMPLATE_URL) + .then(data => { TEMPLATE = data; UDATA.SetAppState("TEMPLATE", TEMPLATE); }); - return Promise.all([p1, p2]); + return Promise.all([p1,p2]); } + // if got this far... + // NOT OFFLINE MODE so load data into D3DATA + let p1 = DATASTORE.PromiseD3Data() + .then(data => { + if (DBG) console.log(PR, "DATASTORE returned data", data); + m_ConvertData(data); + m_RecalculateAllEdgeWeights(data); + UDATA.SetAppState("D3DATA", data); + // Save off local reference because we don't have D3DATA AppStateChange handler + D3DATA = data; + }); + // load Template data and return it as a promise + // so that react render is called only after the template is loaded + let p2 = DATASTORE.PromiseJSONFile(TEMPLATE_URL) + .then(data => { + if (DBG) console.log(PR, "DATASTORE returned json", data); + TEMPLATE = data; + UDATA.SetAppState("TEMPLATE", TEMPLATE); + }); + return Promise.all([p1, p2]); }); // loadassets /// UNISYS LIFECYCLE HOOKS //////////////////////////////////////////////////// diff --git a/build/brunch-config.js b/build/brunch-config.js index a4dd76d0..d60aad91 100644 --- a/build/brunch-config.js +++ b/build/brunch-config.js @@ -19,6 +19,10 @@ \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ +/// LOAD LIBRARIES //////////////////////////////////////////////////////////// +/// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = +const UDB = require('./app/unisys/server-database'); + // CommonJS module format // exports a configuration object module.exports = { @@ -92,6 +96,7 @@ module.exports = { environment. You can override each env (e.g. production) after all other declarations are done. /*/ overrides: { + // env 'classroom' is set by npm start / npm run start classroom: { plugins: { autoReload: { enabled: false } @@ -107,11 +112,32 @@ module.exports = { } } }, - classroom_zip: { + // env 'package' is set by npm run package + package: { optimize: false, sourceMaps: true, plugins: { autoReload: { enabled: false } + }, + hooks: { + preCompile() { + // save json of database to public/data + UDB.WriteJSON(`${__dirname}/public/data/standalone-db.json`); + } + } + }, + // env 'package_dbg' is set by npm run package:debug + package_debug: { + optimize: false, + sourceMaps: true, + server : { + path: `${__dirname}/brunch-server-static.js` + }, + hooks: { + preCompile() { + // save json of database to public/data + UDB.WriteJSON(`${__dirname}/public/data/standalone-db.json`); + } } } } diff --git a/build/brunch-server-static.js b/build/brunch-server-static.js new file mode 100644 index 00000000..bd84f933 --- /dev/null +++ b/build/brunch-server-static.js @@ -0,0 +1,54 @@ +/*//////////////////////////////// ABOUT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*\ + + NETCREATE CUSTOM STATIC WEB SERVER + serves static files only + invoked by server.path override in brunch-config for package_debug + +\*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ + +module.exports = ( config, callback ) => { + const express = require('express'); + const IP = require('ip'); + const EXEC = require('child_process').exec; + const app = express(); + app.use(express.static(`${__dirname}/public`)); + app.listen(config.port,function() { + const PR = 'PKG_DEBUG'; + const DP = '***'; + console.log(PR); + console.log(PR,DP,`PACKAGE DEBUG SERVER (STATIC HTML ONLY, NO UNISYS)`,DP); + console.log(PR,DP,`MAINAPP - http://localhost:${config.port}`); + console.log(PR,DP,`CLIENTS - http://${IP.address()}:${config.port}`); + console.log(PR); + // git branch information + EXEC('git symbolic-ref --short -q HEAD',(error,stdout,stderr) => { + if (error) { + // console.error(BP,'git symbolic-ref query error',error); + console.log(PR,DP,'You are running a branch'); + } + if (stdout) { + stdout = stdout.trim(); + console.log(PR,DP,'You are running the "'+stdout+'" branch'); + } + }); + // invoke brunch callback + callback(); + }). + on('error', function(err) { + let errstring = `### NETCREATE SERVER ERROR: '${err.errno}'\n`; + switch (err.errno) { + case 'EADDRINUSE': + errstring += `Another program is already using port ${config.port}.\n`; + errstring += `Go to "http://localhost:${config.port}" to check if NetCreate is already running.\n\n`; + errstring += `Still broken? See https://github.com/daveseah/netcreate-2018/issues/4\n`; + break; + default: + errstring += `${err}`; + console.log(err); + } + console.log(`\n\n${errstring}\n### PROGRAM STOP\n`); + throw new Error(err.errno); + }); + return app +}; + diff --git a/build/brunch-server.js b/build/brunch-server.js index 5312ebb4..fea7281d 100644 --- a/build/brunch-server.js +++ b/build/brunch-server.js @@ -102,11 +102,11 @@ module.exports = (config, callback) => { EXEC('git symbolic-ref --short -q HEAD',(error,stdout,stderr) => { if (error) { // console.error(BP,'git symbolic-ref query error',error); - console.log(GIT,'You are running a branch'); + console.log(PR,DP,'You are running a branch'); } if (stdout) { stdout = stdout.trim(); - console.log(GIT,'You are running the "'+stdout+'" branch'); + console.log(PR,DP,'You are running the "'+stdout+'" branch'); } }); // now start the UNISYS network diff --git a/build/package.json b/build/package.json index ab5d6c48..66d0267b 100644 --- a/build/package.json +++ b/build/package.json @@ -3,10 +3,12 @@ "version": "1.0.0", "description": "research project for network graph analysis", "scripts": { - "bare": "node --inspect brunch-server.js", "dev": "brunch watch -s", - "start": "brunch build -e classroom", - "zip": "brunch build -e classroom_zip", + "dev:inspect": "node --inspect brunch-server.js", + "start": "npm run classroom", + "classroom": "brunch build -e classroom", + "package": "brunch build -e package", + "package:debug": "brunch watch -e package_debug -s", "clean": "rm -rf ./public ./node_modules; npm ci", "debug": "LOGGY_STACKS=true BRUNCH_DEVTOOLS=true ./brunch-debug watch --server", "log": "tail -f -n100 \"$(ls -at ./runtime/logs/* | head -n 1)\"" From 1007d286a5d204e4af11ccb357d32b985ef64313 Mon Sep 17 00:00:00 2001 From: Dave 'Sri' Seah Date: Sat, 12 Jan 2019 19:50:03 -0500 Subject: [PATCH 13/13] dev-ds-offline-view: rename OFFLINE to STANDALONE, add instructions and clean up prompting --- build/app/assets/index.html | 6 +++++- build/app/init-appshell.jsx | 2 +- build/app/system/datastore.js | 16 ++++++++++++---- build/app/unisys/client-network.js | 18 +++++++++--------- build/app/unisys/client.js | 8 ++++---- build/app/unisys/common-netmessage-class.js | 14 +++++++------- build/app/unisys/component/SessionShell.jsx | 17 ++++++++++++++++- build/app/unisys/server-database.js | 18 +++++++----------- build/app/view/netcreate/nc-logic.js | 9 ++++----- build/brunch-config.js | 17 +++++++++++++++++ build/brunch-server-static.js | 2 +- build/brunch-server.js | 4 ++-- build/package.json | 2 +- 13 files changed, 86 insertions(+), 47 deletions(-) diff --git a/build/app/assets/index.html b/build/app/assets/index.html index ef2f11bf..68aceace 100644 --- a/build/app/assets/index.html +++ b/build/app/assets/index.html @@ -14,9 +14,13 @@ // index.html is loaded only by standalone mode without a server // server-based operation uses index.ejs window.NC_UNISYS = { - standalone : true, + standalone : { + prompt: 'STANDALONE VIEW', + timestamp: '1/1/2019' + }, server : { ip : 'standalone' }, client : { ip : 'standalone' }, + } diff --git a/build/app/init-appshell.jsx b/build/app/init-appshell.jsx index 4267c760..a7c5c44c 100644 --- a/build/app/init-appshell.jsx +++ b/build/app/init-appshell.jsx @@ -176,7 +176,7 @@ class AppShell extends UNISYS.Component { return (
- NetCreate August Demo + NetCreate Spring Demo {/*/ (1) add navigation links here /*/} diff --git a/build/app/system/datastore.js b/build/app/system/datastore.js index f60bf196..cd030725 100644 --- a/build/app/system/datastore.js +++ b/build/app/system/datastore.js @@ -72,8 +72,8 @@ DSTOR.Data = function() { /*/ DSTOR.UpdateServerDB = function(data) { // check that network is online - if (UNISYS.IsOfflineMode()) { - console.warn(PR,`OFFLINE MODE: UpdateServerDB() suppressed!`); + if (UNISYS.IsStandaloneMode()) { + console.warn(PR,`STANDALONE MODE: UpdateServerDB() suppressed!`); return; } // it is! @@ -95,7 +95,11 @@ DSTOR.PromiseNewNodeID = function() { if (DBG) console.log(PR, "server allocated node_id", data.nodeID); resolve(data.nodeID); } else { - reject(new Error("unknown error" + JSON.stringify(data))); + if (UNISYS.IsStandaloneMode()) { + reject(new Error("STANDALONE MODE: UI should prevent PromiseNewNodeID() from running!")); + } else { + reject(new Error("unknown error" + JSON.stringify(data))); + } } }); }); @@ -110,7 +114,11 @@ DSTOR.PromiseNewEdgeID = function() { if (DBG) console.log(PR, "server allocated edge_id:", data.edgeID); resolve(data.edgeID); } else { - reject(new Error("unknown error" + JSON.stringify(data))); + if (UNISYS.IsStandaloneMode()) { + reject(new Error("STANDALONE MODE: UI should prevent PromiseNewEdgeID() from running!")); + } else { + reject(new Error("unknown error" + JSON.stringify(data))); + } } }); }); diff --git a/build/app/unisys/client-network.js b/build/app/unisys/client-network.js index 82beb76c..1d569172 100644 --- a/build/app/unisys/client-network.js +++ b/build/app/unisys/client-network.js @@ -32,7 +32,7 @@ const M1_CONNECTING = 1; const M2_CONNECTED = 2; const M3_REGISTERED = 3; const M4_READY = 4; -const M_OFFLINE = 5; +const M_STANDALONE = 5; const M_NOCONNECT = 6; var m_status = M0_INIT; var m_options = {}; @@ -49,11 +49,11 @@ var UDATA = null; // assigned during NETWORK.Connect() completely. /*/ NETWORK.Connect = function(datalink, opt) { - // special case: OFFLINE mode is set by a different set of magical + // special case: STANDALONE mode is set by a different set of magical // window.NC_UNISYS properties if (window.NC_UNISYS.server.ip==='standalone') { - m_status = M_OFFLINE; - console.warn(PR,"OFFLINE MODE: NETWORK.Connect() suppressed!"); + m_status = M_STANDALONE; + console.warn(PR,"STANDALONE MODE: NETWORK.Connect() suppressed!"); NetMessage.GlobalOfflineMode(); if (typeof opt.success === "function") opt.success(); return; @@ -93,7 +93,7 @@ NETWORK.Connect = function(datalink, opt) { NETWORK.AddListener("close", function(event) { if (DBG.connect) console.log(PR, "..CLOSE", event.target.url); NetMessage.GlobalOfflineMode(); - m_status = M_OFFLINE; + m_status = M_STANDALONE; }); // handle socket errors NETWORK.AddListener("error", function(event) { @@ -113,8 +113,8 @@ NETWORK.Connect = function(datalink, opt) { case appCache.UPDATEREADY: /* falls-through */ case appCache.OBSOLETE: // this occurs - console.info(WARN, "OFFLINE MODE. USING CACHED DATA"); - m_status = M_OFFLINE; + console.info(WARN, "STANDALONE MODE. USING CACHED DATA"); + m_status = M_STANDALONE; NetMessage.GlobalOfflineMode(); // deregister socket // force promise to succeed if (typeof m_options.success === "function") m_options.success(); @@ -281,8 +281,8 @@ NETWORK.SocketUADDR = function() { return NetMessage.SocketUADDR(); }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -NETWORK.IsOfflineMode = function() { - return m_status === M_OFFLINE; +NETWORK.IsStandaloneMode = function() { + return m_status === M_STANDALONE; }; /// EXPORT MODULE DEFINITION ////////////////////////////////////////////////// diff --git a/build/app/unisys/client.js b/build/app/unisys/client.js index af88eec7..543f1215 100644 --- a/build/app/unisys/client.js +++ b/build/app/unisys/client.js @@ -60,8 +60,8 @@ var UDATA = new UniData(UNISYS); /// UNISYS MESSAGE REGISTRATION /////////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UNISYS.RegisterMessagesPromise = (messages = []) => { - if (NETWORK.IsOfflineMode()) { - console.warn(PR,"OFFLINE MODE: RegisterMessagesPromise() suppressed!"); + if (NETWORK.IsStandaloneMode()) { + console.warn(PR,"STANDALONE MODE: RegisterMessagesPromise() suppressed!"); return Promise.resolve(); } if (messages.length) { @@ -280,8 +280,8 @@ UNISYS.SocketUADDR = () => { return NETWORK.SocketUADDR(); }; /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -UNISYS.IsOfflineMode = () => { - return NETWORK.IsOfflineMode(); +UNISYS.IsStandaloneMode = () => { + return NETWORK.IsStandaloneMode(); } /// DATA LOGGING ////////////////////////////////////////////////////////////// diff --git a/build/app/unisys/common-netmessage-class.js b/build/app/unisys/common-netmessage-class.js index 851bf813..3b65d4f0 100644 --- a/build/app/unisys/common-netmessage-class.js +++ b/build/app/unisys/common-netmessage-class.js @@ -29,7 +29,7 @@ var m_group_id = null; const M_INIT = "init"; const M_ONLINE = "online"; -const M_OFFLINE = "offline"; +const M_STANDALONE = "offline"; const M_CLOSED = "closed"; const M_ERROR = "error"; var m_mode = M_INIT; @@ -252,7 +252,7 @@ class NetMessage { console.log(PR, status); } socket.send(this.JSON()); - } else if (m_mode !== M_OFFLINE) { + } else if (m_mode !== M_STANDALONE) { console.log( PR, "SocketSend: Can't send because NetMessage mode is", @@ -261,7 +261,7 @@ class NetMessage { } else { console.warn( PR, - "OFFLINE MODE: SocketSend() suppressed!" + "STANDALONE MODE: SocketSend() suppressed!" ); } // FYI: global m_netsocket is not defined on server, since packets arrive on multiple sockets @@ -270,8 +270,8 @@ class NetMessage { /*/ Create a promise to resolve when packet returns /*/ QueueTransaction(socket = m_netsocket) { - if (m_mode === M_OFFLINE) { - console.warn(PR, "OFFLINE MODE: QueueTransaction() suppressed!"); + if (m_mode === M_STANDALONE) { + console.warn(PR, "STANDALONE MODE: QueueTransaction() suppressed!"); return Promise.resolve(); } // global m_netsocket is not defined on server, since packets arrive on multiple sockets @@ -391,9 +391,9 @@ NetMessage.GlobalCleanup = function() { server and the client. This is a client feature. /*/ NetMessage.GlobalOfflineMode = function() { - m_mode = M_OFFLINE; + m_mode = M_STANDALONE; if (m_netsocket) { - console.warn(PR, "OFFLINE MODE: NetMessage disabling network"); + console.warn(PR, "STANDALONE MODE: NetMessage disabling network"); m_netsocket = null; let event = new CustomEvent("UNISYSDisconnect", {}); console.log("dispatching event to", document, event); diff --git a/build/app/unisys/component/SessionShell.jsx b/build/app/unisys/component/SessionShell.jsx index 7ece6ce5..7049fbec 100644 --- a/build/app/unisys/component/SessionShell.jsx +++ b/build/app/unisys/component/SessionShell.jsx @@ -80,6 +80,7 @@ class SessionShell extends UNISYS.Component { classId: null, projId: null, hashedId: null, + subId: null, groupId: null, isValid: false }; @@ -175,6 +176,20 @@ class SessionShell extends UNISYS.Component { // this.props.history, location, match added by withRouter(AppShell) // way back in init-appshell.jsx + // if standalone mode, no login possible + if (UNISYS.IsStandaloneMode()) { + const { prompt, timestamp } = window.NC_UNISYS.standalone; + return ( + + + + + + + + + ); + } // no token so just render login let token = this.props.match.params.token; if (!token) return this.renderLogin(); @@ -193,7 +208,7 @@ class SessionShell extends UNISYS.Component { handleChange(event) { let token = event.target.value; let decoded = SESSION.DecodeToken(token); - let { classId, projId, hashedId, groupId } = decoded; + let { classId, projId, hashedId, groupId, subId } = decoded; this.setState(decoded); this.SetAppState("SESSION", decoded); if (decoded.groupId) { diff --git a/build/app/unisys/server-database.js b/build/app/unisys/server-database.js index a972ec59..ced83522 100644 --- a/build/app/unisys/server-database.js +++ b/build/app/unisys/server-database.js @@ -17,7 +17,7 @@ const FS = require("fs-extra"); const SESSION = require("../unisys/common-session"); const LOGGER = require("../unisys/server-logger"); const PROMPTS = require("../system/util/prompts"); -const PR = PROMPTS.Pad("SRV-DB"); +const PR = PROMPTS.Pad("ServerDB"); const DB_FILE = "./runtime/netcreate.loki"; const DB_CLONEMASTER = "alexander.loki"; @@ -89,9 +89,7 @@ DB.InitializeDatabase = function(options = {}) { } else { m_max_edgeID = 0; } - if (DBG) console.log( - PR, - `highest ids: NODE.id='${m_max_nodeID}', EDGE.id='${m_max_edgeID}'` + console.log(PR,`DATABASE LOADED! m_max_nodeID '${m_max_nodeID}', m_max_edgeID '${m_max_edgeID}'` ); if (typeof m_options.onLoadComplete==='function') { @@ -103,10 +101,7 @@ DB.InitializeDatabase = function(options = {}) { function f_AutosaveStatus() { let nodeCount = NODES.count(); let edgeCount = EDGES.count(); - if (DBG) console.log( - PR, - `autosaving ${nodeCount} nodes and ${edgeCount} edges...` - ); + console.log(PR,`AUTOSAVING! ${nodeCount} NODES / ${edgeCount} EDGES <3`); } }; // InitializeDatabase() /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -356,17 +351,18 @@ DB.WriteJSON = function( filePath ) { autoload: true, autoloadCallback: () => { if (typeof filePath==='string') { - console.log(PR,`writing { nodes, edges } to '${filePath}'`); + if (DBG) console.log(PR,`writing { nodes, edges } to '${filePath}'`); let nodes = db.getCollection("nodes").chain() .data({ removeMeta: true }); let edges = db.getCollection("edges").chain() .data({ removeMeta: true }); let data = { nodes, edges }; let json = JSON.stringify(data); - console.log(PR,`ensuring DIR ${PATH.dirname(filePath)}`); + if (DBG) console.log(PR,`ensuring DIR ${PATH.dirname(filePath)}`); FS.ensureDirSync(PATH.dirname( filePath )); - console.log(PR,`writing file ${filePath}`); + if (DBG) console.log(PR,`writing file ${filePath}`); FS.writeFileSync( filePath, json ); + console.log(PR,`*** WROTE JSON DATABASE`); } else { console.log(PR,`ERR path ${filePath} must be a pathname`); } diff --git a/build/app/view/netcreate/nc-logic.js b/build/app/view/netcreate/nc-logic.js index 6901b218..8515173a 100644 --- a/build/app/view/netcreate/nc-logic.js +++ b/build/app/view/netcreate/nc-logic.js @@ -151,11 +151,11 @@ const TEMPLATE_URL = "../templates/alexander.json"; see client-lifecycle.js for description /*/ MOD.Hook("LOADASSETS", () => { - if (UNISYS.IsOfflineMode()) { + if (UNISYS.IsStandaloneMode()) { const USE_CACHE = false; if (USE_CACHE) { - console.warn(PR,"OFFLINE MODE: 'LOADASSETS' using browser cache"); + console.warn(PR,"STANDALONE MODE: 'LOADASSETS' using browser cache"); return new Promise((resolve, reject) => { const lstore = window.localStorage; let ld3 = lstore.getItem("D3DATA"); @@ -171,10 +171,9 @@ MOD.Hook("LOADASSETS", () => { }); } // don't use cache, but instead try loading standalone files - console.warn(PR,"OFFLINE MODE: 'LOADASSETS' is using files (USE_CACHE=false)"); + console.warn(PR,"STANDALONE MODE: 'LOADASSETS' is using files (USE_CACHE=false)"); let p1 = DATASTORE.PromiseJSONFile("../data/standalone-db.json") .then(data => { - console.log(data); m_ConvertData(data); m_RecalculateAllEdgeWeights(data); UDATA.SetAppState("D3DATA", data); @@ -190,7 +189,7 @@ MOD.Hook("LOADASSETS", () => { return Promise.all([p1,p2]); } // if got this far... - // NOT OFFLINE MODE so load data into D3DATA + // NOT STANDALONE MODE so load data into D3DATA let p1 = DATASTORE.PromiseD3Data() .then(data => { if (DBG) console.log(PR, "DATASTORE returned data", data); diff --git a/build/brunch-config.js b/build/brunch-config.js index d60aad91..71406220 100644 --- a/build/brunch-config.js +++ b/build/brunch-config.js @@ -91,6 +91,12 @@ module.exports = { } }, + hooks: { + onCompile() { + console.log(`\n*** NetCreate (DEV) is running ***\n`); + } + }, + /// OVERRIDES FOR PRODUCTION ////////////////////////////////////////////////// /*/ Brunch configuration settings default to development mode in the environment. You can override each env (e.g. production) after all other @@ -106,6 +112,7 @@ module.exports = { const server = require("./brunch-server"); return new Promise((resolve, reject) => { server({ port : 3000}, function() { + console.log(`\n*** NetCreate (CLASSROOM) is running ***\n`); resolve(); }); }); @@ -123,6 +130,12 @@ module.exports = { preCompile() { // save json of database to public/data UDB.WriteJSON(`${__dirname}/public/data/standalone-db.json`); + }, + onCompile() { + console.log(`\n*** STANDALONE PACKAGE has been BUILT`); + console.log(`\n The standalone package is in public/ and run from index.html.`); + console.log(` Edit index.html to change the prompts shown in the app.`); + console.log(` Upload contents of public/ to a web server to use!\n`); } } }, @@ -137,6 +150,10 @@ module.exports = { preCompile() { // save json of database to public/data UDB.WriteJSON(`${__dirname}/public/data/standalone-db.json`); + }, + onCompile() { + console.log(`\n*** STANDALONE PACKAGE DEBUG MODE`); + console.log(` Point browser to MAINAPP or CLIENT addresses indicated `); } } } diff --git a/build/brunch-server-static.js b/build/brunch-server-static.js index bd84f933..e28b53c8 100644 --- a/build/brunch-server-static.js +++ b/build/brunch-server-static.js @@ -13,7 +13,7 @@ module.exports = ( config, callback ) => { const app = express(); app.use(express.static(`${__dirname}/public`)); app.listen(config.port,function() { - const PR = 'PKG_DEBUG'; + const PR = 'PKGDEBUG -'; const DP = '***'; console.log(PR); console.log(PR,DP,`PACKAGE DEBUG SERVER (STATIC HTML ONLY, NO UNISYS)`,DP); diff --git a/build/brunch-server.js b/build/brunch-server.js index fea7281d..8fabad59 100644 --- a/build/brunch-server.js +++ b/build/brunch-server.js @@ -1,7 +1,7 @@ /*//////////////////////////////// ABOUT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*\ NETCREATE CUSTOM APP SERVER - brunch-config.js specifies this custom server + automatically loaded by 'brunch watch -s' from package.json \*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/ @@ -18,7 +18,7 @@ const EXEC = require('child_process').exec; /// LOCAL CONSTANTS, VARS AND FLAGS /////////////////////////////////////////// /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const PROMPTS = require('./app/system/util/prompts'); -const PR = PROMPTS.Pad('AppServer'); +const PR = PROMPTS.Pad('APP_SERV'); const DP = PROMPTS.Stars(3); const GIT = PROMPTS.Pad('GIT'); var UKEY_IDX = 0; diff --git a/build/package.json b/build/package.json index 66d0267b..91be24cf 100644 --- a/build/package.json +++ b/build/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "brunch watch -s", "dev:inspect": "node --inspect brunch-server.js", - "start": "npm run classroom", + "start": "brunch build -e classroom", "classroom": "brunch build -e classroom", "package": "brunch build -e package", "package:debug": "brunch watch -e package_debug -s",