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

233 lines (199 loc) 7.52 kB
'use strict'; var throttleDebounce = require('throttle-debounce'); var geom = require('../../../src/utils/geom.js'); var observable = require('../../../src/utils/observable.js'); var sdkHeadless = require('./sdkHeadless.js'); function _interopNamespaceDefaultOnly (e) { return Object.freeze({ __proto__: null, default: e }); } let removePreviousListener = null; // browser based communication // listens for messages passed through // postMessage and converts them to clientAPI/execute calls.. // responds by sending messages back through postMessage // returns a sendEvent function used for sending events through postMessage as well function getBrowserCom (app) { const clean = ob => JSON.parse(JSON.stringify(ob)); const sendResponse = (payload, clientMsgId) => { const ob = { payload, type: 'LL-server' }; if (clientMsgId) ob.clientMsgId = clientMsgId; try { window.postMessage(ob, '*'); } catch (e) { window.postMessage(clean(ob), '*'); } // failure to send could be due to non-marshalable object - this helps }; const sendError = (payload, clientMsgId) => { const ob = { error: true, payload, type: 'LL-server' }; if (clientMsgId) ob.clientMsgId = clientMsgId; window.postMessage(ob, '*'); }; const sendEvent = (event, payload) => { const ob = { event, payload, type: 'LL-server' }; window.postMessage(ob, '*'); }; function msgHandler (e) { const d = e.data; if (d && d.type === 'LL-client') { // confirm message is from our "client" app.bus.get('clientAPI/execute', d.payload) .then(resOb => sendResponse(resOb, d.msgId)) .catch(er => { if (app.config.debug) console.error(er); sendError(er.message, d.msgId); }); } } // Listen for HTML5 postMessage events - these come from SDKClient apps if (removePreviousListener) removePreviousListener(); window.addEventListener('message', msgHandler); removePreviousListener = () => window.removeEventListener('message', msgHandler); return sendEvent } // For node, command sending and response sending is handled via // nodeEntry - no postMessage required. // sendEvent attaches a hook into app to receive them... function getNodeCom (app) { const eventListener = observable(); app.eventListener = eventListener; const sendEvent = (event, payload) => eventListener.fire(event, payload); return sendEvent } function registerCustomTypes (app) { app.bus.send('clientAPI/registerCustomType', { name: 'latLngOrdLocation', spec: { type: 'object', props: [ { name: 'lat', type: 'float' }, { name: 'lng', type: 'float' }, { name: 'ord', type: 'integer' } ] } }); app.bus.send('clientAPI/registerCustomType', { name: 'latLngFloorLocation', spec: { type: 'object', props: [ { name: 'lat', type: 'float' }, { name: 'lng', type: 'float' }, { name: 'floorId', type: 'string' } ] } }); app.bus.send('clientAPI/registerCustomType', { name: 'poiIdLocation', spec: { type: 'object', props: [ { name: 'poiId', type: 'integer', min: 0 } ] } }); app.bus.send('clientAPI/registerCustomType', { name: 'location', spec: { type: 'multi', types: [ { type: 'poiIdLocation' }, { type: 'latLngOrdLocation' }, { type: 'latLngFloorLocation' } ] } }); app.bus.send('clientAPI/registerCustomType', { name: 'viewSettings', spec: { type: 'object', props: [ { name: 'zoom', type: 'float', optional: true }, { name: 'pitch', type: 'float', optional: true }, { name: 'bearing', type: 'float', optional: true } ] } }); } function registerEvents (app, sendEvent) { app.bus.monitor('map/userMoveStart', async ({ pitch, zoom, bearing }) => { const { lat, lng, floorId, ordinal, structureId } = await app.bus.get('map/getMapCenter'); sendEvent('userMoveStart', { lat, lng, floorId, ord: ordinal, structureId, pitch, zoom, bearing }); }); const userMoving = async ({ pitch, zoom, bearing }) => { const { lat, lng, floorId, ordinal, structureId } = await app.bus.get('map/getMapCenter'); sendEvent('userMoving', { lat, lng, floorId, ord: ordinal, structureId, pitch, zoom, bearing }); }; app.bus.monitor('map/userMoving', throttleDebounce.throttle(500, userMoving)); app.bus.monitor('map/moveEnd', async ({ pitch, zoom, bearing }) => { const { lat, lng, floorId, ordinal, structureId } = await app.bus.get('map/getMapCenter'); sendEvent('moveEnd', { lat, lng, floorId, ord: ordinal, structureId, pitch, zoom, bearing }); }); app.bus.monitor('map/floorChanged', ({ structure, floor }) => sendEvent('levelChange', { floorId: floor ? floor.id : null, floorName: floor ? floor.name : null, ord: floor ? floor.ordinal : null, structureId: structure ? structure.id : null, structureName: structure ? structure.name : null })); app.bus.monitor('map/poiClicked', ({ poi }) => sendEvent('poiSelected', poi)); app.bus.monitor('poiDetails/showPoi', ({ poi }) => sendEvent('poiShown', poi)); app.bus.monitor('map/click', async ({ lat, lng, ord }) => { const structures = await app.bus.get('venueData/getStructures'); const mapviewBBox = await app.bus.get('map/getViewBBox'); const { building: structure, floor } = geom.getStructureAndFloorAtPoint(structures, lat, lng, ord, mapviewBBox, true); sendEvent('mapClicked', { lat, lng, ord, building: structure, floor }); }); } async function create (app, config) { const sendEvent = app.env.isBrowser ? getBrowserCom(app) : getNodeCom(app); const init = async () => { // Register the Custom Types registerCustomTypes(app); // Register our commands sdkHeadless.headlessCommands.forEach(cdef => app.bus.send('clientAPI/registerCommand', cdef)); sdkHeadless.handleHeadless(app); if (!config.headless) { await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefaultOnly(require('../../../_virtual/_empty_module_placeholder.js')); }) .then(sdkVisual => { sdkVisual.visualCommands.forEach(cdef => app.bus.send('clientAPI/registerCommand', cdef)); sdkVisual.handleVisual(app, sendEvent); }); } const weReady = async () => { await app.bus.send('system/readywhenyouare'); // give other modules a chance to hold us up... app.bus.get('clientAPI/execute', { command: 'getCommandJSON' }) .then(commandJSON => sendEvent('ready', { commandJSON })); if (!config.headless && app.config.uiHide && app.config.uiHide.sidebar && app.env.isDesktop()) app.bus.send('map/changePadding', { padding: { left: 55, right: 55, top: 72, bottom: 22 } }); }; if (config.headless) // in headless case, we are ready once data is ready Promise.all([ new Promise(resolve => app.bus.monitor('venueData/navGraphLoaded', resolve)), new Promise(resolve => app.bus.monitor('venueData/poiDataLoaded', resolve)) ]).then(weReady); else app.bus.on('map/mapReadyToShow', weReady); app.bus.on('sdkServer/sendEvent', ({ eventName, ...props }) => sendEvent(eventName, props)); }; // Register Events registerEvents(app, sendEvent); return { init } } exports.create = create;