(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["nf"] = factory(); else root["nf"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; var _Facade = __webpack_require__(1); var _Facade2 = _interopRequireDefault(_Facade); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } module.exports = new _Facade2.default(); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _WebChannel = __webpack_require__(2); var _WebChannel2 = _interopRequireDefault(_WebChannel); var _ServiceProvider = __webpack_require__(4); var _ServiceProvider2 = _interopRequireDefault(_ServiceProvider); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Facade = function () { function Facade() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, Facade); this.defaults = { webrtc: {} }; this.settings = Object.assign({}, this.defaults, options); } _createClass(Facade, [{ key: 'create', value: function create() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; return new _WebChannel2.default(); } }, { key: 'join', value: function join(key) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var defaults = { connector: 'WebRTCService', protocol: 'ExchangeProtocolService' }; var settings = Object.assign({}, defaults, options); var connector = _ServiceProvider2.default.get(settings.connector); var protocol = _ServiceProvider2.default.get(settings.protocol); var connectorOptions = { signaling: settings.signaling, facade: this }; return new Promise(function (resolve, reject) { connector.join(key, connectorOptions).then(function (channel) { var webChannel = new _WebChannel2.default(options); channel.webChannel = webChannel; channel.onmessage = protocol.onmessage; webChannel.channels.add(channel); webChannel.onopen = function () { resolve(webChannel); }; }, reject); }); } }, { key: 'invite', value: function invite() { // TODO } }, { key: '_onJoining', value: function _onJoining() { // TODO } }, { key: '_onLeaving', value: function _onLeaving() { // TODO } }, { key: '_onMessage', value: function _onMessage() { // TODO } }, { key: '_onPeerMessage', value: function _onPeerMessage() { // TODO } }, { key: '_onInvite', value: function _onInvite() { // TODO } }]); return Facade; }(); exports.default = Facade; /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _constants = __webpack_require__(3); var cs = _interopRequireWildcard(_constants); var _ServiceProvider = __webpack_require__(4); var _ServiceProvider2 = _interopRequireDefault(_ServiceProvider); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var WebChannel = function () { function WebChannel() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, WebChannel); this.defaults = { connector: cs.WEBRTC_SERVICE, topology: cs.FULLYCONNECTED_SERVICE, protocol: cs.EXCHANGEPROTOCOL_SERVICE }; this.settings = Object.assign({}, this.defaults, options); // Private attributes this.protocol = cs.EXCHANGEPROTOCOL_SERVICE; // Public attributes this.id; this.myID = this._generateID(); this.channels = new Set(); this.onjoining; this.onleaving; this.onmessage; } _createClass(WebChannel, [{ key: 'leave', value: function leave() {} }, { key: 'send', value: function send(data) { var channel = this; return new Promise(function (resolve, reject) { if (channel.channels.size === 0) { resolve(); } var protocol = _ServiceProvider2.default.get(channel.settings.protocol); channel.topologyService.broadcast(channel, protocol.message(cs.USER_DATA, { id: channel.myID, data: data })).then(resolve, reject); }); } }, { key: 'sendPing', value: function sendPing() { var channel = this; return new Promise(function (resolve, reject) { if (channel.channels.size === 0) { resolve(); } var protocol = _ServiceProvider2.default.get(channel.settings.protocol); channel.topologyService.broadcast(channel, protocol.message(cs.PING, { data: '' })).then(resolve, reject); }); } }, { key: 'getHistory', value: function getHistory(historyKeeperID) { var channel = this; return new Promise(function (resolve, reject) { var protocol = _ServiceProvider2.default.get(channel.settings.protocol); channel.topologyService.sendTo(historyKeeperID, channel, protocol.message(cs.GET_HISTORY, { id: channel.myID, data: '' })).then(resolve, reject); }); } }, { key: 'sendTo', value: function sendTo(id, msg) { var channel = this; return new Promise(function (resolve, reject) { var protocol = _ServiceProvider2.default.get(channel.settings.protocol); channel.topologyService.sendTo(id, channel, protocol.message(cs.USER_DATA, { id: channel.myID, data: msg })).then(resolve, reject); }); } }, { key: 'openForJoining', value: function openForJoining() { var _this = this; var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var settings = Object.assign({}, this.settings, options); var connector = _ServiceProvider2.default.get(settings.connector); return connector.open(function (channel) { // 1) New dataChannel connection established. // NEXT: add it to the network var protocol = _ServiceProvider2.default.get(_this.protocol); _this.topologyService = _ServiceProvider2.default.get(_this.settings.topology); channel.webChannel = _this; channel.onmessage = protocol.onmessage; // 2.1) Send to the new client the webChannel topology name channel.send(protocol.message(cs.JOIN_START, _this.settings.topology)); // 2.2) Ask to topology to add the new client to this webChannel _this.topologyService.addStart(channel, _this).then(function (id) { _this.topologyService.broadcast(_this, protocol.message(cs.JOIN_FINISH, id)); _this.onJoining(id); }); }, settings).then(function (data) { return data; }); } }, { key: 'closeForJoining', value: function closeForJoining() {} }, { key: 'isInviting', value: function isInviting() {} }, { key: '_generateID', value: function _generateID() { var MIN_LENGTH = 10; var DELTA_LENGTH = 10; var MASK = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var result = ''; var length = MIN_LENGTH + Math.round(Math.random() * DELTA_LENGTH); for (var i = 0; i < length; i++) { result += MASK[Math.round(Math.random() * (MASK.length - 1))]; } return result; } }, { key: 'topology', set: function set(topologyServiceName) { this.settings.topology = topologyServiceName; this.topologyService = _ServiceProvider2.default.get(topologyServiceName); }, get: function get() { return this.settigns.topology; } }]); return WebChannel; }(); exports.default = WebChannel; /***/ }, /* 3 */ /***/ function(module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); // API user's message var USER_DATA = exports.USER_DATA = 0; var GET_HISTORY = exports.GET_HISTORY = 6; // Internal messages var JOIN_START = exports.JOIN_START = 2; var JOIN_FINISH = exports.JOIN_FINISH = 4; var YOUR_NEW_ID = exports.YOUR_NEW_ID = 5; var PING = exports.PING = 7; // Internal message to a specific Service var SERVICE_DATA = exports.SERVICE_DATA = 3; var WEBRTC_SERVICE = exports.WEBRTC_SERVICE = 'WebRTCService'; var WEBSOCKET_SERVICE = exports.WEBSOCKET_SERVICE = 'WebSocketService'; var FULLYCONNECTED_SERVICE = exports.FULLYCONNECTED_SERVICE = 'FullyConnectedService'; var STAR_SERVICE = exports.STAR_SERVICE = 'StarTopologyService'; var EXCHANGEPROTOCOL_SERVICE = exports.EXCHANGEPROTOCOL_SERVICE = 'ExchangeProtocolService'; var WSPROTOCOL_SERVICE = exports.WSPROTOCOL_SERVICE = 'WebSocketProtocolService'; /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _constants = __webpack_require__(3); var cs = _interopRequireWildcard(_constants); var _FullyConnectedService = __webpack_require__(5); var _FullyConnectedService2 = _interopRequireDefault(_FullyConnectedService); var _StarTopologyService = __webpack_require__(6); var _StarTopologyService2 = _interopRequireDefault(_StarTopologyService); var _WebRTCService = __webpack_require__(7); var _WebRTCService2 = _interopRequireDefault(_WebRTCService); var _WebSocketService = __webpack_require__(8); var _WebSocketService2 = _interopRequireDefault(_WebSocketService); var _ExchangeProtocolService = __webpack_require__(9); var _ExchangeProtocolService2 = _interopRequireDefault(_ExchangeProtocolService); var _WebSocketProtocolService = __webpack_require__(10); var _WebSocketProtocolService2 = _interopRequireDefault(_WebSocketProtocolService); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var services = new Map(); var ServiceProvider = function () { function ServiceProvider() { _classCallCheck(this, ServiceProvider); } _createClass(ServiceProvider, null, [{ key: 'get', value: function get(code) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var service = undefined; switch (code) { case cs.WEBRTC_SERVICE: service = new _WebRTCService2.default(options); break; case cs.WEBSOCKET_SERVICE: service = new _WebSocketService2.default(options); break; case cs.FULLYCONNECTED_SERVICE: service = new _FullyConnectedService2.default(options); break; case cs.STAR_SERVICE: service = new _StarTopologyService2.default(options); break; case cs.EXCHANGEPROTOCOL_SERVICE: service = new _ExchangeProtocolService2.default(options); break; case cs.WSPROTOCOL_SERVICE: service = new _WebSocketProtocolService2.default(options); break; } services.set(code, service); return service; } }]); return ServiceProvider; }(); exports.default = ServiceProvider; /***/ }, /* 5 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _constants = __webpack_require__(3); var cs = _interopRequireWildcard(_constants); var _ServiceProvider = __webpack_require__(4); var _ServiceProvider2 = _interopRequireDefault(_ServiceProvider); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var FullyConnectedService = function () { function FullyConnectedService() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, FullyConnectedService); } _createClass(FullyConnectedService, [{ key: 'addStart', value: function addStart(channel, webChannel) { var _this = this; var protocol = _ServiceProvider2.default.get(cs.EXCHANGEPROTOCOL_SERVICE); return new Promise(function (resolve, reject) { channel.peerID = _this._generateID(); channel.send(protocol.message(cs.YOUR_NEW_ID, { newID: channel.peerID, myID: webChannel.myID })); if (Reflect.has(webChannel, 'aboutToJoin') && webChannel.aboutToJoin instanceof Map) { webChannel.aboutToJoin.set(channel.peerID, channel); } else { webChannel.aboutToJoin = new Map(); } if (webChannel.channels.size === 0) { webChannel.channels.add(channel); channel.onclose = function () { webChannel.onLeaving(channel.peerID); webChannel.channels.delete(channel); }; resolve(channel.peerID); } else { (function () { webChannel.successfullyConnected = new Map(); webChannel.successfullyConnected.set(channel.peerID, 0); webChannel.connectionSucceed = function (id, withId) { var counter = webChannel.successfullyConnected.get(withId); webChannel.successfullyConnected.set(withId, ++counter); if (webChannel.successfullyConnected.get(withId) === webChannel.channels.size) { _this.addFinish(webChannel, withId); resolve(withId); } }; var connector = _ServiceProvider2.default.get(cs.WEBRTC_SERVICE); webChannel.channels.forEach(function (c) { connector.connect(channel.peerID, webChannel, c.peerID); }); })(); } }); } }, { key: 'addFinish', value: function addFinish(webChannel, id) { if (id != webChannel.myID) { (function () { var channel = webChannel.aboutToJoin.get(id); webChannel.channels.add(webChannel.aboutToJoin.get(id)); channel.onclose = function () { webChannel.onLeaving(channel.peerID); webChannel.channels.delete(channel); }; //webChannel.aboutToJoin.delete(id) if (Reflect.has(webChannel, 'successfullyConnected')) { webChannel.successfullyConnected.delete(id); } })(); } else { webChannel.onopen(); } } }, { key: 'broadcast', value: function broadcast(webChannel, data) { return new Promise(function (resolve, reject) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = webChannel.channels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var c = _step.value; c.send(data); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } resolve(); }); } }, { key: 'sendTo', value: function sendTo(id, webChannel, data) { return new Promise(function (resolve, reject) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = webChannel.channels[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var c = _step2.value; if (c.peerID == id) { c.send(data); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } resolve(); }); } }, { key: 'leave', value: function leave(webChannel) { this.broadcast(webChannel); } }, { key: '_generateID', value: function _generateID() { var MIN_LENGTH = 10; var DELTA_LENGTH = 10; var MASK = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var length = MIN_LENGTH + Math.round(Math.random() * DELTA_LENGTH); var maskLastIndex = MASK.length - 1; var result = ''; for (var i = 0; i < length; i++) { result += MASK[Math.round(Math.random() * maskLastIndex)]; } return result; } }]); return FullyConnectedService; }(); exports.default = FullyConnectedService; /***/ }, /* 6 */ /***/ function(module, exports) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var StarTopologyService = function () { function StarTopologyService() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, StarTopologyService); } _createClass(StarTopologyService, [{ key: 'broadcast', value: function broadcast(webChannel, data) { return new Promise(function (resolve, reject) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = webChannel.channels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var c = _step.value; var msg = undefined; // Create the string message var date = new Date().getTime(); if (data.type === 'PING') { msg = JSON.stringify([c.seq++, 'PING', date]); } else { msg = JSON.stringify([c.seq++, data.type, webChannel.id, data.msg]); } // Store the message with his sequence number to know what message has caused the reception of an ACK // This is used in WebSocketProtocolService var srvMsg = JSON.parse(msg); srvMsg.shift(); srvMsg.unshift(webChannel.myID); srvMsg.unshift(0); webChannel.waitingAck[c.seq - 1] = { resolve: resolve, reject: reject, time: date, data: srvMsg }; // Send the message to the server c.send(msg); } // resolve(); } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }); } }, { key: 'sendTo', value: function sendTo(id, webChannel, data) { return new Promise(function (resolve, reject) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = webChannel.channels[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var c = _step2.value; var msg = JSON.stringify([c.seq++, data.type, id, data.msg]); c.send(msg); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } resolve(); }); } }]); return StarTopologyService; }(); exports.default = StarTopologyService; /***/ }, /* 7 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _constants = __webpack_require__(3); var cs = _interopRequireWildcard(_constants); var _ServiceProvider = __webpack_require__(4); var _ServiceProvider2 = _interopRequireDefault(_ServiceProvider); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var WEBRTC_DATA = 0; var CONNECT_WITH = 1; var CONNECT_WITH_SUCCEED = 2; var WebRTCService = function () { function WebRTCService() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, WebRTCService); this.NAME = this.constructor.name; this.protocol = _ServiceProvider2.default.get(cs.EXCHANGEPROTOCOL_SERVICE); this.defaults = { signaling: 'ws://localhost:8000', webRTCOptions: { iceServers: [{ urls: ['stun:23.21.150.121', 'stun:stun.l.google.com:19302'] }, { urls: 'turn:numb.viagenie.ca', credential: 'webrtcdemo', username: 'louis%40mozilla.com' }] } }; this.settings = Object.assign({}, this.defaults, options); // Declare WebRTC related global(window) constructors this.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.msRTCPeerConnection; this.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.RTCIceCandidate || window.msRTCIceCandidate; this.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription || window.msRTCSessionDescription; } _createClass(WebRTCService, [{ key: 'connect', value: function connect(newPeerID, webChannel, peerID) { webChannel.topologyService.sendTo(peerID, webChannel, this._msg(CONNECT_WITH, { key: newPeerID, intermediaryID: webChannel.myID })); } }, { key: 'open', value: function open(onchannel) { var _this = this; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var defaults = { key: this._randomKey() }; var settings = Object.assign({}, this.settings, defaults, options); return new Promise(function (resolve, reject) { var connections = []; var socket = new window.WebSocket(settings.signaling); socket.onopen = function () { socket.send(JSON.stringify({ key: settings.key })); resolve({ url: _this.settings.signaling, key: settings.key }); }; socket.onmessage = function (e) { var msg = JSON.parse(e.data); if (Reflect.has(msg, 'id') && Reflect.has(msg, 'data')) { if (Reflect.has(msg.data, 'offer')) { (function () { var connection = new _this.RTCPeerConnection(settings.webRTCOptions); connections.push(connection); connection.ondatachannel = function (e) { e.channel.onopen = function () { onchannel(e.channel); }; }; connection.onicecandidate = function (e) { if (e.candidate !== null) { var candidate = { candidate: e.candidate.candidate, sdpMLineIndex: e.candidate.sdpMLineIndex }; socket.send(JSON.stringify({ id: msg.id, data: { candidate: candidate } })); } }; var sd = Object.assign(new _this.RTCSessionDescription(), msg.data.offer); connection.setRemoteDescription(sd, function () { connection.createAnswer(function (answer) { connection.setLocalDescription(answer, function () { socket.send(JSON.stringify({ id: msg.id, data: { answer: connection.localDescription.toJSON() } })); }, function () {}); }, function () {}); }, function () {}); })(); } else if (Reflect.has(msg.data, 'candidate')) { var candidate = new _this.RTCIceCandidate(msg.data.candidate); connections[msg.id].addIceCandidate(candidate); } } }; socket.onerror = reject; }); } }, { key: 'join', value: function join(key) { var _this2 = this; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var settings = Object.assign({}, this.settings, options); return new Promise(function (resolve, reject) { var connection = undefined; var socket = new window.WebSocket(settings.signaling); socket.onopen = function () { connection = new _this2.RTCPeerConnection(settings.webRTCOptions); connection.onicecandidate = function (e) { if (e.candidate !== null) { var candidate = { candidate: e.candidate.candidate, sdpMLineIndex: e.candidate.sdpMLineIndex }; socket.send(JSON.stringify({ data: { candidate: candidate } })); } }; var dc = connection.createDataChannel(key); dc.onopen = function () { resolve(dc); }; connection.createOffer(function (offer) { connection.setLocalDescription(offer, function () { socket.send(JSON.stringify({ join: key, data: { offer: connection.localDescription.toJSON() } })); }, reject); }, reject); }; socket.onclose = function (e) { reject(e); }; socket.onmessage = function (e) { var msg = JSON.parse(e.data); if (Reflect.has(msg, 'data')) { if (Reflect.has(msg.data, 'answer')) { var sd = Object.assign(new _this2.RTCSessionDescription(), msg.data.answer); connection.setRemoteDescription(sd, function () {}, reject); } else if (Reflect.has(msg.data, 'candidate')) { var candidate = new _this2.RTCIceCandidate(msg.data.candidate); connection.addIceCandidate(candidate); } else { reject(); } } else { reject(); } }; socket.onerror = reject; }); } }, { key: '_randomKey', value: function _randomKey() { var MIN_LENGTH = 10; var DELTA_LENGTH = 10; var MASK = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var result = ''; var length = MIN_LENGTH + Math.round(Math.random() * DELTA_LENGTH); for (var i = 0; i < length; i++) { result += MASK[Math.round(Math.random() * (MASK.length - 1))]; } return result; } }, { key: '_msg', value: function _msg(code, data) { var msg = { service: this.constructor.name }; msg.data = {}; msg.data.code = code; Object.assign(msg.data, data); return this.protocol.message(cs.SERVICE_DATA, msg); } }, { key: 'onmessage', value: function onmessage(channel, msg) { var _this3 = this; var webChannel = channel.webChannel; if (!Reflect.has(webChannel, 'connections')) { webChannel.connections = new Map(); } switch (msg.code) { case WEBRTC_DATA: if (webChannel.myID === msg.recipientPeerID) { if (Reflect.has(msg, 'sdp')) { if (msg.sdp.type === 'offer') { (function () { var connection = new _this3.RTCPeerConnection(_this3.settings.webRTCOptions); webChannel.connections.set(msg.senderPeerID, connection); connection.ondatachannel = function (e) { e.channel.onopen = function () { e.channel.peerID = msg.senderPeerID; e.channel.webChannel = webChannel; e.channel.onmessage = _this3.protocol.onmessage; webChannel.channels.add(e.channel); e.channel.onclose = function () { webChannel.onLeaving(e.channel.peerID); webChannel.channels.delete(e.channel); }; }; }; connection.onicecandidate = function (e) { if (e.candidate !== null) { var candidate = { candidate: e.candidate.candidate, sdpMLineIndex: e.candidate.sdpMLineIndex }; channel.send(_this3._msg(WEBRTC_DATA, { senderPeerID: webChannel.myID, recipientPeerID: msg.senderPeerID, candidate: candidate })); } }; var sd = Object.assign(new _this3.RTCSessionDescription(), msg.sdp); connection.setRemoteDescription(sd, function () { connection.createAnswer(function (answer) { connection.setLocalDescription(answer, function () { channel.send(_this3._msg(WEBRTC_DATA, { senderPeerID: webChannel.myID, recipientPeerID: msg.senderPeerID, sdp: connection.localDescription.toJSON() })); }, function () {}); }, function () {}); }, function () {}); })(); } else if (msg.sdp.type === 'answer') { var sd = Object.assign(new this.RTCSessionDescription(), msg.sdp); webChannel.connections.get(msg.senderPeerID).setRemoteDescription(sd, function () {}, function () {}); } } else if (Reflect.has(msg, 'candidate')) { webChannel.connections.get(msg.senderPeerID).addIceCandidate(new this.RTCIceCandidate(msg.candidate)); } } else { var data = this._msg(WEBRTC_DATA, msg); if (webChannel.aboutToJoin.has(msg.recipientPeerID)) { webChannel.aboutToJoin.get(msg.recipientPeerID).send(data); } else { webChannel.topologyService.sendTo(msg.recipientPeerID, webChannel, data); } } break; case CONNECT_WITH: var connection = new this.RTCPeerConnection(this.settings.webRTCOptions); connection.onicecandidate = function (e) { if (e.candidate !== null) { var candidate = { candidate: e.candidate.candidate, sdpMLineIndex: e.candidate.sdpMLineIndex }; webChannel.topologyService.sendTo(msg.intermediaryID, webChannel, _this3._msg(WEBRTC_DATA, { senderPeerID: webChannel.myID, recipientPeerID: msg.key, candidate: candidate })); } }; var dc = connection.createDataChannel(msg.key); dc.onopen = function () { if (!Reflect.has(webChannel, 'aboutToJoin')) { webChannel.aboutToJoin = new Map(); } webChannel.aboutToJoin.set(dc.label, dc); dc.onmessage = _this3.protocol.onmessage; dc.peerID = dc.label; dc.webChannel = webChannel; webChannel.topologyService.sendTo(msg.intermediaryID, webChannel, _this3._msg(CONNECT_WITH_SUCCEED, { senderPeerID: webChannel.myID, recipientPeerID: dc.label })); }; connection.createOffer(function (offer) { connection.setLocalDescription(offer, function () { webChannel.topologyService.sendTo(msg.intermediaryID, webChannel, _this3._msg(WEBRTC_DATA, { senderPeerID: webChannel.myID, recipientPeerID: msg.key, sdp: connection.localDescription.toJSON() })); webChannel.connections.set(msg.key, connection); }, function () {}); }, function () {}); break; case CONNECT_WITH_SUCCEED: webChannel.connectionSucceed(msg.senderPeerID, msg.recipientPeerID); break; } } }]); return WebRTCService; }(); exports.default = WebRTCService; /***/ }, /* 8 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _constants = __webpack_require__(3); var cs = _interopRequireWildcard(_constants); var _ServiceProvider = __webpack_require__(4); var _ServiceProvider2 = _interopRequireDefault(_ServiceProvider); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var WebSocketService = function () { function WebSocketService() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, WebSocketService); this.NAME = this.constructor.name; this.protocol = _ServiceProvider2.default.get(cs.EXCHANGEPROTOCOL_SERVICE); this.defaults = { signaling: 'ws://localhost:9000', REQUEST_TIMEOUT: 5000 }; this.settings = Object.assign({}, this.defaults, options); } _createClass(WebSocketService, [{ key: 'join', value: function join(key) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var settings = Object.assign({}, this.settings, options); return new Promise(function (resolve, reject) { var connection = undefined; var socket = new window.WebSocket(settings.signaling); setInterval(function () { if (socket.webChannel && socket.webChannel.waitingAck) { var waitingAck = socket.webChannel.waitingAck; for (var id in waitingAck) { var req = waitingAck[id]; var now = new Date().getTime(); if (now - req.time > settings.REQUEST_TIMEOUT) { delete socket.webChannel.waitingAck[id]; req.reject({ type: 'TIMEOUT', message: 'waited ' + now - req.time + 'ms' }); } } } }, 5000); socket.seq = 1; socket.facade = options.facade || null; socket.onopen = function () { if (key && key !== '') { socket.send(JSON.stringify([socket.seq++, 'JOIN', key])); } else { socket.send(JSON.stringify([socket.seq++, 'JOIN'])); } resolve(socket); }; socket.onerror = reject; }); } }]); return WebSocketService; }(); exports.default = WebSocketService; /***/ }, /* 9 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _constants = __webpack_require__(3); var cs = _interopRequireWildcard(_constants); var _ServiceProvider = __webpack_require__(4); var _ServiceProvider2 = _interopRequireDefault(_ServiceProvider); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var ExchangeProtocolService = function () { function ExchangeProtocolService() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, ExchangeProtocolService); } _createClass(ExchangeProtocolService, [{ key: 'onmessage', value: function onmessage(e) { var msg = JSON.parse(e.data); var channel = e.currentTarget; var webChannel = channel.webChannel; switch (msg.code) { case cs.USER_DATA: webChannel.onmessage(msg.id, msg.data); break; case cs.GET_HISTORY: webChannel.onPeerMessage(msg.id, msg.code); break; case cs.SERVICE_DATA: var service = _ServiceProvider2.default.get(msg.service); service.onmessage(channel, msg.data); break; case cs.YOUR_NEW_ID: // TODO: change names webChannel.myID = msg.newID; channel.peerID = msg.myID; break; case cs.JOIN_START: // 2.1) Send to the new client the webChannel topology webChannel.topology = msg.topology; webChannel.topologyService = _ServiceProvider2.default.get(msg.topology); break; case cs.JOIN_FINISH: webChannel.topologyService.addFinish(webChannel, msg.id); if (msg.id != webChannel.myID) { // A new user has just registered webChannel.onJoining(msg.id); } else { (function () { // We're fully synced, trigger onJoining for all existing users var waitForOnJoining = function waitForOnJoining() { if (typeof webChannel.onJoining !== "function") { setTimeout(waitForOnJoining, 500); return; } var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = webChannel.channels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var c = _step.value; webChannel.onJoining(c.peerID); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }; waitForOnJoining(); })(); } break; } } }, { key: 'message', value: function message(code, data) { var msg = { code: code }; switch (code) { case cs.USER_DATA: msg.id = data.id; msg.data = data.data; break; case cs.GET_HISTORY: msg.id = data.id; break; case cs.SERVICE_DATA: msg.service = data.service; msg.data = Object.assign({}, data.data); break; case cs.YOUR_NEW_ID: msg.newID = data.newID; msg.myID = data.myID; break; case cs.JOIN_START: msg.topology = data; break; case cs.JOIN_FINISH: msg.id = data; break; } return JSON.stringify(msg); } }]); return ExchangeProtocolService; }(); exports.default = ExchangeProtocolService; /***/ }, /* 10 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _constants = __webpack_require__(3); var cs = _interopRequireWildcard(_constants); var _ServiceProvider = __webpack_require__(4); var _ServiceProvider2 = _interopRequireDefault(_ServiceProvider); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var WebSocketProtocolService = function () { function WebSocketProtocolService() { var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _classCallCheck(this, WebSocketProtocolService); } _createClass(WebSocketProtocolService, [{ key: 'onmessage', value: function onmessage(e) { var msg = JSON.parse(e.data); var socket = e.currentTarget; var webChannel = socket.webChannel; var topology = cs.STAR_SERVICE; var topologyService = _ServiceProvider2.default.get(topology); var history_keeper = webChannel.hc; if (msg[0] !== 0 && msg[1] !== 'ACK') { return; } if (msg[2] === 'IDENT' && msg[1] === '') { socket.uid = msg[3]; webChannel.myID = msg[3]; webChannel.peers = []; webChannel.waitingAck = []; webChannel.topology = topology; return; } if (msg[1] === 'PING') { msg[1] = 'PONG'; socket.send(JSON.stringify(msg)); return; } if (msg[1] === 'ACK') { var seq = msg[0]; if (webChannel.waitingAck[seq]) { var waitingAck = webChannel.waitingAck[seq]; waitingAck.resolve(); var newMsg = waitingAck.data; if (newMsg[2] === 'PING') { // PING message : set the lag var lag = new Date().getTime() - newMsg[3]; webChannel.getLag = function () { return lag; }; } delete webChannel.waitingAck[seq]; } return; } // We have received a new direct message from another user if (msg[2] === 'MSG' && msg[3] === socket.uid) { // If it comes from the history keeper, send it to the user if (msg[1] === history_keeper) { if (msg[4] === 0) { webChannel.onmessage(msg[1], msg[4]); return; } var msgHistory = JSON.parse(msg[4]); webChannel.onmessage(msgHistory[1], msgHistory[4]); } return; } if (msg[2] === 'JOIN' && (webChannel.id == null || webChannel.id === msg[3])) { if (!webChannel.id) { // New unnamed channel : get its name from the first "JOIN" message if (!window.location.hash) { var chanName = window.location.hash = msg[3]; } webChannel.id = msg[3]; } if (msg[1] === socket.uid) { // If the user catches himself registering, he is synchronized with the server webChannel.onopen(); } else { // Trigger onJoining() when another user is joining the channel // Register the user in the list of peers in the channel if (webChannel.peers.length === 0 && msg[1].length === 16) { // We've just catched the history keeper (16 characters length name) history_keeper = msg[1]; webChannel.hc = history_keeper; } var linkQuality = msg[1] === history_keeper ? 1000 : 0; var sendToPeer = function sendToPeer(data) { topologyService.sendTo(msg[1], webChannel, { type: 'MSG', msg: data }); }; var peer = { id: msg[1], connector: socket, linkQuality: linkQuality, send: sendToPeer }; if (webChannel.peers.indexOf(peer) === -1) { webChannel.peers.push(peer); } if (msg[1] !== history_keeper) { // Trigger onJoining with that peer once the function is loaded (i.e. once the channel is synced) var waitForOnJoining = function waitForOnJoining() { if (typeof webChannel.onJoining !== "function") { setTimeout(waitForOnJoining, 500); return; } webChannel.onJoining(msg[1]); }; waitForOnJoining(); } } return; } // We have received a new message in that channel from another peer if (msg[2] === 'MSG' && msg[3] === webChannel.id) { // Find the peer who sent the message and display it //TODO Use Peer instead of peer.id (msg[1]) : if (typeof webChannel.onmessage === "function") webChannel.onmessage(msg[1], msg[4]); return; } // Someone else has left the channel, remove him from the list of peers if (msg[2] === 'LEAVE' && msg[3] === webChannel.id) { //TODO Use Peer instead of peer.id (msg[1]) : if (typeof webChannel.onLeaving === "function") webChannel.onLeaving(msg[1], webChannel); return; } } }, { key: 'message', value: function message(code, data) { var type = undefined; switch (code) { case cs.USER_DATA: type = 'MSG'; break; case cs.JOIN_START: type = 'JOIN'; break; case cs.PING: type = 'PING'; break; } return { type: type, msg: data.data }; } }]); return WebSocketProtocolService; }(); exports.default = WebSocketProtocolService; /***/ } /******/ ]) }); ;