Web Worker: Problems on NodeJS - javascript

I tried to implement the w3schools demo of web worker on nodejs, but it doesn't work. The OS is Windows 10 (v10.0.18363.1256), Chrome (v88.0.4324.104) is set on developer mode and nodejs (x64-v14.15.4). No errors appear both on the browser and command prompt, which tells me that it's all ok (Server running at http://127.0.0.1:80/). The web page appears, but the buttons don't work. You can read the code below. The file myapp.js to start the node is in my own user folder, whereas demo_worker.js and index.html are in the folder c:\nodejsworker.
Thank you in advance for your help.
// demo_workers.js
var i = 0;
function timedCount() {
i = i + 1;
postMessage(i);
setTimeout("timedCount()", 500);
}
timedCount();
//myapp.js
const http = require('http');
var fs = require('fs');
const hostname = '127.0.0.1';
const port = 80;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
// res.end('Hello World');
var myReadStream = fs.createReadStream('C:/nodejsworker/index.html', 'utf8');
myReadStream.pipe(res);
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
<!DOCTYPE html>
<html>
<body>
<p>Count numbers: <output id="result"></output></p>
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
<script>
var w;
function startWorker() {
if (typeof(Worker) !== "undefined") {
if (typeof(w) == "undefined") {
w = new Worker("./demo_workers.js");
}
w.onmessage = function(event) {
document.getElementById("result").innerHTML = event.data;
};
} else {
document.getElementById("result").innerHTML = "Sorry! No Web Worker support.";
}
}
function stopWorker() {
w.terminate();
w = undefined;
}
</script>
</body>
</html>

Related

JQuery ajax call returns 404 page not found

I am trying to parse a JSON string upon loading the page but I get the following error in the web dev tools: GET http://ipaddress/CulturalEvents/calWrapper 404 not found (Note: ipaddress is the address for our IIS web server). When I click on the error I get the following error: Failed to load resource: the server responded with a status of 404 not found.
Here is my index.js
var titles = new Array();
var descriptions = new Array();
var count = 0;
// Function to cycle through events on display
function changeText() {
$('#evtName').html(titles[count]);
$('#evtDesc').html(descriptions[count]);
if (count < titles.length - 1) {
count++;
} else {
count = 0;
}
}
$(document).ready(function () {
$.ajax({
url:'/CulturalEvents/calWrapper',
type: 'GET',
dataType: 'json',
success: function(calJSON){
let eventCheck = 0;
var today = new Date();
var yyyy = today.getFullYear();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0');
today = yyyy + mm + dd;
console.log(today);
for (let i = 0; i < calJSON.length; i++){
if (calJSON[i].startDT == today){
eventCheck = 1;
} else {
eventCheck = 0;
}
if (eventCheck == 1){
titles.push(calJSON[i].summary);
if (calJSON[i].description == ""){
descriptions.push("No description.");
} else{
descriptions.push(calJSON[i].description)
}
} else {
titles.push("No events today.");
descriptions.push("If you know of an event that is not displayed feel free to contact the Diversity Equity and Inclusion committee.")
}
}
}
});
// Rotate through events
changeText();
setInterval(changeText, 10000);
});
It can't find my ajax url '/CulturalEvents/calWrapper'. Note I can run this locally and look for the endpoint /calWrapper and it works perfectly fine, but when I run it on the IIS server it stops working.
Here is my app.js as well:
// C library API
const ffi = require('ffi');
// Express app
const express = require('express');
const app = express();
const path = require('path')
const port = process.env.PORT
const fs = require('fs');
app.use(express.static('public'));
// Send HTML
app.get('/CulturalEvents/', function(req, res){
res.sendFile(path.join(__dirname + '/public/index.html'));
});
// Send style
app.get('/CulturalEvents/style.css', function(req, res) {
res.sendFile(path.join(__dirname + '/public/style.css'));
});
// send JavaScript
app.get('/CulturalEvents/index.js', function (req, res) {
res.readFile(path.join(__dirname + '/public/index.js'), 'utf8', function(err, contents){
res.send(contents);
});
});
// Wrapper function for c library
let wrapper = ffi.Library('./bin/libcalWrapper', {
'calWrapper': [ 'string', [ 'string' ] ]
});
app.get('/CulturalEvents/calWrapper', function (req, res) {
var tempStr = JSON.parse(wrapper.calWrapper(__dirname + "/multiculturalcalendar2021.ics"));
res.send(tempStr);
});
app.listen(port, () => {
console.log(__dirname + '/public/index.js');
});
Also the directory structure is as follows:
CulturalEvents/
public/
index.js
index.html
style.css
app.js
package.json
web.confi

How to prevent the abortion of html script on execution of node js program

What I'm trying to do: Real time speech to text conversion during a video chat
Using: Webrtc, web speech api, node js v9.9.0
What happens: index.html loads > the video element shows up appropriately with a button that begins speech to text on click > Button is clicked and speech to text works normally
BUT
when I open another tab, open localhost, the second video element also shows up appropriately next to the first, however, the speech to text aborted
Reason: Because there's another program with higher priority that starts executing. I'm guessing this other program is the node js program.
What I need to know, is if there's a way the html script can continue executing even after the node js program, or whatever program is currently aborting it, begins its execution.
I have tried using web workers in HTML but that just ended up not executing the speech to text script at all.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Realtime communication with WebRTC</title>
<style>
body {
font-family: monospace;
font-size: 22px;
}
</style>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<span id="speech"></span>
<span id="interim"></span>
<div id="videos">
<video id="localVideo" autoplay></video>
<video id="remoteVideo" autoplay></video>
</div>
<script id = "STT">
function upgrade() {
alert('Please use Google Chrome for best experience');
}
window.onload = function() {
if (!(window.webkitSpeechRecognition) && !(window.speechRecognition)) {
upgrade();
} else {
var recognizing,
transcription = document.getElementById('speech'),
interim_span = document.getElementById('interim');
interim_span.style.opacity = '0.5';
function reset() {
//recognizing = false;
interim_span.innerHTML = '';
transcription.innerHTML = '';
speech.start();
}
var speech = new webkitSpeechRecognition() || speechRecognition();
speech.continuous = true;
speech.interimResults = true;
speech.lang = 'en-US'; // check google web speech example source for more lanuages
speech.start(); //enables recognition on default
speech.onstart = function() {
// When recognition begins
recognizing = true;
};
speech.onresult = function(event) {
// When recognition produces result
var interim_transcript = '';
var final_transcript = '';
// main for loop for final and interim results
for (var i = event.resultIndex; i < event.results.length; ++i) {
if (event.results[i].isFinal) {
final_transcript += event.results[i][0].transcript;
} else {
interim_transcript += event.results[i][0].transcript;
}
}
transcription.innerHTML = final_transcript;
interim_span.innerHTML = interim_transcript;
};
speech.onerror = function(event) {
// Either 'No-speech' or 'Network connection error'
console.error(event.error);
};
speech.onend = function() {
// When recognition ends
reset();
};
}
};
</script>
<button onclick="STT">click</button>
<script src="/socket.io/socket.io.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
index.js:
'use strict';
var os = require('os');
var nodeStatic = require('node-static');
var http = require('http');
var socketIO = require('socket.io');
var fs = require('fs');
var Connect = require('connect');
const threads = require('webworker-threads');
console.log("in index.js");
var fileServer = new(nodeStatic.Server)();
console.log(' var fileServer = new(nodeStatic.Server)();' + fileServer);
var app = http.createServer(function(req, res){
console.log("creating a server");
/*
var worker = new threads.Worker(function(){
function voiceRex(){
console.log('at voiceRex');
fs.readFile('./js/speechreg.html', function (err, html) {
if (err) {
throw err;
}
});
/*res.writeHeader(200, {"Content-Type": "text/html"});
res.write(html);
res.end();
}
this.onmessage = function(event){
res.writeHeader(200, {"Content-Type": "text/html"});
res.write(html);
}
});
worker.onmessage = function(event) {
res.end();
};*/
fileServer.serve(req, res);
}).listen(8000);
console.log('app:' + app);
var io = socketIO.listen(app);
console.log('io:' + io);
io.sockets.on('connection', function(socket) {
console.log('io.sockets.on(connection, function(socket)');
console.log('recieved connection ');
// convenience function to log server messages on the client
function log() {
console.log('in log in index.js');
var array = ['Message from server:'];
console.log('message from server:' + array);
array.push.apply(array, arguments);
socket.emit('log', array);
}
socket.on('message', function(message) {
console.log('Client said: ', message);
log('Client said: ', message);
// for a real app, would be room-only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', function(room) {
console.log('Received request to create or join room ' + room);
log('Received request to create or join room ' + room);
var clientsInRoom = io.sockets.adapter.rooms[room];
console.log(clientsInRoom);
var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0;
console.log(numClients);
log('Room ' + room + ' now has ' + numClients + ' client(s)');
console.log('Room ' + room + ' now has ' + numClients + ' client(s)');
if (numClients === 0) {
console.log('number of clients is 0');
socket.join(room);
log('Client ID ' + socket.id + ' created room ' + room);
socket.emit('created', room, socket.id);
} else if (numClients === 1) {
console.log('number of clients is 1');
log('Client ID ' + socket.id + ' joined room ' + room);
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room, socket.id);
io.sockets.in(room).emit('ready');
} else { // max two clients
console.log('number of clients is max');
socket.emit('full', room);
}
}, );
socket.on('ipaddr', function() {
console.log('in ipaddr');
var ifaces = os.networkInterfaces();
for (var dev in ifaces) {
ifaces[dev].forEach(function(details) {
if (details.family === 'IPv4' && details.address !== '127.0.0.1') {
socket.emit('ipaddr', details.address);
}
});
}
});
});
index.html: With web worker
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Realtime communication with WebRTC</title>
<style>
body {
font-family: monospace;
font-size: 22px;
}
</style>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>Realtime communication with WebRTC</h1>
<span id="speech"></span>
<span id="interim"></span>
<div id="videos">
<video id="localVideo" autoplay></video>
<video id="remoteVideo" autoplay></video>
</div>
<script id="worker" type="javascript/worker">
var voice = function(transcription, interim_span){
var recognizing;
function reset() {
//recognizing = false;
interim_span.innerHTML = "";
transcription.innerHTML = "";
speech.start();
}
var speech = new webkitSpeechRecognition() || speechRecognition();
speech.lang = "en"; // check google web speech example source for more lanuages
speech.continuous = true;
speech.interimResults = true;
speech.start(); //enables recognition on default
speech.onstart = function() {
// When recognition begins
recognizing = true;
};
speech.onresult = function(event) {
// When recognition produces result
var interim_transcript = '';
var final_transcript = '';
var interim_transcript_post = interim_transcript;
var final_transcript_post = final_transcript;
var final_transcript_post = JSON.parse(JSON.stringify(final_transcript_post));
// main for loop for final and interim results
for (var i = event.resultIndex; i < event.results.length; ++i) {
if (event.results[i].isFinal) {
final_transcript += event.results[i][0].transcript;
final_transcript_post += JSON.parse(JSON.stringify(final_transcript));
} else {
interim_transcript += event.results[i][0].transcript;
interim_transcript_post += JSON.parse(JSON.stringify(interim_transcript));
}
}
self.postMessage({
'final_transcript_post': final_transcript_post,
'interim_transcript_post': interim_transcript_post
});
};
speech.onerror = function(event) {
// Either 'No-speech' or 'Network connection error'
console.error(event.error);
};
speech.onend = function() {
// When recognition ends
reset();
};
};
</script>
<script>
var transcription = document.getElementById('speech');
var interim_span = document.getElementById('interim');
var transcription = JSON.parse(JSON.stringify(transcription));
var interim_span = JSON.parse(JSON.stringify(interim_span));
document.getElementById('interim').style.opacity = '0.5';
var blob = new Blob([document.getElementById('worker').textContent]);
var w = new Worker(window.URL.createObjectURL(blob));
w.postMessage({
'transcription': transcription,
'interim_span':interim_span
});
w.onmessage = function(event){
transcription.innerHTML = event.data.final_transcript_post;
interim_span.innerHTML = event.data.interim_transcript_post;
};
</script>
<script src="/socket.io/socket.io.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
When one tab is open. speech to text works fine
Two tabs are open and speech to text is aborted
Web speech api can not access microphone simultaneously with the video chat. If you want transcription inside video chat you should just stream the audio recorded to the external transcription service and display the results. Google Speech API supports streams for example, but it pretty expensive.

