/*! * Buttplug JS Source Code File - Visit https://buttplug.io for more info about * the project. Licensed under the BSD 3-Clause license. See LICENSE file in the * project root for full license information. * * @copyright Copyright (c) Nonpolynomial Labs LLC. All rights reserved. */ 'use strict'; import { EventEmitter } from 'eventemitter3'; import { ButtplugMessage } from '../core/Messages'; export class ButtplugBrowserWebsocketConnector extends EventEmitter { protected _ws: WebSocket | undefined; protected _websocketConstructor: typeof WebSocket | null = null; public constructor(private _url: string) { super(); } public get Connected(): boolean { return this._ws !== undefined; } public connect = async (): Promise => { return new Promise((resolve, reject) => { const ws = new (this._websocketConstructor ?? WebSocket)(this._url); const onErrorCallback = (event: Event) => {reject(event)} const onCloseCallback = (event: CloseEvent) => reject(event.reason) ws.addEventListener('open', async () => { this._ws = ws; try { await this.initialize(); this._ws.addEventListener('message', (msg) => { this.parseIncomingMessage(msg); }); this._ws.removeEventListener('close', onCloseCallback); this._ws.removeEventListener('error', onErrorCallback); this._ws.addEventListener('close', this.disconnect); resolve(); } catch (e) { reject(e); } }); // In websockets, our error rarely tells us much, as for security reasons // browsers usually only throw Error Code 1006. It's up to those using this // library to state what the problem might be. ws.addEventListener('error', onErrorCallback) ws.addEventListener('close', onCloseCallback); }); }; public disconnect = async (): Promise => { if (!this.Connected) { return; } this._ws!.close(); this._ws = undefined; this.emit('disconnect'); }; public sendMessage(msg: ButtplugMessage) { if (!this.Connected) { throw new Error('ButtplugBrowserWebsocketConnector not connected'); } this._ws!.send('[' + JSON.stringify(msg) + ']'); } public initialize = async (): Promise => { return Promise.resolve(); }; protected parseIncomingMessage(event: MessageEvent) { if (typeof event.data === 'string') { const msgs: ButtplugMessage[] = JSON.parse(event.data); this.emit('message', msgs); } else if (event.data instanceof Blob) { // No-op, we only use text message types. } } protected onReaderLoad(event: Event) { const msgs: ButtplugMessage[] = JSON.parse((event.target as FileReader).result as string); this.emit('message', msgs); } }