Coding a hidden message in image - javascript

I want to code a hidden message in image using js, but don`t know how this works. I have been searching for some algorithm but dont found one. Can some one explain how to encode message in image using js?

Use this library https://www.peter-eigenschink.at/projects/steganographyjs/
It will allow you to hide text into images
EDIT - Adding the encoding code from the link
Cover.prototype.encode = function(message, image, options) {
// Handle image url
if(image.length) {
image = util.loadImg(image);
} else if(image.src) {
image = util.loadImg(image.src);
} else if(!(image instanceof HTMLImageElement)) {
throw new Error('IllegalInput: The input image is neither an URL string nor an image.');
}
options = options || {};
var config = this.config;
var t = options.t || config.t,
threshold = options.threshold || config.threshold,
codeUnitSize = options.codeUnitSize || config.codeUnitSize,
prime = util.findNextPrime(Math.pow(2,t)),
args = options.args || config.args,
messageDelimiter = options.messageDelimiter || config.messageDelimiter;
if(!t || t < 1 || t > 7) throw new Error('IllegalOptions: Parameter t = " + t + " is not valid: 0 < t < 8');
var shadowCanvas = document.createElement('canvas'),
shadowCtx = shadowCanvas.getContext('2d');
shadowCanvas.style.display = 'none';
shadowCanvas.width = options.width || image.width;
shadowCanvas.height = options.height || image.height;
if(options.height && options.width) {
shadowCtx.drawImage(image, 0, 0, options.width, options.height );
} else {
shadowCtx.drawImage(image, 0, 0);
}
var imageData = shadowCtx.getImageData(0, 0, shadowCanvas.width, shadowCanvas.height),
data = imageData.data;
// bundlesPerChar ... Count of full t-bit-sized bundles per Character
// overlapping ... Count of bits of the currently handled character which are not handled during each run
// dec ... UTF-16 Unicode of the i-th character of the message
// curOverlapping ... The count of the bits of the previous character not handled in the previous run
// mask ... The raw initial bitmask, will be changed every run and if bits are overlapping
var bundlesPerChar = codeUnitSize/t >> 0,
overlapping = codeUnitSize%t,
modMessage = [],
decM, oldDec, oldMask, left, right,
dec, curOverlapping, mask;
var i, j;
for(i=0; i<=message.length; i+=1) {
dec = message.charCodeAt(i) || 0;
curOverlapping = (overlapping*i)%t;
if(curOverlapping > 0 && oldDec) {
// Mask for the new character, shifted with the count of overlapping bits
mask = Math.pow(2,t-curOverlapping) - 1;
// Mask for the old character, i.e. the t-curOverlapping bits on the right
// of that character
oldMask = Math.pow(2, codeUnitSize) * (1 - Math.pow(2, -curOverlapping));
left = (dec & mask) << curOverlapping;
right = (oldDec & oldMask) >> (codeUnitSize - curOverlapping);
modMessage.push(left+right);
if(i<message.length) {
mask = Math.pow(2,2*t-curOverlapping) * (1 - Math.pow(2, -t));
for(j=1; j<bundlesPerChar; j+=1) {
decM = dec & mask;
modMessage.push(decM >> (((j-1)*t)+(t-curOverlapping)));
mask <<= t;
}
if((overlapping*(i+1))%t === 0) {
mask = Math.pow(2, codeUnitSize) * (1 - Math.pow(2,-t));
decM = dec & mask;
modMessage.push(decM >> (codeUnitSize-t));
}
else if(((((overlapping*(i+1))%t) + (t-curOverlapping)) <= t)) {
decM = dec & mask;
modMessage.push(decM >> (((bundlesPerChar-1)*t)+(t-curOverlapping)));
}
}
}
else if(i<message.length) {
mask = Math.pow(2,t) - 1;
for(j=0; j<bundlesPerChar; j+=1) {
decM = dec & mask;
modMessage.push(decM >> (j*t));
mask <<= t;
}
}
oldDec = dec;
}
// Write Data
var offset, index, subOffset, delimiter = messageDelimiter(modMessage,threshold),
q, qS;
for(offset = 0; (offset+threshold)*4 <= data.length && (offset+threshold) <= modMessage.length; offset += threshold) {
qS=[];
for(i=0; i<threshold && i+offset < modMessage.length; i+=1) {
q = 0;
for(j=offset; j<threshold+offset && j<modMessage.length; j+=1)
q+=modMessage[j]*Math.pow(args(i),j-offset);
qS[i] = (255-prime+1)+(q%prime);
}
for(i=offset*4; i<(offset+qS.length)*4 && i<data.length; i+=4)
data[i+3] = qS[(i/4)%threshold];
subOffset = qS.length;
}
// Write message-delimiter
for(index = (offset+subOffset); index-(offset+subOffset)<delimiter.length && (offset+delimiter.length)*4<data.length; index+=1)
data[(index*4)+3]=delimiter[index-(offset+subOffset)];
// Clear remaining data
for(i=((index+1)*4)+3; i<data.length; i+=4) data[i] = 255;
imageData.data = data;
shadowCtx.putImageData(imageData, 0, 0);
return shadowCanvas.toDataURL();
};

Related

Paint with numbers with Adobe Illustrator Javascript

Hello thanks to moluapple, I got this script from the adobe support community, I'm a beginner in javascript but I have a task to make, I have to paint numbers in each color of the image, actually, I see that in this solution brings me numbers in each layer, but doesn't work for the colors, can someone has a similar solution?
Let me add the expected result which is for each color on the picture the script adds a number to the color 1 for green 2 for yellow etc, let me show you.enter image description here
here is the original link
https://community.adobe.com/t5/illustrator-discussions/script-insert-text-number-in-the-middle-of-visible-bounds-of-the-each-object/td-p/8088914/page/3
(function() {
var doc = app.activeDocument,
lays = doc.layers,
WORK_LAY = lays.add(),
NUM_LAY = lays.add(),
i = lays.length - 1,
lay;
// main working loop
for (; i > 1; i--) {
//process each layer
lay = lays[i];
lay.name = lay.name + " Num:" + (i - 1); // i-1 as 2 layers beed added.
process(lay.pathItems, false);
process(lay.compoundPathItems, true); // if any
}
//clean up
NUM_LAY.name = "Numbers";
WORK_LAY.remove();
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
// add stroke
if (isCompound) {
strokeComPath(op);
} else {
!op.closed && op.closed = true;
op.filled = false;
op.stroked = true;
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 4 : 6; // adjust font size for small area paths.
add_nums(i - 1, xy, s);
}
}
function getMinVisibleSize(b) {
var s = Math.min(b[2] - b[0], b[1] - b[3]);
return Math.abs(s);
}
function getGeometricCenter(p) {
var b = p.geometricBounds;
return [(b[0] + b[2]) / 2, (b[1] + b[3]) / 2];
}
// returns square of distance between p1 and p2
function getDist2(p1, p2) {
return Math.pow(p1[0] + p2[0], 2) + Math.pow(p1[1] + p2[1], 2);
}
// returns visibleBounds of a path in a compoundPath p
// which is closest to center of the original path op
function findBestBounds(op, p) {
var opc = getGeometricCenter(op);
var idx = 0,
d;
var minD = getDist2(opc, getGeometricCenter(p.pathItems[0]));
for (var i = 0, iEnd = p.pathItems.length; i < iEnd; i++) {
d = getDist2(opc, getGeometricCenter(p.pathItems[i]));
if (d < minD) {
minD = d;
idx = i;
}
}
return p.pathItems[idx].visibleBounds;
}
function applyOffset(op, checkBounds) {
var p = op.duplicate(WORK_LAY, ElementPlacement.PLACEATBEGINNING),
// offset value the small the better, but meantime more slow.
offset = function() {
var minsize = Math.min(p.width, p.height);
if (minsize >= 50) {
return '-1'
} else if (20 < minsize && minsize < 50) {
return '-0.5'
} else {
return '-0.2' // 0.2 * 2 (both side ) * 50 (Times) = 20
}
},
xmlstring = '<LiveEffect name="Adobe Offset Path"><Dict data="I jntp 2 R mlim 4 R ofst #offset"/></LiveEffect>'
.replace('#offset', offset()),
TIMES = 100; // if shapes are too large, should increase the value.
if (checkBounds) {
// check its size only if it needs, because it's too slow
while (TIMES-- && getMinVisibleSize(p.visibleBounds) > 3) p.applyEffect(xmlstring);
} else {
while (TIMES--) p.applyEffect(xmlstring);
}
return p;
}
function getCenterBounds(op) {
var originalMinSize = getMinVisibleSize(op.visibleBounds);
var p = applyOffset(op, false);
if (getMinVisibleSize(p.visibleBounds) > originalMinSize) {
// in some cases, path p becomes larger for some unknown reason
p.remove();
p = applyOffset(op, true);
}
var b = p.visibleBounds;
if (getMinVisibleSize(b) > 10) {
activeDocument.selection = [p];
executeMenuCommand("expandStyle");
p = activeDocument.selection[0];
if (p.typename == "CompoundPathItem") {
b = findBestBounds(op, p);
}
}
p.remove();
return b;
}
function add_nums(n, xy, s) {
var txt = NUM_LAY.textFrames.add();
txt.contents = n;
txt.textRange.justification = Justification.CENTER;
txt.textRange.characterAttributes.size = s;
txt.position = [xy[0] - txt.width / 2, xy[1] + txt.height / 2];
}
function strokeComPath(compoundPath) {
var p = compoundPath.pathItems,
l = p.length,
i = 0;
for (; i < l; i++) {
!p[i].closed && p[i].closed = true;
p[i].stroked = true;
p[i].filled = false;
};
}
})();
I don't understand what exactly you're trying to gain. So here is a guess:
If you change the function process() this way:
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
c = op.fillColor; // <--- here
color = 'CMYK ' + // <--- here
[ Math.round(c.cyan), // <--- here
Math.round(c.magenta), // <--- here
Math.round(c.yellow), // <--- here
Math.round(c.black) // <--- here
].join(','); // <--- here
// add stroke
if (isCompound) {
strokeComPath(op);
} else {
!op.closed && op.closed = true;
// op.filled = false; // <--- here
// op.stroked = true; // <--- here
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 4 : 6; // adjust font size for small area paths.
// add_nums(i - 1 + color, xy, s);
add_nums(color, xy, s); // <--- here
}
}
It will put CMYK percents inside the shapes instead of numbers.
Here is the another modification of the main function process(). It's turned out that it can be done if you add & modify just two lines:
function process(items, isCompound) {
var j = 0,
b, xy, s, p, op;
for (; j < items.length; j++) {
// process each pathItem
op = items[j];
try { color = op.fillColor.spot.name } catch(e) { continue } // <-- HERE
// add stroke
if (isCompound) {
strokeComPath(op);
} else {
!op.closed && op.closed = true;
op.filled = false;
op.stroked = true;
};
b = getCenterBounds(op);
xy = [b[0] + (b[2] - b[0]) / 2, b[1] + (b[3] - b[1]) / 2];
s = (
Math.min(op.height, op.width) < 20 ||
(op.area && Math.abs(op.area) < 150)
) ? 4 : 6; // adjust font size for small area paths.
add_nums(color, xy, s); // <--- HERE
}
}
And the colors should be Spot Global colors. The script put in the middle of every shape a name of the swatch color.
Input:
Output:

p5.js Prim's Algorithm Maze Generation: Stuck in Infinite Loop

Note: I also posted this question on p5.js' forum: https://discourse.processing.org/t/p5-js-prims-algorithm-maze-generation-stuck-in-infinite-loop/8277
I'm trying to implement a randomized Prim's algorithm to generate a maze. However, the program keeps getting stuck in an infinite loop as the length of the list of walls (wallList) is always in the thousands. Currently I am using an if statement that stops the maze generation after 11500 iterations to prevent an infinite loop.
My pseudocode is based on Wikipedia's description of the algorithm:
Start with a grid full of walls.
Pick a cell, mark it as part of the
maze. Add the walls of the cell to the wall list.
While there are
walls in the list:
Pick a random wall from the list. If only one of
the two cells that the wall divides is visited, then:
Make the wall a
passage and mark the unvisited cell as part of the maze.
Add the
neighboring walls of the cell to the wall list.
Remove the wall from
the list.
HTML:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.js"></script>
</script>
</head>
<body>
<div id="game-wrapper">
<div id="canvas-wrapper">
</div>
</div>
</div>
<script type="text/javascript" src="maze.js"></script>
</body>
</html>
JavaScript (maze.js):
// Note to self: JavaScript variables have function scope, not block scope
var numIterations = 0; // Just for debugging purposes (stop at 1000 iterations otherwise the program goes into
// an infinite loop
// The directions and vectors arrays correspond to each other
// For example, the first element of directions is "N" and the first element of vectors also represents a
// north vector
var directions = ["N", "E", "S", "W"]
var vectors = [
[-1, 0],
[0, 1],
[1, 0],
[0, -1]
];
var wallList = {}; // Structure of a wall [rol (num), col (num), direction (string)]
function Maze(numRows, numColumns) {
/*
Defines a maze given the number of rows and the number of columns in the maze
*/
this.numColumns = numColumns;
this.numRows = numRows;
this.numCells = numRows * numColumns;
this.cellGraph = [];
for (var i = 0; i < numRows; i++) { // For every single row
this.cellGraph.push([]); // Start out with an empty row
}
}
Maze.prototype.computeFrontierWalls = function (cellRow, cellColumn) {
/*
The frontier walls of a cell is defined as all the walls of the adjacent cells
*/
/*
Coordinates of adjacent cells:
Up [cellRow - 1, cellColumn]
Down [cellRow + 1, cellColumn]
Right [cellRow, cellColumn + 1]
Left [cellRow, cellColumn - 1]
*/
var coordinates = [
[cellRow - 1, cellColumn],
[cellRow + 1, cellColumn],
[cellRow, cellColumn + 1],
[cellRow, cellColumn - 1]
];
var computedFrontier = []; // List of frontier cells
var originalCell = this.cellGraph[cellRow][cellColumn]; // We want to calculate the frontier of the original cell
for (var i = 0; i < coordinates.length; i++) {
// Get the coordinates of the adjacent cell
var coordinate = coordinates[i];
var row = coordinate[0];
var col = coordinate[1];
// See if a cell exists at that area
// If there is a cell that exists, add all of the walls of the cell to the computedFrontier array
if (row >= 0 && row < this.cellGraph.length && col >= 0 && col < this.cellGraph[0].length) {
var cell = this.cellGraph[parseInt(row)][parseInt(col)];
for (var j = 0; j < directions.length; j++) {
computedFrontier.push([cell.row, cell.column, directions[j]]);
}
}
}
return computedFrontier;
}
function Cell(cellSize, row, column) {
this.cellSize = cellSize; // The width and height of the cell
this.column = column;
this.row = row;
this.xPos = column * cellSize;
this.yPos = row * cellSize;
this.walls = [true, true, true, true]; // 0 = top, 1 = right, 2 = bottom, 3 = left
this.visited = false; // Whether the cell has been traversed or not
}
function getRandomPos(widthCells, heightCells) {
var row = Math.floor(Math.random() * heightCells); // Generate a random row
var column = Math.floor(Math.random() * widthCells); // Generate a random column
return [row, column];
}
var mazeIntro = function (p) {
var maze = new Maze(20, 35); // Generate a new maze with 20 rows and 35 columns
Maze.prototype.createMaze = function () { // Build an empty maze
for (var i = 0; i < this.numRows; i++) { // Iterate through every row
for (var j = 0; j < this.numColumns; j++) { // Iterate through every column
var cell = new Cell(20, i, j); // Create a new size at row i and column j with size 20
maze.cellGraph[i].push(cell); // Add the cell to the row
}
}
}
maze.createMaze(); // Build the maze
p.setup = function () {
var canvas = p.createCanvas(700, 400);
p.background(255, 255, 255);
p.smooth();
p.noLoop();
// Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list
var pos = getRandomPos(maze.cellGraph[0].length, maze.cellGraph.length);
;
var row = pos[0];
var column = pos[1];
maze.cellGraph[row][column].visited = true;
for (var k = 0; k < directions.length; k++) {
var key = row.toString() + column.toString() + directions[k].toString();
if (!wallList[key]) {
wallList[key] = [row, column, directions[k]];
}
}
}
Cell.prototype.display = function () {
/*
For each wall:
1. Check if it is on the border of the maze:
2. If it is on the border: don't draw the wall
3. If it isn't on the border: draw the wall
*/
p.stroke(0, 0, 0);
if (this.walls[0] && this.row != 0) { // Top
p.line(this.xPos, this.yPos, this.xPos + this.cellSize, this.yPos);
}
if (this.walls[1] && this.column != maze.widthCells - 1) { // Right
p.line(this.xPos + this.cellSize, this.yPos, this.xPos + this.cellSize, this.yPos + this.cellSize);
}
if (this.walls[2] && this.row != maze.heightCells - 1) { // Bottom
p.line(this.xPos + this.cellSize, this.yPos + this.cellSize, this.xPos, this.yPos + this.cellSize);
}
if (this.walls[3] && this.column != 0) { // Left
p.line(this.xPos, this.yPos + this.cellSize, this.xPos, this.yPos);
}
p.noStroke();
}
Cell.prototype.toString = function () {
/*
Mainly used for debugging purposes, converts the object into a string containing the row and the column of the cell
*/
return this.row + "|" + this.column;
}
function deleteWall(current, neighbor) {
/*
Deletes two walls separating two cells: current and neighbor
Calculates if neighbor is to the left, right, top, or bottom of cell
Removes the current's wall and the corresponding neighbor's wall
*/
var deltaX = current.column - neighbor.column;
var deltaY = current.row - neighbor.row;
if (deltaX == 1) { // Current is to the right of the neighbor
current.walls[3] = false;
neighbor.walls[1] = false;
}
if (deltaX == -1) { // Current is to the left of the neighbor
current.walls[1] = false;
neighbor.walls[3] = false;
}
if (deltaY == 1) { // Current is to the bottom of the neighbor
current.walls[0] = false;
neighbor.walls[2] = false;
}
if (deltaY == -1) { // Current is to the top of the neighbor
current.walls[2] = false;
neighbor.walls[0] = false;
}
}
function isWall(cellA, cellB) {
// Whether there's a wall or not depends on the orientation of the blocks
// If it's vertical, it has to be false between even numbers
// If it's horizontal, it has to be false between odd numbers
for (var j = 0; j < cellA.walls.length; j++) {
for (var k = 0; k < cellB.walls.length; k++) {
if (Math.abs(j - k) == 2 && !cellA.walls[j] && !cellB.walls[k]) {
var rA = cellA.row;
var cA = cellA.column;
var rB = cellB.row;
var cB = cellB.column
if ((rA - rB) == 1 && j == 0 || (rA - rB) == -1 && j == 2 || (cA - cB) == 1 && j == 3 || (cA - cB) == -1 && j == 1) {
return false;
}
}
}
}
return true;
}
function calculateCellDivision(wall) {
// Calculate the two cells that the wall divides
// For example:
// If the wall is [10, 11, "N"]
// The two cells that the wall divides are (10, 11) and (9, 11)
var row = wall[0];
var col = wall[1];
var cell1 = maze.cellGraph[row][col]; // Get the cell of the wall
// Get the corresponding vector based upon the direction of the wall
var vectorIndex = directions.indexOf(wall[2]);
// Add the vector to the position of cell1
var cell2Row = parseInt(cell1.row) + vectors[vectorIndex][0];
var cell2Column = parseInt(cell1.column) + vectors[vectorIndex][1];
if (cell2Row < 0 || cell2Row >= maze.cellGraph.length || cell2Column < 0 || cell2 >= maze.cellGraph[0].length) {
return -1;
}
var cell2 = maze.cellGraph[cell2Row][cell2Column]; // Get the corresponding cell
var cellsVisited = 0;
var unvisitedCell;
if (cell1.visited) {
cellsVisited += 1;
unvisitedCell = cell2;
}
if (!cell2) { // This means that the wall is a border wall
return -1;
}
if (cell2.visited) {
cellsVisited += 1;
unvisitedCell = cell1;
}
if (cellsVisited == 1) {
return [cell1, cell2, cellsVisited, unvisitedCell];
}
return -1;
}
function getCellWalls(row, col) {
// Gets a cell's walls
var cellWalls = [];
for (var i = 0; i < directions.length; i++) {
cellWalls.push(row + col + directions[i]);
}
return cellWalls;
}
p.draw = function () {
while (Object.keys(wallList).length > 0) { // While there are still walls in the list
console.log("Object.keys(wallList).length = " +
// Pick a random wall of the list
var wallListKeys = $.map(wallList, function (value, key) {
return key;
});
var randomKey = wallListKeys[Math.floor(Math.random() * wallListKeys.length)];
var randomWall = wallList[randomKey];
var components = calculateCellDivision(randomWall);
if (components != -1) {
var numVisited = components[2];
var cell1 = components[0];
var cell2 = components[1];
// If only one of the two cells that the wall divides is visited, then:
// 1. Make the wall a passage and mark the unvisited cell as part of the maze.
// 2. Add the neighboring walls of the cell to the wall list.
// Remove the wall from the list.
if (numVisited == 1) {
deleteWall(cell1, cell2);
var unvisitedCell = maze.cellGraph[components[3].row][components[3].column];
unvisitedCell.visited = true;
var unvisitedString = unvisitedCell.row + "|" + unvisitedCell.column;
// Add the neighboring walls of the cell to the wall list
// Format of the walls (by index):
// 0 = top, 1 = right, 2 = bottom, 3 = left
var computedFrontierWalls = maze.computeFrontierWalls(unvisitedCell.row, unvisitedCell.column);
for (var k = 0; k < computedFrontierWalls.length; k++) {
var computedWall = computedFrontierWalls[k];
var keyString = computedWall[0].toString() + computedWall[1].toString() + computedWall[2];
if (!wallList[keyString]) {
wallList[keyString] = computedWall;
}
}
// Delete randomKey from the list of walls, and then delete the same wall from the corresponding cell
delete wallList[randomKey];
// Calculate the corresponding cell
var direction = randomWall[2];
var directionIndex = directions.indexOf(direction);
var oppositeDirectionIndex = -1;
if (directionIndex == 0) {
oppositeDirectionIndex = 2;
}
if (directionIndex == 2) {
oppositeDirectionIndex = 0;
}
if (directionIndex == 1) {
oppositeDirectionIndex = 3;
}
if (directionIndex == 3) {
oppositeDirectionIndex = 1;
}
var vector = vectors[directionIndex];
var correspondingString = (randomWall[0] + vector[0]).toString() + (randomWall[1] + vector[1]).toString() + directions[oppositeDirectionIndex];
delete wallList[correspondingString];
}
}
numIterations += 1;
if (numIterations == 11500) { // Prevents infinite loop
break;
}
}
p.clear();
// Draw the maze
for (var i = 0; i < maze.cellGraph.length; i++) { // Iterate through row
for (var j = 0; j < maze.cellGraph[i].length; j++) { // Iterate through every column
maze.cellGraph[i][j].display(); // Display the cell
}
}
p.line(0, 400, 400, 400);
}
};
var myp5 = new p5(mazeIntro, "canvas-wrapper"); // Initialize the graphics engine for the canvas
The generation does work, as shown in the screenshot below. But I'm pretty sure my implementation is not correct as the wall list shouldn't contain thousands of walls.
According to the Wikipedia article, a wall must be removed once is picked randomly and analysed, regardless of the results of said analysis. In your code, the lines delete wallList[randomKey]; and delete wallList[correspondingString]; must be outside of the conditional for the wall to be eliminated from the list.
After deleting said lines, replace this:
numIterations += 1;
if (numIterations == 11500) { // Prevents infinite loop
break;
}
with this:
delete wallList[randomKey];
delete wallList[correspondingString];
and you're good to go. (Disclaimer: I tested it and it worked; but that's a hefty amount of code you have there, so I'm not sure if anything else breaks).
I wanted to try and make it more readable so I could understand better and came up with this. Hope it helps.
// 1. Start with a grid full of walls.
const _WALL = '█';
const _PATH = ' ';
const _COLS = 60;
const _ROWS = 60;
let maze = [];
for(let i = 0; i < _COLS; i++){
maze.push([]);
for(let j = 0; j < _ROWS; j++)
maze[i][j] = _WALL;
}
// 2. Pick a cell, mark it as part of the maze.
let cell = {x:Math.floor(Math.random()*_COLS), y:Math.floor(Math.random()*_ROWS)};
maze[cell.x][cell.y] = _PATH;
// 2.1 Add the walls of the cell to the wall list.
let walls = [];
if(cell.x+1 < _COLS) walls.push({x:cell.x+1, y:cell.y});
if(cell.x-1 >= 0) walls.push({x:cell.x-1, y:cell.y});
if(cell.y+1 < _ROWS) walls.push({x:cell.x, y:cell.y+1});
if(cell.y-1 >= 0) walls.push({x:cell.x, y:cell.y-1});
// 3. While there are walls in the list:
while(walls.length > 0){
// 3.1 Pick a random wall from the list.
let wallIndex = Math.floor(Math.random() * walls.length);
let wall = walls[wallIndex];
// 3.2 If only one of the two cells that the wall divides is visited, then:
let uc = []; // uc will be short for 'unvisited cell'
if(wall.x+1 < _COLS && maze[wall.x+1][wall.y] === _PATH) uc.push({x:wall.x-1, y:wall.y});
if(wall.x-1 >= 0 && maze[wall.x-1][wall.y] === _PATH) uc.push({x:wall.x+1, y:wall.y});
if(wall.y+1 < _ROWS && maze[wall.x][wall.y+1] === _PATH) uc.push({x:wall.x, y:wall.y-1});
if(wall.y-1 >= 0 && maze[wall.x][wall.y-1] === _PATH) uc.push({x:wall.x, y:wall.y+1});
if(uc.length === 1){
// 3.2.1 Make the wall a passage and mark the unvisited cell as part of the maze.
maze[wall.x][wall.y] = _PATH;
if(uc[0].x >=0 && uc[0].x <_COLS && uc[0].y >=0 && uc[0].y<_ROWS){
maze[uc[0].x][uc[0].y] = _PATH;
// 3.2.2 Add the neighboring walls of the cell to the wall list.
if(uc[0].x+1 < _COLS && maze[uc[0].x+1][uc[0].y] === _WALL) walls.push({x:uc[0].x+1, y:uc[0].y});
if(uc[0].x-1 >= 0 && maze[uc[0].x-1][uc[0].y] === _WALL) walls.push({x:uc[0].x-1, y:uc[0].y});
if(uc[0].y+1 < _ROWS && maze[uc[0].x][uc[0].y+1] === _WALL) walls.push({x:uc[0].x, y:uc[0].y+1});
if(uc[0].y-1 >= 0 && maze[uc[0].x][uc[0].y-1] === _WALL) walls.push({x:uc[0].x, y:uc[0].y-1});
}
}
// 3.3 Remove the wall from the list.
walls.splice(wallIndex, 1);
}
console.table(maze);
function setup(){
createCanvas(400, 400);
fill(0);
let widthUnit = width / _COLS;
let heightUnit = height / _ROWS;
for(let i = 0; i < _COLS; i++)
for(let j = 0; j < _ROWS; j++)
if(maze[i][j] === _WALL){
//rect(i*widthUnit, j*heightUnit, widthUnit, heightUnit);
if(i-1 >= 0 && i+1 < _COLS){
if(maze[i-1][j] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, i*widthUnit, (j+0.5)*heightUnit);
if(maze[i+1][j] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, (i+1)*widthUnit, (j+0.5)*heightUnit);
}
if(j-1 >= 0 && j+1 < _ROWS){
if(maze[i][j-1] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, (i+0.5)*widthUnit, j*heightUnit);
if(maze[i][j+1] === _WALL) line((i+0.5)*widthUnit, (j+0.5)*heightUnit, (i+0.5)*widthUnit, (j+1)*heightUnit);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>

An algorithm to translate relative coordinates to absolute coordinates

Are there any math geniuses out there that are aware of or able to devise an algorithm that will translate relative coordinates into absolute coordinates?
I created an SVG (codepen) with loads and loads and loads of paths (roughly 600) and other SVG elements, a great number of which (roughly 100) have CSS transformations applied to them. Unfortunately, I created it using Chrome and never bothered to check Firefox or other browsers.
On Firefox, none of them work correctly because the transforms are happening in relation to either the viewport or the SVG viewbox; I'm not entirely sure which one since I set the same values for the viewBox and the width and height. Microsoft Edge is another beast. It seems as if Edge doesn't even support CSS transforms applied to SVGs at the moment.
I've come across other questions (and responses) which state an adequate cross browser solution is to use "absolute" coordinates (or coordinates relative to the viewBox).
So again, is there an easy way to translate such coordinates accordingly?
I ended up writing a script that provides me with the necessary adjustments, which can be seen below!
for (i = 0; i < groundDoorIds.length; i++) {
var a = groundDoorIds[i].replace('g-o-', '');
console.log(a);
var b = document.getElementById(groundDoorIds[i]);
var c = b.getAttribute('d');
var d = c.substr(2, 11);
var e;
if (d.indexOf('h') < 0) {
if (d.indexOf('H') < 0) {
if (d.indexOf('v') < 0) {
e = d.indexOf('V');
} else {
e = d.indexOf('v');
}
} else {
e = d.indexOf('H');
}
} else {
e = d.indexOf('h');
}
var f = d.slice(0, e);
var g = f.indexOf(',');
var h = f.slice(0, g);
var j = f.slice(g + 1);
var k;
var l;
if (a.indexOf('door-right-top') > 0 || a.indexOf('door-right-bottom') > 0) {
k = Number(h) + 0.5;
l = Number(j);
} else if (a.indexOf('door-left-top') > 0 || a.indexOf('door-left-bottom') > 0) {
k = Number(h) - 0.5;
l = Number(j);
} else if (a.indexOf('door-bottom-left') > 0 || a.indexOf('door-bottom-right') > 0) {
k = Number(h);
l = Number(j) + 0.5;
} else if (a.indexOf('door-top-left') > 0 || a.indexOf('door-top-right') > 0) {
k = Number(h);
l = Number(j) - 0.5;
}
console.log('style="transform-origin:' + k + 'px ' + l + 'px;"')
console.log(`
`)
}
// which logs to the console the following information for each door
//
// construction-shop-stairwell-c-door-right-top [part of id of ele]
// style="transform-origin:92.5px 11px;" [new "absolute" coords]
For those who were confused by my question, below is basically what I was asking for.
for (i = 0; i < groundDoorIds.length; i++) {
var a = groundDoorIds[i].replace('g-o-', '');
console.log(a);
var b = document.getElementById(groundDoorIds[i]);
var c = b.getAttribute('d');
var d = c.substr(2, 11);
var e;
if (d.indexOf('h') < 0) {
if (d.indexOf('H') < 0) {
if (d.indexOf('v') < 0) {
e = d.indexOf('V');
} else {
e = d.indexOf('v');
}
} else {
e = d.indexOf('H');
}
} else {
e = d.indexOf('h');
}
var f = d.slice(0, e);
var g = f.indexOf(',');
var h = f.slice(0, g);
var j = f.slice(g + 1);
var k;
var l;
if (a.indexOf('door-right-top') > 0 || a.indexOf('door-right-bottom') > 0) {
k = Number(h) + 0.5;
l = Number(j);
} else if (a.indexOf('door-left-top') > 0 || a.indexOf('door-left-bottom') > 0) {
k = Number(h) - 0.5;
l = Number(j);
} else if (a.indexOf('door-bottom-left') > 0 || a.indexOf('door-bottom-right') > 0) {
k = Number(h);
l = Number(j) + 0.5;
} else if (a.indexOf('door-top-left') > 0 || a.indexOf('door-top-right') > 0) {
k = Number(h);
l = Number(j) - 0.5;
}
console.log('style="transform-origin:' + k + 'px ' + l + 'px;"')
console.log(`
`)
}
...which logs to console the following information for each door.
construction-shop-stairwell-c-door-right-top // part of id of ele
style="transform-origin:92.5px 11px;" // new "absolute" coords

Find Server Seed from Hash Verification Script

View this jsfiddle: https://jsfiddle.net/1L1uqcgv/6/
function refreshTable(){
var hash = document.getElementById("gameHash").value;
var lastHash = "";
var amount = document.getElementById("gameAmount").value;
var tableBody = document.getElementById("tbody");
tableBody.innerHTML = "";
for(var i=0; i<amount; i++){
var gameHash = (lastHash!=""?genGameHash(lastHash):hash);
var gameCrash = crashPointFromHash((lastHash!=""?genGameHash(lastHash):hash));
var clr = gameCrash > 1.97 ? 'green': (gameCrash < 1.97 ? 'red' : 'blue');
tableBody.innerHTML += "<tr><td>"+gameHash+"</td><td style='background:" + clr + "'>"+gameCrash+"</td></tr>";
lastHash = gameHash;
}
}
function divisible(hash, mod) {
// So ABCDEFGHIJ should be chunked like AB CDEF GHIJ
var val = 0;
var o = hash.length % 4;
for (var i = o > 0 ? o - 4 : 0; i < hash.length; i += 4) {
val = ((val << 16) + parseInt(hash.substring(i, i+4), 16)) % mod;
}
return val === 0;
}
function genGameHash(serverSeed) {
return CryptoJS.SHA256(serverSeed).toString()
};
function hmac(key, v) {
var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);
return hmacHasher.finalize(v).toString();
}
function crashPointFromHash(serverSeed) {
// see: provably fair seeding event
var hash = hmac(serverSeed, '000000000000000007a9a31ff7f07463d91af6b5454241d5faf282e5e0fe1b3a');
// In 1 of 101 games the game crashes instantly.
if (divisible(hash, 101))
return 0;
// Use the most significant 52-bit from the hash to calculate the crash point
var h = parseInt(hash.slice(0,52/4),16);
var e = Math.pow(2,52);
return (Math.floor((100 * e - h) / (e - h))/100).toFixed(2);
};
Is it possible to find the "serverSeed" as shown in lines 31 and 41?
To find previous games, a hash is entered into the box, showing all previous games. Would serverSeed be used to find these hashes, or does the single hash create all previous hashes?
The ServerSeed is the hash you input in order to see the crash point and to view the previous games.

Uint8Array to string in Javascript

I have some UTF-8 encoded data living in a range of Uint8Array elements in Javascript. Is there an efficient way to decode these out to a regular javascript string (I believe Javascript uses 16 bit Unicode)? I dont want to add one character at the time as the string concaternation would become to CPU intensive.
TextEncoder and TextDecoder from the Encoding standard, which is polyfilled by the stringencoding library, converts between strings and ArrayBuffers:
var uint8array = new TextEncoder().encode("someString");
var string = new TextDecoder().decode(uint8array);
This should work:
// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
/* utf.js - UTF-8 <=> UTF-16 convertion
*
* Copyright (C) 1999 Masanao Izumo <iz#onicos.co.jp>
* Version: 1.0
* LastModified: Dec 25 1999
* This library is free. You can redistribute it and/or modify it.
*/
function Utf8ArrayToStr(array) {
var out, i, len, c;
var char2, char3;
out = "";
len = array.length;
i = 0;
while(i < len) {
c = array[i++];
switch(c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}
It's somewhat cleaner as the other solutions because it doesn't use any hacks nor depends on Browser JS functions, e.g. works also in other JS environments.
Check out the JSFiddle demo.
Also see the related questions: here and here
Here's what I use:
var str = String.fromCharCode.apply(null, uint8Arr);
In Node "Buffer instances are also Uint8Array instances", so buf.toString() works in this case.
In NodeJS, we have Buffers available, and string conversion with them is really easy. Better, it's easy to convert a Uint8Array to a Buffer. Try this code, it's worked for me in Node for basically any conversion involving Uint8Arrays:
let str = Buffer.from(uint8arr.buffer).toString();
We're just extracting the ArrayBuffer from the Uint8Array and then converting that to a proper NodeJS Buffer. Then we convert the Buffer to a string (you can throw in a hex or base64 encoding if you want).
If we want to convert back to a Uint8Array from a string, then we'd do this:
let uint8arr = new Uint8Array(Buffer.from(str));
Be aware that if you declared an encoding like base64 when converting to a string, then you'd have to use Buffer.from(str, "base64") if you used base64, or whatever other encoding you used.
This will not work in the browser without a module! NodeJS Buffers just don't exist in the browser, so this method won't work unless you add Buffer functionality to the browser. That's actually pretty easy to do though, just use a module like this, which is both small and fast!
Found in one of the Chrome sample applications, although this is meant for larger blocks of data where you're okay with an asynchronous conversion.
/**
* Converts an array buffer to a string
*
* #private
* #param {ArrayBuffer} buf The buffer to convert
* #param {Function} callback The function to call when conversion is complete
*/
function _arrayBufferToString(buf, callback) {
var bb = new Blob([new Uint8Array(buf)]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
}
The solution given by Albert works well as long as the provided function is invoked infrequently and is only used for arrays of modest size, otherwise it is egregiously inefficient. Here is an enhanced vanilla JavaScript solution that works for both Node and browsers and has the following advantages:
• Works efficiently for all octet array sizes
• Generates no intermediate throw-away strings
• Supports 4-byte characters on modern JS engines (otherwise "?" is substituted)
var utf8ArrayToStr = (function () {
var charCache = new Array(128); // Preallocate the cache for the common single byte chars
var charFromCodePt = String.fromCodePoint || String.fromCharCode;
var result = [];
return function (array) {
var codePt, byte1;
var buffLen = array.length;
result.length = 0;
for (var i = 0; i < buffLen;) {
byte1 = array[i++];
if (byte1 <= 0x7F) {
codePt = byte1;
} else if (byte1 <= 0xDF) {
codePt = ((byte1 & 0x1F) << 6) | (array[i++] & 0x3F);
} else if (byte1 <= 0xEF) {
codePt = ((byte1 & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
} else if (String.fromCodePoint) {
codePt = ((byte1 & 0x07) << 18) | ((array[i++] & 0x3F) << 12) | ((array[i++] & 0x3F) << 6) | (array[i++] & 0x3F);
} else {
codePt = 63; // Cannot convert four byte code points, so use "?" instead
i += 3;
}
result.push(charCache[codePt] || (charCache[codePt] = charFromCodePt(codePt)));
}
return result.join('');
};
})();
Uint8Array to String
let str = Buffer.from(key.secretKey).toString('base64');
String to Uint8Array
let uint8arr = new Uint8Array(Buffer.from(data,'base64'));
I was frustrated to see that people were not showing how to go both ways or showing that things work on none trivial UTF8 strings. I found a post on codereview.stackexchange.com that has some code that works well. I used it to turn ancient runes into bytes, to test some crypo on the bytes, then convert things back into a string. The working code is on github here. I renamed the methods for clarity:
// https://codereview.stackexchange.com/a/3589/75693
function bytesToSring(bytes) {
var chars = [];
for(var i = 0, n = bytes.length; i < n;) {
chars.push(((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff));
}
return String.fromCharCode.apply(null, chars);
}
// https://codereview.stackexchange.com/a/3589/75693
function stringToBytes(str) {
var bytes = [];
for(var i = 0, n = str.length; i < n; i++) {
var char = str.charCodeAt(i);
bytes.push(char >>> 8, char & 0xFF);
}
return bytes;
}
The unit test uses this UTF-8 string:
// http://kermitproject.org/utf8.html
// From the Anglo-Saxon Rune Poem (Rune version)
const secretUtf8 = `ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ
ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ
ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬`;
Note that the string length is only 117 characters but the byte length, when encoded, is 234.
If I uncomment the console.log lines I can see that the string that is decoded is the same string that was encoded (with the bytes passed through Shamir's secret sharing algorithm!):
Do what #Sudhir said, and then to get a String out of the comma seperated list of numbers use:
for (var i=0; i<unitArr.byteLength; i++) {
myString += String.fromCharCode(unitArr[i])
}
This will give you the string you want,
if it's still relevant
If you can't use the TextDecoder API because it is not supported on IE:
You can use the FastestSmallestTextEncoderDecoder polyfill recommended by the Mozilla Developer Network website;
You can use this function also provided at the MDN website:
function utf8ArrayToString(aBytes) {
var sView = "";
for (var nPart, nLen = aBytes.length, nIdx = 0; nIdx < nLen; nIdx++) {
nPart = aBytes[nIdx];
sView += String.fromCharCode(
nPart > 251 && nPart < 254 && nIdx + 5 < nLen ? /* six bytes */
/* (nPart - 252 << 30) may be not so safe in ECMAScript! So...: */
(nPart - 252) * 1073741824 + (aBytes[++nIdx] - 128 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 247 && nPart < 252 && nIdx + 4 < nLen ? /* five bytes */
(nPart - 248 << 24) + (aBytes[++nIdx] - 128 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 239 && nPart < 248 && nIdx + 3 < nLen ? /* four bytes */
(nPart - 240 << 18) + (aBytes[++nIdx] - 128 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 223 && nPart < 240 && nIdx + 2 < nLen ? /* three bytes */
(nPart - 224 << 12) + (aBytes[++nIdx] - 128 << 6) + aBytes[++nIdx] - 128
: nPart > 191 && nPart < 224 && nIdx + 1 < nLen ? /* two bytes */
(nPart - 192 << 6) + aBytes[++nIdx] - 128
: /* nPart < 127 ? */ /* one byte */
nPart
);
}
return sView;
}
let str = utf8ArrayToString([50,72,226,130,130,32,43,32,79,226,130,130,32,226,135,140,32,50,72,226,130,130,79]);
// Must show 2H₂ + O₂ ⇌ 2H₂O
console.log(str);
Try these functions,
var JsonToArray = function(json)
{
var str = JSON.stringify(json, null, 0);
var ret = new Uint8Array(str.length);
for (var i = 0; i < str.length; i++) {
ret[i] = str.charCodeAt(i);
}
return ret
};
var binArrayToJson = function(binArray)
{
var str = "";
for (var i = 0; i < binArray.length; i++) {
str += String.fromCharCode(parseInt(binArray[i]));
}
return JSON.parse(str)
}
source: https://gist.github.com/tomfa/706d10fed78c497731ac, kudos to Tomfa
I'm using this function, which works for me:
function uint8ArrayToBase64(data) {
return btoa(Array.from(data).map((c) => String.fromCharCode(c)).join(''));
}
For ES6 and UTF8 string
decodeURIComponent(escape(String.fromCharCode(...uint8arrData)))
By far the easiest way that has worked for me is:
//1. Create or fetch the Uint8Array to use in the example
const bufferArray = new Uint8Array([10, 10, 10])
//2. Turn the Uint8Array into a regular array
const array = Array.from(bufferArray);
//3. Stringify it (option A)
JSON.stringify(array);
//3. Stringify it (option B: uses #serdarsenay code snippet to decode each item in array)
let binArrayToString = function(binArray) {
let str = "";
for (let i = 0; i < binArray.length; i++) {
str += String.fromCharCode(parseInt(binArray[i]));
}
return str;
}
binArrayToString(array);
class UTF8{
static encode(str:string){return new UTF8().encode(str)}
static decode(data:Uint8Array){return new UTF8().decode(data)}
private EOF_byte:number = -1;
private EOF_code_point:number = -1;
private encoderError(code_point) {
console.error("UTF8 encoderError",code_point)
}
private decoderError(fatal, opt_code_point?):number {
if (fatal) console.error("UTF8 decoderError",opt_code_point)
return opt_code_point || 0xFFFD;
}
private inRange(a:number, min:number, max:number) {
return min <= a && a <= max;
}
private div(n:number, d:number) {
return Math.floor(n / d);
}
private stringToCodePoints(string:string) {
/** #type {Array.<number>} */
let cps = [];
// Based on http://www.w3.org/TR/WebIDL/#idl-DOMString
let i = 0, n = string.length;
while (i < string.length) {
let c = string.charCodeAt(i);
if (!this.inRange(c, 0xD800, 0xDFFF)) {
cps.push(c);
} else if (this.inRange(c, 0xDC00, 0xDFFF)) {
cps.push(0xFFFD);
} else { // (inRange(c, 0xD800, 0xDBFF))
if (i == n - 1) {
cps.push(0xFFFD);
} else {
let d = string.charCodeAt(i + 1);
if (this.inRange(d, 0xDC00, 0xDFFF)) {
let a = c & 0x3FF;
let b = d & 0x3FF;
i += 1;
cps.push(0x10000 + (a << 10) + b);
} else {
cps.push(0xFFFD);
}
}
}
i += 1;
}
return cps;
}
private encode(str:string):Uint8Array {
let pos:number = 0;
let codePoints = this.stringToCodePoints(str);
let outputBytes = [];
while (codePoints.length > pos) {
let code_point:number = codePoints[pos++];
if (this.inRange(code_point, 0xD800, 0xDFFF)) {
this.encoderError(code_point);
}
else if (this.inRange(code_point, 0x0000, 0x007f)) {
outputBytes.push(code_point);
} else {
let count = 0, offset = 0;
if (this.inRange(code_point, 0x0080, 0x07FF)) {
count = 1;
offset = 0xC0;
} else if (this.inRange(code_point, 0x0800, 0xFFFF)) {
count = 2;
offset = 0xE0;
} else if (this.inRange(code_point, 0x10000, 0x10FFFF)) {
count = 3;
offset = 0xF0;
}
outputBytes.push(this.div(code_point, Math.pow(64, count)) + offset);
while (count > 0) {
let temp = this.div(code_point, Math.pow(64, count - 1));
outputBytes.push(0x80 + (temp % 64));
count -= 1;
}
}
}
return new Uint8Array(outputBytes);
}
private decode(data:Uint8Array):string {
let fatal:boolean = false;
let pos:number = 0;
let result:string = "";
let code_point:number;
let utf8_code_point = 0;
let utf8_bytes_needed = 0;
let utf8_bytes_seen = 0;
let utf8_lower_boundary = 0;
while (data.length > pos) {
let _byte = data[pos++];
if (_byte == this.EOF_byte) {
if (utf8_bytes_needed != 0) {
code_point = this.decoderError(fatal);
} else {
code_point = this.EOF_code_point;
}
} else {
if (utf8_bytes_needed == 0) {
if (this.inRange(_byte, 0x00, 0x7F)) {
code_point = _byte;
} else {
if (this.inRange(_byte, 0xC2, 0xDF)) {
utf8_bytes_needed = 1;
utf8_lower_boundary = 0x80;
utf8_code_point = _byte - 0xC0;
} else if (this.inRange(_byte, 0xE0, 0xEF)) {
utf8_bytes_needed = 2;
utf8_lower_boundary = 0x800;
utf8_code_point = _byte - 0xE0;
} else if (this.inRange(_byte, 0xF0, 0xF4)) {
utf8_bytes_needed = 3;
utf8_lower_boundary = 0x10000;
utf8_code_point = _byte - 0xF0;
} else {
this.decoderError(fatal);
}
utf8_code_point = utf8_code_point * Math.pow(64, utf8_bytes_needed);
code_point = null;
}
} else if (!this.inRange(_byte, 0x80, 0xBF)) {
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
pos--;
code_point = this.decoderError(fatal, _byte);
} else {
utf8_bytes_seen += 1;
utf8_code_point = utf8_code_point + (_byte - 0x80) * Math.pow(64, utf8_bytes_needed - utf8_bytes_seen);
if (utf8_bytes_seen !== utf8_bytes_needed) {
code_point = null;
} else {
let cp = utf8_code_point;
let lower_boundary = utf8_lower_boundary;
utf8_code_point = 0;
utf8_bytes_needed = 0;
utf8_bytes_seen = 0;
utf8_lower_boundary = 0;
if (this.inRange(cp, lower_boundary, 0x10FFFF) && !this.inRange(cp, 0xD800, 0xDFFF)) {
code_point = cp;
} else {
code_point = this.decoderError(fatal, _byte);
}
}
}
}
//Decode string
if (code_point !== null && code_point !== this.EOF_code_point) {
if (code_point <= 0xFFFF) {
if (code_point > 0)result += String.fromCharCode(code_point);
} else {
code_point -= 0x10000;
result += String.fromCharCode(0xD800 + ((code_point >> 10) & 0x3ff));
result += String.fromCharCode(0xDC00 + (code_point & 0x3ff));
}
}
}
return result;
}
`
Using base64 as the encoding format works quite well. This is how it was implemented for passing secrets via urls in Firefox Send. You will need the base64-js package. These are the functions from the Send source code:
const b64 = require("base64-js")
function arrayToB64(array) {
return b64.fromByteArray(array).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
}
function b64ToArray(str) {
return b64.toByteArray(str + "===".slice((str.length + 3) % 4))
}
With vanilla, browser side, recording from microphone, base64 functions worked for me (I had to implement an audio sending function to a chat).
const ui8a = new Uint8Array(e.target.result);
const string = btoa(ui8a);
const ui8a_2 = atob(string).split(',');
Full code now. Thanks to Bryan Jennings & breakspirit#py4u.net for the code.
https://medium.com/#bryanjenningz/how-to-record-and-play-audio-in-javascript-faa1b2b3e49b
https://www.py4u.net/discuss/282499
index.html
<html>
<head>
<title>Record Audio Test</title>
<meta name="encoding" charset="utf-8" />
</head>
<body>
<h1>Audio Recording Test</h1>
<script src="index.js"></script>
<button id="action" onclick="start()">Start</button>
<button id="stop" onclick="stop()">Stop</button>
<button id="play" onclick="play()">Listen</button>
</body>
</html>
index.js:
const recordAudio = () =>
new Promise(async resolve => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mediaRecorder = new MediaRecorder(stream);
const audioChunks = [];
mediaRecorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
const start = () => mediaRecorder.start();
const stop = () =>
new Promise(resolve => {
mediaRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks);
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
const play = () => audio.play();
resolve({ audioBlob, audioUrl, play });
});
mediaRecorder.stop();
});
resolve({ start, stop });
});
let recorder = null;
let audio = null;
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
const start = async () => {
recorder = await recordAudio();
recorder.start();
}
const stop = async () => {
audio = await recorder.stop();
read(audio.audioUrl);
}
const play = ()=> {
audio.play();
}
const read = (blobUrl)=> {
var xhr = new XMLHttpRequest;
xhr.responseType = 'blob';
xhr.onload = function() {
var recoveredBlob = xhr.response;
const reader = new FileReader();
// This fires after the blob has been read/loaded.
reader.addEventListener('loadend', (e) => {
const ui8a = new Uint8Array(e.target.result);
const string = btoa(ui8a);
const ui8a_2 = atob(string).split(',');
playByteArray(ui8a_2);
});
// Start reading the blob as text.
reader.readAsArrayBuffer(recoveredBlob);
};
// get the blob through blob url
xhr.open('GET', blobUrl);
xhr.send();
}
window.onload = init;
var context; // Audio context
var buf; // Audio buffer
function init() {
if (!window.AudioContext) {
if (!window.webkitAudioContext) {
alert("Your browser does not support any AudioContext and cannot play back this audio.");
return;
}
window.AudioContext = window.webkitAudioContext;
}
context = new AudioContext();
}
function playByteArray(byteArray) {
var arrayBuffer = new ArrayBuffer(byteArray.length);
var bufferView = new Uint8Array(arrayBuffer);
for (i = 0; i < byteArray.length; i++) {
bufferView[i] = byteArray[i];
}
context.decodeAudioData(arrayBuffer, function(buffer) {
buf = buffer;
play2();
});
}
// Play the loaded file
function play2() {
// Create a source node from the buffer
var source = context.createBufferSource();
source.buffer = buf;
// Connect to the final output node (the speakers)
source.connect(context.destination);
// Play immediately
source.start(0);
}
var decodedString = decodeURIComponent(escape(String.fromCharCode(...new Uint8Array(err))));
var obj = JSON.parse(decodedString);
I am using this Typescript snippet:
function UInt8ArrayToString(uInt8Array: Uint8Array): string
{
var s: string = "[";
for(var i: number = 0; i < uInt8Array.byteLength; i++)
{
if( i > 0 )
s += ", ";
s += uInt8Array[i];
}
s += "]";
return s;
}
Remove the type annotations if you need the JavaScript version.
Hope this helps!

Categories