Sending data from serialport to socket.io

I'm willing to do a simple dialogue between my Arduino and a web interface.
I've been able to do this in the command line so now I'm trying to do the same with some buttons.
I've been using socket.io to manage the sockets and I can emit a socket to Arduino but when I try to emit a socket from Arduino to the server it doesn't seem to work.
Here's my code.
server.js:
var fs = require('fs');
var http = require('http');
var io = require('socket.io');
var url = require("url");
var SerialPort = require("serialport").SerialPort;
var socketServer;
var serialport;
var portName = '/dev/ttyACM0'; //change this to your Arduino port
var sendData = "";
function startServer(route,handle,debug) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received");
var content = route(handle,pathname,response,request,debug);
}
var httpServer = http.createServer(onRequest).listen(1337, function(){
console.log("Listening at: http://localhost:1337");
});
initSocketIO(httpServer,debug);
}
function initSocketIO(httpServer,debug) {
socketServer = io.listen(httpServer);
if(debug == false) socketServer.set('log level', 1);
socketServer.on('connection', function (socket) {
console.log("Connected");
socket.emit('onconnection', {msg:sendData});
socket.on('buttonval', function(data) {
console.log('A letter ' + data + ' was sent');
serialport.write(data);
});
socket.on('update', function(data) {
console.log('Yaaaaayaaa');
socket.emit('updateData',{msg:data.buffer});
});
socket.on('error', function(err) {
console.log(err);
});
serialListener(debug, socket);
});
}
// Listen to serial port
function serialListener(debug, socket) {
var receivedData = "";
serialport = new SerialPort(portName, {
baudrate: 9600,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false
});
serialport.on('open', function(){
var buffer = "";
serialport.on('data', function(data){
var match = /\r|\n/.exec(data);
if (match) {
buffer += data;
console.log("%s", buffer);
sendData = buffer;
socket.emit('update', {msg:buffer});
buffer = "";
} else buffer += data;
});
});
}
exports.start = startServer;
index.html:
<!DOCTYPE HTML>
<html>
<head>
<style>
</style>
<script src="http://code.jquery.com/jquery-1.8.3.js"></script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var iosocket;
function initSocketIO() {
iosocket = io.connect();
iosocket.on('onconnection', function(value) {
initButton();
//recieve changed values by other client from server
iosocket.on('updateData', function (recievedData) {
alert(recievedData.msg);
$("#result").html(recievedData.msg);
});
});
}
function initButton(){$( "#check" ).button();}
window.onload = function() {initSocketIO();};
$(document).ready(function() {
$('#check').click(function() {
iosocket.emit('buttonval', 'S');
});
});
</script>
</head>
<body>
<div id="rData">
<h2>Data from Arduino</h2>
<div id="result"></div>
</div>
<input type="button" id="check" value="Measure"/></div>
</div>
</body>
</html>
arduino.ino:
float rP = 0;
float lP = 0;
float cP = 0;
void initMeasurement() {
Serial.println("Ready for measures");
}
void doMeasure() {
rP = 0.1;
lP = 0.2;
cP = 0.35;
}
boolean start = false;
void setup() {
// initialize serial:
Serial.begin(9600);
// init something
initMeasurement();
}
void loop() {
// Recieve data from Node and write it to a String
if (Serial.available() && !start) {
char inChar = (char)Serial.read();
if(inChar == 'S'){ // end character for led
start = true;
}
} else if (start) {
Serial.println("Measuring...");
doMeasure();
Serial.println("Results are:");
Serial.print("Rp = ");
Serial.println(rP);
Serial.print("Lp = ");
Serial.println(lP);
Serial.print("Cp = ");
Serial.println(cP);
start = false;
Serial.println("Ready again");
}
//delay(50); // give the Arduino some breathing room.
}

