UNPKG

atriusmaps-node-sdk

Version:

This project provides an API to Atrius Personal Wayfinder maps within a Node environment. See the README.md for more information

155 lines (129 loc) 4.65 kB
'use strict'; var Zousan = require('zousan'); /** * Bustle is a highly performant event bus that offers event order guarantees. */ const bustleResponseComparitor = (r1, r2) => { if (r1.responseOrder == null) // null or undefined return r2.responseOrder == null ? 0 : 1 // any order is higher pri than none if (r2.responseOrder == null) return -1 return r1.responseOrder - r2.responseOrder }; // Remove element from array at index specified and return the removed element. // Source array will then have item removed. const arRmAt = (ar, index) => ar.splice(index, 1)[0]; // oh yah, thats intuitive! // Removes the value from the array if it exists. The array is then returned const arRm = (ar, value) => { let i = 0; do { i = ar.indexOf(value, i); if (i >= 0) arRmAt(ar, i); } while (i >= 0) return ar }; // This creates a bus instance. When used as a Singleton, you will only call this once // for the life of your app. To isolate events (perhaps for performance or security // reasons) you can create multple bus instances. function create (opts = { }) { // If a log option is defined, use it - and if it supports sublogs, instantiate that. If all else fails, use console const log = opts.log ? (opts.log.sublog ? opts.log.sublog('bustle', { color: 'pink' }) : opts.log) : console; const ons = { }; // holds your listeners const mons = { }; // holds your monitors // subscribes to an event. Your listener will always be called // with a single argument. I am considering adding another way // to subscribe to events by observing an event and then // subscribing to that observer. function on (ev, fn) { if (!ons[ev]) ons[ev] = []; ons[ev].push(fn); return () => off(ev, fn) } // Similar to "on" but is always called after all normal "on" listeners, // and any values returned by a monitor call are ignored (not added to the // response). Also, any calls to monitor listeners contains both the message // object AND a promise containing the response from the normal listeners. function monitor (ev, fn) { if (!mons[ev]) mons[ev] = []; mons[ev].push(fn); return () => moff(ev, fn) } function off (ev, fn) { // run ASAP but not immediate, in case it removes in its own function Zousan.soon(() => { if (!ons[ev]) return arRm(ons[ev], fn); }); } function moff (ev, fn) { // run ASAP but not immediate, in case it removes in its own function Zousan.soon(() => { if (!mons[ev]) return arRm(mons[ev], fn); }); } const serve = (ev, ob, done) => () => { // const myOns = (ons[ev] || []).concat(ons["*"] ? ons["*"] : []) const myOns = ons[ev]; const myMons = mons[ev]; const res = []; if (myOns) { for (const listener of myOns) { try { res.push(listener(ob)); } catch (err) { if (opts.reportAllErrors) log.error(err); if (opts.rejectOnError) res.push(Zousan.reject(err)); // will cause the Zousan.all to reject the send else res.push(err); } } } const results = Zousan.all(res); if (myMons) { for (const listener of myMons) { try { listener(ob, results); } catch (err) { if (opts.reportAllErrors) log.error(err); } } } done(results); }; // Used when there is a single listener that provides information or a service, // a get sends a message to that listener, ensures it gets exactly 1 result, // and returns that result. If there are 0 or more than 1 listener, the returned // promise is rejected const get = (ev, ob) => send(ev, ob).then(res => res.length !== 1 ? Zousan.reject(`${ev} event did not return a single result, but ${res.length} results.`) : res[0]); // Allows for any number of listeners to exist - but extracts the first one // and returns it. If there are NO listeners, undefined is returned. The promise // is never rejected. const getFirst = (ev, ob) => send(ev, ob).then(res => res.length >= 1 ? res.sort(bustleResponseComparitor)[0] : undefined); function send (ev, ob) { if (opts.showEvents) { if (typeof opts.showEvents === 'function') { if (opts.showEvents(ev, ob)) log.info('send with', ev, ' and ', ob); } else log.info('send with', ev, ' and ', ob); } return new Zousan(resolve => Zousan.soon(serve(ev, ob, resolve))) } return { get, getFirst, moff, monitor, off, on, send } } exports.create = create;