UNPKG

@webrtc-remote-control/vue

Version:

Thin abstraction layer above peerjs that will let you be more productive at making WebRTC data channels based apps.

679 lines (574 loc) 20.6 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) : typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) : (global = global || self, factory(global.webrtcRemoteControlVue = {}, global.Vue)); })(this, (function (exports, vue) { function createCommonjsModule(fn) { var module = { exports: {} }; return fn(module, module.exports), module.exports; } var eventemitter3 = createCommonjsModule(function (module) { var has = Object.prototype.hasOwnProperty, prefix = '~'; /** * Constructor to create a storage for our `EE` objects. * An `Events` instance is a plain object whose properties are event names. * * @constructor * @private */ function Events() {} // // We try to not inherit from `Object.prototype`. In some engines creating an // instance in this way is faster than calling `Object.create(null)` directly. // If `Object.create(null)` is not supported we prefix the event names with a // character to make sure that the built-in object properties are not // overridden or used as an attack vector. // if (Object.create) { Events.prototype = Object.create(null); // // This hack is needed because the `__proto__` property is still inherited in // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. // if (!new Events().__proto__) prefix = false; } /** * Representation of a single event listener. * * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} [once=false] Specify if the listener is a one-time listener. * @constructor * @private */ function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } /** * Add a listener for a given event. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} once Specify if the listener is a one-time listener. * @returns {EventEmitter} * @private */ function addListener(emitter, event, fn, context, once) { if (typeof fn !== 'function') { throw new TypeError('The listener must be a function'); } var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } /** * Clear event by name. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} evt The Event name. * @private */ function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events();else delete emitter._events[evt]; } /** * Minimal `EventEmitter` interface that is molded against the Node.js * `EventEmitter` interface. * * @constructor * @public */ function EventEmitter() { this._events = new Events(); this._eventsCount = 0; } /** * Return an array listing the events for which the emitter has registered * listeners. * * @returns {Array} * @public */ EventEmitter.prototype.eventNames = function eventNames() { var names = [], events, name; if (this._eventsCount === 0) return names; for (name in events = this._events) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; /** * Return the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Array} The registered listeners. * @public */ EventEmitter.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event, handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; /** * Return the number of listeners listening to a given event. * * @param {(String|Symbol)} event The event name. * @returns {Number} The number of listeners. * @public */ EventEmitter.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event, listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; /** * Calls each of the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Boolean} `true` if the event had listeners, else `false`. * @public */ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt], len = arguments.length, args, i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len - 1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length, j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; /** * Add a listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; /** * Add a one-time listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; /** * Remove the listeners of a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn Only remove the listeners that match this function. * @param {*} context Only remove the listeners that have this context. * @param {Boolean} once Only remove one-time listeners. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) { events.push(listeners[i]); } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;else clearEvent(this, evt); } return this; }; /** * Remove all listeners, or those of the specified event. * * @param {(String|Symbol)} [event] The event name. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; // // Alias methods names because people roll like that. // EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.addListener = EventEmitter.prototype.on; // // Expose the prefix. // EventEmitter.prefixed = prefix; // // Allow `EventEmitter` to be imported as module namespace. // EventEmitter.EventEmitter = EventEmitter; // // Expose the module. // { module.exports = EventEmitter; } }); function o() { return o = Object.assign || function (e) { for (var o = 1; o < arguments.length; o++) { var n = arguments[o]; for (var t in n) { Object.prototype.hasOwnProperty.call(n, t) && (e[t] = n[t]); } } return e; }, o.apply(this, arguments); } function n() { var e = "from-webrtc-remote-control"; return { isConnectionFromRemote: function isConnectionFromRemote(o) { return o.metadata === e; }, connMetadata: e }; } function t(_temp) { var _ref = _temp === void 0 ? {} : _temp, e = _ref.sessionStorageKey, t = _ref.humanErrors; var r = function (_temp2) { var _ref3 = _temp2 === void 0 ? { mapping: {}, withTechicalErrorMessage: !0 } : _temp2, e = _ref3.mapping, n = _ref3.withTechicalErrorMessage; var t = o({ "browser-incompatible": "Your browser doesn't support WebRTC features, please try with a recent browser.", disconnected: "You are disconnected and can't make any more peer connection, please reload.", network: "It seems you're experimenting some network problems.", "peer-unavailable": "The peer you were connected to seems to have lost connection, try to reload it.", "server-error": "An error occured on the signaling server. Sorry, try to come back later.", "default": function _default(e) { return "An error occured" + (e.type ? " - type: " + e.type : ""); } }, e); return function (e) { var o = t[e.type] || ("function" == typeof t["default"] ? t["default"](e) : t["default"]); return o && e.message && n ? o + " (" + e.message + ")" : o; }; }(t), _n = n(), s = _n.isConnectionFromRemote, _ref2 = function (e) { if (e === void 0) { e = "webrtc-remote-control-peer-id"; } return { getPeerId: function getPeerId() { return sessionStorage.getItem(e); }, setPeerIdToSessionStorage: function setPeerIdToSessionStorage(o) { sessionStorage.setItem(e, o); } }; }(e), i = _ref2.getPeerId, a = _ref2.setPeerIdToSessionStorage; return { humanizeError: r, isConnectionFromRemote: s, getPeerId: i, setPeerIdToSessionStorage: a }; } var r = { __proto__: null, "default": function _default(_ref4) { var o = _ref4.humanizeError, n = _ref4.isConnectionFromRemote, t = _ref4.getPeerId, r = _ref4.setPeerIdToSessionStorage; return { humanizeError: o, isConnectionFromRemote: n, getPeerId: t, bindConnection: function bindConnection(o) { return new Promise(function (t) { var s = new eventemitter3(), i = new Map(), a = { sendTo: function sendTo(e, o) { var n = i.get(e); return n ? n.send(o) : null; }, sendAll: function sendAll(e) { [].concat(i.values()).forEach(function (o) { o.send(e); }); }, on: s.on.bind(s), off: s.off.bind(s) }; o.on("open", function (e) { r(e), t(a); }), o.on("connection", function (e) { n(e) && (i.set(e.peer, e), e.on("open", function () { s.emit("remote.connect", { id: e.peer }), console.log("connections", i); }), e.on("data", function (o) { s.emit("data", { id: e.peer, from: "remote" }, o); }), e.on("close", function () { i["delete"](e.peer), s.emit("remote.disconnect", { id: e.peer }), console.log("connections", i); })); }); }); } }; }, prepareUtils: t }, s = { __proto__: null, "default": function _default(_ref5) { var o = _ref5.humanizeError, t = _ref5.getPeerId, r = _ref5.setPeerIdToSessionStorage; return { humanizeError: o, getPeerId: t, bindConnection: function bindConnection(o, t) { return new Promise(function (s) { var i = null; var a = new eventemitter3(), c = { send: function send(e) { i ? i.send(e) : console.warning("You called `send` with no connection"); }, on: a.on.bind(a), off: a.off.bind(a) }, d = function d(e) { i = null, i = function (e, o, t, r) { var _n2 = n(), s = _n2.connMetadata, i = e.connect(o, { serialization: "json", metadata: s }); return i.on("open", function () { "function" == typeof r && r(); }), i.on("data", function (e) { t.emit("data", { from: "master" }, e); }), i; }(o, t, a, e), i.on("close", function () { a.emit("remote.disconnect", { id: o.id }), d(function () { a.emit("remote.reconnect", { id: o.id }); }); }); }; o.on("open", function (e) { r(e), d(function () { return s(c); }), i.on("error", function (e) { console.log("conn.error", e); }), window.addEventListener("beforeunload", function () { i && i.disconnect && i.disconnect(); }); }); }); } }; }, prepareUtils: t }; // eslint-disable-next-line import/no-extraneous-dependencies var MyContext = Symbol("context-webrtc-remote-control"); function provideWebTCRemoteControl(init, mode, _temp) { var _ref = _temp === void 0 ? {} : _temp, masterPeerId = _ref.masterPeerId, sessionStorageKey = _ref.sessionStorageKey, humanErrors = _ref.humanErrors; var allowedMode = ["master", "remote"]; if (!allowedMode.includes(mode)) { throw new Error("Unsupported \"" + mode + "\" mode. Only " + allowedMode.map(function (a) { return "\"" + a + "\""; }).join(", ") + " accepted."); } if (mode === "master" && masterPeerId) { console.log(typeof masterPeerId); throw new Error("`masterPeerId` prop not allowed in \"master\" mode - \"" + masterPeerId + "\" was passed."); } if (mode === "remote" && !masterPeerId) { throw new Error("`masterPeerId` prop required in \"remote\" mode."); } var utils = t({ sessionStorageKey: sessionStorageKey, humanErrors: humanErrors }); var providerValue = vue.shallowRef({ peer: null, promise: null, mode: mode, masterPeerId: masterPeerId }); // expose providerValue so that it can be injected inside the hook vue.provide(MyContext, providerValue); vue.watchEffect(function (onCleanup) { console.log("Provider.watch"); providerValue.value.mode = mode; providerValue.value.humanizeError = utils.humanizeError; if (mode === "master") { providerValue.value.isConnectionFromRemote = utils.isConnectionFromRemote; } // init callback that should return a peer instance like: // `({ getPeerId }) => new Peer(getPeerId())` providerValue.value.peer = init({ humanizeError: utils.humanizeError, getPeerId: utils.getPeerId, isConnectionFromRemote: mode === "master" ? utils.isConnectionFromRemote : undefined }); providerValue.value.promise = (mode === "master" ? r : s)["default"](utils).bindConnection(providerValue.value.peer, s ? masterPeerId : undefined); // start resolving the promise as soon as possible (it will be used in `usePeer`) providerValue.value.promise.then(function (wrcApi) { console.log("Provider.then", wrcApi); }); // register cleanup onCleanup(function () { console.log("Provider.onInvalidate", providerValue.value); if (providerValue.value) { providerValue.value.peer.disconnect(); } }); }); } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function usePeer() { console.log("usePeer"); // const ready = ref(false); var context = vue.inject(MyContext); // const resolvedWrcApi = shallowRef(null); console.log("context", context); var result = vue.reactive(_extends({}, vue.unref(context), { peerReady: false, ready: false, api: null })); vue.watchEffect(function () { // run on next tick (ensure the `then` of the Provider has executed + retrieve the api from the resolve promise) Promise.resolve().then(function () { var _context$value, _context$value$promis; console.log("hooks.Promise.resolve", context); result.peerReady = true; (_context$value = context.value) == null ? void 0 : (_context$value$promis = _context$value.promise) == null ? void 0 : _context$value$promis.then(function (wrcApi) { console.log("hooks.Promise.resolve - context.promise.then", wrcApi); // resolvedWrcApi.value = wrcApi; // ready.value = true; result.ready = true; result.api = wrcApi; }); }); }); // use toRefs ? https://vuejs.org/api/reactivity-utilities.html#torefs return vue.toRefs(result); // todo - is spread necessary ? } exports.provideWebTCRemoteControl = provideWebTCRemoteControl; exports.usePeer = usePeer; })); //# sourceMappingURL=webrtc-remote-control-vue.umd.dev.js.map