How do I code a node.js proxy to use NTLMv2 authentication

I've tried to search through stackoverflow for a similar question but most people are asking about the client-side of the NTLMv2 protocol.
I'm implementing a proxy that is performing the server-side of the protocol to authenticate users connecting to the proxy.
I've coded a lot of the protocol but I'm now stuck because the documentation that should take me further is difficult to understand.
This is the best documentation I've found so far: http://www.innovation.ch/personal/ronald/ntlm.html, but how to deal with the LM and NT responses is oblivious to me.
The proxy is located on an application server. The domain server is a different machine.
Example code for the node proxy:
var http = require('http')
, request = require('request')
, ProxyAuth = require('./proxyAuth');
function handlerProxy(req, res) {
ProxyAuth.authorize(req, res);
var options = {
url: req.url,
method: req.method,
headers: req.headers
}
req.pipe(request(options)).pipe(res)
}
var server = http.createServer(handlerProxy);
server.listen(3000, function(){
console.log('Express server listening on port ' + 3000);
});
ProxyAuth.js code:
ProxyAuth = {
parseType3Msg: function(buf) {
var lmlen = buf.readUInt16LE(12);
var lmoff = buf.readUInt16LE(16);
var ntlen = buf.readUInt16LE(20);
var ntoff = buf.readUInt16LE(24);
var dlen = buf.readUInt16LE(28);
var doff = buf.readUInt16LE(32);
var ulen = buf.readUInt16LE(36);
var uoff = buf.readUInt16LE(40);
var hlen = buf.readUInt16LE(44);
var hoff = buf.readUInt16LE(48);
var domain = buf.slice(doff, doff+dlen).toString('utf8');
var user = buf.slice(uoff, uoff+ulen).toString('utf8');
var host = buf.slice(hoff, hoff+hlen).toString('utf8');
var lmresp = buf.slice(lmoff, lmoff+lmlen).toString('utf8');
var ntresp = buf.slice(ntoff, ntoff+ntlen).toString('utf8');
console.log(user, lmresp, ntresp);
/* NOW WHAT DO I DO? */
},
authorize: function(req, res) {
var auth = req.headers['authorization'];
if (!auth) {
res.writeHead(401, {
'WWW-Authenticate': 'NTLM',
});
res.end('<html><body>Proxy Authentication Required</body></html>');
}
else if(auth) {
var header = auth.split(' ');
var buf = new Buffer(header[1], 'base64');
var msg = buf.toString('utf8');
console.log("Decoded", msg);
if (header[0] == "NTLM") {
if (msg.substring(0,8) != "NTLMSSP\x00") {
res.writeHead(401, {
'WWW-Authenticate': 'NTLM',
});
res.end('<html><body>Header not recognized</body></html>');
}
// Type 1 message
if (msg[8] == "\x01") {
console.log(buf.toString('hex'));
var challenge = require('crypto').randomBytes(8);
var type2msg = "NTLMSSP\x00"+
"\x02\x00\x00\x00"+ // 8 message type
"\x00\x00\x00\x00"+ // 12 target name len/alloc
"\x00\x00\x00\x00"+ // 16 target name offset
"\x01\x82\x00\x00"+ // 20 flags
challenge.toString('utf8')+ // 24 challenge
"\x00\x00\x00\x00\x00\x00\x00\x00"+ // 32 context
"\x00\x00\x00\x00\x00\x00\x00\x00"; // 40 target info len/alloc/offset
type2msg = new Buffer(type2msg).toString('base64');
res.writeHead(401, {
'WWW-Authenticate': 'NTLM '+type2msg.trim(),
});
res.end();
}
else if (msg[8] == "\x03") {
console.log(buf.toString('hex'));
ProxyAuth.parseType3Msg(buf);
/* NOW WHAT DO I DO? */
}
}
else if (header[0] == "Basic") {
}
}
}
};
module.exports = ProxyAuth;
The /* NOW WHAT DO I DO? */ comment specifies where I am stuck.
I hope I put enough information there, but let me know if anything else is needed.

