webp2p
Version:
The server-less signaling channel for WebRTC
1,069 lines (985 loc) • 40.7 kB
JavaScript
/*
* Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Jean-Claude Dufourd
* This code was developed with the Coltram project, funded by the French ANR.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* This notice must stay in all subsequent versions of this code.
*/
;
var NSDPlusPlus = {};
(function () {
var conn = null;
var eventValues = [];
//noinspection UnnecessaryLocalVariableJS
var discoveredServices = [];
NSDPlusPlus.discoveredServices = discoveredServices;
//noinspection UnnecessaryLocalVariableJS
var connectedCallbacks = [];
NSDPlusPlus.connectedCallbacks = connectedCallbacks;
//noinspection UnnecessaryLocalVariableJS
var initializedCallbacks = [];
NSDPlusPlus.initializedCallbacks = initializedCallbacks;
NSDPlusPlus.initialized = false;
var serviceImplementations = [];
//
// add function to Array, to remove the first occurence of obj in the array
//
Array.prototype.remove = function (obj) {
var i = this.indexOf(obj);
if (i >= 0) {
this.splice(i, 1);
}
};
NSDPlusPlus.isDiscovered = function (service) {
if (service == null) {
throw "testing null service for past discovery";
}
for (var i = 0; i < discoveredServices.length; i++) {
if (discoveredServices[i].id == service.id) {
return true;
}
}
return false;
};
//
// add NSD fields not present in Java
//
NSDPlusPlus.addDiscoveredService = function (serviceFromJava) {
if (serviceFromJava == null) {
throw "service received from agent is null";
}
discoveredServices.push(serviceFromJava);
};
/////////////////////////////////////////////////////
//// Objects ////////////////////////////////////////
/////////////////////////////////////////////////////
//
// class Action
//
//noinspection JSUnusedLocalSymbols
function Action(name, args) {
this.name = name;
this.args = args;
}
//
// class Argument
//
//noinspection JSUnusedLocalSymbols
function Argument(name, relatedStateVar, direction) {
this.name = name;
//noinspection JSUnusedGlobalSymbols
this.relatedStateVariable = relatedStateVar;
this.dir = direction;
}
NSDPlusPlus.ServiceImplementation = function () {
};
//NSDPlusPlus.nbOfServiceImplementations = function () {
// return serviceImplementations.length;
//};
NSDPlusPlus.getImplementation = function (implementationId) {
//console.log("getImplementation", serviceImplementations, implementationId);
return serviceImplementations[implementationId];
};
/////////////////////////////////////////////////////
//// Connect ////////////////////////////////////////
/////////////////////////////////////////////////////
function errorFunction(msg) {
NSDPlusPlus.logger("Error " + msg);
for (var i in msg) {
//noinspection JSUnfilteredForInLoop
if (!(typeof msg[i] == 'function')) {
//noinspection JSUnfilteredForInLoop
NSDPlusPlus.logger(">" + i + ":" + msg[i]);
}
}
}
//
// first call to lib: connection to the proxy
//
NSDPlusPlus.connect = function (host) {
NSDPlusPlus.logger("connecting to " + host);
if (conn) {
var defered = function() {
if (!webSocketConnected) {
setTimeout(function() {
defered();
}, 100);
} else {
socketConnected();
}
};
defered();
return;
}
try {
conn = new WebSocket(host == null ? "ws://localhost:56797/" : "ws://" + host + ":56797/");
} catch (e) {
NSDPlusPlus.logger("error creating WebSocket " + e);
NSDPlusPlus.logger("cannot continue...");
return;
}
conn.onopen = socketConnected;
conn.onmessage = msgHandler;
conn.onclose = socketClosed;
conn.onerror = errorFunction;
};
var webSocketConnected = false;
//
// internal: when a socket is connected
//
function socketConnected() {
//
// then call the connected callbacks
//
if (connectedCallbacks.length > 0) {
for (var i = 0; i < connectedCallbacks.length; i++) {
connectedCallbacks[i]();
NSDPlusPlus.removeEventListener("connected", connectedCallbacks[i]);
}
}
webSocketConnected = true;
}
//
// internal: message handler
//
function msgHandler(e) {
var obj;
try {
// obj = JSON.parse(e.data);
eval('obj = ' + e.data); // works better on long strings ?
//Coltram.logger(e.data);
} catch (error) {
NSDPlusPlus.logger("+------+");
NSDPlusPlus.logger("|" + error + " " + e.data);
NSDPlusPlus.logger("+------+");
}
// NSDPlusPlus.resetLog();
// NSDPlusPlus.logger(obj.purpose+" "+(e.data+"").substr(0, 400));
messageHandlers[obj.purpose](obj);
}
//
// internal: msg handling functions
//
var messageHandlers = {};
//
// allow to define message handlers from outside the module
//
NSDPlusPlus.addMessageHandler = function (name, func) {
messageHandlers[name] = func;
};
//
// internal: when a socket is closed
//
function socketClosed() {
NSDPlusPlus.logger("Disconnected");
}
//noinspection JSUnusedLocalSymbols
NSDPlusPlus.logger = function (s) {};
NSDPlusPlus.resetLog = function () {};
// allows to keep a unique reference to the server service object
var exposeduniqueSequence = 1;
var exposeduniqueTimestamp = new Date().getTime();
var getExposedUniqueId = function(type, protocol) {
var id = type + "_" + protocol + "_" + exposeduniqueTimestamp + "_" + (exposeduniqueSequence++);
return id;
};
/////////////////////////////////////////////////////
//// Expose ////////////////////////////////////////
/////////////////////////////////////////////////////
//
// expose a service
//
NSDPlusPlus.expose = function (type, protocol, serviceImplementation) {
if (arguments.length < 3) {
throw "NSDPlusPlus.expose called with not enough parameters";
}
var obj = {};
obj.purpose = "exposeService";
obj.localService = {};
obj.localService.uniqueId = getExposedUniqueId(type, protocol);
obj.localService.type = type;
obj.localService.protocol = protocol;
obj.localService.actionList = computeServiceDescriptionFromImplementation(serviceImplementation);
obj.localService.eventList = null;
if (serviceImplementation.EVENTS) {
obj.localService.eventList = serviceImplementation.EVENTS;
for (var i = 0; i < serviceImplementation.EVENTS.length; i++) {
eventValues[serviceImplementation.EVENTS[i]] = null;
}
}
//
// assumption for the moment:
// - max one service per atom
// - the developer has to create a NSDPlusPlus.ServiceImplementation object to implement the actions
// - the interface specification is in the manifest
//
serviceImplementation.service = obj.localService;
serviceImplementations.push(serviceImplementation);
obj.serviceImplementation = "" + (serviceImplementations.length - 1);
conn.send(JSON.stringify(obj));
return obj.localService.uniqueId;
};
NSDPlusPlus.unexpose = function (uniqueId) {
//console.log("unexpose: ", uniqueId);
var obj = {};
obj.purpose = "unexposeService";
obj.localService = {};
obj.localService.uniqueId = uniqueId;
conn.send(JSON.stringify(obj));
};
//
//
//
function computeServiceDescriptionFromImplementation(implementation) {
var result = [];
for (var f in implementation) {
try {
if (implementation.hasOwnProperty(f) && typeof implementation[f] == 'function') {
// create the action
var action = {};
action.name = f;
action.args = [];
result.push(action);
var source = implementation[f].toString();
//Coltram.logger(f + " :" + source);
// find the IN args
var beginArgs = source.indexOf('(');
var endArgs = source.indexOf(')');
// if there are in args (no () )
if (endArgs > beginArgs + 1) {
var args = source.substring(beginArgs + 1, endArgs).split(',');
for (var i = 0; i < args.length; i++) {
var arg = {};
arg.name = args[i].trim();
arg.dir = "IN";
action.args.push(arg);
}
}
// find the OUT args
var out = source.indexOf("return");
// if there is a return
if (out >= 0) {
var outbrace = source.indexOf('{', out);
var outcolon = source.indexOf(';', out);
// and if there is a {} before the ;, so there are OUT args
if (outbrace < outcolon) {
var outargs = source.substring(outbrace + 1, source.indexOf('}', outbrace + 1)).split(',');
// this is like the inside of a JSON object
for (var j = 0; j < outargs.length; j++) {
var arg1 = {}, k = outargs[j].split(':');
arg1.name = k[0].trim();
arg1.dir = "OUT";
action.args.push(arg1);
}
}
}
}
} catch (error) {
throw "error parsing service implementation at " + f + " :" + error;
}
}
return result;
}
/////////////////////////////////////////////////////
//// updateEvent ////////////////////////////////////
/////////////////////////////////////////////////////
NSDPlusPlus.updateEvent = function (eventName, eventValue) {
// propagate the value to the agent
if (eventValues[eventName] != eventValue) {
var obj = {};
obj.purpose = "updateEvent";
obj.eventName = eventName;
obj.eventValue = JSON.stringify(eventValue);
conn.send(JSON.stringify(obj));
}
};
/////////////////////////////////////////////////////
//// Bind ////////////////////////////////////////
/////////////////////////////////////////////////////
function getServiceByServiceId(serviceId) {
for (var i = 0; i < discoveredServices.length; i++) {
if (discoveredServices[i].id == serviceId) {
return discoveredServices[i];
}
}
return null;
}
//
// bind service, creating a proxy object with one function per action
//
NSDPlusPlus.bindServiceMappedArguments = function (serviceId) {
var service = getServiceByServiceId(serviceId);
if (service == null) {
NSDPlusPlus.logger("service with id " + serviceId + " not found in bindServiceMappedArguments");
return null;
}
// create proxy object
var proxy = {};
for (var i = 0; i < service.actionList.length; i++) {
proxy[service.actionList[i].name] = createMappedProxyFunction(serviceId, service.actionList[i].name);
}
// todo events
return proxy;
};
function createMappedProxyFunction(serviceId, actionName) {
return function (args, replyCallBack) {
var obj = {};
obj.purpose = "callAction2";
obj.serviceId = serviceId;
obj.actionName = actionName;
for (var i in args) {
if (args.hasOwnProperty(i) && typeof args[i] != 'string') {
args[i] = "" + args[i];
NSDPlusPlus.logger("call argument " + i + " of " + actionName + " should be a string");
}
}
obj["arguments"] = args;
obj.replyCallBack = getTokenForCallBack(replyCallBack);
var s = JSON.stringify(obj);
//NSDPlusPlus.logger(s);
conn.send(s);
};
}
var callBackTable = [];
function getTokenForCallBack(callBack) {
callBackTable.push(callBack);
return (callBackTable.length - 1) + "";
}
function getCallBackFromToken(token) {
return callBackTable[+token];
}
//
// bind service, creating a proxy object with one function per action
//
NSDPlusPlus.bindService = function (serviceId) {
var service = getServiceByServiceId(serviceId);
if (service == null) {
NSDPlusPlus.logger("service with id " + serviceId + " not found in bindService");
return null;
} //else {
//Coltram.logger("service "+serviceId);
//for (var i = 0; i < service.eventList.length; i++) {
// Coltram.logger(service.eventList[i]);
//}
//}
if (service.actionList == null) {
NSDPlusPlus.logger("trying to bind to a service with no actionList: serviceId="+serviceId);
return null;
}
// create proxy object
var proxy = {};
for (var i = 0; i < service.actionList.length; i++) {
proxy[service.actionList[i].name] = createProxyFunction(serviceId, service.actionList[i].name, service.actionList[i].args);
}
if (service.eventList) {
proxy.subscribe = function (eventList, serviceId, conn) {
return function (eventName, callback) {
if (typeof callback != 'string') {
NSDPlusPlus.logger("callback "+callback+" should be a string in subscribe");
throw "callback "+callback+" should be a string in subscribe";
}
if (eventList.indexOf(eventName) >= 0) {
var obj = {};
obj.purpose = "subscribe";
obj.serviceId = serviceId;
obj.eventName = eventName;
obj.callback = callback;
var s = JSON.stringify(obj);
//Coltram.logger(s);
conn.send(s);
} else {
NSDPlusPlus.logger("event "+eventName+" is not part of the interface of this service");
throw "event "+eventName+" is not part of the interface of this service";
}
}
}(service.eventList, serviceId, conn);
proxy.unsubscribe = function (eventList, serviceId, conn) {
return function (eventName, callback) {
if (eventList.indexOf(eventName) >= 0) {
var obj = {};
obj.purpose = "unsubscribe";
obj.serviceId = serviceId;
obj.eventName = eventName;
obj.callback = callback;
var s = JSON.stringify(obj);
//Coltram.logger(s);
conn.send(s);
} else {
NSDPlusPlus.logger("event "+eventName+" is not part of the interface of this service");
throw "event "+eventName+" is not part of the interface of this service";
}
}
}(service.eventList, serviceId, conn);
} else {
proxy.subscribe = function () {
NSDPlusPlus.logger("this service does not have events");
throw "this service does not have events";
};
proxy.unsubscribe = function () {
NSDPlusPlus.logger("this service does not have events");
throw "this service does not have events";
};
}
return proxy;
};
// version without eval
function createProxyFunction(serviceId, actionName, args) {
return function () {
var obj = {};
obj.purpose = 'callAction';
obj.serviceId = serviceId;
obj.actionName = actionName;
obj["arguments"] = {};
var j = 0;
for (var i = 0; i < args.length; i++) {
if (args[i].dir.toLowerCase() == "in") {
if (j >= arguments.length) {
NSDPlusPlus.logger("not enough arguments provided to a proxy function: " + actionName);
return;
}
obj["arguments"][args[i].name] = JSON.stringify(arguments[j++]);
}
}
// if all arguments have been consumed, no replyCallBack
if (j < arguments.length) {
if (j < arguments.length - 1) {
NSDPlusPlus.logger("too many arguments provided to a proxy function: " +
actionName + " " + (arguments.length - 1 - j));
return;
}
obj.replyCallBack = getTokenForCallBack(arguments[arguments.length - 1]);
}
var s = JSON.stringify(obj);
conn.send(s);
};
}
/////////////////////////////////////////////////////
//// Messages from Agent ////////////////////////////
/////////////////////////////////////////////////////
messageHandlers.updateEvent = function(obj) {
window[obj.callback].call(null, JSON.parse(obj.eventValue));
}
//
// message sent by the agent to inform the atom about ids and names
//
messageHandlers.initialize = function (obj) {
NSDPlusPlus.hostName = obj.hostName;
NSDPlusPlus.agentHostName = obj.agentHostName;
//
// then call the initialized callbacks
//
if (initializedCallbacks.length > 0) {
for (var i = 0; i < initializedCallbacks.length; i++) {
initializedCallbacks[i]();
}
initializedCallbacks = []; // initialized call backs are called once only
}
NSDPlusPlus.initialized = true;
};
//
// message sent by the agent getting a reply after having called an action onto a service outside
// variant with reply arguments mapped to attributes of an object
//
messageHandlers.mappedReply = function (obj) {
var fun = getCallBackFromToken(obj.callBack);
if (typeof fun == 'function') {
fun.call(this, obj);
}
else {
NSDPlusPlus.logger("reply with an unknown callback token: " + obj.callBack);
}
};
//
// message sent by the agent getting a reply to a callAction
// variant with reply arguments as individual parameters to a function
//
messageHandlers.reply = function (obj) {
var fun = getCallBackFromToken(obj.callBack);
if (typeof fun == 'function') {
var service = getServiceByServiceId(obj.serviceId);
if (service == null) {
NSDPlusPlus.logger("reply from an unknown service " + obj.serviceId + " " + obj.actionName);
return;
}
for (var i = 0; i < service.actionList.length; i++) {
if (service.actionList[i].name == obj.actionName) {
var replyArgs = [];
var al = service.actionList[i].args;
for (var j = 0; j < al.length; j++) {
// one path to here has arguments directly in obj, the other in obj.arguments
var property = obj[al[j].name];
if (property) {
replyArgs.push(property);
} else if (obj["arguments"]) {
property = obj["arguments"][al[j].name];
if (property) {
replyArgs.push(property);
}
}
}
fun.apply(obj, replyArgs);
return;
}
}
NSDPlusPlus.logger("reply with an unknown actionName " + obj.actionName + " " + obj.serviceId);
}
else {
NSDPlusPlus.logger("reply with an unknown callback token: " + obj.callBack);
}
};
//
// message sent by the agent when someone else calls an action of a service exposed by this atom
//
messageHandlers.serviceAction = function (obj) {
var impl = serviceImplementations[parseInt(obj.implementation)];
serviceActionInternal(obj, impl, obj);
};
function serviceActionInternal(obj, impl, args2) {
if (typeof impl[obj.actionName] == 'function') {
// prepare the arguments from the service action description, one arg per argument described as 'in'
var args = [];
for (var i = 0; i < impl.service.actionList.length; i++) {
var action = impl.service.actionList[i];
if (action.name == obj.actionName) {
for (var j in action.args) {
if (action.args.hasOwnProperty(j)) {
var arg = action.args[j];
if (arg.dir.toLowerCase() == "in") {
args.push(JSON.parse(args2[arg.name]));
}
}
}
}
}
var result = impl[obj.actionName].apply(obj, args);
if (typeof result != 'undefined') {
processResult(result, obj);
}
}
else {
NSDPlusPlus.logger("unimplemented action " + obj.actionName + " in locally exposed service");
}
}
//
// message sent by the agent when someone else calls an action of a bonjour service exposed by this atom
//
messageHandlers.callAction = function (obj) {
// when someone else calls an action of a service exposed by this atom
// TODO: this code assumes one service per page, so the serviceID is ignored --> allow more services and deal with serviceId
serviceActionInternal(obj, serviceImplementations[0], obj["arguments"]);
};
//
// message sent by the agent when someone else calls an action of a bonjour service exposed by this atom
//
messageHandlers.callAction2 = function (obj) {
// when someone else calls an action of a service exposed by this atom
// TODO: this code assumes one service per page, so the serviceID is ignored --> allow more services and deal with serviceId
var impl = serviceImplementations[0];
if (typeof impl[obj.actionName] == 'function') {
// prepare the arguments from the service action description, one arg per argument described as 'in'
var args = [];
args.push(obj["arguments"]);
var result = impl[obj.actionName].apply(obj, args);
if (typeof result != 'undefined') {
processResult(result, obj);
}
} else {
NSDPlusPlus.logger("unimplemented action " + obj.actionName + " in locally exposed service");
}
};
function processResult(result, obj) {
// check that all returned values are strings
for (var i in result) {
if (result.hasOwnProperty(i) && typeof result[i] != 'string') {
result[i] = "" + result[i];
NSDPlusPlus.logger("reply argument " + i + " of " + obj.actionName + " has a non-string value");
}
}
var answer = {};
answer.purpose = 'reply';
answer["arguments"] = result;
answer.callBack = obj.replyCallBack;
if (obj.hasOwnProperty("address")) {
answer.address = obj.address;
answer.replyPort = obj.replyPort;
if (obj.hasOwnProperty("originAtom")) {
answer.originAtom = obj.originAtom;
}
answer.serviceId = obj.serviceId;
answer.actionName = obj.actionName;
}
conn.send(JSON.stringify(answer));
}
//
// end internal: msg handlers
//
//
// looser version of getNetworkServices, searching for a substring of the type
// to be independent from the actual protocol
//
NSDPlusPlus.discover = function (serviceTypeFragment, callBack, errorCallBack) {
var result = new NetworkServices();
// look for services already discovered
getAlreadyDiscoveredServices1(serviceTypeFragment, callBack, errorCallBack, result);
// register a callback for future discoveries
registerSpecificServicesCallback1(serviceTypeFragment, result);
//networkServicesArray.push(result);
};
function getAlreadyDiscoveredServicesInternal1(serviceTypeFragment, result) {
for (var i = 0; i < discoveredServices.length; i++) {
var service = discoveredServices[i];
if (service.type.indexOf(serviceTypeFragment) >= 0) {
result.push(service);
result.servicesAvailable++;
}
}
}
//noinspection JSUnusedLocalSymbols
function getAlreadyDiscoveredServices1(serviceTypeFragment, callBack, errorCallBack, result) {
if (typeof serviceTypeFragment == "string") {
// single type requested
getAlreadyDiscoveredServicesInternal1(serviceTypeFragment, result);
} else {
// multiple types requested
for (var i = 0; i < serviceTypeFragment.length; i++) {
getAlreadyDiscoveredServicesInternal1(serviceTypeFragment[i], result);
}
}
callBack.call(null, result);
}
function registerSpecificServicesCallback1(serviceTypeFragment, networkServices) {
if (typeof serviceTypeFragment == "string") {
if (newServiceTypeCallback(serviceTypeFragment)) {
specificServiceDiscoveredCallbacks.push(new SpecificServiceCallBack1(serviceTypeFragment, networkServices));
}
} else {
// multiple types requested
for (var i = 0; i < serviceTypeFragment.length; i++) {
if (newServiceTypeCallback(serviceTypeFragment)) {
specificServiceDiscoveredCallbacks.push(new SpecificServiceCallBack1(serviceTypeFragment[i], networkServices));
}
}
}
}
function SpecificServiceCallBack1(serviceTypeFragment, networkServices) {
this.type = serviceTypeFragment;
this.networkServices = networkServices;
}
SpecificServiceCallBack1.prototype.compare = function (service) {
return service.type.indexOf(this.type) >= 0;
};
///////////////////////////////////////////////////////////////////////////////
////// section for Network Service Discovery //////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
var specificServiceDiscoveredCallbacks = []; //, networkServicesArray = [];
NSDPlusPlus.getNetworkServices = function (serviceType, callBack, errorCallBack) {
var discoveredCallback = null;
for (var i = 0; i < specificServiceDiscoveredCallbacks.length; i++) {
if (serviceType == specificServiceDiscoveredCallbacks[i].type) {
discoveredCallback = specificServiceDiscoveredCallbacks[i];
break;
}
}
var networkServices;
if (discoveredCallback) {
networkServices = discoveredCallback.networkServices;
} else {
networkServices = new NetworkServices();
}
// look for services already discovered
getAlreadyDiscoveredServices(serviceType, callBack, errorCallBack, networkServices);
// register a callback for future discoveries
registerSpecificServicesCallback(serviceType, networkServices);
//networkServicesArray.push(result);
};
function getAlreadyDiscoveredServicesInternal(serviceType, result) {
for (var i = 0; i < discoveredServices.length; i++) {
var service = discoveredServices[i];
if (service.type == serviceType) {
var found = false;
for (var j = 0; j < result.length; j++) {
if (result[j].id == service.id) {
found = true;
break;
}
}
if (!found) {
result.push(service);
//result.servicesAvailable++;
}
}
}
}
//noinspection JSUnusedLocalSymbols
function getAlreadyDiscoveredServices(serviceType, callBack, errorCallBack, result) {
if (typeof serviceType == "string") {
// single type requested
getAlreadyDiscoveredServicesInternal(serviceType, result);
} else {
// multiple types requested
for (var i = 0; i < serviceType.length; i++) {
getAlreadyDiscoveredServicesInternal(serviceType[i], result);
}
}
callBack.call(null, result);
}
function registerSpecificServicesCallback(serviceType, networkServices) {
if (typeof serviceType == "string") {
if (newServiceTypeCallback(serviceType)) {
specificServiceDiscoveredCallbacks.push(new SpecificServiceCallBack(serviceType, networkServices));
}
} else {
// multiple types requested
for (var i = 0; i < serviceType.length; i++) {
if (newServiceTypeCallback(serviceType[i])) {
specificServiceDiscoveredCallbacks.push(new SpecificServiceCallBack(serviceType[i], networkServices));
}
}
}
}
function newServiceTypeCallback(serviceType) {
for (var i = 0; i < specificServiceDiscoveredCallbacks.length; i++) {
if (serviceType == specificServiceDiscoveredCallbacks[i].type) {
return false;
}
}
return true;
}
function SpecificServiceCallBack(serviceType, networkServices) {
this.type = serviceType;
this.networkServices = networkServices;
}
SpecificServiceCallBack.prototype.compare = function (service) {
return this.type == service.type;
};
//NetworkServices.prototype = [];
function NetworkServices() {
var r = [];
r.servicesAvailable = 0;
r.onserviceavailable = null;
r.onserviceunavailable = null;
return r;
}
NSDPlusPlus.wrapInNetworkServices = function (service) {
var ns = new NetworkServices();
ns.push(service);
ns.servicesAvailable = 1;
return ns;
};
NSDPlusPlus.getEmptyNetworkServices = function (service) {
return new NetworkServices();
};
//noinspection JSUnusedGlobalSymbols
NetworkServices.prototype.getServiceById = function (id) {
for (var service in this) {
if (this.hasOwnProperty(service) && service.id == id) {
return service;
}
}
return null;
};
//
// internal: call all callbacks for service discovery
// obj is an array of services
//
NSDPlusPlus.callServiceDiscoveredCallbacks = function (obj) {
for (var cb = 0; cb < specificServiceDiscoveredCallbacks.length; cb++) {
for (var i = 0; i < obj.length; i++) {
var service = obj[i];
if (specificServiceDiscoveredCallbacks[cb].compare(service)) {
var networkServices = specificServiceDiscoveredCallbacks[cb].networkServices;
// we know it is a new service, otherwise it would have been filtered in the caller
networkServices.servicesAvailable++;
if (typeof networkServices.onserviceavailable == 'function') {
networkServices.onserviceavailable.call(null);
}
// if it is this service, mark as online and call onserviceonline
for (var j = 0; j < networkServices.length; j++) {
if (networkServices[j].id == service.id) {
networkServices[j].online = true;
if (typeof networkServices[j].onserviceonline == 'function') {
networkServices[j].onserviceonline.call(null);
}
}
}
}
}
}
};
//
// internal: call all callbacks for service removal
// obj is an array of services
//
NSDPlusPlus.callServiceRemovedCallbacks = function (obj) {
for (var cb = 0; cb < specificServiceDiscoveredCallbacks.length; cb++) {
for (var i = 0; i < obj.length; i++) {
var service = obj[i];
if (specificServiceDiscoveredCallbacks[cb].compare(obj[i])) {
var networkServices = specificServiceDiscoveredCallbacks[cb].networkServices;
// we know it is a removed service, otherwise it would have been filtered in the caller
networkServices.servicesAvailable--;
if (typeof networkServices.onserviceunavailable == 'function') {
networkServices.onserviceunavailable.call(null);
}
// if it is this service, mark as online=false and call onserviceoffline
for (var j = 0; j < networkServices.length; j++) {
if (networkServices[j].id == service.id) {
networkServices[j].online = false;
if (typeof networkServices[j].onserviceoffline == 'function') {
networkServices[j].onserviceoffline.call(null);
}
}
}
}
}
}
};
}());
// to maintain the original API
var NSD = {};
NSD.getNetworkServices = NSDPlusPlus.getNetworkServices;
/////////////////////////////////////////////////////
//// Events ////////////////////////////////////////
/////////////////////////////////////////////////////
(function (discoveredServices, connectedCallbacks, initializedCallbacks) {
//
// event callbacks
//
var serviceDiscoveredCallbacks = [];
var serviceRemovedCallbacks = [];
// end event callbacks
//
// add event listener (generic mechanism)
//
NSDPlusPlus.addEventListener = function (eventType, callBack) {
if (typeof callBack != 'function') {
return;
}
switch (eventType) {
case "serviceDiscovered":
serviceDiscoveredCallbacks.push(callBack);
break;
case "serviceRemoved":
serviceRemovedCallbacks.push(callBack);
break;
case "connected":
connectedCallbacks.push(callBack);
break;
case "initialized":
// special case for initialized: if the event is in the past, call the callback now
if (NSDPlusPlus.initialized) {
callBack.call();
}
else {
initializedCallbacks.push(callBack);
}
break;
default:
NSDPlusPlus.logger("unknown event " + eventType);
break;
}
};
//
// remove event listener (generic mechanism)
//
NSDPlusPlus.removeEventListener = function (eventType, callBack) {
if (typeof callBack != 'function') {
return;
}
switch (eventType) {
case "serviceDiscovered":
serviceDiscoveredCallbacks.remove(callBack);
break;
case "serviceRemoved":
serviceRemovedCallbacks.remove(callBack);
break;
case "connected":
connectedCallbacks.remove(callBack);
break;
case "initialized":
initializedCallbacks.remove(callBack);
break;
default:
NSDPlusPlus.logger("unknown event " + eventType);
break;
}
};
NSDPlusPlus.addMessageHandler("serviceDiscovered", function (obj) {
//NSDPlusPlus.logger("service discovered " + obj.services.length);
var i, newServices = [];
for (i = 0; i < obj.services.length; i++) {
if (!NSDPlusPlus.isDiscovered(obj.services[i])) {
NSDPlusPlus.addDiscoveredService(obj.services[i]);
//NSDPlusPlus.logger("D:" + obj.services[i].type + " " + obj.services[i].name);
newServices.push(obj.services[i]);
}
}
if (newServices.length > 0) {
callServiceDiscoveredCallbacks(newServices);
}
});
//
// special remove for array of objects
//
function removeSpecial(array, object) {
for (var i = 0; i < array.length; i++) {
if (array[i].id == object.id &&
array[i].name == object.name &&
array[i].type == object.type) {
array.splice(i, 1);
return;
}
}
}
NSDPlusPlus.addMessageHandler("serviceRemoved", function (obj) {
var i, removedServices = [];
for (i = 0; i < obj.services.length; i++) {
if (NSDPlusPlus.isDiscovered(obj.services[i])) {
removeSpecial(discoveredServices, obj.services[i]);
removedServices.push(obj.services[i]);
}
}
//NSDPlusPlus.logger("R:"+obj.purpose+" "+obj.name);
if (removedServices.length > 0) {
callServiceRemovedCallbacks(removedServices);
}
});
//
// internal: call all callbacks for service discovery
//
function callServiceDiscoveredCallbacks(obj) {
for (var cb in serviceDiscoveredCallbacks) {
if (serviceDiscoveredCallbacks.hasOwnProperty(cb) &&
typeof serviceDiscoveredCallbacks[cb] == 'function') {
serviceDiscoveredCallbacks[cb].call(obj);
}
}
NSDPlusPlus.callServiceDiscoveredCallbacks(obj);
}
//
// internal: call all callbacks for service removal
//
function callServiceRemovedCallbacks(obj) {
for (var cb in serviceRemovedCallbacks) {
if (serviceRemovedCallbacks.hasOwnProperty(cb) &&
typeof serviceRemovedCallbacks[cb] == 'function') {
serviceRemovedCallbacks[cb].call(obj);
}
}
NSDPlusPlus.callServiceRemovedCallbacks(obj);
}
}(NSDPlusPlus.discoveredServices, NSDPlusPlus.connectedCallbacks, NSDPlusPlus.initializedCallbacks));
// NSDPlusPlus.discoveredServices is only exposed temporarily to allow the event module
// to use it. The next line removes that exposure.
// NSDPlusPlus.discoveredServices = null;
//NSDPlusPlus.connectedCallbacks = null;
NSDPlusPlus.initializedCallbacks = null;