UNPKG

endpointjs

Version:

Endpoint.js enables modules within a web application to discover and use each other, whether that be on the same web page, other browser windows and tabs, iframes, servers and web workers in a reactive way by providing robust discovery, execution and stre

1,599 lines (1,403 loc) 1.27 MB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ (function (__filename){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ /* globals __filename */ 'use strict'; var EventEmitter = require('events').EventEmitter, inherits = require('util').inherits, appUtils = require('../util/appUtils'), log = appUtils.getLogger(__filename); inherits(Link, EventEmitter); module.exports = Link; /** * Abstract base class for links. This class uses an incrementing * value to identify links, and subscribes to the window object * to close each link before the window closes * @param {Number} linkId - the unique identifier for this link * @param {Object} settings * @param {Object} settings.transformFactory - a function for adding/removing transforms to a link stream * @param {Number} settings.heartbeatTimeout - amount of time to wait before killing link * @constructor */ function Link(linkId, settings) { if (!(this instanceof Link)) { return new Link(linkId, settings); } this._linkId = linkId; this._settings = settings; log.log(log.DEBUG, 'Link initialized: [Type: %s] [ID: %s]', this.getType(), this.getId()); } /** * Returns the heartbeat timeout for this link. If no information is received in this * interval, then the link will timeout. Default to 1.5 minutes */ Link.prototype.getHeartbeatTimeout = function() { return this._settings.heartbeatTimeout; }; /** * A transform factory is a function that takes a {LinkTransform} interface and adds * additional read/write transforms to the link after a connection is made */ Link.prototype.getTransformFactory = function() { return this._settings.transformFactory; }; /** * Returns the type of link this is * @returns {Error} */ Link.prototype.getType = function() { return new Error('not implemented'); }; /** * Return the unique id of this link. * @returns {*} */ Link.prototype.getId = function() { return this._linkId; }; /** * The cost to transmit to this link * @returns {number} */ Link.prototype.getCost = function() { return 0; }; /** * If true, routing information from this host should be treated as * 'external', meaning it cannot affect the internal routing table */ Link.prototype.isExternal = function() { return false; }; /** * Close all open streams */ Link.prototype.close = function() { log.log(log.DEBUG, 'Link closed: [Type: %s] [ID: %s]', this.getType(), this.getId()); }; }).call(this,"/js\\app\\link\\link.js") },{"../util/appUtils":4,"events":107,"util":244}],2:[function(require,module,exports){ (function (__filename){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ /* globals __filename */ 'use strict'; var Link = require('./link'), inherits = require('util').inherits, uuid = require('node-uuid'), expHash = require('../util/expirable-hash'), appUtils = require('../util/appUtils'), log = appUtils.getLogger(__filename), through2 = require('through2'); inherits(ProtocolLink, Link); module.exports = ProtocolLink; /** * Abstract base class for links which require a protocol to establish * a link. The protocol is based on a simple 3-step process: * - greetings - broadcast existence * - hi - reply to greetings * - ready - can start receiving data * @augments Link * @param instanceId - unique identifier for the endpoint.js instance * @param linkId - unique identifier for this link * @param {Object} settings * @constructor */ function ProtocolLink(instanceId, linkId, settings) { if (!(this instanceof ProtocolLink)) { return new ProtocolLink(instanceId, linkId, settings); } this._instanceId = instanceId; Link.call(this, linkId, settings); // Allow 15 seconds for hosts to establish a stream this._handshakes = expHash(15, 'Protocol link: ' + linkId); this._handshakes.on('expired', function(key, value) { log.log(log.WARN, 'Host exchange expired'); value.closeFn(); }); // List of streamInfo, indexed by uuid. this._streams = {}; } /** * In response to a greetings message, allocate a link * stream for this sender * @param fromUuid * @param [metadata] * @private */ ProtocolLink.prototype._handleGreeting = function(msg, edgeId, instanceId) { // This is the host we will report to our upper level/layer if this // is an external connection. We also use this as the 'sender' in // future messages var streamId = uuid(); instanceId = instanceId || msg.s; edgeId = edgeId || uuid(); // Ensure the values are valid. if (!appUtils.isUuid(instanceId) || !appUtils.isUuid(edgeId)) { throw new Error('Invalid instance or edge id'); } // Destination information for the initial greeting var inject = { d: msg.s, // send initial replies to opposite greetings id. s: streamId }; // Create the sender for this destination, as well as // the client side user stream. // Use a unique value for edgeId in case we have to // re-establish this link in the future (prevent collisions) var streamInfo = { streamId: streamId, instanceId: instanceId || msg.s, edgeId: this.isExternal() ? edgeId : instanceId, ready: false, readTransport: through2.obj(), sendTransport: this._createInjectStream(inject, msg), closeFn: endFunc }; // When the stream ends, remove it var ended = false; var _this = this; function endFunc() { if (!ended) { log.log(log.DEBUG, 'Lost connection: [Link Type: %s] [Link ID: %s] [Edge ID: %s] [From: %s]', _this.getType(), _this.getId(), streamInfo.edgeId, streamInfo.instanceId); // Do not re-execute this code. ended = true; // Make sure both streams are ended streamInfo.readTransport.push(null); streamInfo.readTransport.end(); // Remove from the cache (if it's there) if (streamInfo.ready) { delete _this._streams[streamId]; // Tell anyone listening _this.emit('connection-close', _this, streamInfo.edgeId); } else { _this._handshakes.remove(streamId); // Need to force end the sendTransport, since link stream hasn't // been created yet, meaning it won't propagate the close. streamInfo.sendTransport.push(null); streamInfo.sendTransport.end(); } } } streamInfo.sendTransport.on('finish', endFunc); streamInfo.sendTransport.on('end', endFunc); // Save the data for later this._handshakes.add(streamId, streamInfo); log.log(log.DEBUG, 'New connection: [Link Type: %s] [Link ID: %s] [Edge ID: %s] [From: %s]', this.getType(), this.getId(), streamInfo.edgeId, streamInfo.instanceId); return streamInfo; }; /** * When a sender disconnects, then kill his link stream * @param destinationUuid * @param [metadata] * @private */ ProtocolLink.prototype._handleGoodbye = function(msg) { var streamInfo = this._streams[msg.d]; if (streamInfo) { // This will trigger the link close. streamInfo.readTransport.push(null); streamInfo.readTransport.end(); } }; /** * Creates a writer that can send protocol messages via the 'sendProtocolCommand' * and sends the message * @param command * @param [message] * @returns {Error} * @private */ ProtocolLink.prototype._sendProtocolCommand = function(toUuid, command, message) { var streamInfo = this._streams[toUuid] || this._handshakes.get(toUuid); if (streamInfo) { this._sendProtocolCommandTo(streamInfo.sendTransport, command, message); } }; /** * Send a protocol command to a specific host (transport) * @param transport * @param command * @param message * @private */ ProtocolLink.prototype._sendProtocolCommandTo = function(transport, command, message) { transport.write({ p: command, m: message }); }; /** * This function will create a writer stream to the given destination */ ProtocolLink.prototype._createInjectStream = function(inject, metadata) { // Add the destination, only if the 'p' protocol flag isn't // set. var writeStream = through2.obj( function(chunk, encoding, cb) { for (var prop in inject) { chunk[prop] = inject[prop]; } this.push(chunk); cb(); }); writeStream.updateInject = function(data) { inject = data; }; // Pipe the write stream through a sender stream writeStream.pipe(this._createSenderStream(metadata)); return writeStream; }; /** * Happens as soon as the destination has created its buffer streams * and is ready for me to start sending messages * @param msg * @private */ ProtocolLink.prototype._handleReady = function(msg, hisInstanceId) { var streamId = msg.d; var streamInfo = this._handshakes.get(streamId); if (streamInfo && !streamInfo.ready) { // Update his instance id if he specified it if (hisInstanceId) { // Ensure the values are valid. if (!appUtils.isUuid(hisInstanceId)) { throw new Error('Invalid instance id'); } streamInfo.instanceId = hisInstanceId; } // Address messages the way he wants them. streamInfo.sendTransport.updateInject({ d: msg.s }); streamInfo.ready = true; // Move the stream to this._streams this._streams[streamInfo.streamId] = streamInfo; this._handshakes.remove(streamInfo.streamId); // Tell listeners this.emit('connection', this, streamInfo.edgeId, { read: streamInfo.readTransport, write: streamInfo.sendTransport }, streamInfo.instanceId); return true; } return false; }; /** * This is a message chunk from an external source (fromUuid) * directed at me * @param fromUuid * @param message * @private */ ProtocolLink.prototype._handleReader = function(reader) { var assignedReaders = {}; // This will keep track of this messenger locally in this closure, so // that if the reader transport fails, we can kill all the dependent // streams var registerReader = function(streamId) { if (!assignedReaders[streamId]) { assignedReaders[streamId] = this._streams[streamId].readTransport; assignedReaders[streamId].once('end', function() { delete assignedReaders[streamId]; }); } }.bind(this); var handle = function() { var msg; while ((msg = reader.read()) !== null) { if (!msg || !msg.d) { continue; } try { var streamInfo; if (msg.p) { // Respond to protocol events. Mostly from hello's and goodbyes. switch (msg.p) { case 'greetings': if (msg.d == 'broadcast') { // Make sure it's not from myself. IE bug! if (msg.s != this._instanceId) { // Message sent that says 'Hi, I'm new here!' streamInfo = this._handleGreeting(msg); // Reply to destination that we're here. this._sendProtocolCommand(streamInfo.streamId, 'hi', { i: this._instanceId, e: streamInfo.edgeId }); } } break; case 'hi': if (msg.d == this._instanceId && msg.m) { // Message sent that says 'Hi, I'm new here!' streamInfo = this._handleGreeting(msg, msg.m.e, msg.m.i); // Reply to destination that we're ready. this._sendProtocolCommand(streamInfo.streamId, 'ready'); // Seamlessly transition our destination id to the // newly generated id. msg.d = streamInfo.streamId; // Create streams for the destination this._handleReady(msg, msg.m.i); registerReader(msg.d); } break; case 'ready': if (this._handleReady(msg)) { registerReader(msg.d); } break; case 'goodbye': // Message sent that says 'Goodbye, I'm leaving!' this._handleGoodbye(msg); break; } } else { // Not a protocol message, send to the next layer. streamInfo = this._streams[msg.d]; if (streamInfo) { streamInfo.readTransport.push(msg); } } } catch (e) { log.log(log.ERROR, 'Exception reading: %s', e.stack); } } }.bind(this); var terminate = function() { reader.removeListener('readable', handle); for (var streamId in assignedReaders) { assignedReaders[streamId].push(null); } assignedReaders = {}; }.bind(this); reader.once('end', terminate); reader.on('readable', handle); }; /** * Send the protocol message to the given destination * @param metadata * @private */ ProtocolLink.prototype._announce = function(metadata) { var inject = { d: 'broadcast', s: this._instanceId }; var writeStream = this._createInjectStream(inject, metadata); this._sendProtocolCommandTo(writeStream, 'greetings'); writeStream.end(); }; /** * Will manually create a 'send' transport stream for the specific destination * @param destinationUuid * @param [metadata] * @returns {*} * @private */ ProtocolLink.prototype._createSenderStream = function(metadata) { return new Error('not implemented'); }; /** * Close the specific stream key. * @param streamKey */ ProtocolLink.prototype.closeLink = function(streamKey) { // This will trigger the close of this stream var streamInfo = this._streams[streamKey]; if (streamInfo) { this._sendProtocolCommandTo(streamInfo.sendTransport, 'goodbye'); streamInfo.closeFn(); } }; /** * Close all open streams */ ProtocolLink.prototype.close = function() { // Close all streams var streamKeys = Object.keys(this._streams); streamKeys.forEach(function(streamKey) { this.closeLink(streamKey); }, this); // Tell parent Link.prototype.close.call(this); }; }).call(this,"/js\\app\\link\\protocol-link.js") },{"../util/appUtils":4,"../util/expirable-hash":6,"./link":1,"node-uuid":126,"through2":237,"util":244}],3:[function(require,module,exports){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ /* globals __filename */ 'use strict'; var through2 = require('through2'), isBuffer = require('util').isBuffer, Buffer = require('buffer').Buffer; /** * The buffer stringify stream supports two forms of encoding node.js * buffers. The first will perform a base64 encoding of buffer objects * in place, and then run that through JSON.stringify. The second method * will put 'placeholders' where the buffers were, and transfer the raw * 'buffer' objects in a modified packet. * @module stringify-stream */ module.exports = {}; /** * Stringify the chunk if it's an object. * @returns {*} */ module.exports.encode = function(inPlace) { return through2.obj(function(chunk, encoding, cb) { this.push(module.exports.encodeFunction(inPlace, chunk)); cb(); }); }; /** * TODO: Be able to support typed arrays and array buffer encoding, * for object mode streams that contain binary. * This is the function used by the encode stream * @param chunk * @returns {*} */ module.exports.encodeFunction = function(inPlace, chunk) { var obj; if (inPlace) { chunk = traverse('', chunk, function(key, value) { if (value._isBuffer || isBuffer(value)) { value = { type: 'buffer-i', data: value.toString('base64') }; } return value; }); obj = JSON.stringify(chunk); } else { var transfer = []; var i = 0; chunk = traverse('', chunk, function(key, value) { if (chunk._isBuffer || isBuffer(value)) { var newValue = { type: 'buffer-o', index: i++ }; transfer.push(value.toArrayBuffer()); return newValue; } return value; }); obj = JSON.stringify(chunk); obj = { data: obj, transfer: transfer }; } return obj; }; /** * Parse the chunk if it's a string. * @returns {*} */ module.exports.decode = function(inPlace) { return through2.obj(function(chunk, encoding, cb) { this.push(module.exports.decodeFunction(inPlace, chunk)); cb(); }); }; /** * This is the function used by the decode stream * @param chunk * @returns {*} */ module.exports.decodeFunction = function(inPlace, chunk) { var obj; if (inPlace) { obj = JSON.parse(chunk); obj = traverse('', obj, function(key, value) { if (value && value.type == 'buffer-i') { return new Buffer(value.data, 'base64'); } return value; }); } else { obj = JSON.parse(chunk.data); obj = traverse('', obj, function(key, value) { if (value && value.type == 'buffer-o') { return new Buffer(chunk.transfer[value.index]); } return value; }); } return obj; }; /** * Traverse the object stack and execute func on it. * @param funcCB */ function traverse(key, value, funcCB) { var v1 = funcCB(key, value); if (value === v1) { if (v1 && typeof (v1) == 'object') { for (var k in v1) { if (Object.prototype.hasOwnProperty.call(v1, k)) { if (v1[k]) { v1[k] = traverse(k, v1[k], funcCB); } } } } } else { return v1; } return value; } },{"buffer":59,"through2":237,"util":244}],4:[function(require,module,exports){ (function (process,global){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ /*globals global, process */ 'use strict'; var logger = require('./logger').Logger, constants = require('./constants'), isString = require('util').isString; /** * A set of utilities, such as creating loggers or setting the script name. Also contains * event subscription utility functions. * @namespace * @property {String} _scriptName */ var appUtils = { /** * The name of the executing script, populated by loader.js */ _scriptName: null, /** * This function will determine the Endpoint.js script name * @returns {*} */ initScriptName: function() { if (!this._scriptName) { var wnd = this.getGlobalObject(); if (wnd.document) { // The most recently executing script should be Endpoint.js. Lets // get the address for it. var scripts = wnd.document.getElementsByTagName('script'); var script = scripts[scripts.length - 1]; this._scriptName = script.src; } } }, /** * Ensure the UUID is the right size * @param uuid */ isUuid: function(uuid) { if (isString(uuid) && uuid.length == 36) { return true; } return false; }, /** * Ensure the External UUID is the right size * @param uuid */ isExtUuid: function(uuid) { if (isString(uuid) && uuid.length == 40 && uuid.indexOf('-ext') === 36) { return true; } return false; }, /** * Return the script name * @returns {string} */ getScriptName: function() { return this._scriptName; }, // This module returns a reference to the global (Window) object. // See the link below for an in depth explanation as to why this // is necessary. // // http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function // // The gist is that: // // (1) we want to operate in strict mode at all times in all files // (2) outside of strict mode, 'this' would refer to the global // Window object, but when running in strict mode, 'this' is // undefined // (3) Because of RequireJS, we often wrap our code in a closure, // which prevents some other popular techniques from working // (4) The approach below works in all Browsers, Engines, ES3, // ES5, strict, nested scope, etc. and will pass JSHint/JSLint // getGlobalObject: function() { return global; }, /** * Execute the given function on the next tick. * @param func */ nextTick: function(func) { process.nextTick(func); }, /** * __filename doesn't properly work in browserify on windows. * @param location * @returns {*} */ getScriptBasename: function(location) { if (location && typeof (location) == 'string' && (location[0] == '/' || location[0] == '\\')) { location = location.replace(/\\/g, '/'); var pieces = location.split('/'); location = pieces[pieces.length - 1]; } return location; }, /** * This function will instantiate a logger and return it. * @param location */ getLogger: function(location) { return logger(this.getScriptBasename(location)); }, /** * IE Safe event listener * @param event * @param cb * @param bubble */ addEventListener: function(obj, event, cb, bubble) { if ('addEventListener' in obj) { obj.addEventListener(event, cb, bubble); } else { obj.attachEvent('on' + event, cb); } }, /** * IE Safe event listener * @param event * @param cb * @param bubble */ removeEventListener: function(obj, event, cb, bubble) { if ('removeEventListener' in obj) { obj.removeEventListener(event, cb, bubble); } else { obj.detachEvent('on' + event, cb); } }, /** * Attempt to parse the neighborhood, and if undefined, use default. */ getNeighborhood: function(specifiedNeighborhood, defaultNeighborhood) { var nDefault = constants.Neighborhood[(defaultNeighborhood + '').toUpperCase()]; var nSpecified = constants.Neighborhood[(specifiedNeighborhood + '').toUpperCase()]; return typeof (nSpecified) == 'undefined' ? nDefault : nSpecified; } }; module.exports = appUtils; }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./constants":5,"./logger":7,"_process":149,"util":244}],5:[function(require,module,exports){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ 'use strict'; /** * Common constants used throughout the API. * @module */ module.exports = { /** * The valid types of endpoints within the system */ EndpointType: { CLIENT: 'Client', CLIENT_INSTANCE: 'Client Instance', FACADE: 'Facade', ADAPTER: 'Adapter', OBJECT_INSTANCE: 'Object Instance', CALL: 'Call', QUERY: 'Query' }, /** * The valid types of Links that can connect to * endpoints in the system */ LinkType: { SERVER: 'server', WORKER: 'worker', TAB: 'tab', WINDOW: 'window' }, /** * When a message is sent or received, this allows for determining * how wide to send it, or where it originated from. These values * are set by the router as messages come in and leave the system. */ Neighborhood: { UNIVERSAL: 3, GLOBAL: 2, GROUP: 1, LOCAL: 0 } }; },{}],6:[function(require,module,exports){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ 'use strict'; var periodicTimer = require('./periodic-timer'), inherits = require('util').inherits, EventEmitter = require('events').EventEmitter; inherits(ExpirableHash, EventEmitter); module.exports = ExpirableHash; /** * A Javascript Hash object where the objects expire after a certain amount of * time in milliseconds * @constructor */ function ExpirableHash(duration, name) { if (!(this instanceof ExpirableHash)) { return new ExpirableHash(duration, name); } EventEmitter.call(this); this._duration = duration * 1000; this._items = 0; this._itemsArray = []; this._itemsHash = {}; this._timer = periodicTimer(name || 'Expirable Hash', this._duration); this._timer.on('period', this._check.bind(this)); } /** * Add a key to the dictionary * @param key * @param value */ ExpirableHash.prototype.add = function(key, value) { this._clean(); this.remove(key); var item = { k: key, v: value, t: (new Date()).getTime() + this._duration }; this._items += 1; this._itemsArray.push(item); this._itemsHash[key] = item; this._timer.addReference(); }; /** * Get a key from the dictionary. * @param key */ ExpirableHash.prototype.get = function(key) { // Check for expired keys this._check(); var item = this._itemsHash[key]; if (item) { return item.v; } }; /** * Update the time value of the given key * @param key */ ExpirableHash.prototype.touch = function(key) { var val = this.get(key); if (val) { this.remove(key); this.add(key, val); } }; /** * Remove a key from the dictionary * @param key */ ExpirableHash.prototype.remove = function(key) { var item = this._itemsHash[key]; if (item) { this._items -= 1; item.v = item.k = null; item.t = 0; delete this._itemsHash[key]; this._timer.removeReference(); } }; /** * Remove expired keys * @private */ ExpirableHash.prototype._check = function() { var now = (new Date()).getTime(); while (this._itemsArray.length > 0) { var item = this._itemsArray[0]; if (item.t > now) { break; } else if (item.t > 0) { this.emit('expired', item.k, item.v); this.remove(item.k); } this._itemsArray.shift(); } }; /** * Clean up the hash if the amount of dead items is double the * amount of items in the hash. But only if the amount of items * is greater than 10. * @private */ ExpirableHash.prototype._clean = function() { if (this._itemsArray.length > 10 && this._itemsArray.length > (this._items * 2)) { var newArray = []; this._itemsArray.forEach(function(item) { if (item.t > 0) { newArray.push(item); } }); this._itemsArray = newArray; } }; },{"./periodic-timer":8,"events":107,"util":244}],7:[function(require,module,exports){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ 'use strict'; var format = require('util').format; module.exports = { Logger: Logger, logLevel: 'warn' }; /** * This is a really basic logger module that passes through to * console.log depending on the log level. * @param {Object} location - a string or object name identifying log location * @constructor */ function Logger(location) { if (!(this instanceof Logger)) { return new Logger(location); } location = location || 'Unknown'; var useLocation = 'Unknown'; if (typeof (location) == 'string') { useLocation = location; } else if (typeof (location) == 'object') { if (location.constructor) { useLocation = location.constructor.name; } } this._location = location; } /** * Trace level * @type {string} */ Logger.prototype.TRACE = 'trace'; /** * Debug Level (3) * @type {string} */ Logger.prototype.DEBUG3 = 'debug3'; /** * Debug Level (2) * @type {string} */ Logger.prototype.DEBUG2 = 'debug2'; /** * Debug Level (1) * @type {string} */ Logger.prototype.DEBUG = 'debug'; /** * Info Level * @type {string} */ Logger.prototype.INFO = 'info'; /** * Warn Level * @type {string} */ Logger.prototype.WARN = 'warn'; /** * Error Level * @type {string} */ Logger.prototype.ERROR = 'error'; /** * None Level * @type {string} */ Logger.prototype.NONE = 'none'; /** * The priority of every log level * @type {{debug3: number, debug2: number, debug: number, info: number, warn: number, error: number}} */ Logger.prototype._LevelPriority = { trace: 8, debug3: 7, debug2: 6, debug: 5, info: 4, warn: 3, error: 2, none: 1 }; /** * Try to use this command on 'console' when logging * these items, if possible. */ Logger.prototype._ConsoleMap = { trace: 'debug', debug3: 'debug', debug2: 'debug', debug: 'debug', info: 'info', warn: 'warn', error: 'error' }; /** * Function to log. Additional arguments are treated as inputs * to util.format. * @param level * @param message */ Logger.prototype.log = function(level, message) { if (typeof (console) == 'undefined') { return; } if (!this._LevelPriority[level]) { level = this.DEBUG; } var currentPriority = this._LevelPriority[module.exports.logLevel]; var inputPriority = this._LevelPriority[level]; if (inputPriority <= currentPriority) { if (arguments.length > 2) { var args = Array.prototype.slice.call(arguments, 2); args.unshift(message); message = format.apply(format, args); } var date = new Date(); var msg = format('%s:%s:%s - [%s] [%s] %s', date.getHours(), date.getMinutes(), date.getSeconds(), level, this._location, message); if (typeof (console[this._ConsoleMap[level]]) == 'function') { console[this._ConsoleMap[level]](msg); } else { console.log(msg); } } }; },{"util":244}],8:[function(require,module,exports){ (function (__filename){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ /* globals __filename, clearInterval, setInterval */ 'use strict'; var appUtils = require('../util/appUtils'), log = appUtils.getLogger(__filename), inherits = require('util').inherits, EventEmitter = require('events').EventEmitter; inherits(PeriodicTimer, EventEmitter); module.exports = PeriodicTimer; /** * A periodic timer will execute the callback function, only if the * reference counter is greater than zero. It will automatically * start/stop the interval timer based on the reference count. * @param {String} name - descriptive name * @param {Number} timeout - how long to set the interval for * @constructor */ function PeriodicTimer(name, timeout) { if (!(this instanceof PeriodicTimer)) { return new PeriodicTimer(name, timeout); } EventEmitter.call(this); this._name = name; this._timeout = timeout || 15000; this._references = 0; this._timerId = null; } /** * Returns the number of references in this periodic timer. */ PeriodicTimer.prototype.getReferenceCounter = function() { return this._references; }; /** * Add a reference and start the timer (if necessary) */ PeriodicTimer.prototype.addReference = function() { this._references++; return this._checkTimer(); }; /** * Remove a reference and remove the timer (if necessary) */ PeriodicTimer.prototype.removeReference = function() { this._references--; return this._checkTimer(); }; /** * Start or stop the periodic timer based on the number of active * links * @private */ PeriodicTimer.prototype._checkTimer = function() { var reportTimerId = this._timerId; if (this._references === 0 && this._timerId !== null) { // Stop timer log.log(log.DEBUG2, 'Stopping [%s] interval timer: %s', this._name, this._timerId); clearInterval(this._timerId); this._timerId = null; this.emit('period', true); } else if (this._references > 0 && this._timerId === null) { this._timerId = setInterval(function() { this.emit('period'); }.bind(this), this._timeout); log.log(log.DEBUG2, 'Starting [%s] interval timer: %s', this._name, this._timerId); reportTimerId = this._timerId; } return reportTimerId; }; }).call(this,"/js\\app\\util\\periodic-timer.js") },{"../util/appUtils":4,"events":107,"util":244}],9:[function(require,module,exports){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ /* globals __filename */ var EventEmitter = require('events').EventEmitter, logManager = require('../../app/util/logger'), webrtcLink = require('./webrtc-link'), toPull = require('stream-to-pull-stream'), quickConnect = require('rtc-quickconnect'), appUtils = require('../../app/util/appUtils'); /** * @module webrtc-plugin */ // Whether the custom link has been added to endpoint. var globalObj = appUtils.getGlobalObject(); var endpoint = globalObj.endpoint; if (!endpoint) { throw new Error('Cannot find Endpoint.js (window.endpoint)'); } // Set our logging to match Endpoint.js logManager.logLevel = globalObj.endpointLogLevel; // Initialize the webrtc link inside Endpoint.js var linkType = 'webrtc'; var linkCreateFunction = function(instanceId, linkId, settings) { return webrtcLink(instanceId, linkId, settings); }; globalObj.endpoint.getConfiguration().addCustomLinkType(linkType, linkCreateFunction); /** * Provide a mechanism to initialize & subscribe to discovery API * @example * var plugin = require('webrtc-plugin')(window.endpoint); * plugin.on('ready', function(conference) { * // create webrtc link and add conference * }); * @param endpoint - a reference to endpoint.js instance * @param {Object} [opts] * @param {String} [opts.linkId] - the link to broadcast web-rtc-discovery to, or all links if undefined * @param {String} [opts.room] - the room to use on the signal host * @param {String} [opts.quickconnect] - options to send to rtc-quickconnect (rtc.io) * @param {Object} [opts.settings] - settings passed to facade (neighborhood defaults to global) * @return {EventEmitter} emits the 'ready' event when connected to signal host */ globalObj.endpointWebRTC = function(opts) { var emitter = new EventEmitter(); opts = opts || {}; var settings = opts.settings || {}; settings.neighborhood = settings.neighborhood || 'global'; if (settings.linkId) { var bridge = endpoint.getConfiguration().createBridge([settings.linkId]); settings.bridgeId = bridge.getId(); } // Look for the webrtc discovery api. var fmgr = endpoint.manageFacades(['web-rtc-discovery', '1.0', settings]); var rtc; fmgr.on('ready', function() { // Init the transfer stream var stream = fmgr.getApi('web-rtc-discovery').register().stream(); var ended = false; stream.on('finish', function() { ended = true; }); // Create the messenger. var messenger = function() { return function(callback) { if (ended) { callback(new Error('stream has ended')); return; } var duplex = toPull.duplex(stream); callback(null, duplex.source, duplex.sink); }; }; var quickOpts = opts.quickconnect || {}; quickOpts.room = settings.room || quickOpts.room || 'endpointjs-room'; quickOpts.messenger = messenger; // Create the RTC connection var rtc = quickConnect('fake', quickOpts); emitter.emit('ready', rtc); }); return emitter; }; },{"../../app/util/appUtils":4,"../../app/util/logger":7,"./webrtc-link":10,"events":107,"rtc-quickconnect":194,"stream-to-pull-stream":229}],10:[function(require,module,exports){ (function (__filename){ /* * (C) 2016 * Booz Allen Hamilton, All rights reserved * Powered by InnoVision, created by the GIAT * * Endpoint.js was developed at the * National Geospatial-Intelligence Agency (NGA) in collaboration with * Booz Allen Hamilton [http://www.boozallen.com]. The government has * "unlimited rights" and is releasing this software to increase the * impact of government investments by providing developers with the * opportunity to take things in new directions. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* jshint -W097 */ /* globals __filename */ var ProtocolLink = require('../../app/link/protocol-link'), inherits = require('util').inherits, through2 = require('through2'), stringifyStream = require('../../app/streams/stringify-stream'), appUtils = require('../../app/util/appUtils'), log = appUtils.getLogger(__filename); inherits(WebRTC, ProtocolLink); m