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
JavaScript
;
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;