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