/* socket.js, a simple wrapper for a YWOT websocket connection */ const ws = require('ws') const EventEmitter = require('events'); class retryws extends EventEmitter{ // a wrapper on ws that retries on failure constructor(addr) { let sock; this.sockdown = true; this.send = (message) => { if (sockdown){ throw "Socket is down!"; } sock.send(message); } function sockinit(){ // addr is not expected to change, so not handed as input; similarly, sock is not passed in a chain because behavior is expected to be serial (either initializing or retrying) sock = new ws(addr); sock.on('open', () => { this.emit('open'); sockdown = false; }); sock.on('close', (err) => { setTimeout(sockinit,1000); sockdown = true; this.emit('close'); }); sock.on('error', (err) => { sock.close(); }); sock.on('message', (message) => { this.emit('message',message); }); } } } class Socket extends retryws { constructor(world='') { // Takes the name of the world, which can be an empty string. let loc = (name = '') ? '' : `/${name}`; let addr = `wss://www.yourworldoftext.com/${loc}ws`; super(addr); this.on('message', (msg)=>{ msg = JSON.parse(msg); switch (message.kind){ case 'write': this.emit('write', message.accepted, message.rejected); break; // A confirmation message for writes (accepted/rejected updates) case 'cursor': this.emit('cursor', message.positions, message.sender); break; // Must be used for self-identification case 'tileUpdate': this.emit('tileUpdate', message.sender, message.source, message.tiles); break; // Any change by another user or possibly oneself case 'fetch': this.emit('fetch', message.tiles); break; // The response to a fetch request } }); this.fetch = function(coords){ //coords is a list of quadruplets, each a min/max pair of y/x coordinate pairs which describes at most 1000 tiles //Unchecked for speed for (var i=0; i 200){ throw "Too many characters to write"; } for (var i=0; i