binary.js and file broadcast to connected clients

What i'm trying to do here is to broadcast a file to other clients connected to the server via websocket/binaryjs using a binaryjs client/server system.
The problem comes with large files (or maybe is totally random?!?!). The client stops sending data to the server giving an error.
This is the first time i try something like this and i'm pretty new to socket programming so i might be missing something.... or everything.
Here is the client
<html>
<head>
<script src="http://cdn.binaryjs.com/0/binary.js" type="text/javascript" language="javascript"></script>
<script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript" language="javascript"></script>
<script>
$(document).ready(start);
function upload(e){
var file = e.target.files[0];
var chunkSize = 1024 * 1024; //1mb in this case but the problem seems to be uneffected by the chunk size
var fileSize = file.size;
var start = 0;
var end = chunkSize;
var s = client.createStream({name: file.name, size: file.size});
while(start < fileSize) {
w = s.write(file.slice(start, end));
console.log(w);
start = end;
end = start + chunkSize;
}
/*
//the client.send way
var reader = new FileReader();
reader.onload = function(e) {
var contents = e.target.result;
client.send(contents, {name: file.name, size: file.size});
};
reader.readAsArrayBuffer(file);
*/
}
var client = new BinaryClient('ws://0.0.0.0');
function start() {
var o = $('#output');
client.on('stream', function(stream, meta){
var parts = [];
var meta = meta;
var downloaded = 0;
var element = $('<div />');
o.append(element);
stream.on('data', function(data){
downloaded += data.byteLength;
element.html(meta.name+' '+(Math.round(downloaded/meta.size*100 *100)/100)+'% ');
parts.push(data);
});
stream.on('end', function(){
var a = document.createElement("a");
a.innerHTML='download';
a.href = (window.URL || window.webkitURL).createObjectURL(new Blob(parts));
a.download = meta.name;
a.target = '_blank';
element.append(a);
});
});
}
</script>
</head>
<body>
<input type="file" id="files" name="files" />
<div id="output"></div>
<script>
document.getElementById('files').addEventListener('change', upload, false);
</script>
</body>
</html>
And then comes the server code
var BinaryServer = require('binaryjs').BinaryServer;
var server = BinaryServer({port:80});
var clients = [];
server.on('connection', function(client){
clients.push(client);
console.log(clients.length);
client.on('stream', function(stream, meta){
var s = [];
var d = 0;
for (var i=0;i<clients.length;i++) {
if (clients[i].id == client.id) {
} else {
s.push(clients[i].createStream(meta));
}
}
var name = meta;
var parts = [];
stream.on('data', function(data){
d += data.length;
console.log('data in '+d);
for (var i=0;i<s.length;i++) {
s[i].write(data);
}
});
stream.on('end', function(){
console.log('end');
for (var i=0;i<s.length;i++) {
s[i].end();
}
});
});
client.on('close', function() {
for (var i=0;i<clients.length;i++) {
if(clients[i].id == client.id) {
clients.splice(i, 1);
}
}
client.close();
});
client.on('error', function(){
console.log('error');
});
});
While uploading from the client, i get this error message at random moments (client binary.js)
WebSocket connection to 'ws://0.0.0.0/' failed: Failed to send WebSocket frame. binary.js:1341
WebSocket connection to 'ws://0.0.0.0/' failed: Failed to load Blob: error code = 3 binary.js:1341
No problem with small files (for instance it NEVER fails broadcasting a 25mb wmv). Dunno if it's just a coincidence.
Any help would be appreciated.

Categories