thali
Version:
537 lines (490 loc) • 27 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: NextGeneration/wifiBasedNativeMock.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: NextGeneration/wifiBasedNativeMock.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>'use strict';
var Promise = require('lie');
var ThaliWifiInfrastructure = require('ThaliWifiInfrastructure');
/** @module WifiBasedNativeMock */
/**
* @file
*
* This is a mock of {@link module:thaliMobileNative}. It is intended to
* replicate all the capabilities of {@link module:thaliMobileNative} so that we
* can build and test code intended to use {@link module:thaliMobileNative} but
* on the desktop.
*
* We are intentionally replicating the lowest layer of the stack in order to
* to be able to test on the desktop all the layers on top of it. This includes
* emulating behaviors unique to iOS and Android.
*
* For testing purposes if callNative or registerToNative do not get all the
* parameters they were expecting then a "Bad Arguments" exception MUST be
* thrown.
*/
/**
* This is the method that actually handles processing the native requests. In
* general this method just records the arguments for later use.
*
* @param {string} mobileMethodName This is the name of the method that was
* passed in on the mobile object
* @param {platformChoice} platform
* @param {Object} router
* @param {WifiBasedNativeMock} wifiBasedNativeMock
* @constructor
*/
function MobileCallInstance(mobileMethodName, platform, router,
wifiBasedNativeMock) {
this.mobileMethodName = mobileMethodName;
this.platform = platform;
this.router = router;
this.wifiBasedNativeMock = wifiBasedNativeMock;
}
MobileCallInstance.prototype.wifiBasedNativeMock = null;
MobileCallInstance.prototype.mobileMethodName = null;
MobileCallInstance.prototype.platform = null;
MobileCallInstance.prototype.router = null;
/**
* In effect this listens for SSDP:alive and SSDP:byebye messages along with
* the use of SSDP queries to find out who is around. These will be translated
* to peer availability callbacks as specified below. This code MUST meet the
* same requirements for using a unique SSDP port, syntax for requests, etc. as
* {@link module:ThaliWifiInfrastructure}.
*
* Other requirements for this method MUST match those of {@link
* external:"Mobile('startListeningForAdvertisements')".callNative} in terms of
* idempotency. This also means we MUST return "Radio Turned Off" if we are
* emulating Bluetooth as being off.
*
* @public
* @param {module:thaliMobileNative~ThaliMobileCallback} callBack
*/
MobileCallInstance.prototype.startListeningForAdvertisements =
function(callBack) {
};
/**
* This shuts down the SSDP listener/query code. It MUST otherwise behave as
* given for {@link
* external:"Mobile('stopListeningForAdvertisements')".callNative}.
*
* @public
* @param {module:thaliMobileNative~ThaliMobileCallback} callBack
*/
MobileCallInstance.prototype.stopListeningForAdvertisements =
function(callBack) {
};
/**
* This method tells the system to both start advertising and to accept
* incoming connections. In both cases we need to accept incoming connections.
* The main challenge is simulating what happens when stop is called. This is
* supposed to shut down all incoming connections. So we can't just advertise
* our 127.0.0.1 port and let the other mocks running on the same machine
* connect since stop wouldn't behave properly. To handle the stop behavior,
* that is to disconnect all incoming connections, we have to introduce a TCP
* level proxy. The reason we need a TCP proxy is that we are using direct SSL
* connections in a way that may or may not properly work through a HTTPS proxy.
* So it's simpler to just introduce the TCP proxy. We will advertise the TCP
* proxy's listener port in SSDP and when someone connects we will create a TCP
* client connection to portNumber and then pipe the two connections together.
*
* __Open Issue:__ If we directly pipe the TCP listener socket (from connect)
* and the TCP client socket (that we created) then will the system
* automatically kill the pipe if either socket is killed? We need to test this.
* If it doesn't then we just need to hook the close event and close the other
* side of the pipe.
*
* __Note:__ For now we are going to not simulate the Bluetooth handshake for
* Android. This covers the scenario where device A doesn't discover device B
* over BLE but device B discovered device A over BLE and then connected over
* Bluetooth. The handshake would create a simulated discovery event but we are
* going to assume that the SSDP discovery will arrive in a timely manner and so
* the behavior should be the same.
*
* For advertising we will use SSDP both to make SSDP:alive as well as to
* answer queries as given in {@link module:ThaliWifiInfrastructure}.
*
* For incoming connections we will, as described above, just rely on everyone
* running on 127.0.0.1.
*
* Otherwise the behavior MUST be the same as defined for (@link
* external:"Mobile('startUpdateAdvertisingAndListening')".ca
* llNative}. That includes returning the "Call Start!" error as appropriate as
* well as returning "Radio Turned Off" if we are emulating Bluetooth as being
* off.
*
* @param {number} portNumber
* @param {module:thaliMobileNative~ThaliMobileCallback} callBack
*/
MobileCallInstance.prototype.startUpdateAdvertisingAndListening =
function(portNumber, callBack) {
};
/**
* This function MUST behave like {@link module:ThaliWifiInfrastructure} and
* send a proper SSDP:byebye and then stop responding to queries or sending
* SSDP:alive messages. Otherwise it MUST act like
* (@link external:"Mobile('stopAdvertisingAndListening')".callNative}
* including terminating the TCP proxy and all of its connections to simulate
* killing all incoming connections.
*
* @param {module:thaliMobileNative~ThaliMobileCallback} callBack
*/
MobileCallInstance.prototype.stopAdvertisingAndListening =
function (callBack) {
};
/**
* All the usual restrictions on connect apply including throwing errors if
* start listening isn't active, handling consecutive calls, etc. Please see the
* details in {@link external:"Mobile('connect')".callNative}. In this case the
* mock MUST keep track of the advertised IP and port for each peerIdentifier
* and then be able to establish a TCP/IP listener on 127.0.0.1 and use a TCP
* proxy to relay any connections to the 127.0.0.1 port to the IP address and
* port that was advertised over SSDP. The point of all this redirection is to
* fully simulate the native layer so we can run tests of the Wrapper and above
* with full fidelity. This lets us do fun things like simulate turning off
* radios as well as properly enforce behaviors such as those below that let our
* local listener only accept one connection and simulating time outs on a
* single peer correctly (e.g. the other side is still available but we had no
* activity locally and so need to tear down). If setting up the outgoing TCP
* proxy is a big enough pain we could probably figure a way around it but I'm
* guessing that since we need it anyway for incoming connections it shouldn't
* be a big deal.
*
* In the case of simulating Android we just have to make sure that at any
* time we have exactly one outgoing connection to any peerIdentifier. So if we
* get a second connect for the same peerIdentifier then we have to return the
* port for the existing TCP listener we are using, even if it is connected. We
* also need the right tear down behavior so that if the local app connection to
* the local TCP listener (that will then relay to the remote peer's port) is
* torn down then we tear down the connection to the remote peer and vice versa.
*
* On iOS we need the same behavior as Android plus we have to deal with the
* MCSession problem. This means we have to look at the peerIdentifier, compare
* it to the peerIdentifier that we generated at the SSDP layer and do a lexical
* comparison. If we are lexically smaller then we have to simulate the trick
* that iOS uses where we create a MCSession but don't establish any connections
* over it. The MCSession is just used as a signaling mechanism to let the
* lexically larger peer know that the lexically smaller peer wants to connect.
* See the sections below on /ConnectToMeForMock and /IConnectedMock for
* details.
*
* ## Making requests to /ConnectToMeForMock
*
* After we receive a connect when we are simulating iOS and the requester is
* lexically smaller than the target peerIdentifier then we MUST make a GET
* request to the target peer's /ConnectToMeForMock endpoint with a query
* argument of the form "?port=x&peerIdentifier=y". The port is the port the
* current peer wishes the target peer to connect over and the peerIdentifier is
* the current peer's peerIdentifier.
*
* If we get a 400 response then we MUST return the "Connection could not be
* established" error.
*
* If we get a 200 OK then we just have to wait for a /IConnectedMock request
* to come in telling us that the remote peer has established a connection. See
* the section below on how we handle this. Note that the usual timeout rules
* apply so if the /IConnectedMock request does not come within the timeout
* period the we MUST issue a "Connection wait time out" error.
*
* We do not include IP addresses in the request or response because we are
* only running the mock amongst instances that are all hosted on the same box
* and talking over 127.0.0.1.
*
* ## Sending responses to /ConnectToMeForMock
*
* If we are not currently simulating an iOS device then we MUST return a 500
* Server Error because something really bad has happened. We do not currently
* support simulating mixed scenarios, everyone in the test run needs to be
* either simulating iOS or Android.
*
* If we are not currently listening for incoming connections then we MUST
* return a 400 Bad Request. But we MUST also log the fact that this happened
* since baring some nasty race conditions we really shouldn't get a call to
* this endpoint unless we are listening.
*
* If we are listening then we MUST issue a peerAvailabilityChanged callback
* and set the peerIdentifier to the value in the query argument, peerAvailable
* to true and pleaseConnect to true. We MUST also record the port in the query
* argument so that if we get a connect request we know what port to submit.
*
* In theory it's possible for us to get into a situation where we get one
* port for a peerIdentifier in the /ConnectToMeForMock request and a different
* port in a SSDP request. We should just publish the PeerAvailablityChanged
* event as they come in and for internal mapping of peerIdentifier to port we
* should just record whatever came in last. And yes, this can lead to fun race
* conditions which is the situation in the real world too.
*
* ## Making requests to /IConnectedMock
*
* If we are simulating iOS and if we are establishing a TCP connection to a
* remote peer then by definition we are the lexically larger peer. However the
* iOS protocol shares our peerIdentifier with the remote peer, TCP does not. To
* work around this anytime we are simulating iOS and have successfully
* established a TCP connection to a remote peer we MUST issue a GET request to
* the /IConnectedMock endpoint of the remote peer with the query string
* "?clientPort=x&serverPort=z&peerIdentifier=y". The clientPort and serverPort
* are the client port and server port values from the TCP connection that
* caused us to send this request in the first place. The peerIdentifier is our
* peerIdentifier. If we get a 400 response back then we MUST log this event as
* it really should not have happened.
*
* ## Sending response to /IConnectedMock
*
* If we are not currently simulating an iOS device then we MUST return a 500
* Server Error because something really bad has happened. We do not currently
* support simulating mixed scenarios, everyone in the test run needs to be
* either simulating iOS or Android.
*
* If we are not currently listening for incoming connections then we MUST
* return a 400 Bad Request. But we MUST also log the fact that this happened
* since baring some nasty race conditions we really shouldn't have been able to
* set up the TCP connection in the first place.
*
* Otherwise we MUST return a 200 OK.
*
* When we return a 200 OK we MUST issue a peerAvailabilityChanged callback
* with peerIdentifier set to the submitted peerIdentifier, peerAvailable set to
* true and pleaseConnect set to false. If we have an outstanding connect
* request to the specified peerIdentifier then we MUST look up the specified
* clientPort/serverPort and see if we can match it to any of the incoming
* connections to the TCP proxy. If we can then we MUST return the
* clientPort/serverPort being used by the TCP proxy as the connect response
* with listeningPort set to null and clientPort/serverPort set to the values
* the TCP proxy is using. If we cannot match the connection via the TCP proxy
* then this means that the connection might have died or been killed while this
* request to /IConnectedMock was being sent. In that case we should send bogus
* values in the connect response to simulate a situation where a peer connects
* but then the connection dies before the connect callback is returned.
*
* @param {string} peerIdentifier
* @param {module:thaliMobileNative~ConnectCallback} callback
*/
MobileCallInstance.prototype.connect = function(peerIdentifier, callback) {
};
/**
* If we aren't emulating iOS then this method has to return the "Not
* Supported" error. If we are emulating iOS then we have to kill all the TCP
* listeners we are using to handling outgoing connections and the TCP proxy we
* are using to handle incoming connections.
*
* @public
* @param {module:thaliMobileNative~ThaliMobileCallback} callback
*/
MobileCallInstance.prototype.killConnections = function (callback) {
};
/**
* Handles processing callNative requests. The actual params differ based on
* the particular Mobile method that is being called.
*/
MobileCallInstance.prototype.callNative = function () {
switch (this.mobileMethodName) {
case 'startListeningForAdvertisements':
{
return this.startListeningForAdvertisements(arguments[0]);
}
case 'stopListeningForAdvertisements':
{
return this.stopListeningForAdvertisements(arguments[0]);
}
case 'startUpdateAdvertisingAndListening':
{
return this.startUpdateAdvertisingAndListening(
arguments[0], arguments[1]);
}
case 'stopAdvertisingAndListening':
{
return this.stopAdvertisingAndListening(
arguments[0]);
}
case 'connect':
{
return this.connect(arguments[0], arguments[1]);
}
case 'killConnections':
{
return this.killConnections(arguments[0]);
}
default:
{
throw new Error('The supplied mobileName does not have a matching ' +
'callNative method: ' + this.mobileMethodName);
}
}
};
/**
* Anytime we are looking for advertising and we receive a SSDP:alive,
* SSDP:byebye or a response to one of our periodic queries we should use it to
* create a peerAvailabilityChanged call back. In practice we don't really need
* to batch these messages so we can just fire them as we get them. The
* peerIdentifier is the USN from the SSDP message, peerAvailable is true or
* false based on the SSDP response and pleaseConnect is false except for the
* situation described above for /ConnectToMeforMock.
*
* @param {module:thaliMobileNative~peerAvailabilityChangedCallback} callback
*/
MobileCallInstance.prototype.peerAvailabilityChanged = function (callback) {
};
/**
* Any time there is a call to start and stop or if Bluetooth is turned off on
* Android (which also MUST mean that we have disabled both advertising and
* discovery) then we MUST fire this event.
*
* @public
* @param {module:thaliMobileNative~discoveryAdvertisingStateUpdateNonTCPCallback} callback
*/
MobileCallInstance.prototype.discoveryAdvertisingStateUpdateNonTCP =
function (callback) {
};
/**
* At this point this event would only fire because we called toggleBluetooth
* or toggleWifi. For the moment we will treat toggleBluetooth and turning
* on/off both blueToothLowEnergy and blueTooth.
*
* __Open Issue:__ Near as I can tell both Android and iOS have a single
* Bluetooth switch that activates and de-activates Bluetooth and BLE. Note
* however that in theory it's possible to still have one available and not the
* other to a particular application because of app level permissions but that
* isn't an issue for the mock.
*
* @public
* @param {module:thaliMobileNative~networkChangedCallback} callback
*/
MobileCallInstance.prototype.networkChanged = function (callback) {
};
/**
* This is used anytime the TCP proxy for incoming connections cannot connect
* to the portNumber set in
* {@link module:WifiBasedNativeMock~MobileCallInstance.startUpdateAdvertisingAndListening}.
*
* @public
* @param {module:thaliMobileNative~incomingConnectionToPortNumberFailedCallback} callback
*/
MobileCallInstance.prototype.incomingConnectionToPortNumberFailed =
function (callback) {
};
MobileCallInstance.prototype.registerToNative = function () {
switch (this.mobileMethodName) {
case 'peerAvailabilityChanged':
{
return this.peerAvailabilityChanged(arguments[0]);
}
case 'discoveryAdvertisingStateUpdateNonTCP':
{
return this.discoveryAdvertisingStateUpdateNonTCP(arguments[0]);
}
case 'networkChanged':
{
return this.networkChanged(arguments[0]);
}
case 'incomingConnectionToPortNumberFailed':
{
return this.incomingConnectionToPortNumberFailed(arguments[0]);
}
default:
{
throw new Error('The supplied mobileName does not have a matching ' +
'registerToNative method: ' + this.mobileMethodName);
}
}
};
/**
* Enum to describe the platforms we can simulate, this mostly controls how we
* handle connect
*
* @public
* @readonly
* @enum {string}
*/
var platformChoice = {
ANDROID: 'Android',
IOS: 'iOS'
};
/**
* This simulates turning Bluetooth on and off.
*
* If we are emulating Android then we MUST start with Bluetooth and WiFi
* turned off.
*
* __Open Issue:__ I believe that JXCore will treat this as a NOOP if called
* on iOS. We need to check and emulate their behavior.
*
* @param {platformChoice} platform
* @param {ThaliWifiInfrastructure} wifiBasedNativeMock
* @returns {Function}
*/
function toggleBluetooth (platform, wifiBasedNativeMock) {
return function (setting, callback) {
return null;
};
}
/**
* If we are on Android then then is a NOOP since we don't care (although to
* be good little programmers we should still fire a network changed event). We
* won't be using Wifi for discovery or connectivity in the near future.
*
* __Open Issue:__ I believe that JXCore will treat this as a NOOP if called
* on iOS. We need to check and emulate their behavior.
*
* @param {platformChoice} platform
* @param {ThaliWifiInfrastructure} wifiBasedNativeMock
* @returns {Function}
*/
function toggleWiFi(platform, wifiBasedNativeMock) {
return function (setting, callback) {
return null;
};
}
/**
* To use this mock save the current global object Mobile (if it exists) and
* replace it with this object. In general this object won't exist on the
* desktop.
*
* If we are simulating iOS then we MUST add the /ConnectToMeForMock and
* /IConnectedMock endpoints as described above to the router object.
*
* @public
* @constructor
* @param {platformChoice} platform
* @param {Object} router This is the express router being used up in the
* stack. We need it here so we can add a router to simulate the iOS case where
* we need to let the other peer know we want a connection.
*/
function WifiBasedNativeMock(platform, router) {
var thaliWifiInfrastructure = new ThaliWifiInfrastructure();
var mobileHandler = function (mobileMethodName) {
return new MobileCallInstance(mobileMethodName, platform, router,
thaliWifiInfrastructure);
};
mobileHandler.toggleBluetooth = toggleBluetooth(thaliWifiInfrastructure);
mobileHandler.toggleWiFi = toggleWiFi(thaliWifiInfrastructure);
return mobileHandler;
}
module.exports = WifiBasedNativeMock;
</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-TCPServersManager.html">TCPServersManager</a></li><li><a href="module-thaliMobile.html">thaliMobile</a></li><li><a href="module-thaliMobileNative.html">thaliMobileNative</a></li><li><a href="module-thaliMobileNativeWrapper.html">thaliMobileNativeWrapper</a></li><li><a href="module-thaliNotificationAction.html">thaliNotificationAction</a></li><li><a href="module-thaliNotificationBeacons.html">thaliNotificationBeacons</a></li><li><a href="module-thaliNotificationClient.html">thaliNotificationClient</a></li><li><a href="module-thaliNotificationServer.html">thaliNotificationServer</a></li><li><a href="module-thaliPeerAction.html">thaliPeerAction</a></li><li><a href="module-thaliPeerDictionary.html">thaliPeerDictionary</a></li><li><a href="module-thaliPeerPoolInterface.html">thaliPeerPoolInterface</a></li><li><a href="module-ThaliWifiInfrastructure.html">ThaliWifiInfrastructure</a></li><li><a href="module-WifiBasedNativeMock.html">WifiBasedNativeMock</a></li></ul><h3>Externals</h3><ul><li><a href="external-_Mobile(_connect_)_.html">Mobile('connect')</a></li><li><a href="external-_Mobile(_discoveryAdvertisingStateUpdateNonTCP_)_.html">Mobile('discoveryAdvertisingStateUpdateNonTCP')</a></li><li><a href="external-_Mobile(_incomingConnectionToPortNumberFailed_)_.html">Mobile('incomingConnectionToPortNumberFailed')</a></li><li><a href="external-_Mobile(_killConnections_)_.html">Mobile('killConnections')</a></li><li><a href="external-_Mobile(_networkChanged_)_.html">Mobile('networkChanged')</a></li><li><a href="external-_Mobile(_peerAvailabilityChanged_)_.html">Mobile('peerAvailabilityChanged')</a></li><li><a href="external-_Mobile(_startListeningForAdvertisements_)_.html">Mobile('startListeningForAdvertisements')</a></li><li><a href="external-_Mobile(_startUpdateAdvertisingAndListening_)_.html">Mobile('startUpdateAdvertisingAndListening')</a></li><li><a href="external-_Mobile(_stopAdvertisingAndListening_)_.html">Mobile('stopAdvertisingAndListening')</a></li><li><a href="external-_Mobile(_stopListeningForAdvertisements_)_.html">Mobile('stopListeningForAdvertisements')</a></li></ul><h3>Classes</h3><ul><li><a href="ConnectionTable.html">ConnectionTable</a></li><li><a href="module-TCPServersManager-TCPServersManager.html">TCPServersManager</a></li><li><a href="module-thaliNotificationAction-NotificationAction.html">NotificationAction</a></li><li><a href="module-thaliNotificationBeacons-ParseBeaconsResponse.html">ParseBeaconsResponse</a></li><li><a href="module-thaliNotificationClient-ThaliNotificationClient.html">ThaliNotificationClient</a></li><li><a href="module-thaliNotificationServer-ThaliNotificationServer.html">ThaliNotificationServer</a></li><li><a href="module-thaliPeerAction-PeerAction.html">PeerAction</a></li><li><a href="module-thaliPeerDictionary-NotificationPeerDictionaryEntry.html">NotificationPeerDictionaryEntry</a></li><li><a href="module-thaliPeerDictionary-PeerConnectionInformation.html">PeerConnectionInformation</a></li><li><a href="module-thaliPeerDictionary-PeerDictionary.html">PeerDictionary</a></li><li><a href="module-thaliPeerPoolInterface-ThaliPeerPoolInterface.html">ThaliPeerPoolInterface</a></li><li><a href="module-ThaliWifiInfrastructure-ThaliWifiInfrastructure.html">ThaliWifiInfrastructure</a></li><li><a href="module-WifiBasedNativeMock-MobileCallInstance.html">MobileCallInstance</a></li><li><a href="module-WifiBasedNativeMock-WifiBasedNativeMock.html">WifiBasedNativeMock</a></li></ul><h3>Events</h3><ul><li><a href="module-thaliMobileNativeWrapper.html#~event:discoveryAdvertisingStateUpdateNonTCPEvent">discoveryAdvertisingStateUpdateNonTCPEvent</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:discoveryAdvertisingStateUpdateWifiEvent">discoveryAdvertisingStateUpdateWifiEvent</a></li><li><a href="module-TCPServersManager.html#~event:failedConnection">failedConnection</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:incomingConnectionToPortNumberFailed">incomingConnectionToPortNumberFailed</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:networkChangedNonTCP">networkChangedNonTCP</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:networkChangedWifi">networkChangedWifi</a></li><li><a href="module-thaliMobileNativeWrapper.html#~event:nonTCPPeerAvailabilityChangedEvent">nonTCPPeerAvailabilityChangedEvent</a></li><li><a href="module-TCPServersManager.html#~event:routerPortConnectionFailed">routerPortConnectionFailed</a></li><li><a href="module-ThaliWifiInfrastructure.html#~event:wifiPeerAvailabilityChanged">wifiPeerAvailabilityChanged</a></li><li><a href="module-thaliMobile.html#.event:event:discoveryAdvertisingStateUpdate">discoveryAdvertisingStateUpdate</a></li><li><a href="module-thaliMobile.html#.event:event:networkChanged">networkChanged</a></li><li><a href="module-thaliMobile.html#.event:event:peerAvailabilityChanged">peerAvailabilityChanged</a></li><li><a href="module-thaliNotificationAction-NotificationAction.html#.event:event:Resolved">Resolved</a></li><li><a href="module-thaliNotificationClient.html#.event:event:peerAdvertisesDataForUs">peerAdvertisesDataForUs</a></li></ul><h3>Global</h3><ul><li><a href="global.html#getPKCS12Content">getPKCS12Content</a></li><li><a href="global.html#getPublicKeyHash">getPublicKeyHash</a></li><li><a href="global.html#stopThaliReplicationManager">stopThaliReplicationManager</a></li><li><a href="global.html#ThaliEmitter">ThaliEmitter</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Mon Jan 18 2016 11:19:31 GMT+0200 (EET)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>