summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/renderers/256_colors.js23
-rw-r--r--lib/renderers/ansi.js17
-rw-r--r--lib/renderers/fake_color.js12
-rw-r--r--lib/renderers/true_color.js14
-rw-r--r--lib/screens/circle.js38
-rw-r--r--lib/screens/gradients.js22
-rw-r--r--lib/screens/mirrors.js38
-rw-r--r--lib/screens/random.js22
-rw-r--r--lib/screens/sprinkles.js34
-rw-r--r--lib/tomato_sauce.js162
-rw-r--r--lib/util.js62
11 files changed, 287 insertions, 157 deletions
diff --git a/lib/renderers/256_colors.js b/lib/renderers/256_colors.js
index e8e6e37..f6e57d3 100644
--- a/lib/renderers/256_colors.js
+++ b/lib/renderers/256_colors.js
@@ -1,16 +1,19 @@
'use strict';
-// Returns a 256 color, see
-// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
-// under 256 colors for more info.
-const TwoFiftySixColors = function (red, blue, green) {
- let redValue = Math.round(red * 5 / 255);
- let blueValue = Math.round(blue * 5 / 255);
- let greenValue = Math.round(green * 5 / 255);
+/**
+ * Returns a 256 color, see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
+ * for more info.
+ *
+ * @function 256ColorsRenderer
+ * @implements IRenderer
+ */
+module.exports = function (red, blue, green) {
- let colorNumber = 16 + 36 * redValue + 6 * greenValue + blueValue;
+ const redValue = Math.round(red * 5 / 255);
+ const blueValue = Math.round(blue * 5 / 255);
+ const greenValue = Math.round(green * 5 / 255);
+
+ const colorNumber = 16 + 36 * redValue + 6 * greenValue + blueValue;
return `\x1B[48;5;${colorNumber}m`;
};
-
-module.exports = TwoFiftySixColors;
diff --git a/lib/renderers/ansi.js b/lib/renderers/ansi.js
index c3975da..b3a461d 100644
--- a/lib/renderers/ansi.js
+++ b/lib/renderers/ansi.js
@@ -1,13 +1,16 @@
'use strict';
-// Returns a basic ANSI color. See the color table in:
-// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
-const ANSI = function (red, blue, green) {
- let colorOffset = Math.round((red + blue + green) * 7 / (255 * 3));
+/**
+ * Returns a basic ansi color, see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
+ * for more info.
+ *
+ * @function ANSIRenderer
+ * @implements IRenderer
+ */
+module.exports = function (red, blue, green) {
- let colorNumber = 40 + colorOffset;
+ const colorOffset = Math.round((red + blue + green) * 7 / (255 * 3));
+ const colorNumber = 40 + colorOffset;
return `\x1B[${colorNumber}m`;
};
-
-module.exports = ANSI;
diff --git a/lib/renderers/fake_color.js b/lib/renderers/fake_color.js
index b82bb45..b03309c 100644
--- a/lib/renderers/fake_color.js
+++ b/lib/renderers/fake_color.js
@@ -1,8 +1,12 @@
'use strict';
-// Sends malformed ANSI 24-bit color strings
-const FakeColor = function (red, blue, green) {
+/**
+ * Returns a malformed 24-bit ansi color
+ *
+ * @function FakeColorRenderer
+ * @implements IRenderer
+ */
+module.exports = function (red, blue, green) {
+
return `\x1B[28;2;${Math.round(red)};${Math.round(green)};${Math.round(blue)}m`;
};
-
-module.exports = FakeColor;
diff --git a/lib/renderers/true_color.js b/lib/renderers/true_color.js
index 5f7725f..811d186 100644
--- a/lib/renderers/true_color.js
+++ b/lib/renderers/true_color.js
@@ -4,8 +4,16 @@
// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
// and look for 24-bit colors. Only looks good in supported terminals,
// otherwise looks like FakeColor.
-const TrueColor = function (red, blue, green) {
+/**
+ * Returns an ANSI code for 24-bit True Color, see
+ * https://en.wikipedia.org/wiki/ANSI_escape_code#Colors and look for
+ * 24-bit colors for more info. Only looks good in supported terminals,
+ * otherwise looks like FakeColor
+ *
+ * @function TrueColorRenderer
+ * @implements IRenderer
+ */
+module.exports = function (red, blue, green) {
+
return `\x1B[48;2;${Math.round(red)};${Math.round(green)};${Math.round(blue)}m`;
};
-
-module.exports = TrueColor;
diff --git a/lib/screens/circle.js b/lib/screens/circle.js
index d8a4d10..720e250 100644
--- a/lib/screens/circle.js
+++ b/lib/screens/circle.js
@@ -1,26 +1,32 @@
'use strict';
-// Draws concentric circles. Each ring has its own color.
-const Circle = function (modulation, width, height, renderer) {
- let response = [];
+/**
+ * Draws concentric circles. Each ring has its own color.
+ *
+ * @function CircleScreen
+ * @implements IScreen
+ */
+module.exports = function (modulation, width, height, renderer) {
- let circles = width > height ? height : width;
+ const response = [];
- for (let i = 0; i < circles; i++) {
- let centerX = Math.round(width / 2) + 1;
- let centerY = Math.round(height / 2) + 1;
+ const circles = width > height ? height : width;
- let red = Math.floor(Math.random() * 255);
- let blue = Math.floor(Math.random() * 255);
- let green = Math.floor(Math.random() * 255);
+ for (let i = 0; i < circles; ++i) {
+ const centerX = Math.round(width / 2) + 1;
+ const centerY = Math.round(height / 2) + 1;
- for (let j = 0; j < 180; j++) {
- let angle = 2 * j * (Math.PI / 180);
- let x = Math.round(centerX + Math.sin(angle) * i);
- let y = Math.round(centerY + Math.cos(angle) * i);
+ const red = Math.floor(Math.random() * 255);
+ const blue = Math.floor(Math.random() * 255);
+ const green = Math.floor(Math.random() * 255);
+
+ for (let j = 0; j < 180; ++j) {
+ const angle = 2 * j * (Math.PI / 180);
+ const x = Math.round(centerX + Math.sin(angle) * i);
+ const y = Math.round(centerY + Math.cos(angle) * i);
if (x <= width && x > 0 && y <= height && y > 0) {
- let position = `\x1B[${y};${x}H`; // Move cursor to y,x (CSI y;x H)
+ const position = `\x1B[${y};${x}H`; // Move cursor to y,x (CSI y;x H)
response += `${position}${renderer(red, blue, green)} `;
}
}
@@ -28,5 +34,3 @@ const Circle = function (modulation, width, height, renderer) {
return response;
};
-
-module.exports = Circle;
diff --git a/lib/screens/gradients.js b/lib/screens/gradients.js
index 5e63d5b..2d4a2f0 100644
--- a/lib/screens/gradients.js
+++ b/lib/screens/gradients.js
@@ -1,14 +1,20 @@
'use strict';
-// Draws moving gradient boxes.
-const Gradients = function (modulation, width, height, renderer) {
+/**
+ * Draws moving gradient boxes
+ *
+ * @function GradientsScreen
+ * @implements IScreen
+ */
+module.exports = function (modulation, width, height, renderer) {
+
let response = '';
- for (let i = 0; i < height; i++) {
- for (let j = 0; j < width; j++) {
- let red = ((modulation + i) * 255 / height) % 255;
- let blue = ((modulation + j) * 255 / width) % 255;
- let green = ((modulation + i * j) * 255 / (width * height)) % 255;
+ for (let i = 0; i < height; ++i) {
+ for (let j = 0; j < width; ++j) {
+ const red = ((modulation + i) * 255 / height) % 255;
+ const blue = ((modulation + j) * 255 / width) % 255;
+ const green = ((modulation + i * j) * 255 / (width * height)) % 255;
response = response + renderer(red, blue, green);
response = response + ' ';
@@ -21,5 +27,3 @@ const Gradients = function (modulation, width, height, renderer) {
return response;
};
-
-module.exports = Gradients;
diff --git a/lib/screens/mirrors.js b/lib/screens/mirrors.js
index d683fb6..e842217 100644
--- a/lib/screens/mirrors.js
+++ b/lib/screens/mirrors.js
@@ -1,30 +1,36 @@
'use strict';
-// Draws small moving gradient boxes and repeats them.
-const Mirrors = function (modulation, width, height, renderer) {
- let response = [];
+/**
+ * Draws small moving gradient boxes and repeats them.
+ *
+ * @function MirrorsScreen
+ * @implements IScreen
+ */
+module.exports = function (modulation, width, height, renderer) {
- let scale = 2 + Math.round(Math.random() * 4);
- let scaledHeight = Math.floor(height / scale);
- let scaledWidth = Math.floor(width / scale);
+ const response = [];
- for (let i = 0; i < scaledHeight; i++) {
- let row = [];
- for (let j = 0; j < scaledWidth; j++) {
- let red = ((modulation + i) * 255 / height) % 255;
- let blue = ((modulation + j) * 255 / width) % 255;
- let green = ((modulation + i * j) * 255 / (width * height)) % 255;
+ const scale = 2 + Math.round(Math.random() * 4);
+ const scaledHeight = Math.floor(height / scale);
+ const scaledWidth = Math.floor(width / scale);
- let cell = [renderer(red, blue, green), ' '];
+ for (let i = 0; i < scaledHeight; ++i) {
+ const row = [];
+ for (let j = 0; j < scaledWidth; ++j) {
+ const red = ((modulation + i) * 255 / height) % 255;
+ const blue = ((modulation + j) * 255 / width) % 255;
+ const green = ((modulation + i * j) * 255 / (width * height)) % 255;
+
+ const cell = [renderer(red, blue, green), ' '];
row.push(cell.join(''));
}
let rowText = '';
- for (let j = 0; j < scale; j++) {
+ for (let j = 0; j < scale; ++j) {
rowText += row.reverse().join('');
}
- for (let j = 1; j < scale; j++) {
+ for (let j = 1; j < scale; ++j) {
response[j * i] = rowText;
response[(height - 1 - (j * i))] = rowText;
}
@@ -32,5 +38,3 @@ const Mirrors = function (modulation, width, height, renderer) {
return response.join('\n');
};
-
-module.exports = Mirrors;
diff --git a/lib/screens/random.js b/lib/screens/random.js
index 6ac706f..0cacea7 100644
--- a/lib/screens/random.js
+++ b/lib/screens/random.js
@@ -1,14 +1,20 @@
'use strict';
-// random colors
-const Random = function (modulation, width, height, renderer) {
+/**
+ * Draws random colors
+ *
+ * @function RandomScreen
+ * @implements IScreen
+ */
+module.exports = function (modulation, width, height, renderer) {
+
let response = '';
- for (let i = 0; i < height; i++) {
- for (let j = 0; j < width; j++) {
- let red = Math.floor(Math.random() * 255);
- let blue = Math.floor(Math.random() * 255);
- let green = Math.floor(Math.random() * 255);
+ for (let i = 0; i < height; ++i) {
+ for (let j = 0; j < width; ++j) {
+ const red = Math.floor(Math.random() * 255);
+ const blue = Math.floor(Math.random() * 255);
+ const green = Math.floor(Math.random() * 255);
response = response + renderer(red, blue, green);
response = response + ' ';
@@ -21,5 +27,3 @@ const Random = function (modulation, width, height, renderer) {
return response;
};
-
-module.exports = Random;
diff --git a/lib/screens/sprinkles.js b/lib/screens/sprinkles.js
index 446f46e..f12200e 100644
--- a/lib/screens/sprinkles.js
+++ b/lib/screens/sprinkles.js
@@ -1,28 +1,32 @@
'use strict';
-// Places random sprinkles in the screen each frame. Same color per
-// frame.
-const Sprinkles = function (modulation, width, height, renderer) {
+/**
+ * Draws random sprinkles in the screen each frame. Same color per
+ * frame.
+ *
+ * @function SprinklesScreen
+ * @implements IScreen
+ */
+module.exports = function (modulation, width, height, renderer) {
+
let response = '';
- let maxSprinkleCount = (width * height) / 2;
- let minSprinkleCount = (width * height) / 8;
- let sprinkleCount = Math.round(Math.random() * (maxSprinkleCount - minSprinkleCount)) + minSprinkleCount;
+ const maxSprinkleCount = (width * height) / 2;
+ const minSprinkleCount = (width * height) / 8;
+ const sprinkleCount = Math.round(Math.random() * (maxSprinkleCount - minSprinkleCount)) + minSprinkleCount;
- let red = Math.floor(Math.random() * 255);
- let blue = Math.floor(Math.random() * 255);
- let green = Math.floor(Math.random() * 255);
+ const red = Math.floor(Math.random() * 255);
+ const blue = Math.floor(Math.random() * 255);
+ const green = Math.floor(Math.random() * 255);
- for (let i = 0; i < sprinkleCount; i++) {
- let x = Math.round(Math.random() * (width - 1)) + 1;
- let y = Math.round(Math.random() * (height - 1)) + 1;
+ for (let i = 0; i < sprinkleCount; ++i) {
+ const x = Math.round(Math.random() * (width - 1)) + 1;
+ const y = Math.round(Math.random() * (height - 1)) + 1;
- let position = `\x1B[${y};${x}H`; // Move cursor to y,x (CSI y;x H)
+ const position = `\x1B[${y};${x}H`; // Move cursor to y,x (CSI y;x H)
response += `${position}${renderer(red, blue, green)} `;
}
return response;
};
-
-module.exports = Sprinkles;
diff --git a/lib/tomato_sauce.js b/lib/tomato_sauce.js
index 0ff087a..f3017ea 100644
--- a/lib/tomato_sauce.js
+++ b/lib/tomato_sauce.js
@@ -5,89 +5,150 @@ const EventEmitter = require('events');
const Util = require('./util');
-// Interpret as Command Sequences.
-const kEscape = new Buffer([0xFF, 0xF4, 0XFF, 0xFD, 0x06]); // IAC IP IAC DO TIMING_MARK
-const kNAWSRequest = new Buffer([0xFF, 0xFD, 0X1F]); // IAC DO NAWS
-const kNAWSResponse = new Buffer([0xFF, 0xFB, 0X1F, 0xFF, 0xFA, 0X1F]); // IAC WILL NAWS IAC SB NAWS
+const internals = {
+ // Interpret as Command Sequences.
+ kEscape: new Buffer([0xFF, 0xF4, 0XFF, 0xFD, 0x06]), // IAC IP IAC DO TIMING_MARK
+ kNAWSRequest: new Buffer([0xFF, 0xFD, 0X1F]), // IAC DO NAWS
+ kNAWSResponse: new Buffer([0xFF, 0xFB, 0X1F, 0xFF, 0xFA, 0X1F]) // IAC WILL NAWS IAC SB NAWS
+};
+
+/**
+ * A function that represents a screen, it is called frequently and should
+ * return a string consisting of commands to run.
+ *
+ * @interface IScreen
+ * @type function
+ * @param {Number} modulation A number between 0 and 255 representing the current
+ * step of the modulation
+ * @param {Number} width The width of the screen
+ * @param {Number} height The height of the screen
+ * @param {IRenderer} renderer The renderer used to colorize the scfeen
+ * @return {String} The commands used to render the screen elements
+ */
+
+/**
+ * A function that represents a renderer, it should take in a color in RGB and
+ * return a string with the appropriate code to colorize the terminal.
+ *
+ * @interface IRenderer
+ * @type function
+ * @param {Number} red The red component of the color between 0 and 255
+ * @param {Number} green The green component of the color between 0 and 255
+ * @param {Number} blue The green component of the color between 0 and 255
+ * @return {String} The commands used to colorize the terminal
+ */
-// Main tomato sauce class. Properties:
-// * screens <TomatoSauce.IScreen[]>
-// * renderers <TomatoSauce.IRenderer[]>
-// * port <int>
-// * frequency <int>
-// * modulation <int>
-//
-// The main entry point is the #run() function.
-//
-// It emits a listening event that contains the server information on
-// the `server` key inside the `data` property of the event.
-//
-// It also emits an error event that contains the error information on
-// the `error` key inside the `data` property of the event.
+/**
+ * The main application for tomato sauce. Listens for connections and serves
+ * random combinations of screens and renderers
+ *
+ * The main entry point is the `#run()` function.
+ *
+ * It emits a listening event that contains the server information on
+ * the `server` key inside the `data` property of the event.
+ *
+ * It also emits an error event that contains the error information on
+ * the `error` key inside the `data` property of the event.
+ *
+ * @class TomatoSauce
+ * @extends EventEmitter
+ *
+ * @param {object} config the configuration object used to extend the properties.
+ *
+ * @property {Array<IScreen>} screens an array of screens available to serve
+ * @property {Array<IRenderer>} renderers an array of renderers available to colorize
+ * @property {Number} [port=9999] the port to listen on
+ * @property {Number} [frequency=333] how often to update the screen
+ * @property {Number} [modulation=5] number between 0-255 depicting current modulation step
+ */
const TomatoSauce = class TomatoSauce extends EventEmitter {
- constructor (config) {
+ constructor(config = {}) {
+
super();
this.screens = [];
this.renderers = [];
- Object.assign(this, config || {});
+ // Defaults.
+ this.port = 9999;
+ this.frequency = 333;
+ this.modulation = 5;
+
+ Object.assign(this, config);
}
- // Here's where things get started.
- run () {
+ /**
+ * Main entry point, initializes the server and binds events for connections
+ *
+ * @function run
+ * @instance
+ * @memberof TomatoSauce
+ */
+ run() {
+
this._startServer();
this._bindEvents();
}
// Creates a socket, server based on the configuration. Emits the
// listening event when ready.
- _startServer () {
+
+ _startServer() {
+
const server = Net.createServer();
this.server = server;
- server.listen(this.port, function () {
+ server.listen(this.port, () => {
+
this.emit('listening', {
data: {
- server: server
+ server
}
});
- }.bind(this));
+ });
}
- _bindEvents () {
+ // Binds the connection and error events
+
+ _bindEvents() {
+
// Send the error event all the way up.
- this.server.on('error', function (err) {
+ this.server.on('error', (error) => {
+
this.emit('error', {
data: {
- error: err
+ error
}
});
- }.bind(this));
+ });
// Send the error event all the way up.
- this.server.on('connection', function (socket) {
+ this.server.on('connection', (socket) => {
+
this._renderScreen(socket);
- }.bind(this));
+ });
}
// Obtains viewport size, and initializes a random screen with a random
// renderer, setting the required interval to draw.
- _renderScreen (socket) {
- let connectionData = {
+
+ _renderScreen(socket) {
+
+ const connectionData = {
width: null,
height: null,
modulation: 0,
screen: this._getScreen(),
renderer: this._getRenderer(),
- socket: socket
+ socket
};
- let interval = null;
+ const interval = null;
+
+ socket.write(internals.kNAWSRequest);
- socket.write(kNAWSRequest);
+ socket.on('data', (data) => {
- socket.on('data', function (data) {
- if (data.slice(0, 6).compare(kNAWSResponse) === 0) {
+ if (data.slice(0, 6).compare(internals.kNAWSResponse) === 0) {
connectionData.width = Util.parse16BitBuffer(data.slice(6, 8));
connectionData.height = Util.parse16BitBuffer(data.slice(8, 10));
@@ -95,35 +156,34 @@ const TomatoSauce = class TomatoSauce extends EventEmitter {
interval = setInterval(this._writeMessage.bind(this, connectionData), this.frequency);
}
- if (data.compare(kEscape) === 0) {
+ if (data.compare(internals.kEscape) === 0) {
socket.write('\n');
clearInterval(interval);
socket.end();
}
- }.bind(this));
+ });
}
// Resets the cursor, gets a frame and sends it to the socket.
- _writeMessage (connectionData) {
- let payload = connectionData.screen(connectionData.modulation, connectionData.width, connectionData.height, connectionData.renderer);
- let message = `\x1B[1;1H${payload}`; // reset cursor position before payload
+
+ _writeMessage(connectionData) {
+
+ const payload = connectionData.screen(connectionData.modulation, connectionData.width, connectionData.height, connectionData.renderer);
+ const message = `\x1B[1;1H${payload}`; // reset cursor position before payload
connectionData.modulation = (connectionData.modulation + this.modulation) % 256;
connectionData.socket.write(message);
}
- _getScreen () {
+ _getScreen() {
+
return Util.pickRandom(this.screens);
}
- _getRenderer () {
+ _getRenderer() {
+
return Util.pickRandom(this.renderers);
}
};
-// Defaults.
-TomatoSauce.prototype.port = 9999;
-TomatoSauce.prototype.frequency = 333;
-TomatoSauce.prototype.modulation = 5;
-
module.exports = TomatoSauce;
diff --git a/lib/util.js b/lib/util.js
index 3234d3e..65f14ae 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -3,38 +3,70 @@
const Fs = require('fs');
const Path = require('path');
-// Module containing utility functions.
+/**
+ * Module containing utility functions
+ *
+ * @name Util
+ * @type Object
+ */
const Util = {
- // Parses a 16 bit number buffer.
- parse16BitBuffer: function (buffer) {
+ /**
+ * Parses a 16 bit number buffer
+ *
+ * @function parse16BitBuffer
+ * @memberof Util
+ * @param {Array<String>} buffer the buffer to parse
+ * @return {Number} the parsed value
+ */
+ parse16BitBuffer(buffer) {
+
return buffer[0] * 256 + buffer[1];
},
- // Picks a random element from an array.
- pickRandom: function (array) {
+ /**
+ * Picks a random element from an array
+ *
+ * @function pickRandom
+ * @memberof Util
+ * @param {Array} array the array to use
+ * @return {Any} the picked element
+ */
+ pickRandom(array) {
+
return array[Math.floor(Math.random() * array.length)];
},
- // For a given path, requires all of the files and returns an array
- // with the results. If the directory contains any non-requireable file,
- // it will fail.
- loadFiles: function (path) {
- return new Promise(function (resolve, reject) {
- Fs.readdir(path, function (err, files) {
+ /**
+ * For a gi ven path, requires all of the files and returns an array
+ * with the results. If the directory contains any non-requireable
+ * files, it will fail.
+ *
+ * @function loadFiles
+ * @memberof Util
+ * @param {String} path the path where the files are located
+ * @return {Array} the array of all the loaded modules
+ */
+ loadFiles(path) {
+
+ return new Promise((resolve, reject) => {
+
+ Fs.readdir(path, (err, files) => {
+
if (err) {
return reject(err);
}
- let loadedFiles = [];
+ const loadedFiles = [];
- for (let file of files) {
- let filePath = Path.join(path, file);
+ for (const file of files) {
+ const filePath = Path.join(path, file);
let loadedFile;
try {
loadedFile = require(filePath);
- } catch (err) {
+ }
+ catch (err) {
return reject(err);
}